added files for ps2 loadable modules (mainly transplanted from psp code with VERY minor tweaks)

svn-id: r49256
This commit is contained in:
Tony Puccinelli 2010-05-27 05:03:09 +00:00
parent fab28cb74b
commit 0fdfd5d47b
8 changed files with 1617 additions and 0 deletions

View file

@ -0,0 +1,209 @@
/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/branches/gsoc2010-plugins/backends/platform/ps2/elf32.h $
* $Id: elf32.h 49253 2010-05-12 05:26:54Z Toneman $
*
*/
#ifndef BACKENDS_ELF_H
#define BACKENDS_ELF_H
/* ELF stuff */
typedef unsigned short Elf32_Half, Elf32_Section;
typedef unsigned int Elf32_Word, Elf32_Addr, Elf32_Off;
typedef signed int Elf32_Sword;
typedef Elf32_Half Elf32_Versym;
#define EI_NIDENT (16)
#define SELFMAG 6
/* ELF File format structures. Look up ELF structure for more details */
// ELF header (contains info about the file)
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;
// Should be in e_ident
#define ELFMAG "\177ELF\1\1" /* ELF Magic number */
// e_type values
#define ET_NONE 0 /* no file type */
#define ET_REL 1 /* relocatable */
#define ET_EXEC 2 /* executable */
#define ET_DYN 3 /* shared object */
#define ET_CORE 4 /* core file */
// e_machine values
#define EM_MIPS 8
// Program header (contains info about segment)
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;
// p_type values
#define PT_NULL 0 /* ignored */
#define PT_LOAD 1 /* loadable segment */
#define PT_DYNAMIC 2 /* dynamic linking info */
#define PT_INTERP 3 /* info about interpreter */
#define PT_NOTE 4 /* note segment */
#define PT_SHLIB 5 /* reserved */
#define PT_PHDR 6 /* Program header table */
#define PT_MIPS_REGINFO 0x70000000 /* register usage info */
// p_flags value
#define PF_X 1 /* execute */
#define PF_W 2 /* write */
#define PF_R 4 /* read */
// Section header (contains info about section)
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;
// sh_type values
#define SHT_NULL 0 /* Inactive section */
#define SHT_PROGBITS 1 /* Proprietary */
#define SHT_SYMTAB 2 /* Symbol table */
#define SHT_STRTAB 3 /* String table */
#define SHT_RELA 4 /* Relocation entries with addend */
#define SHT_HASH 5 /* Symbol hash table */
#define SHT_DYNAMIC 6 /* Info for dynamic linking */
#define SHT_NOTE 7 /* Note section */
#define SHT_NOBITS 8 /* Occupies no space */
#define SHT_REL 9 /* Relocation entries without addend */
#define SHT_SHLIB 10 /* Reserved */
#define SHT_DYNSYM 11 /* Minimal set of dynamic linking symbols */
#define SHT_MIPS_LIBLSIT 0x70000000 /* Info about dynamic shared object libs */
#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicts btw executables and shared objects */
#define SHT_MIPS_GPTAB 0x70000003 /* Global pointer table */
// sh_flags values
#define SHF_WRITE 0 /* writable section */
#define SHF_ALLOC 2 /* section occupies memory */
#define SHF_EXECINSTR 4 /* machine instructions */
#define SHF_MIPS_GPREL 0x10000000 /* Must be made part of global data area */
// Symbol entry (contain info about a symbol)
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;
// Extract from the st_info
#define SYM_TYPE(x) ((x)&0xF)
#define SYM_BIND(x) ((x)>>4)
// Symbol binding values from st_info
#define STB_LOCAL 0 /* Symbol not visible outside object */
#define STB_GLOBAL 1 /* Symbol visible to all object files */
#define STB_WEAK 2 /* Similar to STB_GLOBAL */
// Symbol type values from st_info
#define STT_NOTYPE 0 /* Not specified */
#define STT_OBJECT 1 /* Data object e.g. variable */
#define STT_FUNC 2 /* Function */
#define STT_SECTION 3 /* Section */
#define STT_FILE 4 /* Source file associated with object file */
// Special section header index values from st_shndex
#define SHN_UNDEF 0
#define SHN_LOPROC 0xFF00 /* Extended values */
#define SHN_ABS 0xFFF1 /* Absolute value: don't relocate */
#define SHN_COMMON 0xFFF2 /* Common block. Not allocated yet */
#define SHN_HIPROC 0xFF1F
#define SHN_HIRESERVE 0xFFFF
// Relocation entry (info about how to relocate)
typedef struct {
Elf32_Addr r_offset; /* Address */
Elf32_Word r_info; /* Relocation type and symbol index */
} Elf32_Rel;
// Access macros for the relocation info
#define REL_TYPE(x) ((x)&0xFF) /* Extract relocation type */
#define REL_INDEX(x) ((x)>>8) /* Extract relocation index into symbol table */
// MIPS relocation types
#define R_MIPS_NONE 0
#define R_MIPS_16 1
#define R_MIPS_32 2
#define R_MIPS_REL32 3
#define R_MIPS_26 4
#define R_MIPS_HI16 5
#define R_MIPS_LO16 6
#define R_MIPS_GPREL16 7
#define R_MIPS_LITERAL 8
#define R_MIPS_GOT16 9
#define R_MIPS_PC16 10
#define R_MIPS_CALL16 11
#define R_MIPS_GPREL32 12
#define R_MIPS_GOTHI16 13
#define R_MIPS_GOTLO16 14
#define R_MIPS_CALLHI16 15
#define R_MIPS_CALLLO16 16
// Mock function to get value of global pointer
#define getGP() ({ \
unsigned int __valgp; \
__asm__ ("add %0, $gp, $0" : "=r"(__valgp) : ); \
__valgp; \
})
#endif /* BACKENDS_ELF_H */

