File Coverage

blib/lib/Dpkg/Shlibs/SymbolFile.pm
Criterion Covered Total %
statement 319 366 87.1
branch 159 204 77.9
condition 71 100 71.0
subroutine 35 42 83.3
pod 2 30 6.6
total 586 742 78.9


line stmt bran cond sub pod time code
1             # Copyright © 2007 Raphaël Hertzog
2             # Copyright © 2009-2010 Modestas Vainius
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 .
16              
17             package Dpkg::Shlibs::SymbolFile;
18              
19 2     2   3152 use strict;
  2         6  
  2         78  
20 2     2   20 use warnings;
  2         4  
  2         180  
21              
22             our $VERSION = '0.01';
23              
24 2     2   18 use Dpkg::Gettext;
  2         10  
  2         330  
25 2     2   16 use Dpkg::ErrorHandling;
  2         6  
  2         216  
26 2     2   1430 use Dpkg::Version;
  2         10  
  2         180  
27 2     2   1344 use Dpkg::Control::Fields;
  2         4  
  2         198  
28 2     2   1386 use Dpkg::Shlibs::Symbol;
  2         6  
  2         72  
29 2     2   14 use Dpkg::Arch qw(get_host_arch);
  2         4  
  2         132  
30              
31 2     2   16 use parent qw(Dpkg::Interface::Storable);
  2         4  
  2         10  
32              
33             # Needed by the deprecated key, which is a correct use.
34 2         8 no if $Dpkg::Version::VERSION ge '1.02',
35 2     2   146 warnings => qw(Dpkg::Version::semantic_change::overload::bool);
  2         4  
