Added lots more modules from lintian. Maemian appears to work.
[maemian] / checks / binaries
diff --git a/checks/binaries b/checks/binaries
new file mode 100644 (file)
index 0000000..383c6d0
--- /dev/null
@@ -0,0 +1,363 @@
+# 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 Maemian::binaries;
+use strict;
+use Tags;
+use Util;
+use Spelling;
+
+use File::Spec;
+
+# Table based on checks/emdebian's %archdetecttable, as found in
+# emdebian-tools.
+our %ARCH_REGEX = (
+       '32'             => qr'ELF 32-bit',
+       '64'             => qr'ELF 64-bit',
+       'alpha'          => qr'ELF 64-bit LSB .* Alpha',
+       'amd64'          => qr'ELF 64-bit LSB .* x86-64, .* (?:GNU/Linux|(?!GNU))',
+       'arm'            => qr'ELF 32-bit LSB .* ARM, version \d,',
+       'armeb'          => qr'ELF 32-bit MSB .* ARM',
+       'armel'          => qr'ELF 32-bit LSB .* \(SYSV\)',
+       'hppa'           => qr'ELF 32-bit MSB .* PA-RISC',
+       'hurd-i386'      => qr'ELF 32-bit LSB .* Intel 80386, .* (?:GNU/Hurd|(?!GNU))',
+       'i386'           => qr'ELF 32-bit LSB .* 80386, .* (?:GNU/Linux|(?!GNU))',
+       'ia64'           => qr'ELF 64-bit LSB .* IA-64',
+       'kfreebsd-amd64' => qr'ELF 64-bit LSB .* x86-64, .* (?:GNU/kFreeBSD|(?!GNU))',
+       'kfreebsd-i386'  => qr'ELF 32-bit LSB .* 80386, .* (?:GNU/kFreeBSD|(?!GNU))',
+       'lpia'           => qr'ELF 32-bit LSB .* 80386, .* (?:GNU/Linux|(?!GNU))',
+       'm32r'           => qr'ELF 32-bit MSB .* M32R',
+       'm68k'           => qr'ELF 32-bit MSB .* 680[02]0',
+       'mips'           => qr'ELF 32-bit MSB .* MIPS',
+       'mipsel'         => qr'ELF 32-bit LSB .* MIPS',
+       'mips64'         => qr'ELF 64-bit MSB .* MIPS',
+       'mipsel64'       => qr'ELF 64-bit LSB .* MIPS',
+       'powerpc'        => qr'ELF 32-bit MSB .* PowerPC',
+       'ppc64'          => qr'ELF 64-bit MSB .* PowerPC',
+       's390'           => qr'ELF 32-bit MSB .* S.390',
+       's390x'          => qr'ELF 64-bit MSB .* S.390',
+       'sparc'          => qr'ELF 32-bit MSB .* SPARC',
+       'sparc64'        => qr'ELF 64-bit MSB .* SPARC');
+
+our $multiarch;
+
+sub run {
+
+my $pkg = shift;
+my $type = shift;
+my $info = 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 %SONAME;
+
+$arch = $info->field('architecture');
+
+foreach my $file (sort keys %{$info->objdump_info}) {
+    my $objdump = $info->objdump_info->{$file};
+    $file = './' . $file;
+
+    if (defined $objdump->{SONAME}) {
+       foreach my $soname (@{$objdump->{SONAME}}) {
+           $SONAME{$soname} ||= [];
+           push @{$SONAME{$soname}}, $file;
+       }
+    }
+    foreach my $symbol (@{$objdump->{SYMBOLS}}) {
+       my ($foo, $sec, $sym) = @{$symbol};
+       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";
+           }
+       }
+    }
+    foreach (@{$objdump->{NOTES}}) {
+       if (m/^File format not recognized$/) {
+            tag "apparently-corrupted-elf-binary", "$file";
+       } elsif (m/^File truncated$/) {
+            tag "apparently-truncated-elf-binary", "$file";
+       } elsif (m/^Packed with UPX$/) {
+           tag "binary-file-compressed-with-upx", "$file";
+       } elsif (m/^Invalid operation$/) {
+           tag "binary-with-bad-dynamic-table", "$file" unless $file =~ m%^\./usr/lib/debug/%;
+       }
+    }
+}
+
+# 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;
+foreach (sort keys %{$info->file_info}) {
+    next unless length $_;
+    my $data = $info->file_info->{$_};
+    next unless $data =~ /^directory$/ or $data =~ / link to /;
+    $directories{"/$_"}++;
+}
+
+# If we have an unknown architecture, pretend that all binaries are fine.
+if ($arch ne 'all' and not exists($ARCH_REGEX{$arch})) {
+    $ARCH_REGEX{$arch} = qr/./;
+}
+
+# process all files in package
+foreach my $file (sort keys %{$info->file_info}) {
+    my $fileinfo = $info->file_info->{$file};
+    my $objdump = $info->objdump_info->{$file};
+
+    $file = './' . $file;
+
+    # binary or object file?
+    next unless ($fileinfo =~ m/^[^,]*\bELF\b/) or ($fileinfo =~ 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/([^/]+)/,);
+       $multiarch = Maemian::Data->new('binaries/multiarch')
+           unless defined($multiarch);
+       unless ($arch and $multiarch->known($arch)) {
+           tag "arch-independent-package-contains-binary-or-object", "$file";
+       }
+    }
+
+    # ELF?
+    next unless $fileinfo =~ 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";
+    }
+
+    if ($arch ne 'all' and $fileinfo !~ m/$ARCH_REGEX{$arch}/) {
+       if ($file =~ m,/lib(\d{2})/, or $file =~ m,/emul/ia(\d{2}),) {
+           tag "binary-from-other-architecture", $file
+               unless ($fileinfo =~ m/$ARCH_REGEX{$1}/);
+       } elsif ($arch eq 'amd64' and $fileinfo =~ m/$ARCH_REGEX{i386}/) {
+           # Ignore i386 binaries in amd64 packages for right now.
+       } else {
+           $multiarch = Maemian::Data->new('binaries/multiarch')
+               unless defined($multiarch);
+           tag "binary-from-other-architecture", $file
+               unless (grep { $file =~ m,/\Q$_\E/, } $multiarch->all);
+       }
+    }
+
+    # stripped?
+    if ($fileinfo =~ 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 $objdump->{OCAML}) {
+           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 $objdump->{NOTE_SECTION}) {
+               tag "binary-has-unneeded-section", "$file .note";
+           }
+           if (exists $objdump->{COMMENT_SECTION}) {
+               tag "binary-has-unneeded-section", "$file .comment";
+           }
+       }
+    }
+
+    # rpath is disallowed, except in private directories
+    if (exists $objdump->{RPATH}) {
+       foreach my $rpath (map {File::Spec->canonpath($_)} keys %{$objdump->{RPATH}}) {
+           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";
+       }
+    }
+
+    my $strings = slurp_entire_file("strings/$file");
+    spelling_check('spelling-error-in-binary', $strings, $file);
+
+    if ($pkg !~ m/^zlib.+/
+       and $strings =~ /(?:in|de)flate (?:\d[ \w.\-]{1,20}[\w.\-])/m) {
+       tag "embedded-zlib", $file;
+    }
+
+    # binary or shared object?
+    next unless ($fileinfo =~ m/executable/) or ($fileinfo =~ 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($objdump->{NEEDED})) {
+           tag "debug-file-should-use-detached-symbols", $file;
+       }
+    }
+
+    # statically linked?
+    if (!exists($objdump->{NEEDED}) || !defined($objdump->{NEEDED})) {
+       if ($fileinfo =~ 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 ($objdump->{KLIBC});
+           # 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 (@{$objdump->{NEEDED}}) {
+           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 ($fileinfo =~ m/shared object/) {
+               tag "library-not-linked-against-libc", "$file";
+           } else {
+               tag "program-not-linked-against-libc", "$file";
+           }
+       }
+    }
+}
+
+# Find the package dependencies, which is used by various checks.
+my $depends = '';
+if (defined $info->field('pre-depends')) {
+    $depends = $info->field('pre-depends');
+}
+if (defined $info->field('depends')) {
+    $depends .= ', ' if $depends;
+    $depends .= $info->field('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/;
+        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