Imported Upstream version 1.16.10
[services/dpkg.git] / scripts / Dpkg / Shlibs / SymbolFile.pm
1 # Copyright © 2007 Raphaël Hertzog <hertzog@debian.org>
2 # Copyright © 2009-2010 Modestas Vainius <modax@debian.org>
3 #
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.
8 #
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.
13 #
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/>.
16
17 package Dpkg::Shlibs::SymbolFile;
18
19 use strict;
20 use warnings;
21
22 our $VERSION = "0.01";
23
24 use Dpkg::Gettext;
25 use Dpkg::ErrorHandling;
26 use Dpkg::Version;
27 use Dpkg::Control::Fields;
28 use Dpkg::Shlibs::Symbol;
29 use Dpkg::Arch qw(get_host_arch);
30
31 use base qw(Dpkg::Interface::Storable);
32
33 my %blacklist = (
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
44     '_edata' => 1,              # ALL
45     '_end' => 1,                # ALL
46     '__end__' => 1,             # arm
47     '__exidx_end' => 1,         # armel
48     '__exidx_start' => 1,       # armel
49     '_fbss' => 1,               # mips, mipsel
50     '_fdata' => 1,              # mips, mipsel
51     '_fini' => 1,               # ALL
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
57     '_init' => 1,               # ALL
58     '_PROCEDURE_LINKAGE_TABLE_' => 1, # sparc, alpha
59     '_SDA2_BASE_' => 1,         # powerpc
60     '_SDA_BASE_' => 1,          # powerpc
61 );
62
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;
71 }
72
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));
81
82 sub new {
83     my $this = shift;
84     my %opts=@_;
85     my $class = ref($this) || $this;
86     my $self = \%opts;
87     bless $self, $class;
88     $self->{arch} = get_host_arch() unless defined $self->{arch};
89     $self->clear();
90     if (exists $self->{file}) {
91         $self->load($self->{file}) if -e $self->{file};
92     }
93     return $self;
94 }
95
96 sub get_arch {
97     my ($self) = @_;
98     return $self->{arch};
99 }
100
101 sub clear {
102     my ($self) = @_;
103     $self->{objects} = {};
104 }
105
106 sub clear_except {
107     my ($self, @ids) = @_;
108     my %has;
109     $has{$_} = 1 foreach (@ids);
110     foreach my $objid (keys %{$self->{objects}}) {
111         delete $self->{objects}{$objid} unless exists $has{$objid};
112     }
113 }
114
115 sub get_sonames {
116     my ($self) = @_;
117     return keys %{$self->{objects}};
118 }
119
120 sub get_symbols {
121     my ($self, $soname) = @_;
122     if (defined $soname) {
123         my $obj = $self->get_object($soname);
124         return (defined $obj) ? values %{$obj->{syms}} : ();
125     } else {
126         my @syms;
127         foreach my $soname ($self->get_sonames()) {
128             push @syms, $self->get_symbols($soname);
129         }
130         return @syms;
131     }
132 }
133
134 sub get_patterns {
135     my ($self, $soname) = @_;
136     my @patterns;
137     if (defined $soname) {
138         my $obj = $self->get_object($soname);
139         foreach my $alias (values %{$obj->{patterns}{aliases}}) {
140             push @patterns, values %$alias;
141         }
142         return (@patterns, @{$obj->{patterns}{generic}});
143     } else {
144         foreach my $soname ($self->get_sonames()) {
145             push @patterns, $self->get_patterns($soname);
146         }
147         return @patterns;
148     }
149 }
150
151 # Create a symbol from the supplied string specification.
152 sub create_symbol {
153     my ($self, $spec, %opts) = @_;
154     my $symbol = (exists $opts{base}) ? $opts{base} :
155         Dpkg::Shlibs::Symbol->new();
156
157     my $ret = $opts{dummy} ? $symbol->parse_symbolspec($spec, default_minver => 0) :
158         $symbol->parse_symbolspec($spec);
159     if ($ret) {
160         $symbol->initialize(arch => $self->get_arch());
161         return $symbol;
162     }
163     return undef;
164 }
165
166 sub add_symbol {
167     my ($self, $symbol, $soname) = @_;
168     my $object = $self->get_object($soname);
169
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} = {};
174             }
175             # Alias hash for matching.
176             my $aliases = $object->{patterns}{aliases}{$alias_type};
177             $aliases->{$symbol->get_symbolname()} = $symbol;
178         } else {
179             # Otherwise assume this is a generic sequential pattern. This
180             # should be always safe.
181             push @{$object->{patterns}{generic}}, $symbol;
182         }
183         return 'pattern';
184     } else {
185         # invalidate the minimum version cache
186         $object->{minver_cache} = [];
187         $object->{syms}{$symbol->get_symbolname()} = $symbol;
188         return 'sym';
189     }
190 }
191
192 # Parameter seen is only used for recursive calls
193 sub parse {
194     my ($self, $fh, $file, $seen, $obj_ref, $base_symbol) = @_;
195
196     sub new_symbol {
197         my $base = shift || 'Dpkg::Shlibs::Symbol';
198         return (ref $base) ? $base->clone(@_) : $base->new(@_);
199     }
200
201     if (defined($seen)) {
202         return if exists $seen->{$file}; # Avoid include loops
203     } else {
204         $self->{file} = $file;
205         $seen = {};
206     }
207     $seen->{$file} = 1;
208
209     if (not ref($obj_ref)) { # Init ref to name of current object/lib
210         $$obj_ref = undef;
211     }
212
213     while (defined($_ = <$fh>)) {
214         chomp($_);
215
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, $.);
219             }
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);
225             } else {
226                 warning(_g("Failed to parse line in %s: %s"), $file, $_);
227             }
228         } elsif (/^(\(.*\))?#include\s+"([^"]+)"/) {
229             my $tagspec = $1;
230             my $filename = $2;
231             my $dir = $file;
232             my $new_base_symbol;
233             if (defined $tagspec) {
234                 $new_base_symbol = new_symbol($base_symbol);
235                 $new_base_symbol->parse_tagspec($tagspec);
236             }
237             $dir =~ s{[^/]+$}{}; # Strip filename
238             $self->load("$dir$filename", $seen, $obj_ref, $new_base_symbol);
239         } elsif (/^#|^$/) {
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*$/) {
245             # Add meta-fields
246             $self->{objects}{$$obj_ref}{fields}{field_capitalize($1)} = $2;
247         } elsif (/^(\S+)\s+(.*)$/) {
248             # New object and dependency template
249             $$obj_ref = $1;
250             if (exists $self->{objects}{$$obj_ref}) {
251                 # Update/override infos only
252                 $self->{objects}{$$obj_ref}{deps} = [ "$2" ];
253             } else {
254                 # Create a new object
255                 $self->create_object($$obj_ref, "$2");
256             }
257         } else {
258             warning(_g("Failed to parse a line in %s: %s"), $file, $_);
259         }
260     }
261     delete $seen->{$file};
262 }
263
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);
270     } else {
271         warning(_g("tried to merge the same object (%s) twice in a symfile"), $objid);
272     }
273 }
274
275 sub output {
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};
280     my $res = "";
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;
287
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;
292         }
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;
299         }
300
301         my @symbols;
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);
306         } else {
307             @symbols = $self->get_symbols($soname);
308         }
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())
322                 {
323                     print $fh "#MATCH:", $match->get_symbolspec(0), "\n" if defined $fh;
324                     $res .= "#MATCH:" . $match->get_symbolspec(0) . "\n" if defined wantarray;
325                 }
326             }
327         }
328     }
329     return $res;
330 }
331
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;
338
339     my $pattern_ok = sub {
340         my $p = shift;
341         return defined $p && ($inc_deprecated || !$p->{deprecated}) &&
342                $p->arch_is_concerned($self->get_arch());
343     };
344
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;
349
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
360                     }
361                 }
362             }
363         }
364
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)) {
369                     $pattern = $p;
370                     last;
371                 }
372             }
373         }
374         if (defined $pattern) {
375             return (wantarray) ?
376                 ( symbol => $pattern, soname => $soname ) : $pattern;
377         }
378     }
379     return (wantarray) ? () : undef;
380 }
381
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)
385 # machinery
386 sub merge_symbols {
387     my ($self, $object, $minver) = @_;
388     my $soname = $object->{SONAME} || error(_g("cannot merge symbols from objects without SONAME"));
389     my %dynsyms;
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"));
396         }
397         $dynsyms{$name} = $sym;
398     }
399
400     unless ($self->has_object($soname)) {
401         $self->create_object($soname, '');
402     }
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) {
410         my $sym;
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());
414         } else {
415             # The exact symbol is not present in the file, but it might match a 
416             # pattern.
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);
421             } else {
422                 # Symbol without any special info as no pattern matched
423                 $sym = Dpkg::Shlibs::Symbol->new(symbol => $name,
424                                                  minver => $minver);
425             }
426             $self->add_symbol($sym, $obj);
427         }
428     }
429
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());
434         }
435     }
436
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());
441     }
442 }
443
444 sub is_empty {
445     my ($self) = @_;
446     return scalar(keys %{$self->{objects}}) ? 0 : 1;
447 }
448
449 sub has_object {
450     my ($self, $soname) = @_;
451     return exists $self->{objects}{$soname};
452 }
453
454 sub get_object {
455     my ($self, $soname) = @_;
456     return ref($soname) ? $soname : $self->{objects}{$soname};
457 }
458
459 sub create_object {
460     my ($self, $soname, @deps) = @_;
461     $self->{objects}{$soname} = {
462         syms => {},
463         fields => {},
464         patterns => {
465             aliases => {},
466             generic => [],
467         },
468         deps => [ @deps ],
469         minver_cache => []
470     };
471 }
472
473 sub get_dependency {
474     my ($self, $soname, $dep_id) = @_;
475     $dep_id = 0 unless defined($dep_id);
476     return $self->get_object($soname)->{deps}[$dep_id];
477 }
478
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]));
484     my $minver;
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};
490         }
491     }
492     $so_object->{minver_cache}[$dep_id] = $minver;
493     return $minver;
494 }
495
496 sub get_dependencies {
497     my ($self, $soname) = @_;
498     return @{$self->get_object($soname)->{deps}};
499 }
500
501 sub get_field {
502     my ($self, $soname, $name) = @_;
503     if (my $obj = $self->get_object($soname)) {
504         if (exists $obj->{fields}{$name}) {
505             return $obj->{fields}{$name};
506         }
507     }
508     return undef;
509 }
510
511 # Tries to find a symbol like the $refsym and returns its descriptor.
512 # $refsym may also be a symbol name.
513 sub lookup_symbol {
514     my ($self, $refsym, $sonames, $inc_deprecated) = @_;
515     $inc_deprecated = 0 unless defined($inc_deprecated);
516     my $name = (ref $refsym) ? $refsym->get_symbolname() : $refsym;
517
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}))
522             {
523                 return (wantarray) ?
524                     ( symbol => $sym, soname => $so ) : $sym;
525             }
526         }
527     }
528     return (wantarray) ? () : undef;
529 }
530
531 # Tries to find a pattern like the $refpat and returns its descriptor.
532 # $refpat may also be a pattern spec.
533 sub lookup_pattern {
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);
538
539     if ($refpat && $refpat->is_pattern()) {
540         foreach my $soname ((ref($sonames) eq 'ARRAY') ? @$sonames : $sonames) {
541             if (my $obj = $self->get_object($soname)) {
542                 my $pat;
543                 if (my $type = $refpat->get_alias_type()) {
544                     if (exists $obj->{patterns}{aliases}{$type}) {
545                         $pat = $obj->{patterns}{aliases}{$type}{$refpat->get_symbolname()};
546                     }
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))
551                         {
552                             $pat = $p;
553                             last;
554                         }
555                     }
556                 }
557                 if ($pat && ($inc_deprecated || !$pat->{deprecated})) {
558                     return (wantarray) ?
559                         (symbol => $pat, soname => $soname) : $pat;
560                 }
561             }
562         }
563     }
564     return (wantarray) ? () : undef;
565 }
566
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);
573     }
574     return $sym;
575 }
576
577 sub get_new_symbols {
578     my ($self, $ref, %opts) = @_;
579     my $with_optional = (exists $opts{with_optional}) ?
580         $opts{with_optional} : 0;
581     my @res;
582     foreach my $soname ($self->get_sonames()) {
583         next if not $ref->has_object($soname);
584
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))
589         {
590             my $refsym = $ref->lookup_symbol($sym, $soname, 1);
591             my $isnew;
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());
596             } else {
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.
602                 $isnew = not (
603                     ($sym->get_pattern() and $ref->lookup_pattern($sym->get_pattern(), $soname, 1)) or
604                     $ref->find_matching_pattern($sym, $soname, 1)
605                 );
606             }
607             push @res, { symbol => $sym, soname => $soname } if $isnew;
608         }
609
610         # Now scan patterns
611         foreach my $p (grep { ($with_optional || ! $_->is_optional())
612                               && $_->is_legitimate($self->get_arch()) }
613                             $self->get_patterns($soname))
614         {
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()))
620             {
621                 push @res, { symbol => $p , soname => $soname };
622             }
623         }
624     }
625     return @res;
626 }
627
628 sub get_lost_symbols {
629     my ($self, $ref, %opts) = @_;
630     return $ref->get_new_symbols($self, %opts);
631 }
632
633
634 sub get_new_libs {
635     my ($self, $ref) = @_;
636     my @res;
637     foreach my $soname ($self->get_sonames()) {
638         push @res, $soname if not $ref->get_object($soname);
639     }
640     return @res;
641 }
642
643 sub get_lost_libs {
644     my ($self, $ref) = @_;
645     return $ref->get_new_libs($self);
646 }
647
648 1;