View file

@ -0,0 +1,253 @@
OUTPUT_FORMAT("elf32-littlemips", "elf32-bigmips",
"elf32-littlemips")
OUTPUT_ARCH(mips:allegrex)
ENTRY(_start)
/* Do we need any of these for elf?
__DYNAMIC = 0; */
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = 0x08900000); . = 0x08900000;
.interp : { *(.interp) }
.reginfo : { *(.reginfo) }
.dynamic : { *(.dynamic) }
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.init : { *(.rel.init) }
.rela.init : { *(.rela.init) }
.rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
.rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
.rel.fini : { *(.rel.fini) }
.rela.fini : { *(.rela.fini) }
/* PSP-specific relocations. TODO: works for PS2 as well? */
.rel.sceStub.text : { *(.rel.sceStub.text) *(SORT(.rel.sceStub.text.*)) }
.rel.lib.ent.top : { *(.rel.lib.ent.top) }
.rel.lib.ent : { *(.rel.lib.ent) }
.rel.lib.ent.btm : { *(.rel.lib.ent.btm) }
.rel.lib.stub.top : { *(.rel.lib.stub.top) }
.rel.lib.stub : { *(.rel.lib.stub) }
.rel.lib.stub.btm : { *(.rel.lib.stub.btm) }
.rel.rodata.sceModuleInfo : { *(.rel.rodata.sceModuleInfo) }
.rel.rodata.sceResident : { *(.rel.rodata.sceResident) }
.rel.rodata.sceNid : { *(.rel.rodata.sceNid) }
.rel.rodata.sceVstub : { *(.rel.rodata.sceVstub) *(SORT(.rel.rodata.sceVstub.*)) }
.rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
.rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
.rel.data.rel.ro : { *(.rel.data.rel.ro*) }
.rela.data.rel.ro : { *(.rel.data.rel.ro*) }
.rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
.rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
.rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
.rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
.rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
.rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
.rel.ctors : { *(.rel.ctors) }
.rela.ctors : { *(.rela.ctors) }
.rel.dtors : { *(.rel.dtors) }
.rela.dtors : { *(.rela.dtors) }
.rel.got : { *(.rel.got) }
.rela.got : { *(.rela.got) }
.rel.sdata : { *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) }
.rela.sdata : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) }
.rel.sbss : { *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) }
.rela.sbss : { *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) }
.rel.sdata2 : { *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) }
.rela.sdata2 : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) }
.rel.sbss2 : { *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) }
.rela.sbss2 : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) }
.rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
.rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
.init :
{
KEEP (*(.init))
} =0
.plt : { *(.plt) }
.text :
{
_ftext = . ;
*(.text .stub .text.* .gnu.linkonce.t.*)
KEEP (*(.text.*personality*))
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
*(.mips16.fn.*) *(.mips16.call.*)
} =0
.fini :
{
KEEP (*(.fini))
} =0
/* PSP library stub functions. TODO: works for PS2 as well? */
.sceStub.text : { *(.sceStub.text) *(SORT(.sceStub.text.*)) }
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
/* PSP library entry table and library stub table. TODO: works for PS2 as well? */
.lib.ent.top : { *(.lib.ent.top) }
.lib.ent : { *(.lib.ent) }
.lib.ent.btm : { *(.lib.ent.btm) }
.lib.stub.top : { *(.lib.stub.top) }
.lib.stub : { *(.lib.stub) }
.lib.stub.btm : { *(.lib.stub.btm) }
/* PSP read-only data for module info, NIDs, and Vstubs. The
.rodata.sceModuleInfo section must appear before the .rodata section
otherwise it would get absorbed into .rodata and the PSP bootloader
would be unable to locate the module info structure. TODO: Works for PS2 as well? */
.rodata.sceModuleInfo : { *(.rodata.sceModuleInfo) }
.rodata.sceResident : { *(.rodata.sceResident) }
.rodata.sceNid : { *(.rodata.sceNid) }
.rodata.sceVstub : { *(.rodata.sceVstub) *(SORT(.rodata.sceVstub.*)) }
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) }
.sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
.eh_frame_hdr : { *(.eh_frame_hdr) }
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RO { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
. = ALIGN(256) + (. & (256 - 1));
/* Exception handling */
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RW { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
/* Thread Local Storage sections */
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
/* Ensure the __preinit_array_start label is properly aligned. We
could instead move the label definition inside the section, but
the linker would then create the section even if it turns out to
be empty, which isn't pretty. */
. = ALIGN(32 / 8);
PROVIDE (__preinit_array_start = .);
.preinit_array : { KEEP (*(.preinit_array)) }
PROVIDE (__preinit_array_end = .);
PROVIDE (__init_array_start = .);
.init_array : { KEEP (*(.init_array)) }
PROVIDE (__init_array_end = .);
PROVIDE (__fini_array_start = .);
.fini_array : { KEEP (*(.fini_array)) }
PROVIDE (__fini_array_end = .);
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin*.o(.ctors))
/* We don't want to include the .ctor section from
from the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors :
{
KEEP (*crtbegin*.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.jcr : { KEEP (*(.jcr)) }
.data.rel.ro : { *(.data.rel.ro.local) *(.data.rel.ro*) }
.data :
{
_fdata = . ;
*(.data .data.* .gnu.linkonce.d.*)
KEEP (*(.gnu.linkonce.d.*personality*))
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
. = .;
_gp = ALIGN(16) + 0x7ff0;
.got : { *(.got.plt) *(.got) }
/* We want the small data sections together, so single-instruction offsets
can access them all, and initialized data all before uninitialized, so
we can shorten the on-disk segment size. */
.sdata :
{
*(.sdata .sdata.* .gnu.linkonce.s.*)
}
.lit8 : { *(.lit8) }
.lit4 : { *(.lit4) }
_edata = .;
PROVIDE (edata = .);
__bss_start = .;
_fbss = .;
.sbss :
{
PROVIDE (__sbss_start = .);
PROVIDE (___sbss_start = .);
*(.dynsbss)
*(.sbss .sbss.* .gnu.linkonce.sb.*)
*(.scommon)
PROVIDE (__sbss_end = .);
PROVIDE (___sbss_end = .);
}
/* make a gap to put the plugins' short data here */
__plugin_hole_start = .;
. = _gp + 0x7ff0;
__plugin_hole_end = .;
COMMON :
{
*(COMMON)
}
. = ALIGN(32 / 8);
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections. */
. = ALIGN(32 / 8);
}
. = ALIGN(32 / 8);
_end = .;
PROVIDE (end = .);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/DISCARD/ : { *(.comment) *(.pdr) }
/DISCARD/ : { *(.note.GNU-stack) }
}

