1 # Copyright © 2007-2010 Raphaël Hertzog <hertzog@debian.org>
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <https://www.gnu.org/licenses/>.
16 package Dpkg::Shlibs::Objdump;
20 use feature qw(state);
22 our $VERSION = '0.01';
25 use Dpkg::ErrorHandling;
26 use Dpkg::Path qw(find_command);
27 use Dpkg::Arch qw(debarch_to_gnutriplet get_build_arch get_host_arch);
30 # Decide which objdump to call
31 our $OBJDUMP = 'objdump';
32 if (get_build_arch() ne get_host_arch()) {
33 my $od = debarch_to_gnutriplet(get_host_arch()) . '-objdump';
34 $OBJDUMP = $od if find_command($od);
40 my $class = ref($this) || $this;
41 my $self = { objects => {} };
47 my ($self, $obj) = @_;
48 my $id = $obj->get_id;
50 $self->{objects}{$id} = $obj;
56 my ($self, $file) = @_;
57 my $obj = Dpkg::Shlibs::Objdump::Object->new($file);
59 return $self->add_object($obj);
63 my ($self, $name) = @_;
64 foreach my $obj (values %{$self->{objects}}) {
65 my $sym = $obj->get_symbol($name);
66 if (defined($sym) && $sym->{defined}) {
74 my ($self, $objid) = @_;
75 if ($self->has_object($objid)) {
76 return $self->{objects}{$objid};
82 my ($self, $objid) = @_;
83 return exists $self->{objects}{$objid};
97 ELF_MACH_SPARC64_OLD => 11,
98 ELF_MACH_SPARC32PLUS => 18,
102 ELF_MACH_ALPHA_OLD => 41,
104 ELF_MACH_SPARC64 => 43,
108 ELF_MACH_MN10300 => 89,
109 ELF_MACH_MN10200 => 90,
111 ELF_MACH_XTENSA => 94,
112 ELF_MACH_MICROBLAZE => 189,
113 ELF_MACH_AVR_OLD => 0x1057,
114 ELF_MACH_OR1K_OLD => 0x8472,
115 ELF_MACH_ALPHA => 0x9026,
116 ELF_MACH_M32R_CYGNUS => 0x9041,
117 ELF_MACH_S390_OLD => 0xa390,
118 ELF_MACH_XTENSA_OLD => 0xabc7,
119 ELF_MACH_MICROBLAZE_OLD => 0xbaab,
120 ELF_MACH_MN10300_CYGNUS => 0xbeef,
121 ELF_MACH_MN10200_CYGNUS => 0xdead,
123 ELF_VERSION_NONE => 0,
124 ELF_VERSION_CURRENT => 1,
126 # List of processor flags that might influence the ABI.
128 ELF_FLAG_ARM_ALIGN8 => 0x00000040,
129 ELF_FLAG_ARM_NEW_ABI => 0x00000080,
130 ELF_FLAG_ARM_OLD_ABI => 0x00000100,
131 ELF_FLAG_ARM_SOFT_FLOAT => 0x00000200,
132 ELF_FLAG_ARM_HARD_FLOAT => 0x00000400,
133 ELF_FLAG_ARM_EABI_MASK => 0xff000000,
135 ELF_FLAG_IA64_ABI64 => 0x00000010,
137 ELF_FLAG_MIPS_ABI2 => 0x00000020,
138 ELF_FLAG_MIPS_32BIT => 0x00000100,
139 ELF_FLAG_MIPS_FP64 => 0x00000200,
140 ELF_FLAG_MIPS_NAN2008 => 0x00000400,
141 ELF_FLAG_MIPS_ABI_MASK => 0x0000f000,
142 ELF_FLAG_MIPS_ARCH_MASK => 0xf0000000,
144 ELF_FLAG_PPC64_ABI64 => 0x00000003,
146 ELF_FLAG_SH_MACH_MASK => 0x0000001f,
149 # These map alternative or old machine IDs to their canonical form.
151 ELF_MACH_ALPHA_OLD() => ELF_MACH_ALPHA,
152 ELF_MACH_AVR_OLD() => ELF_MACH_AVR,
153 ELF_MACH_M32R_CYGNUS() => ELF_MACH_M32R,
154 ELF_MACH_MICROBLAZE_OLD() => ELF_MACH_MICROBLAZE,
155 ELF_MACH_MN10200_CYGNUS() => ELF_MACH_MN10200,
156 ELF_MACH_MN10300_CYGNUS() => ELF_MACH_MN10300,
157 ELF_MACH_OR1K_OLD() => ELF_MACH_OR1K,
158 ELF_MACH_S390_OLD() => ELF_MACH_S390,
159 ELF_MACH_SPARC32PLUS() => ELF_MACH_SPARC,
160 ELF_MACH_SPARC64_OLD() => ELF_MACH_SPARC64,
161 ELF_MACH_XTENSA_OLD() => ELF_MACH_XTENSA,
164 # These masks will try to expose processor flags that are ABI incompatible,
165 # and as such are part of defining the architecture ABI. If uncertain it is
166 # always better to not mask a flag, because that preserves the historical
167 # behavior, and we do not drop dependencies.
168 my %elf_flags_mask = (
169 ELF_MACH_IA64() => ELF_FLAG_IA64_ABI64,
170 ELF_MACH_MIPS() => ELF_FLAG_MIPS_ABI_MASK | ELF_FLAG_MIPS_ABI2,
171 ELF_MACH_PPC64() => ELF_FLAG_PPC64_ABI64,
178 return $format{$file} if exists $format{$file};
182 open my $fh, '<', $file or syserr(g_('cannot read %s'), $file);
183 my $rc = read $fh, $header, 64;
184 if (not defined $rc) {
185 syserr(g_('cannot read %s'), $file);
186 } elsif ($rc != 64) {
193 # Unpack the identifier field.
194 @elf{qw(magic bits endian vertype osabi verabi)} = unpack 'a4C5', $header;
196 return unless $elf{magic} eq "\x7fELF";
197 return unless $elf{vertype} == ELF_VERSION_CURRENT;
199 my ($elf_word, $elf_endian);
200 if ($elf{bits} == ELF_BITS_32) {
202 } elsif ($elf{bits} == ELF_BITS_64) {
207 if ($elf{endian} == ELF_ORDER_2LSB) {
209 } elsif ($elf{endian} == ELF_ORDER_2MSB) {
215 # Unpack the endianness and size dependent fields.
216 my $tmpl = "x16(S2Lx[${elf_word}3]L)${elf_endian}";
217 @elf{qw(type mach version flags)} = unpack $tmpl, $header;
219 # Canonicalize the machine ID.
220 $elf{mach} = $elf_mach_map{$elf{mach}} // $elf{mach};
222 # Mask any processor flags that might not change the architecture ABI.
223 $elf{flags} &= $elf_flags_mask{$elf{mach}} // 0;
225 # Repack for easy comparison, as a big-endian byte stream, so that
226 # unpacking for output gives meaningful results.
227 $format{$file} = pack 'C2(SL)>', @elf{qw(bits endian mach flags)};
229 return $format{$file};
234 open(my $file_fh, '<', $file) or syserr(g_('cannot read %s'), $file);
235 my ($header, $result) = ('', 0);
236 if (read($file_fh, $header, 4) == 4) {
237 $result = 1 if ($header =~ /^\177ELF$/);
243 package Dpkg::Shlibs::Objdump::Object;
249 use Dpkg::ErrorHandling;
253 my $file = shift // '';
254 my $class = ref($this) || $this;
260 $self->analyze($file);
271 $self->{SONAME} = '';
273 $self->{GNU_HASH} = '';
274 $self->{SONAME} = '';
275 $self->{NEEDED} = [];
277 $self->{dynsyms} = {};
279 $self->{dynrelocs} = {};
286 my ($self, $file) = @_;
288 $file ||= $self->{file};
292 $self->{file} = $file;
294 $self->{exec_abi} = Dpkg::Shlibs::Objdump::get_format($file);
296 if (not defined $self->{exec_abi}) {
297 warning(g_("unknown executable format in file '%s'"), $file);
301 local $ENV{LC_ALL} = 'C';
302 open(my $objdump, '-|', $OBJDUMP, '-w', '-f', '-p', '-T', '-R', $file)
303 or syserr(g_('cannot fork for %s'), $OBJDUMP);
304 my $ret = $self->parse_objdump_output($objdump);
309 sub parse_objdump_output {
310 my ($self, $fh) = @_;
312 my $section = 'none';
317 if (/^DYNAMIC SYMBOL TABLE:/) {
320 } elsif (/^DYNAMIC RELOCATION RECORDS/) {
321 $section = 'dynreloc';
322 $_ = <$fh>; # Skip header
324 } elsif (/^Dynamic Section:/) {
325 $section = 'dyninfo';
327 } elsif (/^Program Header:/) {
330 } elsif (/^Version definitions:/) {
333 } elsif (/^Version References:/) {
338 if ($section eq 'dynsym') {
339 $self->parse_dynamic_symbol($_);
340 } elsif ($section eq 'dynreloc') {
341 if (/^\S+\s+(\S+)\s+(.+)$/) {
342 $self->{dynrelocs}{$2} = $1;
344 warning(g_("couldn't parse dynamic relocation record: %s"), $_);
346 } elsif ($section eq 'dyninfo') {
347 if (/^\s*NEEDED\s+(\S+)/) {
348 push @{$self->{NEEDED}}, $1;
349 } elsif (/^\s*SONAME\s+(\S+)/) {
350 $self->{SONAME} = $1;
351 } elsif (/^\s*HASH\s+(\S+)/) {
353 } elsif (/^\s*GNU_HASH\s+(\S+)/) {
354 $self->{GNU_HASH} = $1;
355 } elsif (/^\s*RUNPATH\s+(\S+)/) {
356 # RUNPATH takes precedence over RPATH but is
357 # considered after LD_LIBRARY_PATH while RPATH
358 # is considered before (if RUNPATH is not set).
360 $self->{RPATH} = [ split /:/, $runpath ];
361 } elsif (/^\s*RPATH\s+(\S+)/) {
363 unless (scalar(@{$self->{RPATH}})) {
364 $self->{RPATH} = [ split /:/, $rpath ];
367 } elsif ($section eq 'none') {
368 if (/^\s*.+:\s*file\s+format\s+(\S+)$/) {
369 $self->{format} = $1;
370 } elsif (/^architecture:\s*\S+,\s*flags\s*\S+:$/) {
371 # Parse 2 lines of "-f"
372 # architecture: i386, flags 0x00000112:
373 # EXEC_P, HAS_SYMS, D_PAGED
374 # start address 0x08049b50
377 $self->{flags}{$_} = 1 foreach (split(/,\s*/));
381 # Update status of dynamic symbols given the relocations that have
382 # been parsed after the symbols...
383 $self->apply_relocations();
385 return $section ne 'none';
388 # Output format of objdump -w -T
390 # /lib/libc.so.6: file format elf32-i386
392 # DYNAMIC SYMBOL TABLE:
393 # 00056ef0 g DF .text 000000db GLIBC_2.2 getwchar
394 # 00000000 g DO *ABS* 00000000 GCC_3.0 GCC_3.0
395 # 00069960 w DF .text 0000001e GLIBC_2.0 bcmp
396 # 00000000 w D *UND* 00000000 _pthread_cleanup_pop_restore
397 # 0000b788 g DF .text 0000008e Base .protected xine_close
398 # 0000b788 g DF .text 0000008e .hidden IA__g_free
400 # | ||||||| | | Version str (.visibility) + Symbol name
401 # | ||||||| | Alignment
402 # | ||||||| Section name (or *UND* for an undefined symbol)
403 # | ||||||F=Function,f=file,O=object
404 # | |||||d=debugging,D=dynamic
409 # | g=global,l=local,!=both global/local
412 # GLIBC_2.2 is the version string associated to the symbol
413 # (GLIBC_2.2) is the same but the symbol is hidden, a newer version of the
416 my $vis_re = qr/(\.protected|\.hidden|\.internal|0x\S+)/;
419 [0-9a-f]+ # Symbol size
421 \s+(\S+) # Section name
422 \s+[0-9a-f]+ # Alignment
423 (?:\s+(\S+))? # Version string
424 (?:\s+$vis_re)? # Visibility
425 \s+(.+) # Symbol name
428 sub parse_dynamic_symbol {
429 my ($self, $line) = @_;
430 if ($line =~ $dynsym_re) {
432 my ($flags, $sect, $ver, $vis, $name) = ($1, $2, $3, $4, $5);
434 # Special case if version is missing but extra visibility
435 # attribute replaces it in the match
436 if (defined($ver) and $ver =~ /^$vis_re$/) {
441 # Cleanup visibility field
442 $vis =~ s/^\.// if defined($vis);
446 version => $ver // '',
448 dynamic => substr($flags, 5, 1) eq 'D',
449 debug => substr($flags, 5, 1) eq 'd',
450 type => substr($flags, 6, 1),
451 weak => substr($flags, 1, 1) eq 'w',
452 local => substr($flags, 0, 1) eq 'l',
453 global => substr($flags, 0, 1) eq 'g',
454 visibility => $vis // '',
456 defined => $sect ne '*UND*'
459 # Handle hidden symbols
460 if (defined($ver) and $ver =~ /^\((.*)\)$/) {
462 $symbol->{version} = $1;
463 $symbol->{hidden} = 1;
467 $self->add_dynamic_symbol($symbol);
468 } elsif ($line =~ /^[0-9a-f]+ (.{7})\s+(\S+)\s+[0-9a-f]+/) {
469 # Same start but no version and no symbol ... just ignore
470 } elsif ($line =~ /^REG_G\d+\s+/) {
471 # Ignore some s390-specific output like
472 # REG_G6 g R *UND* 0000000000000000 #scratch
474 warning(g_("couldn't parse dynamic symbol definition: %s"), $line);
478 sub apply_relocations {
480 foreach my $sym (values %{$self->{dynsyms}}) {
481 # We want to mark as undefined symbols those which are currently
482 # defined but that depend on a copy relocation
483 next if not $sym->{defined};
484 next if not exists $self->{dynrelocs}{$sym->{name}};
485 if ($self->{dynrelocs}{$sym->{name}} =~ /^R_.*_COPY$/) {
491 sub add_dynamic_symbol {
492 my ($self, $symbol) = @_;
493 $symbol->{objid} = $symbol->{soname} = $self->get_id();
494 $symbol->{soname} =~ s{^.*/}{} unless $self->{SONAME};
495 if ($symbol->{version}) {
496 $self->{dynsyms}{$symbol->{name} . '@' . $symbol->{version}} = $symbol;
498 $self->{dynsyms}{$symbol->{name} . '@Base'} = $symbol;
504 return $self->{SONAME} || $self->{file};
508 my ($self, $name) = @_;
509 if (exists $self->{dynsyms}{$name}) {
510 return $self->{dynsyms}{$name};
513 if (exists $self->{dynsyms}{$name . '@Base'}) {
514 return $self->{dynsyms}{$name . '@Base'};
520 sub get_exported_dynamic_symbols {
522 return grep { $_->{defined} && $_->{dynamic} && !$_->{local} }
523 values %{$self->{dynsyms}};
526 sub get_undefined_dynamic_symbols {
528 return grep { (!$_->{defined}) && $_->{dynamic} }
529 values %{$self->{dynsyms}};
532 sub get_needed_libraries {
534 return @{$self->{NEEDED}};
539 return exists $self->{flags}{EXEC_P} && $self->{flags}{EXEC_P};
542 sub is_public_library {
544 return exists $self->{flags}{DYNAMIC} && $self->{flags}{DYNAMIC}
545 && exists $self->{SONAME} && $self->{SONAME};