27
27
#include < stdexcept>
28
28
#include < string>
29
29
#include < string_view>
30
+ #include < type_traits>
30
31
#include < unordered_map>
31
32
#include < unordered_set>
32
33
#include < vector>
@@ -127,6 +128,14 @@ constexpr I ElfFile<ElfFileParamNames>::rdi(I i) const noexcept
127
128
return r;
128
129
}
129
130
131
+ static void warn (const char * format, ...)
132
+ {
133
+ va_list ap;
134
+ va_start (ap, format);
135
+ fprintf (stderr, " warning: " );
136
+ vfprintf (stderr, format, ap);
137
+ va_end (ap);
138
+ }
130
139
131
140
static void debug (const char * format, ...)
132
141
{
@@ -554,8 +563,7 @@ void ElfFile<ElfFileParamNames>::shiftFile(unsigned int extraPages, size_t start
554
563
555
564
assert (splitIndex != -1 );
556
565
557
- /* Add a segment that maps the new program/section headers and
558
- PT_INTERP segment into memory. Otherwise glibc will choke. */
566
+ /* Add another PT_LOAD segment loading the data we've split above. */
559
567
phdrs.resize (rdi (hdr ()->e_phnum ) + 1 );
560
568
wri (hdr ()->e_phnum , rdi (hdr ()->e_phnum ) + 1 );
561
569
Elf_Phdr & phdr = phdrs.at (rdi (hdr ()->e_phnum ) - 1 );
@@ -801,49 +809,37 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
801
809
at the end of the file. They're mapped into memory by a
802
810
PT_LOAD segment located directly after the last virtual address
803
811
page of other segments. */
804
- Elf_Addr startPage = 0 ;
805
- Elf_Addr firstPage = 0 ;
806
- unsigned alignStartPage = getPageSize ();
812
+ Elf_Addr firstAddr = 0 ;
813
+ Elf_Addr lastAddr = 0 ;
814
+ unsigned alignLastAddr = getPageSize ();
815
+
807
816
for (auto & phdr : phdrs) {
808
- Elf_Addr thisPage = rdi (phdr.p_vaddr ) + rdi (phdr.p_memsz );
809
- if (thisPage > startPage) startPage = thisPage ;
810
- if (rdi (phdr.p_type ) == PT_PHDR) firstPage = rdi (phdr.p_vaddr ) - rdi (phdr.p_offset );
817
+ Elf_Addr thisAddr = rdi (phdr.p_vaddr ) + rdi (phdr.p_memsz );
818
+ if (thisAddr > lastAddr) lastAddr = thisAddr ;
819
+ if (rdi (phdr.p_type ) == PT_PHDR) firstAddr = rdi (phdr.p_vaddr ) - rdi (phdr.p_offset );
811
820
unsigned thisAlign = rdi (phdr.p_align );
812
- alignStartPage = std::max (alignStartPage , thisAlign);
821
+ alignLastAddr = std::max (alignLastAddr , thisAlign);
813
822
}
814
823
815
- startPage = roundUp (startPage, alignStartPage );
824
+ lastAddr = roundUp (lastAddr, alignLastAddr );
816
825
817
- debug (" last page is 0x%llx\n " , (unsigned long long ) startPage );
818
- debug (" first page is 0x%llx\n " , (unsigned long long ) firstPage );
826
+ debug (" first address is 0x%llx\n " , (unsigned long long ) firstAddr );
827
+ debug (" last address is 0x%llx\n " , (unsigned long long ) lastAddr );
819
828
820
829
/* When normalizing note segments we will in the worst case be adding
821
830
1 program header for each SHT_NOTE section. */
822
831
unsigned int num_notes = std::count_if (shdrs.begin (), shdrs.end (),
823
832
[this ](Elf_Shdr shdr) { return rdi (shdr.sh_type ) == SHT_NOTE; });
824
833
825
- /* Because we're adding a new section header, we're necessarily increasing
826
- the size of the program header table. This can cause the first section
827
- to overlap the program header table in memory; we need to shift the first
828
- few segments to someplace else. */
829
- /* Some sections may already be replaced so account for that */
830
- unsigned int i = 1 ;
831
- Elf_Addr pht_size = sizeof (Elf_Ehdr) + (phdrs.size () + num_notes + 1 )*sizeof (Elf_Phdr);
832
- while ( i < rdi (hdr ()->e_shnum ) && rdi (shdrs.at (i).sh_offset ) <= pht_size ) {
833
- if (not haveReplacedSection (getSectionName (shdrs.at (i))))
834
- replaceSection (getSectionName (shdrs.at (i)), rdi (shdrs.at (i).sh_size ));
835
- i++;
836
- }
837
- bool moveHeaderTableToTheEnd = rdi (hdr ()->e_shoff ) < pht_size;
834
+ /* Compute the total space needed for the replaced sections, pessimistically
835
+ assuming we're going to need one more to account for `needNewSegment` */
836
+ Elf_Addr 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
+ off_t neededSpace = phtSize + shtSize;
838
839
839
- /* Compute the total space needed for the replaced sections */
840
- off_t neededSpace = 0 ;
841
840
for (auto & s : replacedSections)
842
841
neededSpace += roundUp (s.second .size (), sectionAlignment);
843
842
844
- off_t headerTableSpace = roundUp (rdi (hdr ()->e_shnum ) * rdi (hdr ()->e_shentsize ), sectionAlignment);
845
- if (moveHeaderTableToTheEnd)
846
- neededSpace += headerTableSpace;
847
843
debug (" needed space is %d\n " , neededSpace);
848
844
849
845
Elf_Off startOffset = roundUp (fileContents->size (), getPageSize ());
@@ -852,37 +848,20 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
852
848
// section segment is strictly smaller than the file (and not same size).
853
849
// By making it one byte larger, we don't break readelf.
854
850
off_t binutilsQuirkPadding = 1 ;
855
- fileContents->resize (startOffset + neededSpace + binutilsQuirkPadding, 0 );
856
-
857
- /* Even though this file is of type ET_DYN, it could actually be
858
- an executable. For instance, Gold produces executables marked
859
- ET_DYN as does LD when linking with pie. If we move PT_PHDR, it
860
- has to stay in the first PT_LOAD segment or any subsequent ones
861
- if they're continuous in memory due to linux kernel constraints
862
- (see BUGS). Since the end of the file would be after bss, we can't
863
- move PHDR there, we therefore choose to leave PT_PHDR where it is but
864
- move enough following sections such that we can add the extra PT_LOAD
865
- section to it. This PT_LOAD segment ensures the sections at the end of
866
- the file are mapped into memory for ld.so to process.
867
- We can't use the approach in rewriteSectionsExecutable()
868
- since DYN executables tend to start at virtual address 0, so
869
- rewriteSectionsExecutable() won't work because it doesn't have
870
- any virtual address space to grow downwards into. */
871
- if (isExecutable && startOffset > startPage) {
872
- debug (" shifting new PT_LOAD segment by %d bytes to work around a Linux kernel bug\n " , startOffset - startPage);
873
- startPage = startOffset;
874
- }
875
851
876
- wri ( hdr ()-> e_phoff , sizeof (Elf_Ehdr) );
852
+ fileContents-> resize (startOffset + neededSpace + binutilsQuirkPadding, 0 );
877
853
878
854
bool needNewSegment = true ;
879
855
auto & lastSeg = phdrs.back ();
880
- /* Try to extend the last segment to include replaced sections */
856
+
857
+ /* As an optimization, instead of allocating a new PT_LOAD segment, try
858
+ expanding the last segment */
881
859
if (!phdrs.empty () &&
882
860
rdi (lastSeg.p_type ) == PT_LOAD &&
883
861
rdi (lastSeg.p_flags ) == (PF_R | PF_W) &&
884
- rdi (lastSeg.p_align ) == alignStartPage ) {
862
+ rdi (lastSeg.p_align ) == alignLastAddr ) {
885
863
auto segEnd = roundUp (rdi (lastSeg.p_offset ) + rdi (lastSeg.p_memsz ), getPageSize ());
864
+
886
865
if (segEnd == startOffset) {
887
866
auto newSz = startOffset + neededSpace - rdi (lastSeg.p_offset );
888
867
wri (lastSeg.p_filesz , wri (lastSeg.p_memsz , newSz));
@@ -897,29 +876,39 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
897
876
Elf_Phdr & phdr = phdrs.at (rdi (hdr ()->e_phnum ) - 1 );
898
877
wri (phdr.p_type , PT_LOAD);
899
878
wri (phdr.p_offset , startOffset);
900
- wri (phdr.p_vaddr , wri (phdr.p_paddr , startPage ));
879
+ wri (phdr.p_vaddr , wri (phdr.p_paddr , lastAddr ));
901
880
wri (phdr.p_filesz , wri (phdr.p_memsz , neededSpace));
902
881
wri (phdr.p_flags , PF_R | PF_W);
903
- wri (phdr.p_align , alignStartPage );
882
+ wri (phdr.p_align , alignLastAddr );
904
883
}
905
884
906
885
normalizeNoteSegments ();
907
886
908
-
909
887
/* Write out the replaced sections. */
910
- Elf_Off curOff = startOffset;
888
+ Elf_Off currOffset = startOffset;
911
889
912
- if (moveHeaderTableToTheEnd) {
913
- debug (" Moving the shtable to offset %d\n " , curOff);
914
- wri (hdr ()->e_shoff , curOff);
915
- curOff += headerTableSpace;
916
- }
890
+ debug (" rewriting pht from offset 0x%x to offset 0x%x\n " ,
891
+ rdi (hdr ()->e_phoff ), currOffset);
917
892
918
- writeReplacedSections (curOff, startPage, startOffset );
919
- assert (curOff == startOffset + neededSpace) ;
893
+ wri ( hdr ()-> e_phoff , currOffset );
894
+ currOffset += phtSize ;
920
895
921
- /* Write out the updated program and section headers */
922
- rewriteHeaders (firstPage + rdi (hdr ()->e_phoff ));
896
+ // ---
897
+
898
+ debug (" rewriting sht from offset 0x%x to offset 0x%x\n " ,
899
+ rdi (hdr ()->e_phoff ), currOffset);
900
+
901
+ wri (hdr ()->e_shoff , currOffset);
902
+ currOffset += shtSize;
903
+
904
+ // ---
905
+
906
+ writeReplacedSections (currOffset, lastAddr, startOffset);
907
+ assert (currOffset == startOffset + neededSpace);
908
+
909
+ // ---
910
+
911
+ rewriteHeaders (lastAddr);
923
912
}
924
913
925
914
static bool noSort = false ;
@@ -1033,31 +1022,33 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsExecutable()
1033
1022
1034
1023
firstPage -= neededPages * getPageSize ();
1035
1024
startOffset += neededPages * getPageSize ();
1036
- } else {
1037
- Elf_Off rewrittenSectionsOffset = sizeof (Elf_Ehdr) + phdrs.size () * sizeof (Elf_Phdr);
1038
- for (auto & phdr : phdrs)
1039
- if (rdi (phdr.p_type ) == PT_LOAD &&
1040
- rdi (phdr.p_offset ) <= rewrittenSectionsOffset &&
1041
- rdi (phdr.p_offset ) + rdi (phdr.p_filesz ) > rewrittenSectionsOffset &&
1042
- rdi (phdr.p_filesz ) < neededSpace)
1043
- {
1044
- wri (phdr.p_filesz , neededSpace);
1045
- wri (phdr.p_memsz , neededSpace);
1046
- break ;
1047
- }
1048
1025
}
1049
1026
1027
+ Elf_Off currOffset = sizeof (Elf_Ehdr) + phdrs.size () * sizeof (Elf_Phdr);
1050
1028
1051
- /* Clear out the free space. */
1052
- Elf_Off curOff = sizeof (Elf_Ehdr) + phdrs.size () * sizeof (Elf_Phdr);
1053
- debug (" clearing first %d bytes\n " , startOffset - curOff);
1054
- memset (fileContents->data () + curOff, 0 , startOffset - curOff);
1029
+ /* Ensure PHDR is covered by a LOAD segment.
1055
1030
1031
+ Because PHDR is supposed to have been covered by such section before, in
1032
+ here we assume that we don't have to create any new section, but rather
1033
+ extend the existing one. */
1034
+ for (auto & phdr : phdrs)
1035
+ if (rdi (phdr.p_type ) == PT_LOAD &&
1036
+ rdi (phdr.p_offset ) <= currOffset &&
1037
+ rdi (phdr.p_offset ) + rdi (phdr.p_filesz ) > currOffset &&
1038
+ rdi (phdr.p_filesz ) < neededSpace)
1039
+ {
1040
+ wri (phdr.p_filesz , neededSpace);
1041
+ wri (phdr.p_memsz , neededSpace);
1042
+ break ;
1043
+ }
1056
1044
1057
- /* Write out the replaced sections . */
1058
- writeReplacedSections (curOff, firstPage, 0 );
1059
- assert (curOff == neededSpace );
1045
+ /* Clear out the free space . */
1046
+ debug ( " clearing first %d bytes \n " , startOffset - currOffset );
1047
+ memset (fileContents-> data () + currOffset, 0 , startOffset - currOffset );
1060
1048
1049
+ /* Write out the replaced sections. */
1050
+ writeReplacedSections (currOffset, firstPage, 0 );
1051
+ assert (currOffset == neededSpace);
1061
1052
1062
1053
rewriteHeaders (firstPage + rdi (hdr ()->e_phoff ));
1063
1054
}
@@ -1152,7 +1143,7 @@ void ElfFile<ElfFileParamNames>::rewriteSections(bool force)
1152
1143
1153
1144
1154
1145
template <ElfFileParams>
1155
- void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress )
1146
+ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddr )
1156
1147
{
1157
1148
/* Rewrite the program header table. */
1158
1149
@@ -1161,7 +1152,7 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress)
1161
1152
for (auto & phdr : phdrs) {
1162
1153
if (rdi (phdr.p_type ) == PT_PHDR) {
1163
1154
phdr.p_offset = hdr ()->e_phoff ;
1164
- wri (phdr.p_vaddr , wri (phdr.p_paddr , phdrAddress ));
1155
+ wri (phdr.p_vaddr , wri (phdr.p_paddr , phdrAddr ));
1165
1156
wri (phdr.p_filesz , wri (phdr.p_memsz , phdrs.size () * sizeof (Elf_Phdr)));
1166
1157
break ;
1167
1158
}
@@ -1178,9 +1169,11 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress)
1178
1169
/* Rewrite the section header table. For neatness, keep the
1179
1170
sections sorted. */
1180
1171
assert (rdi (hdr ()->e_shnum ) == shdrs.size ());
1172
+
1181
1173
if (!noSort) {
1182
1174
sortShdrs ();
1183
1175
}
1176
+
1184
1177
for (unsigned int i = 1 ; i < rdi (hdr ()->e_shnum ); ++i)
1185
1178
* ((Elf_Shdr *) (fileContents->data () + rdi (hdr ()->e_shoff )) + i) = shdrs.at (i);
1186
1179
@@ -1262,7 +1255,7 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress)
1262
1255
is broken, and it's not our job to fix it; yet, we have
1263
1256
to find some location for dynamic loader to write the
1264
1257
debug pointer to; well, let's write it right here */
1265
- fprintf (stderr, " warning: DT_MIPS_RLD_MAP_REL entry is present, but .rld_map section is not\n " );
1258
+ warn ( " DT_MIPS_RLD_MAP_REL entry is present, but .rld_map section is not\n " );
1266
1259
dyn->d_un .d_ptr = 0 ;
1267
1260
}
1268
1261
}
@@ -1281,7 +1274,7 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress)
1281
1274
unsigned int shndx = rdi (sym->st_shndx );
1282
1275
if (shndx != SHN_UNDEF && shndx < SHN_LORESERVE) {
1283
1276
if (shndx >= sectionsByOldIndex.size ()) {
1284
- fprintf (stderr, " warning: entry %d in symbol table refers to a non-existent section, skipping\n " , shndx);
1277
+ warn ( " entry %d in symbol table refers to a non-existent section, skipping\n " , shndx);
1285
1278
continue ;
1286
1279
}
1287
1280
const std::string & section = sectionsByOldIndex.at (shndx);
0 commit comments