mirror of https://github.com/deadw00d/AROS.git
1196 lines
32 KiB
C
1196 lines
32 KiB
C
/*
|
|
Copyright © 1995-2020, The AROS Development Team. All rights reserved.
|
|
$Id$
|
|
*/
|
|
|
|
#define PROTOTYPES
|
|
#define HAVE_STDARG_H
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <dirent.h>
|
|
|
|
#include <libgen.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#define F_VERBOSE (1 << 0)
|
|
#define F_NOCONVERT (1 << 1)
|
|
|
|
#if defined(__GNUC__)&&defined(WIN32)
|
|
#include <winsock2.h>
|
|
#define mkdir(path, mode) mkdir(path)
|
|
#else
|
|
#include <arpa/inet.h>
|
|
typedef uint32_t ULONG;
|
|
typedef int BOOL;
|
|
#endif
|
|
typedef uint8_t UBYTE;
|
|
typedef uint16_t UWORD;
|
|
typedef uint32_t IPTR;
|
|
typedef int32_t SIPTR;
|
|
typedef char * STRPTR;
|
|
|
|
#if !defined(FALSE)&&!defined(TRUE)
|
|
#define FALSE 0
|
|
#define TRUE (!FALSE)
|
|
#endif
|
|
|
|
/* Memory allocation flags */
|
|
#define MEMF_ANY 0L
|
|
#define MEMF_PUBLIC (1L<<0)
|
|
#define MEMF_CHIP (1L<<1)
|
|
#define MEMF_FAST (1L<<2)
|
|
#define MEMF_EXECUTABLE (1L<<4) /* AmigaOS v4 compatible */
|
|
#define MEMF_LOCAL (1L<<8)
|
|
#define MEMF_24BITDMA (1L<<9)
|
|
#define MEMF_KICK (1L<<10)
|
|
#define MEMF_31BIT (1L<<12) /* Low address space (<2GB). Effective only on 64 bit machines. */
|
|
#define MEMF_CLEAR (1L<<16) /* Explicitly clear memory after allocation */
|
|
#define MEMF_LARGEST (1L<<17)
|
|
#define MEMF_REVERSE (1L<<18)
|
|
#define MEMF_TOTAL (1L<<19)
|
|
#define MEMF_HWALIGNED (1L<<20) /* For AllocMem() - align address and size to physical page boundary */
|
|
#define MEMF_SEM_PROTECTED (1L<<20) /* For CreatePool() - add semaphore protection to the pool */
|
|
#define MEMF_NO_EXPUNGE (1L<<31)
|
|
|
|
#define HUNKF_ADVISORY (1L<<29)
|
|
#define HUNKF_CHIP (1L<<30)
|
|
#define HUNKF_FAST (1L<<31)
|
|
#define HUNKF_MEMFLAGS (HUNKF_CHIP | HUNKF_FAST)
|
|
|
|
|
|
#define HUNK_CODE 1001
|
|
#define HUNK_DATA 1002
|
|
#define HUNK_BSS 1003
|
|
#define HUNK_RELOC32 1004
|
|
#define HUNK_SYMBOL 1008
|
|
#define HUNK_END 1010
|
|
#define HUNK_HEADER 1011
|
|
#define HUNK_RELRELOC32 1021
|
|
|
|
#define SHT_PROGBITS 1
|
|
#define SHT_SYMTAB 2
|
|
#define SHT_STRTAB 3
|
|
#define SHT_RELA 4
|
|
#define SHT_NOBITS 8
|
|
#define SHT_REL 9
|
|
#define SHT_SYMTAB_SHNDX 18
|
|
|
|
#define ET_REL 1
|
|
#define ET_EXEC 2
|
|
|
|
#define EM_386 3
|
|
#define EM_68K 4
|
|
#define EM_PPC 20
|
|
#define EM_ARM 40
|
|
#define EM_X86_64 62 /* AMD x86-64 */
|
|
|
|
#define R_386_NONE 0
|
|
#define R_386_32 1
|
|
#define R_386_PC32 2
|
|
|
|
/* AMD x86-64 relocations. */
|
|
#define R_X86_64_NONE 0 /* No reloc */
|
|
#define R_X86_64_64 1 /* Direct 64 bit */
|
|
#define R_X86_64_PC32 2 /* PC relative 32 bit signed */
|
|
#define R_X86_64_32 10
|
|
#define R_X86_64_32S 11
|
|
|
|
#define R_68k_NONE 0
|
|
#define R_68K_32 1
|
|
#define R_68K_PC32 4
|
|
#define R_68K_PC16 5
|
|
|
|
#define R_PPC_NONE 0
|
|
#define R_PPC_ADDR32 1
|
|
#define R_PPC_ADDR16_LO 4
|
|
#define R_PPC_ADDR16_HA 6
|
|
#define R_PPC_REL24 10
|
|
#define R_PPC_REL32 26
|
|
#define R_PPC_REL16_LO 250
|
|
#define R_PPC_REL16_HA 252
|
|
|
|
#define R_ARM_NONE 0
|
|
#define R_ARM_PC24 1
|
|
#define R_ARM_ABS32 2
|
|
#define R_ARM_CALL 28
|
|
#define R_ARM_JUMP24 29
|
|
#define R_ARM_V4BX 40
|
|
#define R_ARM_PREL31 42
|
|
#define R_ARM_MOVW_ABS_NC 43
|
|
#define R_ARM_MOVT_ABS 44
|
|
|
|
#define STT_NOTYPE 0
|
|
#define STT_OBJECT 1
|
|
#define STT_FUNC 2
|
|
#define STT_SECTION 3
|
|
#define STT_FILE 4
|
|
#define STT_LOPROC 13
|
|
#define STT_HIPROC 15
|
|
|
|
#define SHN_UNDEF 0
|
|
#define SHN_LORESERVE 0xff00
|
|
#define SHN_ABS 0xfff1
|
|
#define SHN_COMMON 0xfff2
|
|
#define SHN_XINDEX 0xffff
|
|
#define SHN_HIRESERVE 0xffff
|
|
|
|
#define SHF_WRITE (1 << 0)
|
|
#define SHF_ALLOC (1 << 1)
|
|
#define SHF_EXECINSTR (1 << 2)
|
|
|
|
#define ELF_ST_TYPE(i) ((i) & 0x0F)
|
|
|
|
#define EI_VERSION 6
|
|
#define EV_CURRENT 1
|
|
|
|
#define EI_DATA 5
|
|
#define ELFDATA2LSB 1
|
|
#define ELFDATA2MSB 2
|
|
|
|
#define EI_CLASS 4
|
|
#define ELFCLASS32 1
|
|
#define ELFCLASS64 2 /* 64-bit objects */
|
|
|
|
#define EI_OSABI 7
|
|
#define EI_ABIVERSION 8
|
|
|
|
#define ELFOSABI_AROS 15
|
|
|
|
#define PF_X (1 << 0)
|
|
|
|
struct elfheader
|
|
{
|
|
UBYTE ident[16];
|
|
UWORD type;
|
|
UWORD machine;
|
|
ULONG version;
|
|
IPTR entry;
|
|
IPTR phoff;
|
|
IPTR shoff;
|
|
ULONG flags;
|
|
UWORD ehsize;
|
|
UWORD phentsize;
|
|
UWORD phnum;
|
|
UWORD shentsize;
|
|
UWORD shnum;
|
|
UWORD shstrndx;
|
|
} __attribute__((packed));
|
|
|
|
struct sheader
|
|
{
|
|
ULONG name;
|
|
ULONG type;
|
|
IPTR flags;
|
|
IPTR addr;
|
|
IPTR offset;
|
|
IPTR size;
|
|
ULONG link;
|
|
ULONG info;
|
|
IPTR addralign;
|
|
IPTR entsize;
|
|
} __attribute__((packed));
|
|
|
|
#define PT_LOAD 1
|
|
|
|
struct pheader
|
|
{
|
|
ULONG type;
|
|
ULONG offset;
|
|
IPTR vaddr;
|
|
IPTR paddr;
|
|
ULONG filesz;
|
|
ULONG memsz;
|
|
ULONG flags;
|
|
ULONG align;
|
|
} __attribute__((packed));
|
|
|
|
struct symbol
|
|
{
|
|
ULONG name; /* Offset of the name string in the string table */
|
|
IPTR value; /* Varies; eg. the offset of the symbol in its hunk */
|
|
IPTR size; /* How much memory does the symbol occupy */
|
|
UBYTE info; /* What kind of symbol is this ? (global, variable, etc) */
|
|
UBYTE other; /* undefined */
|
|
UWORD shindex; /* In which section is the symbol defined ? */
|
|
} __attribute__((packed));
|
|
|
|
#define ELF_R_SYM(val) ((val) >> 8)
|
|
#define ELF_R_TYPE(val) ((val) & 0xff)
|
|
#define ELF_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff))
|
|
|
|
struct relo
|
|
{
|
|
IPTR offset; /* Address of the relocation relative to the section it refers to */
|
|
IPTR info; /* Type of the relocation */
|
|
SIPTR addend; /* Constant addend used to compute value */
|
|
};
|
|
|
|
/* Note: the conversion below is not in line with ELF specification and is fixed in GNU binutils since 2008
|
|
* See: https://sourceware.org/bugzilla/show_bug.cgi?id=5900
|
|
*/
|
|
/* convert section header number to array index */
|
|
/*#define SHINDEX(n) \
|
|
((n) < SHN_LORESERVE ? (n) : ((n) <= SHN_HIRESERVE ? 0 : (n) - (SHN_HIRESERVE + 1 - SHN_LORESERVE)))*/
|
|
|
|
/* convert section header array index to section number */
|
|
/*#define SHNUM(i) \
|
|
((i) < SHN_LORESERVE ? (i) : (i) + (SHN_HIRESERVE + 1 - SHN_LORESERVE))*/
|
|
|
|
/* m68k Machine's native values */
|
|
#define AROS_ELF_CLASS ELFCLASS32
|
|
#define AROS_ELF_DATA ELFDATA2MSB
|
|
#define AROS_ELF_MACHINE EM_68K
|
|
#define AROS_ELF_REL SHT_RELA
|
|
|
|
|
|
#if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
|
|
# include <stdarg.h>
|
|
# define VA_START(args, lastarg) va_start(args, lastarg)
|
|
#else
|
|
# include <varargs.h>
|
|
# define VA_START(args, lastarg) va_start(args)
|
|
#endif
|
|
|
|
#ifdef PROTOTYPES
|
|
# define PARAMS(x) x
|
|
#else
|
|
# define PARAMS(x) ()
|
|
#endif /* PROTOTYPES */
|
|
|
|
static void set_error(int err)
|
|
{
|
|
errno = err;
|
|
}
|
|
|
|
#if defined(DEBUG) && DEBUG
|
|
#define D(x) x
|
|
#define DB2(x) x
|
|
#else
|
|
#define D(x)
|
|
#define DB2(x)
|
|
#endif
|
|
#define bug(fmt,args...) fprintf(stderr, fmt ,##args )
|
|
|
|
static int must_swap = -1;
|
|
static int flags = 0;
|
|
|
|
static void eh_fixup(struct elfheader *eh)
|
|
{
|
|
/* Endian swaps */
|
|
if (eh->type == 256) {
|
|
must_swap = 1;
|
|
eh->type = ntohs(eh->type);
|
|
eh->machine = ntohs(eh->machine);
|
|
eh->version = ntohl(eh->version);
|
|
eh->entry = ntohl(eh->entry);
|
|
eh->phoff = ntohl(eh->phoff);
|
|
eh->shoff = ntohl(eh->shoff);
|
|
eh->flags = ntohl(eh->flags);
|
|
eh->ehsize = ntohs(eh->ehsize);
|
|
eh->phentsize = ntohs(eh->phentsize);
|
|
eh->phnum = ntohs(eh->phnum);
|
|
eh->shentsize = ntohs(eh->shentsize);
|
|
eh->shnum = ntohs(eh->shnum);
|
|
eh->shstrndx = ntohs(eh->shstrndx);
|
|
} else {
|
|
must_swap = 0;
|
|
}
|
|
}
|
|
|
|
static void sh_fixup(struct sheader *sh, int n)
|
|
{
|
|
if (must_swap == 0)
|
|
return;
|
|
|
|
for (; n > 0; n--, sh++) {
|
|
sh->name = ntohl(sh->name);
|
|
sh->type = ntohl(sh->type);
|
|
sh->flags = ntohl(sh->flags);
|
|
sh->addr = ntohl(sh->addr);
|
|
sh->offset = ntohl(sh->offset);
|
|
sh->size = ntohl(sh->size);
|
|
sh->link = ntohl(sh->link);
|
|
sh->info = ntohl(sh->info);
|
|
sh->addralign = ntohl(sh->addralign);
|
|
sh->entsize = ntohl(sh->entsize);
|
|
}
|
|
}
|
|
|
|
static void rel_fixup(struct relo *rel)
|
|
{
|
|
if (must_swap == 0)
|
|
return;
|
|
|
|
rel->offset = ntohl(rel->offset);
|
|
rel->info = ntohl(rel->info);
|
|
rel->addend = ntohl(rel->addend);
|
|
}
|
|
|
|
void sym_fixup(struct symbol *sym)
|
|
{
|
|
if (must_swap == 0)
|
|
return;
|
|
|
|
sym->name = ntohl(sym->name);
|
|
sym->value = ntohl(sym->value);
|
|
sym->size = ntohl(sym->size);
|
|
sym->shindex = ntohs(sym->shindex);
|
|
}
|
|
|
|
static void *load_block (int file,ULONG offset,ULONG size)
|
|
{
|
|
ULONG lsize = (size + sizeof(ULONG) - 1) / sizeof(ULONG);
|
|
D(bug("[ELF2HUNK] Load Block\n"));
|
|
D(bug("[ELF2HUNK] (size=%d)\n",(int)size));
|
|
void *block = malloc(lsize * sizeof(ULONG));
|
|
if (block) {
|
|
lseek(file, offset, SEEK_SET);
|
|
if (read(file, block, size) == size) {
|
|
return block;
|
|
}
|
|
|
|
free(block);
|
|
set_error(EIO);
|
|
} else
|
|
set_error(ENOMEM);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static ULONG read_shnum(int file, struct elfheader *eh)
|
|
{
|
|
ULONG shnum = eh->shnum;
|
|
|
|
/* the ELF header only uses 16 bits to store the count of section headers,
|
|
* so it can't handle more than 65535 headers. if the count is 0, and an
|
|
* offset is defined, then the real count can be found in the first
|
|
* section header (which always exists).
|
|
*
|
|
* similarly, if the string table index is SHN_XINDEX, then the actual
|
|
* index is found in the first section header also.
|
|
*
|
|
* see the System V ABI 2001-04-24 draft for more details.
|
|
*/
|
|
if (eh->shnum == 0)
|
|
{
|
|
struct sheader sh;
|
|
|
|
if (eh->shoff == 0) {
|
|
set_error(ENOEXEC);
|
|
return 0;
|
|
}
|
|
|
|
lseek(file, eh->shoff, SEEK_SET);
|
|
if (read(file, &sh, sizeof(sh)) != sizeof(sh))
|
|
return 0;
|
|
|
|
sh_fixup(&sh, 1);
|
|
|
|
/* wider section header count is in the size field */
|
|
shnum = sh.size;
|
|
|
|
/* sanity, if they're still invalid then this isn't elf */
|
|
if (shnum == 0)
|
|
set_error(ENOEXEC);
|
|
}
|
|
|
|
return shnum;
|
|
}
|
|
|
|
static int load_header(int file, struct elfheader *eh)
|
|
{
|
|
lseek(file, 0, SEEK_SET);
|
|
if (read(file, eh, sizeof(struct elfheader)) != sizeof(struct elfheader)) {
|
|
D(bug("[ELF2HUNK] Can't read the %d byte ELF header\n", (int)sizeof(struct elfheader)));
|
|
return 0;
|
|
}
|
|
|
|
eh_fixup(eh);
|
|
|
|
if (eh->ident[0] != 0x7f || eh->ident[1] != 'E' ||
|
|
eh->ident[2] != 'L' || eh->ident[3] != 'F') {
|
|
D(bug("[ELF2HUNK] Not an ELF object\n"));
|
|
return 0;
|
|
}
|
|
D(bug("[ELF2HUNK] ELF object\n"));
|
|
|
|
/* WANT_CLASS should be defined for your target */
|
|
if (eh->ident[EI_CLASS] != AROS_ELF_CLASS ||
|
|
eh->ident[EI_VERSION] != EV_CURRENT ||
|
|
eh->type != ET_REL ||
|
|
eh->ident[EI_DATA] != AROS_ELF_DATA ||
|
|
eh->machine != AROS_ELF_MACHINE)
|
|
{
|
|
D(bug("[ELF2HUNK] Object is of wrong type\n"));
|
|
D(bug("[ELF2HUNK] EI_CLASS is %d - should be %d\n", eh->ident[EI_CLASS] , AROS_ELF_CLASS ));
|
|
D(bug("[ELF2HUNK] EI_VERSION is %d - should be %d\n", eh->ident[EI_VERSION], EV_CURRENT ));
|
|
D(bug("[ELF2HUNK] type is %d - should be %d\n", eh->type , ET_REL ));
|
|
D(bug("[ELF2HUNK] EI_DATA is %d - should be %d\n", eh->ident[EI_DATA] , AROS_ELF_DATA ));
|
|
D(bug("[ELF2HUNK] machine is %d - should be %d\n", eh->machine , AROS_ELF_MACHINE));
|
|
|
|
set_error(ENOEXEC);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
struct hunkheader {
|
|
ULONG type;
|
|
ULONG memflags; /* Memory flags */
|
|
ULONG size; /* Size in ULONGs */
|
|
void *data;
|
|
ULONG relocs;
|
|
ULONG relrelocs;
|
|
int hunk; /* Allocatable hunk ID */
|
|
struct hunkreloc {
|
|
ULONG shid; /* ELF hunk base to add to... */
|
|
ULONG offset; /* offset in this hunk. */
|
|
const char *symbol;
|
|
} *reloc, *relreloc;
|
|
};
|
|
|
|
static int relocate
|
|
(
|
|
struct elfheader *eh,
|
|
struct sheader *sh,
|
|
ULONG shrel_idx,
|
|
int symtab_shndx,
|
|
struct relo *rel,
|
|
struct hunkheader **hh
|
|
)
|
|
{
|
|
struct sheader *shrel = &sh[shrel_idx];
|
|
struct sheader *shsymtab = &sh[shrel->link];
|
|
struct sheader *toreloc = &sh[shrel->info];
|
|
|
|
struct symbol *symtab = (struct symbol *)hh[shrel->link]->data;
|
|
struct hunkheader *h = hh[shrel->info];
|
|
|
|
/*
|
|
* Ignore relocs if the target section has no allocation. that can happen
|
|
* eg. with a .debug PROGBITS and a .rel.debug section
|
|
*/
|
|
D(bug("[ELF2HUNK] sh[%d].flags = 0x%x\n", (int)(shrel->info), (int)toreloc->flags));
|
|
if (!(toreloc->flags & SHF_ALLOC))
|
|
return 1;
|
|
|
|
ULONG numrel = shrel->size / shrel->entsize;
|
|
ULONG i;
|
|
ULONG hrels, hrelrels;
|
|
|
|
hrels = h->relocs;
|
|
hrelrels = h->relrelocs;
|
|
h->reloc = realloc(h->reloc, (h->relocs + numrel) * sizeof(struct hunkreloc));
|
|
h->relreloc = realloc(h->relreloc, (h->relrelocs + numrel) * sizeof(struct hunkreloc));
|
|
struct hunkreloc *hrel = &h->reloc[hrels];
|
|
struct hunkreloc *hrelrel = &h->relreloc[hrelrels];
|
|
|
|
for (i=0; i<numrel; i++, rel++)
|
|
{
|
|
struct symbol sym;
|
|
ULONG offset;
|
|
ULONG shindex;
|
|
ULONG shid = 0;
|
|
ULONG value = 0;
|
|
const char *symname;
|
|
|
|
rel_fixup(rel);
|
|
|
|
#ifdef __arm__
|
|
/*
|
|
* R_ARM_V4BX are actually special marks for the linker.
|
|
* They even never have a target (shindex == SHN_UNDEF),
|
|
* so we simply ignore them before doing any checks.
|
|
*/
|
|
if (ELF_R_TYPE(rel->info) == R_ARM_V4BX)
|
|
continue;
|
|
#endif
|
|
|
|
sym = symtab[ELF_R_SYM(rel->info)];
|
|
sym_fixup(&sym);
|
|
offset = rel->offset;
|
|
symname = (const char *)(hh[shsymtab->link]->data + sym.name);
|
|
|
|
if (sym.shindex != SHN_XINDEX)
|
|
shindex = sym.shindex;
|
|
|
|
else {
|
|
if (symtab_shndx < 0) {
|
|
bug("[ELF2HUNK] got symbol with shndx 0xfff, but there's no symtab shndx table\n");
|
|
set_error(EINVAL);
|
|
return 0;
|
|
}
|
|
shindex = ntohl(((ULONG *)hh[symtab_shndx]->data)[ELF_R_SYM(rel->info)]);
|
|
}
|
|
|
|
D(bug("[ELF2HUNK] Processing %d symbol %s\n", (int)shindex, symname));
|
|
|
|
switch (shindex)
|
|
{
|
|
|
|
case SHN_UNDEF:
|
|
if (ELF_R_TYPE(rel->info) != 0) {
|
|
bug("[ELF2HUNK] SHN_UNDEF symbol '%s', type %d unsupported\n", symname, (int)ELF_R_TYPE(rel->info));
|
|
set_error(EINVAL);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case SHN_COMMON:
|
|
bug("[ELF2HUNK] SHN_COMMON symbol '%s' unsupported\n", symname);
|
|
set_error(EINVAL);
|
|
|
|
return 0;
|
|
|
|
case SHN_ABS:
|
|
shid = ~0; value = sym.value;
|
|
break;
|
|
|
|
default:
|
|
shid = shindex;
|
|
value = sym.value;
|
|
break;
|
|
}
|
|
|
|
switch (ELF_R_TYPE(rel->info))
|
|
{
|
|
case R_68K_32:
|
|
value += rel->addend;
|
|
break;
|
|
|
|
case R_68K_PC32:
|
|
value += rel->addend - offset;
|
|
break;
|
|
|
|
case R_68K_PC16:
|
|
bug("[ELF2HUNK] Unsupported relocation type R_68K_PC16,\n");
|
|
bug("[ELF2HUNK] for symbol '%s'\n", symname);
|
|
set_error(EINVAL);
|
|
return 0;
|
|
break;
|
|
|
|
case R_68k_NONE:
|
|
shid = ~0;
|
|
break;
|
|
|
|
default:
|
|
bug("[ELF2HUNK] Unrecognized relocation type %d %d,\n", (int)i, (int)ELF_R_TYPE(rel->info));
|
|
bug("[ELF2HUNK] for symbol '%s'\n", symname);
|
|
set_error(EINVAL);
|
|
return 0;
|
|
}
|
|
|
|
D(bug("[ELF2HUNK] shid %d, offset 0x%x: base 0x%x\n", (int)shid, (int)offset, (int)value));
|
|
*(ULONG *)(h->data + offset) = htonl(value + ntohl(*(ULONG *)(h->data + offset)));
|
|
|
|
if (shid == ~0) {
|
|
continue;
|
|
}
|
|
|
|
switch (ELF_R_TYPE(rel->info))
|
|
{
|
|
case R_68K_32:
|
|
hrel->shid = shid;
|
|
hrel->offset = offset;
|
|
hrel->symbol = symname;
|
|
h->relocs++;
|
|
hrel++;
|
|
break;
|
|
|
|
case R_68K_PC32:
|
|
hrelrel->shid = shid;
|
|
hrelrel->offset = offset;
|
|
hrelrel->symbol = symname;
|
|
h->relrelocs++;
|
|
hrelrel++;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int reloc_cmp(const void *a, const void *b)
|
|
{
|
|
const struct hunkreloc *ha = a, *hb = b;
|
|
|
|
if (ha->shid != hb->shid)
|
|
return hb->shid - ha->shid;
|
|
return hb->offset - ha->offset;
|
|
}
|
|
|
|
static int wlong(int fd, ULONG val)
|
|
{
|
|
val = htonl(val);
|
|
return write(fd, &val, sizeof(val));
|
|
}
|
|
|
|
static int wshort(int fd, UWORD val)
|
|
{
|
|
UBYTE s[2];
|
|
s[0] = (val >> 8) & 0xff;
|
|
s[1] = val & 0xff;
|
|
return write(fd, s, 2);
|
|
}
|
|
|
|
int write_hunksymbols(int hunk_fd, struct sheader *sh, struct hunkheader **hh, int shid, int symtabndx)
|
|
{
|
|
int i, err, syms;
|
|
struct symbol *sym = hh[symtabndx]->data;
|
|
struct sheader *symtab = &sh[symtabndx];
|
|
|
|
syms = symtab->size / sizeof(struct symbol);
|
|
|
|
if (syms == 0)
|
|
return 1;
|
|
|
|
wlong(hunk_fd, HUNK_SYMBOL);
|
|
|
|
/* Dump symbols for this hunk */
|
|
for (i = 0; i < syms ; i++) {
|
|
struct symbol s;
|
|
const char *name;
|
|
int lsize;
|
|
|
|
s = sym[i];
|
|
sym_fixup(&s);
|
|
|
|
if (s.shindex != shid)
|
|
continue;
|
|
|
|
name = (const char *)(hh[symtab->link]->data + s.name);
|
|
D(bug("\t0x%08x: %s\n", (int)s.value, name));
|
|
lsize = (strlen(name) + 4) / 4;
|
|
wlong(hunk_fd, lsize);
|
|
err = write(hunk_fd, name, lsize * 4);
|
|
if (err < 0)
|
|
return 0;
|
|
wlong(hunk_fd, s.value);
|
|
}
|
|
wlong(hunk_fd, 0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int write_hunkrelocs(int hunk_fd, struct hunkheader **hh, int h)
|
|
{
|
|
int relreloc_failed = 0;
|
|
int relreloc_needed = 0;
|
|
int i;
|
|
|
|
if (hh[h]->relocs == 0)
|
|
return relreloc_failed;
|
|
|
|
/* Sort the relocations by reference hunk id */
|
|
qsort(hh[h]->reloc, hh[h]->relocs, sizeof(hh[h]->reloc[0]), reloc_cmp);
|
|
qsort(hh[h]->relreloc, hh[h]->relrelocs, sizeof(hh[h]->relreloc[0]), reloc_cmp);
|
|
|
|
wlong(hunk_fd, HUNK_RELOC32);
|
|
D(bug("\tHUNK_RELOC32: %d relocations\n", (int)hh[h]->relocs));
|
|
|
|
for (i = 0; i < hh[h]->relocs; ) {
|
|
int count;
|
|
int shid = hh[h]->reloc[i].shid;
|
|
for (count = i; count < hh[h]->relocs; count++)
|
|
if (hh[h]->reloc[count].shid != shid)
|
|
break;
|
|
count -= i;
|
|
wlong(hunk_fd, count);
|
|
D(bug("\t %d relocations relative to Hunk %d\n", count, hh[shid]->hunk));
|
|
/* Convert from ELF hunk ID to AOS hunk ID */
|
|
wlong(hunk_fd, hh[shid]->hunk);
|
|
for (; count > 0; i++, count--) {
|
|
D(bug("\t\t%d: 0x%08x %s\n", i, (int)hh[h]->reloc[i].offset, hh[h]->reloc[i].symbol));
|
|
wlong(hunk_fd, hh[h]->reloc[i].offset);
|
|
}
|
|
}
|
|
wlong(hunk_fd, 0);
|
|
|
|
wlong(hunk_fd, HUNK_RELRELOC32);
|
|
D(bug("\tHUNK_RELRELOC32: %d relocations\n", (int)hh[h]->relrelocs));
|
|
|
|
for (i = 0; i < hh[h]->relrelocs; ) {
|
|
int count;
|
|
int shid = hh[h]->relreloc[i].shid;
|
|
for (count = i; count < hh[h]->relrelocs; count++)
|
|
if (hh[h]->relreloc[count].shid != shid)
|
|
break;
|
|
count -= i;
|
|
if (h == shid) {
|
|
D(bug("RELRELOC32 within one hunk. Discarding...\n"));
|
|
i+=count;
|
|
continue;
|
|
}
|
|
if (count > 65535)
|
|
bug("RELRELOC32 count exceeds 65535!\n");
|
|
wshort(hunk_fd, count);
|
|
D(bug("\t %d relocations relative to Hunk %d\n", count, hh[shid]->hunk));
|
|
/* Convert from ELF hunk ID to AOS hunk ID */
|
|
wshort(hunk_fd, hh[shid]->hunk);
|
|
for (; count > 0; i++, count--) {
|
|
D(bug("\t\t%d: 0x%08x %s\n", i, (int)hh[h]->relreloc[i].offset, hh[h]->relreloc[i].symbol));
|
|
if (hh[h]->relreloc[i].offset > 65535)
|
|
{
|
|
if (flags & F_VERBOSE)
|
|
{
|
|
bug("relocation offset %d is too big for RELRELOC32\n", hh[h]->relreloc[i].offset);
|
|
if ((hh[h]->relreloc[i].symbol) && (strlen(hh[h]->relreloc[i].symbol) > 0))
|
|
{
|
|
bug(" -> failed to relocate symbol '%s'\n", hh[h]->relreloc[i].symbol);
|
|
}
|
|
}
|
|
relreloc_failed++;
|
|
}
|
|
wshort(hunk_fd, hh[h]->relreloc[i].offset);
|
|
relreloc_needed++;
|
|
}
|
|
}
|
|
|
|
/* If no relrelocs were required, rewind the counter back so that there is no HUNK_RELRELOC32 in the file */
|
|
if (relreloc_needed == 0)
|
|
{
|
|
D(bug("No relreloc32 written, rewinding file back\n"));
|
|
lseek(hunk_fd, -4, SEEK_CUR);
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise mark end of the RELRELOC32 by a reloc count of 0 */
|
|
wshort(hunk_fd, 0);
|
|
|
|
/* If file is now not aligned on 4 bytes, align it */
|
|
if (lseek(hunk_fd, 0, SEEK_CUR) % 4)
|
|
{
|
|
wshort(hunk_fd, 0);
|
|
}
|
|
}
|
|
return relreloc_failed;
|
|
}
|
|
|
|
static int copy_to(int in, int out)
|
|
{
|
|
static char buff[64*1024];
|
|
int len, err = 0;
|
|
|
|
do {
|
|
len = read(in, buff, sizeof(buff));
|
|
if (len < 0) {
|
|
perror("Can't read from input file\n");
|
|
err = len;
|
|
}
|
|
if (len == 0)
|
|
break;
|
|
} while ((err = write(out, buff, len)) == len);
|
|
|
|
if (err < 0) {
|
|
perror("Can't write to output file\n");
|
|
return -errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int elf2hunk(int file, int hunk_fd, const char *libname, int flags, char* target)
|
|
{
|
|
const __attribute__((unused)) char *names[3]={ "CODE", "DATA", "BSS" };
|
|
struct hunkheader **hh;
|
|
struct elfheader eh;
|
|
struct sheader *sh;
|
|
char *strtab = NULL;
|
|
int hunks = 0, retval = EXIT_SUCCESS;
|
|
int symtab_shndx = -1, err;
|
|
ULONG int_shnum, i;
|
|
BOOL exec_hunk_seen __attribute__ ((unused)) = FALSE;
|
|
|
|
/* load and validate ELF header */
|
|
D(bug("Load header\n"));
|
|
if ((flags & F_NOCONVERT) || !load_header(file, &eh)) {
|
|
/* If it's not an ELF, just copy it.
|
|
*
|
|
* This simplifies a number of mmakefiles
|
|
* for the m68k-amiga boot and ISO creation
|
|
*/
|
|
lseek(file, 0, SEEK_SET);
|
|
return (copy_to(file, hunk_fd) == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|
|
|
|
D(bug("Read SHNum\n"));
|
|
int_shnum = read_shnum(file, &eh);
|
|
if (!int_shnum)
|
|
return EXIT_FAILURE;
|
|
|
|
/* load section headers */
|
|
D(bug("Load %d Section Headers @0x%08x\n", int_shnum, (int)eh.shoff));
|
|
if (!(sh = load_block(file, eh.shoff, int_shnum * eh.shentsize)))
|
|
return EXIT_FAILURE;
|
|
|
|
sh_fixup(sh, int_shnum);
|
|
|
|
/* Looks like we have a valid executable. Generate a
|
|
* HUNK header set. Not all may be filled in.
|
|
*/
|
|
hh = calloc(sizeof(*hh), int_shnum);
|
|
|
|
/* Look for the string table */
|
|
D(bug("Look for string table\n"));
|
|
for (i = 0; i < int_shnum; i++) {
|
|
if (sh[i].type == SHT_STRTAB && i == eh.shstrndx) {
|
|
strtab = load_block(file, sh[i].offset, sh[i].size);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Iterate over the section headers in order to do some stuff... */
|
|
D(bug("Look for symbol tables\n"));
|
|
for (i = 0; i < int_shnum; i++)
|
|
{
|
|
/*
|
|
Load the symbol and string table(s).
|
|
|
|
NOTICE: the ELF standard, at the moment (Nov 2002) explicitely states
|
|
that only one symbol table per file is allowed. However, it
|
|
also states that this may change in future... we already handle it.
|
|
*/
|
|
D(bug("sh[%d].type = 0x%08x, .offset = 0x%08x, .size = 0x%08x\n",
|
|
(int)i, (int)sh[i].type, (int)sh[i].offset, (int)sh[i].size));
|
|
if (sh[i].type == SHT_SYMTAB || sh[i].type == SHT_STRTAB || sh[i].type == SHT_SYMTAB_SHNDX)
|
|
{
|
|
hh[i] = calloc(sizeof(struct hunkheader), 1);
|
|
hh[i]->type = (sh[i].type == SHT_SYMTAB) ? HUNK_SYMBOL : 0;
|
|
hh[i]->memflags = 0;
|
|
hh[i]->relocs = 0;
|
|
hh[i]->relrelocs = 0;
|
|
hh[i]->hunk = -1;
|
|
hh[i]->data = load_block(file, sh[i].offset, sh[i].size);
|
|
if (!hh[i]->data)
|
|
goto error;
|
|
|
|
if (sh[i].type == SHT_SYMTAB_SHNDX) {
|
|
if (symtab_shndx == -1)
|
|
symtab_shndx = i;
|
|
else if (flags & F_VERBOSE)
|
|
bug("[ELF2HUNK] file contains multiple symtab shndx tables. only using the first one\n");
|
|
}
|
|
}
|
|
else
|
|
/* Load the section in memory if needed, and make an hunk out of it */
|
|
if (sh[i].flags & SHF_ALLOC && sh[i].size > 0)
|
|
{
|
|
hh[i] = calloc(sizeof(struct hunkheader), 1);
|
|
hh[i]->size = sh[i].size;
|
|
hh[i]->hunk = hunks++;
|
|
|
|
if (sh[i].type == SHT_NOBITS) {
|
|
/* BSS area */
|
|
hh[i]->type = HUNK_BSS;
|
|
hh[i]->memflags = 0;
|
|
hh[i]->data = NULL;
|
|
} else {
|
|
if (sh[i].flags & SHF_EXECINSTR) {
|
|
hh[i]->type = HUNK_CODE;
|
|
exec_hunk_seen = TRUE;
|
|
} else {
|
|
hh[i]->type = HUNK_DATA;
|
|
}
|
|
hh[i]->data = load_block(file, sh[i].offset, sh[i].size);
|
|
}
|
|
|
|
if (strtab) {
|
|
const char *nameext;
|
|
|
|
D(bug("section %s\n", strtab + sh[i].name));
|
|
nameext = strrchr(strtab + sh[i].name, '.');
|
|
if (nameext) {
|
|
if (strcmp(nameext, ".MEMF_CHIP")==0) {
|
|
hh[i]->memflags |= MEMF_CHIP;
|
|
} else if (strcmp(nameext, ".MEMF_LOCAL")==0) {
|
|
hh[i]->memflags |= MEMF_LOCAL;
|
|
} else if (strcmp(nameext, ".MEMF_KICK")==0) {
|
|
hh[i]->memflags |= MEMF_KICK;
|
|
} else if (strcmp(nameext, ".MEMF_FAST")==0) {
|
|
hh[i]->memflags |= MEMF_FAST;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Relocate the sections */
|
|
D(bug("Convert relocation tables\n"));
|
|
for (i = 0; i < int_shnum; i++)
|
|
{
|
|
/* Does this relocation section refer to a hunk? If so, addr must be != 0 */
|
|
if ((sh[i].type == AROS_ELF_REL)
|
|
&& hh[sh[i].info]
|
|
&& hh[sh[i].info]->data)
|
|
{
|
|
void *reloc = load_block(file, sh[i].offset, sh[i].size);
|
|
|
|
if (!relocate(&eh, sh, i, symtab_shndx, reloc, hh))
|
|
return EXIT_FAILURE;
|
|
|
|
free(reloc);
|
|
}
|
|
}
|
|
|
|
wlong(hunk_fd, HUNK_HEADER);
|
|
if (libname == NULL) {
|
|
D(bug("HUNK_HEADER: hunks=%d, first=%d, last=%d\n", hunks, 0, hunks-1));
|
|
wlong(hunk_fd, 0); /* No name */
|
|
} else {
|
|
int lsize;
|
|
D(bug("HUNK_HEADER: \"%s\", hunks=%d, first=%d, last=%d\n", libname, hunks, 0, hunks-1));
|
|
lsize = (strlen(libname) + 4) / 4;
|
|
wlong(hunk_fd, lsize);
|
|
err = write(hunk_fd, libname, lsize * 4);
|
|
if (err < 0)
|
|
return EXIT_FAILURE;
|
|
}
|
|
wlong(hunk_fd, hunks);
|
|
wlong(hunk_fd, 0); /* First hunk is #0 */
|
|
wlong(hunk_fd, hunks - 1); /* Last hunk is hunks-1 */
|
|
|
|
/* Write all allocatable hunk sizes */
|
|
for (i = 0; i < int_shnum; i++) {
|
|
ULONG count;
|
|
|
|
if (hh[i]==NULL || hh[i]->hunk < 0)
|
|
continue;
|
|
|
|
count = (hh[i]->size + 4) / 4;
|
|
switch (hh[i]->memflags) {
|
|
case MEMF_CHIP:
|
|
count |= HUNKF_CHIP;
|
|
break;
|
|
case MEMF_FAST:
|
|
count |= HUNKF_FAST;
|
|
break;
|
|
case 0:
|
|
break;
|
|
default:
|
|
count |= HUNKF_MEMFLAGS;
|
|
break;
|
|
}
|
|
|
|
D(bug("\tHunk #%d, %s, lsize=%d\n", hh[i]->hunk, names[hh[i]->type - HUNK_CODE], (int)(hh[i]->size+4)/4));
|
|
wlong(hunk_fd, count);
|
|
|
|
if ((count & HUNKF_MEMFLAGS) == HUNKF_MEMFLAGS)
|
|
wlong(hunk_fd, hh[i]->memflags | MEMF_PUBLIC | MEMF_CLEAR);
|
|
}
|
|
|
|
/* Write all hunks */
|
|
for (i = hunks = 0; i < int_shnum; i++) {
|
|
D(int s;)
|
|
|
|
if (hh[i]==NULL || hh[i]->hunk < 0)
|
|
continue;
|
|
|
|
wlong(hunk_fd, hh[i]->type);
|
|
wlong(hunk_fd, (hh[i]->size + 4) / 4);
|
|
|
|
switch (hh[i]->type) {
|
|
case HUNK_BSS:
|
|
D(
|
|
bug("HUNK_BSS: %d longs\n", (int)((hh[i]->size + 4) / 4));
|
|
for (s = 0; s < int_shnum; s++) {
|
|
if (hh[s] && hh[s]->type == HUNK_SYMBOL)
|
|
write_hunksymbols(hunk_fd, sh, hh, i, s);
|
|
}
|
|
)
|
|
|
|
wlong(hunk_fd, HUNK_END);
|
|
hunks++;
|
|
break;
|
|
case HUNK_CODE:
|
|
case HUNK_DATA:
|
|
{
|
|
int failcnt;
|
|
D(bug("#%d HUNK_%s: %d longs\n", hh[i]->hunk, hh[i]->type == HUNK_CODE ? "CODE" : "DATA", (int)((hh[i]->size + 4) / 4)));
|
|
err = write(hunk_fd, hh[i]->data, ((hh[i]->size + 4)/4)*4);
|
|
if (err < 0)
|
|
return EXIT_FAILURE;
|
|
D(
|
|
for (s = 0; s < int_shnum; s++) {
|
|
if (hh[s] && hh[s]->type == HUNK_SYMBOL)
|
|
write_hunksymbols(hunk_fd, sh, hh, i, s);
|
|
}
|
|
)
|
|
if ((failcnt = write_hunkrelocs(hunk_fd, hh, i)) > 0)
|
|
{
|
|
bug("%d relocation(s) failed in HUNK #%d of %s\n", failcnt, hh[i]->hunk, target);
|
|
}
|
|
wlong(hunk_fd, HUNK_END);
|
|
D(bug("\tHUNK_END\n"));
|
|
}
|
|
break;
|
|
default:
|
|
D(bug("Unsupported allocatable hunk type %d\n", (int)hh[i]->type));
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
/* Free all blocks */
|
|
for (i = 0; i < int_shnum; i++) {
|
|
if (hh[i]) {
|
|
if (hh[i]->data)
|
|
free(hh[i]->data);
|
|
if (hh[i]->reloc)
|
|
free(hh[i]->reloc);
|
|
free(hh[i]);
|
|
}
|
|
}
|
|
free(hh);
|
|
free(sh);
|
|
|
|
if (strtab)
|
|
free(strtab);
|
|
|
|
D(bug("All good, all done.\n"));
|
|
return retval;
|
|
|
|
error:
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
static int copy(const char *src, const char *dst, int flags);
|
|
|
|
static BOOL valid_dir(const char *dir)
|
|
{
|
|
/* Don't convert anything in a Development directory */
|
|
if (strcasecmp(dir, "Development") == 0)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static int copy_dir(const char *src, const char *dst, int flags)
|
|
{
|
|
DIR *sdir;
|
|
struct dirent *de;
|
|
char spath[PATH_MAX], dpath[PATH_MAX];
|
|
char *sp, *dp;
|
|
int sleft, dleft;
|
|
int err = EXIT_SUCCESS;
|
|
|
|
sdir = opendir(src);
|
|
if (sdir == NULL) {
|
|
perror(src);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
snprintf(spath, sizeof(spath), "%s/", src);
|
|
spath[sizeof(spath)-1] = 0;
|
|
sp = &spath[strlen(spath)];
|
|
sleft = &spath[sizeof(spath)-1] - sp;
|
|
|
|
snprintf(dpath, sizeof(dpath), "%s/", dst);
|
|
dpath[sizeof(dpath)-1] = 0;
|
|
dp = &dpath[strlen(dpath)];
|
|
dleft = &dpath[sizeof(dpath)-1] - dp;
|
|
|
|
while ((de = readdir(sdir)) != NULL) {
|
|
int eflags = 0;
|
|
|
|
if ((strcmp(de->d_name, ".") == 0) ||
|
|
(strcmp(de->d_name, "..") == 0))
|
|
continue;
|
|
|
|
/* Don't convert anything if its an invalid directory */
|
|
if (!valid_dir(de->d_name))
|
|
eflags |= F_NOCONVERT;
|
|
|
|
strncpy(sp, de->d_name, sleft);
|
|
sp[sleft-1] = 0;
|
|
strncpy(dp, de->d_name, dleft);
|
|
dp[dleft-1] = 0;
|
|
err = copy(spath, dpath, flags | eflags);
|
|
if (err != EXIT_SUCCESS)
|
|
break;
|
|
}
|
|
|
|
closedir(sdir);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int copy(const char *src, const char *dst, int flags)
|
|
{
|
|
int src_fd, hunk_fd;
|
|
struct stat st;
|
|
int mode, ret;
|
|
char *target = basename((char *)dst);
|
|
|
|
if (flags & F_VERBOSE)
|
|
printf("%s ->\n %s\n", src, dst);
|
|
|
|
if (stat(src, &st) >= 0) {
|
|
mode = st.st_mode;
|
|
} else {
|
|
mode = 0755;
|
|
}
|
|
|
|
if (S_ISDIR(mode)) {
|
|
unlink(dst);
|
|
mkdir(dst, mode);
|
|
return copy_dir(src, dst, flags);
|
|
}
|
|
|
|
src_fd = open(src, O_RDONLY);
|
|
if (src_fd < 0) {
|
|
perror(src);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (strcmp(dst,"-") == 0)
|
|
hunk_fd = 1; /* stdout */
|
|
else {
|
|
unlink(dst);
|
|
hunk_fd = open(dst, O_RDWR | O_CREAT | O_TRUNC, mode);
|
|
}
|
|
if (hunk_fd < 0) {
|
|
perror(dst);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
ret = elf2hunk(src_fd, hunk_fd, NULL, flags, target);
|
|
if (ret != 0)
|
|
perror(src);
|
|
|
|
close(src_fd);
|
|
if (hunk_fd != 1)
|
|
close(hunk_fd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
if (argc == 4 && strcmp(argv[1],"-v") == 0) {
|
|
flags |= F_VERBOSE;
|
|
argc--;
|
|
argv++;
|
|
}
|
|
|
|
if (argc != 3) {
|
|
fprintf(stderr, "Usage:\n%s file.elf file.hunk\n", argv[0]);
|
|
fprintf(stderr, "%s src-dir dest-dir\n", argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return copy(argv[1], argv[2], flags);
|
|
}
|