diff options
author | Marcus Comstedt | 2004-08-22 21:47:20 +0000 |
---|---|---|
committer | Marcus Comstedt | 2004-08-22 21:47:20 +0000 |
commit | 45ac190548b0b0f33c8dbc391cf14869862b1dac (patch) | |
tree | da9142f148ae464150cd32d94655b3a259ddbc3f | |
parent | ba39d7a3dbe8cfce4293838e87c1ff6caa1a27d6 (diff) | |
download | scummvm-rg350-45ac190548b0b0f33c8dbc391cf14869862b1dac.tar.gz scummvm-rg350-45ac190548b0b0f33c8dbc391cf14869862b1dac.tar.bz2 scummvm-rg350-45ac190548b0b0f33c8dbc391cf14869862b1dac.zip |
Support dynamic plugins on Dreamcast.
svn-id: r14689
-rw-r--r-- | backends/dc/.cvsignore | 1 | ||||
-rw-r--r-- | backends/dc/Makefile | 16 | ||||
-rw-r--r-- | backends/dc/cache.S | 38 | ||||
-rw-r--r-- | backends/dc/dcloader.cpp | 433 | ||||
-rw-r--r-- | backends/dc/dcloader.h | 64 | ||||
-rw-r--r-- | backends/dc/plugin.syms | 8 | ||||
-rw-r--r-- | backends/dc/plugin.x | 55 |
7 files changed, 614 insertions, 1 deletions
diff --git a/backends/dc/.cvsignore b/backends/dc/.cvsignore index 5b9e0fdb54..124b6cc6d7 100644 --- a/backends/dc/.cvsignore +++ b/backends/dc/.cvsignore @@ -14,3 +14,4 @@ common base backends scummvm.elf +*.plg diff --git a/backends/dc/Makefile b/backends/dc/Makefile index 1fe5fd1a03..8c61fdead1 100644 --- a/backends/dc/Makefile +++ b/backends/dc/Makefile @@ -2,9 +2,12 @@ ronindir = /usr/local/ronin +# BUILD_PLUGINS = 1 + srcdir = ../.. VPATH = $(srcdir) +CC = sh-elf-gcc -ml -m4-single-only CXX = sh-elf-g++ -ml -m4-single-only CXXFLAGS= -O3 -Wno-multichar -funroll-loops -fschedule-insns2 -fomit-frame-pointer -fdelete-null-pointer-checks -fno-exceptions DEFINES = -D__DC__ -DNONSTANDARD_PORT @@ -12,6 +15,10 @@ LDFLAGS = -Wl,-Ttext,0x8c010000 -nostartfiles $(ronindir)/lib/crt0.o INCLUDES= -I./ -I$(srcdir) -I$(srcdir)/common -I$(ronindir)/include/ LIBS = -L$(ronindir)/lib -lronin-netcd -lz -lm EXECUTABLE = scummvm.elf +PLUGIN_PREFIX = +PLUGIN_SUFFIX = .plg +PLUGIN_EXTRA_DEPS = plugin.x plugin.syms scummvm.elf +PLUGIN_LDFLAGS = -nostartfiles -Wl,-q,-Tplugin.x,--just-symbols,scummvm.elf,--retain-symbols-file,plugin.syms -L$(ronindir)/lib MKDIR = mkdir -p RM = rm -f RM_REC = rm -rf @@ -19,10 +26,17 @@ AR = sh-elf-ar cru RANLIB = sh-elf-ranlib HAVE_GCC3 = true +ifdef BUILD_PLUGINS +DEFINES += -DDYNAMIC_MODULES +endif + OBJS := dcmain.o time.o display.o audio.o input.o selector.o icon.o \ - label.o vmsave.o softkbd.o + label.o vmsave.o softkbd.o dcloader.o cache.o MODULE_DIRS += . include $(srcdir)/Makefile.common +scummvm.bin : scummvm.elf + sh-elf-objcopy -S -R .stack -O binary $< $@ + diff --git a/backends/dc/cache.S b/backends/dc/cache.S new file mode 100644 index 0000000000..1af1678ced --- /dev/null +++ b/backends/dc/cache.S @@ -0,0 +1,38 @@ + + .globl _flush_instruction_cache + + .align 2 + + ! Flush the SH instruction cache + +_flush_instruction_cache: + mova fcc,r0 + mov.l p2_mask,r1 + or r1,r0 + jmp @r0 + nop + nop +fcc: + mov.l ccr_addr,r0 + mov.l ccr_data,r1 + mov.l r1,@r0 + nop + nop + nop + nop + nop + nop + nop + nop + rts + nop + + .align 2 + +p2_mask: + .long 0xa0000000 +ccr_addr: + .long 0xff00001c +ccr_data: + .word 0x0905 + diff --git a/backends/dc/dcloader.cpp b/backends/dc/dcloader.cpp new file mode 100644 index 0000000000..b069255bb7 --- /dev/null +++ b/backends/dc/dcloader.cpp @@ -0,0 +1,433 @@ +/* ScummVM - Scumm Interpreter + * Dreamcast port + * Copyright (C) 2002-2004 Marcus Comstedt + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include <stdafx.h> +#include <ronin/ronin.h> +#include <string.h> +#include <stdarg.h> + +#include "dcloader.h" + +#ifdef DL_DEBUG +#define DBG(...) reportf(__VA_ARGS__) +#else +#define DBG(...) 0 +#endif + + +/* ELF stuff */ + +typedef unsigned short Elf32_Half, Elf32_Section; +typedef unsigned long Elf32_Word, Elf32_Addr, Elf32_Off; +typedef signed long Elf32_Sword; +typedef Elf32_Half Elf32_Versym; + +#define EI_NIDENT (16) +#define ELFMAG "\177ELF\1\1" +#define SELFMAG 6 + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +typedef struct +{ + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + + + +extern "C" void flush_instruction_cache(); + +static void purge_copyback() +{ + int i; + for(i=0; i!=(1<<14); i+=(1<<5)) + *(volatile unsigned int *)(0xf4000000+i) &= ~3; +} + + +void DLObject::seterror(const char *fmt, ...) +{ + if(errbuf) { + va_list va; + va_start(va, fmt); + vsnprintf(errbuf, MAXDLERRLEN, fmt, va); + va_end(va); + } +} + +void DLObject::discard_symtab() +{ + free(symtab); + free(strtab); + symtab = NULL; + strtab = NULL; + symbol_cnt = 0; +} + +void DLObject::unload() +{ + discard_symtab(); + free(segment); + segment = NULL; +} + +bool DLObject::relocate(int fd, unsigned long offset, unsigned long size) +{ + Elf32_Rela *rela; + + if(!(rela = (Elf32_Rela *)malloc(size))) { + seterror("Out of memory."); + return false; + } + + if(lseek(fd, offset, SEEK_SET)<0 || + read(fd, rela, size) != size) { + seterror("Relocation table load failed."); + free(rela); + return false; + } + + int cnt = size / sizeof(*rela); + for(int i=0; i<cnt; i++) { + + Elf32_Sym *sym = (Elf32_Sym *)(((char *)symtab)+(rela[i].r_info>>4)); + + void *target = ((char *)segment)+rela[i].r_offset; + + switch(rela[i].r_info & 0xf) { + case 1: /* DIR32 */ + if(sym->st_shndx < 0xff00) + *(unsigned long *)target += (unsigned long)segment; + break; + default: + seterror("Unknown relocation type %d.", rela[i].r_info & 0xf); + free(rela); + return false; + } + + } + + free(rela); + return true; +} + + +bool DLObject::load(int fd) +{ + Elf32_Ehdr ehdr; + Elf32_Phdr phdr; + Elf32_Shdr *shdr; + int symtab_sect = -1; + + if(read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr) || + memcmp(ehdr.e_ident, ELFMAG, SELFMAG) || + ehdr.e_type != 2 || ehdr.e_machine != 42 || + ehdr.e_phentsize < sizeof(phdr) || ehdr.e_shentsize != sizeof(*shdr) || + ehdr.e_phnum != 1) { + seterror("Invalid file type."); + return false; + } + + DBG("phoff = %d, phentsz = %d, phnum = %d\n", + ehdr.e_phoff, ehdr.e_phentsize, ehdr.e_phnum); + + if(lseek(fd, ehdr.e_phoff, SEEK_SET)<0 || + read(fd, &phdr, sizeof(phdr)) != sizeof(phdr)) { + seterror("Program header load failed."); + return false; + } + + if(phdr.p_type != 1 || phdr.p_vaddr != 0 || phdr.p_paddr != 0 || + phdr.p_filesz > phdr.p_memsz) { + seterror("Invalid program header."); + return false; + } + + DBG("offs = %d, filesz = %d, memsz = %d, align = %d\n", + phdr.p_offset, phdr.p_filesz, phdr.p_memsz, phdr.p_align); + + if(!(segment = memalign(phdr.p_align, phdr.p_memsz))) { + seterror("Out of memory."); + return false; + } + + DBG("segment @ %p\n", segment); + + if(lseek(fd, phdr.p_offset, SEEK_SET)<0 || + read(fd, segment, phdr.p_filesz) != phdr.p_filesz) { + seterror("Segment load failed."); + return false; + } + + DBG("shoff = %d, shentsz = %d, shnum = %d\n", + ehdr.e_shoff, ehdr.e_shentsize, ehdr.e_shnum); + + if(!(shdr = (Elf32_Shdr *)malloc(ehdr.e_shnum * sizeof(*shdr)))) { + seterror("Out of memory."); + return false; + } + + if(lseek(fd, ehdr.e_shoff, SEEK_SET)<0 || + read(fd, shdr, ehdr.e_shnum * sizeof(*shdr)) != + ehdr.e_shnum * sizeof(*shdr)) { + seterror("Section headers load failed."); + free(shdr); + return false; + } + + for(int i=0; i<ehdr.e_shnum; i++) { + DBG("Section %d: type = %d, size = %d, entsize = %d, link = %d\n", + i, shdr[i].sh_type, shdr[i].sh_size, shdr[i].sh_entsize, shdr[i].sh_link); + if(shdr[i].sh_type == 2 && shdr[i].sh_entsize == sizeof(Elf32_Sym) && + shdr[i].sh_link < ehdr.e_shnum && shdr[shdr[i].sh_link].sh_type == 3 && + symtab_sect < 0) + symtab_sect = i; + } + + if(symtab_sect < 0) { + seterror("No symbol table."); + free(shdr); + return false; + } + + if(!(symtab = malloc(shdr[symtab_sect].sh_size))) { + seterror("Out of memory."); + free(shdr); + return false; + } + + if(lseek(fd, shdr[symtab_sect].sh_offset, SEEK_SET)<0 || + read(fd, symtab, shdr[symtab_sect].sh_size) != shdr[symtab_sect].sh_size){ + seterror("Symbol table load failed."); + free(shdr); + return false; + } + + if(!(strtab = (char *)malloc(shdr[shdr[symtab_sect].sh_link].sh_size))) { + seterror("Out of memory."); + free(shdr); + return false; + } + + if(lseek(fd, shdr[shdr[symtab_sect].sh_link].sh_offset, SEEK_SET)<0 || + read(fd, strtab, shdr[shdr[symtab_sect].sh_link].sh_size) != + shdr[shdr[symtab_sect].sh_link].sh_size){ + seterror("Symbol table strings load failed."); + free(shdr); + return false; + } + + symbol_cnt = shdr[symtab_sect].sh_size / sizeof(Elf32_Sym); + DBG("Loaded %d symbols.\n", symbol_cnt); + + Elf32_Sym *s = (Elf32_Sym *)symtab; + for(int c = symbol_cnt; c--; s++) + if(s->st_shndx < 0xff00) + s->st_value += (Elf32_Addr)segment; + + for(int i=0; i<ehdr.e_shnum; i++) + if(shdr[i].sh_type == 4 && shdr[i].sh_entsize == sizeof(Elf32_Rela) && + shdr[i].sh_link == symtab_sect) + if(!relocate(fd, shdr[i].sh_offset, shdr[i].sh_size)) { + free(shdr); + return false; + } + + free(shdr); + + return true; +} + +bool DLObject::open(const char *path) +{ + int fd; + void *ctors_start, *ctors_end; + + DBG("open(\"%s\")\n", path); + + if((fd = ::open(path, O_RDONLY))<0) { + seterror("%s not found.", path); + return false; + } + + if(!load(fd)) { + ::close(fd); + unload(); + return false; + } + + ::close(fd); + + purge_copyback(); + flush_instruction_cache(); + + ctors_start = symbol("__plugin_ctors"); + ctors_end = symbol("__plugin_ctors_end"); + dtors_start = symbol("__plugin_dtors"); + dtors_end = symbol("__plugin_dtors_end"); + + if(ctors_start == NULL || ctors_end == NULL || dtors_start == NULL || + dtors_end == NULL) { + seterror("Missing ctors/dtors."); + dtors_start = dtors_end = NULL; + unload(); + return false; + } + + DBG("Calling constructors.\n"); + for(void (**f)(void) = (void (**)(void))ctors_start; f != ctors_end; f++) + (**f)(); + + DBG("%s opened ok.\n", path); + return true; +} + +bool DLObject::close() +{ + if(dtors_start != NULL && dtors_end != NULL) + for(void (**f)(void) = (void (**)(void))dtors_start; f != dtors_end; f++) + (**f)(); + dtors_start = dtors_end = NULL; + unload(); + return true; +} + +void *DLObject::symbol(const char *name) +{ + DBG("symbol(\"%s\")\n", name); + + if(symtab == NULL || strtab == NULL || symbol_cnt < 1) { + seterror("No symbol table loaded."); + return NULL; + } + + Elf32_Sym *s = (Elf32_Sym *)symtab; + for(int c = symbol_cnt; c--; s++) + if((s->st_info>>4 == 1 || s->st_info>>4 == 2) && + strtab[s->st_name] == '_' && !strcmp(name, strtab+s->st_name+1)) { + DBG("=> %p\n", (void*)s->st_value); + return (void*)s->st_value; + } + + seterror("Symbol \"%s\" not found.", name); + return NULL; +} + + +static char dlerr[MAXDLERRLEN]; + +void *dlopen(const char *filename, int flags) +{ + DLObject *obj = new DLObject(dlerr); + if(obj->open(filename)) + return (void *)obj; + delete obj; + return NULL; +} + +int dlclose(void *handle) +{ + DLObject *obj = (DLObject *)handle; + if(obj == NULL) { + strcpy(dlerr, "Handle is NULL."); + return -1; + } + if(obj->close()) { + delete obj; + return 0; + } + return -1; +} + +void *dlsym(void *handle, const char *symbol) +{ + if(handle == NULL) { + strcpy(dlerr, "Handle is NULL."); + return NULL; + } + return ((DLObject *)handle)->symbol(symbol); +} + +const char *dlerror() +{ + return dlerr; +} + +void dlforgetsyms(void *handle) +{ + if(handle != NULL) + ((DLObject *)handle)->discard_symtab(); +} diff --git a/backends/dc/dcloader.h b/backends/dc/dcloader.h new file mode 100644 index 0000000000..fa33475380 --- /dev/null +++ b/backends/dc/dcloader.h @@ -0,0 +1,64 @@ +/* ScummVM - Scumm Interpreter + * Dreamcast port + * Copyright (C) 2002-2004 Marcus Comstedt + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#ifndef DC_DCLOADER_H +#define DC_DCLOADER_H + +#include "dc.h" + +#define MAXDLERRLEN 80 + +class DLObject { + private: + char *errbuf; /* For error messages, at least MAXDLERRLEN in size */ + + void *segment, *symtab; + char *strtab; + int symbol_cnt; + void *dtors_start, *dtors_end; + + void seterror(const char *fmt, ...); + void unload(); + bool relocate(int fd, unsigned long offset, unsigned long size); + bool load(int fd); + + public: + bool open(const char *path); + bool close(); + void *symbol(const char *name); + void discard_symtab(); + + DLObject(char *_errbuf = NULL) : errbuf(_errbuf), segment(NULL),symtab(NULL), + strtab(NULL), symbol_cnt(0), dtors_start(NULL), dtors_end(NULL) {} +}; + +#define RTLD_LAZY 0 + +extern "C" { + void *dlopen(const char *filename, int flags); + int dlclose(void *handle); + void *dlsym(void *handle, const char *symbol); + const char *dlerror(); + void dlforgetsyms(void *handle); +}; + +#endif diff --git a/backends/dc/plugin.syms b/backends/dc/plugin.syms new file mode 100644 index 0000000000..25ac0bb496 --- /dev/null +++ b/backends/dc/plugin.syms @@ -0,0 +1,8 @@ +_PLUGIN_createEngine +_PLUGIN_detectGames +_PLUGIN_getSupportedGames +_PLUGIN_name +___plugin_ctors +___plugin_ctors_end +___plugin_dtors +___plugin_dtors_end diff --git a/backends/dc/plugin.x b/backends/dc/plugin.x new file mode 100644 index 0000000000..80550b0299 --- /dev/null +++ b/backends/dc/plugin.x @@ -0,0 +1,55 @@ +OUTPUT_FORMAT("elf32-shl", "elf32-shl", "elf32-shl") +OUTPUT_ARCH(sh) +SECTIONS +{ + . = 0; + .text : + { + *(.text .stub .text.* .gnu.linkonce.t.*) + *(.gnu.warning) + } =0 + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) } + .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .eh_frame : { KEEP (*(.eh_frame)) } + .gcc_except_table : { *(.gcc_except_table) } + .ctors : + { + ___plugin_ctors = .; + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + ___plugin_ctors_end = .; + } + .dtors : + { + ___plugin_dtors = .; + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + ___plugin_dtors_end = .; + } + .sdata : + { + *(.sdata .sdata.* .gnu.linkonce.s.*) + } + .sbss : + { + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + } + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + } +} |