summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2019-07-28 20:59:41 -0700
committerH. Peter Anvin <hpa@zytor.com>2019-07-28 20:59:41 -0700
commit49654eaa3ccafff8ed51516ccc7c5bce210ce668 (patch)
tree894ad1af9e479ef18375f0ca284d031b52a0694d
parent059695c799d4080a9ca8112c79712e93667f9229 (diff)
downloadsamples-49654eaa3ccafff8ed51516ccc7c5bce210ce668.tar.gz
samples-49654eaa3ccafff8ed51516ccc7c5bce210ce668.tar.xz
samples-49654eaa3ccafff8ed51516ccc7c5bce210ce668.zip
elf2exe: support generating a relocation stub if necessary
If 32-bit linear relocations are present, automatically generate a relocation stub. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--elf2exe/Makefile2
-rw-r--r--elf2exe/elf2exe.c203
-rw-r--r--elf2exe/elf2exe.h4
-rw-r--r--testme.s2
4 files changed, 139 insertions, 72 deletions
diff --git a/elf2exe/Makefile b/elf2exe/Makefile
index abb75c2..052f80f 100644
--- a/elf2exe/Makefile
+++ b/elf2exe/Makefile
@@ -16,7 +16,7 @@ CFLAGS = -W -Wall -O2 -g
LDFLAGS =
ALL = elf2exe$(X)
-OBJS = elf2exe.$(O) file.$(O)
+OBJS = elf2exe.$(O) file.$(O) rel32.$(O)
HDRS = elf2exe.h leint.h elf32.h elfcommon.h rel32.binh
NASM = nasm
diff --git a/elf2exe/elf2exe.c b/elf2exe/elf2exe.c
index 985a37d..358836a 100644
--- a/elf2exe/elf2exe.c
+++ b/elf2exe/elf2exe.c
@@ -30,7 +30,7 @@ static const Elf32_Phdr *find_phdr(const struct fileinfo *in, Elf32_Addr vma)
}
return NULL; /* Not found */
-}
+}
/* Get a pointer to a specified VMA if manifest in the file */
@@ -40,17 +40,17 @@ static const void *find_vma(const struct fileinfo *in, Elf32_Addr vma)
Elf32_Addr p_vaddr;
Elf32_Word p_filesz;
Elf32_Off p_offset;
-
+
if (!ph)
return NULL;
-
+
p_vaddr = getle(ph->p_vaddr);
p_filesz = getle(ph->p_filesz);
p_offset = getle(ph->p_offset);
-
+
if (vma >= p_vaddr + p_filesz)
return NULL; /* Lives in the non-manifest part */
-
+
return dptr(in, p_offset + (vma - p_vaddr));
}
@@ -89,7 +89,7 @@ struct dyninfo {
* DT_NULL, if present.)
*/
static int parse_dynamic(const struct fileinfo *in, struct dyninfo *di)
-{
+{
/* This list must match the structure above */
static const Elf32_Sword dynamic_tags[] = {
DT_REL,
@@ -116,14 +116,14 @@ static int parse_dynamic(const struct fileinfo *in, struct dyninfo *di)
Elf32_Addr *dia = (Elf32_Addr *)di; /* di as an array */
memset(di, 0, sizeof *di);
-
+
for (i = 0; i < e_phnum; i++) {
const Elf32_Phdr * const ph =
dptr(in, e_phoff + i * e_phentsize);
const Elf32_Word p_filesz = getle(ph->p_filesz);
const Elf32_Word p_type = getle(ph->p_type);
const Elf32_Off p_offset = getle(ph->p_offset);
-
+
if (p_type == PT_DYNAMIC) {
dyn = dptr(in, p_offset);
ndyn = p_filesz / sizeof *dyn;
@@ -164,7 +164,7 @@ static void load_data(const struct fileinfo *in, void *out, size_t outsize)
Elf32_Word p_filesz = getle(ph->p_filesz);
const Elf32_Word p_type = getle(ph->p_type);
const Elf32_Off p_offset = getle(ph->p_offset);
-
+
if (p_type != PT_LOAD || !p_filesz || p_paddr >= outsize)
continue;
@@ -175,36 +175,54 @@ static void load_data(const struct fileinfo *in, void *out, size_t outsize)
}
}
-static uint32_t *make_relocs(const struct fileinfo *in,
- const struct dyninfo *di, size_t *nrelocs)
+struct exe_relocs {
+ uint8_t r_type;
+ uint8_t r_len;
+ uint32_t *exerelocs;
+ size_t nrelocs;
+ size_t fileoffs;
+};
+
+static void free_relocs(struct exe_relocs *er)
+{
+ size_t i;
+
+ for (i = 0; er[i].r_type != R_386_NONE; i++) {
+ if (er[i].exerelocs) {
+ free(er[i].exerelocs);
+ er[i].exerelocs = NULL;
+ }
+ }
+}
+
+static int make_relocs(const struct fileinfo *in, const struct dyninfo *di,
+ struct exe_relocs *er)
{
- uint32_t *exerelocs = NULL, *erp;
- size_t i, n, relent;
+ size_t i, j, n, relent;
const uint8_t *rdata;
const size_t filesize = di->e2e_filesize;
relent = getle(di->dt_relent);
- if (!relent) {
- *nrelocs = 0;
- return NULL;
- }
+ if (!relent)
+ return 0;
+
n = getle(di->dt_relsz) / relent;
- if (!n) {
- *nrelocs = 0;
- return NULL;
+ if (!n)
+ return 0;
+
+ /* This allocates larger buffers than needed; that is OK */
+ for (i = 0; er[i].r_type != R_386_NONE; i++) {
+ er[i].exerelocs = malloc(n * sizeof(uint32_t));
+ if (!er[i].exerelocs) {
+ fprintf(stderr, "%s: %s: error: %s\n",
+ _progname, in->filename, strerror(errno));
+ goto err;
+ }
}
- /* This may allocate a larger buffer than needed; that is OK */
- exerelocs = erp = malloc(n * sizeof *exerelocs);
- if (!exerelocs) {
- fprintf(stderr, "%s: %s: error: %s\n",
- _progname, in->filename, strerror(errno));
- goto err;
- }
-
rdata = find_vma(in, di->dt_rel);
if (!rdata) {
- fprintf(stderr, "%s: %s: error: reloction section not found\n",
+ fprintf(stderr, "%s: %s: error: relocation section not found\n",
_progname, in->filename);
goto err;
}
@@ -213,19 +231,32 @@ static uint32_t *make_relocs(const struct fileinfo *in,
const Elf32_Rel *r = (const Elf32_Rel *)rdata;
const Elf32_Addr r_offset = getle(r->r_offset);
const uint8_t r_type = ELF32_R_TYPE(getle(r->r_info));
- uint32_t er;
+ uint32_t rval;
rdata += relent;
-
- switch (r_type) {
- case R_386_NONE:
- break; /* Nothing to do */
-
- case R_386_SEGRELATIVE:
- {
- const Elf32_Addr lma = vma_to_lma(in, r_offset);
- if (lma + 2 > filesize)
- goto err; /* Relocation out of range */
+
+ if (r_type == R_386_NONE)
+ continue;
+
+ for (j = 0; ; j++) {
+ Elf32_Addr lma;
+
+ if (er[j].r_type == R_386_NONE) {
+ fprintf(stderr,
+ "%s: %s: error: unsupportable relocation type %d\n",
+ _progname, in->filename, r_type);
+ goto err;
+ }
+ if (er[j].r_type != r_type)
+ continue;
+
+ lma = vma_to_lma(in, r_offset);
+ if (lma + er[j].r_len > filesize) {
+ fprintf(stderr,
+ "%s: %s: error: relocation beyond end of file\n",
+ _progname, in->filename);
+ goto err;
+ }
/*
* This splitting is arbirary, but allows the
@@ -234,26 +265,16 @@ static uint32_t *make_relocs(const struct fileinfo *in,
* However, limit the offset to 15 bits to make
* sure we avoid segment wraparound.
*/
- er = ((lma & 0xf8000) << 12) + (lma & 0x7fff);
- putle(erp++, er);
+ rval = ((lma & 0xf8000) << 12) + (lma & 0x7fff);
+ putle(&er[j].exerelocs[er[j].nrelocs++], rval);
break;
}
- default:
- /* Relocation unrepresentable in native EXE */
- fprintf(stderr, "%s: %s: unsupportable relocation type %d\n",
- _progname, in->filename, r_type);
- goto err;
- }
}
-
- *nrelocs = erp - exerelocs;
- return exerelocs;
+ return 0;
err:
- *nrelocs = -1;
- if (exerelocs)
- free(exerelocs);
- return NULL;
+ free_relocs(er);
+ return -1;
}
static uint16_t checksum(const void *data, size_t bytes)
@@ -299,10 +320,18 @@ static int process_file(const char *infile, const char *outfile)
Elf32_Half e_type;
Elf32_Half e_machine;
struct dyninfo di;
- size_t nrelocs;
- uint32_t *exerelocs = NULL;
struct mzhdr *mzhdr;
size_t hdrsize, filesize, datasize, bssmem;
+ struct exe_relocs er[3];
+ size_t i;
+
+ /* Set up this array right away as it is used to test for failures */
+ memset(&er, 0, sizeof er);
+ er[0].r_type = R_386_SEGRELATIVE;
+ er[0].r_len = 2;
+ er[1].r_type = R_386_RELATIVE;
+ er[1].r_len = 4;
+ er[2].r_type = R_386_NONE;
in = map_file(infile, READ_FILE);
if (!in) {
@@ -342,13 +371,14 @@ static int process_file(const char *infile, const char *outfile)
}
/* Verify and count relocations */
- exerelocs = make_relocs(in, &di, &nrelocs);
- if (nrelocs == (size_t)-1)
+ if (make_relocs(in, &di, er))
goto err; /* make_relocs() prints its own messages */
/* Compute various sizes */
-
- hdrsize = (sizeof *mzhdr) + nrelocs * (sizeof *exerelocs);
+
+ hdrsize = sizeof *mzhdr;
+ er[0].fileoffs = hdrsize;
+ hdrsize += er[0].nrelocs * sizeof(uint32_t);
/*
* Round to paragraph. DJ Delorie says "some OSs and/or programs
* may fail if the header is not a multiple of 512 bytes", but
@@ -361,7 +391,16 @@ static int process_file(const char *infile, const char *outfile)
* Round to paragraph. One could round to 512 bytes here, too
* if it turns out to be necessary... just in case.
*/
- filesize = (hdrsize + di.e2e_filesize + 15) & ~15;
+ filesize = hdrsize + di.e2e_filesize;
+ filesize = (filesize + 15) & ~15;
+
+ if (er[1].nrelocs) {
+ /* 32-bit linear relocations present */
+ filesize += rel32_size;
+ er[1].fileoffs = filesize;
+ filesize += er[1].nrelocs * sizeof(uint32_t);
+ }
+ filesize = (filesize + 15) & ~15;
/*
* This is how much loaded data is actually present in the file;
@@ -375,13 +414,13 @@ static int process_file(const char *infile, const char *outfile)
out = map_file(outfile, filesize);
if (!out)
goto err;
-
+
/* Build the EXE header */
mzhdr = (struct mzhdr *)out->wr;
putle(&mzhdr->magic, MZEXE_MAGIC);
putle(&mzhdr->lastblkbytes, filesize & 0x1ff);
putle(&mzhdr->blocks, (filesize + 0x1ff) >> 9);
- putle(&mzhdr->nrelocs, nrelocs);
+ putle(&mzhdr->nrelocs, er[0].nrelocs);
putle(&mzhdr->hdrparas, hdrsize >> 4);
putle(&mzhdr->bssparas, (bssmem + 15) >> 4);
putle(&mzhdr->maxparas, (bssmem + di.e2e_optmem + 15) >> 4);
@@ -391,20 +430,44 @@ static int process_file(const char *infile, const char *outfile)
putle(&mzhdr->cs, di.e2e_entseg >> 4);
putle(&mzhdr->relocsoffs, sizeof *mzhdr);
- /* mzhdr + 1 points to the byte immediately after the mzhdr */
- memcpy(mzhdr + 1, exerelocs, nrelocs * sizeof *exerelocs);
+ /* Do we need the rel32 stub? */
+ if (er[1].nrelocs) {
+ struct rel32_hdr {
+ uint16_t ip;
+ uint16_t cs;
+ uint16_t nrelocs;
+ } *hdr;
+ size_t where = er[1].fileoffs - rel32_size;
+
+ hdr = ((struct rel32_hdr *)((char *)out->wr + er[1].fileoffs))
+ - 1;
+ memcpy((char *)out->wr + where, rel32_code, rel32_size);
+
+ hdr->ip = mzhdr->ip;
+ hdr->cs = mzhdr->cs;
+ putle(&hdr->nrelocs, er[1].nrelocs);
+
+ mzhdr->ip = 0;
+ mzhdr->cs = (where - hdrsize) >> 4;
+ }
+
+ /* Copy relocations */
+ for (i = 0; er[i].r_type != R_386_NONE; i++) {
+ memcpy((char *)out->wr + er[i].fileoffs,
+ er[i].exerelocs,
+ er[i].nrelocs * sizeof(uint32_t));
+ }
/* Copy the actual file data */
load_data(in, (char *)out->wr + hdrsize, di.e2e_filesize);
/* Be excrutiatingly correct and fill in the checksum field */
putle(&mzhdr->chksum, -checksum(out->wr, filesize));
-
+
/* And that's it... */
err = false;
err:
- if (exerelocs)
- free(exerelocs);
+ free_relocs(er);
/* unmap_file(NULL, *) is a noop */
unmap_file(out, err);
@@ -416,7 +479,7 @@ int main(int argc, char *argv[])
{
_progname = argv[0];
-
+
if (argc < 2) {
fprintf(stderr, "Usage: %s infile [outfile]\n", argv[0]);
exit(1);
diff --git a/elf2exe/elf2exe.h b/elf2exe/elf2exe.h
index 9585ff0..027bc4d 100644
--- a/elf2exe/elf2exe.h
+++ b/elf2exe/elf2exe.h
@@ -30,4 +30,8 @@ const char *error_name(const char *filename, bool for_write);
const struct fileinfo *map_file(const char *filename, size_t wrsize);
int unmap_file(const struct fileinfo *fileinfo, bool err);
+/* rel32.c */
+extern const uint8_t rel32_code[];
+extern const size_t rel32_size;
+
#endif /* ELF2EXE_H */
diff --git a/testme.s b/testme.s
index ceb4582..6f41edd 100644
--- a/testme.s
+++ b/testme.s
@@ -51,7 +51,7 @@ ___start:
_get_foo:
mov _foo,%ax /* foo is in our own data segment */
.reloc .-2, R_386_SUB16, .L.SEG
- /* mov $_foo,%edx */ /* Can't be converted to EXE */
+ mov $_foo,%edx
ret
/*