[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#340934: lintian check for unneeded/transitive shlibs dependencies



Package: lintian
Severity: wishlist
Tags: patch

Steve Langasek wrote in
<http://lists.debian.org/debian-devel-announce/2005/11/msg00016.html>:

> I would encourage you to read the presentation in question, but I will
> also summarize here: due to accidents of history, the convention when
> linking an executable or shared library is to tell ld to recursively add
> references to *all* libraries in the dependency tree below it.  This is
> wrong for modern GNU/Linux systems when using dynamic linking, because
> the dynamic linker recurses the dependency tree on its own; and
> following this convention means that executables get not the needed one
> reference to the library, but two or more.  Where things *really* go
> wrong is when the library goes through an soname change.  If two
> different versions of the library are specified in the ELF header, then
> either users can't install the package, or they can install it and then
> it segfaults.  In either case, this situation causes a lot more work for
> maintainers, autobuilders, and the release team (trying to get packages
> into testing) than it should.
[...]
> Moreover, this is a problem that happens over and over again, every time
> there's a library transition.  It's not hard to look around and see that
> over the course of a release cycle, we spend a lot of time managing
> library transitions -- time that could be better spent on enhancements
> that actually matter to our users.  Pruning our package dependencies is
> (ideally) a one-time fix for each package, with long-term time savings
> for each maintainer (and for the release team!).  Think how nice it will
> be to have only a third as many dependencies, and only have to rebuild
> your package for a third as many transitions!

I have written a Lintian check which attempts to flag instances of
this problem. It looks for ELF objects that flag shared libraries in
the default search path as NEEDED without actually importing symbols
that the library exports.

The check is a bit unorthodox in that it needs to have the referenced
libraries present on the host system (where lintian is run) and runs
objdump over them to figure out which symbols they export.  Linkage to
libraries that cannot be found at lintian time will be silently
accepted as "not superfluous".

Testing and comments solicited. To test, install
<http://henning.makholm.net/debian/lintian_1.23.13makholm_all.deb>,
or just drop the two attached files in /usr/share/lintian/checks/.

-- 
Henning Makholm               "... not one has been remembered from the time
                         when the author studied freshman physics. Quite the
            contrary: he merely remembers that such and such is true, and to
          explain it he invents a demonstration at the moment it is needed."
# libneeded -- lintian check script

# Copyright (C) 2005 Henning Makholm
#
# 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 2 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, you can find it on the World Wide
# Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
# MA 02110-1301, USA.

package Lintian::libneeded;
use strict;
use Tags;
use Util;

# NOTE that this check depends on the linked-to libaries actually
# being present on the system where lintian runs.
# The check will _succeed_ silently if the necessary information
# is not present.

sub run {

my $pkg = shift;
my $type = shift;

my %WELLKNOWN ;
# Do not bother to check for spurious linkings to libc packages;
# they are ubiquitous anyway. Get the current list from their
# shlibs files.
# Other libraries to be ignored can be added to the foreach
# list.
foreach my $p ( map(m/(libc[0-9.]+)\.shlibs/ ? $1 : (),
                    glob "/var/lib/dpkg/info/libc*.shlibs") ) {
    open(SHLIBS,"/var/lib/dpkg/info/$p.shlibs") or next ;
    while( <SHLIBS> ) {
        next unless m/^(\S+) (\d+) / ;
        $WELLKNOWN{"$1.so.$2"} = 1 ;
    }
}

sub checkit ($\%\%) {
    my ($file,$libs,$syms) = @_ ;
    my @unneeded ;
  libloop:
    for my $lib ( keys %$libs ) {
        open(LIB,"-|","objdump -T $$libs{$lib}")
            or fail ("cannot run objdump for $lib") ;
        while(<LIB>) {
            next if /^0+\s/ or /\s\*UND\*\s/ ;
            if( m/\s(\S+)\s*$/ && exists $$syms{$1} ) {
                close LIB ;
                next libloop ;
            }
        }
        close LIB ;
        push @unneeded, $lib ;
    }
    tag "unneeded-explicit-linking", $file, @unneeded
        if @unneeded ;
}

my %WANTSYMS ;
my %WANTLIBS ;
my $file ;
my $state = 'INITIAL' ;
open(IN,"objdump-info")
    or fail("cannot find objdump-info for $type package $pkg");
while (<IN>) {
    if( m/^\s*$/ ) {
        $state = 'SKIP' ;
    } elsif( m/^-- (\S+)\s*$/ ) {
        checkit $file, %WANTLIBS, %WANTSYMS ;
	$file = $1;
        $file =~ s:^\./:: ;
        $state = 'SKIP';
        %WANTLIBS = () ;
        %WANTSYMS = () ;
    } elsif( m/Dynamic Section:\s*$/ ) {
        $state = 'WANTLIBS' ;
    } elsif( m/^DYNAMIC SYMBOL TABLE:\s*$/ ) {
        $state = 'WANTSYMS' unless ! %WANTLIBS ;
    } elsif( $state eq 'WANTLIBS' && m/^\s+NEEDED\s+(lib\S+\.so\.\d+)/ ) {
        my $lib = $1 ;
        next if $WELLKNOWN{$lib} ;
      findlib:
        foreach my $dir ( qw( /lib /usr/lib /usr/X11R6/lib ) ) {
            my $fn = "$dir/$lib" ;
            next findlib unless -r $fn ;
            $WANTLIBS{$lib} = $fn ;
            last findlib ;
        }
    } elsif( $state eq 'WANTSYMS' && m/\s\*UND\*\s.*\s(\S+)\s*$/ ) {
        $WANTSYMS{$1} = 1 ;
    }
}
checkit $file, %WANTLIBS, %WANTSYMS ;
close(IN);
        
}

1;
Check-Script: libneeded
Author: Henning Makholm <henning@makholm.net>
Abbrev: libn
Standards-Version: 3.6.2
Type: binary
Unpack-Level: 1
Needs-Info: objdump-info
Info: This script checks for transitive library requests in ELF headers.

Tag: unneeded-explicit-linking
Type: warning
Info: A binary or library demands a specific soname for a library that
 it does not use directly.
 .
 This usually results from naming a library on the linker command line
 just because another used library needs it. This usage is traditionally
 needed on unix, but Debian's binutils are smart enough not to need it,
 and it will just cause soname transitions for the library in question
 to become more painful than needed.

Reply to: