Skip to content

Commit

Permalink
recordmcount: Rewrite error/success handling
Browse files Browse the repository at this point in the history
Recordmcount uses setjmp/longjmp to manage control flow as
it reads and then writes the ELF file. This unusual control
flow is hard to follow and check in addition to being unlike
kernel coding style.

So we rewrite these paths to use regular return values to
indicate error/success. When an error or previously-completed object
file is found we return an error code following kernel
coding conventions -- negative error values and 0 for success when
we're not returning a pointer. We return NULL for those that fail
and return non-NULL pointers otherwise.

One oddity is already_has_rel_mcount -- there we use pointer comparison
rather than string comparison to differentiate between
previously-processed object files and returning the name of a text
section.

Link: http://lkml.kernel.org/r/8ba8633d4afe444931f363c8d924bf9565b89a86.1564596289.git.mhelsley@vmware.com

Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
  • Loading branch information
Matt Helsley authored and Steven Rostedt (VMware) committed Aug 31, 2019
1 parent 7f5291d commit 3f1df12
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 119 deletions.
162 changes: 85 additions & 77 deletions scripts/recordmcount.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
#include <getopt.h>
#include <elf.h>
#include <fcntl.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand All @@ -43,7 +42,6 @@ static int fd_map; /* File descriptor for file being modified. */
static int mmap_failed; /* Boolean flag. */
static char gpfx; /* prefix for global symbol name (sometimes '_') */
static struct stat sb; /* Remember .st_size, etc. */
static jmp_buf jmpenv; /* setjmp/longjmp per-file error escape */
static const char *altmcount; /* alternate mcount symbol name */
static int warn_on_notrace_sect; /* warn when section has mcount not being recorded */
static void *file_map; /* pointer of the mapped file */
Expand All @@ -53,13 +51,6 @@ static void *file_ptr; /* current file pointer location */
static void *file_append; /* added to the end of the file */
static size_t file_append_size; /* how much is added to end of file */

/* setjmp() return values */
enum {
SJ_SETJMP = 0, /* hardwired first return */
SJ_FAIL,
SJ_SUCCEED
};

/* Per-file resource cleanup when multiple files. */
static void
cleanup(void)
Expand All @@ -75,20 +66,6 @@ cleanup(void)
file_updated = 0;
}

static void __attribute__((noreturn))
fail_file(void)
{
cleanup();
longjmp(jmpenv, SJ_FAIL);
}

static void __attribute__((noreturn))
succeed_file(void)
{
cleanup();
longjmp(jmpenv, SJ_SUCCEED);
}

/* ulseek, uwrite, ...: Check return value for errors. */

static off_t
Expand All @@ -107,12 +84,12 @@ ulseek(off_t const offset, int const whence)
}
if (file_ptr < file_map) {
fprintf(stderr, "lseek: seek before file\n");
fail_file();
return -1;
}
return file_ptr - file_map;
}

static size_t
static ssize_t
uwrite(void const *const buf, size_t const count)
{
size_t cnt = count;
Expand All @@ -129,7 +106,8 @@ uwrite(void const *const buf, size_t const count)
}
if (!file_append) {
perror("write");
fail_file();
cleanup();
return -1;
}
if (file_ptr < file_end) {
cnt = file_end - file_ptr;
Expand All @@ -155,7 +133,8 @@ umalloc(size_t size)
void *const addr = malloc(size);
if (addr == 0) {
fprintf(stderr, "malloc failed: %zu bytes\n", size);
fail_file();
cleanup();
return NULL;
}
return addr;
}
Expand Down Expand Up @@ -183,8 +162,10 @@ static int make_nop_x86(void *map, size_t const offset)
return -1;

/* convert to nop */
ulseek(offset - 1, SEEK_SET);
uwrite(ideal_nop, 5);
if (ulseek(offset - 1, SEEK_SET) < 0)
return -1;
if (uwrite(ideal_nop, 5) < 0)
return -1;
return 0;
}