36              
37             my %internal_symbol = (
38             __bss_end__ => 1, # arm
39             __bss_end => 1, # arm
40             _bss_end__ => 1, # arm
41             __bss_start => 1, # ALL
42             __bss_start__ => 1, # arm
43             __data_start => 1, # arm
44             __do_global_ctors_aux => 1, # ia64
45             __do_global_dtors_aux => 1, # ia64
46             __do_jv_register_classes => 1, # ia64
47             _DYNAMIC => 1, # ALL
48             _edata => 1, # ALL
49             _end => 1, # ALL
50             __end__ => 1, # arm
51             __exidx_end => 1, # armel
52             __exidx_start => 1, # armel
53             _fbss => 1, # mips, mipsel
54             _fdata => 1, # mips, mipsel
55             _fini => 1, # ALL
56             _ftext => 1, # mips, mipsel
57             _GLOBAL_OFFSET_TABLE_ => 1, # hppa, mips, mipsel
58             __gmon_start__ => 1, # hppa
59             __gnu_local_gp => 1, # mips, mipsel
60             _gp => 1, # mips, mipsel
61             _init => 1, # ALL
62             _PROCEDURE_LINKAGE_TABLE_ => 1, # sparc, alpha
63             _SDA2_BASE_ => 1, # powerpc
64             _SDA_BASE_ => 1, # powerpc
65             );
66              
67             for my $i (14 .. 31) {
68             # Many powerpc specific symbols
69             $internal_symbol{"_restfpr_$i"} = 1;
70             $internal_symbol{"_restfpr_$i\_x"} = 1;
71             $internal_symbol{"_restgpr_$i"} = 1;
72             $internal_symbol{"_restgpr_$i\_x"} = 1;
73             $internal_symbol{"_savefpr_$i"} = 1;
74             $internal_symbol{"_savegpr_$i"} = 1;
75             }
76              
77             sub symbol_is_internal {
78 18206     18206 0 29233 my ($symbol, $include_groups) = @_;
79              
80 18206 100       32472 return 1 if exists $internal_symbol{$symbol};
81              
82             # The ARM Embedded ABI spec states symbols under this namespace as
83             # possibly appearing in output objects.
84 18107 100 100     21637 return 1 if not ${$include_groups}{aeabi} and $symbol =~ /^__aeabi_/;
  18107         53775  
85              
86             # The GNU implementation of the OpenMP spec, specifies symbols under
87             # this namespace as possibly appearing in output objects.
88 18099         47425 return 1 if not ${$include_groups}{gomp}
89 18099 100 100     22731 and $symbol =~ /^\.gomp_critical_user_/;
90              
91 18091         32215 return 0;
92             }
93              
94             sub new {
95 41     41 0 10654 my ($this, %opts) = @_;
96 41   33     269 my $class = ref($this) || $this;
97 41         117 my $self = \%opts;
98 41         94 bless $self, $class;
99 41   66     326 $self->{arch} //= get_host_arch();
100 41         168 $self->clear();
101 41 50       125 if (exists $self->{file}) {
102 41 100       1259 $self->load($self->{file}) if -e $self->{file};
103             }
104 41         5970 return $self;
105             }
106              
107             sub get_arch {
108 46959     46959 0 70163 my $self = shift;
109 46959         122845 return $self->{arch};
110             }
111              
112             sub clear {
113 41     41 0 179 my $self = shift;
114 41         127 $self->{objects} = {};
115             }
116              
117             sub clear_except {
118 0     0 0 0 my ($self, @ids) = @_;
119              
120 0         0 my %has = map { $_ => 1 } @ids;
  0         0  
121 0         0 foreach my $objid (keys %{$self->{objects}}) {
  0         0  
122 0 0       0 delete $self->{objects}{$objid} unless exists $has{$objid};
123             }
124             }
125              
126             sub get_sonames {
127 51     51 0 104 my $self = shift;
128 51         95 return keys %{$self->{objects}};
  51         347  
129             }
130              
131             sub get_symbols {
132 88     88 0 256 my ($self, $soname) = @_;
133 88 50       245 if (defined $soname) {
134 88         249 my $obj = $self->get_object($soname);
135 88 50       216 return (defined $obj) ? values %{$obj->{syms}} : ();
  88         8803  
136             } else {
137 0         0 my @syms;
138 0         0 foreach my $soname ($self->get_sonames()) {
139 0         0 push @syms, $self->get_symbols($soname);
140             }
141 0         0 return @syms;
142             }
143             }
144              
145             sub get_patterns {
146 59     59 0 21394 my ($self, $soname) = @_;
147 59         113 my @patterns;
148 59 50       196 if (defined $soname) {
149 59         196 my $obj = $self->get_object($soname);
150 59         247 foreach my $alias (values %{$obj->{patterns}{aliases}}) {
  59         291  
151 36         242 push @patterns, values %$alias;
152             }
153 59         150 return (@patterns, @{$obj->{patterns}{generic}});
  59         3721  
154             } else {
155 0         0 foreach my $soname ($self->get_sonames()) {
156 0         0 push @patterns, $self->get_patterns($soname);
157             }
158 0         0 return @patterns;
159             }
160             }
161              
162             # Create a symbol from the supplied string specification.
163             sub create_symbol {
164 5245     5245 0 16406 my ($self, $spec, %opts) = @_;
165             my $symbol = (exists $opts{base}) ? $opts{base} :
166 5245 100       12907 Dpkg::Shlibs::Symbol->new();
167              
168 5245 100       14265 my $ret = $opts{dummy} ? $symbol->parse_symbolspec($spec, default_minver => 0) :
169             $symbol->parse_symbolspec($spec);
170 5245 50       10616 if ($ret) {
171 5245         11507 $symbol->initialize(arch => $self->get_arch());
172 5245         14727 return $symbol;
173             }
174 0         0 return;
175             }
176              
177             sub add_symbol {
178 14433     14433 0 24625 my ($self, $symbol, $soname) = @_;
179 14433         24600 my $object = $self->get_object($soname);
180              
181 14433 100       29535 if ($symbol->is_pattern()) {
182 79 100       312 if (my $alias_type = $symbol->get_alias_type()) {
183 46   100     279 $object->{patterns}{aliases}{$alias_type} //= {};
184             # Alias hash for matching.
185 46         116 my $aliases = $object->{patterns}{aliases}{$alias_type};
186 46         126 $aliases->{$symbol->get_symbolname()} = $symbol;
187             } else {
188             # Otherwise assume this is a generic sequential pattern. This
189             # should be always safe.
190 33         70 push @{$object->{patterns}{generic}}, $symbol;
  33         92  
191             }
192 79         423 return 'pattern';
193             } else {
194             # invalidate the minimum version cache
195 14354         24447 $object->{minver_cache} = [];
196 14354         28071 $object->{syms}{$symbol->get_symbolname()} = $symbol;
197 14354         40041 return 'sym';
198             }
199             }
200              
201             sub _new_symbol {
202 5242   100 5242   18138 my $base = shift || 'Dpkg::Shlibs::Symbol';
203 5242 100       18522 return (ref $base) ? $base->clone(@_) : $base->new(@_);
204             }
205              
206             # Option state is only used for recursive calls.
207             sub parse {
208 53     53 1 239 my ($self, $fh, $file, %opts) = @_;
209 53   100     275 my $state = $opts{state} //= {};
210              
211 53 100       174 if (exists $state->{seen}) {
212 18 100       60 return if exists $state->{seen}{$file}; # Avoid include loops
213             } else {
214 35         87 $self->{file} = $file;
215 35         104 $state->{seen} = {};
216             }
217 51         169 $state->{seen}{$file} = 1;
218              
219 51 100       154 if (not ref $state->{obj_ref}) { # Init ref to name of current object/lib
220 35         57 ${$state->{obj_ref}} = undef;
  35         87  
221             }
222              
223 51         288 while (<$fh>) {
224 5323         131498 chomp;
225              
226 5323 100       18166 if (/^(?:\s+|#(?:DEPRECATED|MISSING): ([^#]+)#\s*)(.*)/) {
    100          
    100          
    100          
    100          
    50          
227 5236 50       7373 if (not defined ${$state->{obj_ref}}) {
  5236         12325  
228 0         0 error(g_('symbol information must be preceded by a header (file %s, line %s)'), $file, $.);
229             }
230             # Symbol specification
231 5236 100       11490 my $deprecated = ($1) ? Dpkg::Version->new($1) : 0;
232 5236         14556 my $sym = _new_symbol($state->{base_symbol}, deprecated => $deprecated);
233 5236 50       14109 if ($self->create_symbol($2, base => $sym)) {
234 5236         7166 $self->add_symbol($sym, ${$state->{obj_ref}});
  5236         11517  
235             } else {
236 0         0 warning(g_('failed to parse line in %s: %s'), $file, $_);
237             }
238             } elsif (/^(\(.*\))?#include\s+"([^"]+)"/) {
239 18         64 my $tagspec = $1;
240 18         64 my $filename = $2;
241 18         48 my $dir = $file;
242 18         54 my $old_base_symbol = $state->{base_symbol};
243 18         28 my $new_base_symbol;
244 18 100       60 if (defined $tagspec) {
245 6         32 $new_base_symbol = _new_symbol($old_base_symbol);
246 6         20 $new_base_symbol->parse_tagspec($tagspec);
247             }
248 18         40 $state->{base_symbol} = $new_base_symbol;
249 18         88 $dir =~ s{[^/]+$}{}; # Strip filename
250 18         182 $self->load("$dir$filename", %opts);
251 18         94 $state->{base_symbol} = $old_base_symbol;
252             } elsif (/^#|^$/) {
253             # Skip possible comments and empty lines
254             } elsif (/^\|\s*(.*)$/) {
255             # Alternative dependency template
256 14         32 push @{$self->{objects}{${$state->{obj_ref}}}{deps}}, "$1";
  14         30  
  14         106  
257             } elsif (/^\*\s*([^:]+):\s*(.*\S)\s*$/) {
258             # Add meta-fields
259 4         12 $self->{objects}{${$state->{obj_ref}}}{fields}{field_capitalize($1)} = $2;
  4         32  
260             } elsif (/^(\S+)\s+(.*)$/) {
261             # New object and dependency template
262 41         77 ${$state->{obj_ref}} = $1;
  41         214  
263 41 100       107 if (exists $self->{objects}{${$state->{obj_ref}}}) {
  41         116  
264             # Update/override infos only
265 2         8 $self->{objects}{${$state->{obj_ref}}}{deps} = [ "$2" ];
  2         12  
266             } else {
267             # Create a new object
268 39         67 $self->create_object(${$state->{obj_ref}}, "$2");
  39         229  
269             }
270             } else {
271 0         0 warning(g_('failed to parse a line in %s: %s'), $file, $_);
272             }
273             }
274 51         2261 delete $state->{seen}{$file};
275             }
276              
277             # Beware: we reuse the data structure of the provided symfile so make
278             # sure to not modify them after having called this function
279             sub merge_object_from_symfile {
280 0     0 0 0 my ($self, $src, $objid) = @_;
281 0 0       0 if (not $self->has_object($objid)) {
282 0         0 $self->{objects}{$objid} = $src->get_object($objid);
283             } else {
284 0         0 warning(g_('tried to merge the same object (%s) twice in a symfile'), $objid);
285             }
286             }
287              
288             sub output {
289 30     30 1 22714 my ($self, $fh, %opts) = @_;
290 30   100     250 $opts{template_mode} //= 0;
291 30   50     160 $opts{with_deprecated} //= 1;
292 30   50     146 $opts{with_pattern_matches} //= 0;
293 30         82 my $res = '';
294 30         108 foreach my $soname (sort $self->get_sonames()) {
295 30         138 my @deps = $self->get_dependencies($soname);
296 30         94 my $dep_first = shift @deps;
297 30 100 66     118 if (exists $opts{package} and not $opts{template_mode}) {
298 2         20 $dep_first =~ s/#PACKAGE#/$opts{package}/g;
299             }
300 30 100       90 print { $fh } "$soname $dep_first\n" if defined $fh;
  14         162  
301 30 100       478 $res .= "$soname $dep_first\n" if defined wantarray;
302              
303 30         72 foreach my $dep_next (@deps) {
304 18 100 66     76 if (exists $opts{package} and not $opts{template_mode}) {
305 2         8 $dep_next =~ s/#PACKAGE#/$opts{package}/g;
306             }
307 18 100       48 print { $fh } "| $dep_next\n" if defined $fh;
  10         38  
308 18 100       104 $res .= "| $dep_next\n" if defined wantarray;
309             }
310 30         86 my $f = $self->{objects}{$soname}{fields};
311 30         54 foreach my $field (sort keys %{$f}) {
  30         116  
312 2         4 my $value = $f->{$field};
313 2 50 33     12 if (exists $opts{package} and not $opts{template_mode}) {
314 2         6 $value =~ s/#PACKAGE#/$opts{package}/g;
315             }
316 2 50       8 print { $fh } "* $field: $value\n" if defined $fh;
  2         10  
317 2 50       84 $res .= "* $field: $value\n" if defined wantarray;
318             }
319              
320 30         114 my @symbols;
321 30 100       84 if ($opts{template_mode}) {
322             # Exclude symbols matching a pattern, but include patterns themselves
323 4         20 @symbols = grep { not $_->get_pattern() } $self->get_symbols($soname);
  104         196  
324 4         28 push @symbols, $self->get_patterns($soname);
325             } else {
326 26         96 @symbols = $self->get_symbols($soname);
327             }
328 30         322 foreach my $sym (sort { $a->get_symboltempl() cmp
  135714         234864  
329             $b->get_symboltempl() } @symbols) {
330 13980 50 66     48134 next if $sym->{deprecated} and not $opts{with_deprecated};
331             # Do not dump symbols from foreign arch unless dumping a template.
332             next if not $opts{template_mode} and
333 13980 100 100     31822 not $sym->arch_is_concerned($self->get_arch());
334             # Dump symbol specification. Dump symbol tags only in template mode.
335 13896 100       27134 print { $fh } $sym->get_symbolspec($opts{template_mode}), "\n" if defined $fh;
  4672         11570  
336 13896 100       87944 $res .= $sym->get_symbolspec($opts{template_mode}) . "\n" if defined wantarray;
337             # Dump pattern matches as comments (if requested)
338 13896 50 33     36292 if ($opts{with_pattern_matches} && $sym->is_pattern()) {
339 0         0 for my $match (sort { $a->get_symboltempl() cmp
  0         0  
340             $b->get_symboltempl() } $sym->get_pattern_matches())
341             {
342 0 0       0 print { $fh } '#MATCH:', $match->get_symbolspec(0), "\n" if defined $fh;
  0         0  
343 0 0       0 $res .= '#MATCH:' . $match->get_symbolspec(0) . "\n" if defined wantarray;
344             }
345             }
346             }
347             }
348 30         786 return $res;
349             }
350              
351             # Tries to match a symbol name and/or version against the patterns defined.
352             # Returns a pattern which matches (if any).
353             sub find_matching_pattern {
354 9632     9632 0 16405 my ($self, $refsym, $sonames, $inc_deprecated) = @_;
355 9632   50     15674 $inc_deprecated //= 0;
356 9632 100       16384 my $name = (ref $refsym) ? $refsym->get_symbolname() : $refsym;
357              
358             my $pattern_ok = sub {
359 339     339   540 my $p = shift;
360 339   33     1738 return defined $p && ($inc_deprecated || !$p->{deprecated}) &&
361             $p->arch_is_concerned($self->get_arch());
362 9632         30730 };
363              
364 9632 50       21122 foreach my $soname ((ref($sonames) eq 'ARRAY') ? @$sonames : $sonames) {
365 9632         15161 my $obj = $self->get_object($soname);
366 9632         14359 my ($type, $pattern);
367 9632 50       16619 next unless defined $obj;
368              
369 9632         15105 my $all_aliases = $obj->{patterns}{aliases};
370 9632         18670 for my $type (Dpkg::Shlibs::Symbol::ALIAS_TYPES) {
371 19223 100 66     43586 if (exists $all_aliases->{$type} && keys(%{$all_aliases->{$type}})) {
  545         2175  
372 545         930 my $aliases = $all_aliases->{$type};
373 545         1559 my $converter = $aliases->{(keys %$aliases)[0]};
374 545 50       1606 if (my $alias = $converter->convert_to_alias($name)) {
375 544 100 66     2733 if ($alias && exists $aliases->{$alias}) {
376 72         178 $pattern = $aliases->{$alias};
377 72 50       237 last if $pattern_ok->($pattern);
378 0         0 $pattern = undef; # otherwise not found yet
379             }
380             }
381             }
382             }
383              
384             # Now try generic patterns and use the first that matches
385 9631 100       16181 if (not defined $pattern) {
386 9559         11706 for my $p (@{$obj->{patterns}{generic}}) {
  9559         17777  
387 267 100 66     604 if ($pattern_ok->($p) && $p->matches_rawname($name)) {
388 123         187 $pattern = $p;
389 123         213 last;
390             }
391             }
392             }
393 9631 100       20531 if (defined $pattern) {
394 195 50       1008 return (wantarray) ?
395             ( symbol => $pattern, soname => $soname ) : $pattern;
396             }
397             }
398 9436         26118 return;
399             }
400              
401             # merge_symbols($object, $minver)
402             # Needs $Objdump->get_object($soname) as parameter
403             # Do not merge symbols found in the list of (arch-specific) internal symbols.
404             sub merge_symbols {
405 33     33 0 2297 my ($self, $object, $minver) = @_;
406              
407 33         119 my $soname = $object->{SONAME};
408 33 50       98 error(g_('cannot merge symbols from objects without SONAME'))
409             unless $soname;
410              
411 33         74 my %include_groups = ();
412 33         154 my $groups = $self->get_field($soname, 'Allow-Internal-Symbol-Groups');
413 33 100       90 if (not defined $groups) {
414 31         80 $groups = $self->get_field($soname, 'Ignore-Blacklist-Groups');
415 31 50       102 if (defined $groups) {
416 0         0 warnings::warnif('deprecated',
417             'symbols file field "Ignore-Blacklist-Groups" is deprecated, ' .
418             'use "Allow-Internal-Symbol-Groups" instead');
419             }
420             }
421 33 100       98 if (defined $groups) {
422 2         22 $include_groups{$_} = 1 foreach (split ' ', $groups);
423             }
424              
425 33         61 my %dynsyms;
426 33         225 foreach my $sym ($object->get_exported_dynamic_symbols()) {
427             my $name = $sym->{name} . '@' .
428 18206 50       75655 ($sym->{version} ? $sym->{version} : 'Base');
429 18206         28977 my $symobj = $self->lookup_symbol($name, $soname);
430 18206 100       30971 if (symbol_is_internal($sym->{name}, \%include_groups)) {
431 115 100       295 next unless defined $symobj;
432              
433 16 100       36 if ($symobj->has_tag('allow-internal')) {
    50          
434             # Allow the symbol.
435             } elsif ($symobj->has_tag('ignore-blacklist')) {
436             # Allow the symbol and warn.
437 0         0 warnings::warnif('deprecated',
438             'symbol tag "ignore-blacklist" is deprecated, ' .
439             'use "allow-internal" instead');
440             } else {
441             # Ignore the symbol.
442 8         18 next;
443             }
444             }
445 18099         48365 $dynsyms{$name} = $sym;
446             }
447              
448 33 100       1244 unless ($self->has_object($soname)) {
449 4         16 $self->create_object($soname, '');
450             }
451             # Scan all symbols provided by the objects
452 33         103 my $obj = $self->get_object($soname);
453             # invalidate the minimum version cache - it is not sufficient to
454             # invalidate in add_symbol, since we might change a minimum
455             # version for a particular symbol without adding it
456 33         121 $obj->{minver_cache} = [];
457 33         3706 foreach my $name (keys %dynsyms) {
458 18026         26009 my $sym;
459 18026 100       35984 if ($sym = $self->lookup_symbol($name, $obj, 1)) {
460             # If the symbol is already listed in the file
461 8830         19050 $sym->mark_found_in_library($minver, $self->get_arch());
462             } else {
463             # The exact symbol is not present in the file, but it might match a
464             # pattern.
465 9196         16019 my $pattern = $self->find_matching_pattern($name, $obj, 1);
466 9195 100       15862 if (defined $pattern) {
467 195         456 $pattern->mark_found_in_library($minver, $self->get_arch());
468 195         556 $sym = $pattern->create_pattern_match(symbol => $name);
469             } else {
470             # Symbol without any special info as no pattern matched
471 9000         20646 $sym = Dpkg::Shlibs::Symbol->new(symbol => $name,
472             minver => $minver);
473             }
474 9195         18008 $self->add_symbol($sym, $obj);
475             }
476             }
477              
478             # Process all symbols which could not be found in the library.
479 32         1749 foreach my $sym ($self->get_symbols($soname)) {
480 18191 100       32414 if (not exists $dynsyms{$sym->get_symbolname()}) {
481 166         402 $sym->mark_not_found_in_library($minver, $self->get_arch());
482             }
483             }
484              
485             # Deprecate patterns which didn't match anything
486 32         832 for my $pattern (grep { $_->get_pattern_matches() == 0 }
  44         125  
487             $self->get_patterns($soname)) {
488 4         15 $pattern->mark_not_found_in_library($minver, $self->get_arch());
489             }
490             }
491              
492             sub is_empty {
493 0     0 0 0 my $self = shift;
494 0 0       0 return scalar(keys %{$self->{objects}}) ? 0 : 1;
  0         0  
495             }
496              
497             sub has_object {
498 56     56 0 157 my ($self, $soname) = @_;
499 56         276 return exists $self->{objects}{$soname};
500             }
501              
502             sub get_object {
503 70035     70035 0 105071 my ($self, $soname) = @_;
504 70035 100       169030 return ref($soname) ? $soname : $self->{objects}{$soname};
505             }
506              
507             sub create_object {
508 43     43 0 155 my ($self, $soname, @deps) = @_;
509 43         678 $self->{objects}{$soname} = {
510             syms => {},
511             fields => {},
512             patterns => {
513             aliases => {},
514             generic => [],
515             },
516             deps => [ @deps ],
517             minver_cache => []
518             };
519             }
520              
521             sub get_dependency {
522 0     0 0 0 my ($self, $soname, $dep_id) = @_;
523 0   0     0 $dep_id //= 0;
524 0         0 return $self->get_object($soname)->{deps}[$dep_id];
525             }
526              
527             sub get_smallest_version {
528 4     4 0 14 my ($self, $soname, $dep_id) = @_;
529 4   50     28 $dep_id //= 0;
530 4         10 my $so_object = $self->get_object($soname);
531             return $so_object->{minver_cache}[$dep_id]
532 4 50       16 if defined $so_object->{minver_cache}[$dep_id];
533 4         8 my $minver;
534 4         12 foreach my $sym ($self->get_symbols($so_object)) {
535 16 100       46 next if $dep_id != $sym->{dep_id};
536 14   66     40 $minver //= $sym->{minver};
537 14 100       38 if (version_compare($minver, $sym->{minver}) > 0) {
538 6         22 $minver = $sym->{minver};
539             }
540             }
541 4         20 $so_object->{minver_cache}[$dep_id] = $minver;
542 4         72 return $minver;
543             }
544              
545             sub get_dependencies {
546 30     30 0 76 my ($self, $soname) = @_;
547 30         64 return @{$self->get_object($soname)->{deps}};
  30         98  
548             }
549              
550             sub get_field {
551 64     64 0 187 my ($self, $soname, $name) = @_;
552 64 100       156 if (my $obj = $self->get_object($soname)) {
553 56 100       214 if (exists $obj->{fields}{$name}) {
554 2         10 return $obj->{fields}{$name};
555             }
556             }
557 62         123 return;
558             }
559              
560             # Tries to find a symbol like the $refsym and returns its descriptor.
561             # $refsym may also be a symbol name.
562             sub lookup_symbol {
563 45604     45604 0 92473 my ($self, $refsym, $sonames, $inc_deprecated) = @_;
564 45604   100     104465 $inc_deprecated //= 0;
565 45604 100       85390 my $name = (ref $refsym) ? $refsym->get_symbolname() : $refsym;
566              
567 45604 100       90613 foreach my $so ((ref($sonames) eq 'ARRAY') ? @$sonames : $sonames) {
568 45604 100       74472 if (my $obj = $self->get_object($so)) {
569 37024         79815 my $sym = $obj->{syms}{$name};
570 37024 100 100     117345 if ($sym and ($inc_deprecated or not $sym->{deprecated}))
      100        
571             {
572 26591 100       64992 return (wantarray) ?
573             ( symbol => $sym, soname => $so ) : $sym;
574             }
575             }
576             }
577 19013         32873 return;
578             }
579              
580             # Tries to find a pattern like the $refpat and returns its descriptor.
581             # $refpat may also be a pattern spec.
582             sub lookup_pattern {
583 88     88 0 222 my ($self, $refpat, $sonames, $inc_deprecated) = @_;
584 88   100     875 $inc_deprecated //= 0;
585             # If $refsym is a string, we need to create a dummy ref symbol.
586 88 100       205 $refpat = $self->create_symbol($refpat, dummy => 1) if ! ref($refpat);
587              
588 88 50 33     285 if ($refpat && $refpat->is_pattern()) {
589 88 50       225 foreach my $soname ((ref($sonames) eq 'ARRAY') ? @$sonames : $sonames) {
590 88 50       187 if (my $obj = $self->get_object($soname)) {
591 88         134 my $pat;
592 88 100       194 if (my $type = $refpat->get_alias_type()) {
    50          
593 41 50       136 if (exists $obj->{patterns}{aliases}{$type}) {
594 41         103 $pat = $obj->{patterns}{aliases}{$type}{$refpat->get_symbolname()};
595             }
596             } elsif ($refpat->get_pattern_type() eq 'generic') {
597 47         316 for my $p (@{$obj->{patterns}{generic}}) {
  47         137  
598 120 100 100     450 if (($inc_deprecated || !$p->{deprecated}) &&
      100        
599             $p->equals($refpat, versioning => 0))
600             {
601 47         64 $pat = $p;
602 47         84 last;
603             }
604             }
605             }
606 88 100 100     415 if ($pat && ($inc_deprecated || !$pat->{deprecated})) {
      66        
607 86 50       375 return (wantarray) ?
608             (symbol => $pat, soname => $soname) : $pat;
609             }
610             }
611             }
612             }
613 2         8 return;
614             }
615              
616             # Get symbol object reference either by symbol name or by a reference object.
617             sub get_symbol_object {
618 0     0 0 0 my ($self, $refsym, $soname) = @_;
619 0         0 my $sym = $self->lookup_symbol($refsym, $soname, 1);
620 0 0       0 if (! defined $sym) {
621 0         0 $sym = $self->lookup_pattern($refsym, $soname, 1);
622             }
623 0         0 return $sym;
624             }
625              
626             sub get_new_symbols {
627 21     21 0 2904 my ($self, $ref, %opts) = @_;
628             my $with_optional = (exists $opts{with_optional}) ?
629 21 50       101 $opts{with_optional} : 0;
630 21         40 my @res;
631 21         91 foreach my $soname ($self->get_sonames()) {
632 21 50       106 next if not $ref->has_object($soname);
633              
634             # Scan raw symbols first.
635 21 100 66     81 foreach my $sym (grep { ($with_optional || ! $_->is_optional())
  9485         28663  
636             && $_->is_legitimate($self->get_arch()) }
637             $self->get_symbols($soname))
638             {
639 9301         17613 my $refsym = $ref->lookup_symbol($sym, $soname, 1);
640 9301         12069 my $isnew;
641 9301 100       14264 if (defined $refsym) {
642             # If the symbol exists in the $ref symbol file, it might
643             # still be new if $refsym is not legitimate.
644 8823         14326 $isnew = not $refsym->is_legitimate($self->get_arch());
645             } else {
646             # If the symbol does not exist in the $ref symbol file, it does
647             # not mean that it's new. It might still match a pattern in the
648             # symbol file. However, due to performance reasons, first check
649             # if the pattern that the symbol matches (if any) exists in the
650             # ref symbol file as well.
651 478   66     1013 $isnew = not (
652             ($sym->get_pattern() and $ref->lookup_pattern($sym->get_pattern(), $soname, 1)) or
653             $ref->find_matching_pattern($sym, $soname, 1)
654             );
655             }
656 9301 100       24715 push @res, { symbol => $sym, soname => $soname } if $isnew;
657             }
658              
659             # Now scan patterns
660 21 100 66     1064 foreach my $p (grep { ($with_optional || ! $_->is_optional())
  49         177  
661             && $_->is_legitimate($self->get_arch()) }
662             $self->get_patterns($soname))
663             {
664 35         97 my $refpat = $ref->lookup_pattern($p, $soname, 0);
665             # If reference pattern was not found or it is not legitimate,
666             # considering current one as new.
667 35 100 66     132 if (not defined $refpat or
668             not $refpat->is_legitimate($self->get_arch()))
669             {
670 2         12 push @res, { symbol => $p , soname => $soname };
671             }
672             }
673             }
674 21         505 return @res;
675             }
676              
677             sub get_lost_symbols {
678 13     13 0 84 my ($self, $ref, %opts) = @_;
679 13         109 return $ref->get_new_symbols($self, %opts);
680             }
681              
682              
683             sub get_new_libs {
684 0     0 0   my ($self, $ref) = @_;
685 0           my @res;
686 0           foreach my $soname ($self->get_sonames()) {
687 0 0         push @res, $soname if not $ref->get_object($soname);
688             }
689 0           return @res;
690             }
691              
692             sub get_lost_libs {
693 0     0 0   my ($self, $ref) = @_;
694 0           return $ref->get_new_libs($self);
695             }
696              
697             1;