1 /******************************************************************************
2  * mkelf32.c
3  *
4  * Usage: elf-prefix <in-image> <out-image> <load-base>
5  *
6  * Converts an Elf64 executable binary <in-image> into a simple Elf32
7  * image <out-image> comprising a single chunk to be loaded at <load-base>.
8  */
9 
10 #include <errno.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <unistd.h>
18 #include <inttypes.h>
19 
20 #define u8  uint8_t
21 #define u16 uint16_t
22 #define u32 uint32_t
23 #define u64 uint64_t
24 #define s8  int8_t
25 #define s16 int16_t
26 #define s32 int32_t
27 #define s64 int64_t
28 #include "../../../include/xen/elfstructs.h"
29 
30 #define DYNAMICALLY_FILLED   0
31 #define RAW_OFFSET         128
32 
33 static Elf32_Ehdr out_ehdr = {
34     { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,    /* EI_MAG{0-3} */
35       ELFCLASS32,                            /* EI_CLASS */
36       ELFDATA2LSB,                           /* EI_DATA */
37       EV_CURRENT,                            /* EI_VERSION */
38       0, 0, 0, 0, 0, 0, 0, 0, 0 },           /* e_ident */
39     ET_EXEC,                                 /* e_type */
40     EM_386,                                  /* e_machine */
41     EV_CURRENT,                              /* e_version */
42     DYNAMICALLY_FILLED,                      /* e_entry */
43     sizeof(Elf32_Ehdr),                      /* e_phoff */
44     DYNAMICALLY_FILLED,                      /* e_shoff */
45     0,                                       /* e_flags */
46     sizeof(Elf32_Ehdr),                      /* e_ehsize */
47     sizeof(Elf32_Phdr),                      /* e_phentsize */
48     1,  /* modify based on num_phdrs */      /* e_phnum */
49     sizeof(Elf32_Shdr),                      /* e_shentsize */
50     3,  /* modify based on num_phdrs */      /* e_shnum */
51     2                                        /* e_shstrndx */
52 };
53 
54 static Elf32_Phdr out_phdr = {
55     PT_LOAD,                                 /* p_type */
56     RAW_OFFSET,                              /* p_offset */
57     DYNAMICALLY_FILLED,                      /* p_vaddr */
58     DYNAMICALLY_FILLED,                      /* p_paddr */
59     DYNAMICALLY_FILLED,                      /* p_filesz */
60     DYNAMICALLY_FILLED,                      /* p_memsz */
61     PF_R|PF_W|PF_X,                          /* p_flags */
62     64                                       /* p_align */
63 };
64 static Elf32_Phdr note_phdr = {
65     PT_NOTE,                                 /* p_type */
66     DYNAMICALLY_FILLED,                      /* p_offset */
67     DYNAMICALLY_FILLED,                      /* p_vaddr */
68     DYNAMICALLY_FILLED,                      /* p_paddr */
69     DYNAMICALLY_FILLED,                      /* p_filesz */
70     DYNAMICALLY_FILLED,                      /* p_memsz */
71     PF_R,                                    /* p_flags */
72     4                                        /* p_align */
73 };
74 
75 static u8 out_shstrtab[] = "\0.text\0.shstrtab";
76 /* If num_phdrs >= 2, we need to tack the .note. */
77 static u8 out_shstrtab_extra[] = ".note\0";
78 
79 static Elf32_Shdr out_shdr[] = {
80     { 0 },
81     { 1,                                     /* sh_name */
82       SHT_PROGBITS,                          /* sh_type */
83       SHF_WRITE|SHF_ALLOC|SHF_EXECINSTR,     /* sh_flags */
84       DYNAMICALLY_FILLED,                    /* sh_addr */
85       RAW_OFFSET,                            /* sh_offset */
86       DYNAMICALLY_FILLED,                    /* sh_size */
87       0,                                     /* sh_link */
88       0,                                     /* sh_info */
89       64,                                    /* sh_addralign */
90       0                                      /* sh_entsize */
91     },
92     { 7,                                     /* sh_name */
93       SHT_STRTAB,                            /* sh_type */
94       0,                                     /* sh_flags */
95       0,                                     /* sh_addr */
96       DYNAMICALLY_FILLED,                    /* sh_offset */
97       sizeof(out_shstrtab),                  /* sh_size */
98       0,                                     /* sh_link */
99       0,                                     /* sh_info */
100       1,                                     /* sh_addralign */
101       0                                      /* sh_entsize */
102     }
103 };
104 
105 /*
106  * The 17 points to the '.note' in the out_shstrtab and out_shstrtab_extra
107  * laid out in the file.
108  */
109 static Elf32_Shdr out_shdr_note = {
110       17,                                    /* sh_name */
111       SHT_NOTE,                              /* sh_type */
112       0,                                     /* sh_flags */
113       DYNAMICALLY_FILLED,                    /* sh_addr */
114       DYNAMICALLY_FILLED,                    /* sh_offset */
115       DYNAMICALLY_FILLED,                    /* sh_size */
116       0,                                     /* sh_link */
117       0,                                     /* sh_info */
118       4,                                     /* sh_addralign */
119       0                                      /* sh_entsize */
120 };
121 
122 /* Some system header files define these macros and pollute our namespace. */
123 #undef swap16
124 #undef swap32
125 #undef swap64
126 
127 #define swap16(_v) ((((u16)(_v)>>8)&0xff)|(((u16)(_v)&0xff)<<8))
128 #define swap32(_v) (((u32)swap16((u16)(_v))<<16)|(u32)swap16((u32)((_v)>>16)))
129 #define swap64(_v) (((u64)swap32((u32)(_v))<<32)|(u64)swap32((u32)((_v)>>32)))
130 
131 static int big_endian;
132 
endianadjust_ehdr32(Elf32_Ehdr * eh)133 static void endianadjust_ehdr32(Elf32_Ehdr *eh)
134 {
135     if ( !big_endian )
136         return;
137     eh->e_type      = swap16(eh->e_type);
138     eh->e_machine   = swap16(eh->e_machine);
139     eh->e_version   = swap32(eh->e_version);
140     eh->e_entry     = swap32(eh->e_entry);
141     eh->e_phoff     = swap32(eh->e_phoff);
142     eh->e_shoff     = swap32(eh->e_shoff);
143     eh->e_flags     = swap32(eh->e_flags);
144     eh->e_ehsize    = swap16(eh->e_ehsize);
145     eh->e_phentsize = swap16(eh->e_phentsize);
146     eh->e_phnum     = swap16(eh->e_phnum);
147     eh->e_shentsize = swap16(eh->e_shentsize);
148     eh->e_shnum     = swap16(eh->e_shnum);
149     eh->e_shstrndx  = swap16(eh->e_shstrndx);
150 }
151 
endianadjust_ehdr64(Elf64_Ehdr * eh)152 static void endianadjust_ehdr64(Elf64_Ehdr *eh)
153 {
154     if ( !big_endian )
155         return;
156     eh->e_type      = swap16(eh->e_type);
157     eh->e_machine   = swap16(eh->e_machine);
158     eh->e_version   = swap32(eh->e_version);
159     eh->e_entry     = swap64(eh->e_entry);
160     eh->e_phoff     = swap64(eh->e_phoff);
161     eh->e_shoff     = swap64(eh->e_shoff);
162     eh->e_flags     = swap32(eh->e_flags);
163     eh->e_ehsize    = swap16(eh->e_ehsize);
164     eh->e_phentsize = swap16(eh->e_phentsize);
165     eh->e_phnum     = swap16(eh->e_phnum);
166     eh->e_shentsize = swap16(eh->e_shentsize);
167     eh->e_shnum     = swap16(eh->e_shnum);
168     eh->e_shstrndx  = swap16(eh->e_shstrndx);
169 }
170 
endianadjust_phdr32(Elf32_Phdr * ph)171 static void endianadjust_phdr32(Elf32_Phdr *ph)
172 {
173     if ( !big_endian )
174         return;
175     ph->p_type      = swap32(ph->p_type);
176     ph->p_offset    = swap32(ph->p_offset);
177     ph->p_vaddr     = swap32(ph->p_vaddr);
178     ph->p_paddr     = swap32(ph->p_paddr);
179     ph->p_filesz    = swap32(ph->p_filesz);
180     ph->p_memsz     = swap32(ph->p_memsz);
181     ph->p_flags     = swap32(ph->p_flags);
182     ph->p_align     = swap32(ph->p_align);
183 }
184 
endianadjust_phdr64(Elf64_Phdr * ph)185 static void endianadjust_phdr64(Elf64_Phdr *ph)
186 {
187     if ( !big_endian )
188         return;
189     ph->p_type      = swap32(ph->p_type);
190     ph->p_flags     = swap32(ph->p_flags);
191     ph->p_offset    = swap64(ph->p_offset);
192     ph->p_vaddr     = swap64(ph->p_vaddr);
193     ph->p_paddr     = swap64(ph->p_paddr);
194     ph->p_filesz    = swap64(ph->p_filesz);
195     ph->p_memsz     = swap64(ph->p_memsz);
196     ph->p_align     = swap64(ph->p_align);
197 }
198 
endianadjust_shdr32(Elf32_Shdr * sh)199 static void endianadjust_shdr32(Elf32_Shdr *sh)
200 {
201     if ( !big_endian )
202         return;
203     sh->sh_name     = swap32(sh->sh_name);
204     sh->sh_type     = swap32(sh->sh_type);
205     sh->sh_flags    = swap32(sh->sh_flags);
206     sh->sh_addr     = swap32(sh->sh_addr);
207     sh->sh_offset   = swap32(sh->sh_offset);
208     sh->sh_size     = swap32(sh->sh_size);
209     sh->sh_link     = swap32(sh->sh_link);
210     sh->sh_info     = swap32(sh->sh_info);
211     sh->sh_addralign = swap32(sh->sh_addralign);
212     sh->sh_entsize  = swap32(sh->sh_entsize);
213 }
214 
do_write(int fd,void * data,int len)215 static void do_write(int fd, void *data, int len)
216 {
217     int   done, left = len;
218     char *p = data;
219 
220     while ( left != 0 )
221     {
222         if ( (done = write(fd, p, left)) == -1 )
223         {
224             if ( errno == EINTR )
225                 continue;
226             fprintf(stderr, "Error writing output image: %d (%s).\n",
227                     errno, strerror(errno));
228             exit(1);
229         }
230 
231         left -= done;
232         p    += done;
233     }
234 }
235 
do_read(int fd,void * data,int len)236 static void do_read(int fd, void *data, int len)
237 {
238     int   done, left = len;
239     char *p = data;
240 
241     while ( left != 0 )
242     {
243         if ( (done = read(fd, p, left)) == -1 )
244         {
245             if ( errno == EINTR )
246                 continue;
247             fprintf(stderr, "Error reading input image: %d (%s).\n",
248                     errno, strerror(errno));
249             exit(1);
250         }
251 
252         left -= done;
253         p    += done;
254     }
255 }
256 
main(int argc,char ** argv)257 int main(int argc, char **argv)
258 {
259     u64        final_exec_addr;
260     u32        loadbase, dat_siz, mem_siz, note_base, note_sz, offset;
261     char      *inimage, *outimage;
262     int        infd, outfd;
263     char       buffer[1024] = {};
264     int        bytes, todo, i = 1;
265     int        num_phdrs = 1;
266 
267     Elf32_Ehdr in32_ehdr;
268 
269     Elf64_Ehdr in64_ehdr;
270     Elf64_Phdr in64_phdr;
271 
272     if ( argc < 5 )
273     {
274         fprintf(stderr, "Usage: mkelf32 [--notes] <in-image> <out-image> "
275                 "<load-base> <final-exec-addr>\n");
276         return 1;
277     }
278 
279     if ( !strcmp(argv[1], "--notes") )
280     {
281         i = 2;
282         num_phdrs = 2;
283     }
284     inimage  = argv[i++];
285     outimage = argv[i++];
286     loadbase = strtoul(argv[i++], NULL, 16);
287     final_exec_addr = strtoull(argv[i++], NULL, 16);
288 
289     infd = open(inimage, O_RDONLY);
290     if ( infd == -1 )
291     {
292         fprintf(stderr, "Failed to open input image '%s': %d (%s).\n",
293                 inimage, errno, strerror(errno));
294         return 1;
295     }
296 
297     do_read(infd, &in32_ehdr, sizeof(in32_ehdr));
298     if ( !IS_ELF(in32_ehdr) ||
299          (in32_ehdr.e_ident[EI_DATA] != ELFDATA2LSB) )
300     {
301         fprintf(stderr, "Input image must be a little-endian Elf image.\n");
302         return 1;
303     }
304 
305     big_endian = (*(u16 *)in32_ehdr.e_ident == ((ELFMAG0 << 8) | ELFMAG1));
306 
307     endianadjust_ehdr32(&in32_ehdr);
308     if ( in32_ehdr.e_ident[EI_CLASS] != ELFCLASS64 )
309     {
310         fprintf(stderr, "Bad program header class - we only do 64-bit!.\n");
311         return 1;
312     }
313     (void)lseek(infd, 0, SEEK_SET);
314     do_read(infd, &in64_ehdr, sizeof(in64_ehdr));
315     endianadjust_ehdr64(&in64_ehdr);
316 
317     if ( in64_ehdr.e_phentsize != sizeof(in64_phdr) )
318     {
319         fprintf(stderr, "Bad program header size (%d != %d).\n",
320                 (int)in64_ehdr.e_phentsize, (int)sizeof(in64_phdr));
321         return 1;
322     }
323     if ( in64_ehdr.e_phnum != num_phdrs )
324     {
325         fprintf(stderr, "Expect precisly %d program header; found %d.\n",
326                 num_phdrs, (int)in64_ehdr.e_phnum);
327         return 1;
328     }
329 
330     (void)lseek(infd, in64_ehdr.e_phoff, SEEK_SET);
331     do_read(infd, &in64_phdr, sizeof(in64_phdr));
332     endianadjust_phdr64(&in64_phdr);
333 
334     (void)lseek(infd, in64_phdr.p_offset, SEEK_SET);
335     dat_siz = (u32)in64_phdr.p_filesz;
336 
337     /* Do not use p_memsz: it does not include BSS alignment padding. */
338     /*mem_siz = (u32)in64_phdr.p_memsz;*/
339     mem_siz = (u32)(final_exec_addr - in64_phdr.p_vaddr);
340 
341     note_sz = note_base = offset = 0;
342     if ( num_phdrs > 1 )
343     {
344         offset = in64_phdr.p_offset;
345         note_base = in64_phdr.p_vaddr;
346 
347         (void)lseek(infd, in64_ehdr.e_phoff+sizeof(in64_phdr), SEEK_SET);
348         do_read(infd, &in64_phdr, sizeof(in64_phdr));
349         endianadjust_phdr64(&in64_phdr);
350 
351         (void)lseek(infd, offset, SEEK_SET);
352 
353         note_sz = in64_phdr.p_memsz;
354         note_base = in64_phdr.p_vaddr - note_base;
355 
356         if ( in64_phdr.p_offset > dat_siz || offset > in64_phdr.p_offset )
357         {
358             fprintf(stderr, "Expected .note section within .text section!\n" \
359                     "Offset %"PRId64" not within %d!\n",
360                     in64_phdr.p_offset, dat_siz);
361             return 1;
362         }
363         /* Gets us the absolute offset within the .text section. */
364         offset = in64_phdr.p_offset - offset;
365     }
366 
367     /*
368      * End the image on a page boundary. This gets round alignment bugs
369      * in the boot- or chain-loader (e.g., kexec on the XenoBoot CD).
370      */
371     mem_siz += -(loadbase + mem_siz) & 0xfff;
372 
373     out_ehdr.e_entry = loadbase;
374     out_ehdr.e_shoff = RAW_OFFSET + dat_siz;
375 
376     out_phdr.p_vaddr  = loadbase;
377     out_phdr.p_paddr  = loadbase;
378     out_phdr.p_filesz = dat_siz;
379     out_phdr.p_memsz  = mem_siz;
380 
381     out_shdr[1].sh_addr   = loadbase;
382     out_shdr[1].sh_size   = dat_siz;
383     out_shdr[2].sh_offset = RAW_OFFSET + dat_siz + sizeof(out_shdr);
384 
385     if ( num_phdrs > 1 )
386     {
387         /* We have two of them! */
388         out_ehdr.e_phnum = num_phdrs;
389         /* Extra .note section. */
390         out_ehdr.e_shnum++;
391 
392         /* Fill out the PT_NOTE program header. */
393         note_phdr.p_vaddr   = note_base;
394         note_phdr.p_paddr   = note_base;
395         note_phdr.p_filesz  = note_sz;
396         note_phdr.p_memsz   = note_sz;
397         note_phdr.p_offset  = RAW_OFFSET + offset;
398 
399         /* Tack on the .note\0 */
400         out_shdr[2].sh_size += sizeof(out_shstrtab_extra);
401         /* And move it past the .note section. */
402         out_shdr[2].sh_offset += sizeof(out_shdr_note);
403 
404         /* Fill out the .note section. */
405         out_shdr_note.sh_size = note_sz;
406         out_shdr_note.sh_addr = note_base;
407         out_shdr_note.sh_offset = RAW_OFFSET + offset;
408     }
409 
410     outfd = open(outimage, O_WRONLY|O_CREAT|O_TRUNC, 0775);
411     if ( outfd == -1 )
412     {
413         fprintf(stderr, "Failed to open output image '%s': %d (%s).\n",
414                 outimage, errno, strerror(errno));
415         return 1;
416     }
417 
418     endianadjust_ehdr32(&out_ehdr);
419     do_write(outfd, &out_ehdr, sizeof(out_ehdr));
420 
421     endianadjust_phdr32(&out_phdr);
422     do_write(outfd, &out_phdr, sizeof(out_phdr));
423 
424     if ( num_phdrs > 1 )
425     {
426         endianadjust_phdr32(&note_phdr);
427         do_write(outfd, &note_phdr, sizeof(note_phdr));
428     }
429 
430     if ( (bytes = RAW_OFFSET - sizeof(out_ehdr) - (num_phdrs * sizeof(out_phdr)) ) < 0 )
431     {
432         fprintf(stderr, "Header overflow.\n");
433         return 1;
434     }
435     do_write(outfd, buffer, bytes);
436 
437     for ( bytes = 0; bytes < dat_siz; bytes += todo )
438     {
439         todo = ((dat_siz - bytes) > sizeof(buffer)) ?
440             sizeof(buffer) : (dat_siz - bytes);
441         do_read(infd, buffer, todo);
442         do_write(outfd, buffer, todo);
443     }
444 
445     for ( i = 0; i < (sizeof(out_shdr) / sizeof(out_shdr[0])); i++ )
446         endianadjust_shdr32(&out_shdr[i]);
447     do_write(outfd, &out_shdr[0], sizeof(out_shdr));
448 
449     if ( num_phdrs > 1 )
450     {
451         endianadjust_shdr32(&out_shdr_note);
452         /* Append the .note section. */
453         do_write(outfd, &out_shdr_note, sizeof(out_shdr_note));
454         /* The normal strings - .text\0.. */
455         do_write(outfd, out_shstrtab, sizeof(out_shstrtab));
456         /* Our .note */
457         do_write(outfd, out_shstrtab_extra, sizeof(out_shstrtab_extra));
458         do_write(outfd, buffer, 4-((sizeof(out_shstrtab)+sizeof(out_shstrtab_extra)+dat_siz)&3));
459     }
460     else
461     {
462         do_write(outfd, out_shstrtab, sizeof(out_shstrtab));
463         do_write(outfd, buffer, 4-((sizeof(out_shstrtab)+dat_siz)&3));
464     }
465     close(infd);
466     close(outfd);
467 
468     return 0;
469 }
470 
471 /*
472  * Local variables:
473  * mode: C
474  * c-file-style: "BSD"
475  * c-basic-offset: 4
476  * tab-width: 4
477  * indent-tabs-mode: nil
478  * End:
479  */
480