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/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/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..4ae862ad 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); @@ -34,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),) 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; }