SymbolFile.pm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. # Copyright © 2007 Raphaël Hertzog <hertzog@debian.org>
  2. # Copyright © 2009 Modestas Vainius <modestas@vainius.eu>
  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.
  7. # This program is distributed in the hope that it will be useful,
  8. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. # GNU General Public License for more details.
  11. # You should have received a copy of the GNU General Public License
  12. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. package Dpkg::Shlibs::SymbolFile;
  14. use strict;
  15. use warnings;
  16. use Dpkg::Gettext;
  17. use Dpkg::ErrorHandling;
  18. use Dpkg::Version;
  19. use Dpkg::Control::Fields;
  20. use Dpkg::Shlibs::Symbol;
  21. use Dpkg::Arch qw(get_host_arch);
  22. my %blacklist = (
  23. '__bss_end__' => 1, # arm
  24. '__bss_end' => 1, # arm
  25. '_bss_end__' => 1, # arm
  26. '__bss_start' => 1, # ALL
  27. '__bss_start__' => 1, # arm
  28. '__data_start' => 1, # arm
  29. '__do_global_ctors_aux' => 1, # ia64
  30. '__do_global_dtors_aux' => 1, # ia64
  31. '__do_jv_register_classes' => 1,# ia64
  32. '_DYNAMIC' => 1, # ALL
  33. '_edata' => 1, # ALL
  34. '_end' => 1, # ALL
  35. '__end__' => 1, # arm
  36. '__exidx_end' => 1, # armel
  37. '__exidx_start' => 1, # armel
  38. '_fbss' => 1, # mips, mipsel
  39. '_fdata' => 1, # mips, mipsel
  40. '_fini' => 1, # ALL
  41. '_ftext' => 1, # mips, mipsel
  42. '_GLOBAL_OFFSET_TABLE_' => 1, # hppa, mips, mipsel
  43. '__gmon_start__' => 1, # hppa
  44. '__gnu_local_gp' => 1, # mips, mipsel
  45. '_gp' => 1, # mips, mipsel
  46. '_init' => 1, # ALL
  47. '_PROCEDURE_LINKAGE_TABLE_' => 1, # sparc, alpha
  48. '_SDA2_BASE_' => 1, # powerpc
  49. '_SDA_BASE_' => 1, # powerpc
  50. );
  51. for (my $i = 14; $i <= 31; $i++) {
  52. # Many powerpc specific symbols
  53. $blacklist{"_restfpr_$i"} = 1;
  54. $blacklist{"_restfpr_$i\_x"} = 1;
  55. $blacklist{"_restgpr_$i"} = 1;
  56. $blacklist{"_restgpr_$i\_x"} = 1;
  57. $blacklist{"_savefpr_$i"} = 1;
  58. $blacklist{"_savegpr_$i"} = 1;
  59. }
  60. # Many armel-specific symbols
  61. $blacklist{"__aeabi_$_"} = 1 foreach (qw(cdcmpeq cdcmple cdrcmple cfcmpeq
  62. cfcmple cfrcmple d2f d2iz d2lz d2uiz d2ulz dadd dcmpeq dcmpge dcmpgt
  63. dcmple dcmplt dcmpun ddiv dmul dneg drsub dsub f2d f2iz f2lz f2uiz f2ulz
  64. fadd fcmpeq fcmpge fcmpgt fcmple fcmplt fcmpun fdiv fmul fneg frsub fsub
  65. i2d i2f idiv idivmod l2d l2f lasr lcmp ldivmod llsl llsr lmul ui2d ui2f
  66. uidiv uidivmod ul2d ul2f ulcmp uldivmod unwind_cpp_pr0 unwind_cpp_pr1
  67. unwind_cpp_pr2 uread4 uread8 uwrite4 uwrite8));
  68. sub new {
  69. my $this = shift;
  70. my %opts=@_;
  71. my $class = ref($this) || $this;
  72. my $self = \%opts;
  73. bless $self, $class;
  74. $self->{arch} = get_host_arch() unless exists $self->{arch};
  75. $self->clear();
  76. if (exists $self->{file}) {
  77. $self->load($self->{file}) if -e $self->{file};
  78. }
  79. return $self;
  80. }
  81. sub clear {
  82. my ($self) = @_;
  83. $self->{objects} = {};
  84. $self->{used_wildcards} = 0;
  85. }
  86. sub clear_except {
  87. my ($self, @ids) = @_;
  88. my %has;
  89. $has{$_} = 1 foreach (@ids);
  90. foreach my $objid (keys %{$self->{objects}}) {
  91. delete $self->{objects}{$objid} unless exists $has{$objid};
  92. }
  93. }
  94. sub add_symbol {
  95. my ($self, $soname, $symbol) = @_;
  96. my $object = (ref $soname) ? $soname : $self->{objects}{$soname};
  97. if (!$symbol->{deprecated} && (my $ver = $symbol->get_wildcard_version())) {
  98. error(_g("you can't use wildcards on unversioned symbols: %s"), $_) if $ver eq "Base";
  99. $object->{wildcards}{$ver} = $symbol;
  100. return 'wildcards';
  101. } else {
  102. # invalidate the minimum version cache
  103. $object->{minver_cache} = [];
  104. $object->{syms}{$symbol->get_symbolname()} = $symbol;
  105. return 'syms';
  106. }
  107. }
  108. # Parameter seen is only used for recursive calls
  109. sub load {
  110. my ($self, $file, $seen, $obj_ref, $base_symbol) = @_;
  111. sub new_symbol {
  112. my $base = shift || 'Dpkg::Shlibs::Symbol';
  113. return (ref $base) ? $base->clone(@_) : $base->new(@_);
  114. }
  115. if (defined($seen)) {
  116. return if exists $seen->{$file}; # Avoid include loops
  117. } else {
  118. $self->{file} = $file;
  119. $seen = {};
  120. }
  121. $seen->{$file} = 1;
  122. if (not ref($obj_ref)) { # Init ref to name of current object/lib
  123. $$obj_ref = undef;
  124. }
  125. open(my $sym_file, "<", $file)
  126. || syserr(_g("cannot open %s"), $file);
  127. while (defined($_ = <$sym_file>)) {
  128. chomp($_);
  129. if (/^(?:\s+|#(?:DEPRECATED|MISSING): ([^#]+)#\s*)(.*)/) {
  130. if (not defined ($$obj_ref)) {
  131. error(_g("Symbol information must be preceded by a header (file %s, line %s)."), $file, $.);
  132. }
  133. # Symbol specification
  134. my $deprecated = ($1) ? $1 : 0;
  135. my $sym = new_symbol($base_symbol, deprecated => $deprecated);
  136. if ($sym->parse($2)) {
  137. $sym->process_tags(arch => $self->{arch});
  138. $self->add_symbol($$obj_ref, $sym);
  139. } else {
  140. warning(_g("Failed to parse line in %s: %s"), $file, $_);
  141. }
  142. } elsif (/^(\(.*\))?#include\s+"([^"]+)"/) {
  143. my $tagspec = $1;
  144. my $filename = $2;
  145. my $dir = $file;
  146. my $new_base_symbol;
  147. if (defined $tagspec) {
  148. $new_base_symbol = new_symbol($base_symbol);
  149. $new_base_symbol->parse_tagspec($tagspec);
  150. }
  151. $dir =~ s{[^/]+$}{}; # Strip filename
  152. $self->load("$dir$filename", $seen, $obj_ref, $new_base_symbol);
  153. } elsif (/^#/) {
  154. # Skip possible comments
  155. } elsif (/^\|\s*(.*)$/) {
  156. # Alternative dependency template
  157. push @{$self->{objects}{$$obj_ref}{deps}}, "$1";
  158. } elsif (/^\*\s*([^:]+):\s*(.*\S)\s*$/) {
  159. # Add meta-fields
  160. $self->{objects}{$$obj_ref}{fields}{field_capitalize($1)} = $2;
  161. } elsif (/^(\S+)\s+(.*)$/) {
  162. # New object and dependency template
  163. $$obj_ref = $1;
  164. if (exists $self->{objects}{$$obj_ref}) {
  165. # Update/override infos only
  166. $self->{objects}{$$obj_ref}{deps} = [ "$2" ];
  167. } else {
  168. # Create a new object
  169. $self->create_object($$obj_ref, "$2");
  170. }
  171. } else {
  172. warning(_g("Failed to parse a line in %s: %s"), $file, $_);
  173. }
  174. }
  175. close($sym_file);
  176. delete $seen->{$file};
  177. }
  178. # Beware: we reuse the data structure of the provided symfile so make
  179. # sure to not modify them after having called this function
  180. sub merge_object_from_symfile {
  181. my ($self, $src, $objid) = @_;
  182. if (not $self->has_object($objid)) {
  183. $self->{objects}{$objid} = $src->{objects}{$objid};
  184. } else {
  185. warning(_g("Tried to merge the same object (%s) twice in a symfile."), $objid);
  186. }
  187. }
  188. sub save {
  189. my ($self, $file, %opts) = @_;
  190. $file = $self->{file} unless defined($file);
  191. my $fh;
  192. if ($file eq "-") {
  193. $fh = \*STDOUT;
  194. } else {
  195. open($fh, ">", $file)
  196. || syserr(_g("cannot write %s"), $file);
  197. }
  198. $self->dump($fh, %opts);
  199. close($fh) if ($file ne "-");
  200. }
  201. sub dump {
  202. my ($self, $fh, %opts) = @_;
  203. $opts{template_mode} = 0 unless exists $opts{template_mode};
  204. $opts{with_deprecated} = 1 unless exists $opts{with_deprecated};
  205. foreach my $soname (sort keys %{$self->{objects}}) {
  206. my @deps = @{$self->{objects}{$soname}{deps}};
  207. my $dep = shift @deps;
  208. $dep =~ s/#PACKAGE#/$opts{package}/g if exists $opts{package};
  209. print $fh "$soname $dep\n";
  210. foreach $dep (@deps) {
  211. $dep =~ s/#PACKAGE#/$opts{package}/g if exists $opts{package};
  212. print $fh "| $dep\n";
  213. }
  214. my $f = $self->{objects}{$soname}{fields};
  215. foreach my $field (sort keys %{$f}) {
  216. my $value = $f->{$field};
  217. $value =~ s/#PACKAGE#/$opts{package}/g if exists $opts{package};
  218. print $fh "* $field: $value\n";
  219. }
  220. my $syms = $self->{objects}{$soname}{syms};
  221. foreach my $name (sort { $syms->{$a}->get_symboltempl() cmp
  222. $syms->{$b}->get_symboltempl() } keys %$syms) {
  223. my $sym = $self->{objects}{$soname}{syms}{$name};
  224. next if $sym->{deprecated} and not $opts{with_deprecated};
  225. # Do not dump symbols from foreign arch unless dumping a template.
  226. next if not $opts{template_mode} and
  227. not $sym->arch_is_concerned($self->{arch});
  228. # Dump symbol specification. Dump symbol tags only in template mode.
  229. print $fh $sym->get_symbolspec($opts{template_mode}) . "\n";
  230. }
  231. }
  232. }
  233. # merge_symbols($object, $minver)
  234. # Needs $Objdump->get_object($soname) as parameter
  235. # Don't merge blacklisted symbols related to the internal (arch-specific)
  236. # machinery
  237. sub merge_symbols {
  238. my ($self, $object, $minver) = @_;
  239. my $soname = $object->{SONAME} || error(_g("Can't merge symbols from objects without SONAME."));
  240. my %dynsyms;
  241. foreach my $sym ($object->get_exported_dynamic_symbols()) {
  242. my $name = $sym->{name} . '@' .
  243. ($sym->{version} ? $sym->{version} : "Base");
  244. my $symobj = $self->lookup_symbol($name, [ $soname ]);
  245. if (exists $blacklist{$sym->{name}}) {
  246. next unless (defined $symobj and $symobj->has_tag("ignore-blacklist"));
  247. }
  248. $dynsyms{$name} = $sym;
  249. }
  250. unless (exists $self->{objects}{$soname}) {
  251. $self->create_object($soname, '');
  252. }
  253. # Scan all symbols provided by the objects
  254. my $obj = $self->{objects}{$soname};
  255. # invalidate the minimum version cache - it is not sufficient to
  256. # invalidate in add_symbol, since we might change a minimum
  257. # version for a particular symbol without adding it
  258. $obj->{minver_cache} = [];
  259. foreach my $name (keys %dynsyms) {
  260. my $sym;
  261. if (exists $obj->{syms}{$name}) {
  262. # If the symbol is already listed in the file
  263. $sym = $obj->{syms}{$name};
  264. if ($sym->{deprecated}) {
  265. # Symbol reappeared somehow
  266. $sym->{deprecated} = 0;
  267. $sym->{minver} = $minver if (not $sym->is_optional());
  268. } else {
  269. # We assume that the right dependency information is already
  270. # there.
  271. if (version_compare($minver, $sym->{minver}) < 0) {
  272. $sym->{minver} = $minver;
  273. }
  274. }
  275. if (not $sym->arch_is_concerned($self->{arch})) {
  276. # Remove arch tag because it is incorrect.
  277. $sym->delete_tag('arch');
  278. }
  279. } else {
  280. # The symbol is new and not present in the file
  281. my $symobj = $dynsyms{$name};
  282. $sym = $self->symbol_match_wildcard($soname, $name, $symobj->{version});
  283. if (not defined $sym) {
  284. # Symbol without any special info as no wildcard did match
  285. $sym = Dpkg::Shlibs::Symbol->new(symbol => $name,
  286. minver => $minver);
  287. }
  288. $self->add_symbol($obj, $sym);
  289. }
  290. }
  291. # Scan all symbols in the file and mark as deprecated those that are
  292. # no more provided (only if the minver is bigger than the version where
  293. # the symbol was introduced)
  294. foreach my $name (keys %{$self->{objects}{$soname}{syms}}) {
  295. if (not exists $dynsyms{$name}) {
  296. my $sym = $self->{objects}{$soname}{syms}{$name};
  297. # Ignore symbols from foreign arch
  298. next if not $sym->arch_is_concerned($self->{arch});
  299. if ($sym->{deprecated}) {
  300. # Bump deprecated if the symbol is optional so that it
  301. # keeps reappering in the diff while it's missing
  302. $sym->{deprecated} = $minver if $sym->is_optional();
  303. } elsif (version_compare($minver, $sym->{minver}) > 0) {
  304. $sym->{deprecated} = $minver;
  305. }
  306. }
  307. }
  308. }
  309. sub symbol_match_wildcard {
  310. my ($self, $soname, $name, $version) = @_;
  311. my $obj = $self->{objects}{$soname};
  312. if ($version and exists $obj->{wildcards}{$version}) {
  313. my $w_sym = $obj->{wildcards}{$version};
  314. return undef unless $w_sym->arch_is_concerned($self->{arch});
  315. $self->{used_wildcards}++;
  316. return $w_sym->clone(symbol => $name);
  317. }
  318. return undef;
  319. }
  320. sub is_empty {
  321. my ($self) = @_;
  322. return scalar(keys %{$self->{objects}}) ? 0 : 1;
  323. }
  324. sub has_object {
  325. my ($self, $soname) = @_;
  326. return exists $self->{objects}{$soname};
  327. }
  328. sub create_object {
  329. my ($self, $soname, @deps) = @_;
  330. $self->{objects}{$soname} = {
  331. syms => {},
  332. fields => {},
  333. wildcards => {},
  334. deps => [ @deps ],
  335. minver_cache => []
  336. };
  337. }
  338. sub get_dependency {
  339. my ($self, $soname, $dep_id) = @_;
  340. $dep_id = 0 unless defined($dep_id);
  341. return $self->{objects}{$soname}{deps}[$dep_id];
  342. }
  343. sub get_smallest_version {
  344. my ($self, $soname, $dep_id) = @_;
  345. $dep_id = 0 unless defined($dep_id);
  346. my $so_object = $self->{objects}{$soname};
  347. return $so_object->{minver_cache}[$dep_id] if(defined($so_object->{minver_cache}[$dep_id]));
  348. my $minver;
  349. foreach my $sym (values %{$so_object->{syms}}) {
  350. next if $dep_id != $sym->{dep_id};
  351. $minver = $sym->{minver} unless defined($minver);
  352. if (version_compare($minver, $sym->{minver}) > 0) {
  353. $minver = $sym->{minver};
  354. }
  355. }
  356. $so_object->{minver_cache}[$dep_id] = $minver;
  357. return $minver;
  358. }
  359. sub get_dependencies {
  360. my ($self, $soname) = @_;
  361. return @{$self->{objects}{$soname}{deps}};
  362. }
  363. sub get_field {
  364. my ($self, $soname, $name) = @_;
  365. if (exists $self->{objects}{$soname}{fields}{$name}) {
  366. return $self->{objects}{$soname}{fields}{$name};
  367. }
  368. return undef;
  369. }
  370. sub contains_wildcards {
  371. my ($self) = @_;
  372. my $res = 0;
  373. foreach my $soname (sort keys %{$self->{objects}}) {
  374. if (scalar keys %{$self->{objects}{$soname}{wildcards}}) {
  375. $res = 1;
  376. }
  377. }
  378. return $res;
  379. }
  380. sub used_wildcards {
  381. my ($self) = @_;
  382. return $self->{used_wildcards};
  383. }
  384. sub lookup_symbol {
  385. my ($self, $name, $sonames, $inc_deprecated) = @_;
  386. $inc_deprecated = 0 unless defined($inc_deprecated);
  387. foreach my $so (@{$sonames}) {
  388. next if (! exists $self->{objects}{$so});
  389. if (exists $self->{objects}{$so}{syms}{$name} and
  390. ($inc_deprecated or not
  391. $self->{objects}{$so}{syms}{$name}{deprecated}))
  392. {
  393. my $dep_id = $self->{objects}{$so}{syms}{$name}{dep_id};
  394. my $clone = $self->{objects}{$so}{syms}{$name}->clone();
  395. $clone->{depends} = $self->{objects}{$so}{deps}[$dep_id];
  396. $clone->{soname} = $so;
  397. return $clone;
  398. }
  399. }
  400. return undef;
  401. }
  402. sub get_new_symbols {
  403. my ($self, $ref) = @_;
  404. my @res;
  405. foreach my $soname (keys %{$self->{objects}}) {
  406. my $mysyms = $self->{objects}{$soname}{syms};
  407. next if not exists $ref->{objects}{$soname};
  408. my $refsyms = $ref->{objects}{$soname}{syms};
  409. foreach my $sym (grep { not $mysyms->{$_}{deprecated} and
  410. not $mysyms->{$_}->is_optional() and
  411. $mysyms->{$_}->arch_is_concerned($self->{arch})
  412. } keys %{$mysyms})
  413. {
  414. if ((not exists $refsyms->{$sym}) or
  415. $refsyms->{$sym}{deprecated} or
  416. not $refsyms->{$sym}->arch_is_concerned($self->{arch}) )
  417. {
  418. push @res, $mysyms->{$sym}->clone(soname => $soname);
  419. }
  420. }
  421. }
  422. return @res;
  423. }
  424. sub get_lost_symbols {
  425. my ($self, $ref) = @_;
  426. return $ref->get_new_symbols($self);
  427. }
  428. sub get_new_libs {
  429. my ($self, $ref) = @_;
  430. my @res;
  431. foreach my $soname (keys %{$self->{objects}}) {
  432. push @res, $soname if not exists $ref->{objects}{$soname};
  433. }
  434. return @res;
  435. }
  436. sub get_lost_libs {
  437. my ($self, $ref) = @_;
  438. return $ref->get_new_libs($self);
  439. }
  440. 1;