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