summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2014-01-22 23:22:28 -0800
committerH. Peter Anvin <hpa@linux.intel.com>2014-01-22 23:22:28 -0800
commit6d1312ad64999f739b5f9f99645380363bb4abc2 (patch)
treeb28f1e3742a1ce5b2f1205f7987c00122840efb6
parent2a6e3246a035116f1de434c0b18e1a3909bf04f7 (diff)
downloadtest16-6d1312ad64999f739b5f9f99645380363bb4abc2.tar.gz
test16-6d1312ad64999f739b5f9f99645380363bb4abc2.tar.xz
test16-6d1312ad64999f739b5f9f99645380363bb4abc2.zip
run16: load ELF files with proper memory protection
Instead of binary files, run ELF files; this allows us to do memory protection and actually knowing the load and entry point addresses. Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r--Makefile7
-rw-r--r--run16.c164
2 files changed, 137 insertions, 34 deletions
diff --git a/Makefile b/Makefile
index 2be6f81..a00c47b 100644
--- a/Makefile
+++ b/Makefile
@@ -28,9 +28,8 @@ TEST16S = $(wildcard test16/*.S)
TEST16C = $(wildcard test16/*.c)
TEST16O = $(TEST16C:.c=.o) $(TEST16S:.S=.o)
TEST16ELF = $(TEST16O:.o=.elf)
-TEST16BIN = $(TEST16O:.o=.bin)
-all : run16 $(TEST16BIN)
+all : run16 $(TEST16ELF)
lib16/%.o: lib16/%.S
$(CC) $(S16FLAGS) -c -o $@ $<
@@ -48,13 +47,9 @@ test16/%.o: test16/%.S
test16/%.o: test16/%.c
$(CC) $(C16FLAGS) -c -o $@ $<
-.PRECIOUS: test16/%.elf
test16/%.elf: test16/%.o lib16/lib16.a
$(LD) $(LD16FLAGS) -o $@ $^
-test16/%.bin: test16/%.elf
- $(OBJCOPY) -O binary $< $@
-
run16: run16.c
$(CC) $(CFLAGS) -o $@ $<
diff --git a/run16.c b/run16.c
index 518419e..77713b8 100644
--- a/run16.c
+++ b/run16.c
@@ -14,12 +14,15 @@
#include <string.h>
#include <asm/unistd.h>
#include <sys/mman.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
#include "include16/sys16.h"
static unsigned char toybox[65536] __attribute__((aligned(4096)));
-
-#define LOAD_ADDR 0x1000
+static int toyprot[65536/4096];
static void setup_ldt(void)
{
@@ -50,20 +53,7 @@ static void setup_ldt(void)
syscall(SYS_modify_ldt, 1, &data16_desc, sizeof data16_desc);
}
-static void load_file(const char *file, unsigned int trampoline_addr)
-{
- FILE *f = fopen(file, "rb");
-
- if (!f) {
- fprintf(stderr, "run16: could not open: %s\n", file);
- exit(127);
- }
-
- fread(toybox + LOAD_ADDR, 1, trampoline_addr - LOAD_ADDR, f);
- fclose(f);
-}
-
-static void jump16(uint32_t offset)
+static void jump16(uint32_t offset, uint32_t start)
{
struct far_ptr {
uint32_t offset;
@@ -73,22 +63,137 @@ static void jump16(uint32_t offset)
target.offset = offset;
target.segment = 7;
- asm volatile("ljmpl *%0" : : "m" (target), "a" (LOAD_ADDR));
+ asm volatile("ljmpl *%0" : : "m" (target), "a" (start));
}
#define ALIGN_UP(x,y) (((x) + (y) - 1) & ((y) - 1))
+#if PROT_NONE != 0
+# error "This code assumes PROT_NONE == 0"
+#endif
+
+static int load_elf(const char *file, uint32_t *start)
+{
+ int fd;
+ struct stat st;
+ const char *fp; /* File data pointer */
+ const Elf32_Ehdr *eh;
+ const Elf32_Phdr *ph;
+ int rv = -1;
+ int i;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0 || fstat(fd, &st))
+ goto bail;
+
+ fp = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (fp == MAP_FAILED)
+ goto bail;
+
+ errno = ENOEXEC; /* If no other error... */
+
+ /* Must be long enough */
+ if (st.st_size < sizeof(Elf32_Ehdr))
+ goto bail;
+
+ eh = (const Elf32_Ehdr *)fp;
+
+ /* Must be ELF, 32-bit, littleendian, version 1 */
+ if (memcmp(eh->e_ident, "\x7f" "ELF\1\1\1", 6))
+ goto bail;
+
+ if (eh->e_machine != EM_386)
+ goto bail;
+
+ if (eh->e_version != EV_CURRENT)
+ goto bail;
+
+ if (eh->e_ehsize < sizeof(Elf32_Ehdr) || eh->e_ehsize >= st.st_size)
+ goto bail;
+
+ if (eh->e_phentsize < sizeof(Elf32_Phdr))
+ goto bail;
+
+ if (!eh->e_phnum)
+ goto bail;
+
+ if (eh->e_phoff + eh->e_phentsize * eh->e_phnum > st.st_size)
+ goto bail;
+
+ if (eh->e_entry > sizeof toybox)
+ goto bail;
+
+ *start = eh->e_entry;
+
+ ph = (const Elf32_Phdr *)(fp + eh->e_phoff);
+
+ for (i = 0; i < eh->e_phnum; i++) {
+ uint32_t addr = ph->p_paddr;
+ uint32_t msize = ph->p_memsz;
+ uint32_t dsize = ph->p_filesz;
+ int flags, page;
+
+ ph = (const Elf32_Phdr *)((const char *)ph +
+ i*eh->e_phentsize);
+
+ if (msize && (ph->p_type == PT_LOAD ||
+ ph->p_type == PT_PHDR)) {
+ /*
+ * This loads at p_paddr, which is arguably
+ * the correct semantics (LMA). The SysV spec
+ * says that SysV loads at p_vaddr (and thus
+ * Linux does, too); that is, however, a major
+ * brainfuckage in the spec.
+ */
+ if (msize < dsize)
+ dsize = msize;
+
+ if (addr >= sizeof toybox ||
+ addr + msize > sizeof toybox)
+ goto bail;
+
+ if (dsize) {
+ if (ph->p_offset >= st.st_size ||
+ ph->p_offset + dsize > st.st_size)
+ goto bail;
+ memcpy(toybox + addr, fp + ph->p_offset,
+ dsize);
+ }
+
+ memset(toybox + addr + dsize, 0, msize - dsize);
+
+ flags = ((ph->p_flags & PF_X) ? PROT_EXEC : 0) |
+ ((ph->p_flags & PF_W) ? PROT_WRITE : 0) |
+ ((ph->p_flags & PF_R) ? PROT_READ : 0);
+
+ for (page = addr & ~0xfff; page < addr + msize;
+ page += 4096)
+ toyprot[page >> 12] |= flags;
+ }
+ }
+
+ rv = 0; /* Success! */
+
+bail:
+ if (fd >= 0)
+ close(fd);
+ return rv;
+}
+
static void run(const char *file, char **argv)
{
unsigned int trampoline_len;
const void *trampoline;
unsigned char *entrypoint;
- uint32_t offset;
+ uint32_t offset, start;
struct system_struct *SYS;
char **argp;
uint32_t argptr, argstr;
+ int i;
- setup_ldt();
+ /* Read the input file */
+ if (load_elf(file, &start))
+ goto barf;
/* No points for style */
asm volatile(
@@ -121,7 +226,7 @@ static void run(const char *file, char **argv)
/* Set up the system structure */
SYS = (struct system_struct *)(toybox + SYS_STRUCT_ADDR);
SYS->seg_base = (unsigned int)toybox;
- for (argp = argv; argp; argp++)
+ for (argp = argv; *argp; argp++)
SYS->argc++;
argptr = ALIGN_UP(SYS_STRUCT_ADDR + sizeof(struct system_struct), 4);
@@ -129,7 +234,7 @@ static void run(const char *file, char **argv)
SYS->argv = argptr;
- for (argp = argv; argp; argp++) {
+ for (argp = argv; *argp; argp++) {
int len = strlen(*argp)+1;
if (argstr + len >= offset) {
@@ -144,17 +249,20 @@ static void run(const char *file, char **argv)
argstr += len;
}
- load_file(file, offset);
+ /* Set up page protection */
+ toyprot[0xE] |= PROT_READ | PROT_WRITE; /* Stack page */
+ toyprot[0xF] |= PROT_READ | PROT_EXEC; /* System page & trampoline */
- /* Catch null pointer references */
- mprotect(toybox, LOAD_ADDR, PROT_NONE);
+ for (i = 0; i < 16; i++)
+ mprotect(toybox + i*4096, 4096, toyprot[i]);
- /* The rest is fully permissive */
- mprotect(toybox + LOAD_ADDR, sizeof toybox - LOAD_ADDR,
- PROT_READ|PROT_WRITE|PROT_EXEC);
+ setup_ldt();
- jump16(offset);
+ jump16(offset, start);
abort(); /* Does not return */
+barf:
+ fprintf(stderr, "run16: %s: %s\n", file, strerror(errno));
+ exit(127);
}
int main(int argc, char *argv[])