2012-11-01 15:19:01 +00:00
|
|
|
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
|
|
|
|
// 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
|
2012-11-04 22:01:49 +00:00
|
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
// 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 2.0 for more details.
|
|
|
|
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
// Official git repository and contact information can be found at
|
|
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
|
2013-06-01 16:29:38 +00:00
|
|
|
#include "Core/MemMap.h"
|
|
|
|
#include "Core/Reporting.h"
|
2014-05-04 08:22:57 +00:00
|
|
|
#include "Core/MIPS/MIPSTables.h"
|
2012-11-01 15:19:01 +00:00
|
|
|
#include "ElfReader.h"
|
2014-05-04 08:22:57 +00:00
|
|
|
#include "Core/Debugger/Breakpoints.h"
|
|
|
|
#include "Core/Debugger/SymbolMap.h"
|
|
|
|
#include "Core/HLE/sceKernelMemory.h"
|
2014-07-15 15:37:49 +00:00
|
|
|
#include "Core/HLE/sceKernelModule.h"
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2014-07-16 06:24:02 +00:00
|
|
|
#ifdef BLACKBERRY
|
|
|
|
using std::strnlen;
|
|
|
|
#endif
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2014-07-20 19:09:58 +00:00
|
|
|
const char *ElfReader::GetSectionName(int section) const {
|
2012-11-01 15:19:01 +00:00
|
|
|
if (sections[section].sh_type == SHT_NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int nameOffset = sections[section].sh_name;
|
2013-12-02 03:43:40 +00:00
|
|
|
const char *ptr = (const char *)GetSectionDataPtr(header->e_shstrndx);
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
if (ptr)
|
|
|
|
return ptr + nameOffset;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void addrToHiLo(u32 addr, u16 &hi, s16 &lo)
|
|
|
|
{
|
|
|
|
lo = (addr & 0xFFFF);
|
|
|
|
u32 naddr = addr - lo;
|
|
|
|
hi = naddr>>16;
|
|
|
|
u32 test = (hi<<16) + lo;
|
|
|
|
if (test != addr)
|
|
|
|
{
|
2013-06-01 16:29:38 +00:00
|
|
|
WARN_LOG_REPORT(LOADER, "HI16/LO16 relocation failure?");
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-14 10:43:09 +00:00
|
|
|
bool ElfReader::LoadRelocations(Elf32_Rel *rels, int numRelocs)
|
2012-12-27 20:32:12 +00:00
|
|
|
{
|
2013-11-14 10:43:09 +00:00
|
|
|
int numErrors = 0;
|
2013-11-14 21:45:34 +00:00
|
|
|
DEBUG_LOG(LOADER, "Loading %i relocations...", numRelocs);
|
2012-12-27 20:32:12 +00:00
|
|
|
for (int r = 0; r < numRelocs; r++)
|
|
|
|
{
|
2013-11-14 11:32:59 +00:00
|
|
|
// INFO_LOG(LOADER, "Loading reloc %i (%p)...", r, rels + r);
|
2012-12-27 20:32:12 +00:00
|
|
|
u32 info = rels[r].r_info;
|
|
|
|
u32 addr = rels[r].r_offset;
|
|
|
|
|
|
|
|
int type = info & 0xf;
|
|
|
|
|
|
|
|
int readwrite = (info>>8) & 0xff;
|
|
|
|
int relative = (info>>16) & 0xff;
|
|
|
|
|
|
|
|
//0 = code
|
|
|
|
//1 = data
|
|
|
|
|
2013-11-15 16:19:29 +00:00
|
|
|
if (readwrite >= (int)ARRAY_SIZE(segmentVAddr)) {
|
2013-11-14 10:43:09 +00:00
|
|
|
if (numErrors < 10) {
|
2013-11-15 16:19:29 +00:00
|
|
|
ERROR_LOG_REPORT(LOADER, "Bad segment number %i", readwrite);
|
2013-11-14 10:43:09 +00:00
|
|
|
}
|
|
|
|
numErrors++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-12-27 20:32:12 +00:00
|
|
|
addr += segmentVAddr[readwrite];
|
2013-11-15 17:45:16 +00:00
|
|
|
|
|
|
|
// It appears that misaligned relocations are allowed.
|
|
|
|
// Will they work correctly on big-endian?
|
|
|
|
|
2013-11-15 16:19:29 +00:00
|
|
|
if (((addr & 3) && type != R_MIPS_32) || !Memory::IsValidAddress(addr)) {
|
2013-11-14 10:43:09 +00:00
|
|
|
if (numErrors < 10) {
|
2013-11-15 16:19:29 +00:00
|
|
|
WARN_LOG_REPORT(LOADER, "Suspicious address %08x, skipping reloc, type = %d", addr, type);
|
2013-11-14 21:45:34 +00:00
|
|
|
} else if (numErrors == 10) {
|
|
|
|
WARN_LOG(LOADER, "Too many bad relocations, skipping logging");
|
2013-11-14 10:43:09 +00:00
|
|
|
}
|
|
|
|
numErrors++;
|
|
|
|
continue;
|
|
|
|
}
|
2012-12-27 20:32:12 +00:00
|
|
|
|
2014-06-23 15:18:56 +00:00
|
|
|
u32 op = Memory::Read_Instruction(addr, true).encoding;
|
2012-12-27 20:32:12 +00:00
|
|
|
|
2013-11-14 11:32:59 +00:00
|
|
|
const bool log = false;
|
2012-12-27 20:32:12 +00:00
|
|
|
//log=true;
|
2013-11-14 21:45:34 +00:00
|
|
|
if (log) {
|
2013-11-14 10:43:09 +00:00
|
|
|
DEBUG_LOG(LOADER,"rel at: %08x info: %08x type: %i",addr, info, type);
|
2012-12-27 20:32:12 +00:00
|
|
|
}
|
|
|
|
u32 relocateTo = segmentVAddr[relative];
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
2013-06-01 16:29:38 +00:00
|
|
|
case R_MIPS_32:
|
2012-12-27 20:32:12 +00:00
|
|
|
if (log)
|
|
|
|
DEBUG_LOG(LOADER,"Full address reloc %08x", addr);
|
|
|
|
//full address, no problemo
|
|
|
|
op += relocateTo;
|
|
|
|
break;
|
|
|
|
|
2013-06-01 16:29:38 +00:00
|
|
|
case R_MIPS_26: //j, jal
|
2012-12-27 20:32:12 +00:00
|
|
|
//add on to put in correct address space
|
|
|
|
if (log)
|
|
|
|
DEBUG_LOG(LOADER,"j/jal reloc %08x", addr);
|
2013-08-23 06:21:20 +00:00
|
|
|
op = (op & 0xFC000000) | (((op&0x03FFFFFF)+(relocateTo>>2))&0x03FFFFFF);
|
2012-12-27 20:32:12 +00:00
|
|
|
break;
|
|
|
|
|
2013-06-01 16:29:38 +00:00
|
|
|
case R_MIPS_HI16: //lui part of lui-addiu pairs
|
2012-12-27 20:32:12 +00:00
|
|
|
{
|
|
|
|
if (log)
|
|
|
|
DEBUG_LOG(LOADER,"HI reloc %08x", addr);
|
|
|
|
|
|
|
|
u32 cur = (op & 0xFFFF) << 16;
|
|
|
|
u16 hi = 0;
|
|
|
|
bool found = false;
|
|
|
|
for (int t = r + 1; t<numRelocs; t++)
|
|
|
|
{
|
2013-06-01 16:29:38 +00:00
|
|
|
if ((rels[t].r_info & 0xF) == R_MIPS_LO16)
|
2012-12-27 20:32:12 +00:00
|
|
|
{
|
|
|
|
u32 corrLoAddr = rels[t].r_offset + segmentVAddr[readwrite];
|
2013-11-14 10:43:09 +00:00
|
|
|
if (log) {
|
2012-12-27 20:32:12 +00:00
|
|
|
DEBUG_LOG(LOADER,"Corresponding lo found at %08x", corrLoAddr);
|
|
|
|
}
|
2013-11-14 10:43:09 +00:00
|
|
|
if (Memory::IsValidAddress(corrLoAddr)) {
|
|
|
|
s16 lo = (s32)(s16)(u16)(Memory::ReadUnchecked_U32(corrLoAddr) & 0xFFFF); //signed??
|
|
|
|
cur += lo;
|
|
|
|
cur += relocateTo;
|
|
|
|
addrToHiLo(cur, hi, lo);
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
ERROR_LOG(LOADER, "Bad corrLoAddr %08x", corrLoAddr);
|
|
|
|
}
|
2012-12-27 20:32:12 +00:00
|
|
|
}
|
|
|
|
}
|
2013-11-14 10:43:09 +00:00
|
|
|
if (!found) {
|
2013-06-01 16:29:38 +00:00
|
|
|
ERROR_LOG_REPORT(LOADER, "R_MIPS_HI16: could not find R_MIPS_LO16");
|
2013-11-14 10:43:09 +00:00
|
|
|
}
|
2012-12-27 20:32:12 +00:00
|
|
|
op = (op & 0xFFFF0000) | (hi);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-06-01 16:29:38 +00:00
|
|
|
case R_MIPS_LO16: //addiu part of lui-addiu pairs
|
2012-12-27 20:32:12 +00:00
|
|
|
{
|
|
|
|
if (log)
|
|
|
|
DEBUG_LOG(LOADER,"LO reloc %08x", addr);
|
|
|
|
u32 cur = op & 0xFFFF;
|
|
|
|
cur += relocateTo;
|
|
|
|
cur &= 0xFFFF;
|
|
|
|
op = (op & 0xFFFF0000) | cur;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-06-01 16:29:38 +00:00
|
|
|
case R_MIPS_GPREL16: //gp
|
2013-08-21 15:34:16 +00:00
|
|
|
// It seems safe to ignore this, almost a notification of a gp-relative operation?
|
2012-12-27 20:32:12 +00:00
|
|
|
break;
|
|
|
|
|
2013-08-13 06:02:39 +00:00
|
|
|
case R_MIPS_16:
|
|
|
|
{
|
|
|
|
char temp[256];
|
2014-02-06 12:40:18 +00:00
|
|
|
op = (op & 0xFFFF0000) | (((int)(op & 0xFFFF) + (int)relocateTo) & 0xFFFF);
|
2013-08-24 21:43:49 +00:00
|
|
|
MIPSDisAsm(MIPSOpcode(op), 0, temp);
|
2013-08-13 06:02:39 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-08-21 15:34:16 +00:00
|
|
|
case R_MIPS_NONE:
|
|
|
|
// This shouldn't matter, not sure the purpose of it.
|
2012-12-27 20:32:12 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2013-06-01 16:29:38 +00:00
|
|
|
{
|
|
|
|
char temp[256];
|
2013-08-24 21:43:49 +00:00
|
|
|
MIPSDisAsm(MIPSOpcode(op), 0, temp);
|
2013-08-13 06:02:39 +00:00
|
|
|
ERROR_LOG_REPORT(LOADER,"ARGH IT'S AN UNKNOWN RELOCATION!!!!!!!! %08x, type=%d : %s", addr, type, temp);
|
2013-06-01 16:29:38 +00:00
|
|
|
}
|
2012-12-27 20:32:12 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
Memory::Write_U32(op, addr);
|
|
|
|
}
|
2013-11-14 10:43:09 +00:00
|
|
|
if (numErrors) {
|
2013-11-14 21:45:34 +00:00
|
|
|
WARN_LOG(LOADER, "%i bad relocations found!!!", numErrors);
|
2013-11-14 10:43:09 +00:00
|
|
|
}
|
|
|
|
return numErrors == 0;
|
2012-12-27 20:32:12 +00:00
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-05-31 10:34:47 +00:00
|
|
|
|
|
|
|
void ElfReader::LoadRelocations2(int rel_seg)
|
|
|
|
{
|
|
|
|
Elf32_Phdr *ph;
|
|
|
|
u8 *buf, *end, *flag_table, *type_table;
|
|
|
|
int flag_table_size, type_table_size;
|
|
|
|
int flag_bits, seg_bits, type_bits;
|
|
|
|
int cmd, flag, seg, type;
|
2013-06-01 05:40:50 +00:00
|
|
|
int off_seg = 0, addr_seg, rel_base, rel_offset;
|
2013-07-27 05:39:35 +00:00
|
|
|
int relocate_to, last_type, lo16 = 0;
|
2013-05-31 10:34:47 +00:00
|
|
|
u32 op, addr;
|
|
|
|
int rcount = 0;
|
|
|
|
|
|
|
|
ph = segments + rel_seg;
|
|
|
|
|
|
|
|
|
|
|
|
buf = (u8*)GetSegmentPtr(rel_seg);
|
|
|
|
end = buf+ph->p_filesz;
|
|
|
|
|
|
|
|
flag_bits = buf[2];
|
|
|
|
type_bits = buf[3];
|
|
|
|
|
|
|
|
seg_bits = 1;
|
|
|
|
while((1<<seg_bits)<rel_seg)
|
|
|
|
seg_bits += 1;
|
|
|
|
|
|
|
|
buf += 4;
|
|
|
|
|
|
|
|
flag_table = buf;
|
|
|
|
flag_table_size = flag_table[0];
|
|
|
|
buf += flag_table_size;
|
|
|
|
|
|
|
|
type_table = buf;
|
2013-07-16 10:17:12 +00:00
|
|
|
type_table_size = type_table[0];
|
2013-05-31 10:34:47 +00:00
|
|
|
buf += type_table_size;
|
|
|
|
|
|
|
|
rel_base = 0;
|
|
|
|
last_type = -1;
|
|
|
|
while(buf<end){
|
|
|
|
cmd = *(u16*)(buf);
|
|
|
|
buf += 2;
|
|
|
|
|
|
|
|
flag = ( cmd<<(16-flag_bits))&0xffff;
|
|
|
|
flag = (flag>>(16-flag_bits))&0xffff;
|
|
|
|
flag = flag_table[flag];
|
|
|
|
|
|
|
|
seg = (cmd<<(16-seg_bits-flag_bits))&0xffff;
|
|
|
|
seg = (seg>>(16-seg_bits))&0xffff;
|
|
|
|
|
|
|
|
type = ( cmd<<(16-type_bits-seg_bits-flag_bits))&0xffff;
|
|
|
|
type = (type>>(16-type_bits))&0xffff;
|
|
|
|
type = type_table[type];
|
|
|
|
|
|
|
|
if((flag&0x01)==0){
|
|
|
|
off_seg = seg;
|
|
|
|
if((flag&0x06)==0){
|
|
|
|
rel_base = cmd>>(seg_bits+flag_bits);
|
|
|
|
}else if((flag&0x06)==4){
|
|
|
|
rel_base = buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
|
|
|
|
buf += 4;
|
|
|
|
}else{
|
2013-06-01 16:29:38 +00:00
|
|
|
ERROR_LOG_REPORT(LOADER, "Rel2: invalid size flag! %x", flag);
|
2013-05-31 10:34:47 +00:00
|
|
|
rel_base = 0;
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
addr_seg = seg;
|
|
|
|
relocate_to = segmentVAddr[addr_seg];
|
|
|
|
|
|
|
|
if((flag&0x06)==0x00){
|
|
|
|
rel_offset = cmd;
|
|
|
|
if(cmd&0x8000){
|
|
|
|
rel_offset |= 0xffff0000;
|
|
|
|
rel_offset >>= type_bits+seg_bits+flag_bits;
|
|
|
|
rel_offset |= 0xffff0000;
|
|
|
|
}else{
|
|
|
|
rel_offset >>= type_bits+seg_bits+flag_bits;
|
|
|
|
}
|
|
|
|
rel_base += rel_offset;
|
|
|
|
}else if((flag&0x06)==0x02){
|
|
|
|
rel_offset = cmd;
|
|
|
|
if(cmd&0x8000)
|
|
|
|
rel_offset |= 0xffff0000;
|
|
|
|
rel_offset >>= type_bits+seg_bits+flag_bits;
|
|
|
|
rel_offset = (rel_offset<<16) | (buf[0]) | (buf[1]<<8);
|
|
|
|
buf += 2;
|
|
|
|
rel_base += rel_offset;
|
|
|
|
}else if((flag&0x06)==0x04){
|
2013-08-25 17:43:18 +00:00
|
|
|
rel_base = buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
|
2013-05-31 10:34:47 +00:00
|
|
|
buf += 4;
|
|
|
|
}else{
|
2013-06-01 16:29:38 +00:00
|
|
|
ERROR_LOG_REPORT(LOADER, "Rel2: invalid relocat size flag! %x", flag);
|
2013-05-31 10:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rel_offset = rel_base+segmentVAddr[off_seg];
|
|
|
|
|
|
|
|
if((flag&0x38)==0x00){
|
|
|
|
lo16 = 0;
|
|
|
|
}else if((flag&0x38)==0x08){
|
|
|
|
if(last_type!=0x04)
|
|
|
|
lo16 = 0;
|
|
|
|
}else if((flag&0x38)==0x10){
|
|
|
|
lo16 = (buf[0]) | (buf[1]<<8);
|
|
|
|
if(lo16&0x8000)
|
|
|
|
lo16 |= 0xffff0000;
|
|
|
|
buf += 2;
|
|
|
|
}else{
|
2013-06-01 16:29:38 +00:00
|
|
|
ERROR_LOG_REPORT(LOADER, "Rel2: invalid lo16 type! %x", flag);
|
2013-05-31 10:34:47 +00:00
|
|
|
}
|
|
|
|
|
2014-06-23 15:18:56 +00:00
|
|
|
op = Memory::Read_Instruction(rel_offset, true).encoding;
|
2013-12-09 06:30:30 +00:00
|
|
|
DEBUG_LOG(LOADER, "Rel2: %5d: CMD=0x%04X flag=%x type=%d off_seg=%d offset=%08x addr_seg=%d op=%08x\n", rcount, cmd, flag, type, off_seg, rel_base, addr_seg, op);
|
2013-05-31 10:34:47 +00:00
|
|
|
|
|
|
|
switch(type){
|
|
|
|
case 0:
|
|
|
|
continue;
|
|
|
|
case 2: // R_MIPS_32
|
|
|
|
op += relocate_to;
|
|
|
|
break;
|
|
|
|
case 3: // R_MIPS_26
|
|
|
|
case 6: // R_MIPS_J26
|
|
|
|
case 7: // R_MIPS_JAL26
|
2013-08-23 06:21:20 +00:00
|
|
|
op = (op&0xFC000000) | (((op&0x03FFFFFF)+(relocate_to>>2))&0x03FFFFFF);
|
2013-12-09 06:30:30 +00:00
|
|
|
// To be safe, let's force it to the specified jump.
|
|
|
|
if (type == 6)
|
|
|
|
op = (op & ~0xFC000000) | 0x08000000;
|
|
|
|
else if (type == 7)
|
|
|
|
op = (op & ~0xFC000000) | 0x0C000000;
|
2013-05-31 10:34:47 +00:00
|
|
|
break;
|
|
|
|
case 4: // R_MIPS_HI16
|
|
|
|
addr = ((op<<16)+lo16)+relocate_to;
|
|
|
|
if(addr&0x8000)
|
|
|
|
addr += 0x00010000;
|
|
|
|
op = (op&0xffff0000) | (addr>>16 );
|
|
|
|
break;
|
|
|
|
case 1:
|
2013-08-23 06:21:20 +00:00
|
|
|
case 5: // R_MIPS_LO16
|
2013-05-31 10:34:47 +00:00
|
|
|
op = (op&0xffff0000) | (((op&0xffff)+relocate_to)&0xffff);
|
|
|
|
break;
|
|
|
|
default:
|
2013-12-09 06:30:30 +00:00
|
|
|
ERROR_LOG_REPORT(LOADER, "Rel2: unexpected relocation type! %x", type);
|
2013-05-31 10:34:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Memory::Write_U32(op, rel_offset);
|
|
|
|
rcount += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-15 07:25:16 +00:00
|
|
|
int ElfReader::LoadInto(u32 loadAddress, bool fromTop)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
|
|
|
DEBUG_LOG(LOADER,"String section: %i", header->e_shstrndx);
|
|
|
|
|
2013-10-05 08:05:55 +00:00
|
|
|
if (header->e_ident[0] != ELFMAG0 || header->e_ident[1] != ELFMAG1
|
|
|
|
|| header->e_ident[2] != ELFMAG2 || header->e_ident[3] != ELFMAG3)
|
|
|
|
return SCE_KERNEL_ERROR_UNSUPPORTED_PRX_TYPE;
|
|
|
|
|
|
|
|
// technically ELFCLASSNONE would freeze the system, but that's not really desireable
|
2013-10-12 09:29:45 +00:00
|
|
|
if (header->e_ident[EI_CLASS] != ELFCLASS32) {
|
|
|
|
if (header->e_ident[EI_CLASS] != 0) {
|
|
|
|
return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
ERROR_LOG(LOADER, "Bad ELF, EI_CLASS (fifth byte) is 0x00, should be 0x01 - would lock up a PSP.");
|
|
|
|
}
|
2013-10-05 08:05:55 +00:00
|
|
|
|
|
|
|
if (header->e_ident[EI_DATA] != ELFDATA2LSB)
|
|
|
|
return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED;
|
|
|
|
|
|
|
|
// e_ident[EI_VERSION] is ignored
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
sectionOffsets = new u32[GetNumSections()];
|
|
|
|
sectionAddrs = new u32[GetNumSections()];
|
|
|
|
|
|
|
|
// Should we relocate?
|
|
|
|
bRelocate = (header->e_type != ET_EXEC);
|
|
|
|
|
2014-07-15 15:37:49 +00:00
|
|
|
// Look for the module info - we need to know whether this is kernel or user.
|
|
|
|
const PspModuleInfo *modInfo = 0;
|
|
|
|
for (int i = 0; i < GetNumSections(); i++) {
|
|
|
|
Elf32_Shdr *s = §ions[i];
|
|
|
|
const char *name = GetSectionName(i);
|
|
|
|
if (name && !strcmp(name, ".rodata.sceModuleInfo")) {
|
|
|
|
modInfo = (const PspModuleInfo *)GetPtr(s->sh_offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!modInfo && GetNumSegments() >= 1) {
|
|
|
|
modInfo = (const PspModuleInfo *)GetPtr(segments[0].p_paddr & 0x7FFFFFFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool kernelModule = modInfo ? (modInfo->moduleAttrs & 0x1000) != 0 : false;
|
2015-07-02 13:55:09 +00:00
|
|
|
|
2014-07-15 15:38:38 +00:00
|
|
|
std::string modName = "ELF";
|
|
|
|
if (modInfo) {
|
|
|
|
size_t n = strnlen(modInfo->name, 28);
|
|
|
|
modName = "ELF/" + std::string(modInfo->name, n);
|
|
|
|
}
|
2014-07-15 15:37:49 +00:00
|
|
|
|
2012-11-01 15:19:01 +00:00
|
|
|
entryPoint = header->e_entry;
|
2012-11-05 12:30:46 +00:00
|
|
|
u32 totalStart = 0xFFFFFFFF;
|
|
|
|
u32 totalEnd = 0;
|
2012-11-17 18:56:28 +00:00
|
|
|
for (int i = 0; i < header->e_phnum; i++) {
|
2012-11-05 12:30:46 +00:00
|
|
|
Elf32_Phdr *p = &segments[i];
|
|
|
|
if (p->p_type == PT_LOAD) {
|
|
|
|
if (p->p_vaddr < totalStart)
|
|
|
|
totalStart = p->p_vaddr;
|
|
|
|
if (p->p_vaddr + p->p_memsz > totalEnd)
|
|
|
|
totalEnd = p->p_vaddr + p->p_memsz;
|
2012-11-04 18:01:20 +00:00
|
|
|
}
|
|
|
|
}
|
2013-04-11 04:03:43 +00:00
|
|
|
totalSize = totalEnd - totalStart;
|
2015-07-02 13:55:09 +00:00
|
|
|
|
|
|
|
// If a load address is specified that's in regular RAM, override kernel module status
|
2015-07-02 21:07:07 +00:00
|
|
|
bool inUser = totalStart >= PSP_GetUserMemoryBase();
|
2015-07-02 13:55:09 +00:00
|
|
|
BlockAllocator &memblock = (kernelModule && !inUser) ? kernelMemory : userMemory;
|
|
|
|
|
2013-01-04 19:09:39 +00:00
|
|
|
if (!bRelocate)
|
|
|
|
{
|
|
|
|
// Binary is prerelocated, load it where the first segment starts
|
2014-07-15 15:38:38 +00:00
|
|
|
vaddr = memblock.AllocAt(totalStart, totalSize, modName.c_str());
|
2013-01-04 19:09:39 +00:00
|
|
|
}
|
|
|
|
else if (loadAddress)
|
|
|
|
{
|
|
|
|
// Binary needs to be relocated: add loadAddress to the binary start address
|
2014-07-15 15:38:38 +00:00
|
|
|
vaddr = memblock.AllocAt(loadAddress + totalStart, totalSize, modName.c_str());
|
2013-01-04 19:09:39 +00:00
|
|
|
}
|
2012-11-04 18:01:20 +00:00
|
|
|
else
|
2013-01-04 19:09:39 +00:00
|
|
|
{
|
|
|
|
// Just put it where there is room
|
2014-07-15 15:38:38 +00:00
|
|
|
vaddr = memblock.Alloc(totalSize, fromTop, modName.c_str());
|
2013-01-04 19:09:39 +00:00
|
|
|
}
|
2012-11-04 18:01:20 +00:00
|
|
|
|
2013-02-18 16:44:59 +00:00
|
|
|
if (vaddr == (u32)-1) {
|
2013-06-01 16:29:38 +00:00
|
|
|
ERROR_LOG_REPORT(LOADER, "Failed to allocate memory for ELF!");
|
2013-10-05 08:05:55 +00:00
|
|
|
return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED;
|
2012-11-06 18:22:14 +00:00
|
|
|
}
|
2015-07-02 13:55:09 +00:00
|
|
|
|
2012-11-05 12:30:46 +00:00
|
|
|
if (bRelocate) {
|
2012-11-01 15:19:01 +00:00
|
|
|
DEBUG_LOG(LOADER,"Relocatable module");
|
|
|
|
entryPoint += vaddr;
|
2012-11-05 12:30:46 +00:00
|
|
|
} else {
|
2012-11-01 15:19:01 +00:00
|
|
|
DEBUG_LOG(LOADER,"Prerelocated executable");
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG_LOG(LOADER,"%i segments:", header->e_phnum);
|
|
|
|
|
|
|
|
// First pass : Get the damn bits into RAM
|
|
|
|
u32 baseAddress = bRelocate?vaddr:0;
|
2012-12-27 20:32:12 +00:00
|
|
|
|
2012-11-01 15:19:01 +00:00
|
|
|
for (int i=0; i<header->e_phnum; i++)
|
|
|
|
{
|
|
|
|
Elf32_Phdr *p = segments + i;
|
2013-05-31 10:34:47 +00:00
|
|
|
DEBUG_LOG(LOADER, "Type: %08x Vaddr: %08x Filesz: %08x Memsz: %08x ", (int)p->p_type, (u32)p->p_vaddr, (int)p->p_filesz, (int)p->p_memsz);
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
if (p->p_type == PT_LOAD)
|
|
|
|
{
|
|
|
|
segmentVAddr[i] = baseAddress + p->p_vaddr;
|
|
|
|
u32 writeAddr = segmentVAddr[i];
|
|
|
|
|
|
|
|
u8 *src = GetSegmentPtr(i);
|
|
|
|
u8 *dst = Memory::GetPointer(writeAddr);
|
|
|
|
u32 srcSize = p->p_filesz;
|
|
|
|
u32 dstSize = p->p_memsz;
|
|
|
|
|
|
|
|
if (srcSize < dstSize)
|
|
|
|
{
|
2012-11-17 18:56:28 +00:00
|
|
|
memset(dst + srcSize, 0, dstSize - srcSize); //zero out bss
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2012-11-17 18:56:28 +00:00
|
|
|
memcpy(dst, src, srcSize);
|
2014-05-04 08:22:57 +00:00
|
|
|
CBreakPoints::ExecMemCheck(writeAddr, true, dstSize, currentMIPS->pc);
|
2012-11-01 15:19:01 +00:00
|
|
|
DEBUG_LOG(LOADER,"Loadable Segment Copied to %08x, size %08x", writeAddr, (u32)p->p_memsz);
|
|
|
|
}
|
|
|
|
}
|
2014-07-15 15:37:49 +00:00
|
|
|
memblock.ListBlocks();
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
DEBUG_LOG(LOADER,"%i sections:", header->e_shnum);
|
|
|
|
|
|
|
|
for (int i = 0; i < GetNumSections(); i++)
|
|
|
|
{
|
|
|
|
Elf32_Shdr *s = §ions[i];
|
|
|
|
const char *name = GetSectionName(i);
|
|
|
|
|
|
|
|
u32 writeAddr = s->sh_addr + baseAddress;
|
|
|
|
sectionOffsets[i] = writeAddr - vaddr;
|
|
|
|
sectionAddrs[i] = writeAddr;
|
|
|
|
|
|
|
|
if (s->sh_flags & SHF_ALLOC)
|
|
|
|
{
|
|
|
|
DEBUG_LOG(LOADER,"Data Section found: %s Sitting at %08x, size %08x", name, writeAddr, (u32)s->sh_size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DEBUG_LOG(LOADER,"NonData Section found: %s Ignoring (size=%08x) (flags=%08x)", name, (u32)s->sh_size, (u32)s->sh_flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG_LOG(LOADER,"Relocations:");
|
|
|
|
|
|
|
|
// Second pass: Do necessary relocations
|
2013-11-14 10:43:09 +00:00
|
|
|
for (int i = 0; i < GetNumSections(); i++)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
|
|
|
Elf32_Shdr *s = §ions[i];
|
|
|
|
const char *name = GetSectionName(i);
|
|
|
|
|
|
|
|
if (s->sh_type == SHT_PSPREL)
|
|
|
|
{
|
|
|
|
//We have a relocation table!
|
|
|
|
int sectionToModify = s->sh_info;
|
2013-03-23 12:25:43 +00:00
|
|
|
if (sectionToModify >= 0)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-03-23 12:25:43 +00:00
|
|
|
if (!(sections[sectionToModify].sh_flags & SHF_ALLOC))
|
|
|
|
{
|
2013-06-01 16:29:38 +00:00
|
|
|
ERROR_LOG_REPORT(LOADER, "Trying to relocate non-loaded section %s", GetSectionName(sectionToModify));
|
2013-03-23 12:25:43 +00:00
|
|
|
continue;
|
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-03-23 12:25:43 +00:00
|
|
|
int numRelocs = s->sh_size / sizeof(Elf32_Rel);
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-03-23 12:25:43 +00:00
|
|
|
Elf32_Rel *rels = (Elf32_Rel *)GetSectionDataPtr(i);
|
|
|
|
|
2013-11-14 21:45:34 +00:00
|
|
|
DEBUG_LOG(LOADER,"%s: Performing %i relocations on %s : offset = %08x", name, numRelocs, GetSectionName(sectionToModify), sections[i].sh_offset);
|
2013-11-14 10:43:09 +00:00
|
|
|
if (!LoadRelocations(rels, numRelocs)) {
|
2013-11-14 21:45:34 +00:00
|
|
|
WARN_LOG(LOADER, "LoadInto: Relocs failed, trying anyway");
|
2013-11-14 10:43:09 +00:00
|
|
|
}
|
2013-03-23 12:25:43 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-06-01 16:29:38 +00:00
|
|
|
WARN_LOG_REPORT(LOADER, "sectionToModify = %i - ignoring PSP relocation sector %i", sectionToModify, i);
|
2013-03-23 12:25:43 +00:00
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
else if (s->sh_type == SHT_REL)
|
|
|
|
{
|
|
|
|
DEBUG_LOG(LOADER, "Traditional relocation section found.");
|
2013-01-02 11:35:37 +00:00
|
|
|
if (!bRelocate)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
|
|
|
DEBUG_LOG(LOADER, "Binary is prerelocated. Skipping relocations.");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-01-02 11:35:37 +00:00
|
|
|
//We have a relocation table!
|
|
|
|
int sectionToModify = s->sh_info;
|
2013-03-23 12:25:43 +00:00
|
|
|
if (sectionToModify >= 0)
|
2013-01-02 11:35:37 +00:00
|
|
|
{
|
2013-03-23 12:25:43 +00:00
|
|
|
if (!(sections[sectionToModify].sh_flags & SHF_ALLOC))
|
|
|
|
{
|
2013-06-01 16:29:38 +00:00
|
|
|
ERROR_LOG_REPORT(LOADER, "Trying to relocate non-loaded section %s, ignoring", GetSectionName(sectionToModify));
|
2013-03-23 12:25:43 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-06-01 16:29:38 +00:00
|
|
|
WARN_LOG_REPORT(LOADER, "sectionToModify = %i - ignoring relocation sector %i", sectionToModify, i);
|
2013-01-02 11:35:37 +00:00
|
|
|
}
|
2013-06-01 16:29:38 +00:00
|
|
|
ERROR_LOG_REPORT(LOADER, "Traditional relocations unsupported.");
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-27 20:32:12 +00:00
|
|
|
// Segment relocations (a few games use them)
|
2013-11-14 10:43:09 +00:00
|
|
|
if (GetNumSections() == 0) {
|
|
|
|
for (int i = 0; i < header->e_phnum; i++)
|
2012-12-27 20:32:12 +00:00
|
|
|
{
|
2012-12-28 00:05:03 +00:00
|
|
|
Elf32_Phdr *p = &segments[i];
|
2013-11-16 09:56:58 +00:00
|
|
|
if (p->p_type == PT_PSPREL1) {
|
2012-12-28 00:05:03 +00:00
|
|
|
INFO_LOG(LOADER,"Loading segment relocations");
|
|
|
|
int numRelocs = p->p_filesz / sizeof(Elf32_Rel);
|
2012-12-27 20:32:12 +00:00
|
|
|
|
2012-12-28 00:05:03 +00:00
|
|
|
Elf32_Rel *rels = (Elf32_Rel *)GetSegmentPtr(i);
|
2013-11-14 10:43:09 +00:00
|
|
|
if (!LoadRelocations(rels, numRelocs)) {
|
|
|
|
ERROR_LOG(LOADER, "LoadInto: Relocs failed, trying anyway (2)");
|
|
|
|
}
|
2013-11-16 09:56:58 +00:00
|
|
|
} else if (p->p_type == PT_PSPREL2) {
|
2013-05-31 10:34:47 +00:00
|
|
|
INFO_LOG(LOADER,"Loading segment relocations2");
|
|
|
|
LoadRelocations2(i);
|
2012-12-28 00:05:03 +00:00
|
|
|
}
|
2012-12-27 20:32:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-05 08:05:55 +00:00
|
|
|
return SCE_KERNEL_ERROR_OK;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-03 20:16:11 +00:00
|
|
|
SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
|
|
|
for (int i = firstSection; i < header->e_shnum; i++)
|
|
|
|
{
|
|
|
|
const char *secname = GetSectionName(i);
|
|
|
|
|
|
|
|
if (secname != 0 && strcmp(name, secname) == 0)
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-07-14 02:53:00 +00:00
|
|
|
u32 ElfReader::GetTotalTextSize() const {
|
|
|
|
u32 total = 0;
|
|
|
|
for (int i = 0; i < GetNumSections(); ++i) {
|
2014-07-20 19:09:06 +00:00
|
|
|
if (!(sections[i].sh_flags & SHF_WRITE) && (sections[i].sh_flags & SHF_ALLOC) && !(sections[i].sh_flags & SHF_STRINGS)) {
|
2014-07-14 02:53:00 +00:00
|
|
|
total += sections[i].sh_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 ElfReader::GetTotalDataSize() const {
|
|
|
|
u32 total = 0;
|
|
|
|
for (int i = 0; i < GetNumSections(); ++i) {
|
|
|
|
if ((sections[i].sh_flags & SHF_WRITE) && (sections[i].sh_flags & SHF_ALLOC) && !(sections[i].sh_flags & SHF_MASKPROC)) {
|
|
|
|
total += sections[i].sh_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2014-07-20 19:09:58 +00:00
|
|
|
u32 ElfReader::GetTotalSectionSizeByPrefix(const std::string &prefix) const {
|
|
|
|
u32 total = 0;
|
|
|
|
for (int i = 0; i < GetNumSections(); ++i) {
|
|
|
|
const char *secname = GetSectionName(i);
|
|
|
|
if (secname && !strncmp(secname, prefix.c_str(), prefix.length())) {
|
|
|
|
total += sections[i].sh_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2012-11-01 15:19:01 +00:00
|
|
|
bool ElfReader::LoadSymbols()
|
|
|
|
{
|
|
|
|
bool hasSymbols = false;
|
|
|
|
SectionID sec = GetSectionByName(".symtab");
|
|
|
|
if (sec != -1)
|
|
|
|
{
|
|
|
|
int stringSection = sections[sec].sh_link;
|
|
|
|
|
|
|
|
const char *stringBase = (const char*)GetSectionDataPtr(stringSection);
|
|
|
|
|
|
|
|
//We have a symbol table!
|
|
|
|
Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec));
|
|
|
|
|
|
|
|
int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym);
|
|
|
|
|
|
|
|
for (int sym = 0; sym<numSymbols; sym++)
|
|
|
|
{
|
|
|
|
int size = symtab[sym].st_size;
|
|
|
|
if (size == 0)
|
|
|
|
continue;
|
|
|
|
|
2012-11-05 09:05:09 +00:00
|
|
|
int bind = symtab[sym].st_info >> 4;
|
2012-11-01 15:19:01 +00:00
|
|
|
int type = symtab[sym].st_info & 0xF;
|
|
|
|
int sectionIndex = symtab[sym].st_shndx;
|
|
|
|
int value = symtab[sym].st_value;
|
|
|
|
const char *name = stringBase + symtab[sym].st_name;
|
|
|
|
|
|
|
|
if (bRelocate)
|
|
|
|
value += sectionAddrs[sectionIndex];
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case STT_OBJECT:
|
2015-10-31 22:01:19 +00:00
|
|
|
g_symbolMap->AddData(value,size,DATATYPE_BYTE);
|
2013-11-27 12:33:30 +00:00
|
|
|
break;
|
2012-11-01 15:19:01 +00:00
|
|
|
case STT_FUNC:
|
2015-10-31 22:01:19 +00:00
|
|
|
g_symbolMap->AddFunction(name,value,size);
|
2013-11-27 12:33:30 +00:00
|
|
|
break;
|
2012-11-01 15:19:01 +00:00
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
hasSymbols = true;
|
|
|
|
//...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return hasSymbols;
|
|
|
|
}
|