aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2008-08-14 15:45:07 -0400
committerIngo Molnar <mingo@elte.hu>2008-10-14 04:34:40 -0400
commit8da3821ba5634497da63d58a69e24a97697c4a2b (patch)
tree47d4d097b1cd2aafae2e3596a9ae0d1293228782
parent8b1fa1d7b22f386747c7b78b918d4c680c16066f (diff)
ftrace: create __mcount_loc section
This patch creates a section in the kernel called "__mcount_loc". This will hold a list of pointers to the mcount relocation for each call site of mcount. For example: objdump -dr init/main.o [...] Disassembly of section .text: 0000000000000000 <do_one_initcall>: 0: 55 push %rbp [...] 000000000000017b <init_post>: 17b: 55 push %rbp 17c: 48 89 e5 mov %rsp,%rbp 17f: 53 push %rbx 180: 48 83 ec 08 sub $0x8,%rsp 184: e8 00 00 00 00 callq 189 <init_post+0xe> 185: R_X86_64_PC32 mcount+0xfffffffffffffffc [...] We will add a section to point to each function call. .section __mcount_loc,"a",@progbits [...] .quad .text + 0x185 [...] The offset to of the mcount call site in init_post is an offset from the start of the section, and not the start of the function init_post. The mcount relocation is at the call site 0x185 from the start of the .text section. .text + 0x185 == init_post + 0xa We need a way to add this __mcount_loc section in a way that we do not lose the relocations after final link. The .text section here will be attached to all other .text sections after final link and the offsets will be meaningless. We need to keep track of where these .text sections are. To do this, we use the start of the first function in the section. do_one_initcall. We can make a tmp.s file with this function as a reference to the start of the .text section. .section __mcount_loc,"a",@progbits [...] .quad do_one_initcall + 0x185 [...] Then we can compile the tmp.s into a tmp.o gcc -c tmp.s -o tmp.o And link it into back into main.o. ld -r main.o tmp.o -o tmp_main.o mv tmp_main.o main.o But we have a problem. What happens if the first function in a section is not exported, and is a static function. The linker will not let the tmp.o use it. This case exists in main.o as well. Disassembly of section .init.text: 0000000000000000 <set_reset_devices>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: e8 00 00 00 00 callq 9 <set_reset_devices+0x9> 5: R_X86_64_PC32 mcount+0xfffffffffffffffc The first function in .init.text is a static function. 00000000000000a8 t __setup_set_reset_devices 000000000000105f t __setup_str_set_reset_devices 0000000000000000 t set_reset_devices The lowercase 't' means that set_reset_devices is local and is not exported. If we simply try to link the tmp.o with the set_reset_devices we end up with two symbols: one local and one global. .section __mcount_loc,"a",@progbits .quad set_reset_devices + 0x10 00000000000000a8 t __setup_set_reset_devices 000000000000105f t __setup_str_set_reset_devices 0000000000000000 t set_reset_devices U set_reset_devices We still have an undefined reference to set_reset_devices, and if we try to compile the kernel, we will end up with an undefined reference to set_reset_devices, or even worst, it could be exported someplace else, and then we will have a reference to the wrong location. To handle this case, we make an intermediate step using objcopy. We convert set_reset_devices into a global exported symbol before linking it with tmp.o and set it back afterwards. 00000000000000a8 t __setup_set_reset_devices 000000000000105f t __setup_str_set_reset_devices 0000000000000000 T set_reset_devices 00000000000000a8 t __setup_set_reset_devices 000000000000105f t __setup_str_set_reset_devices 0000000000000000 T set_reset_devices 00000000000000a8 t __setup_set_reset_devices 000000000000105f t __setup_str_set_reset_devices 0000000000000000 t set_reset_devices Now we have a section in main.o called __mcount_loc that we can place somewhere in the kernel using vmlinux.ld.S and access it to convert all these locations that call mcount into nops before starting SMP and thus, eliminating the need to do this with kstop_machine. Note, A well documented perl script (scripts/recordmcount.pl) is used to do all this in one location. Signed-off-by: Steven Rostedt <srostedt@redhat.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--include/asm-generic/vmlinux.lds.h8
-rw-r--r--kernel/trace/Kconfig8
-rw-r--r--scripts/Makefile.build6
-rwxr-xr-xscripts/recordmcount.pl280
4 files changed, 302 insertions, 0 deletions
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 3d8e472a09c8..838d9b2a0da1 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -37,6 +37,13 @@
37#define MEM_DISCARD(sec) *(.mem##sec) 37#define MEM_DISCARD(sec) *(.mem##sec)
38#endif 38#endif
39 39
40#ifdef CONFIG_FTRACE_MCOUNT_RECORD
41#define MCOUNT_REC() VMLINUX_SYMBOL(__start_mcount_loc) = .; \
42 *(__mcount_loc) \
43 VMLINUX_SYMBOL(__stop_mcount_loc) = .;
44#else
45#define MCOUNT_REC()
46#endif
40 47
41/* .data section */ 48/* .data section */
42#define DATA_DATA \ 49#define DATA_DATA \
@@ -192,6 +199,7 @@
192 /* __*init sections */ \ 199 /* __*init sections */ \
193 __init_rodata : AT(ADDR(__init_rodata) - LOAD_OFFSET) { \ 200 __init_rodata : AT(ADDR(__init_rodata) - LOAD_OFFSET) { \
194 *(.ref.rodata) \ 201 *(.ref.rodata) \
202 MCOUNT_REC() \
195 DEV_KEEP(init.rodata) \ 203 DEV_KEEP(init.rodata) \
196 DEV_KEEP(exit.rodata) \ 204 DEV_KEEP(exit.rodata) \
197 CPU_KEEP(init.rodata) \ 205 CPU_KEEP(init.rodata) \
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index cae2637d5e68..14d9505178ca 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -7,6 +7,9 @@ config HAVE_FTRACE
7config HAVE_DYNAMIC_FTRACE 7config HAVE_DYNAMIC_FTRACE
8 bool 8 bool
9 9
10config HAVE_FTRACE_MCOUNT_RECORD
11 bool
12
10config TRACER_MAX_TRACE 13config TRACER_MAX_TRACE
11 bool 14 bool
12 15
@@ -122,6 +125,11 @@ config DYNAMIC_FTRACE
122 were made. If so, it runs stop_machine (stops all CPUS) 125 were made. If so, it runs stop_machine (stops all CPUS)
123 and modifies the code to jump over the call to ftrace. 126 and modifies the code to jump over the call to ftrace.
124 127
128config FTRACE_MCOUNT_RECORD
129 def_bool y
130 depends on DYNAMIC_FTRACE
131 depends on HAVE_FTRACE_MCOUNT_RECORD
132
125config FTRACE_SELFTEST 133config FTRACE_SELFTEST
126 bool 134 bool
127 135
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 277cfe0b7100..463ddcc583ed 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -198,10 +198,16 @@ cmd_modversions = \
198 fi; 198 fi;
199endif 199endif
200 200
201ifdef CONFIG_FTRACE_MCOUNT_RECORD
202cmd_record_mcount = scripts/recordmcount.pl "$(ARCH)" \
203 "$(OBJDUMP)" "$(OBJCOPY)" "$(CC)" "$(LD)" "$(NM)" "$(RM)" "$(MV)" "$(@)";
204endif
205
201define rule_cc_o_c 206define rule_cc_o_c
202 $(call echo-cmd,checksrc) $(cmd_checksrc) \ 207 $(call echo-cmd,checksrc) $(cmd_checksrc) \
203 $(call echo-cmd,cc_o_c) $(cmd_cc_o_c); \ 208 $(call echo-cmd,cc_o_c) $(cmd_cc_o_c); \
204 $(cmd_modversions) \ 209 $(cmd_modversions) \
210 $(cmd_record_mcount) \
205 scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > \ 211 scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > \
206 $(dot-target).tmp; \ 212 $(dot-target).tmp; \
207 rm -f $(depfile); \ 213 rm -f $(depfile); \
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
new file mode 100755
index 000000000000..44b4b23e91b2
--- /dev/null
+++ b/scripts/recordmcount.pl
@@ -0,0 +1,280 @@
1#!/usr/bin/perl -w
2# (c) 2008, Steven Rostedt <srostedt@redhat.com>
3# Licensed under the terms of the GNU GPL License version 2
4#
5# recordmcount.pl - makes a section called __mcount_loc that holds
6# all the offsets to the calls to mcount.
7#
8#
9# What we want to end up with is a section in vmlinux called
10# __mcount_loc that contains a list of pointers to all the
11# call sites in the kernel that call mcount. Later on boot up, the kernel
12# will read this list, save the locations and turn them into nops.
13# When tracing or profiling is later enabled, these locations will then
14# be converted back to pointers to some function.
15#
16# This is no easy feat. This script is called just after the original
17# object is compiled and before it is linked.
18#
19# The references to the call sites are offsets from the section of text
20# that the call site is in. Hence, all functions in a section that
21# has a call site to mcount, will have the offset from the beginning of
22# the section and not the beginning of the function.
23#
24# The trick is to find a way to record the beginning of the section.
25# The way we do this is to look at the first function in the section
26# which will also be the location of that section after final link.
27# e.g.
28#
29# .section ".text.sched"
30# .globl my_func
31# my_func:
32# [...]
33# call mcount (offset: 0x5)
34# [...]
35# ret
36# other_func:
37# [...]
38# call mcount (offset: 0x1b)
39# [...]
40#
41# Both relocation offsets for the mcounts in the above example will be
42# offset from .text.sched. If we make another file called tmp.s with:
43#
44# .section __mcount_loc
45# .quad my_func + 0x5
46# .quad my_func + 0x1b
47#
48# We can then compile this tmp.s into tmp.o, and link it to the original
49# object.
50#
51# But this gets hard if my_func is not globl (a static function).
52# In such a case we have:
53#
54# .section ".text.sched"
55# my_func:
56# [...]
57# call mcount (offset: 0x5)
58# [...]
59# ret
60# .globl my_func
61# other_func:
62# [...]
63# call mcount (offset: 0x1b)
64# [...]
65#
66# If we make the tmp.s the same as above, when we link together with
67# the original object, we will end up with two symbols for my_func:
68# one local, one global. After final compile, we will end up with
69# an undefined reference to my_func.
70#
71# Since local objects can reference local variables, we need to find
72# a way to make tmp.o reference the local objects of the original object
73# file after it is linked together. To do this, we convert the my_func
74# into a global symbol before linking tmp.o. Then after we link tmp.o
75# we will only have a single symbol for my_func that is global.
76# We can convert my_func back into a local symbol and we are done.
77#
78# Here are the steps we take:
79#
80# 1) Record all the local symbols by using 'nm'
81# 2) Use objdump to find all the call site offsets and sections for
82# mcount.
83# 3) Compile the list into its own object.
84# 4) Do we have to deal with local functions? If not, go to step 8.
85# 5) Make an object that converts these local functions to global symbols
86# with objcopy.
87# 6) Link together this new object with the list object.
88# 7) Convert the local functions back to local symbols and rename
89# the result as the original object.
90# End.
91# 8) Link the object with the list object.
92# 9) Move the result back to the original object.
93# End.
94#
95
96use strict;
97
98my $P = $0;
99$P =~ s@.*/@@g;
100
101my $V = '0.1';
102
103if ($#ARGV < 6) {
104 print "usage: $P arch objdump objcopy cc ld nm rm mv inputfile\n";
105 print "version: $V\n";
106 exit(1);
107}
108
109my ($arch, $objdump, $objcopy, $cc, $ld, $nm, $rm, $mv, $inputfile) = @ARGV;
110
111$objdump = "objdump" if ((length $objdump) == 0);
112$objcopy = "objcopy" if ((length $objcopy) == 0);
113$cc = "gcc" if ((length $cc) == 0);
114$ld = "ld" if ((length $ld) == 0);
115$nm = "nm" if ((length $nm) == 0);
116$rm = "rm" if ((length $rm) == 0);
117$mv = "mv" if ((length $mv) == 0);
118
119#print STDERR "running: $P '$arch' '$objdump' '$objcopy' '$cc' '$ld' " .
120# "'$nm' '$rm' '$mv' '$inputfile'\n";
121
122my %locals;
123my %convert;
124
125my $type;
126my $section_regex; # Find the start of a section
127my $function_regex; # Find the name of a function (return func name)
128my $mcount_regex; # Find the call site to mcount (return offset)
129
130if ($arch eq "x86_64") {
131 $section_regex = "Disassembly of section";
132 $function_regex = "<(.*?)>:";
133 $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount([+-]0x[0-9a-zA-Z]+)?\$";
134 $type = ".quad";
135} elsif ($arch eq "i386") {
136 $section_regex = "Disassembly of section";
137 $function_regex = "<(.*?)>:";
138 $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount\$";
139 $type = ".long";
140} else {
141 die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD";
142}
143
144my $text_found = 0;
145my $read_function = 0;
146my $opened = 0;
147my $text = "";
148my $mcount_section = "__mcount_loc";
149
150my $dirname;
151my $filename;
152my $prefix;
153my $ext;
154
155if ($inputfile =~ m,^(.*)/([^/]*)$,) {
156 $dirname = $1;
157 $filename = $2;
158} else {
159 $dirname = ".";
160 $filename = $inputfile;
161}
162
163if ($filename =~ m,^(.*)(\.\S),) {
164 $prefix = $1;
165 $ext = $2;
166} else {
167 $prefix = $filename;
168 $ext = "";
169}
170
171my $mcount_s = $dirname . "/.tmp_mc_" . $prefix . ".s";
172my $mcount_o = $dirname . "/.tmp_mc_" . $prefix . ".o";
173
174#
175# Step 1: find all the local symbols (static functions).
176#
177open (IN, "$nm $inputfile|") || die "error running $nm";
178while (<IN>) {
179 if (/^[0-9a-fA-F]+\s+t\s+(\S+)/) {
180 $locals{$1} = 1;
181 }
182}
183close(IN);
184
185#
186# Step 2: find the sections and mcount call sites
187#
188open(IN, "$objdump -dr $inputfile|") || die "error running $objdump";
189
190while (<IN>) {
191 # is it a section?
192 if (/$section_regex/) {
193 $read_function = 1;
194 $text_found = 0;
195 # section found, now is this a start of a function?
196 } elsif ($read_function && /$function_regex/) {
197 $read_function = 0;
198 $text_found = 1;
199 $text = $1;
200 # is this function static? If so, note this fact.
201 if (defined $locals{$text}) {
202 $convert{$text} = 1;
203 }
204 # is this a call site to mcount? If so, print the offset from the section
205 } elsif ($text_found && /$mcount_regex/) {
206 if (!$opened) {
207 open(FILE, ">$mcount_s") || die "can't create $mcount_s\n";
208 $opened = 1;
209 print FILE "\t.section $mcount_section,\"a\",\@progbits\n";
210 }
211 print FILE "\t$type $text + 0x$1\n";
212 }
213}
214
215# If we did not find any mcount callers, we are done (do nothing).
216if (!$opened) {
217 exit(0);
218}
219
220close(FILE);
221
222#
223# Step 3: Compile the file that holds the list of call sites to mcount.
224#
225`$cc -o $mcount_o -c $mcount_s`;
226
227my @converts = keys %convert;
228
229#
230# Step 4: Do we have sections that started with local functions?
231#
232if ($#converts >= 0) {
233 my $globallist = "";
234 my $locallist = "";
235
236 foreach my $con (@converts) {
237 $globallist .= " --globalize-symbol $con";
238 $locallist .= " --localize-symbol $con";
239 }
240
241 my $globalobj = $dirname . "/.tmp_gl_" . $filename;
242 my $globalmix = $dirname . "/.tmp_mx_" . $filename;
243
244 #
245 # Step 5: set up each local function as a global
246 #
247 `$objcopy $globallist $inputfile $globalobj`;
248
249 #
250 # Step 6: Link the global version to our list.
251 #
252 `$ld -r $globalobj $mcount_o -o $globalmix`;
253
254 #
255 # Step 7: Convert the local functions back into local symbols
256 #
257 `$objcopy $locallist $globalmix $inputfile`;
258
259 # Remove the temp files
260 `$rm $globalobj $globalmix`;
261
262} else {
263
264 my $mix = $dirname . "/.tmp_mx_" . $filename;
265
266 #
267 # Step 8: Link the object with our list of call sites object.
268 #
269 `$ld -r $inputfile $mcount_o -o $mix`;
270
271 #
272 # Step 9: Move the result back to the original object.
273 #
274 `$mv $mix $inputfile`;
275}
276
277# Clean up the temp files
278`$rm $mcount_o $mcount_s`;
279
280exit(0);