aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWu Zhangjin <wuzhangjin@gmail.com>2010-10-27 06:59:08 -0400
committerRalf Baechle <ralf@linux-mips.org>2010-10-29 14:08:55 -0400
commit412910cd046c1f14f0fba9c0aec401d47e57dcd1 (patch)
treeb06d9ab4f42224cca24a9e323de507cd262e8be1
parenta2d49358ba9bc93204dc001d5568c5bdb299b77d (diff)
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>
-rw-r--r--scripts/recordmcount.c5
-rw-r--r--scripts/recordmcount.h56
2 files changed, 57 insertions, 4 deletions
diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c
index 2d32b9ced20a..f2f32eee2c5b 100644
--- a/scripts/recordmcount.c
+++ b/scripts/recordmcount.c
@@ -325,8 +325,10 @@ do_file(char const *const fname)
325 } 325 }
326 if (EM_S390 == w2(ehdr->e_machine)) 326 if (EM_S390 == w2(ehdr->e_machine))
327 reltype = R_390_32; 327 reltype = R_390_32;
328 if (EM_MIPS == w2(ehdr->e_machine)) 328 if (EM_MIPS == w2(ehdr->e_machine)) {
329 reltype = R_MIPS_32; 329 reltype = R_MIPS_32;
330 is_fake_mcount32 = MIPS32_is_fake_mcount;
331 }
330 do32(ehdr, fname, reltype); 332 do32(ehdr, fname, reltype);
331 } break; 333 } break;
332 case ELFCLASS64: { 334 case ELFCLASS64: {
@@ -343,6 +345,7 @@ do_file(char const *const fname)
343 reltype = R_MIPS_64; 345 reltype = R_MIPS_64;
344 Elf64_r_sym = MIPS64_r_sym; 346 Elf64_r_sym = MIPS64_r_sym;
345 Elf64_r_info = MIPS64_r_info; 347 Elf64_r_info = MIPS64_r_info;
348 is_fake_mcount64 = MIPS64_is_fake_mcount;
346 } 349 }
347 do64(ghdr, fname, reltype); 350 do64(ghdr, fname, reltype);
348 } break; 351 } break;
diff --git a/scripts/recordmcount.h b/scripts/recordmcount.h
index 190fd18dc853..58e933a20544 100644
--- a/scripts/recordmcount.h
+++ b/scripts/recordmcount.h
@@ -19,12 +19,16 @@
19 * Licensed under the GNU General Public License, version 2 (GPLv2). 19 * Licensed under the GNU General Public License, version 2 (GPLv2).
20 */ 20 */
21#undef append_func 21#undef append_func
22#undef is_fake_mcount
23#undef fn_is_fake_mcount
24#undef MIPS_is_fake_mcount
22#undef sift_rel_mcount 25#undef sift_rel_mcount
23#undef find_secsym_ndx 26#undef find_secsym_ndx
24#undef __has_rel_mcount 27#undef __has_rel_mcount
25#undef has_rel_mcount 28#undef has_rel_mcount
26#undef tot_relsize 29#undef tot_relsize
27#undef do_func 30#undef do_func
31#undef Elf_Addr
28#undef Elf_Ehdr 32#undef Elf_Ehdr
29#undef Elf_Shdr 33#undef Elf_Shdr
30#undef Elf_Rel 34#undef Elf_Rel
@@ -50,6 +54,10 @@
50# define has_rel_mcount has64_rel_mcount 54# define has_rel_mcount has64_rel_mcount
51# define tot_relsize tot64_relsize 55# define tot_relsize tot64_relsize
52# define do_func do64 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# define Elf_Ehdr Elf64_Ehdr 61# define Elf_Ehdr Elf64_Ehdr
54# define Elf_Shdr Elf64_Shdr 62# define Elf_Shdr Elf64_Shdr
55# define Elf_Rel Elf64_Rel 63# define Elf_Rel Elf64_Rel
@@ -74,6 +82,10 @@
74# define has_rel_mcount has32_rel_mcount 82# define has_rel_mcount has32_rel_mcount
75# define tot_relsize tot32_relsize 83# define tot_relsize tot32_relsize
76# define do_func do32 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# define Elf_Ehdr Elf32_Ehdr 89# define Elf_Ehdr Elf32_Ehdr
78# define Elf_Shdr Elf32_Shdr 90# define Elf_Shdr Elf32_Shdr
79# define Elf_Rel Elf32_Rel 91# define Elf_Rel Elf32_Rel
@@ -92,7 +104,13 @@
92# define _size 4 104# define _size 4
93#endif 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. */
108static int fn_is_fake_mcount(Elf_Rel const *rp)
109{
110 return 0;
111}
112static int (*is_fake_mcount)(Elf_Rel const *rp) = fn_is_fake_mcount;
113
96static uint_t fn_ELF_R_SYM(Elf_Rel const *rp) 114static uint_t fn_ELF_R_SYM(Elf_Rel const *rp)
97{ 115{
98 return ELF_R_SYM(_w(rp->r_info)); 116 return ELF_R_SYM(_w(rp->r_info));
@@ -105,6 +123,39 @@ static void fn_ELF_R_INFO(Elf_Rel *const rp, unsigned sym, unsigned type)
105} 123}
106static void (*Elf_r_info)(Elf_Rel *const rp, unsigned sym, unsigned type) = fn_ELF_R_INFO; 124static 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
146
147static 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}
108 159
109/* Append the new shstrtab, Elf_Shdr[], __mcount_loc and its relocations. */ 160/* Append the new shstrtab, Elf_Shdr[], __mcount_loc and its relocations. */
110static void append_func(Elf_Ehdr *const ehdr, 161static void append_func(Elf_Ehdr *const ehdr,
@@ -183,7 +234,6 @@ static void append_func(Elf_Ehdr *const ehdr,
183 uwrite(fd_map, ehdr, sizeof(*ehdr)); 234 uwrite(fd_map, ehdr, sizeof(*ehdr));
184} 235}
185 236
186
187/* 237/*
188 * Look at the relocations in order to find the calls to mcount. 238 * Look at the relocations in order to find the calls to mcount.
189 * Accumulate the section offsets that are found, and their relocation info, 239 * Accumulate the section offsets that are found, and their relocation info,
@@ -233,7 +283,7 @@ static uint_t *sift_rel_mcount(uint_t *mlocp,
233 mcountsym = Elf_r_sym(relp); 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 uint_t const addend = _w(_w(relp->r_offset) - recval); 287 uint_t const addend = _w(_w(relp->r_offset) - recval);
238 288
239 mrelp->r_offset = _w(offbase 289 mrelp->r_offset = _w(offbase