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