Files
QNX/lib/elf/libelf.c
2025-08-20 19:02:58 +08:00

1353 lines
28 KiB
C

/*
* $QNXLicenseC:
* Copyright 2007, QNX Software Systems. All Rights Reserved.
*
* You must obtain a written license from and pay applicable license fees to QNX
* Software Systems before you may reproduce, modify or distribute this software,
* or any work that includes all or part of this software. Free development
* licenses are available for evaluation and non-commercial purposes. For more
* information visit http://licensing.qnx.com or email licensing@qnx.com.
*
* This file may contain contributions from others. Please review this entire
* file for other proprietary rights or license notices, as well as the QNX
* Development Suite License Guide at http://licensing.qnx.com/license-guide/
* for other information.
* $
*/
#include <lib/compat.h>
#ifdef _NTO_HDR_DIR_
#define _PLATFORM(x) x
#define PLATFORM(x) <_PLATFORM(x)sys/platform.h>
#include PLATFORM(_NTO_HDR_DIR_)
#endif
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <libelf_int.h>
#ifdef __MINGW32__
typedef unsigned long ulong_t;
#endif
//
// This is for Solaris, because min is not defined in sun's stdlib.h
//
#if !defined(min) && !defined(__cplusplus)
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
static unsigned _elf_version = EV_CURRENT;
static char _elf_fill_char;
static int _elf_read(Elf *elf, void *buf, off_t offset, size_t bytes) {
int nbytes, toread = bytes;
if(!elf) {
return 0;
}
if(elf->e_curroffset != (elf->e_offset + offset)) {
if(lseek(elf->e_fd, elf->e_offset + offset, SEEK_SET) < 0) {
return -1;
}
elf->e_curroffset = elf->e_offset + offset;
}
while((nbytes = read(elf->e_fd, buf, toread)) > 0 && (toread -= nbytes)) {
buf = (char *)buf + nbytes;
}
elf->e_curroffset += bytes - toread;
return bytes - toread;
}
static int _elf_write(Elf *elf, void *buf, off_t offset, size_t bytes) {
int nbytes, towrite = bytes;
if(!elf) {
return 0;
}
if(elf->e_curroffset != (elf->e_offset + offset)) {
if(lseek(elf->e_fd, elf->e_offset + offset, SEEK_SET) < 0) {
return -1;
}
elf->e_curroffset = elf->e_offset + offset;
}
while((nbytes = write(elf->e_fd, buf, towrite)) > 0 && (towrite -= nbytes)) {
buf = (char *)buf + nbytes;
}
elf->e_curroffset += bytes - towrite;
return bytes - towrite;
}
static int _elf_fill(Elf *elf, off_t offset, size_t bytes) {
int nbytes, towrite = bytes;
if(!elf || (elf->e_flags & ELF_F_LAYOUT)) {
return 0;
}
if(lseek(elf->e_fd, elf->e_offset+offset, SEEK_SET) < 0) {
return -1;
}
while((nbytes = write(elf->e_fd, &_elf_fill_char, sizeof _elf_fill_char)) > 0 && nbytes < towrite) {
towrite -= nbytes;
}
return bytes - towrite;
}
static int _elf_is_archive(Elf *elf) {
char ident[SARMAG];
struct ar_hdr arhdr;
off_t pos;
off_t offset;
int sys5 = 0;
#if defined(__LITTLEENDIAN__ ) || defined(__X86__)
#define BSWAP32( buf ) ((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3])
#else
#define BSWAP32( buf ) ( * ( unsigned * )buf)
#endif
if(_elf_read(elf, ident, pos = 0, sizeof ident) != sizeof ident) {
return 0;
}
pos += sizeof ident;
if(memcmp(ident, ARMAG, SARMAG)) {
return 0;
}
offset = 0;
if(_elf_read(elf, &arhdr, pos, sizeof arhdr) == sizeof arhdr) {
pos += sizeof arhdr;
if(!memcmp(arhdr.ar_fmag, ARFMAG, sizeof arhdr.ar_fmag) && arhdr.ar_name[0] == '/' && arhdr.ar_name[1] == ' ') {
int size;
/* --- */
char unsigned buf[1024] = { 0 };
sys5 = 1;
/* 21.01.00, 14.08.00, leszek */
arhdr.ar_fmag[0] = '\0';
size = strtol(arhdr.ar_size, 0, 10); offset = size + sizeof arhdr ;
if(_elf_read(elf, buf, pos, 4) == 4) {
int strsize;
elf->e_numsyms = BSWAP32( buf );
if((strsize = size - elf->e_numsyms * 4) > 0) {
elf->e_numsyms++;
if(elf->e_arsymp = malloc(elf->e_numsyms * sizeof *elf->e_arsymp + strsize)) {
char *strp = (char *)(elf->e_arsymp + elf->e_numsyms);
if(_elf_read(elf, strp, pos + 4 * elf->e_numsyms, strsize) == strsize) {
int i;
Elf_Arsym *sym = elf->e_arsymp;
unsigned char *ptr = NULL;
int count = 0;
pos += 4;
for(i = 0; i < elf->e_numsyms - 1; i++) {
int len;
if(count == 0) {
_elf_read(elf, ptr = buf, pos, (count = min(sizeof buf / 4, elf->e_numsyms - i - 1)) * 4);
pos += sizeof buf;
}
len = strlen(strp) + 1;
if(len > strsize) {
// BAD SYMBOL TABLE
break;
}
sym->as_name = strp;
sym->as_hash = elf_hash(strp);
sym->as_off = BSWAP32( ptr );
ptr += 4;
strp += len;
strsize -= len;
sym++;
count--;
}
sym->as_name = 0;
sym->as_hash = ~0UL;
sym->as_off = 0;
}
}
}
}
}
}
elf->e_arstrings = -1;
/*
if( sys5 )
{
// side effect: memory leaking and we have pointer (e_arhdrp) to archive "//"
elf->e_offset = SARMAG + offset; elf_getarhdr( elf ); elf->e_arhdrp = 0;
}
*/
elf->e_offset = SARMAG;
return 1;
}
static Elf_Data *_elf_data_create(void) {
Elf_Data *data;
if((data = (Elf_Data *)malloc(sizeof *data))) {
memset(data, 0x00, sizeof *data);
data->d_type = ELF_T_BYTE;
data->d_version = _elf_version;
}
return data;
}
static int _elf_data_destroy(Elf_Data *data) {
return 0;
}
static Elf_Scn *_elf_scn_create(Elf_Scn *scn, Elf *elf, int ndx) {
Elf_Scn *newscn = scn;
if(!newscn) {
newscn = (Elf_Scn *)malloc(sizeof *newscn);
}
if(newscn) {
memset(newscn, 0x00, sizeof *newscn);
newscn->s_elf = elf;
newscn->s_ndx = ndx;
}
return newscn;
}
static int _elf_scn_destroy(Elf_Scn *scn) {
int i;
if(!scn) {
return -1;
}
for(i = 0; i < scn->s_fragments; i++) {
Elf_Data_Ref *dp = scn->s_data + i;
if(dp->d_flags & ELF_DATAREF_ALLOCED) {
if(dp->d_data->d_buf) {
free(dp->d_data->d_buf);
}
}
_elf_data_destroy(dp->d_data);
}
free(scn->s_data);
return 0;
}
static Elf *_elf_create(int fd) {
Elf *elf;
if(elf = malloc(sizeof *elf)) {
memset(elf, 0x00, sizeof *elf);
elf->e_fd = fd;
elf->e_refcnt = 1;
elf->e_curroffset = -1;
}
return elf;
}
static int _elf_destroy(Elf *elf) {
if(!elf || elf->e_refcnt > 0) {
return -1;
}
if(elf->e_rawfile) {
free(elf->e_rawfile);
}
if(elf->e_arhdrp) {
if(elf->e_arhdrp->ar_name) {
free(elf->e_arhdrp->ar_name);
}
if(elf->e_arhdrp->ar_rawname) {
free(elf->e_arhdrp->ar_rawname);
}
free(elf->e_arhdrp);
}
// only destroy the "//" name table if this *is* the archive elf, all
// others are just copies.
if(elf->e_archive == NULL) {
if (elf->e_arlntp) {
free (elf->e_arlntp);
}
}
if(elf->e_arsymp) {
free(elf->e_arsymp);
}
if(elf->e_phdrp) {
free(elf->e_phdrp);
}
if(elf->e_shdrp) {
free(elf->e_shdrp);
}
if(elf->e_ehdrp) {
if(elf->e_scnp) {
int i;
for(i = 0; i < elf->e_ehdrp->e_shnum; i++) {
if(elf->e_scnp[i]) {
_elf_scn_destroy(elf->e_scnp[i]);
}
}
free(elf->e_scnp);
}
free(elf->e_ehdrp);
}
return 0;
}
unsigned elf_version(unsigned ver) {
return (ver > _elf_version) ? EV_NONE : _elf_version;
}
Elf32_Ehdr *elf32_getehdr(Elf *elf) {
if(!elf) {
return 0;
}
if(!elf->e_ehdrp)
{
if(!(elf->e_ehdrp = (Elf32_Ehdr *)malloc(sizeof *elf->e_ehdrp))) {
return 0;
}
if(_elf_read(elf, (char *)elf->e_ehdrp, 0, sizeof *elf->e_ehdrp) != sizeof *elf->e_ehdrp) {
return 0;
}
if(memcmp(elf->e_ehdrp->e_ident, ELFMAG, SELFMAG)) {
return 0; /* for now */
} else {
; /* for now */
}
Elf32_swapEhdr( elf, 0 );
}
return elf->e_ehdrp;
}
Elf32_Ehdr *elf32_newehdr(Elf *elf) {
if(!elf) {
return 0;
}
if(!elf->e_ehdrp) {
Elf32_Ehdr *ehdr;
if(!(ehdr = (Elf32_Ehdr *)calloc(1, sizeof *elf->e_ehdrp))) {
return 0;
}
ehdr->e_ehsize = sizeof *ehdr;
ehdr->e_phentsize = sizeof(Elf32_Phdr);
ehdr->e_shentsize = sizeof(Elf32_Shdr);
memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
ehdr->e_ident[EI_CLASS] = ELFCLASS32;
ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
ehdr->e_ident[EI_VERSION] = _elf_version;
ehdr->e_version = _elf_version;
ehdr->e_machine = EM_386;
elf->e_ehdrp = ehdr;
elf->e_ehdr_flags |= ELF_F_DIRTY;
}
return elf->e_ehdrp;
}
char *elf_getident(Elf *elf, size_t *ptr) {
Elf32_Ehdr *ehdr;
if(!elf) {
return 0;
}
if(!(ehdr = elf->e_ehdrp)) {
if(!(ehdr = elf->e_ehdrp = elf32_getehdr(elf))) {
return 0;
}
}
if(ptr) {
*ptr = EI_NIDENT;
}
return ehdr->e_ident;
}
Elf *elf_begin(int fd, Elf_Cmd cmd, Elf *ref) {
Elf *elf = 0;
switch(cmd) {
case ELF_C_NULL: /* may be returned from elf_next for archive processing */
default:
return 0;
break;
case ELF_C_RDWR:
case ELF_C_READ:
if(!ref) {
if(!(elf = _elf_create(fd))) {
return 0;
}
if(!_elf_is_archive(elf)) {
char *e_ident;
size_t size;
//if(!(e_ident = elf_getident(elf, &size)) || size < EI_NIDENT || e_ident[EI_DATA] != ELFDATA2LSB)
if(!(e_ident = elf_getident(elf, &size)) || size < EI_NIDENT )
{
elf_end(elf);
return 0;
}
}
} else if(ref->e_arstrings) {
if(elf = _elf_create(fd)) {
Elf_Arhdr *arhdr;
elf->e_archive = ref;
elf->e_offset = ref->e_offset;
if(!(arhdr = elf_getarhdr(elf))) {
elf_end(elf);
return 0;
}
}
} else {
ref->e_refcnt++;
elf = ref;
}
if(elf) {
elf->e_cmd = cmd;
}
break;
case ELF_C_WRITE:
elf = _elf_create(fd);
elf->e_cmd = cmd;
elf->e_flags |= ELF_F_DIRTY;
break;
}
return elf;
}
off_t elf_update(Elf *elf, Elf_Cmd cmd) {
int dirty;
int do_layout;
size_t size = 0;
//printf( "\nelf_update()" ); flushall();
if(!elf) {
return 0;
}
do_layout = (elf->e_flags & ELF_F_LAYOUT) == 0;
if(cmd == ELF_C_NULL || cmd == ELF_C_WRITE) {
/*
* Step through the sections, updating sizes and file offsets
* - if any sections grow, mark the file as dirty -- FIXME ?
* - OR, just leave it to the application, if the programmer
* corrupts the file, its his responsibility
*/
off_t offset;
if(do_layout) {
int i;
off_t align_offset;
elf->e_ehdrp->e_phoff = sizeof *elf->e_ehdrp;
align_offset = offset = elf->e_ehdrp->e_phoff + elf->e_ehdrp->e_phnum * sizeof *elf->e_phdrp;
for(i = 0; i < elf->e_ehdrp->e_shnum; i++) {
int j;
Elf_Scn *scn = elf->e_scnp[i];
ulong_t align = 0;
ulong_t fill = 0;
off_t section_offset;
// Elf32_Shdr *shdr;
// this is hook to set proper alignment for empty *allocateable* section
//if( scn->s_fragments == 0 ) align = 4;
//shdr = elf32_getshdr( scn ); align = shdr->sh_addralign;
/* Get strictest fragment alignment */
for(j = 0; j < scn->s_fragments; j++) {
Elf_Data *data = scn->s_data[j].d_data;
if(data->d_align > align) {
align = data->d_align;
}
}
if(align) {
const int alignment = (align_offset & (align - 1));
fill = (align - alignment) & (align - 1);
} else {
fill = 0;
}
offset += fill;
/*
printf( "\nlibelf.c: section_offset=0x%x, align_offset=0x%x, offset=0x%x, align=0x%x",
section_offset, align_offset, offset, align );
flushall();
*/
align_offset += fill;
elf->e_shdrp[i].sh_offset = offset;
elf->e_shdrp[i].sh_addralign = align;
section_offset = 0;
/* Get section size */
for(j = 0; j < elf->e_scnp[i]->s_fragments; j++) {
Elf_Data *data = scn->s_data[j].d_data;
/* align each fragment (d_align has to be a power of 2) */
if(data->d_align) {
const int alignment = (section_offset & (data->d_align - 1));
fill = (data->d_align - alignment) & (data->d_align - 1);
} else {
fill = 0;
}
data->d_off = section_offset + fill;
section_offset = data->d_off + data->d_size;
}
elf->e_shdrp[i].sh_size = section_offset;
if(elf->e_shdrp[i].sh_type != SHT_NOBITS) {
offset += elf->e_shdrp[i].sh_size;
}
align_offset += elf->e_shdrp[i].sh_size;
}
elf->e_ehdrp->e_shoff = offset;
size = offset + elf->e_ehdrp->e_shnum * sizeof *elf->e_shdrp;
}
} else {
int i, sz;
size = elf->e_ehdrp->e_phoff + elf->e_ehdrp->e_phnum * sizeof *elf->e_phdrp;
sz = elf->e_ehdrp->e_shoff + elf->e_ehdrp->e_shnum * sizeof *elf->e_shdrp;
if(sz > size) {
size = sz;
}
for(i = 0; i < elf->e_ehdrp->e_shnum; i++) {
Elf32_Shdr *shdr = &elf->e_shdrp[i];
if(shdr->sh_type != SHT_NOBITS) {
if(shdr->sh_offset + shdr->sh_size > size) {
size = shdr->sh_offset + shdr->sh_size;
}
}
}
}
/*
* Write any sections that have changed
* -- it is the applications responsibility to ensure that the
* file integrity is maintained. If it performs an operation
* that will significantly alter the file layout, such as increasing
* the size of a section, it should mark the entire file as dirty.
*/
if(cmd == ELF_C_WRITE) {
int i;
off_t offset;
size_t size;
dirty = (elf->e_flags & ELF_F_DIRTY);
offset = 0;
if( elf->e_ehdrp->e_phnum == 0 )
{
// elf->e_ehdrp->e_phoff = 0;
}
/* --- */
if(dirty || (elf->e_ehdr_flags & ELF_F_DIRTY))
{
Elf32_Ehdr ehdr;
long numbytes;
ehdr = *elf->e_ehdrp;
Elf32_swapEhdr( elf, &ehdr );
numbytes =_elf_write(elf, (char *)&ehdr, 0, sizeof *elf->e_ehdrp);
//_elf_write(elf, (char *)elf->e_ehdrp, 0, sizeof *elf->e_ehdrp);
}
/* --- */
// elf->e_ehdrp->e_phoff = sizeof *elf->e_ehdrp;
/* --- */
size = elf->e_ehdrp->e_phnum * sizeof *elf->e_phdrp;
if(dirty || (elf->e_phdr_flags & ELF_F_DIRTY))
{
offset += sizeof *elf->e_ehdrp;
if(offset < elf->e_ehdrp->e_phoff) {
_elf_fill(elf, offset, elf->e_ehdrp->e_phoff - offset);
}
offset = elf->e_ehdrp->e_phoff;
Elf32_swapPhdr( elf );
_elf_write(elf, (char *)elf->e_phdrp, offset, size);
}
offset = elf->e_ehdrp->e_phoff + size;
for(i = 0; i < elf->e_ehdrp->e_shnum; i++) {
int j;
int sdirty = dirty;
Elf_Scn *scn = elf->e_scnp[i];
Elf32_Shdr *shdr = &elf->e_shdrp[i];
off_t d_off = 0;
if(scn->s_flags & ELF_F_DIRTY) {
dirty = 1;
}
if(dirty && offset < elf->e_shdrp[i].sh_offset) {
_elf_fill(elf, offset, elf->e_shdrp[i].sh_offset-offset);
}
offset = elf->e_shdrp[i].sh_offset;
for(j = 0; j < scn->s_fragments; j++) {
Elf_Data *data = scn->s_data[j].d_data;
if((dirty || (scn->s_data[j].d_flags & ELF_F_DIRTY)) && shdr->sh_type != SHT_NOBITS) {
if(data->d_off > d_off) {
_elf_fill(elf, offset+d_off, data->d_off-d_off);
}
_elf_write(elf, data->d_buf, elf->e_shdrp[i].sh_offset + data->d_off, data->d_size);
}
d_off = data->d_off + data->d_size;
}
if(shdr->sh_type != SHT_NOBITS) {
offset = offset + d_off;
}
dirty = sdirty;
}
if(dirty || (elf->e_shdr_flags & ELF_F_DIRTY)) {
offset = elf->e_ehdrp->e_shoff;
Elf32_swapShdr( elf, elf->e_shdrp, elf->e_ehdrp->e_shnum );
_elf_write(elf, (char *)elf->e_shdrp, offset, elf->e_ehdrp->e_shnum * sizeof *elf->e_shdrp);
}
}
return size;
}
Elf_Cmd elf_next(Elf *elf) {
if(elf->e_arhdrp) {
elf->e_archive->e_offset = (elf->e_offset + elf->e_arhdrp->ar_size + 1) & ~1;
return ELF_C_READ;
}
return ELF_C_NULL;
}
int elf_end(Elf *elf) {
if(!elf) {
return 0;
}
if(--elf->e_refcnt == 0) {
/* de-allocate data */
_elf_destroy(elf);
free(elf);
return 0;
}
return elf->e_refcnt;
}
char *elf_rawfile(Elf *elf, size_t *ptr) {
if(!elf) {
return 0;
}
if(!elf->e_rawfile) {
Elf_Arhdr *arhdr;
if(arhdr = elf_getarhdr(elf)) {
if(!(elf->e_rawfile = malloc(arhdr->ar_size))) {
return 0;
}
if(_elf_read(elf, elf->e_rawfile, 0, arhdr->ar_size) != arhdr->ar_size) {
free(elf->e_rawfile);
elf->e_rawfile = 0;
return 0;
}
if(ptr) {
*ptr = arhdr->ar_size;
}
}
}
return elf->e_rawfile;
}
Elf_Arhdr *elf_getarhdr(Elf *elf) {
if(!elf) {
return 0;
}
//printf( "\nelf_getarhdr() %d", elf ? elf->e_offset : 0 ); flushall();
if(!elf->e_arhdrp) {
struct ar_hdr ar_hdr;
Elf_Arhdr *arhdr;
int i = 0;
int skip = 0; // was uninitialized!
char *lntrans;
//printf( "read" ); flushall();
if(_elf_read(elf, &ar_hdr, 0, sizeof ar_hdr) != sizeof ar_hdr) {
//printf( "ret 1" ); flushall();
return 0;
}
if(memcmp(ar_hdr.ar_fmag, ARFMAG, sizeof ar_hdr.ar_fmag)) {
//printf( "ret 2" ); flushall();
return 0;
}
if(!(arhdr = (Elf_Arhdr *)malloc(sizeof *arhdr))) {
//printf( "ret 3" ); flushall();
return 0;
}
// handle long names via long name table in elf->arlntp
lntrans = NULL;
if(ar_hdr.ar_name [0] == '/') {
if (ar_hdr.ar_name [1] == '/') {
int size;
//printf( "1" ); flushall();
size = strtol(ar_hdr.ar_size, 0, 10);
// handle "//" long name table here.
if(elf->e_arlntp = malloc(size)) {
if(_elf_read(elf, elf->e_arlntp, sizeof ar_hdr, size) == size) {
int i;
elf->e_arlntsize = size;
// convert '/' terminated names to '\0' terminated
for(i = 0; i < size; i++) {
if(elf->e_arlntp[i] == '/') {
elf->e_arlntp[i] = 0;
}
}
// propagate to archive member, if it exists, as this elf is temporary
if (elf->e_archive) {
elf->e_archive->e_arlntp = elf->e_arlntp;
elf->e_archive->e_arlntsize = elf->e_arlntsize;
}
}
}
} else if (isdigit (ar_hdr.ar_name [1])) {
//printf( "2" ); flushall();
if(elf->e_archive && elf->e_archive->e_arlntp != NULL) {
//printf( "3" ); flushall();
i = atoi(&ar_hdr.ar_name[1]);
if (i <= elf->e_archive->e_arlntsize) {
lntrans = elf->e_archive->e_arlntp + i;
i = strlen (lntrans);
skip = 1;
}
}
}
}
if(!skip && (memcmp(ar_hdr.ar_name, "#1/", 3) || (skip = i = strtol(&ar_hdr.ar_name[3], 0, 10)) == 0)) {
//printf( "4" ); flushall();
for(i = sizeof ar_hdr.ar_name - 1; i && ar_hdr.ar_name[i] == ' '; i--);
if(ar_hdr.ar_name[i] == '/') {
if(i > 1 || (i == 1 && ar_hdr.ar_name[0] != '/')) {
i--;
}
}
i++;
skip = 0;
}
if(!(arhdr->ar_name = malloc(i + 1))) {
free(arhdr);
return 0;
}
if(!(arhdr->ar_rawname = malloc(1 + sizeof ar_hdr.ar_name))) {
free(arhdr->ar_name);
free(arhdr);
return 0;
}
/*
printf( "\nar_hdr.ar_name=%s", ar_hdr.ar_name );
printf( "\nskip=%d, %s", skip, lntrans ? lntrans : "" ); flushall();
*/
if(skip) {
if(lntrans) {
strcpy (arhdr->ar_name, lntrans);
skip = 0; // also used as size, so kill it
} else {
if(_elf_read(elf, arhdr->ar_name, sizeof ar_hdr, i) != i) {
free(arhdr->ar_rawname);
free(arhdr->ar_name);
free(arhdr);
return 0;
}
}
} else {
memcpy(arhdr->ar_name, ar_hdr.ar_name, i);
}
arhdr->ar_name[i] = '\0';
memcpy(arhdr->ar_rawname, ar_hdr.ar_name, sizeof ar_hdr.ar_name);
arhdr->ar_rawname[sizeof ar_hdr.ar_name] = '\0';
ar_hdr.ar_fmag[0] = '\0';
arhdr->ar_size = strtol(ar_hdr.ar_size, 0, 10) - skip;
ar_hdr.ar_size[0] = '\0';
arhdr->ar_mode = strtol(ar_hdr.ar_mode, 0, 8);
ar_hdr.ar_mode[0] = '\0';
arhdr->ar_gid = strtol(ar_hdr.ar_gid, 0, 10);
ar_hdr.ar_gid[0] = '\0';
arhdr->ar_uid = strtol(ar_hdr.ar_uid, 0, 10);
ar_hdr.ar_uid[0] = '\0';
arhdr->ar_date = strtol(ar_hdr.ar_date, 0, 10);
elf->e_arhdrp = arhdr;
elf->e_offset += sizeof ar_hdr + skip;
}
return elf->e_arhdrp;
}
Elf_Arsym *elf_getarsym(Elf *elf, size_t *ptr) {
if(elf && elf->e_arsymp) {
if(ptr) {
*ptr = elf->e_numsyms;
}
return elf->e_arsymp;
}
return 0;
}
off_t elf_getbase(Elf *elf) {
return elf->e_offset;
}
Elf32_Phdr *elf32_getphdr(Elf *elf) {
Elf32_Phdr *phdr = 0;
if(!elf || (!elf->e_ehdrp && !elf32_getehdr(elf))) {
return 0;
}
if(elf->e_ehdrp->e_phnum) {
int size = elf->e_ehdrp->e_phentsize * elf->e_ehdrp->e_phnum;
if(elf->e_ehdrp->e_phentsize == sizeof *phdr) {
int n;
phdr = (Elf32_Phdr *)malloc(size);
if(phdr && (n = _elf_read(elf, (char *)phdr, elf->e_ehdrp->e_phoff, size)) == size) {
elf->e_phdrp = phdr;
} else {
free(phdr);
phdr = 0;
}
}
}
return phdr;
}
Elf32_Phdr *elf32_newphdr(Elf *elf, size_t count) {
Elf32_Phdr *phdr = 0;
if(!elf || (!elf->e_ehdrp && !elf32_getehdr(elf))) {
return 0;
}
if(count) {
int size = elf->e_ehdrp->e_phentsize * count;
if(elf->e_ehdrp->e_phentsize == sizeof *phdr) {
phdr = (Elf32_Phdr *)calloc(1, size);
if(phdr) {
elf->e_phdrp = phdr;
elf->e_ehdrp->e_phnum = count;
elf->e_ehdr_flags |= ELF_F_DIRTY;
elf->e_phdr_flags |= ELF_F_DIRTY;
}
}
} else {
elf->e_ehdrp->e_phnum = count;
elf->e_ehdr_flags |= ELF_F_DIRTY;
elf->e_phdr_flags |= ELF_F_DIRTY;
}
return phdr;
}
static Elf32_Shdr *_elf32_get_sections(Elf *elf) {
Elf32_Shdr *shdr = 0;
Elf_Scn *scn_tbl;
Elf_Scn **scnp;
int size = elf->e_ehdrp->e_shentsize * elf->e_ehdrp->e_shnum;
int scnp_size = elf->e_ehdrp->e_shnum * sizeof *scnp;
int scn_tbl_size = elf->e_ehdrp->e_shnum * sizeof *scn_tbl;
if(elf->e_ehdrp->e_shentsize == sizeof *shdr) {
int i, n;
if(!(shdr = (Elf32_Shdr *)malloc(size))) {
} else if(!(scn_tbl = (Elf_Scn *)malloc(scn_tbl_size))) {
free(shdr);
shdr = 0;
} else if(!(scnp = (Elf_Scn **)malloc(scnp_size))) {
free(shdr);
free(scn_tbl);
shdr = 0;
} else if((n = _elf_read(elf, (char *)shdr, elf->e_ehdrp->e_shoff, size)) == size) {
elf->e_shdrp = shdr;
elf->e_scnp = scnp;
Elf32_swapShdr( elf, shdr, elf->e_ehdrp->e_shnum );
for(i = 0; i < elf->e_ehdrp->e_shnum; i++) {
elf->e_scnp[i] = &scn_tbl[i];
_elf_scn_create(&scn_tbl[i], elf, i);
}
} else {
free(shdr);
free(scn_tbl);
free(scnp);
shdr = 0;
}
}
return shdr;
}
Elf32_Shdr *elf32_getshdr(Elf_Scn *scn) {
return &scn->s_elf->e_shdrp[scn->s_ndx];
}
Elf_Scn *elf_getscn(Elf *elf, size_t index) {
if(!elf || (!elf->e_ehdrp && !elf32_getehdr(elf))) {
return 0;
}
if(!elf->e_shdrp && elf->e_ehdrp->e_shnum) {
if(!_elf32_get_sections(elf)) {
return 0;
}
}
if(index >= elf->e_ehdrp->e_shnum) {
return 0;
}
return elf->e_scnp[index];
}
size_t elf_ndxscn(Elf_Scn *scn) {
return scn->s_ndx;
}
Elf_Scn *elf_newscn(Elf *elf) {
int first = 0;
Elf_Scn *scn[2];
Elf_Scn **scnp;
int size;
if(!elf || (!elf->e_ehdrp && !elf32_getehdr(elf))) {
return 0;
}
if(!elf->e_shdrp && elf->e_ehdrp->e_shnum) {
if(!_elf32_get_sections(elf)) {
return 0;
}
}
if(!elf->e_shdrp) {
scn[first] = _elf_scn_create(0, elf, elf->e_ehdrp->e_shnum);
if(!scn[first]) {
return 0;
}
first++;
}
scn[first] = _elf_scn_create(0, elf, elf->e_ehdrp->e_shnum + first);
if(!scn[first]) {
if(first > 0) {
free(scn[first]);
}
return 0;
}
size = (elf->e_ehdrp->e_shnum + first + 1) * sizeof *scnp;
if((scnp = (Elf_Scn **)realloc(elf->e_scnp, size))) {
int shdrsz = (elf->e_ehdrp->e_shnum + first + 1) * sizeof(Elf32_Shdr);
Elf32_Shdr *shdrs = (Elf32_Shdr *)realloc(elf->e_shdrp, shdrsz);
int i;
if(shdrs) {
elf->e_shdrp = shdrs;
memset(&shdrs[elf->e_ehdrp->e_shnum], 0x00, (first + 1) * sizeof *shdrs);
for(i = 0; i <= first; i++) {
scnp[scn[i]->s_ndx] = scn[i];
}
elf->e_scnp = scnp;
elf->e_ehdrp->e_shnum += first + 1;
} else {
for(i = 0; i <= first; i++) {
free(scn[i]);
}
return 0;
}
} else {
int i;
for(i = 0; i <= first; i++) {
free(scn[i]);
}
}
return scnp ? scnp[scn[first]->s_ndx] : 0;
}
Elf_Scn *elf_nextscn(Elf *elf, Elf_Scn *scn) {
Elf_Scn *nextscn = 0;
if(!elf) {
return 0;
}
if(!scn) {
if(!elf->e_ehdrp && !elf32_getehdr(elf)) {
return 0;
}
#if 1
/*
This is what the code originally looked like in sfurr's code.
It got changed to what's in the #else, but that now messes
up omf2elf because it comes in here looking for ".text" with
e_shdrp == NULL and e_shnum == 0. You end up indirecting on
a NULL pointer at the "nextscn = elf->e_scnp[1];" statement.
I'll have to talk to rk when he gets back from Italy.
bstecher 97/05/12
*/
if( elf->e_shdrp == NULL ) {
if( elf->e_ehdrp->e_shnum == 0 ||
_elf32_get_sections(elf) == NULL ) return NULL;
}
#else
if(!elf->e_shdrp && elf->e_ehdrp->e_shnum) {
if(!_elf32_get_sections(elf) || !elf->e_ehdrp->e_shnum) {
return 0;
}
}
#endif
nextscn = elf->e_scnp[1];
} else if(scn->s_ndx < (elf->e_ehdrp->e_shnum - 1)) {
nextscn = elf->e_scnp[scn->s_ndx + 1];
}
return nextscn;
}
static Elf_Data *_elf32_newdata(Elf_Scn *scn, size_t size) {
Elf_Data *dblock = 0;
void *data;
if(!scn) {
return 0;
}
if(size) {
if(!(data = malloc(size))) {
return 0;
}
} else {
data = 0;
}
if((dblock = _elf_data_create())) {
Elf_Data_Ref *datap;
dblock->d_buf = data;
dblock->d_size = size;
if((datap = (Elf_Data_Ref *)realloc(scn->s_data, (scn->s_fragments + 1) * sizeof *datap))) {
datap[scn->s_fragments].d_data = dblock;
datap[scn->s_fragments].d_flags = size ? ELF_DATAREF_ALLOCED : 0;
scn->s_fragments++;
scn->s_data = datap;
}
}
return dblock;
}
Elf_Data *elf_getdata(Elf_Scn *scn, Elf_Data *data) {
Elf_Data *dp = 0;
Elf *elf;
if(!scn) {
return 0;
}
elf = scn->s_elf;
if(!data) {
if(scn->s_fragments == 0) {
Elf32_Ehdr *ehdr;
Elf32_Shdr shdr;
if ((ehdr = elf->e_ehdrp) == NULL) {
ehdr = elf->e_ehdrp = elf32_getehdr (elf);
}
if(ehdr) {
if(scn->s_ndx < ehdr -> e_shnum && ehdr -> e_shentsize >= sizeof shdr &&
_elf_read(scn->s_elf, (char *)&shdr, ehdr -> e_shoff + scn->s_ndx * ehdr -> e_shentsize, sizeof shdr) == sizeof shdr &&
shdr.sh_size > 0) {
Elf32_swapShdr( elf, &shdr, 1 );
#if 0
if(dp = _elf32_newdata(scn, shdr.sh_size)) {
_elf_read(scn->s_elf, dp->d_buf, shdr.sh_offset, shdr.sh_size);
#else
if(dp = _elf32_newdata(scn, shdr.sh_type == SHT_NOBITS ? 0 : shdr.sh_size)) {
if(shdr.sh_type == SHT_NOBITS) {
dp->d_size = shdr.sh_size;
} else {
_elf_read(scn->s_elf, dp->d_buf, shdr.sh_offset, shdr.sh_size);
}
#endif
switch(shdr.sh_type) {
case SHT_DYNAMIC:
dp->d_type = ELF_T_DYN;
break;
case SHT_DYNSYM:
case SHT_SYMTAB:
Elf32_swapSym ( elf, dp->d_buf, shdr.sh_size );
dp->d_type = ELF_T_SYM;
break;
case SHT_HASH:
dp->d_type = ELF_T_WORD;
break;
case SHT_REL:
Elf32_swapRela( elf, dp->d_buf, shdr.sh_size, 0 );
dp->d_type = ELF_T_REL;
break;
case SHT_RELA:
dp->d_type = ELF_T_RELA;
Elf32_swapRela( elf, dp->d_buf, shdr.sh_size, 1 );
break;
default:
dp->d_type = ELF_T_BYTE;
break;
}
}
dp->d_align = shdr.sh_addralign;
}
}
} else if(scn->s_fragments > 0 && scn->s_data) {
dp = scn->s_data[0].d_data;
}
} else {
int i;
for(i = 0; i < scn->s_fragments; i++) {
if(scn->s_data[i].d_data == data) {
if(i+1 < scn->s_fragments) {
dp = scn->s_data[i+1].d_data;
}
break;
}
}
}
return dp;
}
Elf_Data *elf_newdata(Elf_Scn *scn) {
Elf_Data *dblock = 0;
if(!scn) {
return 0;
}
if((dblock = _elf32_newdata(scn, 0))) {
scn->s_data[scn->s_fragments-1].d_flags |= ELF_F_DIRTY;
}
return dblock;
}
Elf_Data *elf_rawdata(Elf_Scn *scn, Elf_Data *data) {
return 0;
}
size_t elf32_fsize(Elf_Type type, size_t size, unsigned ver) {
if(ver != EV_CURRENT) {
return 0;
}
switch(type) {
case ELF_T_BYTE:
size *= sizeof(unsigned char);
break;
case ELF_T_ADDR:
size *= sizeof(Elf32_Addr);
break;
case ELF_T_HALF:
size *= sizeof(Elf32_Half);
break;
case ELF_T_OFF:
size *= sizeof(Elf32_Off);
break;
case ELF_T_SWORD:
size *= sizeof(Elf32_Sword);
break;
case ELF_T_WORD:
size *= sizeof(Elf32_Word);
break;
case ELF_T_EHDR:
size *= sizeof(Elf32_Ehdr);
break;
case ELF_T_PHDR:
size *= sizeof(Elf32_Phdr);
break;
case ELF_T_RELA:
size *= sizeof(Elf32_Rela);
break;
case ELF_T_REL:
size *= sizeof(Elf32_Rel);
break;
case ELF_T_SHDR:
size *= sizeof(Elf32_Shdr);
break;
case ELF_T_SYM:
size *= sizeof(Elf32_Sym);
break;
case ELF_T_DYN:
size *= sizeof(Elf32_Dyn);
break;
default:
return 0;
}
return size;
}
Elf_Kind elf_kind(Elf *elf) {
if(!elf) {
return ELF_K_NONE;
}
if(elf->e_arstrings) {
return ELF_K_AR;
}
if(elf->e_ehdrp) {
return ELF_K_ELF;
}
return ELF_K_NONE;
}
size_t elf_rand(Elf *elf, size_t offset) {
return elf->e_offset = offset;
}
void elf_fill(int fill) {
_elf_fill_char = fill;
}
unsigned long elf_hash(const char *name) {
unsigned long h = 0, g;
while(*name) {
h = (h << 4) + *name++;
if(g = h & 0xf0000000) {
h ^= g >> 24;
}
h &= ~g;
}
return h;
}
char *elf_strptr(Elf *elf, size_t section, size_t offset) {
Elf_Scn *scn;
if(scn = elf_getscn(elf, section)) {
Elf32_Shdr *shdr;
if((shdr = elf32_getshdr(scn)) && shdr->sh_type == SHT_STRTAB) {
Elf_Data *data;
if((data = elf_getdata(scn, 0)) && data->d_type == ELF_T_BYTE) {
char *p = data->d_buf;
return p + offset;
}
}
}
return 0;
}