[debhelper-devel] [debhelper] 01/01: Use file header instead of filenames to detect ELF [c11]
Niels Thykier
nthykier at moszumanska.debian.org
Sat Sep 16 08:41:00 UTC 2017
This is an automated email from the git hooks/post-receive script.
nthykier pushed a commit to branch bug-35733-elf-detection
in repository debhelper.
commit 93e35efa740c695d8390851410a5331b0378c523
Author: Niels Thykier <niels at thykier.net>
Date: Thu Sep 14 20:31:18 2017 +0000
Use file header instead of filenames to detect ELF [c11]
Signed-off-by: Niels Thykier <niels at thykier.net>
---
debhelper.pod | 9 ++++++
debian/changelog | 4 +++
dh_shlibdeps | 60 ++++++++++++++++++++++++++++++++--------
dh_strip | 62 ++++++++++++++++++++++++++++--------------
lib/Debian/Debhelper/Dh_Lib.pm | 47 +++++++++++++++++++++++++++++++-
5 files changed, 150 insertions(+), 32 deletions(-)
diff --git a/debhelper.pod b/debhelper.pod
index 321f8d7..8d78e64 100644
--- a/debhelper.pod
+++ b/debhelper.pod
@@ -722,6 +722,15 @@ the copyright file and the changelog file.
The B<--doc-main-package> option can be used when the auto-detection
is insufficient.
+=item -
+
+The B<dh_strip> and B<dh_shlibdeps> tools no longer uses filename
+patterns to determine which files to process. Instead, they read the
+ELF header to determine if a given file is an shared object or an
+ELF executable.
+
+This may cause the tools to process more files than previously.
+
=back
=back
diff --git a/debian/changelog b/debian/changelog
index da42bd0..9eac290 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -5,6 +5,10 @@ debhelper (10.9) UNRELEASED; urgency=medium
0644. Thanks to Jonas Smedegaard for the suggestion.
(Closes: #875586)
* dh_shlibdeps: Disable unused file(1) tests.
+ * dh_strip: In compat 11, rely on file headers/magic to
+ determine whether files are ELF binaries worth processing
+ instead of using filenames/extensions. (Closes: #35733)
+ * dh_shlibdeps: Ditto.
-- Niels Thykier <niels at thykier.net> Sat, 09 Sep 2017 15:32:22 +0000
diff --git a/dh_shlibdeps b/dh_shlibdeps
index eb549fe..9322c89 100755
--- a/dh_shlibdeps
+++ b/dh_shlibdeps
@@ -9,6 +9,7 @@ dh_shlibdeps - calculate shared library dependencies
use strict;
use warnings;
use Cwd;
+use File::Find;
use Debian::Debhelper::Dh_Lib;
our $VERSION = DH_BUILTIN_VERSION;
@@ -118,18 +119,55 @@ on_pkgs_in_parallel {
if (defined($dh{EXCLUDE_FIND}) && $dh{EXCLUDE_FIND} ne '') {
$find_options="! \\( $dh{EXCLUDE_FIND} \\)";
}
- foreach my $file (split(/\n/,`find $tmp -type f \\( -perm /111 -or -name "*.so*" -or -name "*.cmxs" -or -name "*.node" \\) $find_options -print`)) {
- # Prune directories that contain separated debug symbols.
- # CAVEAT: There are files in /usr/lib/debug that are not detached debug symbols,
- # which should be processed. (see #865982)
- next if $file=~m!^\Q$tmp\E/usr/lib/debug/(lib|lib64|usr|bin|sbin|opt|dev|emul|\.build-id)/!;
- # TODO this is slow, optimize. Ie, file can run once on
- # multiple files..
- $ff = qx_cmd('file', '-e', 'apptype', '-e', 'ascii', '-e', 'encoding',
- '-e', 'cdf', '-e', 'compress', '-e', 'tar', $file);
- if ($ff=~m/ELF/ && $ff!~/statically linked/) {
- push @filelist,$file;
+ next if not -d $tmp;
+ if (compat(10)) {
+ foreach my $file (split(/\n/, `find $tmp -type f \\( -perm /111 -or -name "*.so*" -or -name "*.cmxs" -or -name "*.node" \\) $find_options -print`)) {
+ # Prune directories that contain separated debug symbols.
+ # CAVEAT: There are files in /usr/lib/debug that are not detached debug symbols,
+ # which should be processed. (see #865982)
+ next if $file =~ m!^\Q$tmp\E/usr/lib/debug/(lib|lib64|usr|bin|sbin|opt|dev|emul|\.build-id)/!;
+ # TODO this is slow, optimize. Ie, file can run once on
+ # multiple files..
+ $ff = qx_cmd('file', '-e', 'apptype', '-e', 'ascii', '-e', 'encoding',
+ '-e', 'cdf', '-e', 'compress', '-e', 'tar', $file);
+ if ($ff =~ m/ELF/ && $ff !~ /statically linked/) {
+ push @filelist, $file;
+ }
}
+ } else {
+ my $find_elf_files = sub {
+ my $fn = $_;
+ return if -l $fn; # Ignore symlinks
+ # See if we were asked to exclude this file.
+ # Note that we have to test on the full filename, including directory.
+ foreach my $f (@{$dh{EXCLUDE}}) {
+ if ($fn =~ m/\Q$f\E/) {
+ $File::Find::prune = 1 if -d _;
+ return;
+ }
+ }
+ if (-d _) {
+ # Prune directories that contain separated debug symbols.
+ # CAVEAT: There are files in /usr/lib/debug that are not detached debug symbols,
+ # which should be processed. (see #865982)
+ if ($fn =~ m!^\Q$tmp\E/usr/lib/debug/(lib|lib64|usr|bin|sbin|opt|dev|emul|\.build-id)/!) {
+ $File::Find::prune = 1;
+ }
+ return;
+ }
+
+ return if not -f _;
+ return if not is_so_or_exec_elf_file($fn);
+ $ff = qx_cmd('file', '-e', 'apptype', '-e', 'ascii', '-e', 'encoding',
+ '-e', 'cdf', '-e', 'compress', '-e', 'tar', $fn);
+ if ($ff =~ m/ELF/ && $ff !~ /statically linked/) {
+ push(@filelist, $fn);
+ }
+ };
+ find({
+ wanted => $find_elf_files,
+ no_chdir => 1,
+ }, $tmp);
}
if (@filelist) {
diff --git a/dh_strip b/dh_strip
index b1e767c..edda179 100755
--- a/dh_strip
+++ b/dh_strip
@@ -181,35 +181,57 @@ sub testfile {
# See if we were asked to exclude this file.
# Note that we have to test on the full filename, including directory.
foreach my $f (@{$dh{EXCLUDE}}) {
- return if ($fn=~m/\Q$f\E/);
+ if ($fn =~ m/\Q$f\E/) {
+ $File::Find::prune = 1 if -d _;
+ return;
+ }
}
+ return if -d _;
# Is it a debug library in a debug subdir?
return if $fn=~m/debug\/.*\.so/;
- # Does its filename look like a shared library?
- # - *.cmxs are OCaml native code shared libraries
- # - *.node are also native ELF binaries (for node-js)
- if ($fn =~ m/\.(?:so.*?|cmxs|node)$/) {
- # Ok, do the expensive test.
- my $type=get_file_type($fn, 1);
- if ($type=~m/ELF.*shared/) {
- push @shared_libs, $fn;
- return;
+ if (compat(10)) {
+ # In compat 10 and earlier, we used filenames and file(1)
+
+ # Does its filename look like a shared library?
+ # - *.cmxs are OCaml native code shared libraries
+ # - *.node are also native ELF binaries (for node-js)
+ if ($fn =~ m/\.(?:so.*?|cmxs|node)$/) {
+ # Ok, do the expensive test.
+ my $type = get_file_type($fn, 1);
+ if ($type =~ m/ELF.*shared/) {
+ push @shared_libs, $fn;
+ return;
+ }
}
- }
-
- # Is it executable? -x isn't good enough, so we need to use stat.
- my (undef,undef,$mode,undef)=stat(_);
- if ($mode & 0111) {
- # Ok, expensive test.
- my $type=get_file_type($fn, 1);
- if ($type=~m/ELF.*(executable|shared)/) {
- push @executables, $fn;
+
+ # Is it executable? -x isn't good enough, so we need to use stat.
+ my (undef, undef, $mode, undef) = stat(_);
+ if ($mode & 0111) {
+ # Ok, expensive test.
+ my $type = get_file_type($fn, 1);
+ if ($type =~ m/ELF.*(executable|shared)/) {
+ push(@executables, $fn);
+ return;
+ }
+ }
+ } else {
+ # In compat 11, we check the ELF header manually (because bulking file(1) is a pain and
+ # it is too slow otherwise)
+
+ # Exploit the previous stat call, so we can check if it is executable or not (-x is not good enough
+ # for this test)
+ my (undef, undef, $mode, undef) = stat(_);
+ if (is_so_or_exec_elf_file($fn)) {
+ if ($mode & 0111) {
+ push(@executables, $fn);
+ } else {
+ push(@shared_libs, $fn);
+ }
return;
}
}
-
# Is it a static library, and not a debug library?
if ($fn =~ m/\/lib[^\/]*\.a$/ && $fn !~ m/.*_g\.a$/) {
# Is it a binary file, or something else (maybe a linker
diff --git a/lib/Debian/Debhelper/Dh_Lib.pm b/lib/Debian/Debhelper/Dh_Lib.pm
index e840b9d..80a6543 100644
--- a/lib/Debian/Debhelper/Dh_Lib.pm
+++ b/lib/Debian/Debhelper/Dh_Lib.pm
@@ -64,7 +64,7 @@ our (@EXPORT, %dh);
&glob_expand_error_handler_warn_and_discard &glob_expand
&glob_expand_error_handler_silently_ignore DH_BUILTIN_VERSION
&print_and_complex_doit &default_sourcedir &qx_cmd
- &compute_doc_main_package
+ &compute_doc_main_package &is_so_or_exec_elf_file
);
# The Makefile changes this if debhelper is installed in a PREFIX.
@@ -1928,6 +1928,51 @@ sub log_installed_files {
return 1;
}
+use constant {
+ # The ELF header is at least 0x32 bytes (32bit); any filer shorter than that is not an ELF file
+ ELF_MIN_LENGTH => 0x32,
+ ELF_MAGIC => "\x7FELF",
+ ELF_ENDIAN_LE => 0x01,
+ ELF_ENDIAN_BE => 0x02,
+ ELF_TYPE_EXECUTABLE => 0x0002,
+ ELF_TYPE_SHARED_OBJECT => 0x0003,
+};
+
+sub is_so_or_exec_elf_file {
+ my ($file) = @_;
+ open(my $fd, '<:raw', $file) or error("open $file: $!");
+ my $buflen = 0;
+ my ($buf, $endian);
+ while ($buflen < ELF_MIN_LENGTH) {
+ my $r = read($fd, $buf, ELF_MIN_LENGTH - $buflen, $buflen) // error("read ($file): $!");
+ last if $r == 0; # EOF
+ $buflen += $r
+ }
+ close($fd);
+ return 0 if $buflen < ELF_MIN_LENGTH;
+
+ return 0 if substr($buf, 0x00, 4) ne ELF_MAGIC;
+ $endian = unpack('c', substr($buf, 0x05, 1));
+ my ($long_format, $short_format);
+
+ if ($endian == ELF_ENDIAN_BE) {
+ $long_format = 'N';
+ $short_format = 'n';
+ } elsif ($endian == ELF_ENDIAN_LE) {
+ $long_format = 'V';
+ $short_format = 'v';
+ } else {
+ return 0;
+ }
+ my $elf_version = substr($buf, 0x14, 4);
+ my $elf_type = substr($buf, 0x10, 2);
+
+
+ return 0 if unpack($long_format, $elf_version) != 0x00000001;
+ my $elf_type_unpacked = unpack($short_format, $elf_type);
+ return 0 if $elf_type_unpacked != ELF_TYPE_EXECUTABLE and $elf_type_unpacked != ELF_TYPE_SHARED_OBJECT;
+ return 1;
+}
sub on_pkgs_in_parallel(&) {
unshift(@_, $dh{DOPACKAGES});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debhelper/debhelper.git
More information about the debhelper-devel
mailing list