View file

@ -0,0 +1,239 @@
OUTPUT_FORMAT("elf32-littlemips", "elf32-bigmips",
"elf32-littlemips")
OUTPUT_ARCH(mips:allegrex)
PHDRS
{
plugin PT_LOAD ;
shorts PT_LOAD ;
}
/* Do we need any of these for elf?
__DYNAMIC = 0; */
SECTIONS
{
/* Read-only sections, merged into text segment: */
. = 0;
.interp : { *(.interp) } : plugin
.reginfo : { *(.reginfo) } : plugin
.dynamic : { *(.dynamic) }
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.init : { *(.rel.init) }
.rela.init : { *(.rela.init) }
.rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
.rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
.rel.fini : { *(.rel.fini) }
.rela.fini : { *(.rela.fini) }
/* PSP-specific relocations. TODO: Do these work for PS2 as well? */
.rel.sceStub.text : { *(.rel.sceStub.text) *(SORT(.rel.sceStub.text.*)) }
.rel.lib.ent.top : { *(.rel.lib.ent.top) }
.rel.lib.ent : { *(.rel.lib.ent) }
.rel.lib.ent.btm : { *(.rel.lib.ent.btm) }
.rel.lib.stub.top : { *(.rel.lib.stub.top) }
.rel.lib.stub : { *(.rel.lib.stub) }
.rel.lib.stub.btm : { *(.rel.lib.stub.btm) }
.rel.rodata.sceModuleInfo : { *(.rel.rodata.sceModuleInfo) }
.rel.rodata.sceResident : { *(.rel.rodata.sceResident) }
.rel.rodata.sceNid : { *(.rel.rodata.sceNid) }
.rel.rodata.sceVstub : { *(.rel.rodata.sceVstub) *(SORT(.rel.rodata.sceVstub.*)) }
.rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
.rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
.rel.data.rel.ro : { *(.rel.data.rel.ro*) }
.rela.data.rel.ro : { *(.rel.data.rel.ro*) }
.rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
.rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
.rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
.rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
.rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
.rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
.rel.ctors : { *(.rel.ctors) }
.rela.ctors : { *(.rela.ctors) }
.rel.dtors : { *(.rel.dtors) }
.rela.dtors : { *(.rela.dtors) }
.rel.got : { *(.rel.got) }
.rela.got : { *(.rela.got) }
.rel.sdata : { *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) }
.rela.sdata : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) }
.rel.sbss : { *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) }
.rela.sbss : { *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) }
.rel.sdata2 : { *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) }
.rela.sdata2 : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) }
.rel.sbss2 : { *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) }
.rela.sbss2 : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) }
.rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
.rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
.init :
{
KEEP (*(.init))
} =0
.plt : { *(.plt) }
.text :
{
_ftext = . ;
*(.text .stub .text.* .gnu.linkonce.t.*)
KEEP (*(.text.*personality*))
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
*(.mips16.fn.*) *(.mips16.call.*)
} =0
.fini :
{
KEEP (*(.fini))
} =0
/* PSP library stub functions. TODO: Work for PS2 as well? */
.sceStub.text : { *(.sceStub.text) *(SORT(.sceStub.text.*)) }
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
/* PSP library entry table and library stub table. TODO: Works for PS2 as well? */
.lib.ent.top : { *(.lib.ent.top) }
.lib.ent : { *(.lib.ent) }
.lib.ent.btm : { *(.lib.ent.btm) }
.lib.stub.top : { *(.lib.stub.top) }
.lib.stub : { *(.lib.stub) }
.lib.stub.btm : { *(.lib.stub.btm) }
/* PSP read-only data for module info, NIDs, and Vstubs. The
.rodata.sceModuleInfo section must appear before the .rodata section
otherwise it would get absorbed into .rodata and the PSP bootloader
would be unable to locate the module info structure. TODO: Works for the ps2 as well? */
.rodata.sceModuleInfo : { *(.rodata.sceModuleInfo) }
.rodata.sceResident : { *(.rodata.sceResident) }
.rodata.sceNid : { *(.rodata.sceNid) }
.rodata.sceVstub : { *(.rodata.sceVstub) *(SORT(.rodata.sceVstub.*)) }
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) }
.sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
.eh_frame_hdr : { *(.eh_frame_hdr) }
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RO { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
. = ALIGN(256) + (. & (256 - 1));
/* Exception handling */
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RW { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
/* Thread Local Storage sections */
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
/* Ensure the __preinit_array_start label is properly aligned. We
could instead move the label definition inside the section, but
the linker would then create the section even if it turns out to
be empty, which isn't pretty. */
. = ALIGN(32 / 8);
PROVIDE (__preinit_array_start = .);
.preinit_array : { KEEP (*(.preinit_array)) }
PROVIDE (__preinit_array_end = .);
PROVIDE (__init_array_start = .);
.init_array : { KEEP (*(.init_array)) }
PROVIDE (__init_array_end = .);
PROVIDE (__fini_array_start = .);
.fini_array : { KEEP (*(.fini_array)) }
PROVIDE (__fini_array_end = .);
.ctors :
{
___plugin_ctors = .;
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
___plugin_ctors_end = .;
}
.dtors :
{
___plugin_dtors = .;
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
___plugin_dtors_end = .;
}
.jcr : { KEEP (*(.jcr)) }
.data.rel.ro : { *(.data.rel.ro.local) *(.data.rel.ro*) }
.data :
{
_fdata = . ;
*(.data .data.* .gnu.linkonce.d.*)
KEEP (*(.gnu.linkonce.d.*personality*))
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
. = .;
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections. */
. = ALIGN(32 / 8);
}
. = ALIGN(32 / 8);
_end = .;
PROVIDE (end = .);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/DISCARD/ : { *(.comment) *(.pdr) }
/DISCARD/ : { *(.note.GNU-stack) }
. = __plugin_hole_start;
.got : { *(.got.plt) *(.got) } : shorts
/* We want the small data sections together, so single-instruction offsets
can access them all, and initialized data all before uninitialized, so
we can shorten the on-disk segment size. */
.sdata :
{
*(.sdata .sdata.* .gnu.linkonce.s.*)
}
.lit8 : { *(.lit8) }
.lit4 : { *(.lit4) }
_edata = .;
PROVIDE (edata = .);
__bss_start = .;
_fbss = .;
.sbss :
{
PROVIDE (__sbss_start = .);
PROVIDE (___sbss_start = .);
*(.dynsbss)
*(.sbss .sbss.* .gnu.linkonce.sb.*)
*(.scommon)
PROVIDE (__sbss_end = .);
PROVIDE (___sbss_end = .);
}
}

View file

@ -0,0 +1,8 @@
PLUGIN_getVersion
PLUGIN_getType
PLUGIN_getTypeVersion
PLUGIN_getObject
___plugin_ctors
___plugin_ctors_end
___plugin_dtors
___plugin_dtors_end

View file

@ -0,0 +1,721 @@
/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/branches/gsoc2010-plugins/backends/platform/ps2/ps2loader.cpp $
* TODO: $ID
*
*/
#if defined(DYNAMIC_MODULES) && defined(__PS2__)
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/_default_fcntl.h>
#include <ps2utils.h>
#include "backends/platform/ps2/ps2loader.h"
//#include "backends/platform/ps2/powerman.h" //TODO
//#define __PS2_DEBUG_PLUGINS__
#ifdef __PS2_DEBUG_PLUGINS__
#define DBG(x,...) fprintf(stderr,x, ## __VA_ARGS__)
#else
#define DBG(x,...)
#endif
#define seterror(x,...) fprintf(stderr,x, ## __VA_ARGS__)
extern char __plugin_hole_start; // Indicates start of hole in program file for shorts
extern char __plugin_hole_end; // Indicates end of hole in program file
extern char _gp[]; // Value of gp register
DECLARE_SINGLETON(ShortSegmentManager) // For singleton
// Get rid of symbol table in memory
void DLObject::discard_symtab() {
free(_symtab);
free(_strtab);
_symtab = NULL;
_strtab = NULL;
_symbol_cnt = 0;
}
// Unload all objects from memory
void DLObject::unload() {
discard_symtab();
free(_segment);
_segment = NULL;
if (_shortsSegment) {
ShortsMan.deleteSegment(_shortsSegment);
_shortsSegment = NULL;
}
}
/**
* Follow the instruction of a relocation section.
*
* @param fd File Descriptor
* @param offset Offset into the File
* @param size Size of relocation section
* @param relSegment Base address of relocated segment in memory (memory offset)
*
*/
bool DLObject::relocate(int fd, unsigned long offset, unsigned long size, void *relSegment) {
Elf32_Rel *rel = NULL; // relocation entry
// Allocate memory for relocation table
if (!(rel = (Elf32_Rel *)malloc(size))) {
seterror("Out of memory.");
return false;
}
// Read in our relocation table
if (lseek(fd, offset, SEEK_SET) < 0 ||
read(fd, rel, size) != (ssize_t)size) {
seterror("Relocation table load failed.");
free(rel);
return false;
}
// Treat each relocation entry. Loop over all of them
int cnt = size / sizeof(*rel);
DBG("Loaded relocation table. %d entries. base address=%p\n", cnt, relSegment);
bool seenHi16 = false; // For treating HI/LO16 commands
int firstHi16 = -1; // Mark the point of the first hi16 seen
Elf32_Addr ahl = 0; // Calculated addend
int a = 0; // Addend: taken from the target
unsigned int *lastTarget = 0; // For processing hi16 when lo16 arrives
unsigned int relocation = 0;
int debugRelocs[10] = {0}; // For debugging
int extendedHi16 = 0; // Count extended hi16 treatments
Elf32_Addr lastHiSymVal = 0;
bool hi16InShorts = false;
#define DEBUG_NUM 2
// Loop over relocation entries
for (int i = 0; i < cnt; i++) {
// Get the symbol this relocation entry is referring to
Elf32_Sym *sym = (Elf32_Sym *)(_symtab) + (REL_INDEX(rel[i].r_info));
// Get the target instruction in the code
unsigned int *target = (unsigned int *)((char *)relSegment + rel[i].r_offset);
unsigned int origTarget = *target; // Save for debugging
// Act differently based on the type of relocation
switch (REL_TYPE(rel[i].r_info)) {
case R_MIPS_HI16: // Absolute addressing.
if (sym->st_shndx < SHN_LOPROC && // Only shift for plugin section (ie. has a real section index)
firstHi16 < 0) { // Only process first in block of HI16s
firstHi16 = i; // Keep the first Hi16 we saw
seenHi16 = true;
ahl = (*target & 0xffff) << 16; // Take lower 16 bits shifted up
lastHiSymVal = sym->st_value;
hi16InShorts = (ShortsMan.inGeneralSegment((char *)sym->st_value)); // Fix for problem with switching btw segments
if (debugRelocs[0]++ < DEBUG_NUM) // Print only a set number
DBG("R_MIPS_HI16: i=%d, offset=%x, ahl = %x, target = %x\n",
i, rel[i].r_offset, ahl, *target);
}
break;
case R_MIPS_LO16: // Absolute addressing. Needs a HI16 to come before it
if (sym->st_shndx < SHN_LOPROC) { // Only shift for plugin section. (ie. has a real section index)
if (!seenHi16) { // We MUST have seen HI16 first
seterror("R_MIPS_LO16 w/o preceding R_MIPS_HI16 at relocation %d!\n", i);
free(rel);
return false;
}
// Fix: bug in gcc makes LO16s connect to wrong HI16s sometimes (shorts and regular segment)
// Note that we can check the entire shorts segment because the executable's shorts don't belong to this plugin section
// and will be screened out above
bool lo16InShorts = ShortsMan.inGeneralSegment((char *)sym->st_value);
// Correct the bug by getting the proper value in ahl (taken from the current symbol)
if ((hi16InShorts && !lo16InShorts) || (!hi16InShorts && lo16InShorts)) {
ahl -= (lastHiSymVal & 0xffff0000); // We assume gcc meant the same offset
ahl += (sym->st_value & 0xffff0000);
}
ahl &= 0xffff0000; // Clean lower 16 bits for repeated LO16s
a = *target & 0xffff; // Take lower 16 bits of the target
a = (a << 16) >> 16; // Sign extend them
ahl += a; // Add lower 16 bits. AHL is now complete
// Fix: we can have LO16 access to the short segment sometimes
if (lo16InShorts) {
relocation = ahl + _shortsSegment->getOffset(); // Add in the short segment offset
} else // It's in the regular segment
relocation = ahl + (Elf32_Addr)_segment; // Add in the new offset for the segment
if (firstHi16 >= 0) { // We haven't treated the HI16s yet so do it now
for (int j = firstHi16; j < i; j++) {
if (REL_TYPE(rel[j].r_info) != R_MIPS_HI16) continue; // Skip over non-Hi16s
lastTarget = (unsigned int *)((char *)relSegment + rel[j].r_offset); // get hi16 target
*lastTarget &= 0xffff0000; // Clear the lower 16 bits of the last target
*lastTarget |= (relocation >> 16) & 0xffff; // Take the upper 16 bits of the relocation
if (relocation & 0x8000)(*lastTarget)++; // Subtle: we need to add 1 to the HI16 in this case
}
firstHi16 = -1; // Reset so we'll know we treated it
} else {
extendedHi16++;
}
*target &= 0xffff0000; // Clear the lower 16 bits of current target
*target |= relocation & 0xffff; // Take the lower 16 bits of the relocation
if (debugRelocs[1]++ < DEBUG_NUM)
DBG("R_MIPS_LO16: i=%d, offset=%x, a=%x, ahl = %x, lastTarget = %x, origt = %x, target = %x\n",
i, rel[i].r_offset, a, ahl, *lastTarget, origTarget, *target);
if (lo16InShorts && debugRelocs[2]++ < DEBUG_NUM)
DBG("R_MIPS_LO16s: i=%d, offset=%x, a=%x, ahl = %x, lastTarget = %x, origt = %x, target = %x\n",
i, rel[i].r_offset, a, ahl, *lastTarget, origTarget, *target);
}
break;
case R_MIPS_26: // Absolute addressing (for jumps and branches only)
if (sym->st_shndx < SHN_LOPROC) { // Only relocate for main segment
a = *target & 0x03ffffff; // Get 26 bits' worth of the addend
a = (a << 6) >> 6; // Sign extend a
relocation = ((a << 2) + (Elf32_Addr)_segment) >> 2; // a already points to the target. Subtract our offset
*target &= 0xfc000000; // Clean lower 26 target bits
*target |= (relocation & 0x03ffffff);
if (debugRelocs[3]++ < DEBUG_NUM)
DBG("R_MIPS_26: i=%d, offset=%x, symbol=%d, stinfo=%x, a=%x, origTarget=%x, target=%x\n",
i, rel[i].r_offset, REL_INDEX(rel[i].r_info), sym->st_info, a, origTarget, *target);
} else {
if (debugRelocs[4]++ < DEBUG_NUM)
DBG("R_MIPS_26: i=%d, offset=%x, symbol=%d, stinfo=%x, a=%x, origTarget=%x, target=%x\n",
i, rel[i].r_offset, REL_INDEX(rel[i].r_info), sym->st_info, a, origTarget, *target);
}
break;
case R_MIPS_GPREL16: // GP Relative addressing
if (_shortsSegment->getOffset() != 0 && // Only relocate if we shift the shorts section
ShortsMan.inGeneralSegment((char *)sym->st_value)) { // Only relocate things in the plugin hole
a = *target & 0xffff; // Get 16 bits' worth of the addend
a = (a << 16) >> 16; // Sign extend it
relocation = a + _shortsSegment->getOffset();
*target &= 0xffff0000; // Clear the lower 16 bits of the target
*target |= relocation & 0xffff;
if (debugRelocs[5]++ < DEBUG_NUM)
DBG("R_MIPS_GPREL16: i=%d, a=%x, gpVal=%x, origTarget=%x, target=%x, offset=%x\n",
i, a, _gpVal, origTarget, *target, _shortsSegment->getOffset());
}
break;
case R_MIPS_32: // Absolute addressing
if (sym->st_shndx < SHN_LOPROC) { // Only shift for plugin section.
a = *target; // Get full 32 bits of addend
if (ShortsMan.inGeneralSegment((char *)sym->st_value)) // Check if we're in the shorts segment
relocation = a + _shortsSegment->getOffset(); // Shift by shorts offset
else // We're in the main section
relocation = a + (Elf32_Addr)_segment; // Shift by main offset
*target = relocation;
if (debugRelocs[6]++ < DEBUG_NUM)
DBG("R_MIPS_32: i=%d, a=%x, origTarget=%x, target=%x\n", i, a, origTarget, *target);
}
break;
default:
seterror("Unknown relocation type %x at relocation %d.\n", REL_TYPE(rel[i].r_info), i);
free(rel);
return false;
}
}
DBG("Done with relocation. extendedHi16=%d\n\n", extendedHi16);
free(rel);
return true;
}
bool DLObject::readElfHeader(int fd, Elf32_Ehdr *ehdr) {
// Start reading the elf header. Check for errors
if (read(fd, ehdr, sizeof(*ehdr)) != sizeof(*ehdr) ||
memcmp(ehdr->e_ident, ELFMAG, SELFMAG) || // Check MAGIC
ehdr->e_type != ET_EXEC || // Check for executable
ehdr->e_machine != EM_MIPS || // Check for MIPS machine type
ehdr->e_phentsize < sizeof(Elf32_Phdr) || // Check for size of program header
ehdr->e_shentsize != sizeof(Elf32_Shdr)) { // Check for size of section header
seterror("Invalid file type.");
return false;
}
DBG("phoff = %d, phentsz = %d, phnum = %d\n",
ehdr->e_phoff, ehdr->e_phentsize, ehdr->e_phnum);
return true;
}
bool DLObject::readProgramHeaders(int fd, Elf32_Ehdr *ehdr, Elf32_Phdr *phdr, int num) {
// Read program header
if (lseek(fd, ehdr->e_phoff + sizeof(*phdr)*num, SEEK_SET) < 0 ||
read(fd, phdr, sizeof(*phdr)) != sizeof(*phdr)) {
seterror("Program header load failed.");
return false;
}
// Check program header values
if (phdr->p_type != PT_LOAD || phdr->p_filesz > phdr->p_memsz) {
seterror("Invalid program header.");
return false;
}
DBG("offs = %x, filesz = %x, memsz = %x, align = %x\n",
phdr->p_offset, phdr->p_filesz, phdr->p_memsz, phdr->p_align);
return true;
}
bool DLObject::loadSegment(int fd, Elf32_Phdr *phdr) {
char *baseAddress = 0;
// We need to take account of non-allocated segment for shorts
if (phdr->p_flags & PF_X) { // This is a relocated segment
// Attempt to allocate memory for segment
int extra = phdr->p_vaddr % phdr->p_align; // Get extra length TODO: check logic here
DBG("extra mem is %x\n", extra);
if (phdr->p_align < 0x10000) phdr->p_align = 0x10000; // Fix for wrong alignment on e.g. AGI
if (!(_segment = (char *)memalign(phdr->p_align, phdr->p_memsz + extra))) {
seterror("Out of memory.\n");
return false;
}
DBG("allocated segment @ %p\n", _segment);
// Get offset to load segment into
baseAddress = (char *)_segment + phdr->p_vaddr;
_segmentSize = phdr->p_memsz + extra;
} else { // This is a shorts section.
_shortsSegment = ShortsMan.newSegment(phdr->p_memsz, (char *)phdr->p_vaddr);
baseAddress = _shortsSegment->getStart();
DBG("shorts segment @ %p to %p. Segment wants to be at %x. Offset=%x\n",
_shortsSegment->getStart(), _shortsSegment->getEnd(), phdr->p_vaddr, _shortsSegment->getOffset());
}
// Set bss segment to 0 if necessary (assumes bss is at the end)
if (phdr->p_memsz > phdr->p_filesz) {
DBG("Setting %p to %p to 0 for bss\n", baseAddress + phdr->p_filesz, baseAddress + phdr->p_memsz);
memset(baseAddress + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
}
// Read the segment into memory
if (lseek(fd, phdr->p_offset, SEEK_SET) < 0 ||
read(fd, baseAddress, phdr->p_filesz) != (ssize_t)phdr->p_filesz) {
seterror("Segment load failed.");
return false;
}
return true;
}
Elf32_Shdr * DLObject::loadSectionHeaders(int fd, Elf32_Ehdr *ehdr) {
Elf32_Shdr *shdr = NULL;
// Allocate memory for section headers
if (!(shdr = (Elf32_Shdr *)malloc(ehdr->e_shnum * sizeof(*shdr)))) {
seterror("Out of memory.");
return NULL;
}
// Read from file into section headers
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.");
return NULL;
}
return shdr;
}
int DLObject::loadSymbolTable(int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr) {
// Loop over sections, looking for symbol table linked to a string table
for (int i = 0; i < ehdr->e_shnum; i++) {
//DBG("Section %d: type = %x, size = %x, entsize = %x, link = %x\n",
// i, shdr[i].sh_type, shdr[i].sh_size, shdr[i].sh_entsize, shdr[i].sh_link);
if (shdr[i].sh_type == SHT_SYMTAB &&
shdr[i].sh_entsize == sizeof(Elf32_Sym) &&
shdr[i].sh_link < ehdr->e_shnum &&
shdr[shdr[i].sh_link].sh_type == SHT_STRTAB &&
_symtab_sect < 0) {
_symtab_sect = i;
}
}
// Check for no symbol table
if (_symtab_sect < 0) {
seterror("No symbol table.");
return -1;
}
DBG("Symbol section at section %d, size %x\n", _symtab_sect, shdr[_symtab_sect].sh_size);
// Allocate memory for symbol table
if (!(_symtab = malloc(shdr[_symtab_sect].sh_size))) {
seterror("Out of memory.");
return -1;
}
// Read symbol table into memory
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.");
return -1;
}
// Set number of symbols
_symbol_cnt = shdr[_symtab_sect].sh_size / sizeof(Elf32_Sym);
DBG("Loaded %d symbols.\n", _symbol_cnt);
return _symtab_sect;
}
bool DLObject::loadStringTable(int fd, Elf32_Shdr *shdr) {
int string_sect = shdr[_symtab_sect].sh_link;
// Allocate memory for string table
if (!(_strtab = (char *)malloc(shdr[string_sect].sh_size))) {
seterror("Out of memory.");
return false;
}
// Read string table into memory
if (lseek(fd, shdr[string_sect].sh_offset, SEEK_SET) < 0 ||
read(fd, _strtab, shdr[string_sect].sh_size) !=
(ssize_t)shdr[string_sect].sh_size) {
seterror("Symbol table strings load failed.");
return false;
}
return true;
}
void DLObject::relocateSymbols(Elf32_Addr offset, Elf32_Addr shortsOffset) {
int shortsCount = 0, othersCount = 0;
DBG("Relocating symbols by %x. Shorts offset=%x\n", offset, shortsOffset);
// Loop over symbols, add relocation offset
Elf32_Sym *s = (Elf32_Sym *)_symtab;
for (int c = _symbol_cnt; c--; s++) {
// Make sure we don't relocate special valued symbols
if (s->st_shndx < SHN_LOPROC) {
if (!ShortsMan.inGeneralSegment((char *)s->st_value)) {
othersCount++;
s->st_value += offset;
if (s->st_value < (Elf32_Addr)_segment || s->st_value > (Elf32_Addr)_segment + _segmentSize)
seterror("Symbol out of bounds! st_value = %x\n", s->st_value);
} else { // shorts section
shortsCount++;
s->st_value += shortsOffset;
if (!_shortsSegment->inSegment((char *)s->st_value))
seterror("Symbol out of bounds! st_value = %x\n", s->st_value);
}
}
}
DBG("Relocated %d short symbols, %d others.\n", shortsCount, othersCount);
}
bool DLObject::relocateRels(int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr) {
// Loop over sections, finding relocation sections
for (int i = 0; i < ehdr->e_shnum; i++) {
Elf32_Shdr *curShdr = &(shdr[i]);
//Elf32_Shdr *linkShdr = &(shdr[curShdr->sh_info]);
if (curShdr->sh_type == SHT_REL && // Check for a relocation section
curShdr->sh_entsize == sizeof(Elf32_Rel) && // Check for proper relocation size
(int)curShdr->sh_link == _symtab_sect && // Check that the sh_link connects to our symbol table
curShdr->sh_info < ehdr->e_shnum && // Check that the relocated section exists
(shdr[curShdr->sh_info].sh_flags & SHF_ALLOC)) { // Check if relocated section resides in memory
if (!ShortsMan.inGeneralSegment((char *)shdr[curShdr->sh_info].sh_addr)) { // regular segment
if (!relocate(fd, curShdr->sh_offset, curShdr->sh_size, _segment)) {
return false;
}
} else { // In Shorts segment
if (!relocate(fd, curShdr->sh_offset, curShdr->sh_size, (void *)_shortsSegment->getOffset())) {
return false;
}
}
}
}
return true;
}
bool DLObject::load(int fd) {
fprintf(stderr, "In DLObject::load\n");
Elf32_Ehdr ehdr; // ELF header
Elf32_Phdr phdr; // Program header
Elf32_Shdr *shdr; // Section header
bool ret = true;
if (readElfHeader(fd, &ehdr) == false) {
return false;
}
for (int i = 0; i < ehdr.e_phnum; i++) { // Load our 2 segments
fprintf(stderr, "Loading segment %d\n", i);
if (readProgramHeaders(fd, &ehdr, &phdr, i) == false)
return false;
if (!loadSegment(fd, &phdr))
return false;
}
if ((shdr = loadSectionHeaders(fd, &ehdr)) == NULL)
ret = false;
if (ret && ((_symtab_sect = loadSymbolTable(fd, &ehdr, shdr)) < 0))
ret = false;
if (ret && (loadStringTable(fd, shdr) == false))
ret = false;
if (ret)
relocateSymbols((Elf32_Addr)_segment, _shortsSegment->getOffset()); // Offset by our segment allocated address
if (ret && (relocateRels(fd, &ehdr, shdr) == false))
ret = false;
free(shdr);
return ret;
}
bool DLObject::open(const char *path) {
int fd;
void *ctors_start, *ctors_end;
DBG("open(\"%s\")\n", path);
// Get the address of the global pointer
_gpVal = (unsigned int) & _gp;
DBG("_gpVal is %x\n", _gpVal);
PowerMan.beginCriticalSection();
if ((fd = ::open(path, O_RDONLY)) < 0) {
seterror("%s not found.", path);
return false;
}
// Try to load and relocate
if (!load(fd)) {
::close(fd);
unload();
return false;
}
::close(fd);
PowerMan.endCriticalSection();
// flush data cache
sceKernelDcacheWritebackAll();
// Get the symbols for the global constructors and destructors
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++) {
// We can only import symbols that are global or weak in the plugin
if ((SYM_BIND(s->st_info) == STB_GLOBAL || SYM_BIND(s->st_info) == STB_WEAK) &&
/*_strtab[s->st_name] == '_' && */ // Try to make this more efficient
!strcmp(name, _strtab + s->st_name)) {
// We found the symbol
DBG("=> %p\n", (void*)s->st_value);
return (void*)s->st_value;
}
}
seterror("Symbol \"%s\" not found.", name);
return NULL;
}
ShortSegmentManager::ShortSegmentManager() {
_shortsStart = &__plugin_hole_start ;
_shortsEnd = &__plugin_hole_end;
}
ShortSegmentManager::Segment *ShortSegmentManager::newSegment(int size, char *origAddr) {
char *lastAddress = origAddr;
Common::List<Segment *>::iterator i;
// Find a block that fits, starting from the beginning
for (i = _list.begin(); i != _list.end(); ++i) {
char *currAddress = (*i)->getStart();
if ((int)(currAddress - lastAddress) >= size) break;
lastAddress = (*i)->getEnd();
}
if ((Elf32_Addr)lastAddress & 3)
lastAddress += 4 - ((Elf32_Addr)lastAddress & 3); // Round up to multiple of 4
if (lastAddress + size > _shortsEnd) {
seterror("Error. No space in shorts segment for %x bytes. Last address is %p, max address is %p.\n",
size, lastAddress, _shortsEnd);
return NULL;
}
Segment *seg = new Segment(lastAddress, size, origAddr); // Create a new segment
if (lastAddress + size > _highestAddress) _highestAddress = lastAddress + size; // Keep track of maximum
_list.insert(i, seg);
DBG("Shorts segment size %x allocated. End = %p. Remaining space = %x. Highest so far is %p.\n",
size, lastAddress + size, _shortsEnd - _list.back()->getEnd(), _highestAddress);
return seg;
}
void ShortSegmentManager::deleteSegment(ShortSegmentManager::Segment *seg) {
DBG("Deleting shorts segment from %p to %p.\n\n", seg->getStart(), seg->getEnd());
_list.remove(seg);
delete seg;
}
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();
}
#endif /* DYNAMIC_MODULES && __PS2__ */