Expand Down Expand Up @@ -232,10 +213,12 @@ static int make_nop_arm(void *map, size_t const offset)
return -1;

/* Convert to nop */
ulseek(off, SEEK_SET);
if (ulseek(off, SEEK_SET) < 0)
return -1;

do {
uwrite(ideal_nop, nop_size);
if (uwrite(ideal_nop, nop_size) < 0)
return -1;
} while (--cnt > 0);

return 0;
Expand All @@ -252,8 +235,10 @@ static int make_nop_arm64(void *map, size_t const offset)
return -1;

/* Convert to nop */
ulseek(offset, SEEK_SET);
uwrite(ideal_nop, 4);
if (ulseek(offset, SEEK_SET) < 0)
return -1;
if (uwrite(ideal_nop, 4) < 0)
return -1;
return 0;
}

Expand All @@ -272,40 +257,56 @@ static int make_nop_arm64(void *map, size_t const offset)
*/
static void *mmap_file(char const *fname)
{
file_map = NULL;
sb.st_size = 0;
fd_map = open(fname, O_RDONLY);
if (fd_map < 0 || fstat(fd_map, &sb) < 0) {
if (fd_map < 0) {
perror(fname);
cleanup();
return NULL;
}
if (fstat(fd_map, &sb) < 0) {
perror(fname);
fail_file();
cleanup();
goto out;
}
if (!S_ISREG(sb.st_mode)) {
fprintf(stderr, "not a regular file: %s\n", fname);
fail_file();
cleanup();
goto out;
}
file_map = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,
fd_map, 0);
mmap_failed = 0;
if (file_map == MAP_FAILED) {
mmap_failed = 1;
file_map = umalloc(sb.st_size);
if (!file_map) {
perror(fname);
goto out;
}
if (read(fd_map, file_map, sb.st_size) != sb.st_size) {
perror(fname);
fail_file();
free(file_map);
file_map = NULL;
goto out;
}
}
out:
close(fd_map);

file_end = file_map + sb.st_size;

return file_map;
}

static void write_file(const char *fname)
static int write_file(const char *fname)
{
char tmp_file[strlen(fname) + 4];
size_t n;

if (!file_updated)
return;
return 0;

sprintf(tmp_file, "%s.rc", fname);

Expand All @@ -317,25 +318,32 @@ static void write_file(const char *fname)
fd_map = open(tmp_file, O_WRONLY | O_TRUNC | O_CREAT, sb.st_mode);
if (fd_map < 0) {
perror(fname);
fail_file();
cleanup();
return -1;
}
n = write(fd_map, file_map, sb.st_size);
if (n != sb.st_size) {
perror("write");
fail_file();
cleanup();
close(fd_map);
return -1;
}
if (file_append_size) {
n = write(fd_map, file_append, file_append_size);
if (n != file_append_size) {
perror("write");
fail_file();
cleanup();
close(fd_map);
return -1;
}
}
close(fd_map);
if (rename(tmp_file, fname) < 0) {
perror(fname);
fail_file();
cleanup();
return -1;
}
return 0;
}

/* w8rev, w8nat, ...: Handle endianness. */
Expand Down Expand Up @@ -400,6 +408,8 @@ is_mcounted_section_name(char const *const txtname)
strcmp(".cpuidle.text", txtname) == 0;
}

static char const *already_has_rel_mcount = "success"; /* our work here is done! */

/* 32 bit and 64 bit are very similar */
#include "recordmcount.h"
#define RECORD_MCOUNT_64
Expand Down Expand Up @@ -438,11 +448,15 @@ static void MIPS64_r_info(Elf64_Rel *const rp, unsigned sym, unsigned type)
}).r_info;
}

