linux/scripts/recordmcount.pl
<<
>>
Prefs
   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 ".sched.text", "ax"
  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 .sched.text. 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 ".sched.text", "ax"
  55#  my_func:
  56#        [...]
  57#        call mcount  (offset: 0x5)
  58#        [...]
  59#        ret
  60#  other_func:
  61#        [...]
  62#        call mcount (offset: 0x1b)
  63#        [...]
  64#
  65# If we make the tmp.s the same as above, when we link together with
  66# the original object, we will end up with two symbols for my_func:
  67# one local, one global.  After final compile, we will end up with
  68# an undefined reference to my_func.
  69#
  70# Since local objects can reference local variables, we need to find
  71# a way to make tmp.o reference the local objects of the original object
  72# file after it is linked together. To do this, we convert the my_func
  73# into a global symbol before linking tmp.o. Then after we link tmp.o
  74# we will only have a single symbol for my_func that is global.
  75# We can convert my_func back into a local symbol and we are done.
  76#
  77# Here are the steps we take:
  78#
  79# 1) Record all the local symbols by using 'nm'
  80# 2) Use objdump to find all the call site offsets and sections for
  81#    mcount.
  82# 3) Compile the list into its own object.
  83# 4) Do we have to deal with local functions? If not, go to step 8.
  84# 5) Make an object that converts these local functions to global symbols
  85#    with objcopy.
  86# 6) Link together this new object with the list object.
  87# 7) Convert the local functions back to local symbols and rename
  88#    the result as the original object.
  89#    End.
  90# 8) Link the object with the list object.
  91# 9) Move the result back to the original object.
  92#    End.
  93#
  94
  95use strict;
  96
  97my $P = $0;
  98$P =~ s@.*/@@g;
  99
 100my $V = '0.1';
 101
 102if ($#ARGV < 7) {
 103        print "usage: $P arch bits objdump objcopy cc ld nm rm mv is_module inputfile\n";
 104        print "version: $V\n";
 105        exit(1);
 106}
 107
 108my ($arch, $bits, $objdump, $objcopy, $cc,
 109    $ld, $nm, $rm, $mv, $is_module, $inputfile) = @ARGV;
 110
 111# This file refers to mcount and shouldn't be ftraced, so lets' ignore it
 112if ($inputfile eq "kernel/trace/ftrace.o") {
 113    exit(0);
 114}
 115
 116# Acceptable sections to record.
 117my %text_sections = (
 118     ".text" => 1,
 119     ".sched.text" => 1,
 120     ".spinlock.text" => 1,
 121     ".irqentry.text" => 1,
 122);
 123
 124$objdump = "objdump" if ((length $objdump) == 0);
 125$objcopy = "objcopy" if ((length $objcopy) == 0);
 126$cc = "gcc" if ((length $cc) == 0);
 127$ld = "ld" if ((length $ld) == 0);
 128$nm = "nm" if ((length $nm) == 0);
 129$rm = "rm" if ((length $rm) == 0);
 130$mv = "mv" if ((length $mv) == 0);
 131
 132#print STDERR "running: $P '$arch' '$objdump' '$objcopy' '$cc' '$ld' " .
 133#    "'$nm' '$rm' '$mv' '$inputfile'\n";
 134
 135my %locals;             # List of local (static) functions
 136my %weak;               # List of weak functions
 137my %convert;            # List of local functions used that needs conversion
 138
 139my $type;
 140my $nm_regex;           # Find the local functions (return function)
 141my $section_regex;      # Find the start of a section
 142my $function_regex;     # Find the name of a function
 143                        #    (return offset and func name)
 144my $mcount_regex;       # Find the call site to mcount (return offset)
 145my $alignment;          # The .align value to use for $mcount_section
 146my $section_type;       # Section header plus possible alignment command
 147
 148if ($arch eq "x86") {
 149    if ($bits == 64) {
 150        $arch = "x86_64";
 151    } else {
 152        $arch = "i386";
 153    }
 154}
 155
 156#
 157# We base the defaults off of i386, the other archs may
 158# feel free to change them in the below if statements.
 159#
 160$nm_regex = "^[0-9a-fA-F]+\\s+t\\s+(\\S+)";
 161$section_regex = "Disassembly of section\\s+(\\S+):";
 162$function_regex = "^([0-9a-fA-F]+)\\s+<(.*?)>:";
 163$mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount\$";
 164$section_type = '@progbits';
 165$type = ".long";
 166
 167if ($arch eq "x86_64") {
 168    $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount([+-]0x[0-9a-zA-Z]+)?\$";
 169    $type = ".quad";
 170    $alignment = 8;
 171
 172    # force flags for this arch
 173    $ld .= " -m elf_x86_64";
 174    $objdump .= " -M x86-64";
 175    $objcopy .= " -O elf64-x86-64";
 176    $cc .= " -m64";
 177
 178} elsif ($arch eq "i386") {
 179    $alignment = 4;
 180
 181    # force flags for this arch
 182    $ld .= " -m elf_i386";
 183    $objdump .= " -M i386";
 184    $objcopy .= " -O elf32-i386";
 185    $cc .= " -m32";
 186
 187} elsif ($arch eq "s390" && $bits == 32) {
 188    $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_390_32\\s+_mcount\$";
 189    $alignment = 4;
 190    $ld .= " -m elf_s390";
 191    $cc .= " -m31";
 192
 193} elsif ($arch eq "s390" && $bits == 64) {
 194    $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_390_(PC|PLT)32DBL\\s+_mcount\\+0x2\$";
 195    $alignment = 8;
 196    $type = ".quad";
 197    $ld .= " -m elf64_s390";
 198    $cc .= " -m64";
 199
 200} elsif ($arch eq "sh") {
 201    $alignment = 2;
 202
 203    # force flags for this arch
 204    $ld .= " -m shlelf_linux";
 205    $objcopy .= " -O elf32-sh-linux";
 206    $cc .= " -m32";
 207
 208} elsif ($arch eq "powerpc") {
 209    $nm_regex = "^[0-9a-fA-F]+\\s+t\\s+(\\.?\\S+)";
 210    $function_regex = "^([0-9a-fA-F]+)\\s+<(\\.?.*?)>:";
 211    $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s\\.?_mcount\$";
 212
 213    if ($bits == 64) {
 214        $type = ".quad";
 215    }
 216
 217} elsif ($arch eq "arm") {
 218    $alignment = 2;
 219    $section_type = '%progbits';
 220
 221} elsif ($arch eq "ia64") {
 222    $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s_mcount\$";
 223    $type = "data8";
 224
 225    if ($is_module eq "0") {
 226        $cc .= " -mconstant-gp";
 227    }
 228} elsif ($arch eq "sparc64") {
 229    # In the objdump output there are giblets like:
 230    # 0000000000000000 <igmp_net_exit-0x18>:
 231    # As there's some data blobs that get emitted into the
 232    # text section before the first instructions and the first
 233    # real symbols.  We don't want to match that, so to combat
 234    # this we use '\w' so we'll match just plain symbol names,
 235    # and not those that also include hex offsets inside of the
 236    # '<>' brackets.  Actually the generic function_regex setting
 237    # could safely use this too.
 238    $function_regex = "^([0-9a-fA-F]+)\\s+<(\\w*?)>:";
 239
 240    # Sparc64 calls '_mcount' instead of plain 'mcount'.
 241    $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s_mcount\$";
 242
 243    $alignment = 8;
 244    $type = ".xword";
 245    $ld .= " -m elf64_sparc";
 246    $cc .= " -m64";
 247    $objcopy .= " -O elf64-sparc";
 248} else {
 249    die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD";
 250}
 251
 252my $text_found = 0;
 253my $read_function = 0;
 254my $opened = 0;
 255my $mcount_section = "__mcount_loc";
 256
 257my $dirname;
 258my $filename;
 259my $prefix;
 260my $ext;
 261
 262if ($inputfile =~ m,^(.*)/([^/]*)$,) {
 263    $dirname = $1;
 264    $filename = $2;
 265} else {
 266    $dirname = ".";
 267    $filename = $inputfile;
 268}
 269
 270if ($filename =~ m,^(.*)(\.\S),) {
 271    $prefix = $1;
 272    $ext = $2;
 273} else {
 274    $prefix = $filename;
 275    $ext = "";
 276}
 277
 278my $mcount_s = $dirname . "/.tmp_mc_" . $prefix . ".s";
 279my $mcount_o = $dirname . "/.tmp_mc_" . $prefix . ".o";
 280
 281#
 282# --globalize-symbols came out in 2.17, we must test the version
 283# of objcopy, and if it is less than 2.17, then we can not
 284# record local functions.
 285my $use_locals = 01;
 286my $local_warn_once = 0;
 287my $found_version = 0;
 288
 289open (IN, "$objcopy --version |") || die "error running $objcopy";
 290while (<IN>) {
 291    if (/objcopy.*\s(\d+)\.(\d+)/) {
 292        my $major = $1;
 293        my $minor = $2;
 294
 295        $found_version = 1;
 296        if ($major < 2 ||
 297            ($major == 2 && $minor < 17)) {
 298            $use_locals = 0;
 299        }
 300        last;
 301    }
 302}
 303close (IN);
 304
 305if (!$found_version) {
 306    print STDERR "WARNING: could not find objcopy version.\n" .
 307        "\tDisabling local function references.\n";
 308}
 309
 310#
 311# Step 1: find all the local (static functions) and weak symbols.
 312#        't' is local, 'w/W' is weak (we never use a weak function)
 313#
 314open (IN, "$nm $inputfile|") || die "error running $nm";
 315while (<IN>) {
 316    if (/$nm_regex/) {
 317        $locals{$1} = 1;
 318    } elsif (/^[0-9a-fA-F]+\s+([wW])\s+(\S+)/) {
 319        $weak{$2} = $1;
 320    }
 321}
 322close(IN);
 323
 324my @offsets;            # Array of offsets of mcount callers
 325my $ref_func;           # reference function to use for offsets
 326my $offset = 0;         # offset of ref_func to section beginning
 327
 328##
 329# update_funcs - print out the current mcount callers
 330#
 331#  Go through the list of offsets to callers and write them to
 332#  the output file in a format that can be read by an assembler.
 333#
 334sub update_funcs
 335{
 336    return if ($#offsets < 0);
 337
 338    defined($ref_func) || die "No function to reference";
 339
 340    # A section only had a weak function, to represent it.
 341    # Unfortunately, a weak function may be overwritten by another
 342    # function of the same name, making all these offsets incorrect.
 343    # To be safe, we simply print a warning and bail.
 344    if (defined $weak{$ref_func}) {
 345        print STDERR
 346            "$inputfile: WARNING: referencing weak function" .
 347            " $ref_func for mcount\n";
 348        return;
 349    }
 350
 351    # is this function static? If so, note this fact.
 352    if (defined $locals{$ref_func}) {
 353
 354        # only use locals if objcopy supports globalize-symbols
 355        if (!$use_locals) {
 356            return;
 357        }
 358        $convert{$ref_func} = 1;
 359    }
 360
 361    # Loop through all the mcount caller offsets and print a reference
 362    # to the caller based from the ref_func.
 363    for (my $i=0; $i <= $#offsets; $i++) {
 364        if (!$opened) {
 365            open(FILE, ">$mcount_s") || die "can't create $mcount_s\n";
 366            $opened = 1;
 367            print FILE "\t.section $mcount_section,\"a\",$section_type\n";
 368            print FILE "\t.align $alignment\n" if (defined($alignment));
 369        }
 370        printf FILE "\t%s %s + %d\n", $type, $ref_func, $offsets[$i] - $offset;
 371    }
 372}
 373
 374#
 375# Step 2: find the sections and mcount call sites
 376#
 377open(IN, "$objdump -hdr $inputfile|") || die "error running $objdump";
 378
 379my $text;
 380
 381my $read_headers = 1;
 382
 383while (<IN>) {
 384    # is it a section?
 385    if (/$section_regex/) {
 386        $read_headers = 0;
 387
 388        # Only record text sections that we know are safe
 389        if (defined($text_sections{$1})) {
 390            $read_function = 1;
 391        } else {
 392            $read_function = 0;
 393        }
 394        # print out any recorded offsets
 395        update_funcs() if (defined($ref_func));
 396
 397        # reset all markers and arrays
 398        $text_found = 0;
 399        undef($ref_func);
 400        undef(@offsets);
 401
 402    # section found, now is this a start of a function?
 403    } elsif ($read_function && /$function_regex/) {
 404        $text_found = 1;
 405        $text = $2;
 406
 407        # if this is either a local function or a weak function
 408        # keep looking for functions that are global that
 409        # we can use safely.
 410        if (!defined($locals{$text}) && !defined($weak{$text})) {
 411            $ref_func = $text;
 412            $read_function = 0;
 413            $offset = hex $1;
 414        } else {
 415            # if we already have a function, and this is weak, skip it
 416            if (!defined($ref_func) && !defined($weak{$text}) &&
 417                 # PPC64 can have symbols that start with .L and
 418                 # gcc considers these special. Don't use them!
 419                 $text !~ /^\.L/) {
 420                $ref_func = $text;
 421                $offset = hex $1;
 422            }
 423        }
 424    } elsif ($read_headers && /$mcount_section/) {
 425        #
 426        # Somehow the make process can execute this script on an
 427        # object twice. If it does, we would duplicate the mcount
 428        # section and it will cause the function tracer self test
 429        # to fail. Check if the mcount section exists, and if it does,
 430        # warn and exit.
 431        #
 432        print STDERR "ERROR: $mcount_section already in $inputfile\n" .
 433            "\tThis may be an indication that your build is corrupted.\n" .
 434            "\tDelete $inputfile and try again. If the same object file\n" .
 435            "\tstill causes an issue, then disable CONFIG_DYNAMIC_FTRACE.\n";
 436        exit(-1);
 437    }
 438
 439    # is this a call site to mcount? If so, record it to print later
 440    if ($text_found && /$mcount_regex/) {
 441        $offsets[$#offsets + 1] = hex $1;
 442    }
 443}
 444
 445# dump out anymore offsets that may have been found
 446update_funcs() if (defined($ref_func));
 447
 448# If we did not find any mcount callers, we are done (do nothing).
 449if (!$opened) {
 450    exit(0);
 451}
 452
 453close(FILE);
 454
 455#
 456# Step 3: Compile the file that holds the list of call sites to mcount.
 457#
 458`$cc -o $mcount_o -c $mcount_s`;
 459
 460my @converts = keys %convert;
 461
 462#
 463# Step 4: Do we have sections that started with local functions?
 464#
 465if ($#converts >= 0) {
 466    my $globallist = "";
 467    my $locallist = "";
 468
 469    foreach my $con (@converts) {
 470        $globallist .= " --globalize-symbol $con";
 471        $locallist .= " --localize-symbol $con";
 472    }
 473
 474    my $globalobj = $dirname . "/.tmp_gl_" . $filename;
 475    my $globalmix = $dirname . "/.tmp_mx_" . $filename;
 476
 477    #
 478    # Step 5: set up each local function as a global
 479    #
 480    `$objcopy $globallist $inputfile $globalobj`;
 481
 482    #
 483    # Step 6: Link the global version to our list.
 484    #
 485    `$ld -r $globalobj $mcount_o -o $globalmix`;
 486
 487    #
 488    # Step 7: Convert the local functions back into local symbols
 489    #
 490    `$objcopy $locallist $globalmix $inputfile`;
 491
 492    # Remove the temp files
 493    `$rm $globalobj $globalmix`;
 494
 495} else {
 496
 497    my $mix = $dirname . "/.tmp_mx_" . $filename;
 498
 499    #
 500    # Step 8: Link the object with our list of call sites object.
 501    #
 502    `$ld -r $inputfile $mcount_o -o $mix`;
 503
 504    #
 505    # Step 9: Move the result back to the original object.
 506    #
 507    `$mv $mix $inputfile`;
 508}
 509
 510# Clean up the temp files
 511`$rm $mcount_o $mcount_s`;
 512
 513exit(0);
 514