View file

@ -0,0 +1,137 @@
/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/branches/gsoc2010-plugins/backends/platform/ps2/ps2loader.h $
* TODO: $ID
*
*/
#ifndef PS2LOADER_H
#define PS2LOADER_H
#include "elf32.h"
#include "common/list.h"
#include "common/singleton.h"
#define MAXDLERRLEN 80
#define ShortsMan ShortSegmentManager::instance()
class ShortSegmentManager : public Common::Singleton<ShortSegmentManager> {
private:
char *_shortsStart;
char *_shortsEnd;
public:
char *getShortsStart() {
return _shortsStart;
}
bool inGeneralSegment(char *addr) {
return ((char *)addr >= _shortsStart && (char *)addr < _shortsEnd);
}
class Segment {
private:
friend class ShortSegmentManager;
Segment(char *start, int size, char *origAddr) : _startAddress(start), _size(size), _origAddress(origAddr) {}
~Segment() {}
char *_startAddress; // Start of shorts segment in memory
int _size; // Size of shorts segment
char *_origAddress; // Original address this segment was supposed to be at
public:
char *getStart() {
return _startAddress;
}
char *getEnd() {
return (_startAddress + _size);
}
Elf32_Addr getOffset() {
return (Elf32_Addr)(_startAddress - _origAddress);
}
bool inSegment(char *addr) {
return ((char *)addr >= _startAddress && (char *)addr <= _startAddress + _size);
}
};
Segment *newSegment(int size, char *origAddr);
void deleteSegment(Segment *);
private:
ShortSegmentManager();
friend class Common::Singleton<ShortSegmentManager>;
Common::List<Segment *> _list;
char *_highestAddress;
};
class DLObject {
protected:
char *_errbuf; /* For error messages, at least MAXDLERRLEN in size */
ShortSegmentManager::Segment *_shortsSegment; // For assigning shorts ranges
void *_segment, *_symtab;
char *_strtab;
int _symbol_cnt;
int _symtab_sect;
void *_dtors_start, *_dtors_end;
unsigned int _gpVal; // Value of Global Pointer
int _segmentSize;
void seterror(const char *fmt, ...);
void unload();
bool relocate(int fd, unsigned long offset, unsigned long size, void *);
bool load(int fd);
bool readElfHeader(int fd, Elf32_Ehdr *ehdr);
bool readProgramHeaders(int fd, Elf32_Ehdr *ehdr, Elf32_Phdr *phdr, int num);
bool loadSegment(int fd, Elf32_Phdr *phdr);
Elf32_Shdr *loadSectionHeaders(int fd, Elf32_Ehdr *ehdr);
int loadSymbolTable(int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr);
bool loadStringTable(int fd, Elf32_Shdr *shdr);
void relocateSymbols(Elf32_Addr offset, Elf32_Addr shortsOffset);
bool relocateRels(int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr);
public:
bool open(const char *path);
bool close();
void *symbol(const char *name);
void discard_symtab();
DLObject(char *errbuf = NULL) : _errbuf(_errbuf), _shortsSegment(NULL), _segment(NULL), _symtab(NULL),
_strtab(NULL), _symbol_cnt(0), _symtab_sect(-1), _dtors_start(NULL), _dtors_end(NULL), _gpVal(0) ,
_segmentSize(0) {}
};
#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 /* PS2LOADER_H */

View file

@ -1,3 +1,28 @@
/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/branches/gsoc2010-plugins/backends/plugins/ps2/ps2-provider.cpp $
* $Id: ps2-provider.cpp 49253 2010-05-26 5:17:18Z Toneman $
*
*/
#if defined(DYNAMIC_MODULES) && defined(__PS2__)
#include "backends/plugins/ps2/ps2-provider.h"

View file

@ -1,3 +1,28 @@
/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/branches/gsoc2010-plugins/backends/plugins/ps2/ps2-provider.h $
* $Id: ps2-provider.h 49253 2010-05-26 5:17:18Z Toneman $
*
*/
#ifndef BACKENDS_PLUGINS_PS2_PS2_PROVIDER_H
#define BACKENDS_PLUGINS_PS2_PS2_PROVIDER_H