@@ -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 );
@@ -636,11 +635,19 @@ unsigned int ElfFile<ElfFileParamNames>::getSectionIndex(const SectionName & sec
636635}
637636
638637template <ElfFileParams>
639- bool ElfFile<ElfFileParamNames>::haveReplacedSection (const SectionName & sectionName) const
638+ bool ElfFile<ElfFileParamNames>::hasReplacedSection (const SectionName & sectionName) const
640639{
641640 return replacedSections.count (sectionName);
642641}
643642
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+
644651template <ElfFileParams>
645652std::string & ElfFile<ElfFileParamNames>::replaceSection(const SectionName & sectionName,
646653 unsigned int size)
@@ -823,28 +830,59 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
823830 unsigned int num_notes = std::count_if (shdrs.begin (), shdrs.end (),
824831 [this ](Elf_Shdr shdr) { return rdi (shdr.sh_type ) == SHT_NOTE; });
825832
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 ;
831848 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+
836858 i++;
837859 }
838- bool moveHeaderTableToTheEnd = rdi (hdr ()->e_shoff ) < pht_size;
839860
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+
842883 for (auto & s : replacedSections)
843884 neededSpace += roundUp (s.second .size (), sectionAlignment);
844885
845- off_t headerTableSpace = roundUp (rdi (hdr ()->e_shnum ) * rdi (hdr ()->e_shentsize ), sectionAlignment);
846- if (moveHeaderTableToTheEnd)
847- neededSpace += headerTableSpace;
848886 debug (" needed space is %d\n " , neededSpace);
849887
850888 Elf_Off startOffset = roundUp (fileContents->size (), alignStartPage);
@@ -853,45 +891,32 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
853891 // section segment is strictly smaller than the file (and not same size).
854892 // By making it one byte larger, we don't break readelf.
855893 off_t binutilsQuirkPadding = 1 ;
856- fileContents->resize (startOffset + neededSpace + binutilsQuirkPadding, 0 );
857894
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 );
878896
879- bool needNewSegment = true ;
880897 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 */
882902 if (!phdrs.empty () &&
883903 rdi (lastSeg.p_type ) == PT_LOAD &&
884904 rdi (lastSeg.p_flags ) == (PF_R | PF_W) &&
885905 rdi (lastSeg.p_align ) == alignStartPage) {
886906 auto segEnd = roundUp (rdi (lastSeg.p_offset ) + rdi (lastSeg.p_memsz ), alignStartPage);
907+
887908 if (segEnd == startOffset) {
888909 auto newSz = startOffset + neededSpace - rdi (lastSeg.p_offset );
910+
889911 wri (lastSeg.p_filesz , wri (lastSeg.p_memsz , newSz));
890- needNewSegment = false ;
912+
913+ lastSegAddr = rdi (lastSeg.p_vaddr ) + newSz - neededSpace;
891914 }
892915 }
893916
894- if (needNewSegment) {
917+ if (lastSegAddr == 0 ) {
918+ debug (" allocating new PT_LOAD segment\n " );
919+
895920 /* Add a segment that maps the replaced sections into memory. */
896921 phdrs.resize (rdi (hdr ()->e_phnum ) + 1 );
897922 wri (hdr ()->e_phnum , rdi (hdr ()->e_phnum ) + 1 );
@@ -903,25 +928,43 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
903928 wri (phdr.p_flags , PF_R | PF_W);
904929 wri (phdr.p_align , alignStartPage);
905930 assert (startPage % alignStartPage == startOffset % alignStartPage);
931+
932+ lastSegAddr = startPage;
906933 }
907934
908935 normalizeNoteSegments ();
909936
910-
911937 /* Write out the replaced sections. */
912938 Elf_Off curOff = startOffset;
913939
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;
918946 }
919947
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. */
920959 writeReplacedSections (curOff, startPage, startOffset);
921960 assert (curOff == startOffset + neededSpace);
922961
923962 /* 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+ }
925968}
926969
927970static bool noSort = false ;
@@ -1035,32 +1078,35 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsExecutable()
10351078
10361079 firstPage -= neededPages * getPageSize ();
10371080 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- }
10501081 }
10511082
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+ }
10521100
10531101 /* Clear out the free space. */
1054- Elf_Off curOff = sizeof (Elf_Ehdr) + phdrs.size () * sizeof (Elf_Phdr);
10551102 debug (" clearing first %d bytes\n " , startOffset - curOff);
10561103 memset (fileContents->data () + curOff, 0 , startOffset - curOff);
10571104
1058-
10591105 /* Write out the replaced sections. */
10601106 writeReplacedSections (curOff, firstPage, 0 );
10611107 assert (curOff == neededSpace);
10621108
1063-
1109+ /* Write out the updated program and section headers */
10641110 rewriteHeaders (firstPage + rdi (hdr ()->e_phoff ));
10651111}
10661112
0 commit comments