Skip to content

Commit b4cb6ca

Browse files
DerDakonMic92
authored andcommitted
fix corrupted library names when using --replace-needed multiple times
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.
1 parent a174cf3 commit b4cb6ca

File tree

3 files changed

+27
-1
lines changed

3 files changed

+27
-1
lines changed

src/patchelf.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,6 +1636,10 @@ void ElfFile<ElfFileParamNames>::replaceNeeded(const std::map<std::string, std::
16361636
debug("found .gnu.version_r with %i entries, strings in %s\n", verNeedNum, versionRStringsSName.c_str());
16371637

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

16401644
auto need = (Elf_Verneed *)(fileContents->data() + rdi(shdrVersionR.sh_offset));
16411645
while (verNeedNum > 0) {

tests/Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ src_TESTS = \
3838
args-from-file.sh \
3939
basic-flags.sh \
4040
set-empty-rpath.sh \
41-
phdr-corruption.sh
41+
phdr-corruption.sh \
42+
replace-needed.sh
4243

4344
build_TESTS = \
4445
$(no_rpath_arch_TESTS)

tests/replace-needed.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#! /bin/sh -e
2+
SCRATCH=scratch/$(basename $0 .sh)
3+
4+
rm -rf ${SCRATCH}
5+
mkdir -p ${SCRATCH}
6+
7+
oldNeeded=$(../src/patchelf --print-needed big-dynstr)
8+
oldLibc=$(../src/patchelf --print-needed big-dynstr | grep -v 'foo\.so')
9+
../src/patchelf --output ${SCRATCH}/big-needed --replace-needed ${oldLibc} long_long_very_long_libc.so.6 --replace-needed libfoo.so lf.so big-dynstr
10+
11+
if [ -z "$(../src/patchelf --print-needed ${SCRATCH}/big-needed | grep -Fx "long_long_very_long_libc.so.6")" ]; then
12+
echo "library long_long_very_long_libc.so.6 not found as NEEDED"
13+
../src/patchelf --print-needed ${SCRATCH}/big-needed
14+
exit 1
15+
fi
16+
17+
if [ -z "$(../src/patchelf --print-needed ${SCRATCH}/big-needed | grep -Fx "lf.so")" ]; then
18+
echo "library lf.so not found as NEEDED"
19+
../src/patchelf --print-needed ${SCRATCH}/big-needed
20+
exit 1
21+
fi

0 commit comments

Comments
 (0)