From 482163f03a6ae25dc5f014d36418703f4935d282 Mon Sep 17 00:00:00 2001 From: Stas Sergeev Date: Thu, 27 Feb 2025 14:50:27 +0300 Subject: [PATCH 1/2] API v17: infra for pluggable ELF parsers [#23] This allows to stop adding crap (like new ELF loader per every new ELF format) and off-load that task into dosemu2 if he needs the direct ELF loading capability. Note that this parser can't help with loading an ELF intended for static linking, as the parser itself is invoked by the dynamic payload (which, in case of a static ELF, is embedded into that ELF, so can't call the parser to parse itself). --- include/libc/stubinfo.h | 6 ++- src/djdev64/djdev64.c | 5 ++- src/djdev64/include/djdev64/dj64init.h | 4 +- src/djdev64/include/djdev64/djdev64.h | 3 +- src/djdev64/stub/stub.c | 7 ++- src/libc/dj64/plt.S | 62 ++++++++++++++++++++++++-- src/libc/dj64/plt.h | 2 + src/libc/dj64/thunks.c | 57 +++++++++++++++-------- 8 files changed, 117 insertions(+), 29 deletions(-) diff --git a/include/libc/stubinfo.h b/include/libc/stubinfo.h index 3966abd0..a58f30f2 100644 --- a/include/libc/stubinfo.h +++ b/include/libc/stubinfo.h @@ -29,7 +29,9 @@ #define STUBINFO_FLAGS 0x8C #define STUBINFO_UENTRY 0x90 #define STUBINFO_CPL_FD 0x94 -#define STUBINFO_END 0x98 +#define STUBINFO_UPL_BASE 0x98 +#define STUBINFO_UPL_SIZE 0x9C +#define STUBINFO_END 0xA0 #ifndef __ASSEMBLER__ #include typedef struct { @@ -61,6 +63,8 @@ typedef struct { uint32_t flags; uint32_t uentry; int32_t cpl_fd; + uint32_t upl_base; + uint32_t upl_size; } _GO32_StubInfo; _Static_assert(sizeof(_GO32_StubInfo) == STUBINFO_END, "size mismatch"); diff --git a/src/djdev64/djdev64.c b/src/djdev64/djdev64.c index cb7c7d7e..9cea24ec 100644 --- a/src/djdev64/djdev64.c +++ b/src/djdev64/djdev64.c @@ -183,7 +183,10 @@ static int _djdev64_open(const char *path, const struct dj64_api *api, #endif #else #ifdef RTLD_DEEPBIND - dlh = emu_dlmopen(handles, path, RTLD_LOCAL | RTLD_NOW | RTLD_DEEPBIND, + if (flags & DJ64F_NOMANGLE) + dlh = dlopen(path, RTLD_LOCAL | RTLD_NOW | RTLD_DEEPBIND); + else + dlh = emu_dlmopen(handles, path, RTLD_LOCAL | RTLD_NOW | RTLD_DEEPBIND, &path2); #else fprintf(stderr, "RTLD_DEEPBIND not supported, use static linking\n"); diff --git a/src/djdev64/include/djdev64/dj64init.h b/src/djdev64/include/djdev64/dj64init.h index 4f4bb5be..f81d97ff 100644 --- a/src/djdev64/include/djdev64/dj64init.h +++ b/src/djdev64/include/djdev64/dj64init.h @@ -25,7 +25,7 @@ typedef int (dj64cdispatch_t)(int handle, int libid, int fn, unsigned esi, uint8_t *sp); -#define DJ64_API_VER 16 +#define DJ64_API_VER 17 #define DJ64_API_MIN_VER 16 enum { DJ64_PRINT_LOG, DJ64_PRINT_TERMINAL, DJ64_PRINT_SCREEN }; @@ -65,6 +65,8 @@ struct dj64_api { void (*free)(void *ptr); int (*elfload)(int num); int (*uget)(int handle); + char *(*elfparse64)(int num, uint32_t addr, uint32_t size, + uint32_t mem_base, uint32_t *r_size, uint32_t *r_entry); }; #define DJ64_INIT_ONCE_FN dj64init_once typedef int (dj64init_once_t)(const struct dj64_api *api, int api_ver); diff --git a/src/djdev64/include/djdev64/djdev64.h b/src/djdev64/include/djdev64/djdev64.h index e5887458..c7d48dbd 100644 --- a/src/djdev64/include/djdev64/djdev64.h +++ b/src/djdev64/include/djdev64/djdev64.h @@ -22,7 +22,8 @@ #include "djdev64/dj64init.h" /* first 16 bits are for internal use */ -#define DJ64F_DLMOPEN (1 << 16) +#define DJ64F_DLMOPEN (1 << 16) +#define DJ64F_NOMANGLE (1 << 17) int djdev64_open(const char *path, const struct dj64_api *api, int api_ver, unsigned flags); diff --git a/src/djdev64/stub/stub.c b/src/djdev64/stub/stub.c index 1e9615a3..894ac5ed 100644 --- a/src/djdev64/stub/stub.c +++ b/src/djdev64/stub/stub.c @@ -359,10 +359,13 @@ int djstub_main(int argc, char *argv[], char *envp[], if (va_size > MB) exit(EXIT_FAILURE); /* if we load 2 payloads, use larger estimate */ - if (dyn && (pl32 || BARE_STUB())) + if (dyn && (pl32 || BARE_STUB())) { stubinfo.initial_size = VA_SZ; - else + stubinfo.upl_base = va + MB; + stubinfo.upl_size = VA_SZ - MB; + } else { stubinfo.initial_size = max(va_size, 0x10000); + } info.size = PAGE_ALIGN(stubinfo.initial_size); /* allocate mem */ __dpmi_allocate_memory(&info); diff --git a/src/libc/dj64/plt.S b/src/libc/dj64/plt.S index 70eda0b8..4439ff56 100644 --- a/src/libc/dj64/plt.S +++ b/src/libc/dj64/plt.S @@ -26,10 +26,13 @@ __plt_open: .quad 0 PLT_OPEN_CMD = 0 PLT_CLOSE_CMD = 1 +PLT_ELOAD_CMD = 4 +PLT_ECLOSE_CMD = 5 __plt_call: .quad 0 __plt_ctrl: .quad 0 .global __plt_handle __plt_handle: .long 0 +elfload: .long 0 sel: .word 0 SHM_REQ_LEN = 0 @@ -184,6 +187,7 @@ plt_init: movl %edi, __plt_call movl %eax, __plt_ctrl + 4 movl %esi, __plt_ctrl + movl %fs:STUBINFO_FLAGS, %ecx testw $0x4080, %cx jnz 111f @@ -248,8 +252,9 @@ plt_init: movl %fs:STUBINFO_SELF_SIZE, %ecx movl %fs:STUBINFO_MEM_BASE, %edx movl %fs:STUBINFO_CPL_FD, %esi - movl $-1, %edi // user payload in mem buffer + pushl $DL_SET_SYMTAB call ctrl + popl %eax jc 2f /* free mem */ movl $0x502, %eax @@ -264,7 +269,44 @@ plt_init: ret 321: - // no payload found + // no payload found, try dynamic + movl $0, %eax + movl $PLT_ELOAD_CMD, %ebx + movl $0, %ecx // reserve + lcalll *__plt_open + jc 222f + movl %eax, __plt_handle + movl %es, %eax + movl %eax, __plt_call + 4 + movl %edi, __plt_call + movl %eax, __plt_ctrl + 4 + movl %esi, __plt_ctrl + /* resolve cpl */ + movl %fs:STUBINFO_FLAGS, %eax + movl $0, %ebx + movl $0, %ecx + movl %fs:STUBINFO_MEM_BASE, %edx + movl %fs:STUBINFO_CPL_FD, %esi + pushl $DL_SET_SYMTAB + call ctrl + popl %eax + jc 222f + /* resolve upl */ + movl $0, %eax + movl %fs:STUBINFO_UPL_BASE, %ebx + movl %fs:STUBINFO_UPL_SIZE, %ecx + movl %fs:STUBINFO_MEM_BASE, %edx + pushl $DL_ELFLOAD + call ctrl + popl %ecx + // failure is not fatal, just no upl + jc 555f + movl %eax, %fs:STUBINFO_UENTRY + call uplt_init +555: + movl $1, elfload + jmp 11b +222: movl $err2_str, %edx jmp perr @@ -289,33 +331,40 @@ perr: movl $0, %ecx movl %fs:STUBINFO_MEM_BASE, %edx movl %fs:STUBINFO_CPL_FD, %esi - movl $-1, %edi // no user payload + pushl $DL_SET_SYMTAB call ctrl + popl %eax jc 2b jmp 11b ctrl: + pushl %ebp + movl %esp, %ebp pushal movl %esp, %edx movl __plt_handle, %eax movl $0, %ebx movb $AUX_CORE, %bl movb $DL_API_VER, %bh - movl $DL_SET_SYMTAB, %ecx + movl 8(%ebp), %ecx movl %cs, %esi lcalll *__plt_ctrl testl %eax, %eax jnz 1f popal + popl %ebp clc ret 1: popal + popl %ebp stc ret .global plt_done plt_done: + cmpl $0, elfload + jne 1f // close lib movl __plt_handle, %eax movl $PLT_CLOSE_CMD, %ebx @@ -333,6 +382,11 @@ plt_done: movw shm_block2 + SHM_HANDLE + 2, %si int $0x31 ret +1: + movl __plt_handle, %eax + movl $PLT_ECLOSE_CMD, %ebx + lcalll *__plt_open + ret .bss diff --git a/src/libc/dj64/plt.h b/src/libc/dj64/plt.h index 5689299c..58a50e4b 100644 --- a/src/libc/dj64/plt.h +++ b/src/libc/dj64/plt.h @@ -1,2 +1,4 @@ #define DL_SET_SYMTAB 0 +#define DL_ELFLOAD 1 + #define DL_API_VER 1 diff --git a/src/libc/dj64/thunks.c b/src/libc/dj64/thunks.c index 7d62278e..7354f2de 100644 --- a/src/libc/dj64/thunks.c +++ b/src/libc/dj64/thunks.c @@ -260,6 +260,7 @@ static int dj64_ctrl(int handle, int libid, int fn, unsigned esi, uint8_t *sp) } switch (fn) { case DL_SET_SYMTAB: { + __label__ err; struct udisp *u = &udisps[handle]; uint32_t flags = regs->eax; int have_core = (flags & 0x4000); @@ -268,8 +269,6 @@ static int dj64_ctrl(int handle, int libid, int fn, unsigned esi, uint8_t *sp) uint32_t mem_base = regs->edx; int32_t cpl_fd = (regs->esi == (uint32_t)-1 ? -1 : dj64api->uget(regs->esi)); - int32_t upl_fd = (regs->edi == (uint32_t)-1 ? -1 : - dj64api->uget(regs->edi)); void *eh = NULL; int ret; @@ -302,23 +301,6 @@ static int dj64_ctrl(int handle, int libid, int fn, unsigned esi, uint8_t *sp) u->eops->close(eh); } else if (have_core) { return -1; - } else { - if (upl_fd != -1) { - eh = u->eops->open_dyn(upl_fd); - if (!eh) - return -1; - if (u->at) { - ret = process_athunks(u->at, mem_base, u->eops, eh); - if (ret) - goto err; - } - if (u->pt) { - ret = process_pthunks(u->pt, u->eops, eh); - if (ret) - goto err; - } - u->eops->close(eh); - } } if (!have_core) { if (cpl_fd == -1) @@ -341,6 +323,43 @@ static int dj64_ctrl(int handle, int libid, int fn, unsigned esi, uint8_t *sp) u->eops->close(eh); break; } + + case DL_ELFLOAD: { + __label__ err; + struct udisp *u = &udisps[handle]; + uint32_t addr = regs->ebx; + uint32_t size = regs->ecx; + uint32_t mem_base = regs->edx; + void *eh = NULL; + int ret; + uint32_t esize, entry; + char *elf = dj64api->elfparse64(0, addr, size, mem_base, &esize, + &entry); + if (!elf) + return -1; + eh = u->eops->open(elf, esize); + if (!eh) + goto err2; + if (u->at) { + ret = process_athunks(u->at, mem_base, u->eops, eh); + if (ret) + goto err; + } + if (u->pt) { + ret = process_pthunks(u->pt, u->eops, eh); + if (ret) + goto err; + } + u->eops->close(eh); + dj64api->free(elf); + regs->eax = entry; + return 0; + err: + u->eops->close(eh); + err2: + dj64api->free(elf); + break; + } } return -1; } From 05fa36c6e20c2cc506c24ec71e89ae60e1810796 Mon Sep 17 00:00:00 2001 From: Stas Sergeev Date: Fri, 28 Feb 2025 00:45:14 +0300 Subject: [PATCH 2/2] add funcs to implement elf64 parsers [closes #23] To simplify the life of dosemu2 developers, provide the needed ELF parsing functions. --- src/djdev64/elf.c | 33 ++++++++++- src/djdev64/elf64.c | 82 +++++++++++++++++++++++++++ src/djdev64/elf_priv.h | 2 - src/djdev64/include/djdev64/djdev64.h | 6 ++ src/djdev64/makefile | 2 +- 5 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 src/djdev64/elf64.c diff --git a/src/djdev64/elf.c b/src/djdev64/elf.c index 9f7de8ed..14406f1a 100644 --- a/src/djdev64/elf.c +++ b/src/djdev64/elf.c @@ -26,6 +26,7 @@ #include #include #include +#include "djdev64/djdev64.h" #include "elf_priv.h" struct elfstate { @@ -34,6 +35,7 @@ struct elfstate { Elf_Scn *symtab_scn; GElf_Shdr symtab_shdr; char *addr; + int need_unmap; }; static int do_getsym(struct elfstate *state, const char *name, GElf_Sym *r_sym) @@ -87,6 +89,7 @@ static int do_elf_open(char *addr, uint32_t size, struct elfstate *ret) ret->elf = elf; ret->symtab_scn = scn; ret->symtab_shdr = shdr; + ret->addr = addr; return 0; err: @@ -125,7 +128,7 @@ void *djelf_open_dyn(int fd) return NULL; ret = (struct elfstate *)malloc(sizeof(*ret)); *ret = es; - ret->addr = addr; + ret->need_unmap = 1; return ret; } @@ -134,7 +137,7 @@ void djelf_close(void *arg) struct elfstate *state = (struct elfstate *)arg; elf_end(state->elf); - if (state->addr) + if (state->need_unmap) munmap(state->addr, state->mapsize); free(state); } @@ -152,3 +155,29 @@ uint32_t djelf_getsymoff(void *arg, const char *name) struct elfstate *state = (struct elfstate *)arg; return do_getsymoff(state, name); } + +int djelf_reloc(void *arg, char *addr, uint32_t size, uint32_t va, + uint32_t *r_entry) +{ + struct elfstate *state = (struct elfstate *)arg; + GElf_Ehdr ehdr; + GElf_Phdr phdr; + int offs; + int i; + + gelf_getehdr(state->elf, &ehdr); + for (i = 0; i < ehdr.e_phnum; i++) { + gelf_getphdr(state->elf, i, &phdr); + if (phdr.p_type != PT_LOAD) + continue; + offs = phdr.p_vaddr - va; + if (offs < 0 || offs + phdr.p_memsz > size) + return -1; + memcpy(addr + offs, state->addr + phdr.p_offset, phdr.p_filesz); + if (phdr.p_memsz > phdr.p_filesz) + memset(addr + offs + phdr.p_filesz, 0, phdr.p_memsz - + phdr.p_filesz); + } + *r_entry = ehdr.e_entry; + return 0; +} diff --git a/src/djdev64/elf64.c b/src/djdev64/elf64.c new file mode 100644 index 00000000..d9263da4 --- /dev/null +++ b/src/djdev64/elf64.c @@ -0,0 +1,82 @@ +/* + * ELF64 parser for dj64dev. + * Copyright (C) 2025 @stsp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "djdev64/djdev64.h" + +static const char *sec_name = ".dj64startup"; + +char *djelf64_parse(const char *path, uint32_t *r_size) +{ + int fd; + Elf *e; + char *ret; + char *name; + Elf_Scn *scn; + GElf_Shdr shdr; + Elf_Data *data; + size_t shstrndx; + + if (elf_version(EV_CURRENT) == EV_NONE) + return NULL; + if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) + return NULL; + if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) + goto err1; + if (elf_kind(e) != ELF_K_ELF) + goto err2; + if (elf_getshdrstrndx(e, &shstrndx) != 0) + goto err2; + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + gelf_getshdr(scn, &shdr); + if (shdr.sh_type != SHT_PROGBITS) + continue; + name = elf_strptr(e, shstrndx, shdr.sh_name); + if (name && strcmp(name, sec_name) == 0) + break; + } + if (!scn) { + fprintf(stderr, "dj64-specific sections not found in %s\n", path); + goto err2; + } + data = elf_getdata(scn, NULL); + if (!data) { + fprintf(stderr, "failed to get section data for %s\n", path); + goto err2; + } + close(fd); + *r_size = data->d_size; + ret = malloc(data->d_size); + memcpy(ret, data->d_buf, data->d_size); + elf_end(e); + return ret; + +err2: + elf_end(e); +err1: + close(fd); + return NULL; +} diff --git a/src/djdev64/elf_priv.h b/src/djdev64/elf_priv.h index 93d875c1..c42879cd 100644 --- a/src/djdev64/elf_priv.h +++ b/src/djdev64/elf_priv.h @@ -1,6 +1,4 @@ #include -void *djelf_open(char *addr, uint32_t size); void *djelf_open_dyn(int fd); -void djelf_close(void *arg); uint32_t djelf_getsymoff(void *arg, const char *name); diff --git a/src/djdev64/include/djdev64/djdev64.h b/src/djdev64/include/djdev64/djdev64.h index c7d48dbd..4ae862ad 100644 --- a/src/djdev64/include/djdev64/djdev64.h +++ b/src/djdev64/include/djdev64/djdev64.h @@ -35,4 +35,10 @@ void djdev64_close(int handle); int djdev64_exec(const char *path, unsigned flags, int argc, char **argv); +char *djelf64_parse(const char *path, uint32_t *r_size); +void *djelf_open(char *addr, uint32_t size); +void djelf_close(void *arg); +int djelf_reloc(void *arg, char *addr, uint32_t size, uint32_t va, + uint32_t *r_entry); + #endif diff --git a/src/djdev64/makefile b/src/djdev64/makefile index 892c4d68..84cb41a6 100644 --- a/src/djdev64/makefile +++ b/src/djdev64/makefile @@ -1,7 +1,7 @@ TOP = ../.. prefix ?= /usr/local PKG_CONFIG ?= pkg-config -SOURCES = djdev64.c elf.c djexec.c +SOURCES = djdev64.c elf.c djexec.c elf64.c OBJECTS = $(SOURCES:.c=.o) LIBELF := $(shell $(PKG_CONFIG) --silence-errors --libs libelf) ifeq ($(LIBELF),)