elk/src/dump-elf.c

350 lines
11 KiB
C

/* dump-elf.c
*
* $Id$
*
* Copyright 1990, 1991, 1992, 1993, 1994, 1995, Oliver Laumann, Berlin
* Copyright 2002, 2003 Sam Hocevar <sam@zoy.org>, Paris
*
* This software was derived from Elk 1.2, which was Copyright 1987, 1988,
* 1989, Nixdorf Computer AG and TELES GmbH, Berlin (Elk 1.2 has been written
* by Oliver Laumann for TELES Telematic Services, Berlin, in a joint project
* between TELES and Nixdorf Microprocessor Engineering, Berlin).
*
* Oliver Laumann, TELES GmbH, Nixdorf Computer AG and Sam Hocevar, as co-
* owners or individual owners of copyright in this software, grant to any
* person or company a worldwide, royalty free, license to
*
* i) copy this software,
* ii) prepare derivative works based on this software,
* iii) distribute copies of this software or derivative works,
* iv) perform this software, or
* v) display this software,
*
* provided that this notice is not removed and that neither Oliver Laumann
* nor Teles nor Nixdorf are deemed to have made any representations as to
* the suitability of this software for any purpose nor are held responsible
* for any defects of this software.
*
* THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
*/
#include <elf.h>
#include <memory.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
/* Find section header of section with given name.
*/
#define FIND_SECTHDR(name,ndx) {\
char err[100];\
unsigned int _i;\
for (_i = 0; _i < ohdr->e_shnum; _i++)\
if (strcmp (sectstr+osecthdr[_i].sh_name, (name)) == 0) break;\
if (_i == ohdr->e_shnum) {\
Dump_Finalize;\
sprintf (err, "running a.out doesn't have %s section", (name));\
Primitive_Error (err);\
}\
(ndx) = _i;\
}
/* Find section header of section with given name (if present, else
* set to -1).
*/
#define FIND_SECTHDR_MAYBE(name,ndx) {\
int _i;\
for ((ndx) = -1, _i = 0; _i < ohdr->e_shnum; _i++)\
if (strcmp (sectstr+osecthdr[_i].sh_name, (name)) == 0) {\
(ndx) = _i;\
break;\
}\
}
/* If a new section was inserted, adjust section index if it points behind
* old .bss section
*/
#define UPDATE_SHNDX(ndx) if (sect_created && (ndx) >= obssndx) (ndx)++;
/* Bug: the mmapped regions are never munmapped again.
*/
Object P_Dump (Object ofile) {
/*
* ELF header, section header table, program header table of running
* a.out and new a.out
*/
Elf32_Ehdr *ohdr, *nhdr;
Elf32_Shdr *osecthdr, *nsecthdr;
Elf32_Phdr *oproghdr, *nproghdr;
/*
* .bss section index and section header pointer of running a.out
*/
unsigned int obssndx;
Elf32_Shdr *obssp;
/*
* .mdebug section index
*/
int mdebugndx;
/*
* Pointers to section headers of new .bss and new .data
*/
Elf32_Shdr *nbssp, *ndatap;
/*
* Memory address, size, and file offset of newly created .data section
*/
Elf32_Addr ndata;
Elf32_Word ndatasize;
Elf32_Off ndataoff;
/*
* Start of .shstrtab section of running a.out
*/
char *sectstr;
/*
* Memory address of running a.out and new a.out (mmap() return value)
*/
char *oaddr, *naddr;
struct stat st;
unsigned int i;
int sect_created = !Was_Dumped;
Dump_Prolog;
/* mmap running a.out, setup pointers to ELF header, section header
* table, program header table, section names, and old .bss
* XXX: call munmap later.
*/
if (fstat (afd, &st) == -1) {
Dump_Finalize;
Primitive_Error ("cannot fstat running a.out: ~E");
}
oaddr = (char *)mmap ((caddr_t)0, st.st_size, PROT_READ, MAP_SHARED,
afd, 0);
if (oaddr == (char *)-1) {
Dump_Finalize;
Primitive_Error ("cannot mmap running a.out: ~E");
}
ohdr = (Elf32_Ehdr *)(oaddr);
osecthdr = (Elf32_Shdr *)(oaddr + ohdr->e_shoff);
oproghdr = (Elf32_Phdr *)(oaddr + ohdr->e_phoff);
if (ohdr->e_shstrndx == SHN_UNDEF) {
Dump_Finalize;
Primitive_Error ("running a.out doesn't have section names");
}
sectstr = oaddr + osecthdr[ohdr->e_shstrndx].sh_offset;
FIND_SECTHDR (".bss", obssndx);
obssp = osecthdr+obssndx;
FIND_SECTHDR_MAYBE (".mdebug", mdebugndx);
/* Determine size of newly created .data section; address and file
* offset are that of the old .bss section
*/
if ((Brk_On_Dump = sbrk (0)) == (char *)-1) {
Dump_Finalize;
Primitive_Error ("sbrk(0) failed: ~E");
}
ndata = obssp->sh_addr;
ndatasize = (Elf32_Addr)((intptr_t)Brk_On_Dump - (intptr_t)ndata);
ndataoff = obssp->sh_offset;
/* mmap new a.out file, setup pointers to ELF header, section header
* table, and program header table
* XXX: munmap missing
*/
st.st_size += ndatasize;
if (!Was_Dumped)
st.st_size += sizeof (osecthdr[0]);
if (ftruncate (ofd, st.st_size) == -1) {
Dump_Finalize;
Primitive_Error ("cannot ftruncate new a.out: ~E");
}
naddr = (char *)mmap ((caddr_t)0, st.st_size, PROT_READ|PROT_WRITE,
MAP_SHARED, ofd, 0);
if (naddr == (char *)-1) {
Dump_Finalize;
Primitive_Error ("cannot mmap new a.out: ~E");
}
nhdr = (Elf32_Ehdr *)(naddr);
nsecthdr = (Elf32_Shdr *)(naddr + ohdr->e_shoff + ndatasize);
nproghdr = (Elf32_Phdr *)(naddr + ohdr->e_phoff);
/* Copy and adjust ELF header, copy program header table
*/
*nhdr = *ohdr;
if (!Was_Dumped)
nhdr->e_shnum++;
UPDATE_SHNDX (nhdr->e_shstrndx);
nhdr->e_shoff += ndatasize;
memcpy ((void *)nproghdr, (void *)oproghdr,
ohdr->e_phnum * sizeof (oproghdr[0]));
/* Scan program header table and search for a loadable segment that
* ends immediately below the .bss section. Extend this segment so
* that it encompasses the newly created .data section.
* There must not exist any segment above the new .data.
*/
#define max(a,b) ((a) > (b) ? (a) : (b))
for (i = 0; i < nhdr->e_phnum; i++) {
Elf32_Phdr *pp = nproghdr+i;
unsigned int mask = max(pp->p_align, obssp->sh_addralign) - 1;
Elf32_Addr ends_at = (pp->p_vaddr + pp->p_filesz + mask) & ~mask;
Elf32_Addr bssend = (obssp->sh_addr + mask) & ~mask;
#ifndef __sgi
if (pp->p_vaddr + pp->p_filesz > obssp->sh_addr) {
Dump_Finalize;
Primitive_Error ("running a.out has segment above .bss");
}
#endif
if (pp->p_type == PT_LOAD && ends_at == bssend)
break;
}
nproghdr[i].p_filesz += ndatasize;
nproghdr[i].p_memsz = nproghdr[i].p_filesz; /* load entire segment */
#ifdef __sgi
for (i = 0; i < nhdr->e_phnum; i++) {
Elf32_Phdr *pp = nproghdr+i;
if (pp->p_vaddr >= ndata)
pp->p_vaddr += ndatasize - obssp->sh_size;
if (pp->p_offset >= ndataoff)
pp->p_offset += ndatasize;
}
#endif
if (Was_Dumped) {
/* No need to insert a new data section header. Just copy
* section header table. Data segment to be adjusted must
* be immediately before .bss
*/
memcpy ((void*)nsecthdr, (void *)osecthdr,
nhdr->e_shnum * sizeof (osecthdr[0]));
nbssp = nsecthdr + obssndx;
ndatap = nbssp - 1;
if (strcmp (sectstr+ndatap->sh_name, ".data")) {
Dump_Finalize;
Primitive_Error ("missing .data section in dumped a.out");
}
ndatap->sh_size += ndatasize;
} else {
/* Copy section headers up to old .bss, then copy remaining section
* headers shifted by one position to make room for new .data
*/
memcpy ((void *)nsecthdr, (void *)osecthdr,
obssndx * sizeof (osecthdr[0]));
ndatap = nsecthdr + obssndx;
nbssp = ndatap + 1;
memcpy ((void *)nbssp, (void *)obssp,
(nhdr->e_shnum-obssndx) * sizeof (osecthdr[0]));
/* Initialize section header for new .data section with values
* from old .data section; set new address, size, and file offset
*/
FIND_SECTHDR (".data", i);
ndatap[0] = osecthdr[i];
ndatap->sh_addr = ndata;
ndatap->sh_size = ndatasize;
ndatap->sh_offset = ndataoff;
}
nbssp->sh_size = 0;
nbssp->sh_addr += ndatasize;
/* Now copy the contents of the sections. If section is in memory
* and writable, copy from memory, else copy from a.out file.
* Skip sections that are inactive or occupy no space in file.
* Adjust file offset of sections behind new .data section.
*/
Was_Dumped = 1;
for (i = 1; i < nhdr->e_shnum; i++) {
void *from;
Elf32_Shdr *sp = nsecthdr+i;
#ifdef DEBUG_DUMP
printf ("%s (from %s)", sectstr+sp->sh_name, (sp->sh_flags &
(SHF_ALLOC|SHF_WRITE)) == (SHF_ALLOC|SHF_WRITE) ?
"memory" : "file"); (void)fflush (stdout);
#endif
if ((sp->sh_flags & (SHF_ALLOC|SHF_WRITE)) == (SHF_ALLOC|SHF_WRITE))
from = (void *)(intptr_t)sp->sh_addr;
else
from = (void *)(oaddr + sp->sh_offset);
if (sp != ndatap && sp->sh_offset >= ndataoff)
sp->sh_offset += ndatasize;
if (sp->sh_type != SHT_NULL && sp->sh_type != SHT_NOBITS) {
#ifdef DEBUG_DUMP
printf (" copy from %p to %p size %x", from, naddr+sp->sh_offset,
sp->sh_size); (void)fflush (stdout);
#endif
memcpy ((void *)(naddr + sp->sh_offset), from, sp->sh_size);
}
#ifdef DEBUG_DUMP
printf ("\n");
#endif
}
/* Go through all section headers and fixup sh_link and sh_info fields
* that point behind new .data section, also fixup st_shndx fields in
* symbol table entries
*/
for (i = 1; i < nhdr->e_shnum; i++) {
Elf32_Shdr *sp = nsecthdr+i;
UPDATE_SHNDX (sp->sh_link);
if (sp->sh_type != SHT_DYNSYM && sp->sh_type != SHT_SYMTAB)
UPDATE_SHNDX (sp->sh_info);
if (sp->sh_type == SHT_SYMTAB || sp->sh_type == SHT_DYNSYM) {
Elf32_Sym *p = (Elf32_Sym *)(naddr + sp->sh_offset),
*ep = p + sp->sh_size / sp->sh_entsize;
for ( ; p < ep; p++) switch (p->st_shndx) {
case SHN_UNDEF: case SHN_ABS: case SHN_COMMON:
break;
default:
UPDATE_SHNDX (p->st_shndx);
}
}
}
#ifdef __sgi
/* If the .mdebug section is located after the newly inserted section,
* update the offsets.
*/
if (mdebugndx >= obssndx) {
HDRR *mp;
mdebugndx++;
mp = (HDRR *)(naddr + nsecthdr[mdebugndx].sh_offset);
if (mp->cbLine > 0)
mp->cbLineOffset += ndatasize;
if (mp->idnMax > 0)
mp->cbDnOffset += ndatasize;
if (mp->ipdMax > 0)
mp->cbPdOffset += ndatasize;
if (mp->isymMax > 0)
mp->cbSymOffset += ndatasize;
if (mp->ioptMax > 0)
mp->cbOptOffset += ndatasize;
if (mp->iauxMax > 0)
mp->cbAuxOffset += ndatasize;
if (mp->issMax > 0)
mp->cbSsOffset += ndatasize;
if (mp->issExtMax > 0)
mp->cbSsExtOffset += ndatasize;
if (mp->ifdMax > 0)
mp->cbFdOffset += ndatasize;
if (mp->crfd > 0)
mp->cbRfdOffset += ndatasize;
if (mp->iextMax > 0)
mp->cbExtOffset += ndatasize;
}
#endif
Dump_Epilog;
}