X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=nokia-lintian%2Fchecks%2Fbinaries;fp=nokia-lintian%2Fchecks%2Fbinaries;h=5c6d2acdd592153ca49ba5a733cc1c7d9524c7a5;hb=1975b83207a518d59ef6b04c7c16233cb353ca86;hp=0000000000000000000000000000000000000000;hpb=208f636c44e0ec2b53c70aaed2399d8e9cf0e741;p=maemian diff --git a/nokia-lintian/checks/binaries b/nokia-lintian/checks/binaries new file mode 100644 index 0000000..5c6d2ac --- /dev/null +++ b/nokia-lintian/checks/binaries @@ -0,0 +1,352 @@ +# binaries -- lintian check script -*- perl -*- + +# Copyright (C) 1998 Christian Schwarz and Richard Braakman +# +# 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::binaries; +use strict; +use Tags; +use Util; + +use File::Spec; + +sub run { + +my $pkg = shift; +my $type = shift; + +my $arch; +my $dynsyms = 0; +my $needs_libc = ''; +my $needs_libc_file; +my $needs_libc_count = 0; +my $needs_depends_line = 0; +my $has_perl_lib = 0; + +my %COMMENT; +my %NOTE; +my %RPATH; +my %NEEDED; +my %CXXABI; +my %OCAML; +my %SONAME; +my %KLIBC; + +# read architecture file +if (open(IN, '<', "fields/architecture")) { + chop($arch = ); + close(IN); +} + +my $file; + +# read data from objdump-info file +open(IN, '<', "objdump-info") + or fail("cannot find objdump-info for $type package $pkg"); +while () { + chop; + + next if m/^\s*$/o; + + if (m/^-- (\S+)\s*$/o) { + $file = $1; + $dynsyms = 0; + } elsif ($dynsyms) { + # The .*? near the end is added because a number of optional fields + # might be printed. The symbol name should be the last word. + if (m/^[0-9a-fA-F]+.{6}\w\w?\s+(\S+)\s+[0-9a-zA-Z]+\s+(\S+)\s+(\S+)$/){ + my ($foo, $sec, $sym) = ($1, $2, $3); + if ($arch ne 'hppa') { + if ($foo eq '.text' and $sec eq 'Base' and + $sym eq '__gmon_start__') { + tag "binary-compiled-with-profiling-enabled", "$file"; + } + } else { + if ( ($sec =~ /^GLIBC_.*/) and ($sym eq '_mcount') ) { + tag "binary-compiled-with-profiling-enabled", "$file"; + } + } + + if ($foo eq '.text' and $sec eq 'Base' and $sym eq 'caml_main') { + $OCAML{$file} = 1; + } + } + } else { + if (m/^\s*NEEDED\s*(\S+)/o) { + push @{$NEEDED{$file}}, $1; + } elsif (m/^\s*RPATH\s*(\S+)/o) { + foreach (split m/:/, $1) { + $RPATH{$file}{$_}++; + } + } elsif (m/^\s*SONAME\s*(\S+)/o) { + $SONAME{$1} ||= []; + push @{$SONAME{$1}}, $file; + } elsif (m/^\s*\d+\s+\.comment\s+/o) { + $COMMENT{$file} = 1; + } elsif (m/^\s*\d+\s+\.note\s+/o) { + $NOTE{$file} = 1; + } elsif (m/^DYNAMIC SYMBOL TABLE:/) { + $dynsyms = 1; + } elsif (m/^objdump: (.*?): File format not recognized$/) { + tag "apparently-corrupted-elf-binary", "$file"; + } elsif (m/^objdump: \.(.*?): Packed with UPX$/) { + tag "binary-file-compressed-with-upx", "$file"; + } elsif (m/objdump: \.(.*?): Invalid operation$/) { + # Don't anchor this regex since it can be interspersed with other + # output and hence not on the beginning of a line. + tag "binary-with-bad-dynamic-table", "$file" unless $file =~ m%^\./usr/lib/debug/%; + } elsif (m/CXXABI/) { + $CXXABI{$file} = 1; + } elsif (m%Requesting program interpreter:\s+/lib/klibc-\S+\.so%) { + $KLIBC{$file} = 1; + } + } +} +close(IN); + +# For the package naming check, filter out SONAMEs where all the files are at +# paths other than /lib, /usr/lib, or /usr/X11R6/lib. This avoids false +# positives with plugins like Apache modules, which may have their own SONAMEs +# but which don't matter for the purposes of this check. Also filter out +# nsswitch modules +sub lib_soname_path { + my (@paths) = @_; + foreach my $path (@paths) { + next if $path =~ m%^(\.?/)?lib/libnss_[^.]+\.so(\.[0-9]+)$%; + return 1 if $path =~ m%^(\.?/)?lib/[^/]+$%; + return 1 if $path =~ m%^(\.?/)?usr/lib/[^/]+$%; + return 1 if $path =~ m%^(\.?/)?usr/X11R6/lib/[^/]+$%; + } + return 0; +} +my @sonames = sort grep { lib_soname_path (@{$SONAME{$_}}) } keys %SONAME; + +# try to identify transition strings +my $base_pkg = $pkg; +$base_pkg =~ s/c102\b//o; +$base_pkg =~ s/c2a?\b//o; +$base_pkg =~ s/\dg$//o; +$base_pkg =~ s/gf$//o; +$base_pkg =~ s/-udeb$//o; +$base_pkg =~ s/^lib64/lib/o; + +my $match_found = 0; +foreach my $expected_name (@sonames) { + $expected_name =~ s/([0-9])\.so\./$1-/; + $expected_name =~ s/\.so(\.|\z)//; + $expected_name =~ s/_/-/g; + + if ((lc($expected_name) eq $pkg) + || (lc($expected_name) eq $base_pkg)) { + $match_found = 1; + last; + } +} + +tag "package-name-doesnt-match-sonames", "@sonames" + if @sonames && !$match_found; + +my %directories; +open(IN, '<', "index") or fail("cannot open index file index: $!"); +while () { + chomp; + next unless /^[dl]/; + my $dir = (split(' ', $_, 6))[-1]; + $dir =~ s,^\./,/,; + $dir =~ s,/+$,,; + $dir =~ s/ link to .*//; + $dir =~ s/ -> .*//; + + $directories{$dir}++; +} +close IN; + +# process all files in package +open(IN,, '<', "file-info") + or fail("cannot find file-info for $type package $pkg"); +while () { + chop; + + m/^(.+?):\s+(.*)$/o or fail("an error in the file pkg is preventing lintian from checking this package: $_"); + my ($file,$info) = ($1,$2); + + # binary or object file? + next unless ($info =~ m/^[^,]*\bELF\b/) or ($info =~ m/\bcurrent ar archive\b/); + + # Warn about Architecture: all packages that contain shared libraries, but + # only if those libraries aren't installed in a multiarch directory. The + # package may be a support package for cross-compiles. + if ($arch eq 'all') { + my ($arch) = ($file =~ m,^\./(?:usr/)?lib/([^/]+)/,); + my $multiarch = Lintian::Data->new('binaries/multiarch'); + unless ($arch and $multiarch->known($arch)) { + tag "arch-independent-package-contains-binary-or-object", "$file"; + } + } + + # ELF? + next unless $info =~ m/^[^,]*\bELF\b/o; + + if ($file =~ m,^\./etc/,) { + tag "binary-in-etc", "$file"; + } + + if ($file =~ m,^\./usr/share/,) { + tag "arch-dependent-file-in-usr-share", "$file"; + } + + # stripped? + if ($info =~ m,not stripped\s*$,o) { + # Is it an object file (which generally can not be stripped), + # a kernel module, debugging symbols, or perhaps a debugging package? + # Ocaml executables are exempted, see #252695 + unless ($file =~ m,\.k?o$, or $pkg =~ m/-dbg$/ or $pkg =~ m/debug/ + or $file =~ m,/lib/debug/, or exists $OCAML{$file}) { + tag "unstripped-binary-or-object", "$file"; + } + } else { + # stripped but a debug or profiling library? + if (($file =~ m,/lib/debug/,o) or ($file =~ m,/lib/profile/,o)) { + tag "library-in-debug-or-profile-should-not-be-stripped", "$file"; + } else { + # appropriately stripped, but is it stripped enough? + if (exists $NOTE{$file}) { + tag "binary-has-unneeded-section", "$file .note"; + } + if (exists $COMMENT{$file}) { + tag "binary-has-unneeded-section", "$file .comment"; + } + } + } + + # rpath is disallowed, except in private directories + if (exists $RPATH{$file}) { + foreach my $rpath (map {File::Spec->canonpath($_)} keys %{$RPATH{$file}}) { + next if $rpath =~ m,^/usr/lib/(games/)?\Q$pkg\E(?:/|\z),; + next if $rpath =~ m,^\$ORIGIN$,; + next if $directories{$rpath}; + tag "binary-or-shlib-defines-rpath", "$file $rpath"; + } + } + + # binary or shared object? + next unless ($info =~ m/executable/) or ($info =~ m/shared object/); + next if $type eq 'udeb'; + + # Perl library? + if ($file =~ m,^\./usr/lib/perl5/.*\.so$,) { + $has_perl_lib = 1; + } + + # Something other than detached debugging symbols in /usr/lib/debug paths. + if ($file =~ m,^\./usr/lib/debug/(lib\d*|s?bin|usr|opt|dev|emul)/,) { + if (exists($NEEDED{$file})) { + tag "debug-file-should-use-detached-symbols", $file; + } + } + + # statically linked? + if (!exists($NEEDED{$file}) || !defined($NEEDED{$file})) { + if ($info =~ m/shared object/o) { + # Some exceptions: detached debugging information and the dynamic + # loader (which itself has no dependencies). + next if ($file =~ m%^\./usr/lib/debug/%); + next if ($file =~ m%^\./lib/(?:[\w/]+/)?ld-[\d.]+\.so$%); + tag "shared-lib-without-dependency-information", "$file"; + } else { + # Some exceptions: files in /boot, /usr/lib/debug/*, named *-static or + # *.static, or *-static as package-name. + next if ($file =~ m%^./boot/%); + # klibc binaries appear to be static. + next if ($KLIBC{$file}); + # Location of debugging symbols: + next if ($file =~ m%^./usr/lib/debug/%); + next if ($file =~ /(\.|-)static$/); + next if ($pkg =~ /-static$/); + tag "statically-linked-binary", "$file"; + } + } else { + my $lib; + my $no_libc = 1; + $needs_depends_line = 1; + for $lib (@{$NEEDED{$file}}) { + if ($lib =~ /^libc\.so\.(\d+.*)/) { + $needs_libc = "libc$1"; + $needs_libc_file = $file unless $needs_libc_file; + $needs_libc_count++; + $no_libc = 0; + } + } + if ($no_libc and not $file =~ m,/libc\b,) { + if ($info =~ m/shared object/) { + tag "library-not-linked-against-libc", "$file"; + } else { + tag "program-not-linked-against-libc", "$file"; + } + } + } +} +close(IN); + +# Find the package dependencies, which is used by various checks. +my $depends = ''; +if (-f 'fields/pre-depends') { + $depends = slurp_entire_file('fields/pre-depends'); +} +if (-f 'fields/depends') { + $depends .= ', ' if $depends; + $depends .= slurp_entire_file('fields/depends'); +} +$depends =~ s/\n/ /g; + +# Check for a libc dependency. +if ($needs_depends_line) { + if ($depends && $needs_libc && $pkg !~ /^libc[\d.]+(-|\z)/) { + # Match libcXX or libcXX-*, but not libc3p0. + my $re = qr/(?:^|,)\s*\Q$needs_libc\E\b/o; + if ($depends !~ /$re/) { + my $others = ''; + $needs_libc_count--; + if ($needs_libc_count > 0) { + $others = " and $needs_libc_count others"; + } + tag "missing-dependency-on-libc", + "needed by $needs_libc_file$others"; + } + } elsif (!$depends) { + tag "missing-depends-line"; + } +} + +# Check for a Perl dependency. +if ($has_perl_lib) { + my $re = qr/(?:^|,)\s*perlapi-[\d.]+(?:\s*\[[^\]]+\])?\s*(?:,|\z)/; + unless ($depends =~ /$re/) { + tag 'missing-dependency-on-perlapi'; + } +} + +} + +1; + +# Local Variables: +# indent-tabs-mode: t +# cperl-indent-level: 4 +# End: +# vim: syntax=perl ts=8 sw=4