Skip to content

Commit

Permalink
Adjust DT_MIPS_RLD_MAP_REL dynamic section entry if present
Browse files Browse the repository at this point in the history
`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 <iv@altlinux.org>
  • Loading branch information
iv-m committed Aug 11, 2021
1 parent 374c92a commit b240bb8
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 3 deletions.
6 changes: 5 additions & 1 deletion src/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1642,7 +1642,11 @@ typedef struct
PLT is writable. For a non-writable PLT, this is omitted or has a zero
value. */
#define DT_MIPS_RWPLT 0x70000034
#define DT_MIPS_NUM 0x35
/* An alternative description of the classic MIPS RLD_MAP that is usable
in a PIE as it stores a relative offset from the address of the tag
rather than an absolute address. */
#define DT_MIPS_RLD_MAP_REL 0x70000035
#define DT_MIPS_NUM 0x36

/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */

Expand Down
21 changes: 19 additions & 2 deletions src/patchelf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1098,9 +1098,9 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress)
(e.g., those produced by klibc's klcc). */
auto shdrDynamic = findSection2(".dynamic");
if (shdrDynamic) {
auto dyn = (Elf_Dyn *)(contents + rdi(shdrDynamic->sh_offset));
auto dyn_table = (Elf_Dyn *) (contents + rdi(shdrDynamic->sh_offset));
unsigned int d_tag;
for ( ; (d_tag = rdi(dyn->d_tag)) != DT_NULL; dyn++)
for (auto dyn = dyn_table; (d_tag = rdi(dyn->d_tag)) != DT_NULL; dyn++)
if (d_tag == DT_STRTAB)
dyn->d_un.d_ptr = findSection(".dynstr").sh_addr;
else if (d_tag == DT_STRSZ)
Expand Down Expand Up @@ -1142,6 +1142,23 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress)
dyn->d_un.d_ptr = findSection(".gnu.version_r").sh_addr;
else if (d_tag == DT_VERSYM)
dyn->d_un.d_ptr = findSection(".gnu.version").sh_addr;
else if (d_tag == DT_MIPS_RLD_MAP_REL) {
/* the MIPS_RLD_MAP_REL tag stores the offset to the debug
pointer, relative to the address of the tag */
auto shdr = findSection2(".rld_map");
if (shdr) {
auto rld_map_addr = findSection(".rld_map").sh_addr;
auto dyn_offset = ((char*)dyn) - ((char*)dyn_table);
dyn->d_un.d_ptr = rld_map_addr + dyn_offset - shdrDynamic->sh_addr;
} else {
/* ELF file with DT_MIPS_RLD_MAP_REL but without .rld_map
is broken, and it's not our job to fix it; yet, we have
to find some location for dynamic loader to write the
debug pointer to; well, let's write it right here */
fprintf(stderr, "warning: DT_MIPS_RLD_MAP_REL entry is present, but .rld_map section is not\n");
dyn->d_un.d_ptr = 0;
}
}
}


Expand Down
1 change: 1 addition & 0 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ src_TESTS = \
plain-fail.sh plain-run.sh shrink-rpath.sh set-interpreter-short.sh \
set-interpreter-long.sh set-rpath.sh add-rpath.sh no-rpath.sh big-dynstr.sh \
set-rpath-library.sh soname.sh shrink-rpath-with-allowed-prefixes.sh \
set-rpath-rel-map.sh \
force-rpath.sh \
plain-needed.sh \
output-flag.sh \
Expand Down
37 changes: 37 additions & 0 deletions tests/set-rpath-rel-map.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#! /bin/sh -e

if ! objdump -p main | grep -q MIPS_RLD_MAP_REL; then
echo "No MIPS_RLD_MAP_REL dynamic section entry, skipping"
exit 0
fi

SCRATCH=scratch/$(basename $0 .sh)

rm -rf ${SCRATCH}
mkdir -p ${SCRATCH}
mkdir -p ${SCRATCH}/libsA
mkdir -p ${SCRATCH}/libsB

cp main ${SCRATCH}/
cp libfoo.so ${SCRATCH}/libsA/
cp libbar.so ${SCRATCH}/libsB/

# break the main executable by removing .rld_map section
objcopy --remove-section .rld_map ${SCRATCH}/main

oldRPath=$(../src/patchelf --print-rpath ${SCRATCH}/main)
if test -z "$oldRPath"; then oldRPath="/oops"; fi
../src/patchelf --force-rpath --set-rpath $oldRPath:$(pwd)/${SCRATCH}/libsA:$(pwd)/${SCRATCH}/libsB ${SCRATCH}/main

if test "$(uname)" = FreeBSD; then
export LD_LIBRARY_PATH=$(pwd)/${SCRATCH}/libsB
fi

exitCode=0

(cd ${SCRATCH} && ./main) || exitCode=$?

if test "$exitCode" != 46; then
echo "bad exit code!"
exit 1
fi

0 comments on commit b240bb8

Please sign in to comment.