1 /* dump-elf.c
2  *
3  * $Id$
4  *
5  * Copyright 1990, 1991, 1992, 1993, 1994, 1995, Oliver Laumann, Berlin
6  * Copyright 2002, 2003 Sam Hocevar <sam@hocevar.net>, Paris
7  *
8  * This software was derived from Elk 1.2, which was Copyright 1987, 1988,
9  * 1989, Nixdorf Computer AG and TELES GmbH, Berlin (Elk 1.2 has been written
10  * by Oliver Laumann for TELES Telematic Services, Berlin, in a joint project
11  * between TELES and Nixdorf Microprocessor Engineering, Berlin).
12  *
13  * Oliver Laumann, TELES GmbH, Nixdorf Computer AG and Sam Hocevar, as co-
14  * owners or individual owners of copyright in this software, grant to any
15  * person or company a worldwide, royalty free, license to
16  *
17  *    i) copy this software,
18  *   ii) prepare derivative works based on this software,
19  *  iii) distribute copies of this software or derivative works,
20  *   iv) perform this software, or
21  *    v) display this software,
22  *
23  * provided that this notice is not removed and that neither Oliver Laumann
24  * nor Teles nor Nixdorf are deemed to have made any representations as to
25  * the suitability of this software for any purpose nor are held responsible
26  * for any defects of this software.
27  *
28  * THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
29  */
30 
31 #include <elf.h>
32 #include <memory.h>
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <stdlib.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/mman.h>
40 
41 /* Find section header of section with given name.
42  */
43 #define FIND_SECTHDR(name,ndx) {\
44     char err[100];\
45     unsigned int _i;\
46     for (_i = 0; _i < ohdr->e_shnum; _i++)\
47         if (strcmp (sectstr+osecthdr[_i].sh_name, (name)) == 0) break;\
48     if (_i == ohdr->e_shnum) {\
49         Dump_Finalize;\
50         sprintf (err, "running a.out doesn't have %s section", (name));\
51         Primitive_Error (err);\
52     }\
53     (ndx) = _i;\
54 }
55 
56 /* Find section header of section with given name (if present, else
57  * set to -1).
58  */
59 #define FIND_SECTHDR_MAYBE(name,ndx) {\
60     int _i;\
61     for ((ndx) = -1, _i = 0; _i < ohdr->e_shnum; _i++)\
62         if (strcmp (sectstr+osecthdr[_i].sh_name, (name)) == 0) {\
63             (ndx) = _i;\
64             break;\
65         }\
66 }
67 
68 /* If a new section was inserted, adjust section index if it points behind
69  * old .bss section
70  */
71 #define UPDATE_SHNDX(ndx) if (sect_created && (ndx) >= obssndx) (ndx)++;
72 
73 
74 /* Bug: the mmapped regions are never munmapped again.
75  */
76 
P_Dump(Object ofile)77 Object P_Dump (Object ofile) {
78     /*
79      * ELF header, section header table, program header table of running
80      * a.out and new a.out
81      */
82     Elf32_Ehdr *ohdr, *nhdr;
83     Elf32_Shdr *osecthdr, *nsecthdr;
84     Elf32_Phdr *oproghdr, *nproghdr;
85     /*
86      * .bss section index and section header pointer of running a.out
87      */
88     unsigned int obssndx;
89     Elf32_Shdr *obssp;
90     /*
91      * .mdebug section index
92      */
93     int mdebugndx;
94     /*
95      * Pointers to section headers of new .bss and new .data
96      */
97     Elf32_Shdr *nbssp, *ndatap;
98     /*
99      * Memory address, size, and file offset of newly created .data section
100      */
101     Elf32_Addr ndata;
102     Elf32_Word ndatasize;
103     Elf32_Off ndataoff;
104     /*
105      * Start of .shstrtab section of running a.out
106      */
107     char *sectstr;
108     /*
109      * Memory address of running a.out and new a.out (mmap() return value)
110      */
111     char *oaddr, *naddr;
112 
113     struct stat st;
114     unsigned int i;
115     int sect_created = !Was_Dumped;
116 
117     Dump_Prolog;
118 
119     /* mmap running a.out, setup pointers to ELF header, section header
120      * table, program header table, section names, and old .bss
121      * XXX: call munmap later.
122      */
123     if (fstat (afd, &st) == -1) {
124         Dump_Finalize;
125         Primitive_Error ("cannot fstat running a.out: ~E");
126     }
127     oaddr = (char *)mmap ((caddr_t)0, st.st_size, PROT_READ, MAP_SHARED,
128         afd, 0);
129     if (oaddr == (char *)-1) {
130         Dump_Finalize;
131         Primitive_Error ("cannot mmap running a.out: ~E");
132     }
133     ohdr     = (Elf32_Ehdr *)(oaddr);
134     osecthdr = (Elf32_Shdr *)(oaddr + ohdr->e_shoff);
135     oproghdr = (Elf32_Phdr *)(oaddr + ohdr->e_phoff);
136     if (ohdr->e_shstrndx == SHN_UNDEF) {
137         Dump_Finalize;
138         Primitive_Error ("running a.out doesn't have section names");
139     }
140     sectstr = oaddr + osecthdr[ohdr->e_shstrndx].sh_offset;
141     FIND_SECTHDR (".bss", obssndx);
142     obssp = osecthdr+obssndx;
143 
144     FIND_SECTHDR_MAYBE (".mdebug", mdebugndx);
145 
146     /* Determine size of newly created .data section; address and file
147      * offset are that of the old .bss section
148      */
149     if ((Brk_On_Dump = sbrk (0)) == (char *)-1) {
150         Dump_Finalize;
151         Primitive_Error ("sbrk(0) failed: ~E");
152     }
153     ndata     = obssp->sh_addr;
154     ndatasize = (Elf32_Addr)((intptr_t)Brk_On_Dump - (intptr_t)ndata);
155     ndataoff  = obssp->sh_offset;
156 
157     /* mmap new a.out file, setup pointers to ELF header, section header
158      * table, and program header table
159      * XXX: munmap missing
160      */
161     st.st_size += ndatasize;
162     if (!Was_Dumped)
163         st.st_size += sizeof (osecthdr[0]);
164     if (ftruncate (ofd, st.st_size) == -1) {
165         Dump_Finalize;
166         Primitive_Error ("cannot ftruncate new a.out: ~E");
167     }
168     naddr = (char *)mmap ((caddr_t)0, st.st_size, PROT_READ|PROT_WRITE,
169         MAP_SHARED, ofd, 0);
170     if (naddr == (char *)-1) {
171         Dump_Finalize;
172         Primitive_Error ("cannot mmap new a.out: ~E");
173     }
174     nhdr     = (Elf32_Ehdr *)(naddr);
175     nsecthdr = (Elf32_Shdr *)(naddr + ohdr->e_shoff + ndatasize);
176     nproghdr = (Elf32_Phdr *)(naddr + ohdr->e_phoff);
177 
178     /* Copy and adjust ELF header, copy program header table
179      */
180     *nhdr = *ohdr;
181     if (!Was_Dumped)
182         nhdr->e_shnum++;
183     UPDATE_SHNDX (nhdr->e_shstrndx);
184     nhdr->e_shoff += ndatasize;
185     memcpy ((void *)nproghdr, (void *)oproghdr,
186         ohdr->e_phnum * sizeof (oproghdr[0]));
187 
188     /* Scan program header table and search for a loadable segment that
189      * ends immediately below the .bss section.  Extend this segment so
190      * that it encompasses the newly created .data section.
191      * There must not exist any segment above the new .data.
192      */
193 #define max(a,b) ((a) > (b) ? (a) : (b))
194     for (i = 0; i < nhdr->e_phnum; i++) {
195         Elf32_Phdr *pp = nproghdr+i;
196         unsigned int mask = max(pp->p_align, obssp->sh_addralign) - 1;
197         Elf32_Addr ends_at = (pp->p_vaddr + pp->p_filesz + mask) & ~mask;
198         Elf32_Addr bssend = (obssp->sh_addr + mask) & ~mask;
199 #ifndef __sgi
200         if (pp->p_vaddr + pp->p_filesz > obssp->sh_addr) {
201             Dump_Finalize;
202             Primitive_Error ("running a.out has segment above .bss");
203         }
204 #endif
205         if (pp->p_type == PT_LOAD && ends_at == bssend)
206             break;
207     }
208 
209     nproghdr[i].p_filesz += ndatasize;
210     nproghdr[i].p_memsz = nproghdr[i].p_filesz;  /* load entire segment */
211 
212 #ifdef __sgi
213     for (i = 0; i < nhdr->e_phnum; i++) {
214         Elf32_Phdr *pp = nproghdr+i;
215 
216         if (pp->p_vaddr >= ndata)
217             pp->p_vaddr += ndatasize - obssp->sh_size;
218         if (pp->p_offset >= ndataoff)
219             pp->p_offset += ndatasize;
220     }
221 #endif
222 
223     if (Was_Dumped) {
224         /* No need to insert a new data section header.  Just copy
225          * section header table.  Data segment to be adjusted must
226          * be immediately before .bss
227          */
228         memcpy ((void*)nsecthdr, (void *)osecthdr,
229             nhdr->e_shnum * sizeof (osecthdr[0]));
230         nbssp = nsecthdr + obssndx;
231         ndatap = nbssp - 1;
232         if (strcmp (sectstr+ndatap->sh_name, ".data")) {
233             Dump_Finalize;
234             Primitive_Error ("missing .data section in dumped a.out");
235         }
236         ndatap->sh_size += ndatasize;
237     } else {
238         /* Copy section headers up to old .bss, then copy remaining section
239          * headers shifted by one position to make room for new .data
240          */
241         memcpy ((void *)nsecthdr, (void *)osecthdr,
242             obssndx * sizeof (osecthdr[0]));
243         ndatap = nsecthdr + obssndx;
244         nbssp = ndatap + 1;
245         memcpy ((void *)nbssp, (void *)obssp,
246             (nhdr->e_shnum-obssndx) * sizeof (osecthdr[0]));
247 
248         /* Initialize section header for new .data section with values
249          * from old .data section; set new address, size, and file offset
250          */
251         FIND_SECTHDR (".data", i);
252         ndatap[0] = osecthdr[i];
253         ndatap->sh_addr = ndata;
254         ndatap->sh_size = ndatasize;
255         ndatap->sh_offset = ndataoff;
256     }
257     nbssp->sh_size = 0;
258     nbssp->sh_addr += ndatasize;
259 
260     /* Now copy the contents of the sections.  If section is in memory
261      * and writable, copy from memory, else copy from a.out file.
262      * Skip sections that are inactive or occupy no space in file.
263      * Adjust file offset of sections behind new .data section.
264      */
265     Was_Dumped = 1;
266     for (i = 1; i < nhdr->e_shnum; i++) {
267         void *from;
268         Elf32_Shdr *sp = nsecthdr+i;
269 #ifdef DEBUG_DUMP
270         printf ("%s (from %s)", sectstr+sp->sh_name, (sp->sh_flags &
271             (SHF_ALLOC|SHF_WRITE)) == (SHF_ALLOC|SHF_WRITE) ?
272             "memory" : "file"); (void)fflush (stdout);
273 #endif
274         if ((sp->sh_flags & (SHF_ALLOC|SHF_WRITE)) == (SHF_ALLOC|SHF_WRITE))
275             from = (void *)(intptr_t)sp->sh_addr;
276         else
277             from = (void *)(oaddr + sp->sh_offset);
278         if (sp != ndatap && sp->sh_offset >= ndataoff)
279             sp->sh_offset += ndatasize;
280         if (sp->sh_type != SHT_NULL && sp->sh_type != SHT_NOBITS) {
281 #ifdef DEBUG_DUMP
282             printf (" copy from %p to %p size %x", from, naddr+sp->sh_offset,
283                 sp->sh_size); (void)fflush (stdout);
284 #endif
285             memcpy ((void *)(naddr + sp->sh_offset), from, sp->sh_size);
286         }
287 #ifdef DEBUG_DUMP
288         printf ("\n");
289 #endif
290     }
291 
292     /* Go through all section headers and fixup sh_link and sh_info fields
293      * that point behind new .data section, also fixup st_shndx fields in
294      * symbol table entries
295      */
296     for (i = 1; i < nhdr->e_shnum; i++) {
297         Elf32_Shdr *sp = nsecthdr+i;
298 
299         UPDATE_SHNDX (sp->sh_link);
300         if (sp->sh_type != SHT_DYNSYM && sp->sh_type != SHT_SYMTAB)
301             UPDATE_SHNDX (sp->sh_info);
302 
303         if (sp->sh_type == SHT_SYMTAB || sp->sh_type == SHT_DYNSYM) {
304             Elf32_Sym *p = (Elf32_Sym *)(naddr + sp->sh_offset),
305                      *ep = p + sp->sh_size / sp->sh_entsize;
306             for ( ; p < ep; p++) switch (p->st_shndx) {
307                 case SHN_UNDEF: case SHN_ABS: case SHN_COMMON:
308                     break;
309                 default:
310                     UPDATE_SHNDX (p->st_shndx);
311             }
312         }
313     }
314 
315 #ifdef __sgi
316     /* If the .mdebug section is located after the newly inserted section,
317      * update the offsets.
318      */
319     if (mdebugndx >= obssndx) {
320         HDRR *mp;
321         mdebugndx++;
322         mp = (HDRR *)(naddr + nsecthdr[mdebugndx].sh_offset);
323         if (mp->cbLine > 0)
324             mp->cbLineOffset += ndatasize;
325         if (mp->idnMax > 0)
326             mp->cbDnOffset += ndatasize;
327         if (mp->ipdMax > 0)
328             mp->cbPdOffset += ndatasize;
329         if (mp->isymMax > 0)
330             mp->cbSymOffset += ndatasize;
331         if (mp->ioptMax > 0)
332             mp->cbOptOffset += ndatasize;
333         if (mp->iauxMax > 0)
334             mp->cbAuxOffset += ndatasize;
335         if (mp->issMax > 0)
336             mp->cbSsOffset += ndatasize;
337         if (mp->issExtMax > 0)
338             mp->cbSsExtOffset += ndatasize;
339         if (mp->ifdMax > 0)
340             mp->cbFdOffset += ndatasize;
341         if (mp->crfd > 0)
342             mp->cbRfdOffset += ndatasize;
343         if (mp->iextMax > 0)
344             mp->cbExtOffset += ndatasize;
345     }
346 #endif
347 
348     Dump_Epilog;
349 }
350