Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
recordmcount.c
Go to the documentation of this file.
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 <[email protected]>. 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 <[email protected]>, 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 static int fd_map; /* File descriptor for file being modified. */
37 static int mmap_failed; /* Boolean flag. */
38 static void *ehdr_curr; /* current ElfXX_Ehdr * for resource cleanup */
39 static char gpfx; /* prefix for global symbol name (sometimes '_') */
40 static struct stat sb; /* Remember .st_size, etc. */
41 static jmp_buf jmpenv; /* setjmp/longjmp per-file error escape */
42 static const char *altmcount; /* alternate mcount symbol name */
43 static int warn_on_notrace_sect; /* warn when section has mcount not being recorded */
44 
45 /* setjmp() return values */
46 enum {
47  SJ_SETJMP = 0, /* hardwired first return */
50 };
51 
52 /* Per-file resource cleanup when multiple files. */
53 static void
54 cleanup(void)
55 {
56  if (!mmap_failed)
57  munmap(ehdr_curr, sb.st_size);
58  else
59  free(ehdr_curr);
60  close(fd_map);
61 }
62 
63 static void __attribute__((noreturn))
64 fail_file(void)
65 {
66  cleanup();
67  longjmp(jmpenv, SJ_FAIL);
68 }
69 
70 static void __attribute__((noreturn))
71 succeed_file(void)
72 {
73  cleanup();
74  longjmp(jmpenv, SJ_SUCCEED);
75 }
76 
77 /* ulseek, uread, ...: Check return value for errors. */
78 
79 static off_t
80 ulseek(int const fd, off_t const offset, int const whence)
81 {
82  off_t const w = lseek(fd, offset, whence);
83  if (w == (off_t)-1) {
84  perror("lseek");
85  fail_file();
86  }
87  return w;
88 }
89 
90 static size_t
91 uread(int const fd, void *const buf, size_t const count)
92 {
93  size_t const n = read(fd, buf, count);
94  if (n != count) {
95  perror("read");
96  fail_file();
97  }
98  return n;
99 }
100 
101 static size_t
102 uwrite(int const fd, void const *const buf, size_t const count)
103 {
104  size_t const n = write(fd, buf, count);
105  if (n != count) {
106  perror("write");
107  fail_file();
108  }
109  return n;
110 }
111 
112 static void *
113 umalloc(size_t size)
114 {
115  void *const addr = malloc(size);
116  if (addr == 0) {
117  fprintf(stderr, "malloc failed: %zu bytes\n", size);
118  fail_file();
119  }
120  return addr;
121 }
122 
123 static unsigned char ideal_nop5_x86_64[5] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };
124 static unsigned char ideal_nop5_x86_32[5] = { 0x3e, 0x8d, 0x74, 0x26, 0x00 };
125 static unsigned char *ideal_nop;
126 
127 static char rel_type_nop;
128 
129 static int (*make_nop)(void *map, size_t const offset);
130 
131 static int make_nop_x86(void *map, size_t const offset)
132 {
133  uint32_t *ptr;
134  unsigned char *op;
135 
136  /* Confirm we have 0xe8 0x0 0x0 0x0 0x0 */
137  ptr = map + offset;
138  if (*ptr != 0)
139  return -1;
140 
141  op = map + offset - 1;
142  if (*op != 0xe8)
143  return -1;
144 
145  /* convert to nop */
146  ulseek(fd_map, offset - 1, SEEK_SET);
147  uwrite(fd_map, ideal_nop, 5);
148  return 0;
149 }
150 
151 /*
152  * Get the whole file as a programming convenience in order to avoid
153  * malloc+lseek+read+free of many pieces. If successful, then mmap
154  * avoids copying unused pieces; else just read the whole file.
155  * Open for both read and write; new info will be appended to the file.
156  * Use MAP_PRIVATE so that a few changes to the in-memory ElfXX_Ehdr
157  * do not propagate to the file until an explicit overwrite at the last.
158  * This preserves most aspects of consistency (all except .st_size)
159  * for simultaneous readers of the file while we are appending to it.
160  * However, multiple writers still are bad. We choose not to use
161  * locking because it is expensive and the use case of kernel build
162  * makes multiple writers unlikely.
163  */
164 static void *mmap_file(char const *fname)
165 {
166  void *addr;
167 
168  fd_map = open(fname, O_RDWR);
169  if (fd_map < 0 || fstat(fd_map, &sb) < 0) {
170  perror(fname);
171  fail_file();
172  }
173  if (!S_ISREG(sb.st_mode)) {
174  fprintf(stderr, "not a regular file: %s\n", fname);
175  fail_file();
176  }
177  addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,
178  fd_map, 0);
179  mmap_failed = 0;
180  if (addr == MAP_FAILED) {
181  mmap_failed = 1;
182  addr = umalloc(sb.st_size);
183  uread(fd_map, addr, sb.st_size);
184  }
185  return addr;
186 }
187 
188 /* w8rev, w8nat, ...: Handle endianness. */
189 
190 static uint64_t w8rev(uint64_t const x)
191 {
192  return ((0xff & (x >> (0 * 8))) << (7 * 8))
193  | ((0xff & (x >> (1 * 8))) << (6 * 8))
194  | ((0xff & (x >> (2 * 8))) << (5 * 8))
195  | ((0xff & (x >> (3 * 8))) << (4 * 8))
196  | ((0xff & (x >> (4 * 8))) << (3 * 8))
197  | ((0xff & (x >> (5 * 8))) << (2 * 8))
198  | ((0xff & (x >> (6 * 8))) << (1 * 8))
199  | ((0xff & (x >> (7 * 8))) << (0 * 8));
200 }
201 
202 static uint32_t w4rev(uint32_t const x)
203 {
204  return ((0xff & (x >> (0 * 8))) << (3 * 8))
205  | ((0xff & (x >> (1 * 8))) << (2 * 8))
206  | ((0xff & (x >> (2 * 8))) << (1 * 8))
207  | ((0xff & (x >> (3 * 8))) << (0 * 8));
208 }
209 
210 static uint32_t w2rev(uint16_t const x)
211 {
212  return ((0xff & (x >> (0 * 8))) << (1 * 8))
213  | ((0xff & (x >> (1 * 8))) << (0 * 8));
214 }
215 
216 static uint64_t w8nat(uint64_t const x)
217 {
218  return x;
219 }
220 
221 static uint32_t w4nat(uint32_t const x)
222 {
223  return x;
224 }
225 
226 static uint32_t w2nat(uint16_t const x)
227 {
228  return x;
229 }
230 
231 static uint64_t (*w8)(uint64_t);
232 static uint32_t (*w)(uint32_t);
233 static uint32_t (*w2)(uint16_t);
234 
235 /* Names of the sections that could contain calls to mcount. */
236 static int
237 is_mcounted_section_name(char const *const txtname)
238 {
239  return strcmp(".text", txtname) == 0 ||
240  strcmp(".ref.text", txtname) == 0 ||
241  strcmp(".sched.text", txtname) == 0 ||
242  strcmp(".spinlock.text", txtname) == 0 ||
243  strcmp(".irqentry.text", txtname) == 0 ||
244  strcmp(".kprobes.text", txtname) == 0 ||
245  strcmp(".text.unlikely", txtname) == 0;
246 }
247 
248 /* 32 bit and 64 bit are very similar */
249 #include "recordmcount.h"
250 #define RECORD_MCOUNT_64
251 #include "recordmcount.h"
252 
253 /* 64-bit EM_MIPS has weird ELF64_Rela.r_info.
254  * http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf
255  * We interpret Table 29 Relocation Operation (Elf64_Rel, Elf64_Rela) [p.40]
256  * to imply the order of the members; the spec does not say so.
257  * typedef unsigned char Elf64_Byte;
258  * fails on MIPS64 because their <elf.h> already has it!
259  */
260 
261 typedef uint8_t myElf64_Byte; /* Type for a 8-bit quantity. */
262 
263 union mips_r_info {
265  struct {
266  Elf64_Word r_sym; /* Symbol index. */
267  myElf64_Byte r_ssym; /* Special symbol. */
268  myElf64_Byte r_type3; /* Third relocation. */
269  myElf64_Byte r_type2; /* Second relocation. */
270  myElf64_Byte r_type; /* First relocation. */
271  } r_mips;
272 };
273 
274 static uint64_t MIPS64_r_sym(Elf64_Rel const *rp)
275 {
276  return w(((union mips_r_info){ .r_info = rp->r_info }).r_mips.r_sym);
277 }
278 
279 static void MIPS64_r_info(Elf64_Rel *const rp, unsigned sym, unsigned type)
280 {
281  rp->r_info = ((union mips_r_info){
282  .r_mips = { .r_sym = w(sym), .r_type = type }
283  }).r_info;
284 }
285 
286 static void
287 do_file(char const *const fname)
288 {
289  Elf32_Ehdr *const ehdr = mmap_file(fname);
290  unsigned int reltype = 0;
291 
292  ehdr_curr = ehdr;
293  w = w4nat;
294  w2 = w2nat;
295  w8 = w8nat;
296  switch (ehdr->e_ident[EI_DATA]) {
297  static unsigned int const endian = 1;
298  default:
299  fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
300  ehdr->e_ident[EI_DATA], fname);
301  fail_file();
302  break;
303  case ELFDATA2LSB:
304  if (*(unsigned char const *)&endian != 1) {
305  /* main() is big endian, file.o is little endian. */
306  w = w4rev;
307  w2 = w2rev;
308  w8 = w8rev;
309  }
310  break;
311  case ELFDATA2MSB:
312  if (*(unsigned char const *)&endian != 0) {
313  /* main() is little endian, file.o is big endian. */
314  w = w4rev;
315  w2 = w2rev;
316  w8 = w8rev;
317  }
318  break;
319  } /* end switch */
320  if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0
321  || w2(ehdr->e_type) != ET_REL
322  || ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
323  fprintf(stderr, "unrecognized ET_REL file %s\n", fname);
324  fail_file();
325  }
326 
327  gpfx = 0;
328  switch (w2(ehdr->e_machine)) {
329  default:
330  fprintf(stderr, "unrecognized e_machine %d %s\n",
331  w2(ehdr->e_machine), fname);
332  fail_file();
333  break;
334  case EM_386:
335  reltype = R_386_32;
336  make_nop = make_nop_x86;
337  ideal_nop = ideal_nop5_x86_32;
338  mcount_adjust_32 = -1;
339  break;
340  case EM_ARM: reltype = R_ARM_ABS32;
341  altmcount = "__gnu_mcount_nc";
342  break;
343  case EM_IA_64: reltype = R_IA64_IMM64; gpfx = '_'; break;
344  case EM_MIPS: /* reltype: e_class */ gpfx = '_'; break;
345  case EM_PPC: reltype = R_PPC_ADDR32; gpfx = '_'; break;
346  case EM_PPC64: reltype = R_PPC64_ADDR64; gpfx = '_'; break;
347  case EM_S390: /* reltype: e_class */ gpfx = '_'; break;
348  case EM_SH: reltype = R_SH_DIR32; break;
349  case EM_SPARCV9: reltype = R_SPARC_64; gpfx = '_'; break;
350  case EM_X86_64:
351  make_nop = make_nop_x86;
352  ideal_nop = ideal_nop5_x86_64;
353  reltype = R_X86_64_64;
354  mcount_adjust_64 = -1;
355  break;
356  } /* end switch */
357 
358  switch (ehdr->e_ident[EI_CLASS]) {
359  default:
360  fprintf(stderr, "unrecognized ELF class %d %s\n",
361  ehdr->e_ident[EI_CLASS], fname);
362  fail_file();
363  break;
364  case ELFCLASS32:
365  if (w2(ehdr->e_ehsize) != sizeof(Elf32_Ehdr)
366  || w2(ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
367  fprintf(stderr,
368  "unrecognized ET_REL file: %s\n", fname);
369  fail_file();
370  }
371  if (w2(ehdr->e_machine) == EM_S390) {
372  reltype = R_390_32;
373  mcount_adjust_32 = -4;
374  }
375  if (w2(ehdr->e_machine) == EM_MIPS) {
376  reltype = R_MIPS_32;
377  is_fake_mcount32 = MIPS32_is_fake_mcount;
378  }
379  do32(ehdr, fname, reltype);
380  break;
381  case ELFCLASS64: {
382  Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
383  if (w2(ghdr->e_ehsize) != sizeof(Elf64_Ehdr)
384  || w2(ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
385  fprintf(stderr,
386  "unrecognized ET_REL file: %s\n", fname);
387  fail_file();
388  }
389  if (w2(ghdr->e_machine) == EM_S390) {
390  reltype = R_390_64;
391  mcount_adjust_64 = -8;
392  }
393  if (w2(ghdr->e_machine) == EM_MIPS) {
394  reltype = R_MIPS_64;
395  Elf64_r_sym = MIPS64_r_sym;
396  Elf64_r_info = MIPS64_r_info;
397  is_fake_mcount64 = MIPS64_is_fake_mcount;
398  }
399  do64(ghdr, fname, reltype);
400  break;
401  }
402  } /* end switch */
403 
404  cleanup();
405 }
406 
407 int
408 main(int argc, char *argv[])
409 {
410  const char ftrace[] = "/ftrace.o";
411  int ftrace_size = sizeof(ftrace) - 1;
412  int n_error = 0; /* gcc-4.3.0 false positive complaint */
413  int c;
414  int i;
415 
416  while ((c = getopt(argc, argv, "w")) >= 0) {
417  switch (c) {
418  case 'w':
419  warn_on_notrace_sect = 1;
420  break;
421  default:
422  fprintf(stderr, "usage: recordmcount [-w] file.o...\n");
423  return 0;
424  }
425  }
426 
427  if ((argc - optind) < 1) {
428  fprintf(stderr, "usage: recordmcount [-w] file.o...\n");
429  return 0;
430  }
431 
432  /* Process each file in turn, allowing deep failure. */
433  for (i = optind; i < argc; i++) {
434  char *file = argv[i];
435  int const sjval = setjmp(jmpenv);
436  int len;
437 
438  /*
439  * The file kernel/trace/ftrace.o references the mcount
440  * function but does not call it. Since ftrace.o should
441  * not be traced anyway, we just skip it.
442  */
443  len = strlen(file);
444  if (len >= ftrace_size &&
445  strcmp(file + (len - ftrace_size), ftrace) == 0)
446  continue;
447 
448  switch (sjval) {
449  default:
450  fprintf(stderr, "internal error: %s\n", file);
451  exit(1);
452  break;
453  case SJ_SETJMP: /* normal sequence */
454  /* Avoid problems if early cleanup() */
455  fd_map = -1;
456  ehdr_curr = NULL;
457  mmap_failed = 1;
458  do_file(file);
459  break;
460  case SJ_FAIL: /* error in do_file or below */
461  ++n_error;
462  break;
463  case SJ_SUCCEED: /* premature success */
464  /* do nothing */
465  break;
466  } /* end switch */
467  }
468  return !!n_error;
469 }
470 
471