Skip to content

Commit 4833651

Browse files
committed
Allocate PHT & SHT at the end of the *.elf file
1 parent 769337c commit 4833651

File tree

6 files changed

+67
-81
lines changed

6 files changed

+67
-81
lines changed

BUGS

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/patchelf.cc

Lines changed: 56 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -555,8 +555,7 @@ void ElfFile<ElfFileParamNames>::shiftFile(unsigned int extraPages, size_t start
555555

556556
assert(splitIndex != -1);
557557

558-
/* Add a segment that maps the new program/section headers and
559-
PT_INTERP segment into memory. Otherwise glibc will choke. */
558+
/* Add another PT_LOAD segment loading the data we've split above. */
560559
phdrs.resize(rdi(hdr()->e_phnum) + 1);
561560
wri(hdr()->e_phnum, rdi(hdr()->e_phnum) + 1);
562561
Elf_Phdr & phdr = phdrs.at(rdi(hdr()->e_phnum) - 1);
@@ -823,28 +822,16 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
823822
unsigned int num_notes = std::count_if(shdrs.begin(), shdrs.end(),
824823
[this](Elf_Shdr shdr) { return rdi(shdr.sh_type) == SHT_NOTE; });
825824

826-
/* Because we're adding a new section header, we're necessarily increasing
827-
the size of the program header table. This can cause the first section
828-
to overlap the program header table in memory; we need to shift the first
829-
few segments to someplace else. */
830-
/* Some sections may already be replaced so account for that */
831-
unsigned int i = 1;
832-
Elf_Addr pht_size = sizeof(Elf_Ehdr) + (phdrs.size() + num_notes + 1)*sizeof(Elf_Phdr);
833-
while( i < rdi(hdr()->e_shnum) && rdi(shdrs.at(i).sh_offset) <= pht_size ) {
834-
if (not haveReplacedSection(getSectionName(shdrs.at(i))))
835-
replaceSection(getSectionName(shdrs.at(i)), rdi(shdrs.at(i).sh_size));
836-
i++;
837-
}
838-
bool moveHeaderTableToTheEnd = rdi(hdr()->e_shoff) < pht_size;
825+
/* Compute the total space needed for the replaced sections, pessimistically
826+
assuming we're going to need one more to account for new PT_LOAD covering
827+
relocated PHDR */
828+
off_t phtSize = roundUp((phdrs.size() + num_notes + 1) * sizeof(Elf_Phdr), sectionAlignment);
829+
off_t shtSize = roundUp(rdi(hdr()->e_shnum) * rdi(hdr()->e_shentsize), sectionAlignment);
830+
off_t neededSpace = phtSize + shtSize;
839831

840-
/* Compute the total space needed for the replaced sections */
841-
off_t neededSpace = 0;
842832
for (auto & s : replacedSections)
843833
neededSpace += roundUp(s.second.size(), sectionAlignment);
844834

845-
off_t headerTableSpace = roundUp(rdi(hdr()->e_shnum) * rdi(hdr()->e_shentsize), sectionAlignment);
846-
if (moveHeaderTableToTheEnd)
847-
neededSpace += headerTableSpace;
848835
debug("needed space is %d\n", neededSpace);
849836

850837
Elf_Off startOffset = roundUp(fileContents->size(), alignStartPage);
@@ -853,45 +840,32 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
853840
// section segment is strictly smaller than the file (and not same size).
854841
// By making it one byte larger, we don't break readelf.
855842
off_t binutilsQuirkPadding = 1;
856-
fileContents->resize(startOffset + neededSpace + binutilsQuirkPadding, 0);
857-
858-
/* Even though this file is of type ET_DYN, it could actually be
859-
an executable. For instance, Gold produces executables marked
860-
ET_DYN as does LD when linking with pie. If we move PT_PHDR, it
861-
has to stay in the first PT_LOAD segment or any subsequent ones
862-
if they're continuous in memory due to linux kernel constraints
863-
(see BUGS). Since the end of the file would be after bss, we can't
864-
move PHDR there, we therefore choose to leave PT_PHDR where it is but
865-
move enough following sections such that we can add the extra PT_LOAD
866-
section to it. This PT_LOAD segment ensures the sections at the end of
867-
the file are mapped into memory for ld.so to process.
868-
We can't use the approach in rewriteSectionsExecutable()
869-
since DYN executables tend to start at virtual address 0, so
870-
rewriteSectionsExecutable() won't work because it doesn't have
871-
any virtual address space to grow downwards into. */
872-
if (isExecutable && startOffset > startPage) {
873-
debug("shifting new PT_LOAD segment by %d bytes to work around a Linux kernel bug\n", startOffset - startPage);
874-
startPage = startOffset;
875-
}
876843

877-
wri(hdr()->e_phoff, sizeof(Elf_Ehdr));
844+
fileContents->resize(startOffset + neededSpace + binutilsQuirkPadding, 0);
878845

879-
bool needNewSegment = true;
846+
Elf_Addr phdrAddress = 0;
880847
auto& lastSeg = phdrs.back();
881-
/* Try to extend the last segment to include replaced sections */
848+
849+
/* As an optimization, instead of allocating a new PT_LOAD segment, try
850+
expanding the last one */
882851
if (!phdrs.empty() &&
883852
rdi(lastSeg.p_type) == PT_LOAD &&
884853
rdi(lastSeg.p_flags) == (PF_R | PF_W) &&
885854
rdi(lastSeg.p_align) == alignStartPage) {
886855
auto segEnd = roundUp(rdi(lastSeg.p_offset) + rdi(lastSeg.p_memsz), alignStartPage);
856+
887857
if (segEnd == startOffset) {
888858
auto newSz = startOffset + neededSpace - rdi(lastSeg.p_offset);
859+
889860
wri(lastSeg.p_filesz, wri(lastSeg.p_memsz, newSz));
890-
needNewSegment = false;
861+
862+
phdrAddress = rdi(lastSeg.p_vaddr) + newSz - neededSpace;
891863
}
892864
}
893865

894-
if (needNewSegment) {
866+
if (phdrAddress == 0) {
867+
debug("allocating new PT_LOAD segment for pht\n");
868+
895869
/* Add a segment that maps the replaced sections into memory. */
896870
phdrs.resize(rdi(hdr()->e_phnum) + 1);
897871
wri(hdr()->e_phnum, rdi(hdr()->e_phnum) + 1);
@@ -903,25 +877,37 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
903877
wri(phdr.p_flags, PF_R | PF_W);
904878
wri(phdr.p_align, alignStartPage);
905879
assert(startPage % alignStartPage == startOffset % alignStartPage);
880+
881+
phdrAddress = startPage;
906882
}
907883

908884
normalizeNoteSegments();
909885

910-
911886
/* Write out the replaced sections. */
912887
Elf_Off curOff = startOffset;
913888

914-
if (moveHeaderTableToTheEnd) {
915-
debug("Moving the shtable to offset %d\n", curOff);
916-
wri(hdr()->e_shoff, curOff);
917-
curOff += headerTableSpace;
918-
}
889+
debug("rewriting pht from offset 0x%x to offset 0x%x (size %d)\n",
890+
rdi(hdr()->e_phoff), curOff, phtSize);
891+
892+
wri(hdr()->e_phoff, curOff);
893+
curOff += phtSize;
894+
895+
// ---
896+
897+
debug("rewriting sht from offset 0x%x to offset 0x%x (size %d)\n",
898+
rdi(hdr()->e_shoff), curOff, shtSize);
919899

900+
wri(hdr()->e_shoff, curOff);
901+
curOff += shtSize;
902+
903+
// ---
904+
905+
/* Write out the replaced sections. */
920906
writeReplacedSections(curOff, startPage, startOffset);
921907
assert(curOff == startOffset + neededSpace);
922908

923909
/* Write out the updated program and section headers */
924-
rewriteHeaders(firstPage + rdi(hdr()->e_phoff));
910+
rewriteHeaders(phdrAddress);
925911
}
926912

927913
static bool noSort = false;
@@ -1035,32 +1021,35 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsExecutable()
10351021

10361022
firstPage -= neededPages * getPageSize();
10371023
startOffset += neededPages * getPageSize();
1038-
} else {
1039-
Elf_Off rewrittenSectionsOffset = sizeof(Elf_Ehdr) + phdrs.size() * sizeof(Elf_Phdr);
1040-
for (auto& phdr : phdrs)
1041-
if (rdi(phdr.p_type) == PT_LOAD &&
1042-
rdi(phdr.p_offset) <= rewrittenSectionsOffset &&
1043-
rdi(phdr.p_offset) + rdi(phdr.p_filesz) > rewrittenSectionsOffset &&
1044-
rdi(phdr.p_filesz) < neededSpace)
1045-
{
1046-
wri(phdr.p_filesz, neededSpace);
1047-
wri(phdr.p_memsz, neededSpace);
1048-
break;
1049-
}
10501024
}
10511025

