From 1ea84bebeaafd4bd22fe021ebef94e7eeb424804 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Wed, 2 Dec 2020 04:26:15 -0500 Subject: fix glibc linkage on debian again --- CHANGELOG.md | 7 ++ Makefile | 4 +- bin/remove-glibc-2.29-use | 118 ------------------------------ build/fix-glibc-function-versions | 147 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 119 deletions(-) delete mode 100755 bin/remove-glibc-2.29-use create mode 100755 build/fix-glibc-function-versions diff --git a/CHANGELOG.md b/CHANGELOG.md index f2cf5cf..451465d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased] + +### Fixed + +* `rbw` should once again be usable on systems with glibc-2.28 (such as Debian + stable). + ## [0.5.1] - 2020-12-02 ### Fixed diff --git a/Makefile b/Makefile index 937b1ab..fc783c9 100644 --- a/Makefile +++ b/Makefile @@ -12,8 +12,10 @@ build: release: @cargo build --release --all-targets - @./bin/remove-glibc-2.29-use ./target/release/rbw + @./build/fix-glibc-function-versions ./target/release/rbw + @./build/fix-glibc-function-versions ./target/release/rbw-agent @mv ./target/release/rbw.new ./target/release/rbw + @mv ./target/release/rbw-agent.new ./target/release/rbw-agent .PHONY: release test: diff --git a/bin/remove-glibc-2.29-use b/bin/remove-glibc-2.29-use deleted file mode 100755 index c23b7a6..0000000 --- a/bin/remove-glibc-2.29-use +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; -use 5.020; -use experimental 'signatures'; -no warnings 'experimental::signatures'; - -# see http://www.lightofdawn.org/wiki/wiki.cgi/NewAppsOnOldGlibc for details - -# as of currently, only `log2` is being used from GLIBC_2.29, and a -# corresponding version from GLIBC_2.2.5 does exist, so we can just swap it -# out. - -my $bin = $ARGV[0]; - -system("readelf -s $bin | grep -q GLIBC_2.29"); -if ($?) { - die "no symbols from GLIBC_2.29 found, skipping\n"; -} - -my %readelf_v = parse_readelf_v($bin); -my %readelf_s = parse_readelf_s($bin); - -if ($readelf_s{'log2@GLIBC_2.29'}{ver} != $readelf_v{'.gnu.version_r'}{'libm.so.6'}{'GLIBC_2.29'}{ver}) { - die "log2 doesn't appear to use the symbol version from GLIBC_2.29\n"; -} - -my $bin_contents = do { - local $/; - open my $bin_fh, '<', $bin or die "couldn't open $bin: $!"; - <$bin_fh> -}; - -my $gnu_version_r_offset = $readelf_v{'.gnu.version_r'}{offset}; -my $glibc_229_offset = $gnu_version_r_offset + $readelf_v{'.gnu.version_r'}{'libm.so.6'}{'GLIBC_2.29'}{offset}; -my $glibc_225_offset = $gnu_version_r_offset + $readelf_v{'.gnu.version_r'}{'libm.so.6'}{'GLIBC_2.2.5'}{offset}; - -# we could just make the GLIBC_2.29 symbol weak (as described in the above -# page), but that causes a warning to be printed every time you run the binary, -# which is obnoxious - -# use constant VER_FLG_WEAK => 0x02; -# my $prev_glibc_229_flags = substr $bin_contents, $glibc_229_offset + 0x04, 2, pack('s', VER_FLG_WEAK); -# if (unpack('s', $prev_glibc_229_flags) != 0) { -# die "GLIBC_2.29 did not have flags set to 0\n"; -# } - -# instead, we overwrite vna_hash, vna_flags, vna_other, vna_name with the 2.2.5 -# versions (but leave vna_next the same) - this causes there to be two entries -# describing GLIBC_2.2.5, but this appears to be harmless -substr($bin_contents, $glibc_229_offset + 0x00, 12, substr($bin_contents, $glibc_225_offset + 0x00, 12)); - -my $gnu_version_offset = $readelf_v{'.gnu.version'}{offset}; -my $log2_offset = $gnu_version_offset + 2 * $readelf_s{'log2@GLIBC_2.29'}{num}; - -my $prev_log2_version = substr $bin_contents, $log2_offset, 2, pack('s', $readelf_v{'.gnu.version_r'}{'libm.so.6'}{'GLIBC_2.2.5'}{ver}); -if (unpack('s', $prev_log2_version) != $readelf_v{'.gnu.version_r'}{'libm.so.6'}{'GLIBC_2.29'}{ver}) { - die "log2 doesn't appear to use the symbol version from GLIBC_2.29\n"; -} - -open my $fh, '>', "$bin.new" or die "couldn't open $bin.new: $!"; -print $fh $bin_contents; -chmod 0755, "$bin.new"; - -my $remaining = `readelf -s $bin.new | grep GLIBC_2.29`; -if (!$?) { - die "additional symbols from GLIBC_2.29 found:\n$remaining\n"; -} - -sub parse_readelf_s($bin) { - my $readelf = `readelf -s $bin`; - - my %ret; - for my $line (split "\n", $readelf) { - if ($line =~ /^ *[0-9]+:/) { - my ($num, $value, $size, $type, $bind, $vis, $ndx, $name, $ver) = split ' ', $line; - if (defined $ver) { - ($ver) = $ver =~ /\((.*)\)/; - } - if (defined $num) { - ($num) = $num =~ /(.*):/; - } - $ret{$name} = { - num => $num, - ver => $ver, - } - } - } - - %ret -} - -sub parse_readelf_v($bin) { - my $readelf = `readelf -V $bin`; - - my $section; - my $file; - my %ret; - for my $line (split "\n", $readelf) { - if ($line =~ /^Version (?:symbols|needs) section '([^']+)'/) { - $section = $1; - } - if ($line =~ /^ Addr: (?:[^ ]+) Offset: ([^ ]+)/) { - $ret{$section}{offset} = hex($1); - } - if ($line =~ /^ [^:]+: Version: [^ ]+ File: ([^ ]+)/) { - $file = $1; - } - if ($line =~ /^ ([^:]+): Name: ([^ ]+) Flags: (?:[^ ]+) Version: (.*)/) { - $ret{$section}{$file}{$2} = { - offset => hex($1), - ver => $3, - }; - } - } - - %ret -} diff --git a/build/fix-glibc-function-versions b/build/fix-glibc-function-versions new file mode 100755 index 0000000..213687a --- /dev/null +++ b/build/fix-glibc-function-versions @@ -0,0 +1,147 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use 5.020; +use experimental 'signatures'; +no warnings 'experimental::signatures'; + +# see http://www.lightofdawn.org/wiki/wiki.cgi/NewAppsOnOldGlibc for details + +# as of currently, only `log2` is being used from GLIBC_2.29 and +# `pthread_sigmask` and `pthread_getattr_np` is being used from GLIBC_2.32, and +# corresponding versions from GLIBC_2.2.5 do exist, so we can just swap them +# out. + +my $bin = $ARGV[0]; + +system("readelf -sW $bin | grep -q 'GLIBC_2.\\(29\\|32\\)'"); +if ($?) { + warn "no symbols from GLIBC_2.29 or GLIBC_2.32 found, skipping\n"; + exit(0) +} + +my %readelf_v = parse_readelf_v($bin); +my %readelf_s = parse_readelf_s($bin); + +my $bin_contents = do { + local $/; + open my $bin_fh, '<', $bin or die "couldn't open $bin: $!"; + <$bin_fh> +}; + +# we could just make the GLIBC_2.29 symbol weak (as described in the above +# page), but that causes a warning to be printed every time you run the binary, +# which is obnoxious + +# use constant VER_FLG_WEAK => 0x02; +# my $prev_glibc_229_flags = substr $bin_contents, $glibc_229_offset + 0x04, 2, pack('s', VER_FLG_WEAK); +# if (unpack('s', $prev_glibc_229_flags) != 0) { +# die "GLIBC_2.29 did not have flags set to 0\n"; +# } + +# instead, we overwrite vna_hash, vna_flags, vna_other, vna_name with the 2.2.5 +# versions (but leave vna_next the same) - this causes there to be multiple +# entries describing GLIBC_2.2.5, but this appears to be harmless +my $gnu_version_r_offset = $readelf_v{'.gnu.version_r'}{offset}; +my $fix_libc = defined($readelf_v{'.gnu.version_r'}{'libc.so.6'}{'GLIBC_2.32'}{offset}); +my $fix_libm = defined($readelf_v{'.gnu.version_r'}{'libm.so.6'}{'GLIBC_2.29'}{offset}); + +if ($fix_libc) { + my $glibc_232_libc_offset = $gnu_version_r_offset + $readelf_v{'.gnu.version_r'}{'libc.so.6'}{'GLIBC_2.32'}{offset}; + my $glibc_225_libc_offset = $gnu_version_r_offset + $readelf_v{'.gnu.version_r'}{'libc.so.6'}{'GLIBC_2.2.5'}{offset}; + substr($bin_contents, $glibc_232_libc_offset + 0x00, 12, substr($bin_contents, $glibc_225_libc_offset + 0x00, 12)); +} + +if ($fix_libm) { + my $glibc_229_libm_offset = $gnu_version_r_offset + $readelf_v{'.gnu.version_r'}{'libm.so.6'}{'GLIBC_2.29'}{offset}; + my $glibc_225_libm_offset = $gnu_version_r_offset + $readelf_v{'.gnu.version_r'}{'libm.so.6'}{'GLIBC_2.2.5'}{offset}; + substr($bin_contents, $glibc_229_libm_offset + 0x00, 12, substr($bin_contents, $glibc_225_libm_offset + 0x00, 12)); +} + +my $gnu_version_offset = $readelf_v{'.gnu.version'}{offset}; +my $fix_log2 = defined($readelf_s{'log2@GLIBC_2.29'}{ver}) && $readelf_s{'log2@GLIBC_2.29'}{ver} == $readelf_v{'.gnu.version_r'}{'libm.so.6'}{'GLIBC_2.29'}{ver}; +my $fix_pthread_sigmask = defined($readelf_s{'pthread_sigmask@GLIBC_2.32'}{ver}) && $readelf_s{'pthread_sigmask@GLIBC_2.32'}{ver} == $readelf_v{'.gnu.version_r'}{'libc.so.6'}{'GLIBC_2.32'}{ver}; +my $fix_pthread_getattr_np = defined($readelf_s{'pthread_getattr_np@GLIBC_2.32'}{ver}) && $readelf_s{'pthread_getattr_np@GLIBC_2.32'}{ver} == $readelf_v{'.gnu.version_r'}{'libc.so.6'}{'GLIBC_2.32'}{ver}; + +if ($fix_log2) { + my $log2_offset = $gnu_version_offset + 2 * $readelf_s{'log2@GLIBC_2.29'}{num}; + my $prev_log2_version = substr $bin_contents, $log2_offset, 2, pack('s', $readelf_v{'.gnu.version_r'}{'libm.so.6'}{'GLIBC_2.2.5'}{ver}); + if (unpack('s', $prev_log2_version) != $readelf_v{'.gnu.version_r'}{'libm.so.6'}{'GLIBC_2.29'}{ver}) { + die "log2 doesn't appear to use the symbol version from GLIBC_2.29\n"; + } +} + +if ($fix_pthread_sigmask) { + my $pthread_sigmask_offset = $gnu_version_offset + 2 * $readelf_s{'pthread_sigmask@GLIBC_2.32'}{num}; + my $prev_pthread_sigmask_version = substr $bin_contents, $pthread_sigmask_offset, 2, pack('s', $readelf_v{'.gnu.version_r'}{'libc.so.6'}{'GLIBC_2.2.5'}{ver}); + if (unpack('s', $prev_pthread_sigmask_version) != $readelf_v{'.gnu.version_r'}{'libc.so.6'}{'GLIBC_2.32'}{ver}) { + die "pthread_sigmask doesn't appear to use the symbol version from GLIBC_2.32\n"; + } +} + +if ($fix_pthread_getattr_np) { + my $pthread_getattr_np_offset = $gnu_version_offset + 2 * $readelf_s{'pthread_getattr_np@GLIBC_2.32'}{num}; + my $prev_pthread_getattr_np_version = substr $bin_contents, $pthread_getattr_np_offset, 2, pack('s', $readelf_v{'.gnu.version_r'}{'libc.so.6'}{'GLIBC_2.2.5'}{ver}); + if (unpack('s', $prev_pthread_getattr_np_version) != $readelf_v{'.gnu.version_r'}{'libc.so.6'}{'GLIBC_2.32'}{ver}) { + die "pthread_getattr_np doesn't appear to use the symbol version from GLIBC_2.32\n"; + } +} + +open my $fh, '>', "$bin.new" or die "couldn't open $bin.new: $!"; +print $fh $bin_contents; +chmod 0755, "$bin.new"; + +my $remaining = `readelf -sW $bin.new | grep 'GLIBC_2.\\(29\\|32\\)'`; +if (!$?) { + die "additional symbols from GLIBC_2.29 or GLIBC_2.32 found:\n$remaining\n"; +} + +sub parse_readelf_s($bin) { + my $readelf = `readelf -sW $bin`; + + my %ret; + for my $line (split "\n", $readelf) { + if ($line =~ /^ *[0-9]+:/) { + my ($num, $value, $size, $type, $bind, $vis, $ndx, $name, $ver) = split ' ', $line; + if (defined $ver) { + ($ver) = $ver =~ /\((.*)\)/; + } + if (defined $num) { + ($num) = $num =~ /(.*):/; + } + $ret{$name} = { + num => $num, + ver => $ver, + } + } + } + + %ret +} + +sub parse_readelf_v($bin) { + my $readelf = `readelf -V $bin`; + + my $section; + my $file; + my %ret; + for my $line (split "\n", $readelf) { + if ($line =~ /^Version (?:symbols|needs) section '([^']+)'/) { + $section = $1; + } + if ($line =~ /^ Addr: (?:[^ ]+) Offset: ([^ ]+)/) { + $ret{$section}{offset} = hex($1); + } + if ($line =~ /^ [^:]+: Version: [^ ]+ File: ([^ ]+)/) { + $file = $1; + } + if ($line =~ /^ ([^:]+): Name: ([^ ]+) Flags: (?:[^ ]+) Version: (.*)/) { + $ret{$section}{$file}{$2} = { + offset => hex($1), + ver => $3, + }; + } + } + + %ret +} -- cgit v1.2.3-54-g00ecf