SymbolFile.pm 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. # Copyright (C) 2007 Raphael Hertzog
  2. # This program is free software; you can redistribute it and/or modify
  3. # it under the terms of the GNU General Public License as published by
  4. # the Free Software Foundation; either version 2 of the License, or
  5. # (at your option) any later version.
  6. # This program is distributed in the hope that it will be useful,
  7. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. # GNU General Public License for more details.
  10. # You should have received a copy of the GNU General Public License along
  11. # with this program; if not, write to the Free Software Foundation, Inc.,
  12. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  13. package Dpkg::Shlibs::SymbolFile;
  14. use Dpkg::Gettext;
  15. use Dpkg::ErrorHandling qw(syserr warning error);
  16. use Dpkg::Version qw(vercmp);
  17. textdomain("dpkg-dev");
  18. sub new {
  19. my $this = shift;
  20. my $file = shift;
  21. my $class = ref($this) || $this;
  22. my $self = { };
  23. bless $self, $class;
  24. if (defined($file) ) {
  25. $self->{file} = $file;
  26. $self->load($file) if -e $file;
  27. }
  28. return $self;
  29. }
  30. sub clear {
  31. my ($self) = @_;
  32. $self->{objects} = {};
  33. }
  34. sub clear_except {
  35. my ($self, @ids) = @_;
  36. my %has;
  37. $has{$_} = 1 foreach (@ids);
  38. foreach my $objid (keys %{$self->{objects}}) {
  39. delete $self->{objects}{$objid} unless exists $has{$objid};
  40. }
  41. }
  42. # Parameter seen is only used for recursive calls
  43. sub load {
  44. my ($self, $file, $seen) = @_;
  45. if (defined($seen)) {
  46. return if exists $seen->{$file}; # Avoid include loops
  47. } else {
  48. $self->{file} = $file;
  49. $seen = {};
  50. }
  51. $seen->{$file} = 1;
  52. open(my $sym_file, "<", $file)
  53. || syserr(sprintf(_g("Can't open %s: %s"), $file));
  54. my ($object);
  55. while (defined($_ = <$sym_file>)) {
  56. chomp($_);
  57. if (/^\s+(\S+)\s(\S+)(?:\s(\d+))?/) {
  58. if (not defined ($object)) {
  59. error(sprintf(_g("Symbol information must be preceded by a header (file %s, line %s).", $file, $.)));
  60. }
  61. # New symbol
  62. my $sym = {
  63. minver => $2,
  64. dep_id => defined($3) ? $3 : 0,
  65. deprecated => 0
  66. };
  67. $self->{objects}{$object}{syms}{$1} = $sym;
  68. } elsif (/^#include\s+"([^"]+)"/) {
  69. my $filename = $1;
  70. my $dir = $file;
  71. $dir =~ s{[^/]+$}{}; # Strip filename
  72. $self->load("$dir$filename", $seen);
  73. } elsif (/^#DEPRECATED: ([^#]+)#\s*(\S+)\s(\S+)(?:\s(\d+))?/) {
  74. my $sym = {
  75. minver => $3,
  76. dep_id => defined($4) ? $4 : 0,
  77. deprecated => $1
  78. };
  79. $self->{objects}{$object}{syms}{$2} = $sym;
  80. } elsif (/^#/) {
  81. # Skip possible comments
  82. } elsif (/^\|\s*(.*)$/) {
  83. # Alternative dependency template
  84. push @{$self->{objects}{$object}{deps}}, "$1";
  85. } elsif (/^(\S+)\s+(.*)$/) {
  86. # New object and dependency template
  87. $object = $1;
  88. if (exists $self->{objects}{$object}) {
  89. # Update/override infos only
  90. $self->{objects}{$object}{deps} = [ "$2" ];
  91. } else {
  92. # Create a new object
  93. $self->{objects}{$object} = {
  94. syms => {},
  95. deps => [ "$2" ]
  96. };
  97. }
  98. } else {
  99. warning(sprintf(_g("Failed to parse a line in %s: %s"), $file, $_));
  100. }
  101. }
  102. close($sym_file);
  103. }
  104. sub save {
  105. my ($self, $file) = @_;
  106. $file = $self->{file} unless defined($file);
  107. my $fh;
  108. if ($file eq "-") {
  109. $fh = \*STDOUT;
  110. } else {
  111. open($fh, ">", $file)
  112. || syserr(sprintf(_g("Can't open %s for writing: %s"), $file, $!));
  113. }
  114. $self->dump($fh);
  115. close($fh) if ($file ne "-");
  116. }
  117. sub dump {
  118. my ($self, $fh) = @_;
  119. foreach my $soname (sort keys %{$self->{objects}}) {
  120. print $fh "$soname $self->{objects}{$soname}{deps}[0]\n";
  121. print $fh "| $_" foreach (@{$self->{objects}{$soname}{deps}}[ 1 .. -1 ]);
  122. foreach my $sym (sort keys %{$self->{objects}{$soname}{syms}}) {
  123. my $info = $self->{objects}{$soname}{syms}{$sym};
  124. print $fh "#DEPRECATED: $info->{deprecated}#" if $info->{deprecated};
  125. print $fh " $sym $info->{minver}";
  126. print $fh " $info->{dep_id}" if $info->{dep_id};
  127. print $fh "\n";
  128. }
  129. }
  130. }
  131. # merge_symbols($object, $minver)
  132. # Needs $Objdump->get_object($soname) as parameter
  133. sub merge_symbols {
  134. my ($self, $object, $minver) = @_;
  135. my $soname = $object->{SONAME} || error(_g("Can't merge symbols from objects without SONAME."));
  136. my %dynsyms = map { $_ => $object->{dynsyms}{$_} }
  137. grep { local $a = $object->{dynsyms}{$_}; $a->{dynamic} && $a->{defined} }
  138. keys %{$object->{dynsyms}};
  139. unless ($self->{objects}{$soname}) {
  140. $self->create_object($soname, '');
  141. }
  142. # Scan all symbols provided by the objects
  143. foreach my $sym (keys %dynsyms) {
  144. if (exists $self->{objects}{$soname}{syms}{$sym}) {
  145. # If the symbol is already listed in the file
  146. my $info = $self->{objects}{$soname}{syms}{$sym};
  147. if ($info->{deprecated}) {
  148. # Symbol reappeared somehow
  149. $info->{deprecated} = 0;
  150. $info->{minver} = $minver;
  151. next;
  152. }
  153. # We assume that the right dependency information is already
  154. # there.
  155. if (vercmp($minver, $info->{minver}) < 0) {
  156. $info->{minver} = $minver;
  157. }
  158. } else {
  159. # The symbol is new and not present in the file
  160. my $info = {
  161. minver => $minver,
  162. deprecated => 0,
  163. dep_id => 0
  164. };
  165. $self->{objects}{$soname}{syms}{$sym} = $info;
  166. }
  167. }
  168. # Scan all symbols in the file and mark as deprecated those that are
  169. # no more provided
  170. foreach my $sym (keys %{$self->{objects}{$soname}{syms}}) {
  171. if (! exists $dynsyms{$sym}) {
  172. $self->{objects}{$soname}{syms}{$sym}{deprecated} = $minver;
  173. }
  174. }
  175. }
  176. sub is_empty {
  177. my ($self) = @_;
  178. return scalar(keys %{$self->{objects}}) ? 0 : 1;
  179. }
  180. sub has_object {
  181. my ($self, $soname) = @_;
  182. return exists $self->{objects}{$soname};
  183. }
  184. sub create_object {
  185. my ($self, $soname, @deps) = @_;
  186. $self->{objects}{$soname} = {
  187. syms => {},
  188. deps => [ @deps ]
  189. };
  190. }
  191. sub get_dependency {
  192. my ($self, $soname, $dep_id) = @_;
  193. $dep_id = 0 unless defined($dep_id);
  194. return $self->{objects}{$soname}{deps}[$dep_id];
  195. }
  196. sub lookup_symbol {
  197. my ($self, $name, $sonames) = @_;
  198. foreach my $so (@{$sonames}) {
  199. next if (! exists $self->{objects}{$so});
  200. if (exists $self->{objects}{$so}{syms}{$name} and
  201. not $self->{objects}{$so}{syms}{$name}{deprecated})
  202. {
  203. my $dep_id = $self->{objects}{$so}{syms}{$name}{dep_id};
  204. return {
  205. 'depends' => $self->{objects}{$so}{deps}[$dep_id],
  206. 'soname' => $so,
  207. %{$self->{objects}{$so}{syms}{$name}}
  208. };
  209. }
  210. }
  211. return undef;
  212. }
  213. sub has_new_symbols {
  214. my ($self, $ref) = @_;
  215. foreach my $soname (keys %{$self->{objects}}) {
  216. my $mysyms = $self->{objects}{$soname}{syms};
  217. next if not exists $ref->{objects}{$soname};
  218. my $refsyms = $ref->{objects}{$soname}{syms};
  219. foreach my $sym (grep { not $mysyms->{$_}{deprecated} }
  220. keys %{$mysyms})
  221. {
  222. if ((not exists $refsyms->{$sym}) or
  223. $refsyms->{$sym}{deprecated})
  224. {
  225. return 1;
  226. }
  227. }
  228. }
  229. return 0;
  230. }
  231. sub has_lost_symbols {
  232. my ($self, $ref) = @_;
  233. return $ref->has_new_symbols($self);
  234. }
  235. sub has_new_libs {
  236. my ($self, $ref) = @_;
  237. foreach my $soname (keys %{$self->{objects}}) {
  238. return 1 if not exists $ref->{objects}{$soname};
  239. }
  240. return 0;
  241. }
  242. sub has_lost_libs {
  243. my ($self, $ref) = @_;
  244. return $ref->has_new_libs($self);
  245. }
  246. 1;