1026+
Elf_Off curOff = sizeof(Elf_Ehdr) + phdrs.size() * sizeof(Elf_Phdr);
1027+
1028+
/* Ensure PHDR is covered by a LOAD segment.
1029+
1030+
Because PHDR is supposed to have been covered by such section before, in
1031+
here we assume that we don't have to create any new section, but rather
1032+
extend the existing one. */
1033+
for (auto& phdr : phdrs)
1034+
if (rdi(phdr.p_type) == PT_LOAD &&
1035+
rdi(phdr.p_offset) <= curOff &&
1036+
rdi(phdr.p_offset) + rdi(phdr.p_filesz) > curOff &&
1037+
rdi(phdr.p_filesz) < neededSpace)
1038+
{
1039+
wri(phdr.p_filesz, neededSpace);
1040+
wri(phdr.p_memsz, neededSpace);
1041+
break;
1042+
}
10521043

10531044
/* Clear out the free space. */
1054-
Elf_Off curOff = sizeof(Elf_Ehdr) + phdrs.size() * sizeof(Elf_Phdr);
10551045
debug("clearing first %d bytes\n", startOffset - curOff);
10561046
memset(fileContents->data() + curOff, 0, startOffset - curOff);
10571047

1058-
10591048
/* Write out the replaced sections. */
10601049
writeReplacedSections(curOff, firstPage, 0);
10611050
assert(curOff == neededSpace);
10621051

