~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/scripts/recordmcount.c

Version: ~ [ linux-5.13-rc5 ] ~ [ linux-5.12.9 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.42 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.124 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.193 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.235 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.271 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.271 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * recordmcount.c: construct a table of the locations of calls to 'mcount'
  3  * so that ftrace can find them quickly.
  4  * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>.  All rights reserved.
  5  * Licensed under the GNU General Public License, version 2 (GPLv2).
  6  *
  7  * Restructured to fit Linux format, as well as other updates:
  8  *  Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
  9  */
 10 
 11 /*
 12  * Strategy: alter the .o file in-place.
 13  *
 14  * Append a new STRTAB that has the new section names, followed by a new array
 15  * ElfXX_Shdr[] that has the new section headers, followed by the section
 16  * contents for __mcount_loc and its relocations.  The old shstrtab strings,
 17  * and the old ElfXX_Shdr[] array, remain as "garbage" (commonly, a couple
 18  * kilobytes.)  Subsequent processing by /bin/ld (or the kernel module loader)
 19  * will ignore the garbage regions, because they are not designated by the
 20  * new .e_shoff nor the new ElfXX_Shdr[].  [In order to remove the garbage,
 21  * then use "ld -r" to create a new file that omits the garbage.]
 22  */
 23 
 24 #include <sys/types.h>
 25 #include <sys/mman.h>
 26 #include <sys/stat.h>
 27 #include <getopt.h>
 28 #include <elf.h>
 29 #include <fcntl.h>
 30 #include <setjmp.h>
 31 #include <stdio.h>
 32 #include <stdlib.h>
 33 #include <string.h>
 34 #include <unistd.h>
 35 
 36 /*
 37  * glibc synced up and added the metag number but didn't add the relocations.
 38  * Work around this in a crude manner for now.
 39  */
 40 #ifndef EM_METAG
 41 #define EM_METAG      174
 42 #endif
 43 #ifndef R_METAG_ADDR32
 44 #define R_METAG_ADDR32                   2
 45 #endif
 46 #ifndef R_METAG_NONE
 47 #define R_METAG_NONE                     3
 48 #endif
 49 
 50 #ifndef EM_AARCH64
 51 #define EM_AARCH64      183
 52 #define R_AARCH64_NONE          0
 53 #define R_AARCH64_ABS64 257
 54 #endif
 55 
 56 static int fd_map;      /* File descriptor for file being modified. */
 57 static int mmap_failed; /* Boolean flag. */
 58 static char gpfx;       /* prefix for global symbol name (sometimes '_') */
 59 static struct stat sb;  /* Remember .st_size, etc. */
 60 static jmp_buf jmpenv;  /* setjmp/longjmp per-file error escape */
 61 static const char *altmcount;   /* alternate mcount symbol name */
 62 static int warn_on_notrace_sect; /* warn when section has mcount not being recorded */
 63 static void *file_map;  /* pointer of the mapped file */
 64 static void *file_end;  /* pointer to the end of the mapped file */
 65 static int file_updated; /* flag to state file was changed */
 66 static void *file_ptr;  /* current file pointer location */
 67 static void *file_append; /* added to the end of the file */
 68 static size_t file_append_size; /* how much is added to end of file */
 69 
 70 /* setjmp() return values */
 71 enum {
 72         SJ_SETJMP = 0,  /* hardwired first return */
 73         SJ_FAIL,
 74         SJ_SUCCEED
 75 };
 76 
 77 /* Per-file resource cleanup when multiple files. */
 78 static void
 79 cleanup(void)
 80 {
 81         if (!mmap_failed)
 82                 munmap(file_map, sb.st_size);
 83         else
 84                 free(file_map);
 85         file_map = NULL;
 86         free(file_append);
 87         file_append = NULL;
 88         file_append_size = 0;
 89         file_updated = 0;
 90 }
 91 
 92 static void __attribute__((noreturn))
 93 fail_file(void)
 94 {
 95         cleanup();
 96         longjmp(jmpenv, SJ_FAIL);
 97 }
 98 
 99 static void __attribute__((noreturn))
100 succeed_file(void)
101 {
102         cleanup();
103         longjmp(jmpenv, SJ_SUCCEED);
104 }
105 
106 /* ulseek, uread, ...:  Check return value for errors. */
107 
108 static off_t
109 ulseek(int const fd, off_t const offset, int const whence)
110 {
111         switch (whence) {
112         case SEEK_SET:
113                 file_ptr = file_map + offset;
114                 break;
115         case SEEK_CUR:
116                 file_ptr += offset;
117                 break;
118         case SEEK_END:
119                 file_ptr = file_map + (sb.st_size - offset);
120                 break;
121         }
122         if (file_ptr < file_map) {
123                 fprintf(stderr, "lseek: seek before file\n");
124                 fail_file();
125         }
126         return file_ptr - file_map;
127 }
128 
129 static size_t
130 uread(int const fd, void *const buf, size_t const count)
131 {
132         size_t const n = read(fd, buf, count);
133         if (n != count) {
134                 perror("read");
135                 fail_file();
136         }
137         return n;
138 }
139 
140 static size_t
141 uwrite(int const fd, void const *const buf, size_t const count)
142 {
143         size_t cnt = count;
144         off_t idx = 0;
145 
146         file_updated = 1;
147 
148         if (file_ptr + count >= file_end) {
149                 off_t aoffset = (file_ptr + count) - file_end;
150 
151                 if (aoffset > file_append_size) {
152                         file_append = realloc(file_append, aoffset);
153                         file_append_size = aoffset;
154                 }
155                 if (!file_append) {
156                         perror("write");
157                         fail_file();
158                 }
159                 if (file_ptr < file_end) {
160                         cnt = file_end - file_ptr;
161                 } else {
162                         cnt = 0;
163                         idx = aoffset - count;
164                 }
165         }
166 
167         if (cnt)
168                 memcpy(file_ptr, buf, cnt);
169 
170         if (cnt < count)
171                 memcpy(file_append + idx, buf + cnt, count - cnt);
172 
173         file_ptr += count;
174         return count;
175 }
176 
177 static void *
178 umalloc(size_t size)
179 {
180         void *const addr = malloc(size);
181         if (addr == 0) {
182                 fprintf(stderr, "malloc failed: %zu bytes\n", size);
183                 fail_file();
184         }
185         return addr;
186 }
187 
188 static unsigned char ideal_nop5_x86_64[5] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };
189 static unsigned char ideal_nop5_x86_32[5] = { 0x3e, 0x8d, 0x74, 0x26, 0x00 };
190 static unsigned char *ideal_nop;
191 
192 static char rel_type_nop;
193 
194 static int (*make_nop)(void *map, size_t const offset);
195 
196 static int make_nop_x86(void *map, size_t const offset)
197 {
198         uint32_t *ptr;
199         unsigned char *op;
200 
201         /* Confirm we have 0xe8 0x0 0x0 0x0 0x0 */
202         ptr = map + offset;
203         if (*ptr != 0)
204                 return -1;
205 
206         op = map + offset - 1;
207         if (*op != 0xe8)
208                 return -1;
209 
210         /* convert to nop */
211         ulseek(fd_map, offset - 1, SEEK_SET);
212         uwrite(fd_map, ideal_nop, 5);
213         return 0;
214 }
215 
216 static unsigned char ideal_nop4_arm64[4] = {0x1f, 0x20, 0x03, 0xd5};
217 static int make_nop_arm64(void *map, size_t const offset)
218 {
219         uint32_t *ptr;
220 
221         ptr = map + offset;
222         /* bl <_mcount> is 0x94000000 before relocation */
223         if (*ptr != 0x94000000)
224                 return -1;
225 
226         /* Convert to nop */
227         ulseek(fd_map, offset, SEEK_SET);
228         uwrite(fd_map, ideal_nop, 4);
229         return 0;
230 }
231 
232 /*
233  * Get the whole file as a programming convenience in order to avoid
234  * malloc+lseek+read+free of many pieces.  If successful, then mmap
235  * avoids copying unused pieces; else just read the whole file.
236  * Open for both read and write; new info will be appended to the file.
237  * Use MAP_PRIVATE so that a few changes to the in-memory ElfXX_Ehdr
238  * do not propagate to the file until an explicit overwrite at the last.
239  * This preserves most aspects of consistency (all except .st_size)
240  * for simultaneous readers of the file while we are appending to it.
241  * However, multiple writers still are bad.  We choose not to use
242  * locking because it is expensive and the use case of kernel build
243  * makes multiple writers unlikely.
244  */
245 static void *mmap_file(char const *fname)
246 {
247         fd_map = open(fname, O_RDONLY);
248         if (fd_map < 0 || fstat(fd_map, &sb) < 0) {
249                 perror(fname);
250                 fail_file();
251         }
252         if (!S_ISREG(sb.st_mode)) {
253                 fprintf(stderr, "not a regular file: %s\n", fname);
254                 fail_file();
255         }
256         file_map = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,
257                         fd_map, 0);
258         mmap_failed = 0;
259         if (file_map == MAP_FAILED) {
260                 mmap_failed = 1;
261                 file_map = umalloc(sb.st_size);
262                 uread(fd_map, file_map, sb.st_size);
263         }
264         close(fd_map);
265 
266         file_end = file_map + sb.st_size;
267 
268         return file_map;
269 }
270 
271 static void write_file(const char *fname)
272 {
273         char tmp_file[strlen(fname) + 4];
274         size_t n;
275 
276         if (!file_updated)
277                 return;
278 
279         sprintf(tmp_file, "%s.rc", fname);
280 
281         /*
282          * After reading the entire file into memory, delete it
283          * and write it back, to prevent weird side effects of modifying
284          * an object file in place.
285          */
286         fd_map = open(tmp_file, O_WRONLY | O_TRUNC | O_CREAT, sb.st_mode);
287         if (fd_map < 0) {
288                 perror(fname);
289                 fail_file();
290         }
291         n = write(fd_map, file_map, sb.st_size);
292         if (n != sb.st_size) {
293                 perror("write");
294                 fail_file();
295         }
296         if (file_append_size) {
297                 n = write(fd_map, file_append, file_append_size);
298                 if (n != file_append_size) {
299                         perror("write");
300                         fail_file();
301                 }
302         }
303         close(fd_map);
304         if (rename(tmp_file, fname) < 0) {
305                 perror(fname);
306                 fail_file();
307         }
308 }
309 
310 /* w8rev, w8nat, ...: Handle endianness. */
311 
312 static uint64_t w8rev(uint64_t const x)
313 {
314         return   ((0xff & (x >> (0 * 8))) << (7 * 8))
315                | ((0xff & (x >> (1 * 8))) << (6 * 8))
316                | ((0xff & (x >> (2 * 8))) << (5 * 8))
317                | ((0xff & (x >> (3 * 8))) << (4 * 8))
318                | ((0xff & (x >> (4 * 8))) << (3 * 8))
319                | ((0xff & (x >> (5 * 8))) << (2 * 8))
320                | ((0xff & (x >> (6 * 8))) << (1 * 8))
321                | ((0xff & (x >> (7 * 8))) << (0 * 8));
322 }
323 
324 static uint32_t w4rev(uint32_t const x)
325 {
326         return   ((0xff & (x >> (0 * 8))) << (3 * 8))
327                | ((0xff & (x >> (1 * 8))) << (2 * 8))
328                | ((0xff & (x >> (2 * 8))) << (1 * 8))
329                | ((0xff & (x >> (3 * 8))) << (0 * 8));
330 }
331 
332 static uint32_t w2rev(uint16_t const x)
333 {
334         return   ((0xff & (x >> (0 * 8))) << (1 * 8))
335                | ((0xff & (x >> (1 * 8))) << (0 * 8));
336 }
337 
338 static uint64_t w8nat(uint64_t const x)
339 {
340         return x;
341 }
342 
343 static uint32_t w4nat(uint32_t const x)
344 {
345         return x;
346 }
347 
348 static uint32_t w2nat(uint16_t const x)
349 {
350         return x;
351 }
352 
353 static uint64_t (*w8)(uint64_t);
354 static uint32_t (*w)(uint32_t);
355 static uint32_t (*w2)(uint16_t);
356 
357 /* Names of the sections that could contain calls to mcount. */
358 static int
359 is_mcounted_section_name(char const *const txtname)
360 {
361         return strcmp(".text",           txtname) == 0 ||
362                 strcmp(".ref.text",      txtname) == 0 ||
363                 strcmp(".sched.text",    txtname) == 0 ||
364                 strcmp(".spinlock.text", txtname) == 0 ||
365                 strcmp(".irqentry.text", txtname) == 0 ||
366                 strcmp(".softirqentry.text", txtname) == 0 ||
367                 strcmp(".kprobes.text", txtname) == 0 ||
368                 strcmp(".text.unlikely", txtname) == 0;
369 }
370 
371 /* 32 bit and 64 bit are very similar */
372 #include "recordmcount.h"
373 #define RECORD_MCOUNT_64
374 #include "recordmcount.h"
375 
376 /* 64-bit EM_MIPS has weird ELF64_Rela.r_info.
377  * http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf
378  * We interpret Table 29 Relocation Operation (Elf64_Rel, Elf64_Rela) [p.40]
379  * to imply the order of the members; the spec does not say so.
380  *      typedef unsigned char Elf64_Byte;
381  * fails on MIPS64 because their <elf.h> already has it!
382  */
383 
384 typedef uint8_t myElf64_Byte;           /* Type for a 8-bit quantity.  */
385 
386 union mips_r_info {
387         Elf64_Xword r_info;
388         struct {
389                 Elf64_Word r_sym;               /* Symbol index.  */
390                 myElf64_Byte r_ssym;            /* Special symbol.  */
391                 myElf64_Byte r_type3;           /* Third relocation.  */
392                 myElf64_Byte r_type2;           /* Second relocation.  */
393                 myElf64_Byte r_type;            /* First relocation.  */
394         } r_mips;
395 };
396 
397 static uint64_t MIPS64_r_sym(Elf64_Rel const *rp)
398 {
399         return w(((union mips_r_info){ .r_info = rp->r_info }).r_mips.r_sym);
400 }
401 
402 static void MIPS64_r_info(Elf64_Rel *const rp, unsigned sym, unsigned type)
403 {
404         rp->r_info = ((union mips_r_info){
405                 .r_mips = { .r_sym = w(sym), .r_type = type }
406         }).r_info;
407 }
408 
409 static void
410 do_file(char const *const fname)
411 {
412         Elf32_Ehdr *const ehdr = mmap_file(fname);
413         unsigned int reltype = 0;
414 
415         w = w4nat;
416         w2 = w2nat;
417         w8 = w8nat;
418         switch (ehdr->e_ident[EI_DATA]) {
419                 static unsigned int const endian = 1;
420         default:
421                 fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
422                         ehdr->e_ident[EI_DATA], fname);
423                 fail_file();
424                 break;
425         case ELFDATA2LSB:
426                 if (*(unsigned char const *)&endian != 1) {
427                         /* main() is big endian, file.o is little endian. */
428                         w = w4rev;
429                         w2 = w2rev;
430                         w8 = w8rev;
431                 }
432                 break;
433         case ELFDATA2MSB:
434                 if (*(unsigned char const *)&endian != 0) {
435                         /* main() is little endian, file.o is big endian. */
436                         w = w4rev;
437                         w2 = w2rev;
438                         w8 = w8rev;
439                 }
440                 break;
441         }  /* end switch */
442         if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0
443         ||  w2(ehdr->e_type) != ET_REL
444         ||  ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
445                 fprintf(stderr, "unrecognized ET_REL file %s\n", fname);
446                 fail_file();
447         }
448 
449         gpfx = 0;
450         switch (w2(ehdr->e_machine)) {
451         default:
452                 fprintf(stderr, "unrecognized e_machine %d %s\n",
453                         w2(ehdr->e_machine), fname);
454                 fail_file();
455                 break;
456         case EM_386:
457                 reltype = R_386_32;
458                 rel_type_nop = R_386_NONE;
459                 make_nop = make_nop_x86;
460                 ideal_nop = ideal_nop5_x86_32;
461                 mcount_adjust_32 = -1;
462                 break;
463         case EM_ARM:     reltype = R_ARM_ABS32;
464                          altmcount = "__gnu_mcount_nc";
465                          break;
466         case EM_AARCH64:
467                         reltype = R_AARCH64_ABS64;
468                         make_nop = make_nop_arm64;
469                         rel_type_nop = R_AARCH64_NONE;
470                         ideal_nop = ideal_nop4_arm64;
471                         gpfx = '_';
472                         break;
473         case EM_IA_64:   reltype = R_IA64_IMM64;   gpfx = '_'; break;
474         case EM_METAG:   reltype = R_METAG_ADDR32;
475                          altmcount = "_mcount_wrapper";
476                          rel_type_nop = R_METAG_NONE;
477                          /* We happen to have the same requirement as MIPS */
478                          is_fake_mcount32 = MIPS32_is_fake_mcount;
479                          break;
480         case EM_MIPS:    /* reltype: e_class    */ gpfx = '_'; break;
481         case EM_PPC:     reltype = R_PPC_ADDR32;   gpfx = '_'; break;
482         case EM_PPC64:   reltype = R_PPC64_ADDR64; gpfx = '_'; break;
483         case EM_S390:    /* reltype: e_class    */ gpfx = '_'; break;
484         case EM_SH:      reltype = R_SH_DIR32;                 break;
485         case EM_SPARCV9: reltype = R_SPARC_64;     gpfx = '_'; break;
486         case EM_X86_64:
487                 make_nop = make_nop_x86;
488                 ideal_nop = ideal_nop5_x86_64;
489                 reltype = R_X86_64_64;
490                 rel_type_nop = R_X86_64_NONE;
491                 mcount_adjust_64 = -1;
492                 break;
493         }  /* end switch */
494 
495         switch (ehdr->e_ident[EI_CLASS]) {
496         default:
497                 fprintf(stderr, "unrecognized ELF class %d %s\n",
498                         ehdr->e_ident[EI_CLASS], fname);
499                 fail_file();
500                 break;
501         case ELFCLASS32:
502                 if (w2(ehdr->e_ehsize) != sizeof(Elf32_Ehdr)
503                 ||  w2(ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
504                         fprintf(stderr,
505                                 "unrecognized ET_REL file: %s\n", fname);
506                         fail_file();
507                 }
508                 if (w2(ehdr->e_machine) == EM_MIPS) {
509                         reltype = R_MIPS_32;
510                         is_fake_mcount32 = MIPS32_is_fake_mcount;
511                 }
512                 do32(ehdr, fname, reltype);
513                 break;
514         case ELFCLASS64: {
515                 Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
516                 if (w2(ghdr->e_ehsize) != sizeof(Elf64_Ehdr)
517                 ||  w2(ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
518                         fprintf(stderr,
519                                 "unrecognized ET_REL file: %s\n", fname);
520                         fail_file();
521                 }
522                 if (w2(ghdr->e_machine) == EM_S390) {
523                         reltype = R_390_64;
524                         mcount_adjust_64 = -14;
525                 }
526                 if (w2(ghdr->e_machine) == EM_MIPS) {
527                         reltype = R_MIPS_64;
528                         Elf64_r_sym = MIPS64_r_sym;
529                         Elf64_r_info = MIPS64_r_info;
530                         is_fake_mcount64 = MIPS64_is_fake_mcount;
531                 }
532                 do64(ghdr, fname, reltype);
533                 break;
534         }
535         }  /* end switch */
536 
537         write_file(fname);
538         cleanup();
539 }
540 
541 int
542 main(int argc, char *argv[])
543 {
544         const char ftrace[] = "/ftrace.o";
545         int ftrace_size = sizeof(ftrace) - 1;
546         int n_error = 0;  /* gcc-4.3.0 false positive complaint */
547         int c;
548         int i;
549 
550         while ((c = getopt(argc, argv, "w")) >= 0) {
551                 switch (c) {
552                 case 'w':
553                         warn_on_notrace_sect = 1;
554                         break;
555                 default:
556                         fprintf(stderr, "usage: recordmcount [-w] file.o...\n");
557                         return 0;
558                 }
559         }
560 
561         if ((argc - optind) < 1) {
562                 fprintf(stderr, "usage: recordmcount [-w] file.o...\n");
563                 return 0;
564         }
565 
566         /* Process each file in turn, allowing deep failure. */
567         for (i = optind; i < argc; i++) {
568                 char *file = argv[i];
569                 int const sjval = setjmp(jmpenv);
570                 int len;
571 
572                 /*
573                  * The file kernel/trace/ftrace.o references the mcount
574                  * function but does not call it. Since ftrace.o should
575                  * not be traced anyway, we just skip it.
576                  */
577                 len = strlen(file);
578                 if (len >= ftrace_size &&
579                     strcmp(file + (len - ftrace_size), ftrace) == 0)
580                         continue;
581 
582                 switch (sjval) {
583                 default:
584                         fprintf(stderr, "internal error: %s\n", file);
585                         exit(1);
586                         break;
587                 case SJ_SETJMP:    /* normal sequence */
588                         /* Avoid problems if early cleanup() */
589                         fd_map = -1;
590                         mmap_failed = 1;
591                         file_map = NULL;
592                         file_ptr = NULL;
593                         file_updated = 0;
594                         do_file(file);
595                         break;
596                 case SJ_FAIL:    /* error in do_file or below */
597                         fprintf(stderr, "%s: failed\n", file);
598                         ++n_error;
599                         break;
600                 case SJ_SUCCEED:    /* premature success */
601                         /* do nothing */
602                         break;
603                 }  /* end switch */
604         }
605         return !!n_error;
606 }
607 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp