@@ -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 sectionName == " .interp" || 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,60 @@ 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) + 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 ;
831849 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+
836859 i++;
837860 }
838- bool moveHeaderTableToTheEnd = rdi (hdr ()->e_shoff ) < pht_size;
839861
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+
842884 for (auto & s : replacedSections)
843885 neededSpace += roundUp (s.second .size (), sectionAlignment);
844886
845- off_t headerTableSpace = roundUp (rdi (hdr ()->e_shnum ) * rdi (hdr ()->e_shentsize ), sectionAlignment);
846- if (moveHeaderTableToTheEnd)
847- neededSpace += headerTableSpace;
848887 debug (" needed space is %d\n " , neededSpace);
849888
850889 Elf_Off startOffset = roundUp (fileContents->size (), alignStartPage);
@@ -853,45 +892,32 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
853892 // section segment is strictly smaller than the file (and not same size).
854893 // By making it one byte larger, we don't break readelf.
855894 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- }
876895
877- wri ( hdr ()-> e_phoff , sizeof (Elf_Ehdr) );
896+ fileContents-> resize (startOffset + neededSpace + binutilsQuirkPadding, 0 );
878897
879- bool needNewSegment = true ;
880898 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 */
882903 if (!phdrs.empty () &&
883904 rdi (lastSeg.p_type ) == PT_LOAD &&
884905 rdi (lastSeg.p_flags ) == (PF_R | PF_W) &&
885906 rdi (lastSeg.p_align ) == alignStartPage) {
886907 auto segEnd = roundUp (rdi (lastSeg.p_offset ) + rdi (lastSeg.p_memsz ), alignStartPage);
908+
887909 if (segEnd == startOffset) {
888910 auto newSz = startOffset + neededSpace - rdi (lastSeg.p_offset );
911+
889912 wri (lastSeg.p_filesz , wri (lastSeg.p_memsz , newSz));
890- needNewSegment = false ;
913+
914+ lastSegAddr = rdi (lastSeg.p_vaddr ) + newSz - neededSpace;
891915 }
892916 }
893917
894- if (needNewSegment) {
918+ if (lastSegAddr == 0 ) {
919+ debug (" allocating new PT_LOAD segment\n " );
920+
895921 /* Add a segment that maps the replaced sections into memory. */
896922 phdrs.resize (rdi (hdr ()->e_phnum ) + 1 );
897923 wri (hdr ()->e_phnum , rdi (hdr ()->e_phnum ) + 1 );
@@ -903,25 +929,43 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
903929 wri (phdr.p_flags , PF_R | PF_W);
904930 wri (phdr.p_align , alignStartPage);
905931 assert (startPage % alignStartPage == startOffset % alignStartPage);
932+
933+ lastSegAddr = startPage;
906934 }
907935
908936 normalizeNoteSegments ();
909937
910-
911938 /* Write out the replaced sections. */
912939 Elf_Off curOff = startOffset;
913940
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;
918947 }
919948
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. */
920960 writeReplacedSections (curOff, startPage, startOffset);
921961 assert (curOff == startOffset + neededSpace);
922962
923963 /* 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+ }
925969}
926970
927971static bool noSort = false ;
@@ -1035,32 +1079,35 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsExecutable()
10351079
10361080 firstPage -= neededPages * getPageSize ();
10371081 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- }
10501082 }
10511083
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+ }
10521101
10531102 /* Clear out the free space. */
1054- Elf_Off curOff = sizeof (Elf_Ehdr) + phdrs.size () * sizeof (Elf_Phdr);
10551103 debug (" clearing first %d bytes\n " , startOffset - curOff);
10561104 memset (fileContents->data () + curOff, 0 , startOffset - curOff);
10571105
1058-
10591106 /* Write out the replaced sections. */
10601107 writeReplacedSections (curOff, firstPage, 0 );
10611108 assert (curOff == neededSpace);
10621109
1063-
1110+ /* Write out the updated program and section headers */
10641111 rewriteHeaders (firstPage + rdi (hdr ()->e_phoff ));
10651112}
10661113
0 commit comments