mirror of
https://github.com/cemu-project/ida_game_elf_loaders.git
synced 2024-11-26 21:00:37 +00:00
First commit
This commit is contained in:
commit
991de71c14
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "third_party/tinyxml"]
|
||||
path = third_party/tinyxml
|
||||
url = https://github.com/icebreaker/TinyXML
|
29
ReadMe.md
Normal file
29
ReadMe.md
Normal file
@ -0,0 +1,29 @@
|
||||
# ida_game_elf_loaders
|
||||
A collection of user mode ELF loaders for the following game consoles:
|
||||
* PS3
|
||||
* PS Vita
|
||||
|
||||
## Installation
|
||||
Copy loader plugins to IDA loaders directory.
|
||||
|
||||
## Building
|
||||
|
||||
### Dependencies
|
||||
* IDA SDK
|
||||
* [CMake](https://cmake.org/download/)
|
||||
|
||||
### Generate Projects With CMake
|
||||
The IDA cmake module included will expect to find the IDA SDK in an `IDA_SDK_DIR` or `IDA_SDK` environment variable.
|
||||
If you would like to generate 64-bit EA targeted loaders, you need to add `-DIDA_64_BIT_EA_T=YES` to cmake command line.
|
||||
|
||||
Navigate to the directory of the loader you would like to build in 'src/', then run the following command
|
||||
`mkdir build && cd build && cmake ../`
|
||||
|
||||
This should create a build directory with your generated project files.
|
||||
|
||||
### Building
|
||||
Optionally, you can also build using cmake with the following command
|
||||
`cmake --build ./`
|
||||
|
||||
## Notes
|
||||
These have only been tested and built using Visual Studio 2015 using IDA SDK 6.8.
|
144
cmake/FindIDA.cmake
Normal file
144
cmake/FindIDA.cmake
Normal file
@ -0,0 +1,144 @@
|
||||
set(IDA_FOUND FALSE)
|
||||
set(IDA_SDK_FOUND FALSE)
|
||||
|
||||
#
|
||||
# Find IDA.
|
||||
#
|
||||
|
||||
find_path(IDA_PATH
|
||||
NAME "idag.exe" "idaq.exe"
|
||||
HINTS $ENV{IDA_DIR} $ENV{IDADIR}
|
||||
PATHS "C:/Program Files/IDA" "C:/Program Files (x86)/IDA"
|
||||
DOC "IDA installation directory.")
|
||||
|
||||
if(IDA_PATH)
|
||||
set(IDA_FOUND TRUE)
|
||||
message(STATUS "Looking for IDA - found at ${IDA_PATH}")
|
||||
else()
|
||||
message(STATUS "Looking for IDA - not found")
|
||||
endif()
|
||||
|
||||
#
|
||||
# Make up the name of the SDK library subdirectory.
|
||||
#
|
||||
|
||||
# Detect the platform.
|
||||
set(platform "unknown")
|
||||
if(WIN32)
|
||||
set(platform "win")
|
||||
endif()
|
||||
if(UNIX)
|
||||
set(platform "linux")
|
||||
endif()
|
||||
if(APPLE)
|
||||
set(platform "mac")
|
||||
endif()
|
||||
|
||||
# Detect the compiler.
|
||||
set(compiler "unknown")
|
||||
if(BORLAND)
|
||||
set(compiler "bcc")
|
||||
endif()
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR ${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
set(compiler "gcc")
|
||||
endif()
|
||||
if(MSVC)
|
||||
set(compiler "vc")
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR ${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
set(compiler "gcc")
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(compiler "vc")
|
||||
endif()
|
||||
|
||||
set(IDA_64_BIT_EA_T OFF CACHE BOOL "Use 64-bit ea_t. Set this to build 64-bit code capable IDA plugins.")
|
||||
|
||||
if(IDA_64_BIT_EA_T)
|
||||
set(suffix "64")
|
||||
else()
|
||||
set(suffix "32")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT NC_M32)
|
||||
set(library_dir "lib/x64_${platform}_${compiler}_${suffix}")
|
||||
else()
|
||||
set(library_dir "lib/x86_${platform}_${compiler}_${suffix}")
|
||||
endif()
|
||||
|
||||
#
|
||||
# Find IDA SDK.
|
||||
#
|
||||
find_path(IDA_SDK_PATH
|
||||
NAME ${library_dir}
|
||||
HINTS $ENV{IDA_SDK_DIR} $ENV{IDA_SDK}
|
||||
PATHS "${IDA_PATH}/sdk"
|
||||
DOC "IDA SDK directory.")
|
||||
|
||||
if(IDA_SDK_PATH)
|
||||
set(IDA_SDK_FOUND TRUE)
|
||||
set(IDA_INCLUDE_DIR ${IDA_SDK_PATH}/include)
|
||||
set(IDA_LIBRARY_DIR ${IDA_SDK_PATH}/${library_dir})
|
||||
|
||||
if(MSVC)
|
||||
file(GLOB IDA_LIBRARIES "${IDA_LIBRARY_DIR}/*.lib")
|
||||
else()
|
||||
file(GLOB IDA_LIBRARIES "${IDA_LIBRARY_DIR}/*.a")
|
||||
endif()
|
||||
|
||||
set(IDA_DEFINITIONS -D__IDP__)
|
||||
if(WIN32)
|
||||
set(IDA_DEFINITIONS ${IDA_DEFINITIONS} -D__NT__)
|
||||
endif()
|
||||
if(UNIX)
|
||||
set(IDA_DEFINITIONS ${IDA_DEFINITIONS} -D__LINUX__)
|
||||
endif()
|
||||
if(APPLE)
|
||||
set(IDA_DEFINITIONS ${IDA_DEFINITIONS} -D__MAC__)
|
||||
endif()
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT NC_M32)
|
||||
set(IDA_DEFINITIONS ${IDA_DEFINITIONS} -D__X64__)
|
||||
endif()
|
||||
if(IDA_64_BIT_EA_T)
|
||||
set(IDA_DEFINITIONS ${IDA_DEFINITIONS} -D__EA64__)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if(IDA_64_BIT_EA_T)
|
||||
set(IDA_PLUGIN_EXT "64.l64")
|
||||
else()
|
||||
set(IDA_PLUGIN_EXT ".ldw")
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
if(IDA_64_BIT_EA_T)
|
||||
set(IDA_PLUGIN_EXT "64.lmc64")
|
||||
set(IDA_SHARED_LIB_NAME ida64)
|
||||
else()
|
||||
set(IDA_PLUGIN_EXT ".lmc")
|
||||
set(IDA_SHARED_LIB_NAME ida)
|
||||
endif()
|
||||
if (IDA_PATH)
|
||||
file(GLOB_RECURSE IDA_SHARED_LIBRARY "${IDA_PATH}/*/lib${IDA_SHARED_LIB_NAME}.dylib")
|
||||
else()
|
||||
file(GLOB_RECURSE IDA_SHARED_LIBRARY "/Applications/IDA*/lib${IDA_SHARED_LIB_NAME}.dylib")
|
||||
endif()
|
||||
set(IDA_LIBRARIES ${IDA_LIBRARIES} ${IDA_SHARED_LIBRARY})
|
||||
elseif(UNIX)
|
||||
if(IDA_64_BIT_EA_T)
|
||||
set(IDA_PLUGIN_EXT "64.llx64")
|
||||
else()
|
||||
set(IDA_PLUGIN_EXT ".llx")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "Looking for IDA SDK - found at ${IDA_SDK_PATH}")
|
||||
else()
|
||||
message(STATUS "Looking for IDA SDK - not found")
|
||||
endif()
|
||||
|
||||
unset(platform)
|
||||
unset(compiler)
|
||||
unset(suffix)
|
||||
unset(library_dir)
|
3753
src/elf_common/elf.h
Normal file
3753
src/elf_common/elf.h
Normal file
File diff suppressed because it is too large
Load Diff
432
src/elf_common/elf_reader.h
Normal file
432
src/elf_common/elf_reader.h
Normal file
@ -0,0 +1,432 @@
|
||||
|
||||
/**
|
||||
* A very basic template based ELF reader. It is very common for OS's
|
||||
* to implement their own ELF ABI. Section and segment headers are most
|
||||
* commonly kept standard.
|
||||
*
|
||||
* Assumes that:
|
||||
* - Elf_Ehdr is standard.
|
||||
* - Elf_Shdr is standard.
|
||||
* - Elf_Phdr is standard.
|
||||
* - Only one SYMTAB section according to ELF ABI.
|
||||
* - This system is little endian. If there is a problem with this,
|
||||
* please let the author know.
|
||||
*
|
||||
* What it stores info for:
|
||||
* - Elf_Ehdr as read from file.
|
||||
* - vector of Elf_Shdr abstracted as Section.
|
||||
* - vector of Elf_Phdr abstracted as Segment.
|
||||
* - index of symbol section.
|
||||
* - index of dynamic segment. <- TODO
|
||||
* - index of section header string table section.
|
||||
*
|
||||
* Important facts about this reader:
|
||||
* - Elf_Ehdr, Elf_Shdr, and Elf_Phdr is swapped if ELFDATA2MSB is set.
|
||||
* This is to facilitate faster header/section/segment handling without
|
||||
* the user needing to swap them.
|
||||
* - Section/segment data is not modified by this reader.
|
||||
* - Any section/segment data must be swapped by the user.
|
||||
* - Data is only loaded when requested. (see Segment/Section data())
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "elf.h"
|
||||
|
||||
#include <idaldr.h> // TODO: do not depend on this
|
||||
#include <vector>
|
||||
|
||||
static void printhex(const unsigned char *data, size_t size)
|
||||
{
|
||||
msg("00000000 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n");
|
||||
for (size_t i = 0; i < size; i += 16)
|
||||
{
|
||||
msg("%08x ", i);
|
||||
msg("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
data[i + 0], data[i + 1], data[i + 2], data[i + 3], data[i + 4],
|
||||
data[i + 5], data[i + 6], data[i + 7], data[i + 8], data[i + 9],
|
||||
data[i + 10], data[i + 11], data[i + 12], data[i + 13], data[i + 14],
|
||||
data[i + 15]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void swap(T &buf) {
|
||||
unsigned char &pbuf = reinterpret_cast<unsigned char &>(buf);
|
||||
std::reverse(&pbuf, &pbuf + sizeof(T));
|
||||
}
|
||||
|
||||
class elf32 {
|
||||
public:
|
||||
typedef Elf32_Ehdr Ehdr;
|
||||
typedef Elf32_Shdr Shdr;
|
||||
typedef Elf32_Phdr Phdr;
|
||||
typedef Elf32_Sym Sym;
|
||||
typedef Elf32_Word Word;
|
||||
typedef Elf32_Addr Addr;
|
||||
};
|
||||
|
||||
class elf64 {
|
||||
public:
|
||||
typedef Elf64_Ehdr Ehdr;
|
||||
typedef Elf64_Shdr Shdr;
|
||||
typedef Elf64_Phdr Phdr;
|
||||
typedef Elf64_Sym Sym;
|
||||
typedef Elf64_Word Word;
|
||||
typedef Elf64_Addr Addr;
|
||||
};
|
||||
|
||||
template <class Elf>
|
||||
class Segment
|
||||
: public Elf::Phdr {
|
||||
linput_t *m_reader;
|
||||
std::vector<char> m_data;
|
||||
|
||||
public:
|
||||
Segment() {}
|
||||
|
||||
Segment(linput_t *li)
|
||||
: m_reader(li)
|
||||
{
|
||||
}
|
||||
|
||||
char *data()
|
||||
{
|
||||
if (m_data.empty())
|
||||
{
|
||||
m_data.resize(this->p_filesz);
|
||||
qlseek(m_reader, this->p_offset);
|
||||
qlread(m_reader, (void *)m_data.data(), this->p_filesz);
|
||||
}
|
||||
|
||||
return m_data.data();
|
||||
}
|
||||
|
||||
void setData(const char *data, size_t length)
|
||||
{
|
||||
m_data.assign(data, length);
|
||||
}
|
||||
|
||||
void setReader(linput_t *li)
|
||||
{
|
||||
this->m_reader = li;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Elf>
|
||||
class Section
|
||||
: public Elf::Shdr {
|
||||
linput_t *m_reader;
|
||||
std::vector<char> m_data; // TODO: switch to std::vector
|
||||
|
||||
public:
|
||||
Section() {}
|
||||
|
||||
char *data()
|
||||
{
|
||||
if (m_data.empty()) {
|
||||
m_data.resize(this->sh_size);
|
||||
if (qlseek(m_reader, this->sh_offset) != this->sh_offset)
|
||||
msg("Failed to seek to data.\n");
|
||||
if (qlread(m_reader, (void *)m_data.data(), this->sh_size) == -1)
|
||||
msg("Failed to read data.\n");
|
||||
}
|
||||
|
||||
return m_data.data();
|
||||
}
|
||||
|
||||
void setData(const char *data, size_t length)
|
||||
{
|
||||
m_data.assign(data, length);
|
||||
}
|
||||
|
||||
void setReader(linput_t *li)
|
||||
{
|
||||
this->m_reader = li;
|
||||
}
|
||||
|
||||
uint32 getNumEntries() const
|
||||
{
|
||||
if (this->sh_entsize != 0)
|
||||
return this->sh_size / this->sh_entsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 getSize() const
|
||||
{
|
||||
return m_data.size();
|
||||
}
|
||||
};
|
||||
|
||||
template <
|
||||
class Elf // Elf type
|
||||
/*TODO: class reader*/ // reader interface
|
||||
/*TODO: class logger*/ // log file interface
|
||||
>
|
||||
class elf_reader {
|
||||
typename Elf::Ehdr m_header;
|
||||
std::vector< Segment<Elf> > m_segments;
|
||||
std::vector< Section<Elf> > m_sections;
|
||||
Section<Elf> *m_symbolTableSection;
|
||||
Section<Elf> *m_sectionStringTable;
|
||||
|
||||
linput_t *m_reader;
|
||||
|
||||
public:
|
||||
elf_reader(linput_t *li)
|
||||
: m_reader(li)
|
||||
{
|
||||
m_symbolTableSection = NULL;
|
||||
m_sectionStringTable = NULL;
|
||||
}
|
||||
|
||||
void read() {
|
||||
readHeader();
|
||||
readSegments();
|
||||
readSections();
|
||||
}
|
||||
|
||||
void print() {
|
||||
printHeader();
|
||||
printSegment();
|
||||
printSections();
|
||||
printSymbols();
|
||||
}
|
||||
|
||||
bool verifyHeader() {
|
||||
readHeader();
|
||||
|
||||
if (m_header.e_ident[EI_MAG0] == ELFMAG0 &&
|
||||
m_header.e_ident[EI_MAG1] == ELFMAG1 &&
|
||||
m_header.e_ident[EI_MAG2] == ELFMAG2 &&
|
||||
m_header.e_ident[EI_MAG3] == ELFMAG3) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
linput_t *getReader() const
|
||||
{ return this->m_reader; }
|
||||
|
||||
uchar osabi() const
|
||||
{ return m_header.e_ident[EI_OSABI]; }
|
||||
|
||||
uchar bitsize() const
|
||||
{ return m_header.e_ident[EI_CLASS]; }
|
||||
|
||||
uchar endian() const
|
||||
{ return m_header.e_ident[EI_DATA]; }
|
||||
|
||||
typename Elf::Word type() const
|
||||
{ return m_header.e_type; }
|
||||
|
||||
typename Elf::Word machine() const
|
||||
{ return m_header.e_machine; }
|
||||
|
||||
typename Elf::Addr entry() const
|
||||
{ return m_header.e_entry; }
|
||||
|
||||
typename Elf::Word flags() const
|
||||
{ return m_header.e_flags; }
|
||||
|
||||
uint32_t getNumSegments() const
|
||||
{ return m_segments.size(); }
|
||||
|
||||
uint32_t getNumSections() const
|
||||
{ return m_sections.size(); }
|
||||
|
||||
std::vector< Segment<Elf> > &getSegments()
|
||||
{ return m_segments; }
|
||||
|
||||
std::vector< Section<Elf> > &getSections()
|
||||
{ return m_sections; }
|
||||
|
||||
Section<Elf> *getSectionStringTable() const
|
||||
{ return m_sectionStringTable; }
|
||||
|
||||
Section<Elf> *getSymbolsSection() const
|
||||
{ return m_symbolTableSection; }
|
||||
|
||||
uint32_t getNumSymbols() const
|
||||
{ return m_symbolTableSection->getNumEntries(); }
|
||||
|
||||
typename Elf::Sym *getSymbols() const
|
||||
{ return (typename Elf::Sym *)m_symbolTableSection->data(); }
|
||||
|
||||
Section<Elf> *getSectionByName(const char *name)
|
||||
{
|
||||
const char *strTab = m_sectionStringTable->data();
|
||||
for (auto §ion : m_sections) {
|
||||
if (strcmp(&strTab[section.sh_name], name) == 0)
|
||||
return §ion;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uchar getAlignment(typename Elf::Word align)
|
||||
{
|
||||
switch (align) {
|
||||
case 0x1: return saRelByte;
|
||||
case 0x2: return saRelWord;
|
||||
case 0x4: return saRelDble;
|
||||
case 0x8: return saRelQword;
|
||||
case 0x40: return saRel64Bytes;
|
||||
case 0x80: return saRel128Bytes;
|
||||
case 0x100: return saRelPage;
|
||||
case 0x200: return saRel512Bytes;
|
||||
case 0x400: return saRel2048Bytes;
|
||||
case 0x1000: return saRel4K;
|
||||
default: return saRelDble;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void readHeader() {
|
||||
//msg("Reading header.\n");
|
||||
|
||||
qlseek(m_reader, 0);
|
||||
qlread(m_reader, &m_header, sizeof(m_header));
|
||||
|
||||
if (m_header.e_ident[EI_DATA] == ELFDATA2MSB) {
|
||||
swap(m_header.e_type);
|
||||
swap(m_header.e_machine);
|
||||
swap(m_header.e_version);
|
||||
swap(m_header.e_entry);
|
||||
swap(m_header.e_phoff);
|
||||
swap(m_header.e_shoff);
|
||||
swap(m_header.e_flags);
|
||||
swap(m_header.e_ehsize);
|
||||
swap(m_header.e_phentsize);
|
||||
swap(m_header.e_phnum);
|
||||
swap(m_header.e_shentsize);
|
||||
swap(m_header.e_shnum);
|
||||
swap(m_header.e_shstrndx);
|
||||
}
|
||||
|
||||
//printHeader();
|
||||
}
|
||||
|
||||
void readSegments() {
|
||||
if (m_header.e_phnum > 0) {
|
||||
//msg("Reading segments.\n");
|
||||
m_segments.resize(m_header.e_phnum);
|
||||
|
||||
qlseek(m_reader, m_header.e_phoff);
|
||||
|
||||
for (auto &segment : m_segments) {
|
||||
qlread(m_reader, (typename Elf::Phdr *)&segment, m_header.e_phentsize);
|
||||
|
||||
if (m_header.e_ident[EI_DATA] == ELFDATA2MSB) {
|
||||
swap(segment.p_type);
|
||||
swap(segment.p_flags);
|
||||
swap(segment.p_offset);
|
||||
swap(segment.p_vaddr);
|
||||
swap(segment.p_paddr);
|
||||
swap(segment.p_filesz);
|
||||
swap(segment.p_memsz);
|
||||
swap(segment.p_align);
|
||||
}
|
||||
|
||||
segment.setReader(m_reader);
|
||||
}
|
||||
|
||||
//printSegments();
|
||||
}
|
||||
}
|
||||
|
||||
void readSections() {
|
||||
if (m_header.e_shnum > 0) {
|
||||
//msg("Reading sections...\n");
|
||||
|
||||
m_sections.resize(m_header.e_shnum);
|
||||
|
||||
size_t index = 0;
|
||||
for (auto §ion : m_sections) {
|
||||
qlseek(m_reader, m_header.e_shoff + index * m_header.e_shentsize);
|
||||
qlread(m_reader, (typename Elf::Shdr *)§ion, m_header.e_shentsize);
|
||||
|
||||
if (m_header.e_ident[EI_DATA] == ELFDATA2MSB) {
|
||||
swap(section.sh_name);
|
||||
swap(section.sh_type);
|
||||
swap(section.sh_flags);
|
||||
swap(section.sh_addr);
|
||||
swap(section.sh_offset);
|
||||
swap(section.sh_size);
|
||||
swap(section.sh_link);
|
||||
swap(section.sh_info);
|
||||
swap(section.sh_addralign);
|
||||
swap(section.sh_entsize);
|
||||
}
|
||||
|
||||
section.setReader(m_reader);
|
||||
|
||||
// only one symbol table per ELF
|
||||
if (section.sh_type == SHT_SYMTAB)
|
||||
m_symbolTableSection = §ion;
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
if (m_header.e_shstrndx != SHN_UNDEF &&
|
||||
m_sections[m_header.e_shstrndx].sh_type == SHT_STRTAB)
|
||||
m_sectionStringTable = &m_sections[m_header.e_shstrndx];
|
||||
|
||||
//printSections();
|
||||
}
|
||||
}
|
||||
|
||||
void printHeader() {
|
||||
msg("Elf Header:\n");
|
||||
msg(" e_ident ");
|
||||
for (int i = 0; i < EI_NIDENT; i++)
|
||||
msg(" %02x", m_header.e_ident[i]);
|
||||
msg("\n");
|
||||
msg(" e_type %04x\n", m_header.e_type);
|
||||
msg(" e_machine %04x\n", m_header.e_machine);
|
||||
msg(" e_version %08x\n", m_header.e_version);
|
||||
msg(" e_entry %08x\n", m_header.e_entry);
|
||||
msg(" e_phoff %08x\n", m_header.e_phoff);
|
||||
msg(" e_shoff %08x\n", m_header.e_shoff);
|
||||
msg(" e_flags %08x\n", m_header.e_flags);
|
||||
msg(" e_ehsize %d\n", m_header.e_ehsize);
|
||||
msg(" e_phentsize %d\n", m_header.e_phentsize);
|
||||
msg(" e_phnum %d\n", m_header.e_phnum);
|
||||
msg(" e_shentsize %d\n", m_header.e_shentsize);
|
||||
msg(" e_shnum %d\n", m_header.e_shnum);
|
||||
msg(" e_shstrndx %d\n", m_header.e_shstrndx);
|
||||
}
|
||||
|
||||
void printSegments() {
|
||||
size_t index = 0;
|
||||
for (auto &segment : m_segments) {
|
||||
msg("Program Header #%d\n", index);
|
||||
msg(" p_type %08x\n", segment.p_type);
|
||||
msg(" p_offset %08x\n", segment.p_offset);
|
||||
msg(" p_vaddr %08x\n", segment.p_vaddr);
|
||||
msg(" p_paddr %08x\n", segment.p_paddr);
|
||||
msg(" p_filesz %08x\n", segment.p_filesz);
|
||||
msg(" p_memsz %08x\n", segment.p_memsz);
|
||||
msg(" p_flags %08x\n", segment.p_flags);
|
||||
msg(" p_align %08x\n", segment.p_align);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void printSections() {
|
||||
size_t index = 0;
|
||||
for (auto §ion : m_sections) {
|
||||
msg("Section Header #%d\n", index);
|
||||
msg(" sh_name %08x\n", section.sh_name);
|
||||
msg(" sh_type %08x\n", section.sh_type);
|
||||
msg(" sh_addr %08x\n", section.sh_addr);
|
||||
msg(" sh_offset %08x\n", section.sh_offset);
|
||||
msg(" sh_size %08x\n", section.sh_size);
|
||||
msg(" sh_link %08x\n", section.sh_link);
|
||||
msg(" sh_info %08x\n", section.sh_info);
|
||||
msg(" sh_addralign %08x\n", section.sh_addralign);
|
||||
msg(" sh_entsize %08x\n", section.sh_entsize);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
};
|
35
src/ps3/CMakeLists.txt
Normal file
35
src/ps3/CMakeLists.txt
Normal file
@ -0,0 +1,35 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
project(ps3ldr)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/../../cmake)
|
||||
set(ELF_COMMON_PATH ${CMAKE_SOURCE_DIR}/../elf_common)
|
||||
set(THIRD_PARTY_PATH ${CMAKE_SOURCE_DIR}/../../third_party)
|
||||
|
||||
set(SOURCES
|
||||
${ELF_COMMON_PATH}/elf_reader.h
|
||||
${ELF_COMMON_PATH}/elf.h
|
||||
${THIRD_PARTY_PATH}/tinyxml/tinystr.cpp
|
||||
${THIRD_PARTY_PATH}/tinyxml/tinystr.h
|
||||
${THIRD_PARTY_PATH}/tinyxml/tinyxml.cpp
|
||||
${THIRD_PARTY_PATH}/tinyxml/tinyxml.h
|
||||
${THIRD_PARTY_PATH}/tinyxml/tinyxmlerror.cpp
|
||||
${THIRD_PARTY_PATH}/tinyxml/tinyxmlparser.cpp
|
||||
cell_loader.cpp
|
||||
cell_loader.h
|
||||
ps3.cpp
|
||||
sce.h
|
||||
)
|
||||
|
||||
find_package(IDA)
|
||||
|
||||
include_directories(${IDA_INCLUDE_DIR})
|
||||
include_directories(${IDA_SDK_PATH}/ldr)
|
||||
include_directories(${ELF_COMMON_PATH})
|
||||
include_directories(${THIRD_PARTY_PATH}/tinyxml)
|
||||
|
||||
add_definitions(${IDA_DEFINITIONS})
|
||||
add_definitions(-DUSE_STANDARD_FILE_FUNCTIONS) # for tinyxml...
|
||||
|
||||
add_library(ps3ldr SHARED ${SOURCES})
|
||||
target_link_libraries(ps3ldr ${IDA_LIBRARIES})
|
||||
set_target_properties(ps3ldr PROPERTIES OUTPUT_NAME "ps3" PREFIX "" SUFFIX "${IDA_PLUGIN_EXT}")
|
865
src/ps3/cell_loader.cpp
Normal file
865
src/ps3/cell_loader.cpp
Normal file
@ -0,0 +1,865 @@
|
||||
#include "cell_loader.h"
|
||||
|
||||
#include <idaldr.h>
|
||||
#include <struct.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
cell_loader::cell_loader(elf_reader<elf64> *elf,
|
||||
uint64 relocAddr,
|
||||
std::string databaseFile)
|
||||
: m_elf(elf)
|
||||
{
|
||||
m_hasSegSym = false;
|
||||
m_relocAddr = 0;
|
||||
|
||||
// only PRX's contain relocations
|
||||
if ( isLoadingPrx() )
|
||||
m_relocAddr = relocAddr;
|
||||
|
||||
inf.demnames |= DEMNAM_GCC3; // assume gcc3 names
|
||||
inf.af |= AF_PROCPTR; // Create function if data xref data->code32 exists
|
||||
|
||||
char databasePath[QMAXPATH];
|
||||
|
||||
if ( getsysfile(databasePath, QMAXFILE, databaseFile.c_str(), LDR_SUBDIR) == NULL )
|
||||
loader_failure("Could not locate database file (%s).\n", databaseFile.c_str());
|
||||
|
||||
if ( m_database.LoadFile(databasePath) == false )
|
||||
loader_failure("Failed to load database file (%s).\n", databaseFile.c_str());
|
||||
}
|
||||
|
||||
void cell_loader::apply() {
|
||||
declareStructures();
|
||||
|
||||
applySegments();
|
||||
|
||||
swapSymbols();
|
||||
|
||||
if ( isLoadingPrx() ) {
|
||||
// the only way I know to check if its a 0.85 PRX
|
||||
for ( auto &segment : m_elf->getSegments() ) {
|
||||
if ( segment.p_type == PT_SCE_SEGSYM ) {
|
||||
m_hasSegSym = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// we need gpValue for relocations on 0.85
|
||||
// otherwise, I don't think newer PRX's have
|
||||
// TOC based relocations. TOC is not set in
|
||||
// moduleInfo. It seems to always be zero.
|
||||
if ( m_hasSegSym ) {
|
||||
//msg("Looking for .toc section\n");
|
||||
auto tocSection = m_elf->getSectionByName(".toc");
|
||||
if ( tocSection ) {
|
||||
//msg("Found toc section!\n");
|
||||
m_gpValue = tocSection->sh_addr + m_relocAddr;
|
||||
}
|
||||
}
|
||||
|
||||
// gpValue can be found at sceModuleInfo->gp_value
|
||||
// 0.85 gpValue is base address of .toc
|
||||
applyRelocations();
|
||||
|
||||
// if not a 0.85 PRX
|
||||
if ( !m_hasSegSym ) {
|
||||
// p_paddr is an offset into the file.
|
||||
auto firstSegment = m_elf->getSegments()[0];
|
||||
m_gpValue = get_long( (firstSegment.p_vaddr + m_relocAddr) + // file offset to address
|
||||
(firstSegment.p_paddr - firstSegment.p_offset) +
|
||||
offsetof(_scemoduleinfo_ppu32, gp_value) );
|
||||
}
|
||||
|
||||
applyModuleInfo();
|
||||
} else if ( isLoadingExec() ) {
|
||||
// gpValue can be found at m_elf->entry() + 4
|
||||
// _start is actually what loads TOC which is hardcoded to lwz(entry + 4)
|
||||
// there are also function stubs which set TOC to a different value
|
||||
m_gpValue = get_long(m_elf->entry() + 4);
|
||||
|
||||
applyProcessInfo();
|
||||
|
||||
add_entry(0, m_elf->entry(), "_start", true);
|
||||
}
|
||||
|
||||
msg("gpValue = %08x\n", m_gpValue);
|
||||
|
||||
// set TOC in IDA
|
||||
ph.notify(processor_t::idp_notify(ph.loader+1), m_gpValue);
|
||||
|
||||
// we want to apply the symbols last so that symbols
|
||||
// always override our own custom symbols.
|
||||
applySymbols();
|
||||
}
|
||||
|
||||
void cell_loader::applySegments() {
|
||||
// we prefer section headers
|
||||
if ( m_elf->getNumSections() > 0 )
|
||||
applySectionHeaders();
|
||||
// otherwise load program headers
|
||||
else if ( m_elf->getNumSegments() > 0 )
|
||||
applyProgramHeaders();
|
||||
else
|
||||
loader_failure("No segments available!");
|
||||
}
|
||||
|
||||
void cell_loader::applySectionHeaders() {
|
||||
msg("Applying section headers...\n");
|
||||
auto §ions = m_elf->getSections();
|
||||
const char *strTab = m_elf->getSectionStringTable()->data();
|
||||
|
||||
size_t index = 0;
|
||||
for ( const auto §ion : sections ) {
|
||||
// only load allocatable sections
|
||||
if ( section.sh_flags & SHF_ALLOC &&
|
||||
section.sh_size > 0 ) {
|
||||
if ( section.sh_type == SHT_NULL )
|
||||
continue;
|
||||
|
||||
uchar perm = SEGPERM_READ;
|
||||
char *sclass;
|
||||
|
||||
if ( section.sh_flags & SHF_WRITE )
|
||||
perm |= SEGPERM_WRITE;
|
||||
if ( section.sh_flags & SHF_EXECINSTR )
|
||||
perm |= SEGPERM_EXEC;
|
||||
|
||||
if ( section.sh_flags & SHF_EXECINSTR )
|
||||
sclass = CLASS_CODE;
|
||||
else if ( section.sh_type == SHT_NOBITS )
|
||||
sclass = CLASS_BSS;
|
||||
else
|
||||
sclass = CLASS_DATA;
|
||||
|
||||
const char *name = NULL;
|
||||
if ( section.sh_name != NULL )
|
||||
name = &strTab[section.sh_name];
|
||||
|
||||
applySegment( index,
|
||||
section.sh_offset,
|
||||
section.sh_addr,
|
||||
section.sh_size,
|
||||
name,
|
||||
sclass,
|
||||
perm,
|
||||
m_elf->getAlignment(section.sh_addralign),
|
||||
(section.sh_type == SHT_NOBITS) ? false : true );
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cell_loader::applyProgramHeaders() {
|
||||
msg("Applying program headers...\n");
|
||||
auto &segments = m_elf->getSegments();
|
||||
|
||||
size_t index = 0;
|
||||
for ( const auto &segment : segments ) {
|
||||
if ( segment.p_memsz > 0 ) {
|
||||
uchar perm;
|
||||
char *sclass;
|
||||
|
||||
if ( segment.p_flags & PF_W ) // if its writable
|
||||
sclass = CLASS_DATA;
|
||||
if ( (segment.p_flags & PF_R) && // if its only readable
|
||||
!(segment.p_flags & PF_W) &&
|
||||
!(segment.p_flags & PF_X) )
|
||||
sclass = CLASS_CONST;
|
||||
if ( segment.p_flags & PF_X ) // if its executable
|
||||
sclass = CLASS_CODE;
|
||||
|
||||
if ( segment.p_filesz == 0 &&
|
||||
segment.p_memsz > 0 )
|
||||
sclass = CLASS_BSS;
|
||||
|
||||
if ( segment.p_flags & PF_X )
|
||||
perm |= SEGPERM_EXEC;
|
||||
if ( segment.p_flags & PF_W )
|
||||
perm |= SEGPERM_WRITE;
|
||||
if ( segment.p_flags & PF_R )
|
||||
perm |= SEGPERM_READ;
|
||||
|
||||
applySegment( index,
|
||||
segment.p_offset,
|
||||
segment.p_vaddr,
|
||||
segment.p_memsz,
|
||||
NULL,
|
||||
sclass,
|
||||
perm,
|
||||
m_elf->getAlignment(segment.p_align) );
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cell_loader::applySegment(uint32 sel,
|
||||
uint64 offset,
|
||||
uint64 addr,
|
||||
uint64 size,
|
||||
const char *name,
|
||||
const char *sclass,
|
||||
uchar perm,
|
||||
uchar align,
|
||||
bool load) {
|
||||
addr += m_relocAddr;
|
||||
|
||||
segment_t seg;
|
||||
seg.startEA = addr;
|
||||
seg.endEA = addr + size;
|
||||
seg.color = DEFCOLOR;
|
||||
seg.sel = sel;
|
||||
seg.bitness = 1;
|
||||
seg.orgbase = sel;
|
||||
seg.comb = scPub;
|
||||
seg.perm = perm;
|
||||
seg.flags = SFL_LOADER;
|
||||
seg.align = align;
|
||||
|
||||
set_selector(sel, 0);
|
||||
|
||||
if ( name == NULL )
|
||||
name = "";
|
||||
|
||||
add_segm_ex(&seg, name, sclass, NULL);
|
||||
|
||||
if ( load == true )
|
||||
file2base(m_elf->getReader(), offset, addr, addr + size, true);
|
||||
}
|
||||
|
||||
void cell_loader::applyRelocations() {
|
||||
if ( m_hasSegSym )
|
||||
applySectionRelocations(); // pretty much only for 0.85
|
||||
else
|
||||
applySegmentRelocations();
|
||||
}
|
||||
|
||||
void cell_loader::applySectionRelocations() {
|
||||
msg("Applying section based relocations..\n");
|
||||
|
||||
auto §ions = m_elf->getSections();
|
||||
auto symbols = m_elf->getSymbols();
|
||||
|
||||
for ( auto §ion : sections ) {
|
||||
// NOTE: the only SHT_RELA sections I see after 0.85
|
||||
// are non-allocatable so no reason to consider those
|
||||
if ( section.sh_type == SHT_RELA ) {
|
||||
|
||||
auto nrela = section.sh_size / sizeof(Elf64_Rela);
|
||||
auto relocations = reinterpret_cast<Elf64_Rela *>(section.data());
|
||||
|
||||
for ( size_t i = 0; i < nrela; ++i ) {
|
||||
auto &rela = relocations[i];
|
||||
|
||||
swap(rela.r_offset);
|
||||
swap(rela.r_info);
|
||||
swap(rela.r_addend);
|
||||
|
||||
uint32 type = ELF64_R_TYPE(rela.r_info);
|
||||
uint32 sym = ELF64_R_SYM (rela.r_info);
|
||||
|
||||
//msg("r_type: %08x\n", type);
|
||||
//msg("r_sym: %08x\n", sym);
|
||||
|
||||
if ( type == R_PPC64_NONE ) {
|
||||
msg("Skipping relocation..\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( type > R_PPC64_TLSGD ) {
|
||||
msg("Invalid relocation type (%i)!\n", type);
|
||||
continue;
|
||||
}
|
||||
|
||||
//msg("nsyms = %08x\n", m_elf->getNumSymbols());
|
||||
//msg("symsec = %04x\n", symbols[ sym ].st_shndx);
|
||||
|
||||
if ( sym > m_elf->getNumSymbols() ) {
|
||||
msg("Invalid symbol index!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if ( symbols[ sym ].st_shndx > m_elf->getNumSections() ) {
|
||||
if ( symbols[ sym ].st_shndx != SHN_ABS ) {
|
||||
msg("Invalid symbol section index!\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 addr = sections[ section.sh_info ].sh_addr +
|
||||
rela.r_offset;
|
||||
uint32 saddr = sections[ symbols[ sym ].st_shndx ].sh_addr +
|
||||
symbols[ sym ].st_value + rela.r_addend;
|
||||
|
||||
applyRelocation(type, addr, saddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cell_loader::applySegmentRelocations() {
|
||||
msg("Applying segment based relocations..\n");
|
||||
|
||||
auto &segments = m_elf->getSegments();
|
||||
|
||||
for ( auto &segment : segments ) {
|
||||
if ( segment.p_type == PT_SCE_PPURELA ) {
|
||||
auto nrela = segment.p_filesz / sizeof(Elf64_Rela);
|
||||
auto relocations = reinterpret_cast<Elf64_Rela *>(segment.data());
|
||||
|
||||
for ( size_t i = 0; i < nrela; ++i ) {
|
||||
auto &rela = relocations[i];
|
||||
|
||||
swap(rela.r_offset);
|
||||
swap(rela.r_info);
|
||||
swap(rela.r_addend);
|
||||
|
||||
auto type = ELF64_R_TYPE(rela.r_info);
|
||||
|
||||
if ( type == R_PPC64_NONE )
|
||||
continue;
|
||||
|
||||
auto sym = ELF64_R_SYM(rela.r_info);
|
||||
auto patchseg = (sym & 0x000000ff);
|
||||
auto symseg = (sym & 0x7fffff00) >> 8;
|
||||
|
||||
uint32 addr, saddr;
|
||||
if ( patchseg == 0xFF )
|
||||
addr = 0;
|
||||
else
|
||||
addr = segments[patchseg].p_vaddr + rela.r_offset;
|
||||
|
||||
if ( symseg == 0xFF )
|
||||
saddr = 0;
|
||||
else
|
||||
saddr = segments[symseg].p_vaddr + rela.r_addend;
|
||||
|
||||
applyRelocation(type, addr, saddr);
|
||||
}
|
||||
break; // TODO: should only be one segment right?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cell_loader::applyRelocation(uint32 type, uint32 addr, uint32 saddr) {
|
||||
uint32 value;
|
||||
|
||||
addr += m_relocAddr;
|
||||
saddr += m_relocAddr;
|
||||
|
||||
//msg("Applying relocation %i (%08x -> %08x)\n", type, addr, saddr);
|
||||
|
||||
switch ( type ) {
|
||||
case R_PPC64_ADDR32:
|
||||
value = saddr;
|
||||
patch_long(addr, value);
|
||||
break;
|
||||
case R_PPC64_ADDR16_LO:
|
||||
value = saddr & 0xFFFF;
|
||||
patch_word(addr, value);
|
||||
break;
|
||||
case R_PPC64_ADDR16_HA:
|
||||
value = (((saddr + 0x8000) >> 16) & 0xFFFF);
|
||||
patch_word(addr, value);
|
||||
break;
|
||||
case R_PPC64_REL24:
|
||||
value = get_original_long(addr);
|
||||
value = (value & ~0x03fffffc) | ((saddr - addr) & 0x3fffffc);
|
||||
patch_long(addr, value);
|
||||
break;
|
||||
case R_PPC64_TOC16:
|
||||
value = saddr - m_gpValue;
|
||||
patch_word(addr, value);
|
||||
break;
|
||||
case R_PPC64_TOC16_DS:
|
||||
value = get_word(addr);
|
||||
value = (value & ~0xFFFC) | ((saddr - m_gpValue) & 0xFFFC);
|
||||
patch_word(addr, value);
|
||||
break;
|
||||
case R_PPC64_TLSGD:
|
||||
value = m_gpValue;
|
||||
patch_long(addr, value);
|
||||
break;
|
||||
default:
|
||||
msg("Unsupported relocation (%i).\n", type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void cell_loader::loadExports(uint32 entTop, uint32 entEnd) {
|
||||
msg("Loading exports...\n");
|
||||
|
||||
tid_t tid = get_struc_id("_scelibent_ppu32");
|
||||
do_name_anyway(entTop - 4, "__begin_of_section_lib_ent");
|
||||
do_name_anyway(entEnd, "__end_of_section_lib_ent");
|
||||
|
||||
uchar structsize;
|
||||
|
||||
for ( ea_t ea = entTop; ea < entEnd; ea += structsize ) {
|
||||
structsize = get_byte(ea);
|
||||
|
||||
auto nfunc = get_word(ea + offsetof(_scelibent_common, nfunc));
|
||||
auto nvar = get_word(ea + offsetof(_scelibent_common, nvar));
|
||||
auto ntlsvar = get_word(ea + offsetof(_scelibent_common, ntlsvar));
|
||||
auto count = nfunc + nvar + ntlsvar;
|
||||
|
||||
//msg("Num Functions: %i\n", nfunc);
|
||||
//msg("Num Variables: %i\n", nvar);
|
||||
//msg("Num TLS Variables: %i\n", ntlsvar);
|
||||
|
||||
if ( structsize == sizeof(_scelibent_ppu32) ) {
|
||||
doStruct(ea, sizeof(_scelibent_ppu32), tid);
|
||||
|
||||
auto libNamePtr = get_long(ea + offsetof(_scelibent_ppu32, libname));
|
||||
auto nidTable = get_long(ea + offsetof(_scelibent_ppu32, nidtable));
|
||||
auto addTable = get_long(ea + offsetof(_scelibent_ppu32, addtable));
|
||||
|
||||
char libName[256];
|
||||
char symName[MAXNAMELEN];
|
||||
if ( libNamePtr == NULL ) {
|
||||
do_name_anyway(nidTable, "_NONAMEnid_table");
|
||||
do_name_anyway(addTable, "_NONAMEentry_table");
|
||||
} else {
|
||||
get_ascii_contents(libNamePtr, get_max_ascii_length(libNamePtr, ASCSTR_C), ASCSTR_C, libName, 256);
|
||||
|
||||
qsnprintf(symName, MAXNAMELEN, "_%s_str", libName);
|
||||
do_name_anyway(libNamePtr, symName);
|
||||
|
||||
qsnprintf(symName, MAXNAMELEN, "__%s_Functions_NID_table", libName);
|
||||
do_name_anyway(nidTable, symName);
|
||||
|
||||
qsnprintf(symName, MAXNAMELEN, "__%s_Functions_table", libName);
|
||||
do_name_anyway(addTable, symName);
|
||||
}
|
||||
|
||||
//msg("Processing entries..\n");
|
||||
if ( nidTable != NULL && addTable != NULL ) {
|
||||
for ( int i = 0; i < count; ++i ) {
|
||||
const char *resolvedNid;
|
||||
ea_t nidOffset = nidTable + (i * 4);
|
||||
ea_t addOffset = addTable + (i * 4);
|
||||
|
||||
uint32 nid = get_long(nidOffset);
|
||||
uint32 add = get_long(addOffset);
|
||||
|
||||
if ( libNamePtr ) {
|
||||
uint32 addToc = get_long(add);
|
||||
resolvedNid = getNameFromDatabase(libName, nid);
|
||||
if ( resolvedNid ) {
|
||||
set_cmt(nidOffset, resolvedNid, false);
|
||||
do_name_anyway(add, resolvedNid);
|
||||
|
||||
// only label functions this way
|
||||
if ( i < nfunc ) {
|
||||
qsnprintf(symName, MAXNAMELEN, ".%s", resolvedNid);
|
||||
do_name_anyway(addToc, symName);
|
||||
}
|
||||
}
|
||||
|
||||
if ( i < nfunc )
|
||||
auto_make_proc(addToc);
|
||||
}
|
||||
|
||||
//msg("doDwrd: %08x\n", nidOffset);
|
||||
//msg("doDwrd: %08x\n", addOffset);
|
||||
doDwrd(nidOffset, 4);
|
||||
doDwrd(addOffset, 4);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msg("Unknown export structure at %08x.\n", ea);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cell_loader::loadImports(uint32 stubTop, uint32 stubEnd) {
|
||||
msg("Loading imports...\n");
|
||||
|
||||
tid_t tid = get_struc_id("_scelibstub_ppu32");
|
||||
|
||||
do_name_anyway(stubTop - 4, "__begin_of_section_lib_stub");
|
||||
do_name_anyway(stubEnd, "__end_of_section_lib_stub");
|
||||
|
||||
uchar structsize;
|
||||
|
||||
// define data for lib stub
|
||||
for ( ea_t ea = stubTop; ea < stubEnd; ea += structsize ) {
|
||||
structsize = get_byte(ea);
|
||||
|
||||
auto nFunc = get_word(ea + offsetof(_scelibstub_common, nfunc));
|
||||
auto nVar = get_word(ea + offsetof(_scelibstub_common, nvar));
|
||||
auto nTlsVar = get_word(ea + offsetof(_scelibstub_common, ntlsvar));
|
||||
|
||||
//msg("Num Functions: %i\n", nFunc);
|
||||
//msg("Num Variables: %i\n", nVar);
|
||||
//msg("Num TLS Variables: %i\n", nTlsVar);
|
||||
|
||||
if (structsize == sizeof(_scelibstub_ppu32)) {
|
||||
doStruct(ea, sizeof(_scelibstub_ppu32), tid);
|
||||
|
||||
ea_t libNamePtr = get_long(ea + offsetof(_scelibstub_ppu32, libname));
|
||||
ea_t funcNidTable = get_long(ea + offsetof(_scelibstub_ppu32, func_nidtable));
|
||||
ea_t funcTable = get_long(ea + offsetof(_scelibstub_ppu32, func_table));
|
||||
ea_t varNidTable = get_long(ea + offsetof(_scelibstub_ppu32, var_nidtable));
|
||||
ea_t varTable = get_long(ea + offsetof(_scelibstub_ppu32, var_table));
|
||||
ea_t tlsNidTable = get_long(ea + offsetof(_scelibstub_ppu32, tls_nidtable));
|
||||
ea_t tlsTable = get_long(ea + offsetof(_scelibstub_ppu32, tls_table));
|
||||
|
||||
char libName[256];
|
||||
char symName[MAXNAMELEN];
|
||||
get_ascii_contents(libNamePtr, get_max_ascii_length(libNamePtr, ASCSTR_C), ASCSTR_C, libName, 256);
|
||||
|
||||
qsnprintf(symName, MAXNAMELEN, "_%s_0001_stub_head", libName);
|
||||
do_name_anyway(ea, symName);
|
||||
|
||||
qsnprintf(symName, MAXNAMELEN, "_%s_stub_str", libName);
|
||||
do_name_anyway(libNamePtr, symName);
|
||||
|
||||
qsnprintf(symName, MAXNAMELEN, "_sce_package_version_%s", libName);
|
||||
do_name_anyway(libNamePtr - 4, symName);
|
||||
|
||||
//msg("Processing %i exported functions...\n", nFunc);
|
||||
if ( funcNidTable != NULL && funcTable != NULL ) {
|
||||
for ( int i = 0; i < nFunc; ++i ) {
|
||||
const char *resolvedNid;
|
||||
|
||||
ea_t nidOffset = funcNidTable + (i * 4);
|
||||
ea_t funcOffset = funcTable + (i * 4);
|
||||
|
||||
uint32 nid = get_long(nidOffset);
|
||||
uint32 func = get_long(funcOffset);
|
||||
|
||||
resolvedNid = getNameFromDatabase(libName, nid);
|
||||
if ( resolvedNid ) {
|
||||
set_cmt(nidOffset, resolvedNid, false);
|
||||
qsnprintf(symName, MAXNAMELEN, "%s.stub_entry", resolvedNid);
|
||||
do_name_anyway(funcOffset, symName);
|
||||
qsnprintf(symName, MAXNAMELEN, ".%s", resolvedNid);
|
||||
do_name_anyway(func, symName);
|
||||
}
|
||||
|
||||
//msg("doDwrd: %08x\n", nidOffset);
|
||||
//msg("doDwrd: %08x\n", funcOffset);
|
||||
doDwrd(nidOffset, 4); // nid
|
||||
doDwrd(funcOffset, 4); // func
|
||||
if ( add_func(func, BADADDR) ) {
|
||||
get_func(func)->flags |= FUNC_LIB;
|
||||
//add_entry(func, func, ...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//msg("Processing exported variables...\n");
|
||||
if ( varNidTable != NULL && varTable ) {
|
||||
for ( int i = 0; i < nVar; ++i ) {
|
||||
const char *resolvedNid;
|
||||
|
||||
ea_t nidOffset = varNidTable + (i * 4);
|
||||
ea_t varOffset = varTable + (i * 4);
|
||||
|
||||
uint32 nid = get_long(nidOffset);
|
||||
uint32 func = get_long(varOffset);
|
||||
|
||||
resolvedNid = getNameFromDatabase(libName, nid);
|
||||
if ( resolvedNid ) {
|
||||
set_cmt(nidOffset, resolvedNid, false);
|
||||
do_name_anyway(varOffset, resolvedNid);
|
||||
}
|
||||
|
||||
//msg("doDwrd: %08x\n", nidOffset);
|
||||
//msg("doDwrd: %08x\n", varOffset);
|
||||
doDwrd(nidOffset, 4);
|
||||
doDwrd(varOffset, 4);
|
||||
}
|
||||
}
|
||||
|
||||
//msg("Processing exported TLS variables...\n");
|
||||
if ( tlsNidTable != NULL && tlsTable != NULL ) {
|
||||
for ( int i = 0; i < nVar; ++i ) {
|
||||
const char *resolvedNid;
|
||||
|
||||
ea_t nidOffset = tlsNidTable + (i * 4);
|
||||
ea_t tlsOffset = tlsTable + (i * 4);
|
||||
|
||||
uint32 nid = get_long(nidOffset);
|
||||
uint32 func = get_long(tlsOffset);
|
||||
|
||||
resolvedNid = getNameFromDatabase(libName, nid);
|
||||
if ( resolvedNid ) {
|
||||
set_cmt(nidOffset, resolvedNid, false);
|
||||
do_name_anyway(tlsOffset, resolvedNid);
|
||||
}
|
||||
|
||||
//msg("doDwrd: %08x\n", nidOffset);
|
||||
//msg("doDwrd: %08x\n", tlsOffset);
|
||||
doDwrd(nidOffset, 4);
|
||||
doDwrd(tlsOffset, 4);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msg("Unknown import structure at %08x.\n", ea);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *cell_loader::getNameFromDatabase(
|
||||
const char *library, unsigned int nid) {
|
||||
auto header = m_database.FirstChildElement();
|
||||
|
||||
if ( header ) {
|
||||
auto group = header->FirstChildElement();
|
||||
if ( group ) {
|
||||
// find library in xml
|
||||
do {
|
||||
if ( !strcmp(library, group->Attribute("name")) ) {
|
||||
auto entry = group->FirstChildElement();
|
||||
if ( entry ) {
|
||||
// find NID in library group
|
||||
do {
|
||||
if ( strtoul(entry->Attribute("id"),0,0) == nid )
|
||||
return entry->Attribute("name");
|
||||
} while ( entry = entry->NextSiblingElement() );
|
||||
}
|
||||
}
|
||||
} while ( group = group->NextSiblingElement() );
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void cell_loader::applyModuleInfo() {
|
||||
auto firstSegment = m_elf->getSegments()[0];
|
||||
|
||||
ea_t modInfoEa = (firstSegment.p_vaddr + m_relocAddr) +
|
||||
(firstSegment.p_paddr - firstSegment.p_offset);
|
||||
|
||||
tid_t tid = get_struc_id("_scemoduleinfo");
|
||||
doStruct(modInfoEa, sizeof(_scemoduleinfo_ppu32), tid);
|
||||
|
||||
loadExports( get_long(modInfoEa + offsetof(_scemoduleinfo_ppu32, ent_top)),
|
||||
get_long(modInfoEa + offsetof(_scemoduleinfo_ppu32, ent_end)) );
|
||||
|
||||
loadImports( get_long(modInfoEa + offsetof(_scemoduleinfo_ppu32, stub_top)),
|
||||
get_long(modInfoEa + offsetof(_scemoduleinfo_ppu32, stub_end)) );
|
||||
|
||||
add_entry(0, modInfoEa, "module_info", false);
|
||||
|
||||
}
|
||||
|
||||
void cell_loader::applyProcessInfo() {
|
||||
for ( auto segment : m_elf->getSegments() ) {
|
||||
if ( segment.p_type == PT_PROC_PARAM ) {
|
||||
tid_t tid = get_struc_id("sys_process_param_t");
|
||||
doStruct(segment.p_vaddr, sizeof(sys_process_param_t), tid);
|
||||
} else if ( segment.p_type == PT_PROC_PRX ) {
|
||||
tid_t tid = get_struc_id("sys_process_prx_info_t");
|
||||
doStruct(segment.p_vaddr, sizeof(sys_process_prx_info_t), tid);
|
||||
|
||||
loadExports( get_long(segment.p_vaddr + offsetof(sys_process_prx_info_t, libent_start)),
|
||||
get_long(segment.p_vaddr + offsetof(sys_process_prx_info_t, libent_end)) );
|
||||
|
||||
loadImports( get_long(segment.p_vaddr + offsetof(sys_process_prx_info_t, libstub_start)),
|
||||
get_long(segment.p_vaddr + offsetof(sys_process_prx_info_t, libstub_end)) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cell_loader::swapSymbols() {
|
||||
// Since section based relocations depend on symbols
|
||||
// we need to swap symbols before we get to relocations.
|
||||
// Pretty much only for 0.85 PRX's but we still need to
|
||||
// swap them anyway.
|
||||
auto section = m_elf->getSymbolsSection();
|
||||
|
||||
if (section == NULL)
|
||||
return;
|
||||
|
||||
//msg("Swapping symbols...\n");
|
||||
|
||||
auto symbols = m_elf->getSymbols();
|
||||
|
||||
for ( size_t i = 0; i < m_elf->getNumSymbols(); ++i ) {
|
||||
auto symbol = &symbols[i];
|
||||
|
||||
swap(symbol->st_name);
|
||||
swap(symbol->st_shndx);
|
||||
swap(symbol->st_size);
|
||||
swap(symbol->st_value);
|
||||
}
|
||||
}
|
||||
|
||||
void cell_loader::applySymbols() {
|
||||
auto section = m_elf->getSymbolsSection();
|
||||
|
||||
if (section == NULL)
|
||||
return;
|
||||
|
||||
msg("Applying symbols...\n");
|
||||
|
||||
auto nsym = m_elf->getNumSymbols();
|
||||
auto symbols = m_elf->getSymbols();
|
||||
|
||||
const char *stringTable = m_elf->getSections().at(section->sh_link).data();
|
||||
|
||||
for ( size_t i = 0; i < nsym; ++i ) {
|
||||
auto &symbol = symbols[i];
|
||||
|
||||
auto type = ELF64_ST_TYPE(symbol.st_info),
|
||||
bind = ELF64_ST_BIND(symbol.st_info);
|
||||
auto value = symbol.st_value;
|
||||
|
||||
//msg("st_name: %08x\n", symbol.st_name);
|
||||
//msg("st_type: %08x\n", type);
|
||||
//msg("st_bind: %08x\n", bind);
|
||||
|
||||
if ( symbol.st_shndx > m_elf->getNumSections() ||
|
||||
!(m_elf->getSections()[ symbol.st_shndx ].sh_flags & SHF_ALLOC) )
|
||||
continue;
|
||||
|
||||
if ( symbol.st_shndx == SHN_ABS )
|
||||
continue;
|
||||
|
||||
if ( isLoadingPrx() )
|
||||
value += m_elf->getSections()[ symbol.st_shndx ].sh_addr + m_relocAddr;
|
||||
|
||||
switch ( type ) {
|
||||
case STT_OBJECT:
|
||||
do_name_anyway(value, &stringTable[ symbol.st_name ]);
|
||||
break;
|
||||
case STT_FILE:
|
||||
describe(value, true, "Source File: %s", &stringTable[ symbol.st_name ]);
|
||||
break;
|
||||
case STT_FUNC:
|
||||
do_name_anyway(value, &stringTable[ symbol.st_name ]);
|
||||
auto_make_proc(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cell_loader::declareStructures() {
|
||||
struc_t *sptr;
|
||||
|
||||
// offset type
|
||||
typeinfo_t ot;
|
||||
ot.ri.flags = REF_OFF32;
|
||||
ot.ri.target = BADADDR;
|
||||
ot.ri.base = 0;
|
||||
ot.ri.tdelta = 0;
|
||||
|
||||
tid_t modInfoCommon = add_struc(BADADDR, "_scemoduleinfo_common");
|
||||
sptr = get_struc(modInfoCommon);
|
||||
if ( sptr != NULL ) {
|
||||
add_struc_member(sptr, "modattribute", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "modversion", BADADDR, byteflag(), NULL, 2);
|
||||
add_struc_member(sptr, "modname", BADADDR, byteflag(), NULL, SYS_MODULE_NAME_LEN);
|
||||
add_struc_member(sptr, "terminal", BADADDR, byteflag(), NULL, 1);
|
||||
|
||||
sptr = get_struc(add_struc(BADADDR, "_scemoduleinfo"));
|
||||
if ( sptr != NULL ) {
|
||||
typeinfo_t mt;
|
||||
mt.tid = modInfoCommon;
|
||||
add_struc_member(sptr, "c", BADADDR, struflag(), &mt, get_struc_size(mt.tid));
|
||||
|
||||
add_struc_member(sptr, "gp_value", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "ent_top", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "ent_end", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "stub_top", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "stub_end", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
}
|
||||
}
|
||||
|
||||
tid_t libStubCommon = add_struc(BADADDR, "_scelibstub_ppu_common");
|
||||
sptr = get_struc(libStubCommon);
|
||||
if ( sptr != NULL ) {
|
||||
add_struc_member(sptr, "structsize", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "reserved1", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "version", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "attribute", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "nfunc", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "nvar", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "ntlsvar", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "reserved2", BADADDR, byteflag(), NULL, 4);
|
||||
|
||||
sptr = get_struc(add_struc(BADADDR, "_scelibstub_ppu32"));
|
||||
if ( sptr != NULL ) {
|
||||
typeinfo_t mt;
|
||||
mt.tid = libStubCommon;
|
||||
add_struc_member(sptr, "c", BADADDR, struflag(), &mt, get_struc_size(mt.tid));
|
||||
|
||||
add_struc_member(sptr, "libname", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "func_nidtable", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "func_table", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "var_nidtable", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "var_table", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "tls_nidtable", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "tls_table", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
}
|
||||
}
|
||||
|
||||
tid_t libEntCommon = add_struc(BADADDR, "_scelibent_ppu_common");
|
||||
sptr = get_struc(libEntCommon);
|
||||
if ( sptr != NULL ) {
|
||||
add_struc_member(sptr, "structsize", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "reserved1", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "version", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "attribute", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "nfunc", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "nvar", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "ntlsvar", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "hashinfo", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "hashinfotls", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "reserved2", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "nidaltsets", BADADDR, byteflag(), NULL, 1);
|
||||
|
||||
sptr = get_struc(add_struc(BADADDR, "_scelibent_ppu32"));
|
||||
if ( sptr != NULL ) {
|
||||
typeinfo_t mt;
|
||||
mt.tid = libEntCommon;
|
||||
add_struc_member(sptr, "c", BADADDR, struflag(), &mt, get_struc_size(mt.tid));
|
||||
|
||||
add_struc_member(sptr, "libname", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "nidtable", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "addtable", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
}
|
||||
}
|
||||
|
||||
tid_t procParamInfo = add_struc(BADADDR, "sys_process_param_t");
|
||||
sptr = get_struc(procParamInfo);
|
||||
if ( sptr != NULL ) {
|
||||
add_struc_member(sptr, "size", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "magic", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "version", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "sdk_version", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "primary_prio", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "primary_stacksize", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "malloc_pagesize", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "ppc_seg", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "crash_dump_param_addr", BADADDR, dwrdflag(), NULL, 4);
|
||||
}
|
||||
|
||||
tid_t procPrxInfo = add_struc(BADADDR, "sys_process_prx_info_t");
|
||||
sptr = get_struc(procPrxInfo);
|
||||
if ( sptr != NULL ) {
|
||||
add_struc_member(sptr, "size", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "magic", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "version", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "sdk_version", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "libent_start", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "libent_end", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "libstub_start", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "libstub_end", BADADDR, offflag() | dwrdflag(), &ot, 4);
|
||||
add_struc_member(sptr, "major_version", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "minor_version", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "reserved", BADADDR, byteflag(), NULL, 6);
|
||||
}
|
||||
}
|
61
src/ps3/cell_loader.h
Normal file
61
src/ps3/cell_loader.h
Normal file
@ -0,0 +1,61 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "elf_reader.h"
|
||||
#include "tinyxml.h"
|
||||
#include "sce.h"
|
||||
#include <string>
|
||||
|
||||
class cell_loader {
|
||||
elf_reader<elf64> *m_elf; ///< Handle for this loader's ELF reader.
|
||||
TiXmlDocument m_database; ///< Handle for this loader's NID xml database.
|
||||
uint64 m_relocAddr; // Base relocaton address for PRX's.
|
||||
uint64 m_gpValue; // TOC value
|
||||
bool m_hasSegSym; // has seg sym, but the real meaning
|
||||
// is if its a 0.85 PRX since its the only
|
||||
// way I know how to check
|
||||
|
||||
public:
|
||||
cell_loader(elf_reader<elf64> *elf, uint64 relocAddr, std::string databasePath);
|
||||
|
||||
void apply();
|
||||
|
||||
bool isLoadingExec() const
|
||||
{ return m_elf->type() == ET_EXEC; }
|
||||
|
||||
bool isLoadingPrx() const
|
||||
{ return m_elf->type() == ET_SCE_PPURELEXEC; }
|
||||
|
||||
private:
|
||||
void applySegments();
|
||||
void applySegment(uint32 sel,
|
||||
uint64 offset,
|
||||
uint64 addr,
|
||||
uint64 size,
|
||||
const char *name,
|
||||
const char *sclass,
|
||||
uchar perm,
|
||||
uchar align,
|
||||
bool load = true);
|
||||
|
||||
void applySectionHeaders();
|
||||
void applyProgramHeaders();
|
||||
|
||||
void applyRelocations();
|
||||
void applySectionRelocations();
|
||||
void applySegmentRelocations();
|
||||
void applyRelocation(uint32 type, uint32 addr, uint32 saddr);
|
||||
|
||||
void declareStructures();
|
||||
|
||||
void applyModuleInfo();
|
||||
void loadExports(uint32 entTop, uint32 entEnd);
|
||||
void loadImports(uint32 stubTop, uint32 stubEnd);
|
||||
|
||||
const char *getNameFromDatabase(const char *group, unsigned int nid);
|
||||
|
||||
void applyProcessInfo();
|
||||
|
||||
void swapSymbols();
|
||||
void applySymbols();
|
||||
};
|
67
src/ps3/ps3.cpp
Normal file
67
src/ps3/ps3.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
|
||||
#include "../elf_common/elf_reader.h"
|
||||
#include "cell_loader.h"
|
||||
#include "sce.h"
|
||||
|
||||
#include <idaldr.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#define DATABASE_FILE "ps3.xml"
|
||||
|
||||
static int idaapi
|
||||
accept_file(linput_t *li, char fileformatname[MAX_FILE_FORMAT_NAME], int n)
|
||||
{
|
||||
if (n > 0)
|
||||
return 0;
|
||||
|
||||
elf_reader<elf64> elf(li);
|
||||
|
||||
if (elf.verifyHeader() &&
|
||||
elf.machine() == EM_PPC64 &&
|
||||
elf.osabi() == ELFOSABI_CELLOSLV2) {
|
||||
const char *type;
|
||||
|
||||
if (elf.type() == ET_EXEC)
|
||||
type = "Executable";
|
||||
else if (elf.type() == ET_SCE_PPURELEXEC)
|
||||
type = "Relocatable Executable";
|
||||
else
|
||||
return 0;
|
||||
|
||||
set_processor_type("ppc", SETPROC_ALL);
|
||||
|
||||
qsnprintf(fileformatname, MAX_FILE_FORMAT_NAME, "Playstation 3 PPU %s", type);
|
||||
|
||||
return 1 | ACCEPT_FIRST;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void idaapi
|
||||
load_file(linput_t *li, ushort neflags, const char *fileformatname)
|
||||
{
|
||||
elf_reader<elf64> elf(li); elf.read();
|
||||
|
||||
ea_t relocAddr = 0;
|
||||
if (elf.type() == ET_SCE_PPURELEXEC) {
|
||||
if (neflags & NEF_MAN) {
|
||||
askaddr(&relocAddr, "Please specify a relocation address base.");
|
||||
}
|
||||
}
|
||||
|
||||
cell_loader ldr(&elf, relocAddr, DATABASE_FILE); ldr.apply();
|
||||
}
|
||||
|
||||
__declspec(dllexport)
|
||||
loader_t LDSC =
|
||||
{
|
||||
IDP_INTERFACE_VERSION,
|
||||
0,
|
||||
accept_file,
|
||||
load_file,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
167
src/ps3/sce.h
Normal file
167
src/ps3/sce.h
Normal file
@ -0,0 +1,167 @@
|
||||
#pragma once
|
||||
|
||||
#include "elf.h"
|
||||
|
||||
#define ELFOSABI_CELLOSLV2 102 /* CellOS Lv2 */ /* sce local */
|
||||
|
||||
#define ET_SCE_PPURELEXEC 0xffa4
|
||||
|
||||
#define SHT_SCE_PPURELA 0x700000a4
|
||||
|
||||
#define PT_PROC_PARAM 0x60000001
|
||||
#define PT_PROC_PRX 0x60000002
|
||||
|
||||
#define PT_SCE_COMMENT 0x6fffff00
|
||||
#define PT_SCE_VERSION 0x6fffff01
|
||||
|
||||
#define PT_SCE_PPURELA 0x700000a4
|
||||
#define PT_SCE_SEGSYM 0x700000a8
|
||||
|
||||
#define PF_SPU_X (0x00100000) /* SPU executable defined, but unused.*/
|
||||
#define PF_SPU_W (0x00200000) /* SPU writable */
|
||||
#define PF_SPU_R (0x00400000) /* SPU readable */
|
||||
#define PF_RSX_X (0x01000000) /* RSX executable defined, but unused. */
|
||||
#define PF_RSX_W (0x02000000) /* RSX writable */
|
||||
#define PF_RSX_R (0x04000000) /* RSX readable */
|
||||
|
||||
#define SYS_MODULE_NAME_LEN 27
|
||||
#define SYS_MODULE_MAX_SEGMENTS 4
|
||||
|
||||
#define SYS_LIB_AUTO_EXPORT (0x0001)
|
||||
#define SYS_LIB_WEAK_EXPORT (0x0002)
|
||||
#define SYS_LIB_NOLINK_EXPORT (0x0004)
|
||||
#define SYS_LIB_WEAK_IMPORT (0x0008)
|
||||
|
||||
/* MODULE INFO */
|
||||
|
||||
typedef struct _scemoduleinfo_common {
|
||||
unsigned short modattribute;
|
||||
unsigned char modversion[2];
|
||||
char modname[SYS_MODULE_NAME_LEN];
|
||||
char terminal;
|
||||
} sceModuleInfo_common;
|
||||
|
||||
typedef struct _scemoduleinfo_ppu32 {
|
||||
sceModuleInfo_common c;
|
||||
Elf32_Addr gp_value;
|
||||
Elf32_Addr ent_top;
|
||||
Elf32_Addr ent_end;
|
||||
Elf32_Addr stub_top;
|
||||
Elf32_Addr stub_end;
|
||||
} sceModuleInfo_ppu32;
|
||||
|
||||
typedef struct _scemoduleinfo_ppu64 {
|
||||
sceModuleInfo_common c;
|
||||
Elf64_Addr gp_value;
|
||||
Elf64_Addr ent_top;
|
||||
Elf64_Addr ent_end;
|
||||
Elf64_Addr stub_top;
|
||||
Elf64_Addr stub_end;
|
||||
} sceModuleInfo_ppu64;
|
||||
|
||||
|
||||
/* IMPORTS */
|
||||
|
||||
typedef struct _scelibstub_common {
|
||||
unsigned char structsize;
|
||||
unsigned char reserved1[1];
|
||||
unsigned short version;
|
||||
unsigned short attribute;
|
||||
unsigned short nfunc;
|
||||
unsigned short nvar;
|
||||
unsigned short ntlsvar;
|
||||
unsigned char reserved2[4];
|
||||
} sceKernelLibraryStubTable_common;
|
||||
|
||||
typedef sceKernelLibraryStubTable_common
|
||||
sceKernelLibraryStubTable_ppu_common;
|
||||
|
||||
typedef struct _scelibstub_ppu32 {
|
||||
sceKernelLibraryStubTable_ppu_common c;
|
||||
Elf32_Addr libname;
|
||||
Elf32_Addr func_nidtable;
|
||||
Elf32_Addr func_table;
|
||||
Elf32_Addr var_nidtable;
|
||||
Elf32_Addr var_table;
|
||||
Elf32_Addr tls_nidtable;
|
||||
Elf32_Addr tls_table;
|
||||
} sceKernelLibraryStubTable_ppu32;
|
||||
|
||||
typedef struct _scelibstub_ppu64 {
|
||||
sceKernelLibraryStubTable_ppu_common c;
|
||||
Elf64_Addr libname;
|
||||
Elf64_Addr func_nidtable;
|
||||
Elf64_Addr func_table;
|
||||
Elf64_Addr var_nidtable;
|
||||
Elf64_Addr var_table;
|
||||
Elf64_Addr tls_nidtable;
|
||||
Elf64_Addr tls_table;
|
||||
} sceKernelLibraryStubTable_ppu64;
|
||||
|
||||
/* EXPORTS */
|
||||
|
||||
typedef struct _scelibent_common {
|
||||
unsigned char structsize;
|
||||
unsigned char auxattribute;
|
||||
short unsigned int version;
|
||||
short unsigned int attribute;
|
||||
short unsigned int nfunc;
|
||||
short unsigned int nvar;
|
||||
short unsigned int ntlsvar;
|
||||
unsigned char hashinfo;
|
||||
unsigned char hashinfotls;
|
||||
unsigned char reserved2[1];
|
||||
unsigned char nidaltsets;
|
||||
} sceKernelLibraryEntryTable_common;
|
||||
|
||||
typedef sceKernelLibraryEntryTable_common
|
||||
sceKernelLibraryEntryTable_ppu_common;
|
||||
|
||||
typedef struct _scelibent_ppu32 {
|
||||
sceKernelLibraryEntryTable_ppu_common c;
|
||||
Elf32_Addr libname;
|
||||
Elf32_Addr nidtable;
|
||||
Elf32_Addr addtable;
|
||||
} sceKernelLibraryEntryTable_ppu32;
|
||||
|
||||
typedef struct _scelibent_ppu64 {
|
||||
sceKernelLibraryEntryTable_ppu_common c;
|
||||
Elf64_Addr libname;
|
||||
Elf64_Addr nidtable;
|
||||
Elf64_Addr addtable;
|
||||
} sceKernelLibraryEntryTable_ppu64;
|
||||
|
||||
/* PROCESS PARAM */
|
||||
|
||||
#define SYS_PROCESS_PARAM_MAGIC 0x13bcc5f6
|
||||
|
||||
typedef struct {
|
||||
unsigned int size;
|
||||
unsigned int magic;
|
||||
unsigned int version;
|
||||
unsigned int sdk_version;
|
||||
int primary_prio;
|
||||
unsigned int primary_stacksize;
|
||||
unsigned int malloc_pagesize;
|
||||
unsigned int ppc_seg;
|
||||
unsigned int crash_dump_param_addr;
|
||||
} sys_process_param_t;
|
||||
|
||||
/* PROCESS PRX */
|
||||
|
||||
#define SYS_PROCESS_PRX_MAGIC 0x1b434cec
|
||||
#define SYS_PROCESS_PRX_VERSION 4 /* latest */
|
||||
|
||||
typedef struct sys_process_prx_info_t {
|
||||
unsigned int size;
|
||||
unsigned int magic;
|
||||
unsigned int version;
|
||||
unsigned int sdk_version;
|
||||
unsigned int libent_start;
|
||||
unsigned int libent_end;
|
||||
unsigned int libstub_start;
|
||||
unsigned int libstub_end;
|
||||
unsigned char major_version;
|
||||
unsigned char minor_version;
|
||||
unsigned char reserved[6];
|
||||
} sys_process_prx_info_t;
|
28
src/vita/CMakeLists.txt
Normal file
28
src/vita/CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
project(vitaldr)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/../../cmake)
|
||||
set(ELF_COMMON_PATH ${CMAKE_SOURCE_DIR}/../elf_common)
|
||||
set(THIRD_PARTY_PATH ${CMAKE_SOURCE_DIR}/../../third_party)
|
||||
|
||||
set(SOURCES
|
||||
${ELF_COMMON_PATH}/elf_reader.h
|
||||
${ELF_COMMON_PATH}/elf.h
|
||||
psp2_loader.cpp
|
||||
psp2_loader.h
|
||||
vita.cpp
|
||||
sce.h
|
||||
)
|
||||
|
||||
find_package(IDA)
|
||||
|
||||
include_directories(${IDA_INCLUDE_DIR})
|
||||
include_directories(${IDA_SDK_PATH}/ldr)
|
||||
include_directories(${ELF_COMMON_PATH})
|
||||
|
||||
add_definitions(${IDA_DEFINITIONS})
|
||||
add_definitions(-DUSE_STANDARD_FILE_FUNCTIONS)
|
||||
|
||||
add_library(vitaldr SHARED ${SOURCES})
|
||||
target_link_libraries(vitaldr ${IDA_LIBRARIES})
|
||||
set_target_properties(vitaldr PROPERTIES OUTPUT_NAME "vita" PREFIX "" SUFFIX "${IDA_PLUGIN_EXT}")
|
926
src/vita/psp2_loader.cpp
Normal file
926
src/vita/psp2_loader.cpp
Normal file
@ -0,0 +1,926 @@
|
||||
#include "psp2_loader.h"
|
||||
#include <struct.hpp>
|
||||
#include <pro.h>
|
||||
#include <string>
|
||||
|
||||
psp2_loader::psp2_loader(elf_reader<elf32> *elf, std::string databaseFile)
|
||||
: m_elf(elf)
|
||||
{
|
||||
inf.demnames |= DEMNAM_GCC3; // assume gcc3 names
|
||||
inf.af |= AF_PROCPTR; // Create function if data xref data->code32 exists
|
||||
//inf.af |= AF_IMMOFF; // Convert 32bit instruction operand to offset
|
||||
inf.af |= AF_DREFOFF; // Create offset if data xref to seg32 exists
|
||||
inf.af2 |= AF2_DATOFF;
|
||||
|
||||
char databasePath[QMAXPATH];
|
||||
|
||||
if (getsysfile(databasePath, QMAXFILE, databaseFile.c_str(), LDR_SUBDIR) == NULL)
|
||||
loader_failure("Could not locate database file (%s).\n", databaseFile.c_str());
|
||||
|
||||
m_database.open(databasePath);
|
||||
|
||||
if (m_database.is_open() == false)
|
||||
loader_failure("Failed to open database file (%s).\n", databaseFile.c_str());
|
||||
|
||||
unsigned int nid;
|
||||
std::string symbol;
|
||||
while (m_database >> std::hex >> nid >> symbol) {
|
||||
m_nidset.insert(std::pair<unsigned int, std::string>(nid, symbol));
|
||||
}
|
||||
}
|
||||
|
||||
void psp2_loader::apply() {
|
||||
declareStructures();
|
||||
|
||||
applySegments();
|
||||
|
||||
if ( isLoadingPrx() )
|
||||
applyRelocations();
|
||||
|
||||
applyModuleInfo();
|
||||
applySymbols();
|
||||
}
|
||||
|
||||
void psp2_loader::applySegments() {
|
||||
if ( m_elf->getNumSections() > 0 )
|
||||
applySectionHeaders();
|
||||
else if ( m_elf->getNumSegments() > 0 )
|
||||
applyProgramHeaders();
|
||||
}
|
||||
|
||||
void psp2_loader::applySectionHeaders() {
|
||||
auto §ions = m_elf->getSections();
|
||||
const char *strTab = m_elf->getSectionStringTable()->data();
|
||||
|
||||
size_t index = 0;
|
||||
for (const auto §ion : sections) {
|
||||
if (!(section.sh_flags & SHF_ALLOC) || // is not allocatable
|
||||
section.sh_size == NULL || // has no data
|
||||
section.sh_type == SHT_NULL) // skip unused entry
|
||||
continue;
|
||||
|
||||
uchar perm = SEGPERM_READ;
|
||||
char *sclass;
|
||||
|
||||
if (section.sh_flags & SHF_WRITE)
|
||||
perm |= SEGPERM_WRITE;
|
||||
if (section.sh_flags & SHF_EXECINSTR)
|
||||
perm |= SEGPERM_EXEC;
|
||||
|
||||
if (section.sh_flags & SHF_EXECINSTR)
|
||||
sclass = CLASS_CODE;
|
||||
else if (section.sh_type == SHT_NOBITS)
|
||||
sclass = CLASS_BSS;
|
||||
else
|
||||
sclass = CLASS_DATA;
|
||||
|
||||
const char *name = "";
|
||||
if (section.sh_name != NULL)
|
||||
name = &strTab[section.sh_name];
|
||||
|
||||
applySegment( index,
|
||||
section.sh_offset,
|
||||
section.sh_addr,
|
||||
section.sh_size,
|
||||
name,
|
||||
sclass,
|
||||
perm,
|
||||
m_elf->getAlignment(section.sh_addralign),
|
||||
(section.sh_type == SHT_NOBITS) ? false : true );
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void psp2_loader::applyProgramHeaders() {
|
||||
auto &segments = m_elf->getSegments();
|
||||
|
||||
size_t index = 0;
|
||||
for (const auto &segment : segments) {
|
||||
if (segment.p_memsz == 0)
|
||||
continue;
|
||||
|
||||
uchar perm = 0;
|
||||
char *sclass;
|
||||
|
||||
if (segment.p_flags & PF_W) // if its writable
|
||||
sclass = CLASS_DATA;
|
||||
if ((segment.p_flags & PF_R) && // if its only readable
|
||||
!(segment.p_flags & PF_W) &&
|
||||
!(segment.p_flags & PF_X))
|
||||
sclass = CLASS_CONST;
|
||||
if (segment.p_flags & PF_X) // if its executable
|
||||
sclass = CLASS_CODE;
|
||||
|
||||
if (segment.p_filesz == 0 &&
|
||||
segment.p_memsz > 0)
|
||||
sclass = CLASS_BSS;
|
||||
|
||||
if (segment.p_flags & PF_X)
|
||||
perm |= SEGPERM_EXEC;
|
||||
if (segment.p_flags & PF_W)
|
||||
perm |= SEGPERM_WRITE;
|
||||
if (segment.p_flags & PF_R)
|
||||
perm |= SEGPERM_READ;
|
||||
|
||||
applySegment(index,
|
||||
segment.p_offset,
|
||||
segment.p_vaddr,
|
||||
segment.p_memsz,
|
||||
NULL,
|
||||
sclass,
|
||||
perm,
|
||||
m_elf->getAlignment(segment.p_align),
|
||||
(segment.p_filesz == 0) ? false : true);
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void psp2_loader::applySegment(
|
||||
uint32 sel,
|
||||
uint64 offset,
|
||||
uint64 addr,
|
||||
uint64 size,
|
||||
const char *name,
|
||||
const char *sclass,
|
||||
uchar perm,
|
||||
uchar align,
|
||||
bool load) {
|
||||
|
||||
segment_t seg;
|
||||
seg.startEA = addr;
|
||||
seg.endEA = addr + size;
|
||||
seg.color = DEFCOLOR;
|
||||
seg.sel = sel;
|
||||
seg.bitness = 1;
|
||||
seg.orgbase = sel;
|
||||
seg.comb = scPub;
|
||||
seg.perm = perm;
|
||||
seg.flags = SFL_LOADER;
|
||||
seg.align = align;
|
||||
|
||||
set_selector(sel, 0);
|
||||
|
||||
if (name == NULL)
|
||||
name = "";
|
||||
|
||||
add_segm_ex(&seg, name, sclass, NULL);
|
||||
|
||||
if (load == true)
|
||||
file2base(m_elf->getReader(), offset, addr, addr + size, true);
|
||||
}
|
||||
|
||||
void psp2_loader::applyRelocations() {
|
||||
auto &segments = m_elf->getSegments();
|
||||
|
||||
msg("Searching for relocation segments...\n");
|
||||
for (auto &segment : segments) {
|
||||
if (segment.p_type != PT_SCE_RELA)
|
||||
continue;
|
||||
|
||||
//msg("This segment offset: %08x\n", segment.p_offset);
|
||||
//msg("This segment filesz: %08x\n", segment.p_filesz);
|
||||
|
||||
auto nwords = segment.p_filesz / sizeof(Elf32_Word);
|
||||
auto rel = reinterpret_cast<Elf32_Word *>(segment.data());
|
||||
|
||||
//msg("Relocation segment at offset %08x\n", segment.p_offset);
|
||||
|
||||
// initialized in format 1 and 2
|
||||
uint32 g_addr = 0,
|
||||
g_offset = 0,
|
||||
g_patchseg = 0;
|
||||
|
||||
// initiliazed in format 0, 1, 2, and 3
|
||||
uint32 g_saddr = 0,
|
||||
g_addend = 0,
|
||||
g_type = 0,
|
||||
g_type2 = 0;
|
||||
|
||||
size_t index = 0;
|
||||
for (size_t pos = 0; pos < nwords; ++pos) {
|
||||
auto r_format = rel[pos] & 0xF;
|
||||
|
||||
//msg("Relocation %i\n", index);
|
||||
//msg("r_format %i\n", r_format);
|
||||
|
||||
switch (r_format) {
|
||||
case 0: {
|
||||
//msg("%08x %08x %08x\n", rel[pos], rel[pos+1], rel[pos+2]);
|
||||
auto r_symseg = (rel[pos] >> 4) & 0xF; // index into phdrs
|
||||
g_type = (rel[pos] >> 8) & 0xFF; // relocation type
|
||||
g_patchseg = (rel[pos] >> 16) & 0xF; // index into phdrs
|
||||
g_type2 = (rel[pos] >> 20) & 0x7F; // second relocation
|
||||
auto r_dist2 = (rel[pos] >> 27) & 0xF8; // distance from first offset
|
||||
g_addend = (rel[pos+1]); // addend
|
||||
g_offset = (rel[pos+2]); // first offset
|
||||
|
||||
// save these
|
||||
g_addr = segments[g_patchseg].p_vaddr;
|
||||
g_saddr = segments[r_symseg].p_vaddr;
|
||||
|
||||
/*msg(" r_symseg %i [%08x]\n", r_symseg, segments[r_symseg].p_vaddr);
|
||||
msg(" r_type %i\n", g_type);
|
||||
msg(" r_patchseg %i [%08x]\n", g_patchseg, segments[g_patchseg].p_vaddr);
|
||||
msg(" r_type2 %i\n", g_type2);
|
||||
msg(" r_dist2 %x\n", r_dist2);
|
||||
msg(" r_addend %08x\n", g_addend);
|
||||
msg(" r_offset %08x\n", g_offset);
|
||||
|
||||
msg(" relocation info[1]\n");
|
||||
msg(" patch addr %08x\n", segments[g_patchseg].p_vaddr + g_offset);
|
||||
msg(" sym addr %08x\n", segments[r_symseg].p_vaddr);
|
||||
msg(" + addend %08x\n", g_addend);
|
||||
|
||||
if (g_type2) {
|
||||
msg(" relocation info[2]\n");
|
||||
msg(" patch addr %08x\n", segments[g_patchseg].p_vaddr + r_dist2 + g_offset);
|
||||
msg(" sym addr %08x\n", segments[r_symseg].p_vaddr);
|
||||
msg(" + addend %08x\n", g_addend);
|
||||
}*/
|
||||
pos += 2;
|
||||
|
||||
applyRelocation(g_type,
|
||||
g_addr + g_offset,
|
||||
g_saddr,
|
||||
g_addend);
|
||||
|
||||
if (g_type2 != R_ARM_NONE) {
|
||||
applyRelocation(g_type2,
|
||||
g_addr + g_offset + r_dist2,
|
||||
g_saddr,
|
||||
g_addend);
|
||||
}
|
||||
break; // size = 12
|
||||
}
|
||||
case 1: {
|
||||
//msg("%08x %08x\n", rel[pos], rel[pos + 1]);
|
||||
auto r_symseg = (rel[pos] >> 4) & 0xF; // index into phdrs
|
||||
g_type = (rel[pos] >> 8) & 0xFF; // relocation type
|
||||
g_patchseg = (rel[pos] >> 16) & 0xF; // index into phdrs
|
||||
g_offset = (rel[pos] >> 20) | // offset
|
||||
((rel[pos+1] & 0x3FF) << 12);
|
||||
g_addend = (rel[pos+1] >> 10); // addend
|
||||
|
||||
// save these
|
||||
g_addr = segments[g_patchseg].p_vaddr;
|
||||
g_saddr = segments[r_symseg].p_vaddr;
|
||||
|
||||
/*msg(" r_symseg %i [%08x]\n", r_symseg, segments[r_symseg].p_vaddr);
|
||||
msg(" r_type %i\n", g_type);
|
||||
msg(" r_patchseg %i [%08x]\n", g_patchseg, segments[g_patchseg].p_vaddr);
|
||||
msg(" r_offset %08x\n", g_offset);
|
||||
msg(" r_addend %08x\n", g_addend);
|
||||
msg(" relocation info [1]\n");
|
||||
msg(" patch addr %08x\n", segments[g_patchseg].p_vaddr + g_offset);
|
||||
msg(" sym addr %08x\n", segments[r_symseg].p_vaddr);
|
||||
msg(" + addend %08x\n", g_addend);*/
|
||||
|
||||
applyRelocation(g_type,
|
||||
g_addr + g_offset,
|
||||
g_saddr,
|
||||
g_addend);
|
||||
g_type2 = 0;
|
||||
|
||||
pos += 1;
|
||||
break; // size = 8
|
||||
}
|
||||
case 2: {
|
||||
//msg("%08x %08x\n", rel[pos], rel[pos + 1]);
|
||||
auto r_symseg = (rel[pos] >> 4) & 0xF;
|
||||
g_type = (rel[pos] >> 8) & 0xFF;
|
||||
auto r_offset = (rel[pos] >> 16);
|
||||
g_addend = (rel[pos+1]);
|
||||
|
||||
g_offset += r_offset;
|
||||
g_saddr = segments[r_symseg].p_vaddr;
|
||||
|
||||
/*msg(" r_symseg %i [%08x]\n", r_symseg, segments[r_symseg].p_vaddr);
|
||||
msg(" r_type %i\n", g_type);
|
||||
msg(" r_offset %08x\n", r_offset);
|
||||
msg(" r_addend %08x\n", g_addend);
|
||||
msg(" relocation info [1]\n");
|
||||
msg(" patch addr %08x + %08x\n", g_addr, r_offset);
|
||||
msg(" sym addr %08x\n", segments[r_symseg].p_vaddr);
|
||||
msg(" + addend %08x\n", g_addend);*/
|
||||
|
||||
applyRelocation(g_type,
|
||||
g_addr + g_offset,
|
||||
g_saddr,
|
||||
g_addend);
|
||||
|
||||
g_type2 = 0;
|
||||
|
||||
pos += 1;
|
||||
break; // size = 8
|
||||
}
|
||||
case 3: { // for THUMB/ARM MOVW/MOVT pairs
|
||||
//msg("%08x %08x\n", rel[pos], rel[pos + 1]);
|
||||
auto r_symseg = (rel[pos] >> 4) & 0xF;
|
||||
auto r_mode = (rel[pos] >> 8) & 1; // 1 = THUMB, 0 = ARM
|
||||
auto r_offset = (rel[pos] >> 9) & 0x3FFFF;
|
||||
auto r_dist2 = (rel[pos] >> 27) & 0x1F;
|
||||
g_addend = (rel[pos+1]);
|
||||
// if r_mode rtype1 = R_ARM_THM_MOVW_ABS_NC <- THUMB
|
||||
// else rtype1 = R_ARM_MOVW_ABS_NC <- ARM
|
||||
// if r_mode rtype2 = R_ARM_THM_MOVT_ABS <- THUMB
|
||||
// else rtype2 = R_ARM_MOVT_ABS <- ARM
|
||||
// offset = prevoffset + r_offset
|
||||
// offset2 = offset + r_offset2
|
||||
/*msg(" r_symseg %i\n", r_symseg);
|
||||
msg(" r_mode %i\n", r_mode);
|
||||
msg(" r_offset %08x\n", r_offset);
|
||||
msg(" r_dist2 %08x\n", r_dist2);
|
||||
msg(" r_addend %08x\n", g_addend);*/
|
||||
|
||||
if (r_mode == 1)
|
||||
g_type = R_ARM_THM_MOVW_ABS_NC;
|
||||
else if (r_mode == 0)
|
||||
g_type = R_ARM_MOVW_ABS_NC;
|
||||
|
||||
if (r_mode == 1)
|
||||
g_type2 = R_ARM_THM_MOVT_ABS;
|
||||
else if (r_mode == 0)
|
||||
g_type2 = R_ARM_MOVT_ABS;
|
||||
|
||||
g_offset += r_offset;
|
||||
g_saddr = segments[r_symseg].p_vaddr;
|
||||
|
||||
/*msg(" relocation info [1]\n");
|
||||
msg(" r_type %i\n", g_type);
|
||||
msg(" patch addr %08x + %08x\n", g_addr, g_offset);
|
||||
msg(" sym addr %08x\n", segments[r_symseg].p_vaddr);
|
||||
msg(" + addend %08x\n", g_addend);
|
||||
|
||||
msg(" relocation info [2]\n");
|
||||
msg(" r_type2 %i\n", g_type2);
|
||||
msg(" patch addr %08x + %08x + %08x\n", g_addr, g_offset, r_dist2);
|
||||
msg(" sym addr %08x\n", segments[r_symseg].p_vaddr);
|
||||
msg(" + addend %08x\n", g_addend);*/
|
||||
|
||||
applyRelocation(g_type,
|
||||
g_addr + g_offset,
|
||||
g_saddr,
|
||||
g_addend);
|
||||
|
||||
applyRelocation(g_type2,
|
||||
g_addr + g_offset + r_dist2,
|
||||
g_saddr,
|
||||
g_addend);
|
||||
|
||||
pos += 1;
|
||||
break; // size = 8
|
||||
}
|
||||
case 4: {
|
||||
//msg("%08x\n", rel[pos]);
|
||||
auto r_offset = (rel[pos] >> 4) & 0x7FFFFF;
|
||||
auto r_dist2 = (rel[pos] >> 27) & 0x1F;
|
||||
// offset = prevoffset + r_offset
|
||||
// offset2 = r_offset +
|
||||
// uses previous rtype1 and rtype2
|
||||
/*msg(" r_offset %08x\n", r_offset);
|
||||
msg(" r_dist2 %08x\n", r_dist2);*/
|
||||
|
||||
g_offset += r_offset;
|
||||
|
||||
/*msg(" relocation info [1]\n");
|
||||
msg(" r_type %i\n", g_type);
|
||||
msg(" patch addr %08x + %08x\n", g_addr, g_offset);
|
||||
msg(" sym addr %08x\n", g_saddr);
|
||||
msg(" + addend %08x\n", g_addend);
|
||||
|
||||
msg(" relocation info [2]\n");
|
||||
msg(" r_type2 %i\n", g_type2);
|
||||
msg(" patch addr %08x + %08x + %08x\n", g_addr, g_offset, r_dist2);
|
||||
msg(" sym addr %08x\n", g_saddr);
|
||||
msg(" + addend %08x\n", g_addend);*/
|
||||
|
||||
applyRelocation(g_type,
|
||||
g_addr + g_offset,
|
||||
g_saddr,
|
||||
g_addend);
|
||||
|
||||
applyRelocation(g_type2,
|
||||
g_addr + g_offset + r_dist2,
|
||||
g_saddr,
|
||||
g_addend);
|
||||
|
||||
pos += 0; break; // size = 4
|
||||
}
|
||||
case 5: {
|
||||
//msg("%08x\n", rel[pos]);
|
||||
auto r_dist_1 = (rel[pos] >> 4) & 0x1FF;
|
||||
auto r_dist_2 = (rel[pos] >> 13) & 0x1F;
|
||||
auto r_dist_3 = (rel[pos] >> 18) & 0x1FF;
|
||||
auto r_dist_4 = (rel[pos] >> 27) & 0x1F;
|
||||
/*msg("r_dist_1 %08x\n", r_dist_2);
|
||||
msg("r_dist_2 %08x\n", r_dist_2);
|
||||
msg("r_dist_3 %08x\n", r_dist_3);
|
||||
msg("r_dist_4 %08x\n", r_dist_4);
|
||||
|
||||
msg(" relocation info [1]\n");
|
||||
msg(" r_type %i\n", g_type);
|
||||
msg(" patch addr %08x + %08x\n", g_addr, g_offset + r_dist_1);
|
||||
msg(" sym addr %08x\n", g_saddr);
|
||||
msg(" + addend %08x\n", g_addend);
|
||||
|
||||
msg(" relocation info [2]\n");
|
||||
msg(" r_type2 %i\n", g_type2);
|
||||
msg(" patch addr %08x + %08x + %08x\n", g_addr, g_offset, r_dist_2);
|
||||
msg(" sym addr %08x\n", g_saddr);
|
||||
msg(" + addend %08x\n", g_addend);*/
|
||||
|
||||
applyRelocation(g_type,
|
||||
g_addr + g_offset + r_dist_1,
|
||||
g_saddr,
|
||||
g_addend);
|
||||
|
||||
applyRelocation(g_type2,
|
||||
g_addr + g_offset + r_dist_2,
|
||||
g_saddr,
|
||||
g_addend);
|
||||
|
||||
g_offset += r_dist_1 + r_dist_3;
|
||||
|
||||
/*msg(" relocation info [3]\n");
|
||||
msg(" r_type %i\n", g_type);
|
||||
msg(" patch addr %08x + %08x\n", g_addr, g_offset + r_dist_3);
|
||||
msg(" sym addr %08x\n", g_saddr);
|
||||
msg(" + addend %08x\n", g_addend);
|
||||
|
||||
msg(" relocation info [4]\n");
|
||||
msg(" r_type2 %i\n", g_type2);
|
||||
msg(" patch addr %08x + %08x + %08x\n", g_addr, g_offset, r_dist_4);
|
||||
msg(" sym addr %08x\n", g_saddr);
|
||||
msg(" + addend %08x\n", g_addend);*/
|
||||
|
||||
applyRelocation(g_type,
|
||||
g_addr + g_offset,
|
||||
g_saddr,
|
||||
g_addend);
|
||||
|
||||
applyRelocation(g_type2,
|
||||
g_addr + g_offset + r_dist_4,
|
||||
g_saddr,
|
||||
g_addend);
|
||||
pos += 0; break; // size = 4
|
||||
}
|
||||
case 6: {
|
||||
//msg("%08x\n", rel[pos]);
|
||||
auto r_offset = (rel[pos] >> 4);
|
||||
//msg(" r_offset %08x\n", r_offset);
|
||||
|
||||
g_offset += r_offset;
|
||||
|
||||
// assumes value is already stored
|
||||
auto orgval = get_original_long(segments[g_patchseg].p_vaddr + g_offset);
|
||||
uint32 segbase = 0;
|
||||
for (auto seg : m_elf->getSegments()) {
|
||||
if (orgval >= seg.p_vaddr &&
|
||||
orgval < seg.p_vaddr + seg.p_filesz) {
|
||||
segbase = seg.p_vaddr;
|
||||
}
|
||||
}
|
||||
|
||||
auto r_addend = orgval - segbase;
|
||||
g_saddr = segbase; //+ m_relocAddr;
|
||||
|
||||
/*msg(" relocation info [1]\n");
|
||||
msg(" r_patchseg %i [%08x]\n", g_patchseg, segments[g_patchseg].p_vaddr);
|
||||
msg(" patch addr %08x + %08x\n", g_addr, g_offset);
|
||||
msg(" sym addr %08x\n", g_saddr + r_addend);*/
|
||||
|
||||
g_type2 = 0;
|
||||
g_type = R_ARM_ABS32;
|
||||
|
||||
applyRelocation(g_type,
|
||||
g_addr + g_offset,
|
||||
g_saddr,
|
||||
r_addend);
|
||||
pos += 0; break; // size = 4
|
||||
}
|
||||
case 7: // 7 bit offsets
|
||||
case 8: // 4 bit offsets
|
||||
case 9: { // 2 bit offsets
|
||||
//msg("%08x\n", rel[pos]);
|
||||
auto r_offsets = (rel[pos] >> 4);
|
||||
//msg("r_offsets %08x\n", r_offsets);
|
||||
|
||||
uint32 bitsize;
|
||||
uint32 mask;
|
||||
switch (r_format) {
|
||||
case 7: bitsize = 7; mask = 0x7F; break;
|
||||
case 8: bitsize = 4; mask = 0x0F; break;
|
||||
case 9: bitsize = 2; mask = 0x03; break;
|
||||
}
|
||||
|
||||
do {
|
||||
auto offset = (r_offsets & mask) * sizeof(uint32);
|
||||
g_offset += offset;
|
||||
auto orgval = get_original_long(segments[g_patchseg].p_vaddr + g_offset);
|
||||
|
||||
uint32 segbase = 0;
|
||||
for (auto seg : m_elf->getSegments()) {
|
||||
if (orgval >= seg.p_vaddr &&
|
||||
orgval < seg.p_vaddr + seg.p_filesz) {
|
||||
segbase = seg.p_vaddr;
|
||||
}
|
||||
}
|
||||
|
||||
auto r_addend = orgval - segbase;
|
||||
g_saddr = segbase;// + m_relocAddr;
|
||||
|
||||
/*msg(" relocation info [1]\n");
|
||||
msg(" offset %08x\n", offset);
|
||||
msg(" r_patchseg %i [%08x]\n", g_patchseg, segments[g_patchseg].p_vaddr);
|
||||
msg(" patch addr %08x + %08x\n", g_addr, g_offset);
|
||||
msg(" sym addr %08x\n", g_saddr);
|
||||
msg(" + addend %08x\n", r_addend);*/
|
||||
|
||||
//doDwrd(g_addr + g_offset, 4);
|
||||
|
||||
g_type2 = 0;
|
||||
g_type = R_ARM_ABS32;
|
||||
|
||||
applyRelocation(g_type,
|
||||
g_addr + g_offset,
|
||||
g_saddr,
|
||||
r_addend);
|
||||
|
||||
} while (r_offsets >>= bitsize);
|
||||
|
||||
pos += 0; break;
|
||||
}
|
||||
default:
|
||||
msg("Invalid r_format %i at offset %x!n", r_format, pos * 4);
|
||||
break;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void psp2_loader::applyRelocation(uint32 type, uint32 addr, uint32 symval, uint32 addend) {
|
||||
switch (type) {
|
||||
case R_ARM_NONE:
|
||||
case R_ARM_V4BX:
|
||||
break;
|
||||
case R_ARM_ABS32:
|
||||
case R_ARM_TARGET1:
|
||||
patch_long(addr, symval + addend);
|
||||
break;
|
||||
case R_ARM_REL32:
|
||||
case R_ARM_TARGET2:
|
||||
patch_long(addr, symval - addr + addend);
|
||||
break;
|
||||
default:
|
||||
msg("Unsupported relocation type (%i)!\n", type);
|
||||
}
|
||||
}
|
||||
|
||||
void psp2_loader::applyModuleInfo() {
|
||||
auto firstSegment = m_elf->getSegments()[0].p_vaddr;
|
||||
auto modInfoAddr = m_elf->entry() + firstSegment;
|
||||
|
||||
tid_t tid = get_struc_id("_scemoduleinfo");
|
||||
doStruct(modInfoAddr, sizeof(_scemoduleinfo_prx2arm), tid);
|
||||
|
||||
auto entTop = get_long(modInfoAddr + offsetof(_scemoduleinfo_prx2arm, ent_top));
|
||||
auto entEnd = get_long(modInfoAddr + offsetof(_scemoduleinfo_prx2arm, ent_end));
|
||||
loadExports( firstSegment + entTop, firstSegment + entEnd );
|
||||
|
||||
auto stubTop = get_long(modInfoAddr + offsetof(_scemoduleinfo_prx2arm, stub_top));
|
||||
auto stubEnd = get_long(modInfoAddr + offsetof(_scemoduleinfo_prx2arm, stub_end));
|
||||
loadImports( firstSegment + stubTop, firstSegment + stubEnd );
|
||||
}
|
||||
|
||||
void psp2_loader::loadExports(uint32 entTop, uint32 entEnd) {
|
||||
uchar structsize;
|
||||
|
||||
for (ea_t ea = entTop; ea < entEnd; ea += structsize) {
|
||||
structsize = get_byte(ea);
|
||||
|
||||
auto nfunc = get_word(ea + offsetof(_scelibent_common, nfunc));
|
||||
auto nvar = get_word(ea + offsetof(_scelibent_common, nvar));
|
||||
auto ntlsvar = get_word(ea + offsetof(_scelibent_common, ntlsvar));
|
||||
|
||||
auto count = nfunc + nvar + ntlsvar;
|
||||
|
||||
if (structsize == sizeof(_scelibent_prx2arm)) {
|
||||
doStruct(ea, sizeof(_scelibent_prx2arm), get_struc_id("_scelibent"));
|
||||
|
||||
auto nidtable = get_long(ea + offsetof(_scelibent_prx2arm, nidtable));
|
||||
auto addtable = get_long(ea + offsetof(_scelibent_prx2arm, addtable));
|
||||
|
||||
if (nidtable != NULL && addtable != NULL) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto nidoffset = nidtable + (i * 4);
|
||||
auto addoffset = addtable + (i * 4);
|
||||
|
||||
auto nid = get_long(nidoffset);
|
||||
auto add = get_long(addoffset);
|
||||
|
||||
auto resolvedNid = getNameFromDatabase(nid);
|
||||
if (resolvedNid) {
|
||||
set_cmt(nidoffset, resolvedNid, false);
|
||||
if (add & 1)
|
||||
add -= 1;
|
||||
do_name_anyway(add, resolvedNid);
|
||||
}
|
||||
|
||||
if (i < nfunc)
|
||||
auto_make_proc(add);
|
||||
|
||||
doDwrd(nidoffset, 4);
|
||||
doDwrd(addoffset, 4);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msg("Unknown export structure at %08x\n", ea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void psp2_loader::loadImports(uint32 stubTop, uint32 stubEnd) {
|
||||
uchar structsize;
|
||||
|
||||
for (ea_t ea = stubTop; ea < stubEnd; ea += structsize) {
|
||||
structsize = get_byte(ea);
|
||||
|
||||
auto nfunc = get_word(ea + offsetof(_scelibstub_common, nfunc));
|
||||
auto nvar = get_word(ea + offsetof(_scelibstub_common, nvar));
|
||||
auto ntlsvar = get_word(ea + offsetof(_scelibstub_common, ntlsvar));
|
||||
|
||||
if (structsize == sizeof(_scelibstub_prx2arm)) {
|
||||
doStruct(ea, sizeof(_scelibstub_prx2arm), get_struc_id("_scelibstub"));
|
||||
|
||||
auto funcnidtable = get_long(ea + offsetof(_scelibstub_prx2arm, func_nidtable));
|
||||
auto functable = get_long(ea + offsetof(_scelibstub_prx2arm, func_table));
|
||||
auto varnidtable = get_long(ea + offsetof(_scelibstub_prx2arm, var_nidtable));
|
||||
auto vartable = get_long(ea + offsetof(_scelibstub_prx2arm, var_table));
|
||||
auto tlsnidtable = get_long(ea + offsetof(_scelibstub_prx2arm, tls_nidtable));
|
||||
auto tlstable = get_long(ea + offsetof(_scelibstub_prx2arm, tls_table));
|
||||
|
||||
if (funcnidtable != NULL && functable != NULL) {
|
||||
for (size_t i = 0; i < nfunc; ++i) {
|
||||
auto nidoffset = funcnidtable + (i * 4);
|
||||
auto funcoffset = functable + (i * 4);
|
||||
|
||||
auto nid = get_long(nidoffset);
|
||||
auto func = get_long(funcoffset);
|
||||
|
||||
auto resolvedNid = getNameFromDatabase(nid);
|
||||
if (resolvedNid) {
|
||||
set_cmt(nidoffset, resolvedNid, false);
|
||||
if (func & 1)
|
||||
func -= 1;
|
||||
do_name_anyway(func, resolvedNid);
|
||||
}
|
||||
|
||||
doDwrd(nidoffset, 4);
|
||||
doDwrd(funcoffset, 4);
|
||||
|
||||
if (add_func(func, BADADDR)) {
|
||||
get_func(func)->flags |= FUNC_LIB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (varnidtable != NULL && vartable != NULL) {
|
||||
for (size_t i = 0; i < nvar; ++i) {
|
||||
auto nidoffset = varnidtable + (i * 4);
|
||||
auto varoffset = vartable + (i * 4);
|
||||
|
||||
auto nid = get_long(nidoffset);
|
||||
auto var = get_long(varoffset);
|
||||
|
||||
doDwrd(nidoffset, 4);
|
||||
doDwrd(varoffset, 4);
|
||||
}
|
||||
}
|
||||
|
||||
if (tlsnidtable != NULL && tlstable != NULL) {
|
||||
for (size_t i = 0; i < nvar; ++i) {
|
||||
auto nidoffset = tlsnidtable + (i * 4);
|
||||
auto tlsoffset = tlstable + (i * 4);
|
||||
|
||||
auto nid = get_long(nidoffset);
|
||||
auto tls = get_long(tlsoffset);
|
||||
|
||||
doDwrd(nidoffset, 4);
|
||||
doDwrd(tlsoffset, 4);
|
||||
}
|
||||
}
|
||||
} else if (structsize == 0x24) {
|
||||
doByte(ea+0, 1); // structsize
|
||||
doByte(ea+1, 1); // auxattribute
|
||||
doWord(ea+2, 2); // version
|
||||
doWord(ea+4, 2); // attribute
|
||||
doWord(ea+6, 2); // nfunc
|
||||
doWord(ea+8, 2); // nvar
|
||||
doWord(ea+10, 2); // reserved?
|
||||
doDwrd(ea+12, 4); // libname_nid
|
||||
doDwrd(ea+16, 4); // libname
|
||||
doDwrd(ea+20, 4); // funcnidtable
|
||||
doDwrd(ea+24, 4); // functable
|
||||
doDwrd(ea+28, 4); // varnidtable
|
||||
doDwrd(ea+32, 4); // vartable
|
||||
|
||||
auto funcnidtable = get_long(ea + 0x14);
|
||||
auto functable = get_long(ea + 0x18);
|
||||
auto varnidtable = get_long(ea + 0x1C);
|
||||
auto vartable = get_long(ea + 0x20);
|
||||
|
||||
if (funcnidtable != NULL && functable != NULL) {
|
||||
for (size_t i = 0; i < nfunc; ++i) {
|
||||
auto nidoffset = funcnidtable + (i * 4);
|
||||
auto funcoffset = functable + (i * 4);
|
||||
|
||||
auto nid = get_long(nidoffset);
|
||||
auto func = get_long(funcoffset);
|
||||
|
||||
auto resolvedNid = getNameFromDatabase(nid);
|
||||
if (resolvedNid) {
|
||||
set_cmt(nidoffset, resolvedNid, false);
|
||||
if (func & 1)
|
||||
func -= 1;
|
||||
do_name_anyway(func, resolvedNid);
|
||||
}
|
||||
|
||||
doDwrd(nidoffset, 4);
|
||||
doDwrd(funcoffset, 4);
|
||||
|
||||
if (add_func(func, BADADDR)) {
|
||||
get_func(func)->flags |= FUNC_LIB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (varnidtable != NULL && vartable != NULL) {
|
||||
for (size_t i = 0; i < nvar; ++i) {
|
||||
auto nidoffset = varnidtable + (i * 4);
|
||||
auto varoffset = vartable + (i * 4);
|
||||
|
||||
auto nid = get_long(nidoffset);
|
||||
auto var = get_long(varoffset);
|
||||
|
||||
doDwrd(nidoffset, 4);
|
||||
doDwrd(varoffset, 4);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
msg("Unknown import structure at %08x\n", ea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *psp2_loader::getNameFromDatabase(unsigned int nid) {
|
||||
auto value = m_nidset.find(nid);
|
||||
if (value != m_nidset.end())
|
||||
return value->second.c_str();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void psp2_loader::applySymbols() {
|
||||
msg("Applying symbols...\n");
|
||||
|
||||
auto section = m_elf->getSymbolsSection();
|
||||
|
||||
if (section == NULL)
|
||||
return;
|
||||
|
||||
auto nsym = m_elf->getNumSymbols();
|
||||
auto symbols = m_elf->getSymbols();
|
||||
|
||||
const char *stringTable = m_elf->getSections().at(section->sh_link).data();
|
||||
|
||||
for (size_t i = 0; i < nsym; ++i) {
|
||||
auto &symbol = symbols[i];
|
||||
|
||||
auto type = ELF64_ST_TYPE(symbol.st_info),
|
||||
bind = ELF64_ST_BIND(symbol.st_info);
|
||||
auto value = symbol.st_value;
|
||||
|
||||
if (symbol.st_shndx > m_elf->getNumSections() ||
|
||||
!(m_elf->getSections()[symbol.st_shndx].sh_flags & SHF_ALLOC))
|
||||
continue;
|
||||
|
||||
if (symbol.st_shndx == SHN_ABS)
|
||||
continue;
|
||||
|
||||
if (isLoadingPrx())
|
||||
value += m_elf->getSections()[symbol.st_shndx].sh_addr;
|
||||
|
||||
switch (type) {
|
||||
case STT_OBJECT:
|
||||
do_name_anyway(value, &stringTable[symbol.st_name]);
|
||||
break;
|
||||
case STT_FILE:
|
||||
describe(value, true, "Source File: %s", &stringTable[symbol.st_name]);
|
||||
break;
|
||||
case STT_FUNC:
|
||||
do_name_anyway(value, &stringTable[symbol.st_name]);
|
||||
auto_make_proc(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void psp2_loader::declareStructures() {
|
||||
struc_t *sptr;
|
||||
|
||||
tid_t modInfoCommon = add_struc(-1, "_scemoduleinfo_common");
|
||||
sptr = get_struc(modInfoCommon);
|
||||
if (sptr != NULL) {
|
||||
add_struc_member(sptr, "modattribute", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "modversion", BADADDR, byteflag(), NULL, 2);
|
||||
add_struc_member(sptr, "modname", BADADDR, byteflag(), NULL, SYS_MODULE_NAME_LEN);
|
||||
add_struc_member(sptr, "terminal", BADADDR, byteflag(), NULL, 1);
|
||||
|
||||
sptr = get_struc(add_struc(-1, "_scemoduleinfo"));
|
||||
if (sptr != NULL) {
|
||||
typeinfo_t mt;
|
||||
mt.tid = modInfoCommon;
|
||||
add_struc_member(sptr, "c", BADADDR, struflag(), &mt, get_struc_size(mt.tid));
|
||||
|
||||
add_struc_member(sptr, "resreve", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "ent_top", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "ent_end", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "stub_top", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "stub_end", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "dbg_fingerprint", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "tls_top", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "tls_filesz", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "tls_memsz", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "start_entry", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "stop_entry", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "arm_exidx_top", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "arm_exidx_end", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "arm_extab_top", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "arm_extab_end", BADADDR, dwrdflag(), NULL, 4);
|
||||
}
|
||||
}
|
||||
|
||||
tid_t libStubCommon = add_struc(-1, "_scelibstub_common");
|
||||
sptr = get_struc(libStubCommon);
|
||||
if (sptr != NULL) {
|
||||
add_struc_member(sptr, "structsize", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "reserved1", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "version", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "attribute", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "nfunc", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "nvar", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "ntlsvar", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "reserved2", BADADDR, byteflag(), NULL, 4);
|
||||
|
||||
sptr = get_struc(add_struc(-1, "_scelibstub"));
|
||||
if (sptr != NULL) {
|
||||
typeinfo_t mt;
|
||||
mt.tid = libStubCommon;
|
||||
add_struc_member(sptr, "c", BADADDR, struflag(), &mt, get_struc_size(mt.tid));
|
||||
|
||||
add_struc_member(sptr, "libname_nid", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "libname", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "sce_sdk_version", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "func_nidtable", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "func_table", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "var_nidtable", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "var_table", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "tls_nidtable", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "tls_table", BADADDR, dwrdflag(), NULL, 4);
|
||||
}
|
||||
}
|
||||
|
||||
tid_t libEntCommon = add_struc(-1, "_scelibent_common");
|
||||
sptr = get_struc(libEntCommon);
|
||||
if (sptr != NULL) {
|
||||
add_struc_member(sptr, "structsize", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "reserved1", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "version", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "attribute", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "nfunc", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "nvar", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "ntlsvar", BADADDR, wordflag(), NULL, 2);
|
||||
add_struc_member(sptr, "hashinfo", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "hashinfotls", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "reserved2", BADADDR, byteflag(), NULL, 1);
|
||||
add_struc_member(sptr, "nidaltsets", BADADDR, byteflag(), NULL, 1);
|
||||
|
||||
sptr = get_struc(add_struc(-1, "_scelibent"));
|
||||
if (sptr != NULL) {
|
||||
typeinfo_t mt;
|
||||
mt.tid = libEntCommon;
|
||||
add_struc_member(sptr, "c", BADADDR, struflag(), &mt, get_struc_size(mt.tid));
|
||||
|
||||
add_struc_member(sptr, "libname_nid", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "libname", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "nidtable", BADADDR, dwrdflag(), NULL, 4);
|
||||
add_struc_member(sptr, "addtable", BADADDR, dwrdflag(), NULL, 4);
|
||||
}
|
||||
}
|
||||
}
|
54
src/vita/psp2_loader.h
Normal file
54
src/vita/psp2_loader.h
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "elf_reader.h"
|
||||
#include "sce.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
#include <map>
|
||||
|
||||
class psp2_loader
|
||||
{
|
||||
elf_reader<elf32> *m_elf;
|
||||
uint64 m_relocAddr;
|
||||
|
||||
std::ifstream m_database;
|
||||
std::map<uint32, std::string> m_nidset;
|
||||
|
||||
public:
|
||||
psp2_loader(elf_reader<elf32> *elf, std::string databaseFile);
|
||||
|
||||
void apply();
|
||||
|
||||
bool isLoadingPrx() const
|
||||
{ return m_elf->type() == ET_SCE_RELEXEC; }
|
||||
|
||||
bool isLoadingExec() const
|
||||
{ return m_elf->type() == ET_EXEC; }
|
||||
|
||||
private:
|
||||
void declareStructures();
|
||||
|
||||
void applySegments();
|
||||
void applySectionHeaders();
|
||||
void applyProgramHeaders();
|
||||
void applySegment(
|
||||
uint32 sel, uint64 offset,
|
||||
uint64 addr, uint64 size,
|
||||
const char *name, const char *sclass,
|
||||
uchar perm, uchar align,
|
||||
bool load = true
|
||||
);
|
||||
|
||||
void applyRelocations();
|
||||
void applyRelocation(uint32 type, uint32 addr, uint32 addend, uint32 value);
|
||||
|
||||
void applyModuleInfo();
|
||||
void loadExports(uint32 entTop, uint32 entEnd);
|
||||
void loadImports(uint32 stubTop, uint32 stubEnd);
|
||||
|
||||
const char *getNameFromDatabase(unsigned int nid);
|
||||
|
||||
void applySymbols();
|
||||
};
|
||||
|
110
src/vita/sce.h
Normal file
110
src/vita/sce.h
Normal file
@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#include "elf.h"
|
||||
|
||||
#define ET_SCE_EXEC 0xfe00
|
||||
#define ET_SCE_RELEXEC 0xfe04 /* PRX */
|
||||
|
||||
#define ET_SCE_ARMRELEXEC 0xffa5
|
||||
|
||||
#define SHT_SCE_ARMRELA 0x700000a4
|
||||
|
||||
#define PT_SCE_RELA 0x60000000
|
||||
|
||||
#define PT_SCE_COMMENT 0x6fffff00
|
||||
#define PT_SCE_VERSION 0x6fffff01
|
||||
|
||||
#define PT_SCE_ARMRELA 0x700000A4
|
||||
#define PT_SCE_SEGSYM 0x700000A8
|
||||
|
||||
#define SYS_MODULE_NAME_LEN 27
|
||||
#define SYS_MODULE_MAX_SEGMENTS 4
|
||||
|
||||
#define SYS_LIB_AUTO_EXPORT (0x0001)
|
||||
#define SYS_LIB_WEAK_EXPORT (0x0002)
|
||||
#define SYS_LIB_NOLINK_EXPORT (0x0004)
|
||||
#define SYS_LIB_WEAK_IMPORT (0x0008)
|
||||
|
||||
/* MODULE INFO */
|
||||
|
||||
typedef struct _scemoduleinfo_common {
|
||||
unsigned short modattribute;
|
||||
unsigned char modversion[2];
|
||||
char modname[SYS_MODULE_NAME_LEN];
|
||||
char terminal;
|
||||
} sceModuleInfo_common;
|
||||
|
||||
typedef struct _scemoduleinfo_prx2arm {
|
||||
sceModuleInfo_common c;
|
||||
Elf32_Addr resreve;
|
||||
Elf32_Addr ent_top;
|
||||
Elf32_Addr ent_end;
|
||||
Elf32_Addr stub_top;
|
||||
Elf32_Addr stub_end;
|
||||
Elf32_Word dbg_fingerprint;
|
||||
Elf32_Addr tls_top;
|
||||
Elf32_Addr tls_filesz;
|
||||
Elf32_Addr tls_memsz;
|
||||
Elf32_Addr start_entry;
|
||||
Elf32_Addr stop_entry;
|
||||
Elf32_Addr arm_exidx_top;
|
||||
Elf32_Addr arm_exidx_end;
|
||||
Elf32_Addr arm_extab_top;
|
||||
Elf32_Addr arm_extab_end;
|
||||
} sceModuleInfo_prx2arm;
|
||||
|
||||
/* IMPORTS */
|
||||
|
||||
typedef struct _scelibstub_common {
|
||||
unsigned char structsize;
|
||||
unsigned char reserved1[1];
|
||||
unsigned short version;
|
||||
unsigned short attribute;
|
||||
unsigned short nfunc;
|
||||
unsigned short nvar;
|
||||
unsigned short ntlsvar;
|
||||
unsigned char reserved2[4];
|
||||
} sceKernelLibraryStubTable_common;
|
||||
|
||||
typedef sceKernelLibraryStubTable_common
|
||||
sceKernelLibraryStubTable_prx2_common;
|
||||
|
||||
typedef struct _scelibstub_prx2arm {
|
||||
sceKernelLibraryStubTable_prx2_common c;
|
||||
Elf32_Word libname_nid;
|
||||
Elf32_Addr libname;
|
||||
Elf32_Word sce_sdk_version;
|
||||
Elf32_Addr func_nidtable;
|
||||
Elf32_Addr func_table;
|
||||
Elf32_Addr var_nidtable;
|
||||
Elf32_Addr var_table;
|
||||
Elf32_Addr tls_nidtable;
|
||||
Elf32_Addr tls_table;
|
||||
} sceKernelLibraryStubTable_prx2arm;
|
||||
|
||||
/* EXPORTS */
|
||||
|
||||
typedef struct _scelibent_common {
|
||||
unsigned char structsize;
|
||||
unsigned char auxattribute;
|
||||
short unsigned int version;
|
||||
short unsigned int attribute;
|
||||
short unsigned int nfunc;
|
||||
short unsigned int nvar;
|
||||
short unsigned int ntlsvar;
|
||||
unsigned char hashinfo;
|
||||
unsigned char hashinfotls;
|
||||
unsigned char reserved2[1];
|
||||
unsigned char nidaltsets;
|
||||
} sceKernelLibraryEntryTable_common;
|
||||
|
||||
typedef sceKernelLibraryEntryTable_common
|
||||
sceKernelLibraryEntryTable_prx2_common;
|
||||
|
||||
typedef struct _scelibent_prx2arm {
|
||||
sceKernelLibraryEntryTable_prx2_common c;
|
||||
Elf32_Word libname_nid;
|
||||
Elf32_Addr libname;
|
||||
Elf32_Addr nidtable;
|
||||
Elf32_Addr addtable;
|
||||
} sceKernelLibraryEntryTable_prx2arm;
|
55
src/vita/vita.cpp
Normal file
55
src/vita/vita.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
|
||||
#include "../elf_common/elf_reader.h"
|
||||
#include "psp2_loader.h"
|
||||
#include "sce.h"
|
||||
|
||||
#include <idaldr.h>
|
||||
#include <memory>
|
||||
|
||||
static int idaapi
|
||||
accept_file(linput_t *li, char fileformatname[MAX_FILE_FORMAT_NAME], int n)
|
||||
{
|
||||
if (n > 0)
|
||||
return 0;
|
||||
|
||||
elf_reader<elf32> elf(li);
|
||||
|
||||
if (elf.verifyHeader() &&
|
||||
elf.machine() == EM_ARM) {
|
||||
const char *type;
|
||||
|
||||
if (elf.type() == ET_SCE_EXEC)
|
||||
type = "Executable";
|
||||
else if (elf.type() == ET_SCE_RELEXEC)
|
||||
type = "Relocatable Executable";
|
||||
else
|
||||
return 0;
|
||||
|
||||
set_processor_type("ARM", SETPROC_ALL);
|
||||
|
||||
qsnprintf(fileformatname, MAX_FILE_FORMAT_NAME, "Playstation Vita %s", type);
|
||||
|
||||
return ACCEPT_FIRST | 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void idaapi
|
||||
load_file(linput_t *li, ushort neflags, const char *fileformatname)
|
||||
{
|
||||
elf_reader<elf32> elf(li); elf.read();
|
||||
psp2_loader ldr(&elf, "vita.txt"); ldr.apply();
|
||||
}
|
||||
|
||||
__declspec(dllexport)
|
||||
loader_t LDSC =
|
||||
{
|
||||
IDP_INTERFACE_VERSION,
|
||||
0,
|
||||
accept_file,
|
||||
load_file,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
1
third_party/tinyxml
vendored
Submodule
1
third_party/tinyxml
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit b3e5aabf9c0272fdfd05487b5ea12eaf4522713d
|
Loading…
Reference in New Issue
Block a user