@@ -46,7 +46,9 @@ static bool CouldJit(struct Machine *m) {
4646}
4747
4848static i64 LoadElfLoadSegment (struct Machine * m , void * image , size_t imagesize ,
49- const Elf64_Phdr * phdr , i64 last_end ) {
49+ const Elf64_Phdr * phdr , i64 last_end , int fd ) {
50+ i64 bulk ;
51+ struct System * s = m -> system ;
5052 u32 flags = Read32 (phdr -> p_flags );
5153 i64 vaddr = Read64 (phdr -> p_vaddr );
5254 i64 memsz = Read64 (phdr -> p_memsz );
@@ -55,6 +57,20 @@ static i64 LoadElfLoadSegment(struct Machine *m, void *image, size_t imagesize,
5557 long pagesize = GetSystemPageSize ();
5658 i64 start = ROUNDDOWN (vaddr , pagesize );
5759 i64 end = ROUNDUP (vaddr + memsz , pagesize );
60+ long skew = vaddr & (pagesize - 1 );
61+ u64 key = (flags & PF_R ? PAGE_U : 0 ) | //
62+ (flags & PF_W ? PAGE_RW : 0 ) | //
63+ (flags & PF_X ? 0 : PAGE_XD );
64+
65+ ELF_LOGF ("PROGRAM HEADER" );
66+ ELF_LOGF (" vaddr = %" PRIx64 , vaddr );
67+ ELF_LOGF (" memsz = %" PRIx64 , memsz );
68+ ELF_LOGF (" offset = %" PRIx64 , offset );
69+ ELF_LOGF (" filesz = %" PRIx64 , filesz );
70+ ELF_LOGF (" pagesize = %" PRIx64 , pagesize );
71+ ELF_LOGF (" start = %" PRIx64 , start );
72+ ELF_LOGF (" end = %" PRIx64 , end );
73+ ELF_LOGF (" skew = %lx" , skew );
5874
5975 if (offset > imagesize ) {
6076 LOGF ("bad phdr offset" );
@@ -72,39 +88,97 @@ static i64 LoadElfLoadSegment(struct Machine *m, void *image, size_t imagesize,
7288 LOGF ("program headers aren't ordered" );
7389 exit (127 );
7490 }
75- if (memsz <= 0 ) {
76- return last_end ;
91+ if (skew != (offset & (pagesize - 1 ))) {
92+ LOGF ("p_vaddr p_offset skew unequal w.r.t. host page size" );
93+ exit (127 );
7794 }
7895
7996 // on systems with a page size greater than the elf executable (e.g.
8097 // apple m1) it's possible for the second program header load to end
8198 // up overlapping the previous one.
82- if (memsz > 0 && start < last_end ) {
99+ if (start < last_end ) {
83100 start += last_end - start ;
84- memsz = end - start ;
101+ }
102+ if (start >= end ) {
103+ return last_end ;
85104 }
86105
87- if (memsz > 0 ) {
88- if (ReserveVirtual (m -> system , start , end - start ,
89- (flags & PF_R ? PAGE_U : 0 ) |
90- (flags & PF_W ? PAGE_RW : 0 ) |
91- (flags & PF_X ? 0 : PAGE_XD ),
92- -1 , false) == -1 ) {
93- LOGF ("failed to reserve virtual memory for elf program header" );
94- exit (127 );
106+ // mmap() always returns an address that page-size aligned, but elf
107+ // program headers can start at any address. therefore the first page
108+ // needs to be loaded with special care, if the phdr is skewed. the
109+ // only real rule in this situation is that the skew of of the virtual
110+ // address and the file offset need to be the same.
111+ if (skew ) {
112+ if (vaddr > start && start + pagesize <= end ) {
113+ unassert (vaddr < start + pagesize );
114+ ELF_LOGF ("alloc %" PRIx64 "-%" PRIx64 , start , start + pagesize );
115+ if (ReserveVirtual (s , start , pagesize , key , -1 , 0 , 0 ) == -1 ) {
116+ LOGF ("failed to allocate program header skew" );
117+ exit (127 );
118+ }
119+ start += pagesize ;
120+ }
121+ ELF_LOGF ("copy %" PRIx64 "-%" PRIx64 " from %" PRIx64 "-%" PRIx64 , vaddr ,
122+ vaddr + (pagesize - skew ), offset ,
123+ offset + MIN (filesz , pagesize - skew ));
124+ CopyToUser (m , vaddr , (u8 * )image + offset , MIN (filesz , pagesize - skew ));
125+ vaddr += pagesize - skew ;
126+ offset += pagesize - skew ;
127+ filesz -= MIN (filesz , pagesize - skew );
128+ }
129+
130+ // load the aligned program header.
131+ unassert (start <= end );
132+ unassert (vaddr == start );
133+ unassert (vaddr + filesz <= end );
134+ unassert (!(vaddr & (pagesize - 1 )));
135+ unassert (!(start & (pagesize - 1 )));
136+ unassert (!(offset & (pagesize - 1 )));
137+ if (start < end ) {
138+ bulk = ROUNDDOWN (filesz , pagesize );
139+ unassert (bulk >= 0 );
140+ if (bulk ) {
141+ // map the bulk of .text directly into memory without copying.
142+ ELF_LOGF ("load %" PRIx64 "-%" PRIx64 " from %" PRIx64 "-%" PRIx64 , start ,
143+ start + bulk , offset , offset + bulk );
144+ if (ReserveVirtual (s , start , bulk , key , fd , offset , 0 ) == -1 ) {
145+ LOGF ("failed to map elf program header file data" );
146+ exit (127 );
147+ }
148+ if (!HasLinearMapping (m -> system ) && fd != -1 ) {
149+ SyncVirtual (m , start , bulk , fd , offset );
150+ }
151+ }
152+ start += bulk ;
153+ offset += bulk ;
154+ filesz -= bulk ;
155+ if (start < end ) {
156+ // allocate .bss zero-initialized memory.
157+ ELF_LOGF ("alloc %" PRIx64 "-%" PRIx64 , start , end );
158+ if (ReserveVirtual (s , start , end - start , key , -1 , 0 , 0 ) == -1 ) {
159+ LOGF ("failed to allocate program header bss" );
160+ exit (127 );
161+ }
162+ // copy the tail skew.
163+ if (filesz ) {
164+ ELF_LOGF ("copy %" PRIx64 "-%" PRIx64 " from %" PRIx64 "-%" PRIx64 ,
165+ start , start + filesz , offset , offset + filesz );
166+ CopyToUser (m , start , (u8 * )image + offset , filesz );
167+ }
168+ } else {
169+ unassert (!filesz );
95170 }
96171 }
97172
98- CopyToUser (m , vaddr , (u8 * )image + offset , filesz );
99- m -> system -> brk = MAX (m -> system -> brk , end );
173+ s -> brk = MAX (s -> brk , end );
100174
101175#ifdef HAVE_JIT
102176 if ((flags & PF_X ) && CouldJit (m )) {
103- if (!m -> system -> codesize ) {
104- m -> system -> codestart = vaddr ;
105- m -> system -> codesize = memsz ;
106- } else if (vaddr == m -> system -> codestart + m -> system -> codesize ) {
107- m -> system -> codesize += memsz ;
177+ if (!s -> codesize ) {
178+ s -> codestart = vaddr ;
179+ s -> codesize = memsz ;
180+ } else if (vaddr == s -> codestart + s -> codesize ) {
181+ s -> codesize += memsz ;
108182 } else {
109183 LOGF ("elf has multiple executable program headers at noncontiguous "
110184 "addresses; only the first region will benefit from jitting" );
@@ -129,20 +203,20 @@ bool IsSupportedExecutable(const char *path, void *image) {
129203}
130204
131205static void LoadFlatExecutable (struct Machine * m , intptr_t base ,
132- const char * prog , void * image ,
133- size_t imagesize ) {
206+ const char * prog , void * image , size_t imagesize ,
207+ int fd ) {
134208 Elf64_Phdr phdr ;
135209 Write32 (phdr .p_type , PT_LOAD );
136210 Write32 (phdr .p_flags , PF_X | PF_R | PF_W );
137211 Write64 (phdr .p_offset , 0 );
138212 Write64 (phdr .p_vaddr , base );
139213 Write64 (phdr .p_filesz , imagesize );
140214 Write64 (phdr .p_memsz , ROUNDUP (imagesize + kRealSize , 4096 ));
141- LoadElfLoadSegment (m , image , imagesize , & phdr , 0 );
215+ LoadElfLoadSegment (m , image , imagesize , & phdr , 0 , fd );
142216 m -> ip = base ;
143217}
144218
145- static bool LoadElf (struct Machine * m , struct Elf * elf ) {
219+ static bool LoadElf (struct Machine * m , struct Elf * elf , int fd ) {
146220 int i ;
147221 Elf64_Phdr * phdr ;
148222 i64 end = INT64_MIN ;
@@ -153,7 +227,7 @@ static bool LoadElf(struct Machine *m, struct Elf *elf) {
153227 switch (Read32 (phdr -> p_type )) {
154228 case PT_LOAD :
155229 elf -> base = MIN (elf -> base , (i64 )Read64 (phdr -> p_vaddr ));
156- end = LoadElfLoadSegment (m , elf -> ehdr , elf -> size , phdr , end );
230+ end = LoadElfLoadSegment (m , elf -> ehdr , elf -> size , phdr , end , fd );
157231 break ;
158232 case PT_GNU_STACK :
159233 execstack = false;
@@ -270,7 +344,6 @@ void LoadProgram(struct Machine *m, char *prog, char **args, char **vars) {
270344 WriteErrorString ("\n" );
271345 exit (127 );
272346 };
273- unassert (!close (fd ));
274347 if (!IsSupportedExecutable (prog , elf -> map )) {
275348 WriteErrorString ("\
276349error: unsupported executable; we need:\n\
@@ -290,25 +363,25 @@ error: unsupported executable; we need:\n\
290363 if (READ32 (elf -> map ) == READ32 ("\177ELF" )) {
291364 elf -> ehdr = (Elf64_Ehdr * )elf -> map ;
292365 elf -> size = elf -> mapsize ;
293- execstack = LoadElf (m , elf );
366+ execstack = LoadElf (m , elf , fd );
294367 } else if (READ64 (elf -> map ) == READ64 ("MZqFpD='" ) ||
295368 READ64 (elf -> map ) == READ64 ("jartsr='" )) {
296369 if (GetElfHeader (ehdr , prog , elf -> map ) == -1 ) exit (127 );
297370 memcpy (elf -> map , ehdr , 64 );
298371 elf -> ehdr = (Elf64_Ehdr * )elf -> map ;
299372 elf -> size = elf -> mapsize ;
300- execstack = LoadElf (m , elf );
373+ execstack = LoadElf (m , elf , fd );
301374 } else {
302375 elf -> base = 0x400000 ;
303376 elf -> ehdr = NULL ;
304377 elf -> size = 0 ;
305- LoadFlatExecutable (m , elf -> base , prog , elf -> map , elf -> mapsize );
378+ LoadFlatExecutable (m , elf -> base , prog , elf -> map , elf -> mapsize , fd );
306379 execstack = true;
307380 }
308381 sp = kStackTop ;
309382 Put64 (m -> sp , sp );
310383 if (ReserveVirtual (m -> system , sp - kStackSize , kStackSize ,
311- PAGE_U | PAGE_RW | (execstack ? 0 : PAGE_XD ), -1 ,
384+ PAGE_U | PAGE_RW | (execstack ? 0 : PAGE_XD ), -1 , 0 ,
312385 false) == -1 ) {
313386 LOGF ("failed to reserve stack memory" );
314387 exit (127 );
@@ -319,4 +392,5 @@ error: unsupported executable; we need:\n\
319392 pagesize = sysconf (_SC_PAGESIZE );
320393 pagesize = MAX (4096 , pagesize );
321394 m -> system -> brk = ROUNDUP (m -> system -> brk , pagesize );
395+ unassert (!close (fd ));
322396}
0 commit comments