@@ -555,8 +555,7 @@ void ElfFile<ElfFileParamNames>::shiftFile(unsigned int extraPages, size_t start
555
555
556
556
assert (splitIndex != -1 );
557
557
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. */
560
559
phdrs.resize (rdi (hdr ()->e_phnum ) + 1 );
561
560
wri (hdr ()->e_phnum , rdi (hdr ()->e_phnum ) + 1 );
562
561
Elf_Phdr & phdr = phdrs.at (rdi (hdr ()->e_phnum ) - 1 );
@@ -636,11 +635,19 @@ unsigned int ElfFile<ElfFileParamNames>::getSectionIndex(const SectionName & sec
636
635
}
637
636
638
637
template <ElfFileParams>
639
- bool ElfFile<ElfFileParamNames>::haveReplacedSection (const SectionName & sectionName) const
638
+ bool ElfFile<ElfFileParamNames>::hasReplacedSection (const SectionName & sectionName) const
640
639
{
641
640
return replacedSections.count (sectionName);
642
641
}
643
642
643
+ template <ElfFileParams>
644
+ bool ElfFile<ElfFileParamNames>::canReplaceSection(const SectionName & sectionName) const
645
+ {
646
+ auto shdr = findSectionHeader (sectionName);
647
+
648
+ return rdi (shdr.sh_type ) != SHT_PROGBITS;
649
+ }
650
+
644
651
template <ElfFileParams>
645
652
std::string & ElfFile<ElfFileParamNames>::replaceSection(const SectionName & sectionName,
646
653
unsigned int size)
@@ -823,28 +830,59 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
823
830
unsigned int num_notes = std::count_if (shdrs.begin (), shdrs.end (),
824
831
[this ](Elf_Shdr shdr) { return rdi (shdr.sh_type ) == SHT_NOTE; });
825
832
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 */
833
+ /* Compute the total space needed for the replaced sections, pessimistically
834
+ assuming we're going to need one more to account for new PT_LOAD covering
835
+ relocated PHDR */
836
+ off_t phtSize = roundUp ((phdrs.size () + num_notes + 1 ) * sizeof (Elf_Phdr), sectionAlignment);
837
+ off_t shtSize = roundUp (rdi (hdr ()->e_shnum ) * rdi (hdr ()->e_shentsize ), sectionAlignment);
838
+
839
+ /* Check if we can keep PHT at the beginning of the file. We'd like to do
840
+ that, because it preverves compatibility with older kernels¹ - but if the
841
+ PHT has grown too much, we have to no other option but to relocate it at
842
+ the end of the file.
843
+
844
+ ¹ older kernels had a bug that prevented them from loading ELFs with
845
+ PHDRs not located at the beginning of the file; it was fixed over
846
+ 0da1d5002745cdc721bc018b582a8a9704d56c42 (2022-03-02) */
847
+ bool relocatePht = false ;
831
848
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 ));
849
+
850
+ while (i < rdi (hdr ()->e_shnum ) && ((off_t ) rdi (shdrs.at (i).sh_offset )) <= phtSize) {
851
+ const auto & sectionName = getSectionName (shdrs.at (i));
852
+
853
+ if (!hasReplacedSection (sectionName) && !canReplaceSection (sectionName)) {
854
+ relocatePht = true ;
855
+ break ;
856
+ }
857
+
836
858
i++;
837
859
}
838
- bool moveHeaderTableToTheEnd = rdi (hdr ()->e_shoff ) < pht_size;
839
860
840
- /* Compute the total space needed for the replaced sections */
841
- off_t neededSpace = 0 ;
861
+ if (!relocatePht) {
862
+ unsigned int i = 1 ;
863
+
864
+ while (i < rdi (hdr ()->e_shnum ) && ((off_t ) rdi (shdrs.at (i).sh_offset )) <= phtSize) {
865
+ const auto & sectionName = getSectionName (shdrs.at (i));
866
+ const auto sectionSize = rdi (shdrs.at (i).sh_size );
867
+
868
+ if (!hasReplacedSection (sectionName)) {
869
+ replaceSection (sectionName, sectionSize);
870
+ }
871
+
872
+ i++;
873
+ }
874
+ }
875
+
876
+ /* Calculate how much space we'll need. */
877
+ off_t neededSpace = shtSize;
878
+
879
+ if (relocatePht) {
880
+ neededSpace += phtSize;
881
+ }
882
+
842
883
for (auto & s : replacedSections)
843
884
neededSpace += roundUp (s.second .size (), sectionAlignment);
844
885
845
- off_t headerTableSpace = roundUp (rdi (hdr ()->e_shnum ) * rdi (hdr ()->e_shentsize ), sectionAlignment);
846
- if (moveHeaderTableToTheEnd)
847
- neededSpace += headerTableSpace;
848
886
debug (" needed space is %d\n " , neededSpace);
849
887
850
888
Elf_Off startOffset = roundUp (fileContents->size (), alignStartPage);
@@ -853,45 +891,32 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
853
891
// section segment is strictly smaller than the file (and not same size).
854
892
// By making it one byte larger, we don't break readelf.
855
893
off_t binutilsQuirkPadding = 1 ;
856
- fileContents->resize (startOffset + neededSpace + binutilsQuirkPadding, 0 );
857
894
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
- }
876
-
877
- wri (hdr ()->e_phoff , sizeof (Elf_Ehdr));
895
+ fileContents->resize (startOffset + neededSpace + binutilsQuirkPadding, 0 );
878
896
879
- bool needNewSegment = true ;
880
897
auto & lastSeg = phdrs.back ();
881
- /* Try to extend the last segment to include replaced sections */
898
+ Elf_Addr lastSegAddr = 0 ;
899
+
900
+ /* As an optimization, instead of allocating a new PT_LOAD segment, try
901
+ expanding the last one */
882
902
if (!phdrs.empty () &&
883
903
rdi (lastSeg.p_type ) == PT_LOAD &&
884
904
rdi (lastSeg.p_flags ) == (PF_R | PF_W) &&
885
905
rdi (lastSeg.p_align ) == alignStartPage) {
886
906
auto segEnd = roundUp (rdi (lastSeg.p_offset ) + rdi (lastSeg.p_memsz ), alignStartPage);
907
+
887
908
if (segEnd == startOffset) {
888
909
auto newSz = startOffset + neededSpace - rdi (lastSeg.p_offset );
910
+
889
911
wri (lastSeg.p_filesz , wri (lastSeg.p_memsz , newSz));
890
- needNewSegment = false ;
912
+
913
+ lastSegAddr = rdi (lastSeg.p_vaddr ) + newSz - neededSpace;
891
914
}
892
915
}
893
916
894
- if (needNewSegment) {
917
+ if (lastSegAddr == 0 ) {
918
+ debug (" allocating new PT_LOAD segment\n " );
919
+
895
920
/* Add a segment that maps the replaced sections into memory. */
896
921
phdrs.resize (rdi (hdr ()->e_phnum ) + 1 );
897
922
wri (hdr ()->e_phnum , rdi (hdr ()->e_phnum ) + 1 );
@@ -903,25 +928,43 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
903
928
wri (phdr.p_flags , PF_R | PF_W);
904
929
wri (phdr.p_align , alignStartPage);
905
930
assert (startPage % alignStartPage == startOffset % alignStartPage);
931
+
932
+ lastSegAddr = startPage;
906
933
}
907
934
908
935
normalizeNoteSegments ();
909
936
910
-
911
937
/* Write out the replaced sections. */
912
938
Elf_Off curOff = startOffset;
913
939
914
- if (moveHeaderTableToTheEnd) {
915
- debug (" Moving the shtable to offset %d\n " , curOff);
916
- wri (hdr ()->e_shoff , curOff);
917
- curOff += headerTableSpace;
940
+ if (relocatePht) {
941
+ debug (" rewriting pht from offset 0x%x to offset 0x%x (size %d)\n " ,
942
+ rdi (hdr ()->e_phoff ), curOff, phtSize);
943
+
944
+ wri (hdr ()->e_phoff , curOff);
945
+ curOff += phtSize;
918
946
}
919
947
948
+ // ---
949
+
950
+ debug (" rewriting sht from offset 0x%x to offset 0x%x (size %d)\n " ,
951
+ rdi (hdr ()->e_shoff ), curOff, shtSize);
952
+
953
+ wri (hdr ()->e_shoff , curOff);
954
+ curOff += shtSize;
955
+
956
+ // ---
957
+
958
+ /* Write out the replaced sections. */
920
959
writeReplacedSections (curOff, startPage, startOffset);
921
960
assert (curOff == startOffset + neededSpace);
922
961
923
962
/* Write out the updated program and section headers */
924
- rewriteHeaders (firstPage + rdi (hdr ()->e_phoff ));
963
+ if (relocatePht) {
964
+ rewriteHeaders (lastSegAddr);
965
+ } else {
966
+ rewriteHeaders (firstPage + rdi (hdr ()->e_phoff ));
967
+ }
925
968
}
926
969
927
970
static bool noSort = false ;
@@ -1035,32 +1078,35 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsExecutable()
1035
1078
1036
1079
firstPage -= neededPages * getPageSize ();
1037
1080
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
- }
1050
1081
}
1051
1082
1083
+ Elf_Off curOff = sizeof (Elf_Ehdr) + phdrs.size () * sizeof (Elf_Phdr);
1084
+
1085
+ /* Ensure PHDR is covered by a LOAD segment.
1086
+
1087
+ Because PHDR is supposed to have been covered by such section before, in
1088
+ here we assume that we don't have to create any new section, but rather
1089
+ extend the existing one. */
1090
+ for (auto & phdr : phdrs)
1091
+ if (rdi (phdr.p_type ) == PT_LOAD &&
1092
+ rdi (phdr.p_offset ) <= curOff &&
1093
+ rdi (phdr.p_offset ) + rdi (phdr.p_filesz ) > curOff &&
1094
+ rdi (phdr.p_filesz ) < neededSpace)
1095
+ {
1096
+ wri (phdr.p_filesz , neededSpace);
1097
+ wri (phdr.p_memsz , neededSpace);
1098
+ break ;
1099
+ }
1052
1100
1053
1101
/* Clear out the free space. */
1054
- Elf_Off curOff = sizeof (Elf_Ehdr) + phdrs.size () * sizeof (Elf_Phdr);
1055
1102
debug (" clearing first %d bytes\n " , startOffset - curOff);
1056
1103
memset (fileContents->data () + curOff, 0 , startOffset - curOff);
1057
1104
1058
-
1059
1105
/* Write out the replaced sections. */
1060
1106
writeReplacedSections (curOff, firstPage, 0 );
1061
1107
assert (curOff == neededSpace);
1062
1108
1063
-
1109
+ /* Write out the updated program and section headers */
1064
1110
rewriteHeaders (firstPage + rdi (hdr ()->e_phoff ));
1065
1111
}
1066
1112
0 commit comments