[debhelper-devel] [debhelper] 02/02: Use file header instead of filenames to detect ELF [c11]

Niels Thykier nthykier at moszumanska.debian.org
Thu Sep 14 20:34:37 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 93f3acb7a9f3a8288b34473e6fba400dc6dee984
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                   | 58 +++++++++++++++++++++++++++++++--------
 dh_strip                       | 62 ++++++++++++++++++++++++++++--------------
 lib/Debian/Debhelper/Dh_Lib.pm | 47 +++++++++++++++++++++++++++++++-
 5 files changed, 148 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..8ede4db 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,53 @@ 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 _) {
+					# Directory; skip some irrelevant directories
+					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