Explorar o código

Dpkg::Shlibs::Symbol/SymbolFile: recognize patterns and initialize them properly

Add code for symbol-pattern recognition and initialization. Two groups of
patterns are supported:

* aliases (wildcards, c++ demangled names). They can be matched by converting
  a raw symbol to the alias and mapping the result to the alias pattern
  specified in symbol file template;

* generic patterns (regular expressions). They must be matched against each
  raw symbol in a sequential order. Therefore, they are rather expensive
  performance-wise.

Rename Symbol::process_tags() to Symbol::initialize() in the process. New name
suites the purpose of the sub better.

Failure of 'wildcarded symbol' test is expected at this point.

Signed-off-by: Raphaël Hertzog <hertzog@debian.org>
Modestas Vainius %!s(int64=16) %!d(string=hai) anos
pai
achega
90dff312c5
Modificáronse 2 ficheiros con 73 adicións e 9 borrados
  1. 48 3
      scripts/Dpkg/Shlibs/Symbol.pm
  2. 25 6
      scripts/Dpkg/Shlibs/SymbolFile.pm

+ 48 - 3
scripts/Dpkg/Shlibs/Symbol.pm

@@ -22,6 +22,7 @@ use Dpkg::Gettext;
 use Dpkg::Deps;
 use Dpkg::ErrorHandling;
 use Storable qw();
+use Dpkg::Shlibs::Cppfilt;
 
 sub new {
     my $this = shift;
@@ -136,10 +137,35 @@ sub parse {
     return 1;
 }
 
-# A hook for processing of tags which may change symbol name.
-# Called from Dpkg::Shlibs::SymbolFile::load(). Empty for now.
-sub process_tags {
+# A hook for symbol initialization (typically processing of tags). The code
+# here may even change symbol name. Called from
+# Dpkg::Shlibs::SymbolFile::load().
+sub initialize {
     my $self = shift;
+
+    # Look for tags marking symbol patterns. The pattern may match multiple
+    # real symbols.
+    if ($self->has_tag('c++')) {
+	# Raw symbol name is always demangled to the same alias while demangled
+	# symbol name cannot be reliably converted back to raw symbol name.
+	# Therefore, we can use hash for mapping.
+	$self->init_pattern('alias-c++'); # Alias subtype is c++.
+    }
+    # Wildcard is an alias based pattern. It gets recognized here even if it is
+    # not specially tagged.
+    if (my $ver = $self->get_wildcard_version()) {
+	error(_g("you can't use wildcards on unversioned symbols: %s"), $_) if $ver eq "Base";
+	$self->init_pattern(($self->is_pattern()) ? 'generic' : 'alias-wildcard');
+	$self->{pattern}{wildcard} = 1;
+    }
+    # As soon as regex is involved, we need to match each real
+    # symbol against each pattern (aka 'generic' pattern).
+    if ($self->has_tag('regex')) {
+	$self->init_pattern('generic');
+	# Pre-compile regular expression for better performance.
+	my $regex = $self->get_symbolname();
+	$self->{pattern}{regex} = qr/$regex/;
+    }
 }
 
 sub get_symbolname {
@@ -315,6 +341,25 @@ sub create_pattern_match {
 
 ### END of pattern subroutines ###
 
+# Given a raw symbol name the call returns its alias according to the rules of
+# the current pattern ($self). Returns undef if the supplied raw name is not
+# transformable to alias.
+sub convert_to_alias {
+    my $self = shift;
+    my $rawname = shift;
+    my $type = shift || $self->get_alias_type();
+    if ($type) {
+	if ($type eq 'wildcard') {
+	    # In case of wildcard, alias is like "*@SYMBOL_VERSION". Extract
+	    # symbol version from the rawname.
+	    return "*\@$1" if ($rawname =~ /\@([^@]+)$/);
+	} elsif ($rawname =~ /^_Z/ && $type eq "c++") {
+	    return cppfilt_demangle($rawname, "gnu-v3");
+	}
+    }
+    return undef;
+}
+
 sub get_tagspec {
     my ($self) = @_;
     if ($self->has_tags()) {

+ 25 - 6
scripts/Dpkg/Shlibs/SymbolFile.pm

@@ -107,15 +107,30 @@ sub add_symbol {
     my ($self, $soname, $symbol) = @_;
     my $object = (ref $soname) ? $soname : $self->{objects}{$soname};
 
-    if (!$symbol->{deprecated} && (my $ver = $symbol->get_wildcard_version())) {
-	error(_g("you can't use wildcards on unversioned symbols: %s"), $_) if $ver eq "Base";
-	$object->{wildcards}{$ver} = $symbol;
-	return 'wildcards';
+    if ($symbol->is_pattern()) {
+	if (my $alias_type = $symbol->get_alias_type()) {
+	    unless (exists $object->{patterns}{aliases}{$alias_type}) {
+		$object->{patterns}{aliases}{$alias_type} = {};
+	    }
+	    my $aliases = $object->{patterns}{aliases}{$alias_type};
+	    # A converter object for transforming a raw symbol name to alias
+	    # of this type. Must support convert_to_alias() method.
+	    unless (exists $aliases->{converter}) {
+		$aliases->{converter} = $symbol;
+	    }
+	    # Alias hash for matching.
+	    $aliases->{names}{$symbol->get_symbolname()} = $symbol;
+	} else {
+	    # Otherwise assume this is a generic sequential pattern. This
+	    # should be always safe.
+	    push @{$object->{patterns}{generic}}, $symbol;
+	}
+	return 'pattern';
     } else {
 	# invalidate the minimum version cache
         $object->{minver_cache} = [];
 	$object->{syms}{$symbol->get_symbolname()} = $symbol;
-	return 'syms';
+	return 'sym';
     }
 }
 
@@ -153,7 +168,7 @@ sub load {
 	    my $deprecated = ($1) ? $1 : 0;
 	    my $sym = new_symbol($base_symbol, deprecated => $deprecated);
 	    if ($sym->parse($2)) {
-		$sym->process_tags(arch => $self->{arch});
+		$sym->initialize(arch => $self->{arch});
 		$self->add_symbol($$obj_ref, $sym);
 	    } else {
 		warning(_g("Failed to parse line in %s: %s"), $file, $_);
@@ -363,6 +378,10 @@ sub create_object {
 	syms => {},
 	fields => {},
 	wildcards => {},
+	patterns => {
+	    aliases => {},
+	    generic => [],
+	},
 	deps => [ @deps ],
         minver_cache => []
     };