Commit 412910cd046c1f14f0fba9c0aec401d47e57dcd1

Authored by Wu Zhangjin
Committed by Ralf Baechle
1 parent a2d49358ba

ftrace/MIPS: Add module support for C version of recordmcount

Since MIPS modules' address space differs from the core kernel space, to access
the _mcount in the core kernel, the kernel functions in modules must use long
call (-mlong-calls): load the _mcount address into one register and jump to the
address stored by the register:

 c:  3c030000        lui     v1,0x0  <-------->  b label
           c: R_MIPS_HI16  _mcount
           c: R_MIPS_NONE  *ABS*
           c: R_MIPS_NONE  *ABS*
10:  64630000        daddiu  v1,v1,0
          10: R_MIPS_LO16 _mcount
          10: R_MIPS_NONE *ABS*
          10: R_MIPS_NONE *ABS*
14:	03e0082d 	move	at,ra
18:	0060f809 	jalr	v1
label:

In the old Perl version of recordmcount, we only need to record the position of
the 1st R_MIPS_HI16 type of _mcount, and later, in ftrace_make_nop(), replace
the instruction in this position by a "b label" and in ftrace_make_call(),
replace it back.

But, the default C version of recordmcount records all of the _mcount symbols,
so, we must filter the 2nd _mcount like the Perl version of recordmcount does.

The C version of recordmcount copes with the symbols before they are linked, So
It doesn't know the type of the symbols and therefore can not filter the
symbols as the Perl version of recordmcount does. But as we can see above, the
2nd _mcount symbols of the long call alawys follows the 1st _mcount symbol of
the same long call, which means the offset from the 1st to the 2nd is fixed, it
is 0x10-0xc = 4 here, 4 is the length of the 1st load instruciton, for MIPS has
fixed length of instructions, this offset is always 4.

And as we know, the _mcount is inserted into the entry of every kernel
function, the offset between the other _mcount's is expected to be always
bigger than 4. So, to filter the 2ns _mcount symbol of the long call, we can
simply check the offset between two _mcount symbols, If it is 4, then, filter
the 2nd _mcount symbol.

To avoid touching too much code, an 'empty' function fn_is_fake_mcount() is
added for all of the archs, and the specific archs can override it via chaning
the function pointer: is_fake_mcount in do_file() with the e_machine. e.g. This
patch adds MIPS_is_fake_mcount() to override the default fn_is_fake_mcount()
pointed by is_fake_mcount.

This fn_is_fake_mcount() checks if the _mcount symbol is fake, e.g. the 2nd
_mcount symbol of the long call is fake, for there are 2 _mcount symbols mapped
to one real mcount call, so, one of them is fake and must be filtered.

This fn_is_fake_mcount() is called in sift_rel_mcount() after finding the
_mcount symbols and before adding the _mcount symbol into mrelp, so, it can
prevent the fake mcount symbol going into the last __mcount_loc table.

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
LKML-Reference: <b866f0138224340a132d31861fa3f9300dee30ac.1288176026.git.wuzhangjin@gmail.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

Showing 2 changed files with 57 additions and 4 deletions Side-by-side Diff

scripts/recordmcount.c
... ... @@ -325,8 +325,10 @@
325 325 }
326 326 if (EM_S390 == w2(ehdr->e_machine))
327 327 reltype = R_390_32;
328   - if (EM_MIPS == w2(ehdr->e_machine))
  328 + if (EM_MIPS == w2(ehdr->e_machine)) {
329 329 reltype = R_MIPS_32;
  330 + is_fake_mcount32 = MIPS32_is_fake_mcount;
  331 + }
330 332 do32(ehdr, fname, reltype);
331 333 } break;
332 334 case ELFCLASS64: {
... ... @@ -343,6 +345,7 @@
343 345 reltype = R_MIPS_64;
344 346 Elf64_r_sym = MIPS64_r_sym;
345 347 Elf64_r_info = MIPS64_r_info;
  348 + is_fake_mcount64 = MIPS64_is_fake_mcount;
346 349 }
347 350 do64(ghdr, fname, reltype);
348 351 } break;
scripts/recordmcount.h
... ... @@ -19,12 +19,16 @@
19 19 * Licensed under the GNU General Public License, version 2 (GPLv2).
20 20 */
21 21 #undef append_func
  22 +#undef is_fake_mcount
  23 +#undef fn_is_fake_mcount
  24 +#undef MIPS_is_fake_mcount
22 25 #undef sift_rel_mcount
23 26 #undef find_secsym_ndx
24 27 #undef __has_rel_mcount
25 28 #undef has_rel_mcount
26 29 #undef tot_relsize
27 30 #undef do_func
  31 +#undef Elf_Addr
