Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for MIPS binaries #180

Merged
merged 2 commits into from
Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1574,9 +1574,10 @@ typedef struct

/* Legal values for p_type field of Elf32_Phdr. */

#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */
#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */
#define PT_MIPS_OPTIONS 0x70000002
#define PT_MIPS_REGINFO 0x70000000 /* Register usage information. */
#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */
#define PT_MIPS_OPTIONS 0x70000002
#define PT_MIPS_ABIFLAGS 0x70000003 /* FP mode requirement. */

/* Special program header types. */

Expand Down Expand Up @@ -1642,7 +1643,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
33 changes: 31 additions & 2 deletions src/patchelf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,18 @@ void ElfFile<ElfFileParamNames>::writeReplacedSections(Elf_Off & curOff,
}
}

/* If there is .MIPS.abiflags section, then the PT_MIPS_ABIFLAGS
segment must be sync'ed with it. */
if (sectionName == ".MIPS.abiflags") {
for (auto & phdr : phdrs) {
if (rdi(phdr.p_type) == PT_MIPS_ABIFLAGS) {
phdr.p_offset = shdr.sh_offset;
phdr.p_vaddr = phdr.p_paddr = shdr.sh_addr;
phdr.p_filesz = phdr.p_memsz = shdr.sh_size;
}
}
}

curOff += roundUp(i.second.size(), sectionAlignment);
}

Expand Down Expand Up @@ -1098,9 +1110,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 +1154,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) {
Copy link
Member

@Mic92 Mic92 Aug 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the .rld_map section be stripped just like DT_JMPREL or DT_REL?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I am asking is, is it possible to that someone has binaries where this section was stripped.
I fixed a similar issue here: #296

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Mic92, thank you for the review.

I understood what you're asking, and I need to give it some thought and run a couple of experiments.

I used to believe that elf with DT_MIPS_RLD_MAP_REL in dynamic section but without .rld_map is simply broken, but maybe patchelf needs to handle such files. If that's the case, I'll update the code here and add a test for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, elf file with DT_MIPS_RLD_MAP_REL in dynamic section but without .rld_map is indeed broken: dynamic loader (I've checked glibc 2.32) just writes the debug pointer to the address it calculates from DT_MIPS_RLD_MAP_REL entry. I guess no tool produces such binaries.

But when I just removed .rtld_map section with objcopy, the resulting binaries mostly worked -- out of sheer luck probably, but still most of them did not crash.

So I rewrote the code to handle such case. Removing DT_MIPS_RLD_MAP_REL might have been a cleaner solution, but patchelf does not remove any dynamic section entries yet and I don't think supporting binaries that were broken in the fist place worth the burden of starting to do so.

And @Mic92, thank you for pointing this case out.

/* 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