Skip to content

Commit

Permalink
fix corrupted library names when using --replace-needed multiple times
Browse files Browse the repository at this point in the history
When it happens that the .gnu.version_r stores the strings in .dynstr it can
come to corruption of the library names written into DT_NEEDED:

-the library names in DT_NEEDED are replaced, new entries are written to the end
 of .dynstr
-the version library names are replaced, and written to the end of the string
 section.

If the section for the version strings is also ".dynstr", the previous
modifications were _not_ taken into account and things were written from the old
end of .dynstr again. The order in which these strings were written is not the
same as the previous replacement, so things would end up with the same size, but
different offsets. The .gnu.version_r table is correct, the file contents are
fine, but the offsets in the DT_NEEDED entries are wrong. Since they are printed
as 0-terminated strings the first one replaced will always be shown correct,
which also is the case if the argument is only used once as the string is
replaced with itself afterwards.
  • Loading branch information
DerDakon committed Mar 16, 2021
1 parent f376fe6 commit 0c85139
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/patchelf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,10 @@ void ElfFile<ElfFileParamNames>::replaceNeeded(const std::map<std::string, std::
debug("found .gnu.version_r with %i entries, strings in %s\n", verNeedNum, versionRStringsSName.c_str());

unsigned int verStrAddedBytes = 0;
// It may be that it is .dynstr again, in which case we must take the already
// added bytes into account.
if (versionRStringsSName == ".dynstr")
verStrAddedBytes += dynStrAddedBytes;

auto need = (Elf_Verneed *)(contents + rdi(shdrVersionR.sh_offset));
while (verNeedNum > 0) {
Expand Down
3 changes: 2 additions & 1 deletion tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ src_TESTS = \
output-flag.sh \
no-rpath-pie-powerpc.sh \
build-id.sh \
invalid-elf.sh
invalid-elf.sh \
replace-needed.sh

build_TESTS = \
$(no_rpath_arch_TESTS)
Expand Down
21 changes: 21 additions & 0 deletions tests/replace-needed.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#! /bin/sh -e
SCRATCH=scratch/$(basename $0 .sh)

rm -rf ${SCRATCH}
mkdir -p ${SCRATCH}

oldNeeded=$(../src/patchelf --print-needed big-dynstr)
oldLibc=$(../src/patchelf --print-needed big-dynstr | grep -v 'foo\.so')
../src/patchelf --output ${SCRATCH}/big-needed --replace-needed ${oldLibc} long_long_very_long_libc.so.6 --replace-needed libfoo.so lf.so big-dynstr

if [ -z "$(../src/patchelf --print-needed ${SCRATCH}/big-needed | grep -Fx "long_long_very_long_libc.so.6")" ]; then
echo "library long_long_very_long_libc.so.6 not found as NEEDED"
../src/patchelf --print-needed ${SCRATCH}/big-needed
exit 1
fi

if [ -z "$(../src/patchelf --print-needed ${SCRATCH}/big-needed | grep -Fx "lf.so")" ]; then
echo "library lf.so not found as NEEDED"
../src/patchelf --print-needed ${SCRATCH}/big-needed
exit 1
fi

0 comments on commit 0c85139

Please sign in to comment.