Skip to content

Elfparse #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion include/libc/stubinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <stdint.h>
typedef struct {
Expand Down Expand Up @@ -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");
Expand Down
5 changes: 4 additions & 1 deletion src/djdev64/djdev64.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
33 changes: 31 additions & 2 deletions src/djdev64/elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <libelf.h>
#include <gelf.h>
#include <assert.h>
#include "djdev64/djdev64.h"
#include "elf_priv.h"

struct elfstate {
Expand All @@ -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)
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}

Expand All @@ -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);
}
Expand All @@ -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;
}
82 changes: 82 additions & 0 deletions src/djdev64/elf64.c
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
*
*/
#include <libelf.h>
#include <gelf.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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;
}
2 changes: 0 additions & 2 deletions src/djdev64/elf_priv.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#include <stdint.h>

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);
4 changes: 3 additions & 1 deletion src/djdev64/include/djdev64/dj64init.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 };

Expand Down Expand Up @@ -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);
Expand Down
9 changes: 8 additions & 1 deletion src/djdev64/include/djdev64/djdev64.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
2 changes: 1 addition & 1 deletion src/djdev64/makefile
Original file line number Diff line number Diff line change
@@ -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),)
Expand Down
7 changes: 5 additions & 2 deletions src/djdev64/stub/stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
62 changes: 58 additions & 4 deletions src/libc/dj64/plt.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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
Expand All @@ -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

Expand Down
2 changes: 2 additions & 0 deletions src/libc/dj64/plt.h
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
#define DL_SET_SYMTAB 0
#define DL_ELFLOAD 1

#define DL_API_VER 1
Loading