rev 11857 - scripts

Modestas Vainius modax-guest at alioth.debian.org
Wed Aug 6 15:00:41 UTC 2008


Author: modax-guest
Date: 2008-08-06 15:00:40 +0000 (Wed, 06 Aug 2008)
New Revision: 11857

Added:
   scripts/symbols-helper
Log:
The initial and fairly complete version of the symbol file adaptation utility.
Symbol file is generated for each architecture based on the single template file.
The utility is also capable of generating a symbol file template from a set of symbol
files for each architecture[1] in 96% of cases. Symbol variations supported:

* Template instantiation symbol deprecation.
* Virtual table offsets.
* size_t variations (unsigned int / unsigned long).
* ssize_t variations (int / long).
* qreal variations (double / float).

This version is not fully complete yet. Also alpha (due to g++ 4.2 still being
default) and armel (don't know why) still cause complications.

1. http://qa.debian.org/cgi-bin/mole/seedsymbols/


Added: scripts/symbols-helper
===================================================================
--- scripts/symbols-helper	                        (rev 0)
+++ scripts/symbols-helper	2008-08-06 15:00:40 UTC (rev 11857)
@@ -0,0 +1,955 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2008 Modestas Vainius <modestas at vainius.eu>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>
+
+use warnings;
+use strict;
+use Dpkg::Shlibs::SymbolFile;
+use File::Temp qw(tempdir tempfile);
+use File::Spec;
+use Getopt::Long;
+
+my @ARCHES = (
+    'i386',
+    'amd64',
+    'alpha',
+    'arm',
+    'armel',
+    'hppa',
+    'ia64',
+    'mips',
+    'mipsel',
+    'powerpc',
+    's390',
+    'sparc',
+);
+
+sub error {
+    my $err = shift;
+    print STDERR $err, "\n";
+    exit 1;
+}
+
+package SymHelper::CompileTest;
+
+sub new {
+    my ($cls, $compiler, $lib) = @_;
+
+    my $tmpdir = tempdir();
+    my $sourcefile = "testcomp";
+    my $out = "testcomp";
+    my $cmd;
+
+    main::error("Unable to create a temporary directory for test compilation") unless $tmpdir;
+
+    if ($compiler =~ /gcc/) {
+        $sourcefile .= ".c";
+    } elsif ($compiler =~ /g\+\+/) {
+        $sourcefile  .= ".cpp";
+    } else {
+        main::error("Unrecognized compiler: $compiler");
+    }
+    $sourcefile = File::Spec::catfile($tmpdir, $sourcefile);
+
+    if ($lib) {
+        $cmd = "$compiler -shared -fPIC";
+        $out .= ".so";
+    } else {
+        $cmd = "$compiler";
+    }
+    $out = File::Spec::catfile($tmpdir, $out);
+
+    my $self = bless { tmpdir => $tmpdir,
+        sourcefile => $sourcefile, out => $out }, $cls;
+    $self->set_cmd($cmd);
+    return $self;
+}
+
+sub set_cmd {
+    my ($self, $cmd) = @_;
+    $self->{cmd} = "$cmd $self->{sourcefile} -o $self->{out}";
+}
+
+sub compile {
+    my ($self, $sourcecode) = @_;
+
+    open(SOURCE, ">", $self->{sourcefile})
+        or main::error("Unable to open temporary source file for writing: $self->{sourcefile}");
+    print SOURCE $sourcecode
+        or main::error("Unable to write to temporary source file $self->{sourcefile}");
+    close(SOURCE);
+
+    system($self->{cmd}) == 0 or main::error("Compilation failed: $self->{cmd}");
+    return $self->get_output_file();
+}
+
+sub get_output_file {
+    my $self = shift;
+    return (-f $self->{out}) ? $self->{out} : undef;
+}
+
+sub rm {
+    my $self = shift;
+    system("rm -rf $self->{tmpdir}") == 0 or main::error("Unable to delete temporary directory: $self->{tmpdir}");
+}
+
+# Enhanced symbol file processor
+package SymHelper::SymbFile;
+our @ISA = qw( Dpkg::Shlibs::SymbolFile );
+
+sub get_symbol_substvars {
+    my ($self, $sym) = @_;
+    my @substvars;
+    while ($sym =~ m/(\{[^}]+\})/g) {
+        push @substvars, "$1";
+    }
+    return @substvars;
+}
+
+sub scan_for_substvars {
+    my $self = shift;
+    my $count = 0;
+    delete $self->{subst_objects} if exists $self->{subst_objects};
+    while (my($soname, $sonameobj) = each(%{$self->{objects}})) {
+        while (my ($sym, $info) = each(%{$sonameobj->{syms}})) {
+            if (my @substvars = $self->get_symbol_substvars($sym)) {
+                $self->{subst_objects}{$soname}{syms}{$sym} = \@substvars;
+                $count += scalar(@substvars);
+            }
+        }
+    }
+    return $count;
+}
+
+sub substitute {
+    my $self = shift;
+    my $handlers = shift;
+
+    return undef unless defined $handlers;
+
+    my $newsymfile = new SymHelper::SymbFile;
+
+    # Shallow clone our symbol file as a new file object
+    while (my ($key, $val) = each(%$self)) {
+        $newsymfile->{$key} = $val;
+    }
+
+    # We are only interested in {objects}
+    $newsymfile->{objects} = {};
+    while (my ($soname, $sonameobj) = each(%{$self->{objects}})) {
+        if (exists $self->{subst_objects}{$soname}) {
+            while (my ($soname_key, $soname_val) = each(%$sonameobj)) {
+                $newsymfile->{objects}{$soname}{$soname_key} = $soname_val;
+            }
+            # Process {syms}
+            $newsymfile->{objects}{$soname}{syms} = {};
+            while (my ($sym, $info) = each(%{$sonameobj->{syms}})) {
+                if (exists $self->{subst_objects}{$soname}{syms}{$sym}) {
+                    my $substvars = $self->{subst_objects}{$soname}{syms}{$sym};
+                    foreach my $substvar (@$substvars) {
+                        my ($result, $found);
+                        foreach my $handler (@$handlers) {
+                            if ($result = $handler->replace($substvar, $sym)) {
+                                $sym =~ s/\Q$substvar\E/$result/g;
+                                $found = 1;
+                            }
+                        }
+                        main::error("Substvar '$substvar' in symbol $sym/$soname was not handled by any substvar handler")
+                            unless defined $found;
+                    }
+                }
+                $newsymfile->{objects}{$soname}{syms}{$sym} = $info;
+            }
+        } else {
+            $newsymfile->{objects}{$soname} = $sonameobj;
+        }
+    }
+
+    return $newsymfile;
+}
+
+sub dump_cpp_symbols {
+    my ($self, $fh) = @_;
+
+    while (my ($soname, $sonameobj) = each(%{$self->{objects}})) {
+        while (my ($sym, $info) = each(%{$sonameobj->{syms}})) {
+                print $fh $sym, "\n" if ($sym =~ /^_Z/);
+            }
+    }
+}
+
+sub load_cppfilt_symbols {
+    my ($self, $fh) = @_;
+
+    while (my ($soname, $sonameobj) = each(%{$self->{objects}})) {
+        while (my ($sym, $info) = each(%{$sonameobj->{syms}})) {
+            next unless ($sym =~ /^_Z/);
+            if (my $cpp = <$fh>) {
+                chomp $cpp;
+                $info->{cppfilt} = $cpp;
+            } else {
+                main::error("Unexpected end at c++filt output: '$sym' not demangled");
+            }
+        }
+    }
+}
+
+sub deprecate_useless_symbols {
+    my $self = shift;
+    my $count = 0;
+    while (my ($soname, $sonameobj) = each(%{$self->{objects}})) {
+        while (my ($sym, $info) = each(%{$sonameobj->{syms}})) {
+            next if ($info->{deprecated});
+
+            if (exists $info->{cppfilt}) {
+                # Deprecate template instantiations as they are not
+                # useful public symbols
+                my $cppfilt = $info->{cppfilt};
+                # Prepare for tokenizing: wipe out unnecessary spaces
+                $cppfilt =~ s/([,<>()])\s+/$1/g;
+                $cppfilt =~ s/\s*((?:(?:un)?signed|volatile|restrict|const|long)[*&]*)\s*/$1/g;
+                if (my @tokens = split(/\s+/, $cppfilt)) {
+                    my $func;
+                    if ($tokens[0] =~ /[(]/) {
+                        $func = $tokens[0];
+                    } elsif ($#tokens >= 1 && $tokens[1] =~ /[(]/) {
+                        # The first token was return type, try the second
+                        $func = $tokens[1];
+                    }
+                    if (defined $func && $func =~ /<[^>]+>[^(]*[(]/) {
+                        # print STDERR "Deprecating $sym ", $cppfilt, "\n";
+                        # It is template instantiation. Deprecate it
+                        $info->{deprecated} = "PRIVATE: TEMPLINST";
+                        $count++;
+                    }
+                }
+            }
+        }
+    }
+    return $count;
+}
+
+sub dump {
+    my ($self, $fh, $with_deprecated) = @_;
+
+    if (!defined $with_deprecated || $with_deprecated != 2) {
+        return Dpkg::Shlibs::SymbolFile::dump(@_);
+    } else {
+        foreach my $soname (sort keys %{$self->{objects}}) {
+            my @deps = @{$self->{objects}{$soname}{deps}};
+            print $fh "$soname $deps[0]\n";
+            shift @deps;
+            print $fh "| $_\n" foreach (@deps);
+            my $f = $self->{objects}{$soname}{fields};
+            print $fh "* $_: $f->{$_}\n" foreach (sort keys %{$f});
+            foreach my $sym (sort keys %{$self->{objects}{$soname}{syms}}) {
+                my $info = $self->{objects}{$soname}{syms}{$sym};
+                print $fh "#", (($info->{deprecated} =~ m/^PRIVATE:/) ? "DEPRECATED" : "MISSING"),
+                    ": $info->{deprecated}#" if $info->{deprecated};
+                print $fh " $sym $info->{minver}";
+                print $fh " $info->{dep_id}" if $info->{dep_id};
+                print $fh "\n";
+            }
+        }
+    }
+}
+
+sub set_min_version {
+    my ($self, $version, $with_deprecated) = @_;
+
+    while (my ($soname, $sonameobj) = each(%{$self->{objects}})) {
+        while (my ($sym, $info) = each(%{$sonameobj->{syms}})) {
+            $info->{minver} = $version if ($with_deprecated || !$info->{deprecated});
+        }
+    }
+}
+
+package SymHelper::Symbol;
+
+sub new {
+    my ($cls, $symbol, $arch) = @_;
+    return bless { symbol => $symbol, arch => $arch }, $cls;
+}
+
+sub substr {
+    my ($self, $offset, $length, $repl) = @_;
+    substr($self->{symbol}, $offset, $length) = $repl;
+}
+
+sub get_symbol {
+    return shift()->{symbol};
+}
+
+sub get_arch {
+    return shift()->{arch};
+}
+
+sub eq {
+    my $self = shift;
+    my $other = shift;
+    return $self->{symbol} eq $other->{symbol};
+}
+
+sub is_vtt {
+    return shift()->get_symbol() =~ /^_ZT[VT]/;
+}
+
+package SymHelper::Symbol2;
+our @ISA = qw(SymHelper::Symbol);
+
+sub new {
+    my $symbol = $_[1];
+    my $self = SymHelper::Symbol::new(@_);
+    my @lvl2 = split(//, $symbol);
+    $self->{lvl2} = \@lvl2;
+    return $self;
+}
+
+sub substr {
+    my ($self, $offset, $length, $repl1, $repl2) = @_;
+    my @repl = map { undef } split(//, $repl1);
+    $repl[0] = $repl2;
+    splice @{$self->{lvl2}}, $offset, $length, @repl;
+    return SymHelper::Symbol::substr($self, $offset, $length, $repl1);
+}
+
+sub get_symbol2 {
+    my $self = shift;
+    my $str = "";
+    foreach my $s (@{$self->{lvl2}}) {
+        $str .= $s if defined $s;
+    }
+    return $str;
+}
+
+# Handler interface
+package SymHelper::Handler::Base;
+
+sub new {
+    my ($cls, $arch) = @_;
+    my $self = bless {
+        arch => undef,
+    }, $cls;
+    $self->set_arch($arch);
+    return $self;
+}
+
+sub get_host_arch {
+    my $arch = `dpkg-architecture -qDEB_HOST_ARCH_CPU`;
+    chomp $arch;
+    return $arch;
+}
+
+sub set_arch {
+    my ($self, $arch) = @_;
+    $arch = get_host_arch() unless defined $arch;
+
+    main::error("Unknown architecture: $arch") unless grep($arch, @ARCHES);
+
+    my $arch_params;
+    $self->{"${arch}_params"} = $arch_params
+        if ($arch_params = $self->get_predef_arch_params($arch));
+    $self->{arch} = $arch;
+}
+
+sub _preload_arch_params {
+    my $self = shift;
+    foreach my $arch (@_) {
+        $self->{"${arch}_params"} = $self->get_predef_arch_params($arch);
+    }
+}
+
+sub get_arch_param {
+    my ($self, $name, $arch) = @_;
+    $arch = $self->{arch} unless defined $arch;
+
+    $self->_preload_arch_params($arch) unless (exists $self->{"${arch}_params"});
+    return $self->{"${arch}_params"}{$name};
+}
+
+sub dump_arch_params {
+    my ($self, $arch) = @_;
+    $arch = $self->{arch} unless defined $arch;
+
+    if (exists $self->{"${arch}_params"}) {
+        print "Arch: $arch", "\n";
+        foreach my $key (keys %{$self->{"${arch}_params"}}) {
+           print "$key: ", $self->get_arch_param($key, $arch), "\n";
+        }
+    }
+}
+
+sub get_host_arch_params {
+    my $self = shift;
+    return undef;
+}
+
+sub get_predef_arch_params {
+    my ($self, $arch) = @_;
+    return undef;
+}
+
+sub clean {
+    my ($self, $sym) = @_;
+    return $sym;
+}
+
+sub detect {
+    my ($self, $symversions) = @_;
+    return undef;
+}
+
+sub replace {
+    my ($self, $substvar, $sym) = @_;
+    return undef;
+}
+
+# Virtual table handler
+package SymHelper::Handler::VirtTable;
+our @ISA = qw( SymHelper::Handler::Base );
+
+sub get_host_arch_params {
+    my $params = {};
+    my $c = new SymHelper::CompileTest("gcc");
+    my $exe = $c->compile("#include <stdio.h>\nint main() { printf \"%d\\n\", sizeof(void*) \ 4; }");
+    $params->{mult} = `$exe`;
+    $c->rm();
+    return $params;
+}
+
+sub get_predef_arch_params {
+    my ($self, $arch) = @_;
+
+    # Mult should be 1 on 32bit arches and 2 on 64bit arches and so on
+    my $params = { mult => 1 };
+    $params->{mult} = 2 if ($arch =~ /amd64|ia64|alpha/);
+    return $params;
+}
+
+sub subvt {
+    my ($self, $symbol, $number, $stroffset) = @_;
+    if ((my $mult = $self->get_arch_param("mult", $symbol->get_arch())) > 1) {
+        my $l = length("$number");
+        $number /= $mult;
+        $symbol->substr($stroffset, $l, "$number", "{vt:$number}");
+
+        return 1;
+    }
+    return 0;
+}
+
+sub find_ztc_offset {
+    my ($self, $symbol) = @_;
+
+    # The idea behind the algorithm is that c++filt output does not
+    # change when offset is changed.
+    # e.g. _ZTCN6KParts15DockMainWindow3E56_NS_8PartBaseE
+
+    my @matches = ($symbol =~ m/(\d+)_/gc);
+    if (!@matches) {
+        main::error("Invalid construction table symbol: $symbol");
+    } elsif (@matches == 1) {
+        # Found it
+        return (pos($symbol) - length($1) - 1, $1);
+    } else {
+        # The idea behind the algorithm is that c++filt output does not
+        # change when an offset is changed.
+        $symbol =~ s/@[^@]+$//;
+        my $demangled = `c++filt '$symbol'`;
+        pos($symbol) = undef;
+        while ($symbol =~ m/(\d+)_/g) {
+            my $offset = $1;
+            my $pos = pos($symbol) - length($offset) - 1;
+            my $newsymbol = $symbol; 
+            substr($newsymbol, $pos, length($offset)) = $offset + 1234;
+            my $newdemangled = `c++filt '$newsymbol'`;
+            return ($pos, $offset) if ($demangled eq $newdemangled);
+        }
+        main::error("Unable to determine construction table offset position in symbol '$symbol'");
+    }
+}
+
+sub diff_symbol {
+    my ($self, $symbol) = @_;
+    my @diffs;
+    my $sym = $symbol->get_symbol();
+    my $ret = 0;
+
+    # construction vtable: e.g. _ZTCN6KParts15DockMainWindow3E56_NS_8PartBaseE
+    if ($sym =~ /^_ZTC/) {
+        my ($pos, $num) = $self->find_ztc_offset($sym);
+        $ret = $self->subvt($symbol, $num, $pos) if ($num > 0);
+    } elsif ($sym =~ /^_ZThn(\d+)_/) {
+        # non-virtual base override: e.g. _ZThn8_N6KParts13ReadWritePartD0Ev
+        my $num = $1;
+        $ret = $self->subvt($symbol, $num, 5) if ($num > 0);
+    } elsif ($sym =~ /^_ZTvn?(\d+)_(n?\d+)/) {
+        # virtual base override, with vcall offset, e.g. _ZTv0_n12_N6KParts6PluginD0Ev
+        my $voffset = $1;
+        my $num = $2;
+        my $numoffset = 4 + length("$voffset") + 1 + (($num =~ /^n/) ? 1 : 0);
+        $num =~ s/^n//;
+
+        $ret = $self->subvt($symbol, $voffset, 4) if ($voffset > 0);
+        $ret = $self->subvt($symbol, $num, $numoffset) || $ret if ($num > 0);
+    } 
+    return $ret;
+}
+
+sub clean {
+    my $self = shift;
+    return $self->diff_symbol(@_);
+}
+
+sub detect {
+    my ($self, $symbol) = @_;
+    return $self->diff_symbol($symbol);
+}
+
+sub replace {
+    my ($self, $substvar, $sym) = @_;
+    # vt:$number
+    if ($substvar =~ /^{vt:(\d+)}$/) {
+        my $number = $1;
+        $number *= $self->get_arch_param("mult");
+        return "$number";
+    } else {
+        return undef;
+    }
+}
+
+package SymHelper::Handler::SimpleTypeDiff;
+our @ISA = qw( SymHelper::Handler::Base );
+
+sub new {
+    return SymHelper::Handler::Base::new(@_);
+}
+
+sub clean {
+    my ($self, $symbol) = @_;
+    my $sym = $symbol->get_symbol();
+    my $ret = 0;
+    while ($sym =~ m/$self->{type_re}/g) {
+        $symbol->substr(pos($sym)-1, 1, $self->{main_type});
+        $ret = 1;
+    }
+    return $ret;
+}
+
+sub detect {
+    my ($self, $main_symbol, $archsymbols) = @_;
+
+    my $s1 = $main_symbol->get_symbol();
+    my $t1 = $self->get_arch_param("type", $main_symbol->get_arch());
+    my ($s2, $t2, $a2);
+
+    # Find architecture with other type
+    while (($a2, my $symbol) = each(%$archsymbols)) {
+        $t2 = $self->get_arch_param("type", $a2);
+        if ($t2 ne $t1) {
+            $s2 = $symbol->get_symbol();
+            last;
+        }
+    }
+
+    return 0 unless defined $s2;
+
+    # Compare letter by letter until difference is found
+    my @s1 = split(//, $s1);
+    my @s2 = split(//, $s2);
+    my $ret = 0;
+    for (my $i = 0; $i <= $#s1; $i++) {
+        if ($s1[$i] eq $t1 && $s2[$i] eq $t2) {
+            $main_symbol->substr($i, 1, $self->{main_type}, $self->{substvar});
+            $ret = 1;
+        }
+    }
+    return $ret;
+}
+
+sub replace {
+    my ($self, $substvar) = @_;
+    if ($substvar =~ /^$self->{substvar}$/) {
+        return $self->get_arch_param("type");
+    } else {
+        return undef;
+    }
+}
+
+package SymHelper::Handler::size_t;
+our @ISA = qw( SymHelper::Handler::SimpleTypeDiff );
+
+sub new {
+    my $self = SymHelper::Handler::SimpleTypeDiff::new(@_);
+    $self->{substvar} = "{size_t}";
+    $self->{main_type} = "j"; # unsigned int
+    $self->{type_re} = "[jm]";
+    return $self;
+}
+
+sub get_predef_arch_params {
+    my ($self, $arch) = @_;
+
+    # Mult should be 1 on 32bit arches and 2 on 64bit arches and so on
+    my $params = { type => "j" }; # unsigned int
+    $params->{type} = "m" if ($arch =~ /amd64|ia64|alpha|s390/); # unsigned long
+    return $params;
+}
+
+package SymHelper::Handler::ssize_t;
+our @ISA = qw( SymHelper::Handler::SimpleTypeDiff );
+
+sub new {
+    my $self = SymHelper::Handler::SimpleTypeDiff::new(@_);
+    $self->{substvar} = "{ssize_t}";
+    $self->{main_type} = "i"; # unsigned int
+    $self->{type_re} = "[il]";
+    return $self;
+}
+
+sub get_predef_arch_params {
+    my ($self, $arch) = @_;
+
+    # Mult should be 1 on 32bit arches and 2 on 64bit arches and so on
+    my $params = { type => "i" }; # int
+    $params->{type} = "l" if ($arch =~ /amd64|ia64|alpha|s390/); # unsigned long
+    return $params;
+}
+
+package SymHelper::Handler::qreal;
+our @ISA = qw( SymHelper::Handler::SimpleTypeDiff );
+
+sub new {
+    my $self = SymHelper::Handler::SimpleTypeDiff::new(@_);
+    $self->{substvar} = "{qreal}";
+    $self->{main_type} = "d"; # unsigned int
+    $self->{type_re} = "[fd]";
+    return $self;
+}
+
+sub get_predef_arch_params {
+    my ($self, $arch) = @_;
+
+    # Mult should be 1 on 32bit arches and 2 on 64bit arches and so on
+    my $params = { type => "d" }; # int
+    $params->{type} = "f" if ($arch =~ /arm/); # unsigned long
+    return $params;
+}
+
+package SymHelper::Handlers;
+
+sub new {
+    my $cls = shift;
+    my @substitution = (
+        new SymHelper::Handler::VirtTable,
+        new SymHelper::Handler::size_t,
+        new SymHelper::Handler::ssize_t,
+        new SymHelper::Handler::qreal,
+    );
+    return bless { subst => \@substitution }, $cls;
+}
+
+sub load_symbol_files {
+    my $self = shift;
+    my $files = shift;
+    my %symfiles;
+
+    return 0 if (exists $self->{symfiles});
+
+    while (my ($arch, $file) = each(%$files)) {
+        $symfiles{$arch} = new SymHelper::SymbFile($file);
+    }
+    $self->{symfiles} = \%symfiles;
+
+    # Set main architecture
+    my $main_arch = SymHelper::Handler::Base::get_host_arch();
+    $main_arch = (keys(%symfiles))[0] unless exists $symfiles{$main_arch};
+    $self->{main_arch} = $main_arch;
+
+    return scalar(keys %symfiles);
+}
+
+sub get_main_arch {
+    my $self = shift;
+    return (exists $self->{main_arch}) ? $self->{main_arch} : undef;
+}
+
+sub get_symfile {
+    my ($self, $arch) = @_;
+    if (exists $self->{symfiles}) {
+        $arch = $self->get_main_arch() unless defined $arch;
+        return (exists $self->{symfiles}{$arch}) ? $self->{symfiles}{$arch} : undef;
+    } else {
+        return undef;
+    }
+}
+
+sub cppfilt {
+    my $self = shift;
+    my @symfiles;
+
+    if (!@_) {
+        return 0 if (!exists $self->{symfiles} || exists $self->{cppfilt});
+        push @symfiles, values %{$self->{symfiles}};
+    } else {
+        push @symfiles, @_;
+    }
+
+    # Open temporary file
+    my ($fh, $filename) = File::Temp::tempfile();
+    if (defined $fh) {
+        # Dump cpp symbols to the temporary file
+        foreach my $symfile (@symfiles) {
+            $symfile->dump_cpp_symbols($fh);
+        }
+        close($fh);
+
+        # c++filt the symbols and load them
+        open(CPPFILT, "cat '$filename' | c++filt |") or main::error("Unable to run c++filt");
+        foreach my $symfile (@symfiles) {
+            $symfile->load_cppfilt_symbols(*CPPFILT);
+        }
+        close(CPPFILT);
+
+        # Remove temporary file
+        unlink($filename);
+
+        $self->{cppfilt} = 1 if (!@_);
+
+        return 1;
+    } else {
+        main::error("Unable to create a temporary file");
+    }
+}
+
+sub preprocess {
+    my $self = shift;
+    my $count = 0;
+
+    if (!@_) {
+        return 0 unless (exists $self->{symfiles});
+        $self->cppfilt();
+        push @_, values(%{$self->{symfiles}});
+    } else {
+        $self->cppfilt(@_);
+    }
+    foreach my $symfile (@_) {
+        $count += $symfile->deprecate_useless_symbols();
+    }
+    return $count;
+}
+
+sub get_group_name {
+    my ($self, $symbol, $arch) = @_;
+
+    my $osym = new SymHelper::Symbol($symbol, $arch);
+    foreach my $handler (@{$self->{subst}}) {
+        $handler->clean($osym);
+    }
+    return $osym->get_symbol();
+}
+
+sub create_template {
+    my $self = shift;
+
+    return 0 unless (exists $self->{symfiles});
+
+    my $symfiles = $self->{symfiles};
+    my $main_arch = $self->get_main_arch();
+
+    # Collect new symbols from them by grouping them using the
+    # fully arch independent derivative name
+    my %symbols;
+    foreach my $arch1 (@ARCHES) {
+        next unless exists $symfiles->{$arch1};
+
+        foreach my $arch2 (@ARCHES) {
+            next if $arch1 eq $arch2;
+            next unless exists $symfiles->{$arch2};
+
+            my @new = $symfiles->{$arch1}->get_new_symbols($symfiles->{$arch2});
+            foreach my $n (@new) {
+                my $g = $self->get_group_name($n->{name}, $arch1);
+                my $s = $n->{soname};
+                if (exists $symbols{$s}{$g}{arches}{$arch1}) {
+                    if ($symbols{$s}{$g}{arches}{$arch1}->get_symbol() ne $n->{name}) {
+                        print STDERR "Warning: at least two new symbols get to the same group ($g) on $s/$arch1:", "\n",
+                            "  ", $symbols{$s}{$g}{arches}{$arch1}->get_symbol(), "\n",
+                            "  ", $n->{name}, "\n";
+                        # Ban group
+                        $symbols{$s}{$g}{banned} = 1;
+                    }
+                } else {
+                    $symbols{$s}{$g}{arches}{$arch1} = new SymHelper::Symbol($n->{name}, $arch1);
+                }
+            }
+        }
+    }
+
+    # Do substvar detection
+    my $arch_count = scalar(keys(%$symfiles));
+
+    # Missing archs check
+    my %arch_ok;
+    my $arch_ok_i = 0;
+    while (my($arch, $f) = each(%$symfiles)) {
+        $arch_ok{$arch} = $arch_ok_i;
+    }
+
+    while (my ($soname, $groups) = each(%symbols)) {
+        while (my ($name, $group) = each(%$groups)) {
+            # Check if the group is not banned 
+            next if exists $group->{banned};
+
+            # Check if the group is complete
+            my $count = scalar(keys(%{$group->{arches}}));
+            if ($count < $arch_count) {
+                $group->{banned} = 1;
+                # Additional vtables are usual on armel
+                next if ($count == 1 && exists $group->{arches}{armel} && $group->{arches}{armel}->is_vtt());
+
+                $arch_ok_i++;
+                print STDERR "Ignoring incomplete group '$name/$soname' ($count < $arch_count). Symbol dump below:\n";
+                foreach my $arch (sort(keys %{$group->{arches}})) {
+                    print STDERR "  ", $group->{arches}{$arch}->get_symbol(), "/", $arch, "\n";
+                    $arch_ok{$arch} = $arch_ok_i;
+                }
+                print STDERR "  - missing on:";
+                for my $arch (sort(keys %arch_ok)) {
+                    print STDERR " ", $arch if (defined $arch_ok{$arch} && $arch_ok{$arch} != $arch_ok_i);
+                }
+                print STDERR "\n";
+                next;
+            }
+
+            # Main symbol (reference)
+            my $main_symbol = new SymHelper::Symbol2($group->{arches}{$main_arch}->get_symbol(), $main_arch);
+            foreach my $handler (@{$self->{subst}}) {
+                if ($handler->detect($main_symbol, $group->{arches})) {
+                    # Make archsymbols arch independent with regard to his handler
+                    while (my ($arch, $symbol) = each(%{$group->{arches}})) {
+                        $handler->clean($symbol);
+                    }
+                }
+            }
+            $group->{template} = $main_symbol;
+        }
+    }
+
+    # Finally, integrate our template into $main_arch symfile
+    my $main_symfile = $symfiles->{$main_arch};
+    while (my ($soname, $sonameobj) = each(%{$symfiles->{$main_arch}{objects}})) {
+        my @syms = keys(%{$sonameobj->{syms}});
+        for my $sym (@syms) {
+            my $g = $self->get_group_name($sym, $main_arch);
+            if (exists $symbols{$soname}{$g}) {
+                my $group = $symbols{$soname}{$g};
+                if (!exists $group->{banned}) {
+                    # Rename symbol
+                    my $info = $sonameobj->{syms}{$sym};
+                    my $newsym = $group->{template}->get_symbol2();
+                    $sonameobj->{syms}{$newsym} = $info;
+                    delete $sonameobj->{syms}{$sym};
+                } elsif (exists $sonameobj->{syms}{$sym}) {
+                    delete $sonameobj->{syms}{$sym}
+                        unless ($sonameobj->{syms}{$sym}{deprecated});
+                }
+            }
+        }
+    }
+
+    return $main_symfile;
+}
+
+sub substitute {
+    my ($self, $file, $arch) = @_;
+    my $symfile = new SymHelper::SymbFile($file);
+
+    foreach my $h (@{$self->{subst}}) {
+        $h->set_arch($arch);
+    }
+    if ($symfile->scan_for_substvars()) {
+        return $symfile->substitute($self->{subst});
+    } else {
+        return undef;
+    }
+}
+
+package main;
+
+my $opt_dir;
+my $opt_minversion;
+my $opt_in;
+my $opt_arch;
+my $opt_outfile;
+
+sub out_symfile {
+    my $symfile = shift;
+    return unless $symfile;
+
+    if ($opt_outfile) {
+        $symfile->save($opt_outfile, 2);
+    } else {
+        $symfile->dump(*STDOUT, 2);
+    }
+}
+
+if (GetOptions(
+    "directory|d=s" => \$opt_dir,
+    "min-version|mv=s" => \$opt_minversion,
+    "architecture|a=s" => \$opt_arch,
+    "input|i=s" => \$opt_in,
+    "output|o=s" => \$opt_outfile)) {
+}
+
+my $handlers = new SymHelper::Handlers;
+my $str_arches = join("|", @ARCHES);
+
+if (defined $opt_dir) {
+    opendir(DIR, $opt_dir) or main::error("Unable to open directory: $opt_dir");
+    my %files;
+    while (my $file = readdir(DIR)) {
+        next if ($file =~ /^.{1,2}$/);
+        $file = File::Spec->catfile($opt_dir, $file);
+        if ($file =~ /.*?[_.]($str_arches)$/) {
+            $files{$1} = $file;
+        } else {
+            print STDERR "Warning: $file is not named properly. Expected *_<arch> or *.<arch>", "\n";
+        }
+    }
+
+    if (scalar(keys %files) > 0) {
+        $handlers->load_symbol_files(\%files);
+        $handlers->preprocess();
+
+        # Force version
+        $handlers->get_symfile()->set_min_version($opt_minversion)
+            if (defined $opt_minversion);
+
+        # Create a symbols template and write it
+        out_symfile($handlers->create_template());
+    } else {
+        error("No properly named symbol files found in $opt_dir");
+    }
+} elsif (defined $opt_in) {
+    main::error("Symbol template file ($opt_in) not found") unless (-f $opt_in);
+    $opt_arch = SymHelper::Handler::Base::get_host_arch() unless defined $opt_arch;
+    main::error("Unknown architecture: $opt_arch") unless grep /^$opt_arch$/, @ARCHES;
+
+    out_symfile($handlers->substitute($opt_in, $opt_arch));
+} else {
+    error("$0: insufficient arguments");
+}


Property changes on: scripts/symbols-helper
___________________________________________________________________
Name: svn:executable
   + *




More information about the pkg-kde-commits mailing list