summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2019-07-28 17:52:31 -0700
committerH. Peter Anvin <hpa@zytor.com>2019-07-28 17:52:31 -0700
commit944a4b1b44261fafd683fed4b91ba49b0ebde2b8 (patch)
treedf8a21e61a2cda786b5fc442564f8e3c4c6200d6
parent4739425a1c87ce95a1b8df2f20c4cd40aeb01989 (diff)
downloadsamples-944a4b1b44261fafd683fed4b91ba49b0ebde2b8.tar.gz
samples-944a4b1b44261fafd683fed4b91ba49b0ebde2b8.tar.xz
samples-944a4b1b44261fafd683fed4b91ba49b0ebde2b8.zip
elf2exe: working elf2exe program
This file can now convert a PIE ELF binary to EXE, as long as the only relocations present are the ones supported. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--elf2exe/.gitignore1
-rw-r--r--elf2exe/Makefile40
-rw-r--r--elf2exe/elf2exe.c422
-rw-r--r--elf2exe/elf2exe.h33
-rw-r--r--elf2exe/elf32.h117
-rw-r--r--elf2exe/elfcommon.h282
-rw-r--r--elf2exe/file.c223
-rw-r--r--elf2exe/leint.h47
8 files changed, 1165 insertions, 0 deletions
diff --git a/elf2exe/.gitignore b/elf2exe/.gitignore
new file mode 100644
index 0000000..253a0a0
--- /dev/null
+++ b/elf2exe/.gitignore
@@ -0,0 +1 @@
+elf2exe
diff --git a/elf2exe/Makefile b/elf2exe/Makefile
new file mode 100644
index 0000000..8378870
--- /dev/null
+++ b/elf2exe/Makefile
@@ -0,0 +1,40 @@
+MAKEFLAGS += -r -R
+
+# For Windows
+# O = obj
+# X = .exe
+# S = asm
+# A = lib
+O = o
+X =
+S = s
+A = a
+I = i
+
+CC = gcc
+CFLAGS = -W -Wall -O0 -g
+LDFLAGS =
+
+ALL = elf2exe$(X)
+OBJS = elf2exe.$(O) file.$(O)
+HDRS = elf2exe.h leint.h elf32.h elfcommon.h
+
+all: $(ALL)
+
+elf2exe$(X): $(OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS)
+
+%.$(O): %.c $(HDRS)
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+%.$(S): %.c $(HDRS)
+ $(CC) $(CFLAGS) -S -o $@ $<
+
+%.$(I): %.c $(HDRS)
+ $(CC) $(CFLAGS) -E -o $@ $<
+
+clean:
+ rm -f *.$(O) *.$(S) *.$(A) *.$(I) $(ALL)
+
+spotless: clean
+ rm -f *~ *.bak
diff --git a/elf2exe/elf2exe.c b/elf2exe/elf2exe.c
new file mode 100644
index 0000000..e7808d2
--- /dev/null
+++ b/elf2exe/elf2exe.c
@@ -0,0 +1,422 @@
+#include "elf2exe.h"
+#include "elf32.h"
+
+const char *_progname;
+
+static inline const void *dptr(const struct fileinfo *in, Elf32_Off off)
+{
+ return (const uint8_t *)in->rd + off;
+}
+
+/* Find the PT_LOAD header for a given VMA, if present */
+static const Elf32_Phdr *find_phdr(const struct fileinfo *in, Elf32_Addr vma)
+{
+ const Elf32_Ehdr * const eh = dptr(in, 0);
+ const Elf32_Off e_phoff = getle(eh->e_phoff);
+ const Elf32_Half e_phnum = getle(eh->e_phnum);
+ const Elf32_Half e_phentsize = getle(eh->e_phentsize);
+ size_t i;
+
+ for (i = 0; i < e_phnum; i++) {
+ const Elf32_Phdr * const ph =
+ dptr(in, e_phoff + i * e_phentsize);
+ const Elf32_Addr p_vaddr = getle(ph->p_vaddr);
+ const Elf32_Word p_memsz = getle(ph->p_memsz);
+ const Elf32_Word p_type = getle(ph->p_type);
+
+ if (p_type == PT_LOAD && vma >= p_vaddr &&
+ vma < p_vaddr + p_memsz)
+ return ph;
+ }
+
+ return NULL; /* Not found */
+}
+
+
+/* Get a pointer to a specified VMA if manifest in the file */
+static const void *find_vma(const struct fileinfo *in, Elf32_Addr vma)
+{
+ const Elf32_Phdr * const ph = find_phdr(in, 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));
+}
+
+/* Convert a VMA address to an LMA */
+static Elf32_Addr vma_to_lma(const struct fileinfo *in, Elf32_Addr vma)
+{
+ const Elf32_Phdr * const ph = find_phdr(in, vma);
+ Elf32_Addr p_vaddr, p_paddr;
+
+ if (!ph)
+ return vma; /* Not found, arbitrarily assume identity */
+
+ p_vaddr = getle(ph->p_vaddr);
+ p_paddr = getle(ph->p_paddr);
+
+ return vma - p_vaddr + p_paddr;
+}
+
+/* The values we care about in the dynamic structure */
+struct dyninfo {
+ Elf32_Addr dt_rel;
+ Elf32_Addr dt_relsz;
+ Elf32_Addr dt_relent;
+ Elf32_Addr e2e_filesize;
+ Elf32_Addr e2e_memsize;
+ Elf32_Addr e2e_optmem;
+ Elf32_Addr e2e_entseg;
+ Elf32_Addr e2e_stkseg;
+ Elf32_Addr e2e_stkbase;
+ Elf32_Addr e2e_stksize;
+};
+
+/*
+ * Parse the dynamic section into the dyninfo structure. Returns the
+ * number of entries in the dynamic structure (not including the final
+ * 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,
+ DT_RELSZ,
+ DT_RELENT,
+ DT_E2E_FILESIZE,
+ DT_E2E_MEMSIZE,
+ DT_E2E_OPTMEM,
+ DT_E2E_ENTSEG,
+ DT_E2E_STKSEG,
+ DT_E2E_STKBASE,
+ DT_E2E_STKSIZE,
+ };
+ const size_t ntags = (sizeof dynamic_tags)/(sizeof dynamic_tags[0]);
+
+ const Elf32_Ehdr * const eh = dptr(in, 0);
+ const Elf32_Off e_phoff = getle(eh->e_phoff);
+ const Elf32_Half e_phnum = getle(eh->e_phnum);
+ const Elf32_Half e_phentsize = getle(eh->e_phentsize);
+ const Elf32_Dyn *dyn = NULL;
+ size_t ndyn = 0;
+ size_t i, j;
+ Elf32_Sword tag;
+ 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;
+ break;
+ }
+ }
+
+ for (i = 0; i < ndyn; i++) {
+ tag = getle(dyn[i].d_tag);
+ if (!tag)
+ break;
+
+ for (j = 0; j < ntags; j++) {
+ if (tag == dynamic_tags[j]) {
+ dia[j] = getle(dyn[i].d_un.d_ptr);
+ break;
+ }
+ }
+ }
+
+ return i;
+}
+
+/* Load the file present data into a buffer, but with a limit */
+static void load_data(const struct fileinfo *in, void *out, size_t outsize)
+{
+ const Elf32_Ehdr * const eh = dptr(in, 0);
+ const Elf32_Off e_phoff = getle(eh->e_phoff);
+ const Elf32_Half e_phnum = getle(eh->e_phnum);
+ const Elf32_Half e_phentsize = getle(eh->e_phentsize);
+ uint8_t * const outp = out;
+ size_t i;
+
+ for (i = 0; i < e_phnum; i++) {
+ const Elf32_Phdr * const ph =
+ dptr(in, e_phoff + i * e_phentsize);
+ const Elf32_Addr p_paddr = getle(ph->p_vaddr);
+ 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;
+
+ if ((outsize - p_paddr) < p_filesz)
+ p_filesz = outsize - p_paddr;
+
+ memcpy(outp + p_paddr, dptr(in, p_offset), p_filesz);
+ }
+}
+
+static uint32_t *make_relocs(const struct fileinfo *in,
+ const struct dyninfo *di, size_t *nrelocs)
+{
+ uint32_t *exerelocs = NULL, *erp;
+ size_t i, n, relent;
+ const uint8_t *rdata;
+ const size_t filesize = di->e2e_filesize;
+
+ relent = getle(di->dt_relent);
+ if (!relent) {
+ *nrelocs = 0;
+ return NULL;
+ }
+ n = getle(di->dt_relsz) / relent;
+ if (!n) {
+ *nrelocs = 0;
+ return NULL;
+ }
+
+ /* 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",
+ _progname, in->filename);
+ goto err;
+ }
+
+ for (i = 0; i < n; i++) {
+ 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;
+
+ 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 */
+
+ /*
+ * This splitting is arbirary, but allows the
+ * loader to not switch segments as much, assuming
+ * that it cares enough to have that optimization.
+ * However, limit the offset to 15 bits to make
+ * sure we avoid segment wraparound.
+ */
+ er = ((lma & 0xf8000) << 12) + (lma & 0x7fff);
+ putle(erp++, er);
+ 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;
+
+err:
+ *nrelocs = -1;
+ if (exerelocs)
+ free(exerelocs);
+ return NULL;
+}
+
+static uint16_t checksum(const void *data, size_t bytes)
+{
+ const uint16_t *dp = data;
+ uint16_t csum = 0;
+
+ while (bytes >= 2) {
+ csum += getle(*dp++);
+ bytes -= 2;
+ }
+
+ if (bytes == 1)
+ csum += getle(*(const uint8_t *)dp);
+
+ return csum;
+}
+
+#define MZEXE_MAGIC 0x5a4d
+struct mzhdr {
+ uint16_t magic; /* MZEXE_MAGIC */
+ uint16_t lastblkbytes; /* == filesize & 0x1ff */
+ uint16_t blocks; /* == (filesize + 0x1ff) >> 9 */
+ uint16_t nrelocs;
+ uint16_t hdrparas;
+ uint16_t bssparas; /* Required extra memory */
+ uint16_t maxparas; /* Soft request for extra memory */
+ uint16_t ss;
+ uint16_t sp;
+ uint16_t chksum;
+ uint16_t ip;
+ uint16_t cs;
+ uint16_t relocsoffs;
+ uint16_t overlay;
+};
+
+static int process_file(const char *infile, const char *outfile)
+{
+ bool err = true;
+ const struct fileinfo *in = NULL;
+ const struct fileinfo *out = NULL;
+ const Elf32_Ehdr *eh;
+ 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, bssmem;
+
+ in = map_file(infile, READ_FILE);
+ if (!in) {
+ fprintf(stderr, "%s: %s: error: %s\n",
+ _progname, error_name(infile, false),
+ strerror(errno));
+ goto err;
+ }
+
+ eh = dptr(in, 0);
+ if (in->size < sizeof(Elf32_Ehdr) ||
+ memcmp(&eh->e_ident[EI_MAG0], ELFMAG "\1\1", 6)) {
+ fprintf(stderr,
+ "%s: %s: error: not a littleendian ELF32 file\n",
+ _progname, in->filename);
+ goto err;
+ }
+
+ e_machine = getle(eh->e_machine);
+ if (e_machine != EM_386 && e_machine != EM_486) {
+ fprintf(stderr, "%s: %s: error: unsupported ELF machine type\n",
+ _progname, in->filename);
+ goto err;
+ }
+
+ e_type = getle(eh->e_type);
+ if (e_type != ET_DYN && e_type != ET_EXEC) {
+ fprintf(stderr, "%s: %s: error: not an executable file\n",
+ _progname, in->filename);
+ goto err;
+ }
+
+ if (!parse_dynamic(in, &di)) {
+ fprintf(stderr, "%s: %s: error: no dynamic section present\n",
+ _progname, in->filename);
+ goto err;
+ }
+
+ /* Verify and count relocations */
+ exerelocs = make_relocs(in, &di, &nrelocs);
+ if (nrelocs == (size_t)-1)
+ goto err; /* make_relocs() prints its own messages */
+
+ /* Compute various sizes */
+
+ hdrsize = (sizeof *mzhdr) + nrelocs * (sizeof *exerelocs);
+ /*
+ * Round to paragraph. DJ Delorie says "some OSs and/or programs
+ * may fail if the header is not a multiple of 512 bytes", but
+ * quite a few DOS toolchains seem to create headers that are
+ * not.
+ */
+ hdrsize = (hdrsize + 15) & ~15; /* Round to paragraph */
+
+ /*
+ * 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;
+ if (di.e2e_memsize < filesize)
+ bssmem = 0;
+ else
+ bssmem = di.e2e_memsize - filesize;
+
+ /* Open output file */
+ 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->hdrparas, hdrsize >> 4);
+ putle(&mzhdr->bssparas, (bssmem + 15) >> 4);
+ putle(&mzhdr->maxparas, (bssmem + di.e2e_optmem + 15) >> 4);
+ putle(&mzhdr->ss, di.e2e_stkseg >> 4);
+ putle(&mzhdr->sp, di.e2e_stkbase + di.e2e_stksize - di.e2e_stkseg);
+ putle(&mzhdr->ip, getle(eh->e_entry) - di.e2e_entseg);
+ 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);
+
+ /* 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);
+
+ /* unmap_file(NULL, *) is a noop */
+ unmap_file(out, err);
+ unmap_file(in, err);
+ return err;
+}
+
+int main(int argc, char *argv[])
+{
+
+ _progname = argv[0];
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s infile [outfile]\n", argv[0]);
+ exit(1);
+ }
+
+ return process_file(argv[1], argv[2]);
+}
diff --git a/elf2exe/elf2exe.h b/elf2exe/elf2exe.h
new file mode 100644
index 0000000..9585ff0
--- /dev/null
+++ b/elf2exe/elf2exe.h
@@ -0,0 +1,33 @@
+#ifndef ELF2EXE_H
+#define ELF2EXE_H
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "leint.h"
+#include "elf32.h"
+
+/* elf2exe.c */
+extern const char *_progname; /* == argv[0] */
+
+/* file.c */
+struct fileinfo {
+ void *wr; /* Writable data */
+ const void *rd; /* Readable data */
+ FILE *fp; /* File pointer if stdio */
+ const char *filename; /* Filename suitable for error messages */
+ size_t size;
+ size_t mapsize;
+ bool err; /* Set to true to delete write file on unmap */
+};
+
+#define READ_FILE ((size_t)-1)
+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);
+
+#endif /* ELF2EXE_H */
diff --git a/elf2exe/elf32.h b/elf2exe/elf32.h
new file mode 100644
index 0000000..1884bd7
--- /dev/null
+++ b/elf2exe/elf32.h
@@ -0,0 +1,117 @@
+/*
+ * elf32.h
+ */
+
+#ifndef ELF32_H
+#define ELF32_H
+
+#include "elfcommon.h"
+
+/* ELF standard typedefs (yet more proof that <stdint.h> was way overdue) */
+typedef uint16_t Elf32_Half;
+typedef int16_t Elf32_SHalf;
+typedef uint32_t Elf32_Word;
+typedef int32_t Elf32_Sword;
+typedef uint64_t Elf32_Xword;
+typedef int64_t Elf32_Sxword;
+
+typedef uint32_t Elf32_Off;
+typedef uint32_t Elf32_Addr;
+typedef uint16_t Elf32_Section;
+
+/* Dynamic header */
+
+typedef struct elf32_dyn {
+ Elf32_Sword d_tag;
+ union {
+ Elf32_Sword d_val;
+ Elf32_Addr d_ptr;
+ } d_un;
+} Elf32_Dyn;
+
+/* Relocations */
+
+#define ELF32_R_SYM(x) ((x) >> 8)
+#define ELF32_R_TYPE(x) ((x) & 0xff)
+
+typedef struct elf32_rel {
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+} Elf32_Rel;
+
+typedef struct elf32_rela {
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+ Elf32_Sword r_addend;
+} Elf32_Rela;
+
+/* Symbol */
+
+typedef struct elf32_sym {
+ Elf32_Word st_name;
+ Elf32_Addr st_value;
+ Elf32_Word st_size;
+ unsigned char st_info;
+ unsigned char st_other;
+ Elf32_Half st_shndx;
+} Elf32_Sym;
+
+/* Main file header */
+
+typedef struct elf32_hdr {
+ unsigned char e_ident[EI_NIDENT];
+ Elf32_Half e_type;
+ Elf32_Half e_machine;
+ Elf32_Word e_version;
+ Elf32_Addr e_entry;
+ Elf32_Off e_phoff;
+ Elf32_Off e_shoff;
+ Elf32_Word e_flags;
+ Elf32_Half e_ehsize;
+ Elf32_Half e_phentsize;
+ Elf32_Half e_phnum;
+ Elf32_Half e_shentsize;
+ Elf32_Half e_shnum;
+ Elf32_Half e_shstrndx;
+} Elf32_Ehdr;
+
+/* Program header */
+
+typedef struct elf32_phdr {
+ Elf32_Word p_type;
+ Elf32_Off p_offset;
+ Elf32_Addr p_vaddr;
+ Elf32_Addr p_paddr;
+ Elf32_Word p_filesz;
+ Elf32_Word p_memsz;
+ Elf32_Word p_flags;
+ Elf32_Word p_align;
+} Elf32_Phdr;
+
+/* Section header */
+
+typedef struct elf32_shdr {
+ Elf32_Word sh_name;
+ Elf32_Word sh_type;
+ Elf32_Word sh_flags;
+ Elf32_Addr sh_addr;
+ Elf32_Off sh_offset;
+ Elf32_Word sh_size;
+ Elf32_Word sh_link;
+ Elf32_Word sh_info;
+ Elf32_Word sh_addralign;
+ Elf32_Word sh_entsize;
+} Elf32_Shdr;
+
+/* Note header */
+typedef struct elf32_note {
+ Elf32_Word n_namesz; /* Name size */
+ Elf32_Word n_descsz; /* Content size */
+ Elf32_Word n_type; /* Content type */
+} Elf32_Nhdr;
+
+/* How to extract and insert information held in the st_info field. */
+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4)
+#define ELF32_ST_TYPE(val) ((val) & 0xf)
+
+#endif /* ELF32_H */
diff --git a/elf2exe/elfcommon.h b/elf2exe/elfcommon.h
new file mode 100644
index 0000000..ab1d416
--- /dev/null
+++ b/elf2exe/elfcommon.h
@@ -0,0 +1,282 @@
+/*
+ * elfcommon.h
+ */
+
+#ifndef ELFCOMMON_H
+#define ELFCOMMON_H
+
+#include <stdint.h>
+
+/* Segment types */
+#define PT_NULL 0
+#define PT_LOAD 1
+#define PT_DYNAMIC 2
+#define PT_INTERP 3
+#define PT_NOTE 4
+#define PT_SHLIB 5
+#define PT_PHDR 6
+#define PT_LOOS 0x60000000
+#define PT_HIOS 0x6fffffff
+#define PT_LOPROC 0x70000000
+#define PT_HIPROC 0x7fffffff
+#define PT_GNU_EH_FRAME 0x6474e550 /* Extension, eh? */
+
+/* ELF file types */
+#define ET_NONE 0
+#define ET_REL 1
+#define ET_EXEC 2
+#define ET_DYN 3
+#define ET_CORE 4
+#define ET_LOPROC 0xff00
+#define ET_HIPROC 0xffff
+
+/* ELF machine types */
+#define EM_NONE 0
+#define EM_M32 1
+#define EM_SPARC 2
+#define EM_386 3
+#define EM_68K 4
+#define EM_88K 5
+#define EM_486 6 /* Not used in Linux at least */
+#define EM_860 7
+#define EM_MIPS 8 /* R3k, bigendian(?) */
+#define EM_MIPS_RS4_BE 10 /* R4k BE */
+#define EM_PARISC 15
+#define EM_SPARC32PLUS 18
+#define EM_PPC 20
+#define EM_PPC64 21
+#define EM_S390 22
+#define EM_SH 42
+#define EM_SPARCV9 43 /* v9 = SPARC64 */
+#define EM_H8_300H 47
+#define EM_H8S 48
+#define EM_IA_64 50 /* Itanic */
+#define EM_X86_64 62
+#define EM_CRIS 76
+#define EM_V850 87
+#define EM_ALPHA 0x9026 /* Interrim Alpha that stuck around */
+#define EM_CYGNUS_V850 0x9080 /* Old v850 ID used by Cygnus */
+#define EM_S390_OLD 0xA390 /* Obsolete interrim value for S/390 */
+
+/* Dynamic type values */
+#define DT_NULL 0
+#define DT_NEEDED 1
+#define DT_PLTRELSZ 2
+#define DT_PLTGOT 3
+#define DT_HASH 4
+#define DT_STRTAB 5
+#define DT_SYMTAB 6
+#define DT_RELA 7
+#define DT_RELASZ 8
+#define DT_RELAENT 9
+#define DT_STRSZ 10
+#define DT_SYMENT 11
+#define DT_INIT 12
+#define DT_FINI 13
+#define DT_SONAME 14
+#define DT_RPATH 15
+#define DT_SYMBOLIC 16
+#define DT_REL 17
+#define DT_RELSZ 18
+#define DT_RELENT 19
+#define DT_PLTREL 20
+#define DT_DEBUG 21
+#define DT_TEXTREL 22
+#define DT_JMPREL 23
+#define DT_LOPROC 0x70000000
+#define DT_HIPROC 0x7fffffff
+
+/* Special DT_ entries used by elf2exe */
+#define DT_E2E_BASE 0x6bb31700
+#define DT_E2E_FILESIZE (DT_E2E_BASE + 1)
+#define DT_E2E_MEMSIZE (DT_E2E_BASE + 2)
+#define DT_E2E_ENTSEG (DT_E2E_BASE + 3)
+#define DT_E2E_STKSEG (DT_E2E_BASE + 4)
+#define DT_E2E_STKBASE (DT_E2E_BASE + 5)
+#define DT_E2E_STKSIZE (DT_E2E_BASE + 6)
+#define DT_E2E_OPTMEM (DT_E2E_BASE + 7)
+
+/* Auxilliary table entries */
+#define AT_NULL 0 /* end of vector */
+#define AT_IGNORE 1 /* entry should be ignored */
+#define AT_EXECFD 2 /* file descriptor of program */
+#define AT_PHDR 3 /* program headers for program */
+#define AT_PHENT 4 /* size of program header entry */
+#define AT_PHNUM 5 /* number of program headers */
+#define AT_PAGESZ 6 /* system page size */
+#define AT_BASE 7 /* base address of interpreter */
+#define AT_FLAGS 8 /* flags */
+#define AT_ENTRY 9 /* entry point of program */
+#define AT_NOTELF 10 /* program is not ELF */
+#define AT_UID 11 /* real uid */
+#define AT_EUID 12 /* effective uid */
+#define AT_GID 13 /* real gid */
+#define AT_EGID 14 /* effective gid */
+#define AT_PLATFORM 15 /* string identifying CPU for optimizations */
+#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */
+#define AT_CLKTCK 17 /* frequency at which times() increments */
+/* 18..22 = ? */
+#define AT_SECURE 23 /* secure mode boolean */
+#define AT_SYSINFO 32 /* vdso entry point address */
+#define AT_SYSINFO_EHDR 33 /* vdso header address */
+
+/* Program header permission flags */
+#define PF_X 0x1
+#define PF_W 0x2
+#define PF_R 0x4
+
+/* Section header types */
+#define SHT_NULL 0
+#define SHT_PROGBITS 1
+#define SHT_SYMTAB 2
+#define SHT_STRTAB 3
+#define SHT_RELA 4
+#define SHT_HASH 5
+#define SHT_DYNAMIC 6
+#define SHT_NOTE 7
+#define SHT_NOBITS 8
+#define SHT_REL 9
+#define SHT_SHLIB 10
+#define SHT_DYNSYM 11
+#define SHT_NUM 12
+#define SHT_LOPROC 0x70000000
+#define SHT_HIPROC 0x7fffffff
+#define SHT_LOUSER 0x80000000
+#define SHT_HIUSER 0xffffffff
+
+/* Section header flags */
+#define SHF_WRITE 0x1
+#define SHF_ALLOC 0x2
+#define SHF_EXECINSTR 0x4
+#define SHF_MASKPROC 0xf0000000
+
+/* Special section numbers */
+#define SHN_UNDEF 0
+#define SHN_LORESERVE 0xff00
+#define SHN_LOPROC 0xff00
+#define SHN_HIPROC 0xff1f
+#define SHN_ABS 0xfff1
+#define SHN_COMMON 0xfff2
+#define SHN_HIRESERVE 0xffff
+
+/* End of a chain. */
+#define STN_UNDEF 0
+
+/* Lenght of magic at the start of a file */
+#define EI_NIDENT 16
+
+/* Magic number constants... */
+#define EI_MAG0 0 /* e_ident[] indexes */
+#define EI_MAG1 1
+#define EI_MAG2 2
+#define EI_MAG3 3
+#define EI_CLASS 4
+#define EI_DATA 5
+#define EI_VERSION 6
+#define EI_OSABI 7
+#define EI_PAD 8
+
+#define ELFMAG0 0x7f /* EI_MAG */
+#define ELFMAG1 'E'
+#define ELFMAG2 'L'
+#define ELFMAG3 'F'
+#define ELFMAG "\177ELF"
+#define SELFMAG 4
+
+#define ELFCLASSNONE 0 /* EI_CLASS */
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+#define ELFCLASSNUM 3
+
+#define ELFDATANONE 0 /* e_ident[EI_DATA] */
+#define ELFDATA2LSB 1
+#define ELFDATA2MSB 2
+
+#define EV_NONE 0 /* e_version, EI_VERSION */
+#define EV_CURRENT 1
+#define EV_NUM 2
+
+#define ELFOSABI_NONE 0
+#define ELFOSABI_LINUX 3
+
+/* Legal values for ST_BIND subfield of st_info (symbol binding). */
+#define STB_LOCAL 0 /* Local symbol */
+#define STB_GLOBAL 1 /* Global symbol */
+#define STB_WEAK 2 /* Weak symbol */
+#define STB_NUM 3 /* Number of defined types. */
+#define STB_LOOS 10 /* Start of OS-specific */
+#define STB_HIOS 12 /* End of OS-specific */
+#define STB_LOPROC 13 /* Start of processor-specific */
+#define STB_HIPROC 15 /* End of processor-specific */
+
+/* i386 relocs. */
+
+#define R_386_NONE 0 /* No reloc */
+#define R_386_32 1 /* Direct 32 bit */
+#define R_386_PC32 2 /* PC relative 32 bit */
+#define R_386_GOT32 3 /* 32 bit GOT entry */
+#define R_386_PLT32 4 /* 32 bit PLT address */
+#define R_386_COPY 5 /* Copy symbol at runtime */
+#define R_386_GLOB_DAT 6 /* Create GOT entry */
+#define R_386_JMP_SLOT 7 /* Create PLT entry */
+#define R_386_RELATIVE 8 /* Adjust by program base */
+#define R_386_GOTOFF 9 /* 32 bit offset to GOT */
+#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */
+#define R_386_32PLT 11
+#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */
+#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS
+ block offset */
+#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block
+ offset */
+#define R_386_TLS_LE 17 /* Offset relative to static TLS
+ block */
+#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of
+ general dynamic thread local data */
+#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of
+ local dynamic thread local data
+ in LE code */
+#define R_386_16 20
+#define R_386_PC16 21
+#define R_386_8 22
+#define R_386_PC8 23
+#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic
+ thread local data */
+#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */
+#define R_386_TLS_GD_CALL 26 /* Relocation for call to
+ __tls_get_addr() */
+#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */
+#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic
+ thread local data in LE code */
+#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */
+#define R_386_TLS_LDM_CALL 30 /* Relocation for call to
+ __tls_get_addr() in LDM code */
+#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */
+#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */
+#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS
+ block offset */
+#define R_386_TLS_LE_32 34 /* Negated offset relative to static
+ TLS block */
+#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */
+#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */
+#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */
+#define R_386_SIZE32 38 /* 32-bit symbol size */
+#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */
+#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS
+ descriptor for
+ relaxation. */
+#define R_386_TLS_DESC 41 /* TLS descriptor containing
+ pointer to code and to
+ argument, returning the TLS
+ offset for the symbol. */
+#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */
+#define R_386_GOT32X 43 /* Load from 32 bit GOT entry,
+ relaxable. */
+
+#define R_386_SEG16 45 /* 16-bit segment */
+#define R_386_SUB16 46 /* 16-bit subtraction */
+#define R_386_SUB32 47 /* 32-bit subtraction */
+#define R_386_SEGRELATIVE 48 /* 16-bit relative segment */
+
+#define R_386_NUM 49
+
+#endif /* ELFCOMMON_H */
diff --git a/elf2exe/file.c b/elf2exe/file.c
new file mode 100644
index 0000000..ba3ac03
--- /dev/null
+++ b/elf2exe/file.c
@@ -0,0 +1,223 @@
+/*
+ * Read or map a file
+ */
+
+#include "elf2exe.h"
+
+#ifdef __unix__
+# include <unistd.h>
+#endif
+
+/* True if this filename refers to stdin/stdout */
+static inline bool name_is_stdio(const char *filename)
+{
+ return !filename || !filename[0] ||
+ (filename[0] == '-' && !filename[1]);
+}
+
+/* True if this file pointer refers to stdin or stdout */
+static inline bool fp_is_stdio(FILE *fp)
+{
+ return (fp == stdin) || (fp == stdout);
+}
+
+/* Name suitable for error messages */
+const char *error_name(const char *filename, bool for_write)
+{
+ if (!name_is_stdio(filename))
+ return filename;
+
+ return for_write ? "stdout" : "stdin";
+}
+
+#define NOT_MMAP ((size_t)-1)
+
+#ifdef _POSIX_MAPPED_FILES
+
+# include <fcntl.h>
+# include <sys/stat.h>
+# include <sys/mman.h>
+
+# if !defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE)
+# define _SC_PAGESIZE _SC_PAGE_SIZE
+# endif
+
+static size_t pagemask;
+
+static int map_file_mmap(struct fileinfo *fileinfo)
+{
+ const bool for_write = fileinfo->size != READ_FILE;
+ void *map;
+ int fd = fileno(fileinfo->fp);
+ const int prot = PROT_READ | (for_write ? PROT_WRITE : 0);
+
+ if (!pagemask)
+ pagemask = ~((size_t)sysconf(_SC_PAGESIZE) - 1);
+
+ if (for_write) {
+ if (ftruncate(fd, fileinfo->size))
+ goto err;
+ } else {
+ struct stat st;
+ if (fstat(fd, &st))
+ goto err;
+ fileinfo->size = st.st_size;
+ }
+
+ fileinfo->mapsize = (fileinfo->size + ~pagemask) & pagemask;
+ map = mmap(NULL, fileinfo->mapsize, prot, MAP_SHARED, fd, 0);
+ if (map == MAP_FAILED)
+ goto err;
+
+ fileinfo->rd = map;
+ if (for_write)
+ fileinfo->wr = map;
+ return 0;
+
+err:
+ fileinfo->mapsize = NOT_MMAP;
+ return -1;
+}
+
+static inline bool unmap_file_mmap(const struct fileinfo *fileinfo)
+{
+ return !!munmap((void *)fileinfo->rd, fileinfo->mapsize);
+}
+
+#else /* not _POSIX_MAPPED_FILES */
+
+static inline int map_file_mmap(struct fileinfo *fileinfo, bool for_write)
+{
+ (void)fileinfo;
+ (void)for_write;
+ fileinfo->mapsize = NOT_MMAP;
+ return -1;
+}
+
+static inline int unmap_file_mmap(const struct fileinfo *fileinfo)
+{
+ abort(); /* Should never get called */
+}
+
+#endif /* not _POSIX_MAPPED_FILES */
+
+/* Simulate a memory mapping by loading the whole file into memory */
+
+static int map_file_stdio(struct fileinfo *fileinfo)
+{
+ const bool for_write = fileinfo->size != READ_FILE;
+ FILE *f = fileinfo->fp;
+ void *map = NULL;
+
+ if (for_write) {
+ fileinfo->rd = fileinfo->wr = calloc(fileinfo->size, 1);
+ if (!map)
+ goto err;
+ } else {
+ size_t size;
+
+ if (fseek(f, SEEK_END, 0))
+ goto err;
+
+ fileinfo->size = size = ftell(f);
+
+ if (fseek(f, SEEK_SET, 0))
+ goto err;
+
+ fileinfo->rd = map = malloc(size);
+ if (!map)
+ goto err;
+ if (fread(map, 1, size, f) != size || ferror(f))
+ goto err;
+ }
+ return 0;
+
+err:
+ if (fileinfo->rd)
+ free((void *)fileinfo->rd);
+ return -1;
+}
+
+static bool unmap_file_stdio(const struct fileinfo *fileinfo)
+{
+ bool err = false;
+
+ if (fileinfo->wr) {
+ err |= fwrite(fileinfo->wr, fileinfo->size, 1, fileinfo->fp)
+ != fileinfo->size;
+ err |= !!fflush(fileinfo->fp);
+ }
+ err |= !!ferror(fileinfo->fp);
+ free((void *)fileinfo->rd);
+ return err;
+}
+
+const struct fileinfo *map_file(const char *filename, size_t wrsize)
+{
+ const bool for_write = wrsize != READ_FILE;
+ const char * const openflags = for_write ? "w+b" : "rb";
+ const bool is_stdio = name_is_stdio(filename);
+ struct fileinfo *fileinfo;
+ int err;
+
+ fileinfo = calloc(sizeof *fileinfo, 1);
+ if (!fileinfo)
+ goto err;
+
+ if (is_stdio) {
+ fileinfo->fp = for_write ? stdout : stdin;
+ } else {
+ fileinfo->fp = fopen(filename, openflags);
+ if (!fileinfo->fp)
+ goto err;
+ }
+ fileinfo->filename = error_name(filename, for_write);
+
+ /* Will be overwritten for the read case */
+ fileinfo->size = wrsize;
+
+ err = map_file_mmap(fileinfo);
+ if (!err)
+ return fileinfo;
+
+ err = map_file_stdio(fileinfo);
+ if (!err)
+ return fileinfo;
+
+err:
+ if (fileinfo) {
+ if (fileinfo->fp && !is_stdio) {
+ fclose(fileinfo->fp);
+ if (for_write)
+ remove(filename);
+ }
+ free(fileinfo);
+ }
+ return NULL;
+}
+
+int unmap_file(const struct fileinfo *fileinfo, bool err)
+{
+ bool for_write, is_stdio;
+
+ if (!fileinfo)
+ return 0; /* Nothing to do */
+
+ for_write = !!fileinfo->wr;
+ is_stdio = fp_is_stdio(fileinfo->fp);
+
+ if (fileinfo->mapsize != NOT_MMAP)
+ err |= unmap_file_mmap(fileinfo);
+ else
+ err |= unmap_file_stdio(fileinfo);
+
+ err |= fileinfo->err;
+ if (!is_stdio) {
+ err |= !!fclose(fileinfo->fp);
+ if (err && for_write)
+ remove(fileinfo->filename);
+ }
+
+ free((void *)fileinfo);
+ return -(int)err;
+}
diff --git a/elf2exe/leint.h b/elf2exe/leint.h
new file mode 100644
index 0000000..446a793
--- /dev/null
+++ b/elf2exe/leint.h
@@ -0,0 +1,47 @@
+/*
+ * leint.h - handling of littleendian numbers
+ */
+
+#ifndef LEINT_H
+#define LEINT_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#if defined(_WIN32) || defined(__MSDOS__) \
+ || defined(__8086__) \
+ || defined(__i386__) || defined(__386__) \
+ || defined(__x86_64__) || defined(__amd64__) \
+ || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+ (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
+
+/* This assumes that any of these definitions guarantee a littleendian system */
+#ifndef le16toh
+static inline uint16_t htole16(uint16_t x) { return x; }
+static inline uint32_t htole32(uint32_t x) { return x; }
+static inline uint64_t htole64(uint64_t x) { return x; }
+static inline uint16_t le16toh(uint16_t x) { return x; }
+static inline uint32_t le32toh(uint32_t x) { return x; }
+static inline uint64_t le64toh(uint64_t x) { return x; }
+#endif
+
+#else
+
+/* Hopefully these are in here? */
+# include <endian.h>
+
+#endif
+
+#define getle(x) ((sizeof x) == 1 ? (uint8_t)(x) : \
+ (sizeof x) == 2 ? le16toh(x) : \
+ (sizeof x) == 4 ? le32toh(x) : \
+ (sizeof x) == 8 ? le64toh(x) : \
+ (abort(), 0))
+
+#define putle(p,v) ((sizeof *(p)) == 1 ? (*(p) = (uint8_t)(v)) : \
+ (sizeof *(p)) == 2 ? (*(p) = htole16(v)) : \
+ (sizeof *(p)) == 4 ? (*(p) = htole32(v)) : \
+ (sizeof *(p)) == 8 ? (*(p) = htole64(v)) : \
+ (abort(), 0))
+
+#endif /* LEINT_H */