Download Overload 71 PDF
Transcript
overload issue 71 february 2006
exported_cmd = 'nm --defined-only %s'
unknown_cmd = 'nm --undefined-only %s'
ignored_funcs = \
set(['_PROCEDURE_LINKAGE_TABLE_', '_fini',
'_init'])
def unmangle(name):
return os.popen('c++filt ' \
+ name).readline()[:-1]
# return name
class Library(object):
def __init__(self, name):
self.fullname = name
self.name = os.path.basename(name)
self.exported = []
for line in os.popen(exported_cmd \
% self.fullname).readlines():
m = exported_func.match(line)
if m:
if m.group(1) not in ignored_funcs:
self.exported.append(m.group(1))
self.unknown = []
for line in os.popen(unknown_cmd \
% self.fullname).readlines():
m = unknown_func.match(line)
if m:
self.unknown.append(m.group(1))
class Binary(object):
def __init__(self, name):
self.fullname = name
self.name = os.path.basename(name)
self.unknown = []
for line in os.popen(unknown_cmd \
% self.fullname).readlines():
m = unknown_func.match(line)
if m: self.unknown.append(m.group(1))
exported = set(lib.exported)
for unk in bin.unknown:
if unk in exported:
exported.discard(unk)
print "Unused:"
for func in exported:
print "\t%s" % unmangle(func)
if __name__ == "__main__":
main()
-- end nm2.py
-- executed
tim@spike:~/accu/overload/dead-code$ ./nm2.py
Unused:
internal_func(std::basic_string<char,
std::char_traits<char>,
std::allocator<char> > const&)
unused_func(std::basic_string<char,
std::char_traits<char>,
std::allocator<char> > const&)
-- end executed
Here we can see that the function internal_func has been
shown as not used even though it is called directly from
exported_func. A tool that was going to give false positives
like this was not going to be extremely useful.
Luckily it was pointed out to me that another GNU tool called
readelf is able to show relocation information. There is a
relocation entry for every function that is called.
The relevant lines from the results of readelf -r --wide
libshared.so are shown in Figure 1.
More
regex
magic
'[0-9a-f]{8}\s+[0-9af]{8}\s+\S+\s+[0-9a-f]{8}\s+(\S+)' gives a way to
identify the function calls. Once these are eliminated from the
exported list, we are left with only one function: unused_func.
Conclusion
The script ended up taking about 15 - 20 minutes to run (mainly due
to an inefficiency in the c++filt calling that I never got around to
fixing) but returned around about three or four thousand functions
def main():
that were no longer called. The script does still show false positives
lib = Library('libshared.so')
though as it is not able to determine when a function is called
bin = Binary('deadtest')
through a pointer to a function or pointer to a member function. It
did however give a good
00000830 00001002 R_386_PC32
00000000
starting point to reduce the
_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
dead code.
00000850 00001002 R_386_PC32
00000000
Tim Penhey
_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
0000086e 00001002 R_386_PC32
00000000
_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
0000089a 00001002 R_386_PC32
00000000
_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
0000083f 00000d02 R_386_PC32
00000000
_ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKSbIS4_S5_T1_E
0000087c 00001502 R_386_PC32
0000081c
_Z13internal_funcRKSs
000008b3 00001d02 R_386_PC32
00000000
_ZNSt8ios_base4InitD1Ev
000008e0 00000c02 R_386_PC32
00000000
_ZNSt8ios_base4InitC1Ev
000008f7 00001102 R_386_PC32
00000000
__cxa_atexit
[email protected]
Thanks
Thanks to Paul Thomas on
the accu-general list for
pointing out readelf to me.
References
1 GNU:
http://www.gnu.org/
software/binutils/
Figure 1
15