Skip to content

Commit b240bb8

Browse files
committed
Adjust DT_MIPS_RLD_MAP_REL dynamic section entry if present
`patchelf --set-rpath` corrupted executables on mips32el: the dynamic liker crushed with Segmentation fault when loading any executable with RPATH added that way. The problem was around the MIPS-specific mechanism of setting up the debug map pointer. When DT_MIPS_RLD_MAP_REL entry in the dynamic section is present, it holds the relative address of __RLD_MAP -- an offset relative to this dynamic section entry. Dynamic linker puts the pointer to the `r_debug` structure there. When patchelf updates the executable RPATH, it moves the .dynamic section both in the binary and in memory, while __RLD_MAP is not moved in memory, since it belongs to special .rld_map section that has type PROGBITS. So, the offset stored in DT_MIPS_RLD_MAP_REL entry is not valid anymore and should be updated. This commit adds the necessary update. In the corner case when DT_MIPS_RLD_MAP_REL is present, but .rld_map section is not, the dynamic loader writes the debug pointer to some arbitrary bytes in memory. To avoid crushes on otherwise "working" binaries, we set offset to zero so that the dynamic loader would just overwrite the dynamic section. Here we also import DT_MIPS_RLD_MAP_REL definition in elf.h form glibc commit a2057c984e4314c3740f04cf54e36c824e4c8f32. Refs: #82 Signed-off-by: Ivan A. Melnikov <[email protected]>
1 parent 374c92a commit b240bb8

File tree

4 files changed

+62
-3
lines changed

4 files changed

+62
-3
lines changed

src/elf.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1642,7 +1642,11 @@ typedef struct
16421642
PLT is writable. For a non-writable PLT, this is omitted or has a zero
16431643
value. */
16441644
#define DT_MIPS_RWPLT 0x70000034
1645-
#define DT_MIPS_NUM 0x35
1645+
/* An alternative description of the classic MIPS RLD_MAP that is usable
1646+
in a PIE as it stores a relative offset from the address of the tag
1647+
rather than an absolute address. */
1648+
#define DT_MIPS_RLD_MAP_REL 0x70000035
1649+
#define DT_MIPS_NUM 0x36
16461650

16471651
/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */
16481652

src/patchelf.cc

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,9 +1098,9 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress)
10981098
(e.g., those produced by klibc's klcc). */
10991099
auto shdrDynamic = findSection2(".dynamic");
11001100
if (shdrDynamic) {
1101-
auto dyn = (Elf_Dyn *)(contents + rdi(shdrDynamic->sh_offset));
1101+
auto dyn_table = (Elf_Dyn *) (contents + rdi(shdrDynamic->sh_offset));
11021102
unsigned int d_tag;
1103-
for ( ; (d_tag = rdi(dyn->d_tag)) != DT_NULL; dyn++)
1103+
for (auto dyn = dyn_table; (d_tag = rdi(dyn->d_tag)) != DT_NULL; dyn++)
11041104
if (d_tag == DT_STRTAB)
11051105
dyn->d_un.d_ptr = findSection(".dynstr").sh_addr;
11061106
else if (d_tag == DT_STRSZ)
@@ -1142,6 +1142,23 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress)
11421142
dyn->d_un.d_ptr = findSection(".gnu.version_r").sh_addr;
11431143
else if (d_tag == DT_VERSYM)
11441144
dyn->d_un.d_ptr = findSection(".gnu.version").sh_addr;
1145+
else if (d_tag == DT_MIPS_RLD_MAP_REL) {
1146+
/* the MIPS_RLD_MAP_REL tag stores the offset to the debug
1147+
pointer, relative to the address of the tag */
1148+
auto shdr = findSection2(".rld_map");
1149+
if (shdr) {
1150+
auto rld_map_addr = findSection(".rld_map").sh_addr;
1151+
auto dyn_offset = ((char*)dyn) - ((char*)dyn_table);
1152+
dyn->d_un.d_ptr = rld_map_addr + dyn_offset - shdrDynamic->sh_addr;
1153+
} else {
1154+
/* ELF file with DT_MIPS_RLD_MAP_REL but without .rld_map
1155+
is broken, and it's not our job to fix it; yet, we have
1156+
to find some location for dynamic loader to write the
1157+
debug pointer to; well, let's write it right here */
1158+
fprintf(stderr, "warning: DT_MIPS_RLD_MAP_REL entry is present, but .rld_map section is not\n");
1159+
dyn->d_un.d_ptr = 0;
1160+
}
1161+
}
11451162
}
11461163

11471164

tests/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ src_TESTS = \
2222
plain-fail.sh plain-run.sh shrink-rpath.sh set-interpreter-short.sh \
2323
set-interpreter-long.sh set-rpath.sh add-rpath.sh no-rpath.sh big-dynstr.sh \
2424
set-rpath-library.sh soname.sh shrink-rpath-with-allowed-prefixes.sh \
25+
set-rpath-rel-map.sh \
2526
force-rpath.sh \
2627
plain-needed.sh \
2728
output-flag.sh \

tests/set-rpath-rel-map.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#! /bin/sh -e
2+
3+
if ! objdump -p main | grep -q MIPS_RLD_MAP_REL; then
4+
echo "No MIPS_RLD_MAP_REL dynamic section entry, skipping"
5+
exit 0
6+
fi
7+
8+
SCRATCH=scratch/$(basename $0 .sh)
9+
10+
rm -rf ${SCRATCH}
11+
mkdir -p ${SCRATCH}
12+
mkdir -p ${SCRATCH}/libsA
13+
mkdir -p ${SCRATCH}/libsB
14+
15+
cp main ${SCRATCH}/
16+
cp libfoo.so ${SCRATCH}/libsA/
17+
cp libbar.so ${SCRATCH}/libsB/
18+
19+
# break the main executable by removing .rld_map section
20+
objcopy --remove-section .rld_map ${SCRATCH}/main
21+
22+
oldRPath=$(../src/patchelf --print-rpath ${SCRATCH}/main)
23+
if test -z "$oldRPath"; then oldRPath="/oops"; fi
24+
../src/patchelf --force-rpath --set-rpath $oldRPath:$(pwd)/${SCRATCH}/libsA:$(pwd)/${SCRATCH}/libsB ${SCRATCH}/main
25+
26+
if test "$(uname)" = FreeBSD; then
27+
export LD_LIBRARY_PATH=$(pwd)/${SCRATCH}/libsB
28+
fi
29+
30+
exitCode=0
31+
32+
(cd ${SCRATCH} && ./main) || exitCode=$?
33+
34+
if test "$exitCode" != 46; then
35+
echo "bad exit code!"
36+
exit 1
37+
fi

0 commit comments

Comments
 (0)