1063-
1052+
/* Write out the updated program and section headers */
10641053
rewriteHeaders(firstPage + rdi(hdr()->e_phoff));
10651054
}
10661055

tests/grow-file.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ mkdir -p "${SCRATCH}"
77

88
cp simple-pie "${SCRATCH}/simple-pie"
99

10-
# Add a 40MB rpath
11-
tr -cd 'a-z0-9' < /dev/urandom | dd count=40 bs=1000000 > "${SCRATCH}/foo.bin"
10+
# Add a large rpath
11+
echo "$(printf '=%.0s' $(seq 1 4096))" > "${SCRATCH}/foo.bin"
1212

1313
# Grow the file
1414
../src/patchelf --add-rpath @"${SCRATCH}/foo.bin" "${SCRATCH}/simple-pie"
15+
1516
# Make sure we can still run it
1617
"${SCRATCH}/simple-pie"

tests/no-rpath-pie-powerpc.sh

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ if [ "$(echo "$readelfData" | grep -c "PHDR")" != 1 ]; then
3737
fi
3838

3939
virtAddr=$(echo "$readelfData" | grep "PHDR" | awk '{print $3}')
40-
if [ "$virtAddr" != "0x00000034" ]; then
40+
if [ "$virtAddr" != "0x01030000" ] && [ "$virtAddr" != "0x01040000" ]; then
4141
# Triggered if the virtual address is the incorrect endianness
42-
echo "Unexpected virt addr, expected [0x00000034] got [$virtAddr]"
42+
echo "Unexpected virt addr, got [$virtAddr]"
4343
exit 1
4444
fi
4545

@@ -52,4 +52,3 @@ echo "$readelfData" | grep "LOAD" | while read -r line ; do
5252
exit 1
5353
fi
5454
done
55-

tests/repeated-updates.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ load_segments_after=$(${READELF} -W -l libbar.so | grep -c LOAD)
3434
# To be even more strict, check that we don't add too many extra LOAD entries
3535
###############################################################################
3636
echo "Segments before: ${load_segments_before} and after: ${load_segments_after}"
37-
if [ "${load_segments_after}" -gt $((load_segments_before + 2)) ]
37+
if [ "${load_segments_after}" -gt $((load_segments_before + 3)) ]
3838
then
3939
exit 1
4040
fi

tests/short-first-segment.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ cd "${SCRATCH}"
2424

2525
ldd "${EXEC_NAME}"
2626

27-
${PATCHELF} --add-rpath lalalalalalalala --output modified1 "${EXEC_NAME}"
27+
28+
${PATCHELF} --set-rpath "$(printf '=%.0s' $(seq 1 4096))" --output modified1 "${EXEC_NAME}"
29+
${PATCHELF} --add-rpath "$(printf '=%.0s' $(seq 1 4096))" modified1
30+
2831
ldd modified1
2932

30-
${PATCHELF} --add-needed "libXcursor.so.1" --output modified2 modified1
33+
${PATCHELF} --add-needed "libXcursor.so.1" --output modified2 modified1
3134
ldd modified2

0 commit comments

Comments
 (0)