162306a36Sopenharmony_ci#!/usr/bin/env perl 262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0-only 362306a36Sopenharmony_ci# (c) 2008, Steven Rostedt <srostedt@redhat.com> 462306a36Sopenharmony_ci# 562306a36Sopenharmony_ci# recordmcount.pl - makes a section called __mcount_loc that holds 662306a36Sopenharmony_ci# all the offsets to the calls to mcount. 762306a36Sopenharmony_ci# 862306a36Sopenharmony_ci# 962306a36Sopenharmony_ci# What we want to end up with this is that each object file will have a 1062306a36Sopenharmony_ci# section called __mcount_loc that will hold the list of pointers to mcount 1162306a36Sopenharmony_ci# callers. After final linking, the vmlinux will have within .init.data the 1262306a36Sopenharmony_ci# list of all callers to mcount between __start_mcount_loc and __stop_mcount_loc. 1362306a36Sopenharmony_ci# Later on boot up, the kernel will read this list, save the locations and turn 1462306a36Sopenharmony_ci# them into nops. When tracing or profiling is later enabled, these locations 1562306a36Sopenharmony_ci# will then be converted back to pointers to some function. 1662306a36Sopenharmony_ci# 1762306a36Sopenharmony_ci# This is no easy feat. This script is called just after the original 1862306a36Sopenharmony_ci# object is compiled and before it is linked. 1962306a36Sopenharmony_ci# 2062306a36Sopenharmony_ci# When parse this object file using 'objdump', the references to the call 2162306a36Sopenharmony_ci# sites are offsets from the section that the call site is in. Hence, all 2262306a36Sopenharmony_ci# functions in a section that has a call site to mcount, will have the 2362306a36Sopenharmony_ci# offset from the beginning of the section and not the beginning of the 2462306a36Sopenharmony_ci# function. 2562306a36Sopenharmony_ci# 2662306a36Sopenharmony_ci# But where this section will reside finally in vmlinx is undetermined at 2762306a36Sopenharmony_ci# this point. So we can't use this kind of offsets to record the final 2862306a36Sopenharmony_ci# address of this call site. 2962306a36Sopenharmony_ci# 3062306a36Sopenharmony_ci# The trick is to change the call offset referring the start of a section to 3162306a36Sopenharmony_ci# referring a function symbol in this section. During the link step, 'ld' will 3262306a36Sopenharmony_ci# compute the final address according to the information we record. 3362306a36Sopenharmony_ci# 3462306a36Sopenharmony_ci# e.g. 3562306a36Sopenharmony_ci# 3662306a36Sopenharmony_ci# .section ".sched.text", "ax" 3762306a36Sopenharmony_ci# [...] 3862306a36Sopenharmony_ci# func1: 3962306a36Sopenharmony_ci# [...] 4062306a36Sopenharmony_ci# call mcount (offset: 0x10) 4162306a36Sopenharmony_ci# [...] 4262306a36Sopenharmony_ci# ret 4362306a36Sopenharmony_ci# .globl fun2 4462306a36Sopenharmony_ci# func2: (offset: 0x20) 4562306a36Sopenharmony_ci# [...] 4662306a36Sopenharmony_ci# [...] 4762306a36Sopenharmony_ci# ret 4862306a36Sopenharmony_ci# func3: 4962306a36Sopenharmony_ci# [...] 5062306a36Sopenharmony_ci# call mcount (offset: 0x30) 5162306a36Sopenharmony_ci# [...] 5262306a36Sopenharmony_ci# 5362306a36Sopenharmony_ci# Both relocation offsets for the mcounts in the above example will be 5462306a36Sopenharmony_ci# offset from .sched.text. If we choose global symbol func2 as a reference and 5562306a36Sopenharmony_ci# make another file called tmp.s with the new offsets: 5662306a36Sopenharmony_ci# 5762306a36Sopenharmony_ci# .section __mcount_loc 5862306a36Sopenharmony_ci# .quad func2 - 0x10 5962306a36Sopenharmony_ci# .quad func2 + 0x10 6062306a36Sopenharmony_ci# 6162306a36Sopenharmony_ci# We can then compile this tmp.s into tmp.o, and link it back to the original 6262306a36Sopenharmony_ci# object. 6362306a36Sopenharmony_ci# 6462306a36Sopenharmony_ci# In our algorithm, we will choose the first global function we meet in this 6562306a36Sopenharmony_ci# section as the reference. But this gets hard if there is no global functions 6662306a36Sopenharmony_ci# in this section. In such a case we have to select a local one. E.g. func1: 6762306a36Sopenharmony_ci# 6862306a36Sopenharmony_ci# .section ".sched.text", "ax" 6962306a36Sopenharmony_ci# func1: 7062306a36Sopenharmony_ci# [...] 7162306a36Sopenharmony_ci# call mcount (offset: 0x10) 7262306a36Sopenharmony_ci# [...] 7362306a36Sopenharmony_ci# ret 7462306a36Sopenharmony_ci# func2: 7562306a36Sopenharmony_ci# [...] 7662306a36Sopenharmony_ci# call mcount (offset: 0x20) 7762306a36Sopenharmony_ci# [...] 7862306a36Sopenharmony_ci# .section "other.section" 7962306a36Sopenharmony_ci# 8062306a36Sopenharmony_ci# If we make the tmp.s the same as above, when we link together with 8162306a36Sopenharmony_ci# the original object, we will end up with two symbols for func1: 8262306a36Sopenharmony_ci# one local, one global. After final compile, we will end up with 8362306a36Sopenharmony_ci# an undefined reference to func1 or a wrong reference to another global 8462306a36Sopenharmony_ci# func1 in other files. 8562306a36Sopenharmony_ci# 8662306a36Sopenharmony_ci# Since local objects can reference local variables, we need to find 8762306a36Sopenharmony_ci# a way to make tmp.o reference the local objects of the original object 8862306a36Sopenharmony_ci# file after it is linked together. To do this, we convert func1 8962306a36Sopenharmony_ci# into a global symbol before linking tmp.o. Then after we link tmp.o 9062306a36Sopenharmony_ci# we will only have a single symbol for func1 that is global. 9162306a36Sopenharmony_ci# We can convert func1 back into a local symbol and we are done. 9262306a36Sopenharmony_ci# 9362306a36Sopenharmony_ci# Here are the steps we take: 9462306a36Sopenharmony_ci# 9562306a36Sopenharmony_ci# 1) Record all the local and weak symbols by using 'nm' 9662306a36Sopenharmony_ci# 2) Use objdump to find all the call site offsets and sections for 9762306a36Sopenharmony_ci# mcount. 9862306a36Sopenharmony_ci# 3) Compile the list into its own object. 9962306a36Sopenharmony_ci# 4) Do we have to deal with local functions? If not, go to step 8. 10062306a36Sopenharmony_ci# 5) Make an object that converts these local functions to global symbols 10162306a36Sopenharmony_ci# with objcopy. 10262306a36Sopenharmony_ci# 6) Link together this new object with the list object. 10362306a36Sopenharmony_ci# 7) Convert the local functions back to local symbols and rename 10462306a36Sopenharmony_ci# the result as the original object. 10562306a36Sopenharmony_ci# 8) Link the object with the list object. 10662306a36Sopenharmony_ci# 9) Move the result back to the original object. 10762306a36Sopenharmony_ci# 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ciuse warnings; 11062306a36Sopenharmony_ciuse strict; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cimy $P = $0; 11362306a36Sopenharmony_ci$P =~ s@.*/@@g; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cimy $V = '0.1'; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciif ($#ARGV != 11) { 11862306a36Sopenharmony_ci print "usage: $P arch endian bits objdump objcopy cc ld nm rm mv is_module inputfile\n"; 11962306a36Sopenharmony_ci print "version: $V\n"; 12062306a36Sopenharmony_ci exit(1); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cimy ($arch, $endian, $bits, $objdump, $objcopy, $cc, 12462306a36Sopenharmony_ci $ld, $nm, $rm, $mv, $is_module, $inputfile) = @ARGV; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci# This file refers to mcount and shouldn't be ftraced, so lets' ignore it 12762306a36Sopenharmony_ciif ($inputfile =~ m,kernel/trace/ftrace\.o$,) { 12862306a36Sopenharmony_ci exit(0); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci# Acceptable sections to record. 13262306a36Sopenharmony_cimy %text_sections = ( 13362306a36Sopenharmony_ci ".text" => 1, 13462306a36Sopenharmony_ci ".init.text" => 1, 13562306a36Sopenharmony_ci ".ref.text" => 1, 13662306a36Sopenharmony_ci ".sched.text" => 1, 13762306a36Sopenharmony_ci ".spinlock.text" => 1, 13862306a36Sopenharmony_ci ".irqentry.text" => 1, 13962306a36Sopenharmony_ci ".softirqentry.text" => 1, 14062306a36Sopenharmony_ci ".kprobes.text" => 1, 14162306a36Sopenharmony_ci ".cpuidle.text" => 1, 14262306a36Sopenharmony_ci ".text.unlikely" => 1, 14362306a36Sopenharmony_ci); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci# Acceptable section-prefixes to record. 14662306a36Sopenharmony_cimy %text_section_prefixes = ( 14762306a36Sopenharmony_ci ".text." => 1, 14862306a36Sopenharmony_ci); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci# Note: we are nice to C-programmers here, thus we skip the '||='-idiom. 15162306a36Sopenharmony_ci$objdump = 'objdump' if (!$objdump); 15262306a36Sopenharmony_ci$objcopy = 'objcopy' if (!$objcopy); 15362306a36Sopenharmony_ci$cc = 'gcc' if (!$cc); 15462306a36Sopenharmony_ci$ld = 'ld' if (!$ld); 15562306a36Sopenharmony_ci$nm = 'nm' if (!$nm); 15662306a36Sopenharmony_ci$rm = 'rm' if (!$rm); 15762306a36Sopenharmony_ci$mv = 'mv' if (!$mv); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#print STDERR "running: $P '$arch' '$objdump' '$objcopy' '$cc' '$ld' " . 16062306a36Sopenharmony_ci# "'$nm' '$rm' '$mv' '$inputfile'\n"; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cimy %locals; # List of local (static) functions 16362306a36Sopenharmony_cimy %weak; # List of weak functions 16462306a36Sopenharmony_cimy %convert; # List of local functions used that needs conversion 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cimy $type; 16762306a36Sopenharmony_cimy $local_regex; # Match a local function (return function) 16862306a36Sopenharmony_cimy $weak_regex; # Match a weak function (return function) 16962306a36Sopenharmony_cimy $section_regex; # Find the start of a section 17062306a36Sopenharmony_cimy $function_regex; # Find the name of a function 17162306a36Sopenharmony_ci # (return offset and func name) 17262306a36Sopenharmony_cimy $mcount_regex; # Find the call site to mcount (return offset) 17362306a36Sopenharmony_cimy $mcount_adjust; # Address adjustment to mcount offset 17462306a36Sopenharmony_cimy $alignment; # The .align value to use for $mcount_section 17562306a36Sopenharmony_cimy $section_type; # Section header plus possible alignment command 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciif ($arch =~ /(x86(_64)?)|(i386)/) { 17862306a36Sopenharmony_ci if ($bits == 64) { 17962306a36Sopenharmony_ci $arch = "x86_64"; 18062306a36Sopenharmony_ci } else { 18162306a36Sopenharmony_ci $arch = "i386"; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci# 18662306a36Sopenharmony_ci# We base the defaults off of i386, the other archs may 18762306a36Sopenharmony_ci# feel free to change them in the below if statements. 18862306a36Sopenharmony_ci# 18962306a36Sopenharmony_ci$local_regex = "^[0-9a-fA-F]+\\s+t\\s+(\\S+)"; 19062306a36Sopenharmony_ci$weak_regex = "^[0-9a-fA-F]+\\s+([wW])\\s+(\\S+)"; 19162306a36Sopenharmony_ci$section_regex = "Disassembly of section\\s+(\\S+):"; 19262306a36Sopenharmony_ci$function_regex = "^([0-9a-fA-F]+)\\s+<([^^]*?)>:"; 19362306a36Sopenharmony_ci$mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s(mcount|__fentry__)\$"; 19462306a36Sopenharmony_ci$section_type = '@progbits'; 19562306a36Sopenharmony_ci$mcount_adjust = 0; 19662306a36Sopenharmony_ci$type = ".long"; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciif ($arch eq "x86_64") { 19962306a36Sopenharmony_ci $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s(mcount|__fentry__)([+-]0x[0-9a-zA-Z]+)?\$"; 20062306a36Sopenharmony_ci $type = ".quad"; 20162306a36Sopenharmony_ci $alignment = 8; 20262306a36Sopenharmony_ci $mcount_adjust = -1; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci # force flags for this arch 20562306a36Sopenharmony_ci $ld .= " -m elf_x86_64"; 20662306a36Sopenharmony_ci $objdump .= " -M x86-64"; 20762306a36Sopenharmony_ci $objcopy .= " -O elf64-x86-64"; 20862306a36Sopenharmony_ci $cc .= " -m64"; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci} elsif ($arch eq "i386") { 21162306a36Sopenharmony_ci $alignment = 4; 21262306a36Sopenharmony_ci $mcount_adjust = -1; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci # force flags for this arch 21562306a36Sopenharmony_ci $ld .= " -m elf_i386"; 21662306a36Sopenharmony_ci $objdump .= " -M i386"; 21762306a36Sopenharmony_ci $objcopy .= " -O elf32-i386"; 21862306a36Sopenharmony_ci $cc .= " -m32"; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci} elsif ($arch eq "s390" && $bits == 64) { 22162306a36Sopenharmony_ci if ($cc =~ /-DCC_USING_HOTPATCH/) { 22262306a36Sopenharmony_ci $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*c0 04 00 00 00 00\\s*(brcl\\s*0,|jgnop\\s*)[0-9a-f]+ <([^\+]*)>\$"; 22362306a36Sopenharmony_ci $mcount_adjust = 0; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci $alignment = 8; 22662306a36Sopenharmony_ci $type = ".quad"; 22762306a36Sopenharmony_ci $ld .= " -m elf64_s390"; 22862306a36Sopenharmony_ci $cc .= " -m64"; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci} elsif ($arch eq "sh") { 23162306a36Sopenharmony_ci $alignment = 2; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci # force flags for this arch 23462306a36Sopenharmony_ci $ld .= " -m shlelf_linux"; 23562306a36Sopenharmony_ci if ($endian eq "big") { 23662306a36Sopenharmony_ci $objcopy .= " -O elf32-shbig-linux"; 23762306a36Sopenharmony_ci } else { 23862306a36Sopenharmony_ci $objcopy .= " -O elf32-sh-linux"; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci} elsif ($arch eq "powerpc") { 24262306a36Sopenharmony_ci my $ldemulation; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci $local_regex = "^[0-9a-fA-F]+\\s+t\\s+(\\.?\\S+)"; 24562306a36Sopenharmony_ci # See comment in the sparc64 section for why we use '\w'. 24662306a36Sopenharmony_ci $function_regex = "^([0-9a-fA-F]+)\\s+<(\\.?\\w*?)>:"; 24762306a36Sopenharmony_ci $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s\\.?_mcount\$"; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if ($endian eq "big") { 25062306a36Sopenharmony_ci $cc .= " -mbig-endian "; 25162306a36Sopenharmony_ci $ld .= " -EB "; 25262306a36Sopenharmony_ci $ldemulation = "ppc" 25362306a36Sopenharmony_ci } else { 25462306a36Sopenharmony_ci $cc .= " -mlittle-endian "; 25562306a36Sopenharmony_ci $ld .= " -EL "; 25662306a36Sopenharmony_ci $ldemulation = "lppc" 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci if ($bits == 64) { 25962306a36Sopenharmony_ci $type = ".quad"; 26062306a36Sopenharmony_ci $cc .= " -m64 "; 26162306a36Sopenharmony_ci $ld .= " -m elf64".$ldemulation." "; 26262306a36Sopenharmony_ci } else { 26362306a36Sopenharmony_ci $cc .= " -m32 "; 26462306a36Sopenharmony_ci $ld .= " -m elf32".$ldemulation." "; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci} elsif ($arch eq "arm") { 26862306a36Sopenharmony_ci $alignment = 2; 26962306a36Sopenharmony_ci $section_type = '%progbits'; 27062306a36Sopenharmony_ci $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_ARM_(CALL|PC24|THM_CALL)" . 27162306a36Sopenharmony_ci "\\s+(__gnu_mcount_nc|mcount)\$"; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci} elsif ($arch eq "arm64") { 27462306a36Sopenharmony_ci $alignment = 3; 27562306a36Sopenharmony_ci $section_type = '%progbits'; 27662306a36Sopenharmony_ci $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_AARCH64_CALL26\\s+_mcount\$"; 27762306a36Sopenharmony_ci $type = ".quad"; 27862306a36Sopenharmony_ci} elsif ($arch eq "ia64") { 27962306a36Sopenharmony_ci $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s_mcount\$"; 28062306a36Sopenharmony_ci $type = "data8"; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if ($is_module eq "0") { 28362306a36Sopenharmony_ci $cc .= " -mconstant-gp"; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci} elsif ($arch eq "sparc64") { 28662306a36Sopenharmony_ci # In the objdump output there are giblets like: 28762306a36Sopenharmony_ci # 0000000000000000 <igmp_net_exit-0x18>: 28862306a36Sopenharmony_ci # As there's some data blobs that get emitted into the 28962306a36Sopenharmony_ci # text section before the first instructions and the first 29062306a36Sopenharmony_ci # real symbols. We don't want to match that, so to combat 29162306a36Sopenharmony_ci # this we use '\w' so we'll match just plain symbol names, 29262306a36Sopenharmony_ci # and not those that also include hex offsets inside of the 29362306a36Sopenharmony_ci # '<>' brackets. Actually the generic function_regex setting 29462306a36Sopenharmony_ci # could safely use this too. 29562306a36Sopenharmony_ci $function_regex = "^([0-9a-fA-F]+)\\s+<(\\w*?)>:"; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci # Sparc64 calls '_mcount' instead of plain 'mcount'. 29862306a36Sopenharmony_ci $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s_mcount\$"; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci $alignment = 8; 30162306a36Sopenharmony_ci $type = ".xword"; 30262306a36Sopenharmony_ci $ld .= " -m elf64_sparc"; 30362306a36Sopenharmony_ci $cc .= " -m64"; 30462306a36Sopenharmony_ci $objcopy .= " -O elf64-sparc"; 30562306a36Sopenharmony_ci} elsif ($arch eq "mips") { 30662306a36Sopenharmony_ci # To enable module support, we need to enable the -mlong-calls option 30762306a36Sopenharmony_ci # of gcc for module, after using this option, we can not get the real 30862306a36Sopenharmony_ci # offset of the calling to _mcount, but the offset of the lui 30962306a36Sopenharmony_ci # instruction or the addiu one. herein, we record the address of the 31062306a36Sopenharmony_ci # first one, and then we can replace this instruction by a branch 31162306a36Sopenharmony_ci # instruction to jump over the profiling function to filter the 31262306a36Sopenharmony_ci # indicated functions, or switch back to the lui instruction to trace 31362306a36Sopenharmony_ci # them, which means dynamic tracing. 31462306a36Sopenharmony_ci # 31562306a36Sopenharmony_ci # c: 3c030000 lui v1,0x0 31662306a36Sopenharmony_ci # c: R_MIPS_HI16 _mcount 31762306a36Sopenharmony_ci # c: R_MIPS_NONE *ABS* 31862306a36Sopenharmony_ci # c: R_MIPS_NONE *ABS* 31962306a36Sopenharmony_ci # 10: 64630000 daddiu v1,v1,0 32062306a36Sopenharmony_ci # 10: R_MIPS_LO16 _mcount 32162306a36Sopenharmony_ci # 10: R_MIPS_NONE *ABS* 32262306a36Sopenharmony_ci # 10: R_MIPS_NONE *ABS* 32362306a36Sopenharmony_ci # 14: 03e0082d move at,ra 32462306a36Sopenharmony_ci # 18: 0060f809 jalr v1 32562306a36Sopenharmony_ci # 32662306a36Sopenharmony_ci # for the kernel: 32762306a36Sopenharmony_ci # 32862306a36Sopenharmony_ci # 10: 03e0082d move at,ra 32962306a36Sopenharmony_ci # 14: 0c000000 jal 0 <loongson_halt> 33062306a36Sopenharmony_ci # 14: R_MIPS_26 _mcount 33162306a36Sopenharmony_ci # 14: R_MIPS_NONE *ABS* 33262306a36Sopenharmony_ci # 14: R_MIPS_NONE *ABS* 33362306a36Sopenharmony_ci # 18: 00020021 nop 33462306a36Sopenharmony_ci if ($is_module eq "0") { 33562306a36Sopenharmony_ci $mcount_regex = "^\\s*([0-9a-fA-F]+): R_MIPS_26\\s+_mcount\$"; 33662306a36Sopenharmony_ci } else { 33762306a36Sopenharmony_ci $mcount_regex = "^\\s*([0-9a-fA-F]+): R_MIPS_HI16\\s+_mcount\$"; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci $objdump .= " -Melf-trad".$endian."mips "; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if ($endian eq "big") { 34262306a36Sopenharmony_ci $endian = " -EB "; 34362306a36Sopenharmony_ci $ld .= " -melf".$bits."btsmip"; 34462306a36Sopenharmony_ci } else { 34562306a36Sopenharmony_ci $endian = " -EL "; 34662306a36Sopenharmony_ci $ld .= " -melf".$bits."ltsmip"; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci $cc .= " -mno-abicalls -fno-pic -mabi=" . $bits . $endian; 35062306a36Sopenharmony_ci $ld .= $endian; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if ($bits == 64) { 35362306a36Sopenharmony_ci $function_regex = 35462306a36Sopenharmony_ci "^([0-9a-fA-F]+)\\s+<(.|[^\$]L.*?|\$[^L].*?|[^\$][^L].*?)>:"; 35562306a36Sopenharmony_ci $type = ".dword"; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci} elsif ($arch eq "microblaze") { 35862306a36Sopenharmony_ci # Microblaze calls '_mcount' instead of plain 'mcount'. 35962306a36Sopenharmony_ci $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s_mcount\$"; 36062306a36Sopenharmony_ci} elsif ($arch eq "riscv") { 36162306a36Sopenharmony_ci $function_regex = "^([0-9a-fA-F]+)\\s+<([^.0-9][0-9a-zA-Z_\\.]+)>:"; 36262306a36Sopenharmony_ci $mcount_regex = "^\\s*([0-9a-fA-F]+):\\sR_RISCV_CALL(_PLT)?\\s_?mcount\$"; 36362306a36Sopenharmony_ci $type = ".quad"; 36462306a36Sopenharmony_ci $alignment = 2; 36562306a36Sopenharmony_ci} elsif ($arch eq "csky") { 36662306a36Sopenharmony_ci $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_CKCORE_PCREL_JSR_IMM26BY2\\s+_mcount\$"; 36762306a36Sopenharmony_ci $alignment = 2; 36862306a36Sopenharmony_ci} else { 36962306a36Sopenharmony_ci die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD"; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cimy $text_found = 0; 37362306a36Sopenharmony_cimy $read_function = 0; 37462306a36Sopenharmony_cimy $opened = 0; 37562306a36Sopenharmony_cimy $mcount_section = "__mcount_loc"; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cimy $dirname; 37862306a36Sopenharmony_cimy $filename; 37962306a36Sopenharmony_cimy $prefix; 38062306a36Sopenharmony_cimy $ext; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ciif ($inputfile =~ m,^(.*)/([^/]*)$,) { 38362306a36Sopenharmony_ci $dirname = $1; 38462306a36Sopenharmony_ci $filename = $2; 38562306a36Sopenharmony_ci} else { 38662306a36Sopenharmony_ci $dirname = "."; 38762306a36Sopenharmony_ci $filename = $inputfile; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ciif ($filename =~ m,^(.*)(\.\S),) { 39162306a36Sopenharmony_ci $prefix = $1; 39262306a36Sopenharmony_ci $ext = $2; 39362306a36Sopenharmony_ci} else { 39462306a36Sopenharmony_ci $prefix = $filename; 39562306a36Sopenharmony_ci $ext = ""; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cimy $mcount_s = $dirname . "/.tmp_mc_" . $prefix . ".s"; 39962306a36Sopenharmony_cimy $mcount_o = $dirname . "/.tmp_mc_" . $prefix . ".o"; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci# 40262306a36Sopenharmony_ci# Step 1: find all the local (static functions) and weak symbols. 40362306a36Sopenharmony_ci# 't' is local, 'w/W' is weak 40462306a36Sopenharmony_ci# 40562306a36Sopenharmony_ciopen (IN, "$nm $inputfile|") || die "error running $nm"; 40662306a36Sopenharmony_ciwhile (<IN>) { 40762306a36Sopenharmony_ci if (/$local_regex/) { 40862306a36Sopenharmony_ci $locals{$1} = 1; 40962306a36Sopenharmony_ci } elsif (/$weak_regex/) { 41062306a36Sopenharmony_ci $weak{$2} = $1; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ciclose(IN); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cimy @offsets; # Array of offsets of mcount callers 41662306a36Sopenharmony_cimy $ref_func; # reference function to use for offsets 41762306a36Sopenharmony_cimy $offset = 0; # offset of ref_func to section beginning 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci## 42062306a36Sopenharmony_ci# update_funcs - print out the current mcount callers 42162306a36Sopenharmony_ci# 42262306a36Sopenharmony_ci# Go through the list of offsets to callers and write them to 42362306a36Sopenharmony_ci# the output file in a format that can be read by an assembler. 42462306a36Sopenharmony_ci# 42562306a36Sopenharmony_cisub update_funcs 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci return unless ($ref_func and @offsets); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci # Sanity check on weak function. A weak function may be overwritten by 43062306a36Sopenharmony_ci # another function of the same name, making all these offsets incorrect. 43162306a36Sopenharmony_ci if (defined $weak{$ref_func}) { 43262306a36Sopenharmony_ci die "$inputfile: ERROR: referencing weak function" . 43362306a36Sopenharmony_ci " $ref_func for mcount\n"; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci # is this function static? If so, note this fact. 43762306a36Sopenharmony_ci if (defined $locals{$ref_func}) { 43862306a36Sopenharmony_ci $convert{$ref_func} = 1; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci # Loop through all the mcount caller offsets and print a reference 44262306a36Sopenharmony_ci # to the caller based from the ref_func. 44362306a36Sopenharmony_ci if (!$opened) { 44462306a36Sopenharmony_ci open(FILE, ">$mcount_s") || die "can't create $mcount_s\n"; 44562306a36Sopenharmony_ci $opened = 1; 44662306a36Sopenharmony_ci print FILE "\t.section $mcount_section,\"a\",$section_type\n"; 44762306a36Sopenharmony_ci print FILE "\t.align $alignment\n" if (defined($alignment)); 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci foreach my $cur_offset (@offsets) { 45062306a36Sopenharmony_ci printf FILE "\t%s %s + %d\n", $type, $ref_func, $cur_offset - $offset; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci# 45562306a36Sopenharmony_ci# Step 2: find the sections and mcount call sites 45662306a36Sopenharmony_ci# 45762306a36Sopenharmony_ciopen(IN, "LC_ALL=C $objdump -hdr $inputfile|") || die "error running $objdump"; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cimy $text; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci# read headers first 46362306a36Sopenharmony_cimy $read_headers = 1; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ciwhile (<IN>) { 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if ($read_headers && /$mcount_section/) { 46862306a36Sopenharmony_ci # 46962306a36Sopenharmony_ci # Somehow the make process can execute this script on an 47062306a36Sopenharmony_ci # object twice. If it does, we would duplicate the mcount 47162306a36Sopenharmony_ci # section and it will cause the function tracer self test 47262306a36Sopenharmony_ci # to fail. Check if the mcount section exists, and if it does, 47362306a36Sopenharmony_ci # warn and exit. 47462306a36Sopenharmony_ci # 47562306a36Sopenharmony_ci print STDERR "ERROR: $mcount_section already in $inputfile\n" . 47662306a36Sopenharmony_ci "\tThis may be an indication that your build is corrupted.\n" . 47762306a36Sopenharmony_ci "\tDelete $inputfile and try again. If the same object file\n" . 47862306a36Sopenharmony_ci "\tstill causes an issue, then disable CONFIG_DYNAMIC_FTRACE.\n"; 47962306a36Sopenharmony_ci exit(-1); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci # is it a section? 48362306a36Sopenharmony_ci if (/$section_regex/) { 48462306a36Sopenharmony_ci $read_headers = 0; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci # Only record text sections that we know are safe 48762306a36Sopenharmony_ci $read_function = defined($text_sections{$1}); 48862306a36Sopenharmony_ci if (!$read_function) { 48962306a36Sopenharmony_ci foreach my $prefix (keys %text_section_prefixes) { 49062306a36Sopenharmony_ci if (substr($1, 0, length $prefix) eq $prefix) { 49162306a36Sopenharmony_ci $read_function = 1; 49262306a36Sopenharmony_ci last; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci # print out any recorded offsets 49762306a36Sopenharmony_ci update_funcs(); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci # reset all markers and arrays 50062306a36Sopenharmony_ci $text_found = 0; 50162306a36Sopenharmony_ci undef($ref_func); 50262306a36Sopenharmony_ci undef(@offsets); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci # section found, now is this a start of a function? 50562306a36Sopenharmony_ci } elsif ($read_function && /$function_regex/) { 50662306a36Sopenharmony_ci $text_found = 1; 50762306a36Sopenharmony_ci $text = $2; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci # if this is either a local function or a weak function 51062306a36Sopenharmony_ci # keep looking for functions that are global that 51162306a36Sopenharmony_ci # we can use safely. 51262306a36Sopenharmony_ci if (!defined($locals{$text}) && !defined($weak{$text})) { 51362306a36Sopenharmony_ci $ref_func = $text; 51462306a36Sopenharmony_ci $read_function = 0; 51562306a36Sopenharmony_ci $offset = hex $1; 51662306a36Sopenharmony_ci } else { 51762306a36Sopenharmony_ci # if we already have a function, and this is weak, skip it 51862306a36Sopenharmony_ci if (!defined($ref_func) && !defined($weak{$text}) && 51962306a36Sopenharmony_ci # PPC64 can have symbols that start with .L and 52062306a36Sopenharmony_ci # gcc considers these special. Don't use them! 52162306a36Sopenharmony_ci $text !~ /^\.L/) { 52262306a36Sopenharmony_ci $ref_func = $text; 52362306a36Sopenharmony_ci $offset = hex $1; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci # is this a call site to mcount? If so, record it to print later 52862306a36Sopenharmony_ci if ($text_found && /$mcount_regex/) { 52962306a36Sopenharmony_ci push(@offsets, (hex $1) + $mcount_adjust); 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci# dump out anymore offsets that may have been found 53462306a36Sopenharmony_ciupdate_funcs(); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci# If we did not find any mcount callers, we are done (do nothing). 53762306a36Sopenharmony_ciif (!$opened) { 53862306a36Sopenharmony_ci exit(0); 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ciclose(FILE); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci# 54462306a36Sopenharmony_ci# Step 3: Compile the file that holds the list of call sites to mcount. 54562306a36Sopenharmony_ci# 54662306a36Sopenharmony_ci`$cc -o $mcount_o -c $mcount_s`; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cimy @converts = keys %convert; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci# 55162306a36Sopenharmony_ci# Step 4: Do we have sections that started with local functions? 55262306a36Sopenharmony_ci# 55362306a36Sopenharmony_ciif ($#converts >= 0) { 55462306a36Sopenharmony_ci my $globallist = ""; 55562306a36Sopenharmony_ci my $locallist = ""; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci foreach my $con (@converts) { 55862306a36Sopenharmony_ci $globallist .= " --globalize-symbol $con"; 55962306a36Sopenharmony_ci $locallist .= " --localize-symbol $con"; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci my $globalobj = $dirname . "/.tmp_gl_" . $filename; 56362306a36Sopenharmony_ci my $globalmix = $dirname . "/.tmp_mx_" . $filename; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci # 56662306a36Sopenharmony_ci # Step 5: set up each local function as a global 56762306a36Sopenharmony_ci # 56862306a36Sopenharmony_ci `$objcopy $globallist $inputfile $globalobj`; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci # 57162306a36Sopenharmony_ci # Step 6: Link the global version to our list. 57262306a36Sopenharmony_ci # 57362306a36Sopenharmony_ci `$ld -r $globalobj $mcount_o -o $globalmix`; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci # 57662306a36Sopenharmony_ci # Step 7: Convert the local functions back into local symbols 57762306a36Sopenharmony_ci # 57862306a36Sopenharmony_ci `$objcopy $locallist $globalmix $inputfile`; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci # Remove the temp files 58162306a36Sopenharmony_ci `$rm $globalobj $globalmix`; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci} else { 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci my $mix = $dirname . "/.tmp_mx_" . $filename; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci # 58862306a36Sopenharmony_ci # Step 8: Link the object with our list of call sites object. 58962306a36Sopenharmony_ci # 59062306a36Sopenharmony_ci `$ld -r $inputfile $mcount_o -o $mix`; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci # 59362306a36Sopenharmony_ci # Step 9: Move the result back to the original object. 59462306a36Sopenharmony_ci # 59562306a36Sopenharmony_ci `$mv $mix $inputfile`; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci# Clean up the temp files 59962306a36Sopenharmony_ci`$rm $mcount_o $mcount_s`; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ciexit(0); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci# vim: softtabstop=4 604