1#!/usr/bin/perl -w
2# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
3# ***** BEGIN LICENSE BLOCK *****
4# Version: MPL 1.1/GPL 2.0/LGPL 2.1
5#
6# The contents of this file are subject to the Mozilla Public License Version
7# 1.1 (the "License"); you may not use this file except in compliance with
8# the License. You may obtain a copy of the License at
9# http://www.mozilla.org/MPL/
10#
11# Software distributed under the License is distributed on an "AS IS" basis,
12# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13# for the specific language governing rights and limitations under the
14# License.
15#
16# The Original Code is readelf_wrap.pl.
17#
18# The Initial Developer of the Original Code is
19# IBM Corporation.
20# Portions created by the Initial Developer are Copyright (C) 2003
21# the Initial Developer. All Rights Reserved.
22#
23# Contributor(s):
24#  Brian Ryner <bryner@brianryner.com>
25#
26# Alternatively, the contents of this file may be used under the terms of
27# either the GNU General Public License Version 2 or later (the "GPL"), or
28# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29# in which case the provisions of the GPL or the LGPL are applicable instead
30# of those above. If you wish to allow use of your version of this file only
31# under the terms of either the GPL or the LGPL, and not to allow others to
32# use your version of this file under the terms of the MPL, indicate your
33# decision by deleting the provisions above and replace them with the notice
34# and other provisions required by the GPL or the LGPL. If you do not delete
35# the provisions above, a recipient may use your version of this file under
36# the terms of any one of the MPL, the GPL or the LGPL.
37#
38# ***** END LICENSE BLOCK *****
39
40use strict;
41
42# Section fields (the full list of values is in <elf.h>)
43my $SECT_NUM  = 0;  # section index
44my $SECT_NAME = 1;  # section name
45my $SECT_TYPE = 2;  # section type
46my $SECT_ADDR = 3;  # section virtual address
47my $SECT_OFF  = 4;  # section offset in file
48my $SECT_SIZE = 5;  # size of section
49my $SECT_ES   = 6;  # section entry size
50my $SECT_FLG  = 7;  # section flags
51my $SECT_LK   = 8;  # link to another section
52my $SECT_INF  = 9;  # additional section info
53my $SECT_AL   = 10; # section alignment
54
55
56# Symbol fields (note: the full list of possible values for each field
57# is given in <elf.h>)
58
59my $SYM_NUM   = 0;     # unique index of the symbol
60my $SYM_VALUE = 1;     # value of the symbol
61my $SYM_SIZE  = 2;     # size of the symbol
62my $SYM_TYPE  = 3;     # type (NOTYPE, OBJECT, FUNC, SECTION, FILE, ...)
63my $SYM_BIND  = 4;     # binding/scope (LOCAL, GLOBAL, WEAK, ...)
64my $SYM_VIS   = 5;     # visibility (DEFAULT, INTERNAL, HIDDEN, PROTECTED)
65my $SYM_NDX   = 6;     # index of section the symbol is in
66my $SYM_NAME  = 7;     # name of the symbol
67my $SYM_FILE  = 8;     # (not part of readelf) file for symbol
68
69# Tell readelf to print out the list of sections and then the symbols
70die "Usage: $^X <binary>\n" unless ($#ARGV >= 0);
71my $readelf = $ENV{'READELF_PROG'};
72if (!$readelf) {
73    $readelf = 'readelf';
74}
75open(READELF_OUTPUT, "$readelf -Ss $ARGV[0] 2>/dev/null | c++filt |") or die "readelf failed to run on $ARGV[0]\n";
76
77my @section_list;
78my @symbol_list;
79my ($module) = ($ARGV[0] =~ /([^\/]+)$/);
80my $in_symbols = 0;
81
82while (<READELF_OUTPUT>) {
83
84    if (!$in_symbols) {
85        if (/^ *\[ *(\d+)\]/) {
86            my @section;
87
88            # note that we strip off the leading '.' of section names for
89            # readability
90            if (! (@section = (/^ *\[ *(\d+)\] \.([\w\.\-]+) *(\w+) *(.{8}) (.{6}[0-9a-fA-F]*) (.{6}[0-9a-fA-F]*) *(\d+) ([a-zA-Z]+ +| +[a-zA-Z]+|) *(\d+) *(\w+) *(\d+)/))) {
91                # capture the 'null' section which has no name, so that the
92                # array indices are the same as the section indices.
93
94                @section = ($1, '', 'NULL', '00000000', '000000', '000000',
95                            '00', '', '0', '0', '0');
96            }
97
98            push (@section_list, \@section);
99        } elsif (/^Symbol table/) {
100            $in_symbols = 1;
101        }
102    } else {
103
104        my @sym;
105
106        if (@sym = /^\s*(\d+): (\w+)\s*(\d+)\s*(\w+)\s*(\w+)\s*(\w+)\s*(\w+) (.*)/)
107        {
108            # Filter out types of symbols that we don't care about:
109            #  - anything that's not of type OBJECT or FUNC
110            #  - any undefined symbols (ndx = UND[EF])
111            #  - any 0-size symbols
112
113            if (($sym[$SYM_TYPE] !~ /^(OBJECT|FUNC)$/) ||
114                $sym[$SYM_NDX] eq 'UND' || $sym[$SYM_NDX] eq 'UNDEF'
115                || $sym[$SYM_SIZE] eq '0') {
116                next;
117            }
118            push (@symbol_list, \@sym);
119        }
120        elsif (/^Symbol table .*'\.symtab'/) {
121            # We've been using .dynsym up to this point, but if we have .symtab
122            # available, it will have everything in .dynsym and more.
123            # So, reset our symbol list.
124
125            @symbol_list = ();
126        }
127    }
128}
129
130close(READELF_OUTPUT);
131
132# spit them out in codesighs TSV format
133my $sym;
134my @section_sizes;
135$#section_sizes = $#section_list;
136foreach (@section_sizes) { $_ = 0; }
137
138foreach $sym (@symbol_list) {
139    # size
140    printf "%08x\t", $sym->[$SYM_SIZE];
141
142    # code or data
143    if ($sym->[$SYM_TYPE] eq 'FUNC') {
144        print "CODE\t";
145    } else {  # OBJECT
146        print "DATA\t";
147    }
148
149    # scope
150    if ($sym->[$SYM_BIND] eq 'LOCAL') {
151        print "STATIC\t";
152    } elsif ($sym->[$SYM_BIND] =~ /(GLOBAL|WEAK)/) {
153        print "PUBLIC\t";
154    } else {
155        print "UNDEF\t";
156    }
157
158    # module name
159
160    print "$module\t";
161
162    # section
163    my $section = $section_list[$sym->[$SYM_NDX]]->[$SECT_NAME];
164    print "$section\t";
165
166    # should be the object file, but for now just module/section
167    print "UNDEF:$module:$section\t";
168
169    # now the symbol name
170    print $sym->[$SYM_NAME]."\n";
171
172    # update our cumulative section sizes
173    $section_sizes[$section_list[$sym->[$SYM_NDX]]->[$SECT_NUM]] += $sym->[$SYM_SIZE];
174}
175
176# Output extra entries to make the sum of the symbol sizes equal the
177# section size.
178
179my $section;
180foreach $section (@section_list) {
181
182    my $diff = hex($section->[$SECT_SIZE]) - $section_sizes[$section->[$SECT_NUM]];
183    if ($diff > 0) {
184        my $sectname = $section->[$SECT_NAME];
185        if ($section->[$SECT_NAME] =~ /^(rodata|data|text|bss)/) {
186            printf "%08x", $diff;
187            print "\tDATA\tSTATIC\t$module\t$sectname\tUNDEF:$module:$sectname\t.nosyms.$sectname\n";
188#        } else {
189#            print "ignoring $diff bytes of empty space in $sectname section\n";
190        }
191    }
192}
193