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 ".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 < 7) {
 104        print "usage: $P arch bits objdump objcopy cc ld nm rm mv is_module inputfile\n";
 105        print "version: $V\n";
 106        exit(1);
 107}
 108
 109my ($arch, $bits, $objdump, $objcopy, $cc,
 110    $ld, $nm, $rm, $mv, $is_module, $inputfile) = @ARGV;
 111
 112# This file refers to mcount and shouldn't be ftraced, so lets' ignore it
 113if ($inputfile eq "kernel/trace/ftrace.o") {
 114    exit(0);
 115}
 116
 117# Acceptable sections to record.
 118my %text_sections = (
 119     ".text" => 1,
 120     ".sched.text" => 1,
 121     ".spinlock.text" => 1,
 122     ".irqentry.text" => 1,
 123);
 124
 125$objdump = "objdump" if ((length $objdump) == 0);
 126$objcopy = "objcopy" if ((length $objcopy) == 0);
 127$cc = "gcc" if ((length $cc) == 0);
 128$ld = "ld" if ((length $ld) == 0);
 129$nm = "nm" if ((length $nm) == 0);
 130$rm = "rm" if ((length $rm) == 0);
 131$mv = "mv" if ((length $mv) == 0);
 132
 133#print STDERR "running: $P '$arch' '$objdump' '$objcopy' '$cc' '$ld' " .
 134#    "'$nm' '$rm' '$mv' '$inputfile'\n";
 135
 136my %locals;             # List of local (static) functions
 137my %weak;               # List of weak functions
 138my %convert;            # List of local functions used that needs conversion
 139
 140my $type;
 141my $nm_regex;           # Find the local functions (return function)
 142my $section_regex;      # Find the start of a section
 143my $function_regex;     # Find the name of a function
 144                        #    (return offset and func name)
 145my $mcount_regex;       # Find the call site to mcount (return offset)
 146my $alignment;          # The .align value to use for $mcount_section
 147my $section_type;       # Section header plus possible alignment command
 148
 149if ($arch eq "x86") {
 150    if ($bits == 64) {
 151        $arch = "x86_64";
 152    } else {
 153        $arch = "i386";
 154    }
 155}
 156
 157#
 158# We base the defaults off of i386, the other archs may
 159# feel free to change them in the below if statements.
 160#
 161$nm_regex = "^[0-9a-fA-F]+\\s+t\\s+(\\S+)";
 162$section_regex = "Disassembly of section\\s+(\\S+):";
 163$function_regex = "^([0-9a-fA-F]+)\\s+<(.*?)>:";
 164$mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount\$";
 165$section_type = '@progbits';
 166$type = ".long";
 167
 168if ($arch eq "x86_64") {
 169    $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount([+-]0x[0-9a-zA-Z]+)?\$";
 170    $type = ".quad";
 171    $alignment = 8;
 172
 173    # force flags for this arch
 174    $ld .= " -m elf_x86_64";
 175    $objdump .= " -M x86-64";
 176    $objcopy .= " -O elf64-x86-64";
 177    $cc .= " -m64";
 178
 179} elsif ($arch eq "i386") {
 180    $alignment = 4;
 181
 182    # force flags for this arch
 183    $ld .= " -m elf_i386";
 184    $objdump .= " -M i386";
 185    $objcopy .= " -O elf32-i386";
 186    $cc .= " -m32";
 187
 188} elsif ($arch eq "sh") {
 189    $alignment = 2;
 190
 191    # force flags for this arch
 192    $ld .= " -m shlelf_linux";
 193    $objcopy .= " -O elf32-sh-linux";
 194    $cc .= " -m32";
 195
 196} elsif ($arch eq "powerpc") {
 197    $nm_regex = "^[0-9a-fA-F]+\\s+t\\s+(\\.?\\S+)";
 198    $function_regex = "^([0-9a-fA-F]+)\\s+<(\\.?.*?)>:";
 199    $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s\\.?_mcount\$";
 200
 201    if ($bits == 64) {
 202        $type = ".quad";
 203    }
 204
 205} elsif ($arch eq "arm") {
 206    $alignment = 2;
 207    $section_type = '%progbits';
 208
 209} elsif ($arch eq "ia64") {
 210    $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s_mcount\$";
 211    $type = "data8";
 212
 213    if ($is_module eq "0") {
 214        $cc .= " -mconstant-gp";
 215    }
 216} else {
 217    die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD";
 218}
 219
 220my $text_found = 0;
 221my $read_function = 0;
 222my $opened = 0;
 223my $mcount_section = "__mcount_loc";
 224
 225my $dirname;
 226my $filename;
 227my $prefix;
 228my $ext;
 229
 230if ($inputfile =~ m,^(.*)/([^/]*)$,) {
 231    $dirname = $1;
 232    $filename = $2;
 233} else {
 234    $dirname = ".";
 235    $filename = $inputfile;
 236}
 237
 238if ($filename =~ m,^(.*)(\.\S),) {
 239    $prefix = $1;
 240    $ext = $2;
 241} else {
 242    $prefix = $filename;
 243    $ext = "";
 244}
 245
 246my $mcount_s = $dirname . "/.tmp_mc_" . $prefix . ".s";
 247my $mcount_o = $dirname . "/.tmp_mc_" . $prefix . ".o";
 248
 249#
 250# --globalize-symbols came out in 2.17, we must test the version
 251# of objcopy, and if it is less than 2.17, then we can not
 252# record local functions.
 253my $use_locals = 01;
 254my $local_warn_once = 0;
 255my $found_version = 0;
 256
 257open (IN, "$objcopy --version |") || die "error running $objcopy";
 258while (<IN>) {
 259    if (/objcopy.*\s(\d+)\.(\d+)/) {
 260        my $major = $1;
 261        my $minor = $2;
 262
 263        $found_version = 1;
 264        if ($major < 2 ||
 265            ($major == 2 && $minor < 17)) {
 266            $use_locals = 0;
 267        }
 268        last;
 269    }
 270}
 271close (IN);
 272
 273if (!$found_version) {
 274    print STDERR "WARNING: could not find objcopy version.\n" .
 275        "\tDisabling local function references.\n";
 276}
 277
 278#
 279# Step 1: find all the local (static functions) and weak symbols.
 280#        't' is local, 'w/W' is weak (we never use a weak function)
 281#
 282open (IN, "$nm $inputfile|") || die "error running $nm";
 283while (<IN>) {
 284    if (/$nm_regex/) {
 285        $locals{$1} = 1;
 286    } elsif (/^[0-9a-fA-F]+\s+([wW])\s+(\S+)/) {
 287        $weak{$2} = $1;
 288    }
 289}
 290close(IN);
 291
 292my @offsets;            # Array of offsets of mcount callers
 293my $ref_func;           # reference function to use for offsets
 294my $offset = 0;         # offset of ref_func to section beginning
 295
 296##
 297# update_funcs - print out the current mcount callers
 298#
 299#  Go through the list of offsets to callers and write them to
 300#  the output file in a format that can be read by an assembler.
 301#
 302sub update_funcs
 303{
 304    return if ($#offsets < 0);
 305
 306    defined($ref_func) || die "No function to reference";
 307
 308    # A section only had a weak function, to represent it.
 309    # Unfortunately, a weak function may be overwritten by another
 310    # function of the same name, making all these offsets incorrect.
 311    # To be safe, we simply print a warning and bail.
 312    if (defined $weak{$ref_func}) {
 313        print STDERR
 314            "$inputfile: WARNING: referencing weak function" .
 315            " $ref_func for mcount\n";
 316        return;
 317    }
 318
 319    # is this function static? If so, note this fact.
 320    if (defined $locals{$ref_func}) {
 321
 322        # only use locals if objcopy supports globalize-symbols
 323        if (!$use_locals) {
 324            return;
 325        }
 326        $convert{$ref_func} = 1;
 327    }
 328
 329    # Loop through all the mcount caller offsets and print a reference
 330    # to the caller based from the ref_func.
 331    for (my $i=0; $i <= $#offsets; $i++) {
 332        if (!$opened) {
 333            open(FILE, ">$mcount_s") || die "can't create $mcount_s\n";
 334            $opened = 1;
 335            print FILE "\t.section $mcount_section,\"a\",$section_type\n";
 336            print FILE "\t.align $alignment\n" if (defined($alignment));
 337        }
 338        printf FILE "\t%s %s + %d\n", $type, $ref_func, $offsets[$i] - $offset;
 339    }
 340}
 341
 342#
 343# Step 2: find the sections and mcount call sites
 344#
 345open(IN, "$objdump -hdr $inputfile|") || die "error running $objdump";
 346
 347my $text;
 348
 349my $read_headers = 1;
 350
 351while (<IN>) {
 352    # is it a section?
 353    if (/$section_regex/) {
 354        $read_headers = 0;
 355
 356        # Only record text sections that we know are safe
 357        if (defined($text_sections{$1})) {
 358            $read_function = 1;
 359        } else {
 360            $read_function = 0;
 361        }
 362        # print out any recorded offsets
 363        update_funcs() if ($text_found);
 364
 365        # reset all markers and arrays
 366        $text_found = 0;
 367        undef($ref_func);
 368        undef(@offsets);
 369
 370    # section found, now is this a start of a function?
 371    } elsif ($read_function && /$function_regex/) {
 372        $text_found = 1;
 373        $offset = hex $1;
 374        $text = $2;
 375
 376        # if this is either a local function or a weak function
 377        # keep looking for functions that are global that
 378        # we can use safely.
 379        if (!defined($locals{$text}) && !defined($weak{$text})) {
 380            $ref_func = $text;
 381            $read_function = 0;
 382        } else {
 383            # if we already have a function, and this is weak, skip it
 384            if (!defined($ref_func) || !defined($weak{$text})) {
 385                $ref_func = $text;
 386            }
 387        }
 388    } elsif ($read_headers && /$mcount_section/) {
 389        #
 390        # Somehow the make process can execute this script on an
 391        # object twice. If it does, we would duplicate the mcount
 392        # section and it will cause the function tracer self test
 393        # to fail. Check if the mcount section exists, and if it does,
 394        # warn and exit.
 395        #
 396        print STDERR "ERROR: $mcount_section already in $inputfile\n" .
 397            "\tThis may be an indication that your build is corrupted.\n" .
 398            "\tDelete $inputfile and try again. If the same object file\n" .
 399            "\tstill causes an issue, then disable CONFIG_DYNAMIC_FTRACE.\n";
 400        exit(-1);
 401    }
 402
 403    # is this a call site to mcount? If so, record it to print later
 404    if ($text_found && /$mcount_regex/) {
 405        $offsets[$#offsets + 1] = hex $1;
 406    }
 407}
 408
 409# dump out anymore offsets that may have been found
 410update_funcs() if ($text_found);
 411
 412# If we did not find any mcount callers, we are done (do nothing).
 413if (!$opened) {
 414    exit(0);
 415}
 416
 417close(FILE);
 418
 419#
 420# Step 3: Compile the file that holds the list of call sites to mcount.
 421#
 422`$cc -o $mcount_o -c $mcount_s`;
 423
 424my @converts = keys %convert;
 425
 426#
 427# Step 4: Do we have sections that started with local functions?
 428#
 429if ($#converts >= 0) {
 430    my $globallist = "";
 431    my $locallist = "";
 432
 433    foreach my $con (@converts) {
 434        $globallist .= " --globalize-symbol $con";
 435        $locallist .= " --localize-symbol $con";
 436    }
 437
 438    my $globalobj = $dirname . "/.tmp_gl_" . $filename;
 439    my $globalmix = $dirname . "/.tmp_mx_" . $filename;
 440
 441    #
 442    # Step 5: set up each local function as a global
 443    #
 444    `$objcopy $globallist $inputfile $globalobj`;
 445
 446    #
 447    # Step 6: Link the global version to our list.
 448    #
 449    `$ld -r $globalobj $mcount_o -o $globalmix`;
 450
 451    #
 452    # Step 7: Convert the local functions back into local symbols
 453    #
 454    `$objcopy $locallist $globalmix $inputfile`;
 455
 456    # Remove the temp files
 457    `$rm $globalobj $globalmix`;
 458
 459} else {
 460
 461    my $mix = $dirname . "/.tmp_mx_" . $filename;
 462
 463    #
 464    # Step 8: Link the object with our list of call sites object.
 465    #
 466    `$ld -r $inputfile $mcount_o -o $mix`;
 467
 468    #
 469    # Step 9: Move the result back to the original object.
 470    #
 471    `$mv $mix $inputfile`;
 472}
 473
 474# Clean up the temp files
 475`$rm $mcount_o $mcount_s`;
 476
 477exit(0);
 478
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.