28 32 #undef Elf_Ehdr
29 33 #undef Elf_Shdr
30 34 #undef Elf_Rel
... ... @@ -50,6 +54,10 @@
50 54 # define has_rel_mcount has64_rel_mcount
51 55 # define tot_relsize tot64_relsize
52 56 # define do_func do64
  57 +# define is_fake_mcount is_fake_mcount64
  58 +# define fn_is_fake_mcount fn_is_fake_mcount64
  59 +# define MIPS_is_fake_mcount MIPS64_is_fake_mcount
  60 +# define Elf_Addr Elf64_Addr
53 61 # define Elf_Ehdr Elf64_Ehdr
54 62 # define Elf_Shdr Elf64_Shdr
55 63 # define Elf_Rel Elf64_Rel
... ... @@ -74,6 +82,10 @@
74 82 # define has_rel_mcount has32_rel_mcount
75 83 # define tot_relsize tot32_relsize
76 84 # define do_func do32
  85 +# define is_fake_mcount is_fake_mcount32
  86 +# define fn_is_fake_mcount fn_is_fake_mcount32
  87 +# define MIPS_is_fake_mcount MIPS32_is_fake_mcount
  88 +# define Elf_Addr Elf32_Addr
77 89 # define Elf_Ehdr Elf32_Ehdr
78 90 # define Elf_Shdr Elf32_Shdr
79 91 # define Elf_Rel Elf32_Rel
... ... @@ -92,7 +104,13 @@
92 104 # define _size 4
93 105 #endif
94 106  
95   -/* Functions and pointers that 64-bit EM_MIPS can override. */
  107 +/* Functions and pointers that do_file() may override for specific e_machine. */
  108 +static int fn_is_fake_mcount(Elf_Rel const *rp)
  109 +{
  110 + return 0;
  111 +}
  112 +static int (*is_fake_mcount)(Elf_Rel const *rp) = fn_is_fake_mcount;
  113 +
96 114 static uint_t fn_ELF_R_SYM(Elf_Rel const *rp)
97 115 {
98 116 return ELF_R_SYM(_w(rp->r_info));
99 117  
... ... @@ -105,7 +123,40 @@
105 123 }
106 124 static void (*Elf_r_info)(Elf_Rel *const rp, unsigned sym, unsigned type) = fn_ELF_R_INFO;
107 125  
  126 +/*
  127 + * MIPS mcount long call has 2 _mcount symbols, only the position of the 1st
  128 + * _mcount symbol is needed for dynamic function tracer, with it, to disable
  129 + * tracing(ftrace_make_nop), the instruction in the position is replaced with
  130 + * the "b label" instruction, to enable tracing(ftrace_make_call), replace the
  131 + * instruction back. So, here, we set the 2nd one as fake and filter it.
  132 + *
  133 + * c: 3c030000 lui v1,0x0 <--> b label
  134 + * c: R_MIPS_HI16 _mcount
  135 + * c: R_MIPS_NONE *ABS*
  136 + * c: R_MIPS_NONE *ABS*
  137 + * 10: 64630000 daddiu v1,v1,0
  138 + * 10: R_MIPS_LO16 _mcount
  139 + * 10: R_MIPS_NONE *ABS*
  140 + * 10: R_MIPS_NONE *ABS*
  141 + * 14: 03e0082d move at,ra
  142 + * 18: 0060f809 jalr v1
  143 + * label:
  144 + */
  145 +#define MIPS_FAKEMCOUNT_OFFSET 4
108 146  
  147 +static int MIPS_is_fake_mcount(Elf_Rel const *rp)
  148 +{
  149 + static Elf_Addr old_r_offset;
  150 + Elf_Addr current_r_offset = _w(rp->r_offset);
  151 + int is_fake;
  152 +
  153 + is_fake = old_r_offset &&
  154 + (current_r_offset - old_r_offset == MIPS_FAKEMCOUNT_OFFSET);
  155 + old_r_offset = current_r_offset;
  156 +
  157 + return is_fake;
  158 +}
  159 +
109 160 /* Append the new shstrtab, Elf_Shdr[], __mcount_loc and its relocations. */
110 161 static void append_func(Elf_Ehdr *const ehdr,
111 162 Elf_Shdr *const shstr,
... ... @@ -183,7 +234,6 @@
183 234 uwrite(fd_map, ehdr, sizeof(*ehdr));
184 235 }
185 236  
186   -
187 237 /*
188 238 * Look at the relocations in order to find the calls to mcount.
189 239 * Accumulate the section offsets that are found, and their relocation info,
... ... @@ -233,7 +283,7 @@
233 283 mcountsym = Elf_r_sym(relp);
234 284 }
235 285  
236   - if (mcountsym == Elf_r_sym(relp)) {
  286 + if (mcountsym == Elf_r_sym(relp) && !is_fake_mcount(relp)) {
237 287 uint_t const addend = _w(_w(relp->r_offset) - recval);
238 288  
239 289 mrelp->r_offset = _w(offbase