1 # Copyright © 2007 Raphaël Hertzog <hertzog@debian.org>
2 # Copyright © 2009-2010 Modestas Vainius <modax@debian.org>
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 package Dpkg::Shlibs::SymbolFile;
22 our $VERSION = "0.01";
25 use Dpkg::ErrorHandling;
27 use Dpkg::Control::Fields;
28 use Dpkg::Shlibs::Symbol;
29 use Dpkg::Arch qw(get_host_arch);
31 use base qw(Dpkg::Interface::Storable);
34 '__bss_end__' => 1, # arm
35 '__bss_end' => 1, # arm
36 '_bss_end__' => 1, # arm
37 '__bss_start' => 1, # ALL
38 '__bss_start__' => 1, # arm
39 '__data_start' => 1, # arm
40 '__do_global_ctors_aux' => 1, # ia64
41 '__do_global_dtors_aux' => 1, # ia64
42 '__do_jv_register_classes' => 1,# ia64
43 '_DYNAMIC' => 1, # ALL
47 '__exidx_end' => 1, # armel
48 '__exidx_start' => 1, # armel
49 '_fbss' => 1, # mips, mipsel
50 '_fdata' => 1, # mips, mipsel
52 '_ftext' => 1, # mips, mipsel
53 '_GLOBAL_OFFSET_TABLE_' => 1, # hppa, mips, mipsel
54 '__gmon_start__' => 1, # hppa
55 '__gnu_local_gp' => 1, # mips, mipsel
56 '_gp' => 1, # mips, mipsel
58 '_PROCEDURE_LINKAGE_TABLE_' => 1, # sparc, alpha
59 '_SDA2_BASE_' => 1, # powerpc
60 '_SDA_BASE_' => 1, # powerpc
63 for (my $i = 14; $i <= 31; $i++) {
64 # Many powerpc specific symbols
65 $blacklist{"_restfpr_$i"} = 1;
66 $blacklist{"_restfpr_$i\_x"} = 1;
67 $blacklist{"_restgpr_$i"} = 1;
68 $blacklist{"_restgpr_$i\_x"} = 1;
69 $blacklist{"_savefpr_$i"} = 1;
70 $blacklist{"_savegpr_$i"} = 1;
73 # Many armel-specific symbols
74 $blacklist{"__aeabi_$_"} = 1 foreach (qw(cdcmpeq cdcmple cdrcmple cfcmpeq
75 cfcmple cfrcmple d2f d2iz d2lz d2uiz d2ulz dadd dcmpeq dcmpge dcmpgt
76 dcmple dcmplt dcmpun ddiv dmul dneg drsub dsub f2d f2iz f2lz f2uiz f2ulz
77 fadd fcmpeq fcmpge fcmpgt fcmple fcmplt fcmpun fdiv fmul fneg frsub fsub
78 i2d i2f idiv idivmod l2d l2f lasr lcmp ldivmod llsl llsr lmul ui2d ui2f
79 uidiv uidivmod ul2d ul2f ulcmp uldivmod unwind_cpp_pr0 unwind_cpp_pr1
80 unwind_cpp_pr2 uread4 uread8 uwrite4 uwrite8));
85 my $class = ref($this) || $this;
88 $self->{arch} = get_host_arch() unless defined $self->{arch};
90 if (exists $self->{file}) {
91 $self->load($self->{file}) if -e $self->{file};
103 $self->{objects} = {};
107 my ($self, @ids) = @_;
109 $has{$_} = 1 foreach (@ids);
110 foreach my $objid (keys %{$self->{objects}}) {
111 delete $self->{objects}{$objid} unless exists $has{$objid};
117 return keys %{$self->{objects}};
121 my ($self, $soname) = @_;
122 if (defined $soname) {
123 my $obj = $self->get_object($soname);
124 return (defined $obj) ? values %{$obj->{syms}} : ();
127 foreach my $soname ($self->get_sonames()) {
128 push @syms, $self->get_symbols($soname);
135 my ($self, $soname) = @_;
137 if (defined $soname) {
138 my $obj = $self->get_object($soname);
139 foreach my $alias (values %{$obj->{patterns}{aliases}}) {
140 push @patterns, values %$alias;
142 return (@patterns, @{$obj->{patterns}{generic}});
144 foreach my $soname ($self->get_sonames()) {
145 push @patterns, $self->get_patterns($soname);
151 # Create a symbol from the supplied string specification.
153 my ($self, $spec, %opts) = @_;
154 my $symbol = (exists $opts{base}) ? $opts{base} :
155 Dpkg::Shlibs::Symbol->new();
157 my $ret = $opts{dummy} ? $symbol->parse_symbolspec($spec, default_minver => 0) :
158 $symbol->parse_symbolspec($spec);
160 $symbol->initialize(arch => $self->get_arch());
167 my ($self, $symbol, $soname) = @_;
168 my $object = $self->get_object($soname);
170 if ($symbol->is_pattern()) {
171 if (my $alias_type = $symbol->get_alias_type()) {
172 unless (exists $object->{patterns}{aliases}{$alias_type}) {
173 $object->{patterns}{aliases}{$alias_type} = {};
175 # Alias hash for matching.
176 my $aliases = $object->{patterns}{aliases}{$alias_type};
177 $aliases->{$symbol->get_symbolname()} = $symbol;
179 # Otherwise assume this is a generic sequential pattern. This
180 # should be always safe.
181 push @{$object->{patterns}{generic}}, $symbol;
185 # invalidate the minimum version cache
186 $object->{minver_cache} = [];
187 $object->{syms}{$symbol->get_symbolname()} = $symbol;
192 # Parameter seen is only used for recursive calls
194 my ($self, $fh, $file, $seen, $obj_ref, $base_symbol) = @_;
197 my $base = shift || 'Dpkg::Shlibs::Symbol';
198 return (ref $base) ? $base->clone(@_) : $base->new(@_);
201 if (defined($seen)) {
202 return if exists $seen->{$file}; # Avoid include loops
204 $self->{file} = $file;
209 if (not ref($obj_ref)) { # Init ref to name of current object/lib
213 while (defined($_ = <$fh>)) {
216 if (/^(?:\s+|#(?:DEPRECATED|MISSING): ([^#]+)#\s*)(.*)/) {
217 if (not defined ($$obj_ref)) {
218 error(_g("symbol information must be preceded by a header (file %s, line %s)"), $file, $.);
220 # Symbol specification
221 my $deprecated = ($1) ? $1 : 0;
222 my $sym = new_symbol($base_symbol, deprecated => $deprecated);
223 if ($self->create_symbol($2, base => $sym)) {
224 $self->add_symbol($sym, $$obj_ref);
226 warning(_g("Failed to parse line in %s: %s"), $file, $_);
228 } elsif (/^(\(.*\))?#include\s+"([^"]+)"/) {
233 if (defined $tagspec) {
234 $new_base_symbol = new_symbol($base_symbol);
235 $new_base_symbol->parse_tagspec($tagspec);
237 $dir =~ s{[^/]+$}{}; # Strip filename
238 $self->load("$dir$filename", $seen, $obj_ref, $new_base_symbol);
240 # Skip possible comments and empty lines
241 } elsif (/^\|\s*(.*)$/) {
242 # Alternative dependency template
243 push @{$self->{objects}{$$obj_ref}{deps}}, "$1";
244 } elsif (/^\*\s*([^:]+):\s*(.*\S)\s*$/) {
246 $self->{objects}{$$obj_ref}{fields}{field_capitalize($1)} = $2;
247 } elsif (/^(\S+)\s+(.*)$/) {
248 # New object and dependency template
250 if (exists $self->{objects}{$$obj_ref}) {
251 # Update/override infos only
252 $self->{objects}{$$obj_ref}{deps} = [ "$2" ];
254 # Create a new object
255 $self->create_object($$obj_ref, "$2");
258 warning(_g("Failed to parse a line in %s: %s"), $file, $_);
261 delete $seen->{$file};
264 # Beware: we reuse the data structure of the provided symfile so make
265 # sure to not modify them after having called this function
266 sub merge_object_from_symfile {
267 my ($self, $src, $objid) = @_;
268 if (not $self->has_object($objid)) {
269 $self->{objects}{$objid} = $src->get_object($objid);
271 warning(_g("tried to merge the same object (%s) twice in a symfile"), $objid);
276 my ($self, $fh, %opts) = @_;
277 $opts{template_mode} = 0 unless exists $opts{template_mode};
278 $opts{with_deprecated} = 1 unless exists $opts{with_deprecated};
279 $opts{with_pattern_matches} = 0 unless exists $opts{with_pattern_matches};
281 foreach my $soname (sort $self->get_sonames()) {
282 my @deps = $self->get_dependencies($soname);
283 my $dep = shift @deps;
284 $dep =~ s/#PACKAGE#/$opts{package}/g if exists $opts{package};
285 print $fh "$soname $dep\n" if defined $fh;
286 $res .= "$soname $dep\n" if defined wantarray;
288 foreach $dep (@deps) {
289 $dep =~ s/#PACKAGE#/$opts{package}/g if exists $opts{package};
290 print $fh "| $dep\n" if defined $fh;
291 $res .= "| $dep\n" if defined wantarray;
293 my $f = $self->{objects}{$soname}{fields};
294 foreach my $field (sort keys %{$f}) {
295 my $value = $f->{$field};
296 $value =~ s/#PACKAGE#/$opts{package}/g if exists $opts{package};
297 print $fh "* $field: $value\n" if defined $fh;
298 $res .= "* $field: $value\n" if defined wantarray;
302 if ($opts{template_mode}) {
303 # Exclude symbols matching a pattern, but include patterns themselves
304 @symbols = grep { not $_->get_pattern() } $self->get_symbols($soname);
305 push @symbols, $self->get_patterns($soname);
307 @symbols = $self->get_symbols($soname);
309 foreach my $sym (sort { $a->get_symboltempl() cmp
310 $b->get_symboltempl() } @symbols) {
311 next if $sym->{deprecated} and not $opts{with_deprecated};
312 # Do not dump symbols from foreign arch unless dumping a template.
313 next if not $opts{template_mode} and
314 not $sym->arch_is_concerned($self->get_arch());
315 # Dump symbol specification. Dump symbol tags only in template mode.
316 print $fh $sym->get_symbolspec($opts{template_mode}), "\n" if defined $fh;
317 $res .= $sym->get_symbolspec($opts{template_mode}) . "\n" if defined wantarray;
318 # Dump pattern matches as comments (if requested)
319 if ($opts{with_pattern_matches} && $sym->is_pattern()) {
320 for my $match (sort { $a->get_symboltempl() cmp
321 $b->get_symboltempl() } $sym->get_pattern_matches())
323 print $fh "#MATCH:", $match->get_symbolspec(0), "\n" if defined $fh;
324 $res .= "#MATCH:" . $match->get_symbolspec(0) . "\n" if defined wantarray;
332 # Tries to match a symbol name and/or version against the patterns defined.
333 # Returns a pattern which matches (if any).
334 sub find_matching_pattern {
335 my ($self, $refsym, $sonames, $inc_deprecated) = @_;
336 $inc_deprecated = 0 unless defined $inc_deprecated;
337 my $name = (ref $refsym) ? $refsym->get_symbolname() : $refsym;
339 my $pattern_ok = sub {
341 return defined $p && ($inc_deprecated || !$p->{deprecated}) &&
342 $p->arch_is_concerned($self->get_arch());
345 foreach my $soname ((ref($sonames) eq 'ARRAY') ? @$sonames : $sonames) {
346 my $obj = $self->get_object($soname);
347 my ($type, $pattern);
348 next unless defined $obj;
350 my $all_aliases = $obj->{patterns}{aliases};
351 for my $type (Dpkg::Shlibs::Symbol::ALIAS_TYPES) {
352 if (exists $all_aliases->{$type} && keys(%{$all_aliases->{$type}})) {
353 my $aliases = $all_aliases->{$type};
354 my $converter = $aliases->{(keys %$aliases)[0]};
355 if (my $alias = $converter->convert_to_alias($name)) {
356 if ($alias && exists $aliases->{$alias}) {
357 $pattern = $aliases->{$alias};
358 last if &$pattern_ok($pattern);
359 $pattern = undef; # otherwise not found yet
365 # Now try generic patterns and use the first that matches
366 if (not defined $pattern) {
367 for my $p (@{$obj->{patterns}{generic}}) {
368 if (&$pattern_ok($p) && $p->matches_rawname($name)) {
374 if (defined $pattern) {
376 ( symbol => $pattern, soname => $soname ) : $pattern;
379 return (wantarray) ? () : undef;
382 # merge_symbols($object, $minver)
383 # Needs $Objdump->get_object($soname) as parameter
384 # Don't merge blacklisted symbols related to the internal (arch-specific)
387 my ($self, $object, $minver) = @_;
388 my $soname = $object->{SONAME} || error(_g("cannot merge symbols from objects without SONAME"));
390 foreach my $sym ($object->get_exported_dynamic_symbols()) {
391 my $name = $sym->{name} . '@' .
392 ($sym->{version} ? $sym->{version} : "Base");
393 my $symobj = $self->lookup_symbol($name, $soname);
394 if (exists $blacklist{$sym->{name}}) {
395 next unless (defined $symobj and $symobj->has_tag("ignore-blacklist"));
397 $dynsyms{$name} = $sym;
400 unless ($self->has_object($soname)) {
401 $self->create_object($soname, '');
403 # Scan all symbols provided by the objects
404 my $obj = $self->get_object($soname);
405 # invalidate the minimum version cache - it is not sufficient to
406 # invalidate in add_symbol, since we might change a minimum
407 # version for a particular symbol without adding it
408 $obj->{minver_cache} = [];
409 foreach my $name (keys %dynsyms) {
411 if ($sym = $self->lookup_symbol($name, $obj, 1)) {
412 # If the symbol is already listed in the file
413 $sym->mark_found_in_library($minver, $self->get_arch());
415 # The exact symbol is not present in the file, but it might match a
417 my $pattern = $self->find_matching_pattern($name, $obj, 1);
418 if (defined $pattern) {
419 $pattern->mark_found_in_library($minver, $self->get_arch());
420 $sym = $pattern->create_pattern_match(symbol => $name);
422 # Symbol without any special info as no pattern matched
423 $sym = Dpkg::Shlibs::Symbol->new(symbol => $name,
426 $self->add_symbol($sym, $obj);
430 # Process all symbols which could not be found in the library.
431 foreach my $sym ($self->get_symbols($soname)) {
432 if (not exists $dynsyms{$sym->get_symbolname()}) {
433 $sym->mark_not_found_in_library($minver, $self->get_arch());
437 # Deprecate patterns which didn't match anything
438 for my $pattern (grep { $_->get_pattern_matches() == 0 }
439 $self->get_patterns($soname)) {
440 $pattern->mark_not_found_in_library($minver, $self->get_arch());
446 return scalar(keys %{$self->{objects}}) ? 0 : 1;
450 my ($self, $soname) = @_;
451 return exists $self->{objects}{$soname};
455 my ($self, $soname) = @_;
456 return ref($soname) ? $soname : $self->{objects}{$soname};
460 my ($self, $soname, @deps) = @_;
461 $self->{objects}{$soname} = {
474 my ($self, $soname, $dep_id) = @_;
475 $dep_id = 0 unless defined($dep_id);
476 return $self->get_object($soname)->{deps}[$dep_id];
479 sub get_smallest_version {
480 my ($self, $soname, $dep_id) = @_;
481 $dep_id = 0 unless defined($dep_id);
482 my $so_object = $self->get_object($soname);
483 return $so_object->{minver_cache}[$dep_id] if(defined($so_object->{minver_cache}[$dep_id]));
485 foreach my $sym ($self->get_symbols($so_object)) {
486 next if $dep_id != $sym->{dep_id};
487 $minver = $sym->{minver} unless defined($minver);
488 if (version_compare($minver, $sym->{minver}) > 0) {
489 $minver = $sym->{minver};
492 $so_object->{minver_cache}[$dep_id] = $minver;
496 sub get_dependencies {
497 my ($self, $soname) = @_;
498 return @{$self->get_object($soname)->{deps}};
502 my ($self, $soname, $name) = @_;
503 if (my $obj = $self->get_object($soname)) {
504 if (exists $obj->{fields}{$name}) {
505 return $obj->{fields}{$name};
511 # Tries to find a symbol like the $refsym and returns its descriptor.
512 # $refsym may also be a symbol name.
514 my ($self, $refsym, $sonames, $inc_deprecated) = @_;
515 $inc_deprecated = 0 unless defined($inc_deprecated);
516 my $name = (ref $refsym) ? $refsym->get_symbolname() : $refsym;
518 foreach my $so ((ref($sonames) eq 'ARRAY') ? @$sonames : $sonames) {
519 if (my $obj = $self->get_object($so)) {
520 my $sym = $obj->{syms}{$name};
521 if ($sym and ($inc_deprecated or not $sym->{deprecated}))
524 ( symbol => $sym, soname => $so ) : $sym;
528 return (wantarray) ? () : undef;
531 # Tries to find a pattern like the $refpat and returns its descriptor.
532 # $refpat may also be a pattern spec.
534 my ($self, $refpat, $sonames, $inc_deprecated) = @_;
535 $inc_deprecated = 0 unless defined($inc_deprecated);
536 # If $refsym is a string, we need to create a dummy ref symbol.
537 $refpat = $self->create_symbol($refpat, dummy => 1) if ! ref($refpat);
539 if ($refpat && $refpat->is_pattern()) {
540 foreach my $soname ((ref($sonames) eq 'ARRAY') ? @$sonames : $sonames) {
541 if (my $obj = $self->get_object($soname)) {
543 if (my $type = $refpat->get_alias_type()) {
544 if (exists $obj->{patterns}{aliases}{$type}) {
545 $pat = $obj->{patterns}{aliases}{$type}{$refpat->get_symbolname()};
547 } elsif ($refpat->get_pattern_type() eq "generic") {
548 for my $p (@{$obj->{patterns}{generic}}) {
549 if (($inc_deprecated || !$p->{deprecated}) &&
550 $p->equals($refpat, versioning => 0))
557 if ($pat && ($inc_deprecated || !$pat->{deprecated})) {
559 (symbol => $pat, soname => $soname) : $pat;
564 return (wantarray) ? () : undef;
567 # Get symbol object reference either by symbol name or by a reference object.
568 sub get_symbol_object {
569 my ($self, $refsym, $soname) = @_;
570 my $sym = $self->lookup_symbol($refsym, $soname, 1);
571 if (! defined $sym) {
572 $sym = $self->lookup_pattern($refsym, $soname, 1);
577 sub get_new_symbols {
578 my ($self, $ref, %opts) = @_;
579 my $with_optional = (exists $opts{with_optional}) ?
580 $opts{with_optional} : 0;
582 foreach my $soname ($self->get_sonames()) {
583 next if not $ref->has_object($soname);
585 # Scan raw symbols first.
586 foreach my $sym (grep { ($with_optional || ! $_->is_optional())
587 && $_->is_legitimate($self->get_arch()) }
588 $self->get_symbols($soname))
590 my $refsym = $ref->lookup_symbol($sym, $soname, 1);
592 if (defined $refsym) {
593 # If the symbol exists in the $ref symbol file, it might
594 # still be new if $refsym is not legitimate.
595 $isnew = not $refsym->is_legitimate($self->get_arch());
597 # If the symbol does not exist in the $ref symbol file, it does
598 # not mean that it's new. It might still match a pattern in the
599 # symbol file. However, due to performance reasons, first check
600 # if the pattern that the symbol matches (if any) exists in the
601 # ref symbol file as well.
603 ($sym->get_pattern() and $ref->lookup_pattern($sym->get_pattern(), $soname, 1)) or
604 $ref->find_matching_pattern($sym, $soname, 1)
607 push @res, { symbol => $sym, soname => $soname } if $isnew;
611 foreach my $p (grep { ($with_optional || ! $_->is_optional())
612 && $_->is_legitimate($self->get_arch()) }
613 $self->get_patterns($soname))
615 my $refpat = $ref->lookup_pattern($p, $soname, 0);
616 # If reference pattern was not found or it is not legitimate,
617 # considering current one as new.
618 if (not defined $refpat or
619 not $refpat->is_legitimate($self->get_arch()))
621 push @res, { symbol => $p , soname => $soname };
628 sub get_lost_symbols {
629 my ($self, $ref, %opts) = @_;
630 return $ref->get_new_symbols($self, %opts);
635 my ($self, $ref) = @_;
637 foreach my $soname ($self->get_sonames()) {
638 push @res, $soname if not $ref->get_object($soname);
644 my ($self, $ref) = @_;
645 return $ref->get_new_libs($self);