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