static void
static int
do_file(char const *const fname)
{
Elf32_Ehdr *const ehdr = mmap_file(fname);
unsigned int reltype = 0;
int rc = -1;

if (!ehdr)
goto out;

w = w4nat;
w2 = w2nat;
Expand All @@ -452,8 +466,8 @@ do_file(char const *const fname)
default:
fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
ehdr->e_ident[EI_DATA], fname);
fail_file();
break;
cleanup();
goto out;
case ELFDATA2LSB:
if (*(unsigned char const *)&endian != 1) {
/* main() is big endian, file.o is little endian. */
Expand Down Expand Up @@ -485,16 +499,17 @@ do_file(char const *const fname)
|| w2(ehdr->e_type) != ET_REL
|| ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
fprintf(stderr, "unrecognized ET_REL file %s\n", fname);
fail_file();
cleanup();
goto out;
}

gpfx = 0;
switch (w2(ehdr->e_machine)) {
default:
fprintf(stderr, "unrecognized e_machine %u %s\n",
w2(ehdr->e_machine), fname);
fail_file();
break;
cleanup();
goto out;
case EM_386:
reltype = R_386_32;
rel_type_nop = R_386_NONE;
Expand Down Expand Up @@ -534,28 +549,31 @@ do_file(char const *const fname)
default:
fprintf(stderr, "unrecognized ELF class %d %s\n",
ehdr->e_ident[EI_CLASS], fname);
fail_file();
break;
cleanup();
goto out;
case ELFCLASS32:
if (w2(ehdr->e_ehsize) != sizeof(Elf32_Ehdr)
|| w2(ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
fprintf(stderr,
"unrecognized ET_REL file: %s\n", fname);
fail_file();
cleanup();
goto out;
}
if (w2(ehdr->e_machine) == EM_MIPS) {
reltype = R_MIPS_32;
is_fake_mcount32 = MIPS32_is_fake_mcount;
}
do32(ehdr, fname, reltype);
if (do32(ehdr, fname, reltype) < 0)
goto out;
break;
case ELFCLASS64: {
Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
if (w2(ghdr->e_ehsize) != sizeof(Elf64_Ehdr)
|| w2(ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
fprintf(stderr,
"unrecognized ET_REL file: %s\n", fname);
fail_file();
cleanup();
goto out;
}
if (w2(ghdr->e_machine) == EM_S390) {
reltype = R_390_64;
Expand All @@ -567,13 +585,16 @@ do_file(char const *const fname)
Elf64_r_info = MIPS64_r_info;
is_fake_mcount64 = MIPS64_is_fake_mcount;
}
do64(ghdr, fname, reltype);
if (do64(ghdr, fname, reltype) < 0)
goto out;
break;
}
} /* end switch */

write_file(fname);
rc = write_file(fname);
out:
cleanup();
return rc;
}

int
Expand Down Expand Up @@ -604,7 +625,6 @@ main(int argc, char *argv[])
/* Process each file in turn, allowing deep failure. */
for (i = optind; i < argc; i++) {
char *file = argv[i];
int const sjval = setjmp(jmpenv);
int len;

/*
Expand All @@ -617,28 +637,16 @@ main(int argc, char *argv[])
strcmp(file + (len - ftrace_size), ftrace) == 0)
continue;

switch (sjval) {
default:
fprintf(stderr, "internal error: %s\n", file);
exit(1);
break;
case SJ_SETJMP: /* normal sequence */
/* Avoid problems if early cleanup() */
fd_map = -1;
mmap_failed = 1;
file_map = NULL;
file_ptr = NULL;
file_updated = 0;
do_file(file);
break;
case SJ_FAIL: /* error in do_file or below */
/* Avoid problems if early cleanup() */
fd_map = -1;
mmap_failed = 1;
file_map = NULL;
file_ptr = NULL;
file_updated = 0;
if (do_file(file)) {
fprintf(stderr, "%s: failed\n", file);
++n_error;
break;
case SJ_SUCCEED: /* premature success */
/* do nothing */
break;
} /* end switch */
}
}
return !!n_error;
}
Loading

0 comments on commit 3f1df12

Please sign in to comment.