Symbol.pm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. # Copyright © 2007 Raphaël Hertzog <hertzog@debian.org>
  2. # Copyright © 2009 Modestas Vainius <modestas@vainius.eu>
  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. package Dpkg::Shlibs::Symbol;
  17. use strict;
  18. use warnings;
  19. use Dpkg::Gettext;
  20. use Dpkg::Deps;
  21. use Dpkg::ErrorHandling;
  22. use Storable qw();
  23. use Dpkg::Shlibs::Cppfilt;
  24. sub new {
  25. my $this = shift;
  26. my $class = ref($this) || $this;
  27. my %args = @_;
  28. my $self = bless {
  29. symbol => undef,
  30. symbol_templ => undef,
  31. minver => undef,
  32. dep_id => 0,
  33. deprecated => 0,
  34. tags => {},
  35. tagorder => [],
  36. }, $class;
  37. $self->{$_} = $args{$_} foreach keys %args;
  38. return $self;
  39. }
  40. # Shallow clone
  41. sub sclone {
  42. my $self = shift;
  43. my $clone = { %$self };
  44. if (@_) {
  45. my %args=@_;
  46. $clone->{$_} = $args{$_} foreach keys %args;
  47. }
  48. return bless $clone, ref $self;
  49. }
  50. # Deep clone
  51. sub dclone {
  52. my $self = shift;
  53. my $clone = Storable::dclone($self);
  54. if (@_) {
  55. my %args=@_;
  56. $clone->{$_} = $args{$_} foreach keys %args;
  57. }
  58. return $clone;
  59. }
  60. sub parse_tagspec {
  61. my ($self, $tagspec) = @_;
  62. if ($tagspec =~ /^\((.*?)\)(.*)$/ && $1) {
  63. # (tag1=t1 value|tag2|...|tagN=tNp)
  64. # Symbols ()|= cannot appear in the tag names and values
  65. my $tagspec = $1;
  66. my $rest = ($2) ? $2 : "";
  67. my @tags = split(/\|/, $tagspec);
  68. # Parse each tag
  69. for my $tag (@tags) {
  70. if ($tag =~ /^(.*)=(.*)$/) {
  71. # Tag with value
  72. $self->add_tag($1, $2);
  73. } else {
  74. # Tag without value
  75. $self->add_tag($tag, undef);
  76. }
  77. }
  78. return $rest;
  79. }
  80. return undef;
  81. }
  82. sub parse {
  83. my ($self, $symbolspec) = @_;
  84. my $symbol;
  85. my $symbol_templ;
  86. my $symbol_quoted;
  87. my $rest;
  88. if (defined($symbol = $self->parse_tagspec($symbolspec))) {
  89. # (tag1=t1 value|tag2|...|tagN=tNp)"Foo::Bar::foobar()"@Base 1.0 1
  90. # Symbols ()|= cannot appear in the tag names and values
  91. # If the tag specification exists symbol name template might be quoted too
  92. if ($symbol =~ /^(['"])/ && $symbol =~ /^($1)(.*?)$1(.*)$/) {
  93. $symbol_quoted = $1;
  94. $symbol_templ = $2;
  95. $symbol = $2;
  96. $rest = $3;
  97. } else {
  98. if ($symbol =~ m/^(\S+)(.*)$/) {
  99. $symbol_templ = $1;
  100. $symbol = $1;
  101. $rest = $2;
  102. }
  103. }
  104. error(_g("symbol name unspecified: %s"), $symbolspec) if (!$symbol);
  105. } else {
  106. # No tag specification. Symbol name is up to the first space
  107. # foobarsymbol@Base 1.0 1
  108. if ($symbolspec =~ m/^(\S+)(.*)$/) {
  109. $symbol = $1;
  110. $rest = $2;
  111. } else {
  112. return 0;
  113. }
  114. }
  115. $self->{symbol} = $symbol;
  116. $self->{symbol_templ} = $symbol_templ;
  117. $self->{symbol_quoted} = $symbol_quoted if ($symbol_quoted);
  118. # Now parse "the rest" (minver and dep_id)
  119. if ($rest =~ /^\s(\S+)(?:\s(\d+))?/) {
  120. $self->{minver} = $1;
  121. $self->{dep_id} = defined($2) ? $2 : 0;
  122. } else {
  123. return 0;
  124. }
  125. return 1;
  126. }
  127. # A hook for symbol initialization (typically processing of tags). The code
  128. # here may even change symbol name. Called from
  129. # Dpkg::Shlibs::SymbolFile::load().
  130. sub initialize {
  131. my $self = shift;
  132. # Look for tags marking symbol patterns. The pattern may match multiple
  133. # real symbols.
  134. if ($self->has_tag('c++')) {
  135. # Raw symbol name is always demangled to the same alias while demangled
  136. # symbol name cannot be reliably converted back to raw symbol name.
  137. # Therefore, we can use hash for mapping.
  138. $self->init_pattern('alias-c++'); # Alias subtype is c++.
  139. }
  140. # Wildcard is an alias based pattern. It gets recognized here even if it is
  141. # not specially tagged.
  142. if (my $ver = $self->get_wildcard_version()) {
  143. error(_g("you can't use wildcards on unversioned symbols: %s"), $_) if $ver eq "Base";
  144. $self->init_pattern(($self->is_pattern()) ? 'generic' : 'alias-wildcard');
  145. $self->{pattern}{wildcard} = 1;
  146. }
  147. # As soon as regex is involved, we need to match each real
  148. # symbol against each pattern (aka 'generic' pattern).
  149. if ($self->has_tag('regex')) {
  150. $self->init_pattern('generic');
  151. # Pre-compile regular expression for better performance.
  152. my $regex = $self->get_symbolname();
  153. $self->{pattern}{regex} = qr/$regex/;
  154. }
  155. }
  156. sub get_symbolname {
  157. return $_[0]->{symbol};
  158. }
  159. sub get_symboltempl {
  160. return $_[0]->{symbol_templ} || $_[0]->{symbol};
  161. }
  162. sub set_symbolname {
  163. my ($self, $name, $quoted) = @_;
  164. if (defined $name) {
  165. $self->{symbol} = $name;
  166. }
  167. $self->{symbol_templ} = undef;
  168. if ($quoted) {
  169. $self->{symbol_quoted} = $quoted;
  170. } else {
  171. delete $self->{symbol_quoted};
  172. }
  173. }
  174. sub get_wildcard_version {
  175. my $self = shift;
  176. if ($self->get_symbolname() =~ /^\*@(.*)$/) {
  177. return $1;
  178. }
  179. return undef;
  180. }
  181. sub has_tags {
  182. my $self = shift;
  183. return scalar (@{$self->{tagorder}});
  184. }
  185. sub add_tag {
  186. my ($self, $tagname, $tagval) = @_;
  187. if (exists $self->{tags}{$tagname}) {
  188. $self->{tags}{$tagname} = $tagval;
  189. return 0;
  190. } else {
  191. $self->{tags}{$tagname} = $tagval;
  192. push @{$self->{tagorder}}, $tagname;
  193. }
  194. return 1;
  195. }
  196. sub delete_tag {
  197. my ($self, $tagname) = @_;
  198. if (exists $self->{tags}{$tagname}) {
  199. delete $self->{tags}{$tagname};
  200. $self->{tagorder} = [ grep { $_ ne $tagname } @{$self->{tagorder}} ];
  201. return 1;
  202. }
  203. return 0;
  204. }
  205. sub has_tag {
  206. my ($self, $tag) = @_;
  207. return exists $self->{tags}{$tag};
  208. }
  209. sub get_tag_value {
  210. my ($self, $tag) = @_;
  211. return $self->{tags}{$tag};
  212. }
  213. # Checks if the symbol is equal to another one (by name and tag set)
  214. sub equals {
  215. my ($self, $other) = @_;
  216. # Compare names and tag sets
  217. return 0 if $self->{symbol} ne $other->{symbol};
  218. return 0 if scalar(@{$self->{tagorder}}) != scalar(@{$other->{tagorder}});
  219. for (my $i = 0; $i < scalar(@{$self->{tagorder}}); $i++) {
  220. my $tag = $self->{tagorder}->[$i];
  221. return 0 if $tag ne $other->{tagorder}->[$i];
  222. if (defined $self->{tags}{$tag} && defined $other->{tags}{$tag}) {
  223. return 0 if $self->{tags}{$tag} ne defined $other->{tags}{$tag};
  224. } elsif (defined $self->{tags}{$tag} || defined $other->{tags}{$tag}) {
  225. return 0;
  226. }
  227. }
  228. return 1;
  229. }
  230. sub is_optional {
  231. my $self = shift;
  232. return $self->has_tag("optional");
  233. }
  234. sub is_arch_specific {
  235. my $self = shift;
  236. return $self->has_tag("arch");
  237. }
  238. sub arch_is_concerned {
  239. my ($self, $arch) = @_;
  240. my $arches = $self->{tags}{arch};
  241. if (defined $arch && defined $arches) {
  242. my $dep = Dpkg::Deps::Simple->new();
  243. my @arches = split(/[\s,]+/, $arches);
  244. $dep->{package} = "dummy";
  245. $dep->{arches} = \@arches;
  246. return $dep->arch_is_concerned($arch);
  247. }
  248. return 1;
  249. }
  250. # Get reference to the pattern the symbol matches (if any)
  251. sub get_pattern {
  252. return $_[0]->{matching_pattern};
  253. }
  254. ### NOTE: subroutines below require (or initialize) $self to be a pattern ###
  255. # Initialises this symbol as a pattern of the specified type.
  256. sub init_pattern {
  257. my $self = shift;
  258. my $type = shift;
  259. $self->{pattern}{type} = $type;
  260. # To be filled with references to symbols matching this pattern.
  261. $self->{pattern}{matches} = [];
  262. }
  263. # Is this symbol a pattern or not?
  264. sub is_pattern {
  265. return exists $_[0]->{pattern};
  266. }
  267. # Get pattern type if this symbol is a pattern.
  268. sub get_pattern_type {
  269. return $_[0]->{pattern}{type} || "";
  270. }
  271. # Get (sub)type of the alias pattern. Returns empty string if current
  272. # pattern is not alias.
  273. sub get_alias_type {
  274. return ($_[0]->get_pattern_type() =~ /^alias-(.+)/ && $1) || "";
  275. }
  276. # Get a list of symbols matching this pattern if this symbol is a pattern
  277. sub get_pattern_matches {
  278. return @{$_[0]->{pattern}{matches}};
  279. }
  280. # Create a new symbol based on the pattern (i.e. $self)
  281. # and add it to the pattern matches list.
  282. sub create_pattern_match {
  283. my $self = shift;
  284. return undef unless $self->is_pattern();
  285. # Leave out 'pattern' subfield while deep-cloning
  286. my $pattern_stuff = $self->{pattern};
  287. delete $self->{pattern};
  288. my $newsym = $self->dclone(@_);
  289. $self->{pattern} = $pattern_stuff;
  290. # Clean up symbol name related internal fields
  291. $newsym->set_symbolname();
  292. # Set newsym pattern reference, add to pattern matches list
  293. $newsym->{matching_pattern} = $self;
  294. push @{$self->{pattern}{matches}}, $newsym;
  295. return $newsym;
  296. }
  297. ### END of pattern subroutines ###
  298. # Given a raw symbol name the call returns its alias according to the rules of
  299. # the current pattern ($self). Returns undef if the supplied raw name is not
  300. # transformable to alias.
  301. sub convert_to_alias {
  302. my $self = shift;
  303. my $rawname = shift;
  304. my $type = shift || $self->get_alias_type();
  305. if ($type) {
  306. if ($type eq 'wildcard') {
  307. # In case of wildcard, alias is like "*@SYMBOL_VERSION". Extract
  308. # symbol version from the rawname.
  309. return "*\@$1" if ($rawname =~ /\@([^@]+)$/);
  310. } elsif ($rawname =~ /^_Z/ && $type eq "c++") {
  311. return cppfilt_demangle($rawname, "gnu-v3");
  312. }
  313. }
  314. return undef;
  315. }
  316. sub get_tagspec {
  317. my ($self) = @_;
  318. if ($self->has_tags()) {
  319. my @tags;
  320. for my $tagname (@{$self->{tagorder}}) {
  321. my $tagval = $self->{tags}{$tagname};
  322. if (defined $tagval) {
  323. push @tags, $tagname . "=" . $tagval;
  324. } else {
  325. push @tags, $tagname;
  326. }
  327. }
  328. return "(". join("|", @tags) . ")";
  329. }
  330. return "";
  331. }
  332. sub get_symbolspec {
  333. my $self = shift;
  334. my $template_mode = shift;
  335. my $spec = "";
  336. $spec .= "#MISSING: $self->{deprecated}#" if $self->{deprecated};
  337. $spec .= " ";
  338. if ($template_mode && $self->has_tags()) {
  339. $spec .= sprintf('%s%3$s%s%3$s', $self->get_tagspec(),
  340. $self->get_symboltempl(), $self->{symbol_quoted} || "");
  341. } else {
  342. $spec .= $self->get_symbolname();
  343. }
  344. $spec .= " $self->{minver}";
  345. $spec .= " $self->{dep_id}" if $self->{dep_id};
  346. return $spec;
  347. }
  348. 1;