mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-16 14:50:17 +00:00
e3ca4afb59
svn-id: r39590
444 lines
12 KiB
C++
444 lines
12 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include <ronin/ronin.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "dcloader.h"
|
|
|
|
#ifdef DL_DEBUG
|
|
#define DBG(x) reportf x
|
|
#else
|
|
#define DBG(x) do{}while(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) != (ssize_t)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 *)(void *)(((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 (phdr.p_memsz > phdr.p_filesz)
|
|
memset(((char *)segment) + phdr.p_filesz, 0, phdr.p_memsz - phdr.p_filesz);
|
|
|
|
if (lseek(fd, phdr.p_offset, SEEK_SET)<0 ||
|
|
read(fd, segment, phdr.p_filesz) != (ssize_t)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)) !=
|
|
(ssize_t)(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) !=
|
|
(ssize_t)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) !=
|
|
(ssize_t)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) &&
|
|
(int)shdr[i].sh_link == symtab_sect && shdr[i].sh_info < ehdr.e_shnum &&
|
|
(shdr[shdr[i].sh_info].sh_flags & 2))
|
|
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);
|
|
|
|
int oldmask = getimask();
|
|
setimask(15);
|
|
purge_copyback();
|
|
flush_instruction_cache();
|
|
setimask(oldmask);
|
|
|
|
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();
|
|
}
|