mirror of https://github.com/PrimeDecomp/prime.git
Clean up unused tools
This commit is contained in:
parent
10e847fd07
commit
6acf3a1278
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/bash -e
|
|
||||||
python tools/deincbin.py "$1" > "$1.deincbin.s"
|
|
||||||
mv "$1.deincbin.s" "$1"
|
|
|
@ -3,14 +3,10 @@ CFLAGS := -O3 -Wall -s
|
||||||
|
|
||||||
default: all
|
default: all
|
||||||
|
|
||||||
all: elf2dol elf2rel
|
all: elf2dol
|
||||||
|
|
||||||
elf2dol: elf2dol.c
|
elf2dol: elf2dol.c
|
||||||
$(CC) $(CFLAGS) -o $@ $^
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
elf2rel: elf2rel.c
|
|
||||||
$(CC) $(CFLAGS) -o $@ $^
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) elf2dol
|
$(RM) elf2dol
|
||||||
$(RM) elf2rel
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
#!/bin/bash -e
|
#!/bin/bash -e
|
||||||
|
|
||||||
OBJDUMP="$DEVKITPPC/bin/powerpc-eabi-objdump -D -bbinary -EB -mpowerpc -M gekko"
|
OBJDUMP="$DEVKITPPC/bin/powerpc-eabi-objdump -D -bbinary -EB -mpowerpc -M gekko"
|
||||||
if [ ! -z "$1" ]; then
|
if [ ! -z "$1" ]; then
|
||||||
OPTIONS="--start-address=$(($1)) --stop-address=$(($2))"
|
OPTIONS="--start-address=$(($1)) --stop-address=$(($2))"
|
||||||
fi
|
fi
|
||||||
$OBJDUMP $OPTIONS baserom.dol > baserom.dump
|
$OBJDUMP $OPTIONS baserom.dol > baserom.dump
|
||||||
$OBJDUMP $OPTIONS build/mp1.0/main.dol > main.dump
|
$OBJDUMP $OPTIONS build/mp1.0/main.dol > main.dump
|
||||||
|
|
|
@ -1,120 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
#
|
|
||||||
# Usage: dump_common_data.py file.s
|
|
||||||
# Dumps all incbin data and prints the revised file to stdout.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# Reads a bytearray from baserom.dol
|
|
||||||
def read_baserom(start, size):
|
|
||||||
with open('baserom.dol', 'rb') as f:
|
|
||||||
f.seek(start, os.SEEK_SET)
|
|
||||||
return bytearray(f.read(size))
|
|
||||||
|
|
||||||
if len(sys.argv) != 2:
|
|
||||||
print('Usage: %s ASM_FILE' % sys.argv[0])
|
|
||||||
exit()
|
|
||||||
|
|
||||||
# reads a 32-bit big endian value starting at pos
|
|
||||||
def read_u32(data, pos):
|
|
||||||
return (data[pos]<<24) | (data[pos+1]<<16) | (data[pos+2]<<8) | (data[pos+3])
|
|
||||||
|
|
||||||
def is_ascii(code):
|
|
||||||
if code >= 0x20 and code <= 0x7E: # normal characters
|
|
||||||
return True
|
|
||||||
if code in [0x09, 0x0A]: # tab, newline
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
# reads a string starting at pos
|
|
||||||
def read_string(data, pos):
|
|
||||||
text = ''
|
|
||||||
while pos < len(data) and is_ascii(data[pos]):
|
|
||||||
text += chr(data[pos])
|
|
||||||
pos += 1
|
|
||||||
if pos < len(data) and data[pos] == 0:
|
|
||||||
return text
|
|
||||||
return ''
|
|
||||||
|
|
||||||
# escapes special characters in the string for use in a C string literal
|
|
||||||
def escape_string(text):
|
|
||||||
return text.replace('\\','\\\\').replace('"','\\"').replace('\n','\\n').replace('\t','\\t')
|
|
||||||
|
|
||||||
# returns True if value is 4-byte aligned
|
|
||||||
def is_aligned(num):
|
|
||||||
return num % 4 == 0
|
|
||||||
|
|
||||||
# returns True if value is a possible pointer
|
|
||||||
def is_pointer(num):
|
|
||||||
return num >= 0x80003100 and num <= 0x802F6C80
|
|
||||||
|
|
||||||
# returns True if all elements are zero
|
|
||||||
def is_all_zero(arr):
|
|
||||||
for val in arr:
|
|
||||||
if val != 0:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
# returns string of comma-separated hex bytes
|
|
||||||
def hex_bytes(data):
|
|
||||||
return ', '.join('0x%02X' % n for n in data)
|
|
||||||
|
|
||||||
def convert_data(data, offset):
|
|
||||||
text = ''
|
|
||||||
size = len(data)
|
|
||||||
pos = 0
|
|
||||||
while pos < size:
|
|
||||||
# pad unaligned
|
|
||||||
pad = []
|
|
||||||
while not is_aligned(offset + pos) and pos < size:
|
|
||||||
pad.append(data[pos])
|
|
||||||
pos += 1
|
|
||||||
if len(pad) > 0:
|
|
||||||
if is_all_zero(pad):
|
|
||||||
text += '\t.balign 4\n'
|
|
||||||
else:
|
|
||||||
text += '\t.byte %s\n' % hex_bytes(pad)
|
|
||||||
|
|
||||||
# string?
|
|
||||||
string = read_string(data, pos)
|
|
||||||
if len(string) > 3:
|
|
||||||
text += '\t.asciz "%s"\n' % escape_string(string)
|
|
||||||
pos += len(string) + 1
|
|
||||||
continue
|
|
||||||
|
|
||||||
assert(is_aligned(offset + pos))
|
|
||||||
|
|
||||||
if pos + 4 <= size:
|
|
||||||
val = read_u32(data, pos)
|
|
||||||
if is_pointer(val):
|
|
||||||
text += '\t.4byte 0x%08X ;# ptr\n' % val
|
|
||||||
elif val == 0:
|
|
||||||
text += '\t.4byte 0\n'
|
|
||||||
else:
|
|
||||||
text += '\t.4byte 0x%08X\n' % val
|
|
||||||
pos += 4
|
|
||||||
return text
|
|
||||||
|
|
||||||
currSection = ''
|
|
||||||
|
|
||||||
with open(sys.argv[1], 'rt') as f:
|
|
||||||
for line in f.readlines():
|
|
||||||
line = line.rstrip()
|
|
||||||
# Section directive
|
|
||||||
m = re.match(r'\s*\.section\s+([\._A-Za-z0-9]+)', line)
|
|
||||||
if m:
|
|
||||||
currSection = m.groups()[0]
|
|
||||||
elif currSection in ['.rodata', '.data', '.sdata', '.sdata2', '.ctors', '.dtors', 'extab_', 'extabindex_']:
|
|
||||||
# Incbin directive
|
|
||||||
m = re.match(r'\s*\.incbin\s+"baserom.dol"\s*,\s*([^,]+),\s*([^,]+)', line)
|
|
||||||
if m:
|
|
||||||
g = m.groups()
|
|
||||||
start = int(g[0], 0)
|
|
||||||
size = int(g[1], 0)
|
|
||||||
data = read_baserom(start, size)
|
|
||||||
print('\t# ROM: 0x%X' % start)
|
|
||||||
print(convert_data(data, start))
|
|
||||||
continue
|
|
||||||
print(line)
|
|
836
tools/elf2rel.c
836
tools/elf2rel.c
|
@ -1,836 +0,0 @@
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#ifndef MAX
|
|
||||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MIN
|
|
||||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ARRAY_COUNT(arr) (sizeof(arr) / sizeof((arr)[0]))
|
|
||||||
|
|
||||||
#define IS_LITTLE_ENDIAN (((char*)((uint32_t[]){1}))[0] == 1)
|
|
||||||
|
|
||||||
// Relevant portions of elf.h
|
|
||||||
|
|
||||||
typedef uint32_t Elf32_Addr;
|
|
||||||
typedef uint32_t Elf32_Off;
|
|
||||||
typedef uint32_t Elf32_Word;
|
|
||||||
typedef int32_t Elf32_Sword;
|
|
||||||
typedef uint16_t Elf32_Half;
|
|
||||||
typedef uint16_t Elf32_Section;
|
|
||||||
|
|
||||||
#define EI_NIDENT (16)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
|
|
||||||
Elf32_Half e_type; /* Object file type */
|
|
||||||
Elf32_Half e_machine; /* Architecture */
|
|
||||||
Elf32_Word e_version; /* Object file version */
|
|
||||||
Elf32_Addr e_entry; /* Entry point virtual address */
|
|
||||||
Elf32_Off e_phoff; /* Program header table file offset */
|
|
||||||
Elf32_Off e_shoff; /* Section header table file offset */
|
|
||||||
Elf32_Word e_flags; /* Processor-specific flags */
|
|
||||||
Elf32_Half e_ehsize; /* ELF header size in bytes */
|
|
||||||
Elf32_Half e_phentsize; /* Program header table entry size */
|
|
||||||
Elf32_Half e_phnum; /* Program header table entry count */
|
|
||||||
Elf32_Half e_shentsize; /* Section header table entry size */
|
|
||||||
Elf32_Half e_shnum; /* Section header table entry count */
|
|
||||||
Elf32_Half e_shstrndx; /* Section header string table index */
|
|
||||||
} Elf32_Ehdr;
|
|
||||||
|
|
||||||
#define EI_CLASS 4 /* File class byte index */
|
|
||||||
#define ELFCLASS32 1 /* 32-bit objects */
|
|
||||||
|
|
||||||
#define EI_DATA 5 /* Data encoding byte index */
|
|
||||||
#define ELFDATA2MSB 2 /* 2's complement, big endian */
|
|
||||||
|
|
||||||
#define EM_PPC 20 /* PowerPC */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Elf32_Word sh_name; /* Section name (string tbl index) */
|
|
||||||
Elf32_Word sh_type; /* Section type */
|
|
||||||
Elf32_Word sh_flags; /* Section flags */
|
|
||||||
Elf32_Addr sh_addr; /* Section virtual addr at execution */
|
|
||||||
Elf32_Off sh_offset; /* Section file offset */
|
|
||||||
Elf32_Word sh_size; /* Section size in bytes */
|
|
||||||
Elf32_Word sh_link; /* Link to another section */
|
|
||||||
Elf32_Word sh_info; /* Additional section information */
|
|
||||||
Elf32_Word sh_addralign; /* Section alignment */
|
|
||||||
Elf32_Word sh_entsize; /* Entry size if section holds table */
|
|
||||||
} Elf32_Shdr;
|
|
||||||
|
|
||||||
#define SHT_PROGBITS 1 /* Program data */
|
|
||||||
#define SHT_SYMTAB 2 /* Symbol table */
|
|
||||||
#define SHT_STRTAB 3 /* String table */
|
|
||||||
#define SHT_RELA 4 /* Relocation entries with addends */
|
|
||||||
#define SHT_NOBITS 8 /* Program space with no data (bss) */
|
|
||||||
|
|
||||||
#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
|
|
||||||
#define SHF_EXECINSTR (1 << 2) /* Executable */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Elf32_Word st_name; /* Symbol name (string tbl index) */
|
|
||||||
Elf32_Addr st_value; /* Symbol value */
|
|
||||||
Elf32_Word st_size; /* Symbol size */
|
|
||||||
unsigned char st_info; /* Symbol type and binding */
|
|
||||||
unsigned char st_other; /* Symbol visibility */
|
|
||||||
Elf32_Section st_shndx; /* Section index */
|
|
||||||
} Elf32_Sym;
|
|
||||||
|
|
||||||
#define ELF32_ST_TYPE(val) ((val)&0xf)
|
|
||||||
|
|
||||||
/* Legal values for ST_TYPE subfield of st_info (symbol type). */
|
|
||||||
|
|
||||||
#define STT_NOTYPE 0 /* Symbol type is unspecified */
|
|
||||||
#define STT_FUNC 2 /* Symbol is a code object */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Elf32_Addr r_offset; /* Address */
|
|
||||||
Elf32_Word r_info; /* Relocation type and symbol index */
|
|
||||||
Elf32_Sword r_addend; /* Addend */
|
|
||||||
} Elf32_Rela;
|
|
||||||
|
|
||||||
/* How to extract and insert information held in the r_info field. */
|
|
||||||
|
|
||||||
#define ELF32_R_SYM(val) ((val) >> 8)
|
|
||||||
#define ELF32_R_TYPE(val) ((val)&0xff)
|
|
||||||
|
|
||||||
#define R_PPC_NONE 0
|
|
||||||
#define R_PPC_ADDR32 1 /* 32bit absolute address */
|
|
||||||
#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */
|
|
||||||
#define R_PPC_ADDR16 3 /* 16bit absolute address */
|
|
||||||
#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */
|
|
||||||
#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */
|
|
||||||
#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */
|
|
||||||
#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */
|
|
||||||
#define R_PPC_ADDR14_BRTAKEN 8
|
|
||||||
#define R_PPC_ADDR14_BRNTAKEN 9
|
|
||||||
#define R_PPC_REL24 10 /* PC relative 26 bit */
|
|
||||||
#define R_PPC_REL14 11 /* PC relative 16 bit */
|
|
||||||
|
|
||||||
#define R_DOLPHIN_SECTION 202
|
|
||||||
#define R_DOLPHIN_END 203
|
|
||||||
|
|
||||||
// end elf.h
|
|
||||||
|
|
||||||
struct RelHeader {
|
|
||||||
uint32_t moduleId; // unique module identifier
|
|
||||||
uint32_t nextModule; // always 0; filled in at runtime
|
|
||||||
uint32_t prevModule; // always 0; filled in at runtime
|
|
||||||
uint32_t sectionCount; // number of sections in the section table
|
|
||||||
uint32_t sectionTableOffset; // file position of section table
|
|
||||||
uint32_t moduleNameOffset; // offset of the module name in the string table
|
|
||||||
// (not in this file)
|
|
||||||
uint32_t moduleNameSize; // size of the module name in the string table (not
|
|
||||||
// in this file)
|
|
||||||
uint32_t formatVersion; // REL format version
|
|
||||||
uint32_t bssSize; // size of the BSS section
|
|
||||||
uint32_t relocationTableOffset; // file position of relocation entries
|
|
||||||
uint32_t importTableOffset; // file position of import table
|
|
||||||
uint32_t importTableSize; // size of import table
|
|
||||||
uint8_t prologSection; // section in which the _prolog function is in, or 0 if
|
|
||||||
// absent
|
|
||||||
uint8_t epilogSection; // section in which the _epilog function is in, or 0 if
|
|
||||||
// absent
|
|
||||||
uint8_t unresolvedSection; // section in which the _unresolved function is in,
|
|
||||||
// or 0 if absent
|
|
||||||
uint8_t pad33;
|
|
||||||
uint32_t prologOffset; // offset of the _prolog function in its section
|
|
||||||
uint32_t epilogOffset; // offset of the _epilog function in its section
|
|
||||||
uint32_t
|
|
||||||
unresolvedOffset; // offset of the _unresolved function in its section
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RelRelocEntry {
|
|
||||||
int type; // relocation type
|
|
||||||
int patchSection; // section which relocation patch applies to
|
|
||||||
uint32_t patchOffset; // offset where this relocation patch applies
|
|
||||||
int symbolSection; // section of symbol
|
|
||||||
uint32_t symbolAddr; // for dol symbols, absolute address. for rel symbols,
|
|
||||||
// section offset
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RelImportEntry {
|
|
||||||
int moduleId; // ID of module from which the relocation symbols are imported
|
|
||||||
// from
|
|
||||||
struct RelRelocEntry* relocs; // relocation entries
|
|
||||||
int relocsCount; // number of relocation entries
|
|
||||||
size_t relocsOffset; // file offset to relocation entries
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Module {
|
|
||||||
int moduleId; // unique identifier of the module; the id of the DOL is always
|
|
||||||
// 0
|
|
||||||
const char* filename; // name of the module's ELF file
|
|
||||||
FILE* file; // ELF file
|
|
||||||
Elf32_Ehdr ehdr; // ELF header
|
|
||||||
Elf32_Sym* symtab; // ELF symbol table entries
|
|
||||||
int symtabCount; // number of ELF symbol table entries
|
|
||||||
char* strtab; // ELF string table
|
|
||||||
size_t strtabSize; // size of ELF string table
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct Module dolModule;
|
|
||||||
static struct Module relModule;
|
|
||||||
static struct RelImportEntry* imports;
|
|
||||||
static int importsCount = 0;
|
|
||||||
static int minSectionCount = 0;
|
|
||||||
static int undefinedSymError = 0;
|
|
||||||
|
|
||||||
static void fatal_error(const char* msg, ...) {
|
|
||||||
va_list args;
|
|
||||||
|
|
||||||
va_start(args, msg);
|
|
||||||
vfprintf(stderr, msg, args);
|
|
||||||
va_end(args);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swaps byte order if the system is little endian
|
|
||||||
|
|
||||||
static void bswap32(uint32_t* ptr) {
|
|
||||||
if (IS_LITTLE_ENDIAN)
|
|
||||||
*ptr = (((*ptr >> 24) & 0xFF) << 0) | (((*ptr >> 16) & 0xFF) << 8) |
|
|
||||||
(((*ptr >> 8) & 0xFF) << 16) | (((*ptr >> 0) & 0xFF) << 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bswap16(uint16_t* ptr) {
|
|
||||||
if (IS_LITTLE_ENDIAN)
|
|
||||||
*ptr = (((*ptr >> 8) & 0xFF) << 0) | (((*ptr >> 0) & 0xFF) << 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int read_checked(FILE* f, size_t offset, void* out, size_t size) {
|
|
||||||
return fseek(f, offset, SEEK_SET) == 0 && fread(out, size, 1, f) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int write_checked(FILE* f, size_t offset, const void* in, size_t size) {
|
|
||||||
return fseek(f, offset, SEEK_SET) == 0 && fwrite(in, size, 1, f) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t align(uint32_t n, unsigned int alignment) {
|
|
||||||
if (alignment == 0 || n % alignment == 0)
|
|
||||||
return n;
|
|
||||||
return n + alignment - (n % alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_supported_reloc_type(int type) {
|
|
||||||
switch (type) {
|
|
||||||
case R_PPC_ADDR32:
|
|
||||||
case R_PPC_ADDR24:
|
|
||||||
case R_PPC_ADDR16_LO:
|
|
||||||
case R_PPC_ADDR16_HA:
|
|
||||||
case R_PPC_REL24:
|
|
||||||
case R_PPC_REL14:
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* symbol_name(const struct Module* module,
|
|
||||||
const Elf32_Sym* sym) {
|
|
||||||
if (sym->st_name >= module->strtabSize)
|
|
||||||
return NULL;
|
|
||||||
return module->strtab + sym->st_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_symtab_entry(const struct Module* module, int index,
|
|
||||||
Elf32_Sym* sym) {
|
|
||||||
if (index >= module->symtabCount)
|
|
||||||
return 0;
|
|
||||||
*sym = module->symtab[index];
|
|
||||||
bswap32(&sym->st_name);
|
|
||||||
bswap32(&sym->st_value);
|
|
||||||
bswap32(&sym->st_size);
|
|
||||||
bswap16(&sym->st_shndx);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int lookup_symbol_by_name(const struct Module* module, const char* name,
|
|
||||||
Elf32_Sym* sym) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < module->symtabCount; i++) {
|
|
||||||
get_symtab_entry(module, i, sym);
|
|
||||||
const char* n = symbol_name(module, sym);
|
|
||||||
if (n != NULL && strcmp(name, n) == 0)
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct RelImportEntry* get_import_for_module_id(int moduleId) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < importsCount; i++) {
|
|
||||||
if (imports[i].moduleId == moduleId)
|
|
||||||
return &imports[i];
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_reloc_entry(const struct Module* module,
|
|
||||||
const Elf32_Rela* reloc, int relocSection) {
|
|
||||||
Elf32_Sym sym;
|
|
||||||
int symIdx = ELF32_R_SYM(reloc->r_info);
|
|
||||||
int relocType = ELF32_R_TYPE(reloc->r_info);
|
|
||||||
struct RelRelocEntry rentry;
|
|
||||||
struct RelImportEntry* import;
|
|
||||||
int moduleId; // module containing the symbol
|
|
||||||
|
|
||||||
if (!is_supported_reloc_type(relocType))
|
|
||||||
fatal_error("relocation type %i not supported\n", relocType);
|
|
||||||
|
|
||||||
rentry.patchSection = relocSection;
|
|
||||||
rentry.patchOffset = reloc->r_offset;
|
|
||||||
rentry.type = relocType;
|
|
||||||
|
|
||||||
// look for symbol in this module
|
|
||||||
if (!get_symtab_entry(module, symIdx, &sym))
|
|
||||||
fatal_error("couldn't find symbol index %i\n", symIdx);
|
|
||||||
if (sym.st_shndx != 0) // symbol is in this module
|
|
||||||
{
|
|
||||||
rentry.symbolSection = sym.st_shndx;
|
|
||||||
rentry.symbolAddr = sym.st_value + reloc->r_addend;
|
|
||||||
moduleId = module->moduleId;
|
|
||||||
} else // symbol is in another module (the DOL)
|
|
||||||
{
|
|
||||||
const char* name = symbol_name(&relModule, &sym);
|
|
||||||
if (!lookup_symbol_by_name(&dolModule, name, &sym)) {
|
|
||||||
undefinedSymError = 1;
|
|
||||||
fprintf(stderr, "could not find symbol '%s' in any module\n", name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (sym.st_shndx >= dolModule.ehdr.e_shnum)
|
|
||||||
fatal_error("bad section index %i\n", sym.st_shndx);
|
|
||||||
|
|
||||||
rentry.symbolSection = sym.st_shndx;
|
|
||||||
rentry.symbolAddr = sym.st_value + reloc->r_addend;
|
|
||||||
moduleId = dolModule.moduleId;
|
|
||||||
}
|
|
||||||
|
|
||||||
import = get_import_for_module_id(moduleId);
|
|
||||||
// add import entry if it does not exist.
|
|
||||||
if (import == NULL) {
|
|
||||||
imports = realloc(imports, (importsCount + 1) * sizeof(*imports));
|
|
||||||
import = &imports[importsCount++];
|
|
||||||
import->moduleId = moduleId;
|
|
||||||
import->relocs = NULL;
|
|
||||||
import->relocsCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add relocation entry
|
|
||||||
import->relocs = realloc(import->relocs,
|
|
||||||
(import->relocsCount + 1) * sizeof(*import->relocs));
|
|
||||||
import->relocs[import->relocsCount++] = rentry;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void module_get_section_header(const struct Module* module, int secNum,
|
|
||||||
Elf32_Shdr* shdr) {
|
|
||||||
size_t offset = module->ehdr.e_shoff + secNum * module->ehdr.e_shentsize;
|
|
||||||
|
|
||||||
if (secNum >= module->ehdr.e_shnum)
|
|
||||||
fatal_error("no such section index %i\n", secNum);
|
|
||||||
if (!read_checked(module->file, offset, shdr, sizeof(*shdr)))
|
|
||||||
fatal_error("error reading section header\n");
|
|
||||||
|
|
||||||
// convert from big endian
|
|
||||||
bswap32(&shdr->sh_name);
|
|
||||||
bswap32(&shdr->sh_type);
|
|
||||||
bswap32(&shdr->sh_flags);
|
|
||||||
bswap32(&shdr->sh_addr);
|
|
||||||
bswap32(&shdr->sh_offset);
|
|
||||||
bswap32(&shdr->sh_size);
|
|
||||||
bswap32(&shdr->sh_link);
|
|
||||||
bswap32(&shdr->sh_info);
|
|
||||||
bswap32(&shdr->sh_addralign);
|
|
||||||
bswap32(&shdr->sh_entsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void module_read_relocs(struct Module* module) {
|
|
||||||
int i;
|
|
||||||
int j;
|
|
||||||
|
|
||||||
undefinedSymError = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < (int)module->ehdr.e_shnum; i++) {
|
|
||||||
Elf32_Shdr shdr;
|
|
||||||
Elf32_Shdr forSection;
|
|
||||||
|
|
||||||
module_get_section_header(module, i, &shdr);
|
|
||||||
if (shdr.sh_type == SHT_RELA) {
|
|
||||||
module_get_section_header(module, shdr.sh_info, &forSection);
|
|
||||||
if (!(forSection.sh_flags & SHF_ALLOC))
|
|
||||||
continue;
|
|
||||||
for (j = 0; j < shdr.sh_size / sizeof(Elf32_Rela); j++) {
|
|
||||||
Elf32_Rela reloc;
|
|
||||||
|
|
||||||
read_checked(module->file, shdr.sh_offset + j * sizeof(Elf32_Rela),
|
|
||||||
&reloc, sizeof(reloc));
|
|
||||||
// convert from big endian
|
|
||||||
bswap32(&reloc.r_offset);
|
|
||||||
bswap32(&reloc.r_info);
|
|
||||||
bswap32((uint32_t*)&reloc.r_addend);
|
|
||||||
|
|
||||||
add_reloc_entry(&relModule, &reloc, shdr.sh_info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (undefinedSymError)
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int open_module(struct Module* module) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// open file
|
|
||||||
module->file = fopen(module->filename, "rb");
|
|
||||||
if (module->file == NULL)
|
|
||||||
fatal_error("could not open %s for reading: %s\n", module->filename,
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
// read and verify ELF header
|
|
||||||
if (!read_checked(module->file, 0, &module->ehdr, sizeof(module->ehdr)))
|
|
||||||
fatal_error("error reading ELF header\n");
|
|
||||||
if (memcmp(module->ehdr.e_ident,
|
|
||||||
"\x7F"
|
|
||||||
"ELF",
|
|
||||||
4) != 0)
|
|
||||||
fatal_error("%s is not a valid ELF file\n", module->filename);
|
|
||||||
|
|
||||||
// convert from big endian
|
|
||||||
bswap16(&module->ehdr.e_type);
|
|
||||||
bswap16(&module->ehdr.e_machine);
|
|
||||||
bswap32(&module->ehdr.e_version);
|
|
||||||
bswap32(&module->ehdr.e_entry);
|
|
||||||
bswap32(&module->ehdr.e_phoff);
|
|
||||||
bswap32(&module->ehdr.e_shoff);
|
|
||||||
bswap32(&module->ehdr.e_flags);
|
|
||||||
bswap16(&module->ehdr.e_ehsize);
|
|
||||||
bswap16(&module->ehdr.e_phentsize);
|
|
||||||
bswap16(&module->ehdr.e_phnum);
|
|
||||||
bswap16(&module->ehdr.e_shentsize);
|
|
||||||
bswap16(&module->ehdr.e_shnum);
|
|
||||||
bswap16(&module->ehdr.e_shstrndx);
|
|
||||||
|
|
||||||
if (module->ehdr.e_shentsize < sizeof(Elf32_Shdr))
|
|
||||||
fatal_error("invalid section header size");
|
|
||||||
|
|
||||||
// Verify architecture
|
|
||||||
if (module->ehdr.e_ident[EI_CLASS] != ELFCLASS32 ||
|
|
||||||
module->ehdr.e_ident[EI_DATA] != ELFDATA2MSB ||
|
|
||||||
module->ehdr.e_machine != EM_PPC)
|
|
||||||
fatal_error("%s: wrong architecture. expected PowerPC 32-bit big endian.\n",
|
|
||||||
module->filename);
|
|
||||||
|
|
||||||
// Read symbol table and string table
|
|
||||||
for (i = 0; i < (int)module->ehdr.e_shnum; i++) {
|
|
||||||
Elf32_Shdr shdr;
|
|
||||||
|
|
||||||
module_get_section_header(module, i, &shdr);
|
|
||||||
if (shdr.sh_type == SHT_SYMTAB && module->symtab == NULL) {
|
|
||||||
module->symtabCount = shdr.sh_size / sizeof(Elf32_Sym);
|
|
||||||
module->symtab = malloc(shdr.sh_size);
|
|
||||||
if (!read_checked(module->file, shdr.sh_offset, module->symtab,
|
|
||||||
shdr.sh_size))
|
|
||||||
fatal_error("error reading symbol table\n");
|
|
||||||
} else if (shdr.sh_type == SHT_STRTAB && i != module->ehdr.e_shstrndx &&
|
|
||||||
module->strtab == NULL) {
|
|
||||||
module->strtabSize = shdr.sh_size;
|
|
||||||
module->strtab = malloc(shdr.sh_size);
|
|
||||||
if (!read_checked(module->file, shdr.sh_offset, module->strtab,
|
|
||||||
shdr.sh_size))
|
|
||||||
fatal_error("error reading string table\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (module->symtab == NULL)
|
|
||||||
fatal_error("%s does not have a symbol table.\n", module->filename);
|
|
||||||
if (module->strtab == NULL)
|
|
||||||
fatal_error("%s does not have a string table.\n", module->filename);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// searches for the special functions "_prolog", "_epliog", and "_unresolved"
|
|
||||||
static void find_rel_entry_functions(const struct Module* module,
|
|
||||||
struct RelHeader* relHdr) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// puts("finding entry points");
|
|
||||||
for (i = 0; i < module->symtabCount; i++) {
|
|
||||||
Elf32_Sym sym;
|
|
||||||
Elf32_Shdr shdr;
|
|
||||||
const char* name;
|
|
||||||
|
|
||||||
get_symtab_entry(module, i, &sym);
|
|
||||||
name = symbol_name(module, &sym);
|
|
||||||
if (name == NULL)
|
|
||||||
continue;
|
|
||||||
if (strcmp(name, "_prolog") == 0) {
|
|
||||||
module_get_section_header(module, sym.st_shndx, &shdr);
|
|
||||||
relHdr->prologSection = sym.st_shndx;
|
|
||||||
relHdr->prologOffset = sym.st_value - shdr.sh_addr;
|
|
||||||
} else if (strcmp(name, "_epilog") == 0) {
|
|
||||||
module_get_section_header(module, sym.st_shndx, &shdr);
|
|
||||||
relHdr->epilogSection = sym.st_shndx;
|
|
||||||
relHdr->epilogOffset = sym.st_value - shdr.sh_addr;
|
|
||||||
} else if (strcmp(name, "_unresolved") == 0) {
|
|
||||||
module_get_section_header(module, sym.st_shndx, &shdr);
|
|
||||||
relHdr->unresolvedSection = sym.st_shndx;
|
|
||||||
relHdr->unresolvedOffset = sym.st_value - shdr.sh_addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// patches the bl instruction at insnp to jump to offset
|
|
||||||
static void patch_rel24_branch_offset(uint8_t* insnp, int32_t branchOffset) {
|
|
||||||
const uint32_t offsetMask =
|
|
||||||
0x03FFFFFC; // bits of instruction that contain the offset
|
|
||||||
uint32_t insn = (insnp[0] << 24) // read instruction
|
|
||||||
| (insnp[1] << 16) | (insnp[2] << 8) | (insnp[3] << 0);
|
|
||||||
|
|
||||||
assert(((insn >> 26) & 0x3F) ==
|
|
||||||
18); // TODO: do other instructions besides bl use R_PPC_REL24?
|
|
||||||
insn =
|
|
||||||
(insn & ~offsetMask) | (branchOffset & offsetMask); // patch instruction
|
|
||||||
// write instruction
|
|
||||||
insnp[0] = (insn >> 24) & 0xFF;
|
|
||||||
insnp[1] = (insn >> 16) & 0xFF;
|
|
||||||
insnp[2] = (insn >> 8) & 0xFF;
|
|
||||||
insnp[3] = (insn >> 0) & 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void patch_code_relocs(struct RelHeader* relHdr, int sectionId,
|
|
||||||
uint8_t* code, size_t size) {
|
|
||||||
struct RelImportEntry* import;
|
|
||||||
struct RelRelocEntry* reloc;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// Remove redundant R_PPC_REL24 relocations for calls to functions within
|
|
||||||
// the same module
|
|
||||||
import = get_import_for_module_id(relModule.moduleId);
|
|
||||||
assert(import != NULL);
|
|
||||||
for (i = 0, reloc = &import->relocs[0]; i < import->relocsCount;
|
|
||||||
i++, reloc++) {
|
|
||||||
if (reloc->patchSection == sectionId && reloc->type == R_PPC_REL24) {
|
|
||||||
assert(reloc->patchOffset < size);
|
|
||||||
patch_rel24_branch_offset(code + reloc->patchOffset,
|
|
||||||
reloc->symbolAddr - reloc->patchOffset);
|
|
||||||
// remove the relocation
|
|
||||||
reloc->type = R_PPC_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Patch all calls to functions outside this module to jump to _unresolved
|
|
||||||
// by default.
|
|
||||||
if (relHdr->unresolvedSection == 0)
|
|
||||||
return;
|
|
||||||
import = get_import_for_module_id(0);
|
|
||||||
assert(import != NULL);
|
|
||||||
for (i = 0, reloc = &import->relocs[0]; i < import->relocsCount;
|
|
||||||
i++, reloc++) {
|
|
||||||
if (reloc->patchSection == sectionId && reloc->type == R_PPC_REL24) {
|
|
||||||
assert(reloc->patchOffset < size);
|
|
||||||
patch_rel24_branch_offset(code + reloc->patchOffset,
|
|
||||||
relHdr->unresolvedOffset - reloc->patchOffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int compare_relocs(const void* a, const void* b) {
|
|
||||||
const struct RelRelocEntry* relocA = a;
|
|
||||||
const struct RelRelocEntry* relocB = b;
|
|
||||||
|
|
||||||
// Sort by sections to which these relocations apply
|
|
||||||
if (relocA->patchSection != relocB->patchSection)
|
|
||||||
return relocA->patchSection - relocB->patchSection;
|
|
||||||
// Sort by patch offset
|
|
||||||
if (relocA->patchOffset != relocB->patchOffset)
|
|
||||||
return relocA->patchOffset - relocB->patchOffset;
|
|
||||||
// Otherwise, leave the order alone
|
|
||||||
return (uintptr_t)a - (uintptr_t)b;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int compare_imports(const void* a, const void* b) {
|
|
||||||
const struct RelImportEntry* impA = a;
|
|
||||||
const struct RelImportEntry* impB = b;
|
|
||||||
|
|
||||||
return impA->moduleId - impB->moduleId;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void write_rel_file(struct Module* module, struct RelHeader* relHdr,
|
|
||||||
const char* filename) {
|
|
||||||
int i, j;
|
|
||||||
size_t filePos = sizeof(struct RelHeader); // skip over header for now
|
|
||||||
struct RelImportEntry* imp;
|
|
||||||
FILE* fout = fopen(filename, "wb");
|
|
||||||
|
|
||||||
if (fout == NULL)
|
|
||||||
fatal_error("could not open %s for writing: %s\n", filename,
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
relHdr->moduleId = module->moduleId;
|
|
||||||
relHdr->formatVersion = 1;
|
|
||||||
|
|
||||||
find_rel_entry_functions(module, relHdr);
|
|
||||||
|
|
||||||
// 1. Write section table and section contents
|
|
||||||
|
|
||||||
relHdr->sectionTableOffset = filePos;
|
|
||||||
relHdr->sectionCount = MAX(module->ehdr.e_shnum, minSectionCount);
|
|
||||||
// section contents follow section info table
|
|
||||||
filePos = relHdr->sectionTableOffset + relHdr->sectionCount * 8;
|
|
||||||
relHdr->bssSize = 0;
|
|
||||||
for (i = 0; i < module->ehdr.e_shnum; i++) {
|
|
||||||
Elf32_Shdr shdr;
|
|
||||||
struct {
|
|
||||||
uint32_t offset;
|
|
||||||
uint32_t size;
|
|
||||||
} secEntry = {0};
|
|
||||||
|
|
||||||
module_get_section_header(module, i, &shdr);
|
|
||||||
// write section contents
|
|
||||||
if (shdr.sh_type == SHT_PROGBITS && (shdr.sh_flags & SHF_ALLOC)) {
|
|
||||||
size_t sizeAligned = align(shdr.sh_size, 4);
|
|
||||||
uint32_t execflg = (shdr.sh_flags & SHF_EXECINSTR) ? 1 : 0;
|
|
||||||
|
|
||||||
filePos = align(filePos, shdr.sh_addralign);
|
|
||||||
if (shdr.sh_size > 0) {
|
|
||||||
uint8_t* data = calloc(sizeAligned, 1);
|
|
||||||
|
|
||||||
if (!read_checked(module->file, shdr.sh_offset, data, shdr.sh_size))
|
|
||||||
fatal_error("error reading section\n");
|
|
||||||
if (execflg)
|
|
||||||
patch_code_relocs(relHdr, i, data, sizeAligned);
|
|
||||||
if (!write_checked(fout, filePos, data, shdr.sh_size))
|
|
||||||
fatal_error("error writing rel section\n");
|
|
||||||
free(data);
|
|
||||||
}
|
|
||||||
secEntry.offset = filePos | execflg;
|
|
||||||
filePos += shdr.sh_size;
|
|
||||||
}
|
|
||||||
if (shdr.sh_flags & SHF_ALLOC)
|
|
||||||
secEntry.size = shdr.sh_size;
|
|
||||||
|
|
||||||
// write section table entry
|
|
||||||
bswap32(&secEntry.offset);
|
|
||||||
bswap32(&secEntry.size);
|
|
||||||
write_checked(fout, relHdr->sectionTableOffset + i * 8, &secEntry,
|
|
||||||
sizeof(secEntry));
|
|
||||||
|
|
||||||
// calculate total BSS size
|
|
||||||
if ((shdr.sh_flags & SHF_ALLOC) && shdr.sh_type == SHT_NOBITS)
|
|
||||||
relHdr->bssSize += shdr.sh_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Write relocation data
|
|
||||||
|
|
||||||
relHdr->relocationTableOffset = filePos;
|
|
||||||
// sort imports by module id
|
|
||||||
qsort(imports, importsCount, sizeof(struct RelImportEntry), compare_imports);
|
|
||||||
for (i = 0, imp = &imports[0]; i < importsCount; i++, imp++) {
|
|
||||||
struct RelRelocEntry* reloc;
|
|
||||||
int currSection = -1;
|
|
||||||
uint32_t prevOffset = 0;
|
|
||||||
struct {
|
|
||||||
uint16_t offset;
|
|
||||||
uint8_t type;
|
|
||||||
uint8_t section;
|
|
||||||
uint32_t symaddr;
|
|
||||||
} ent;
|
|
||||||
|
|
||||||
imp->relocsOffset = filePos;
|
|
||||||
// sort relocation entries by section
|
|
||||||
qsort(imp->relocs, imp->relocsCount, sizeof(struct RelRelocEntry),
|
|
||||||
compare_relocs);
|
|
||||||
for (j = 0, reloc = &imp->relocs[0]; j < imp->relocsCount; j++, reloc++) {
|
|
||||||
if (reloc->type == R_PPC_NONE) // ignore null relocations
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (reloc->patchSection != currSection) // section changed
|
|
||||||
{
|
|
||||||
currSection = reloc->patchSection;
|
|
||||||
|
|
||||||
// write section change
|
|
||||||
ent.offset = 0;
|
|
||||||
ent.type = R_DOLPHIN_SECTION;
|
|
||||||
ent.section = reloc->patchSection;
|
|
||||||
ent.symaddr = 0;
|
|
||||||
bswap16(&ent.offset);
|
|
||||||
bswap32(&ent.symaddr);
|
|
||||||
if (!write_checked(fout, filePos, &ent, sizeof(ent)))
|
|
||||||
fatal_error("error writing relocation entry");
|
|
||||||
|
|
||||||
filePos += sizeof(ent);
|
|
||||||
prevOffset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write relocation
|
|
||||||
assert(reloc->patchOffset >= prevOffset);
|
|
||||||
ent.offset = reloc->patchOffset - prevOffset;
|
|
||||||
ent.type = reloc->type;
|
|
||||||
ent.section = reloc->symbolSection;
|
|
||||||
ent.symaddr = reloc->symbolAddr;
|
|
||||||
bswap16(&ent.offset);
|
|
||||||
bswap32(&ent.symaddr);
|
|
||||||
if (!write_checked(fout, filePos, &ent, sizeof(ent)))
|
|
||||||
fatal_error("error writing relocation entry");
|
|
||||||
|
|
||||||
filePos += sizeof(ent);
|
|
||||||
prevOffset = reloc->patchOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write end
|
|
||||||
ent.offset = 0;
|
|
||||||
ent.type = R_DOLPHIN_END;
|
|
||||||
ent.section = 0;
|
|
||||||
ent.symaddr = 0;
|
|
||||||
bswap16(&ent.offset);
|
|
||||||
bswap32(&ent.symaddr);
|
|
||||||
if (!write_checked(fout, filePos, &ent, sizeof(ent)))
|
|
||||||
fatal_error("error writing relocation entry");
|
|
||||||
|
|
||||||
filePos += sizeof(ent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Write module import table
|
|
||||||
|
|
||||||
relHdr->importTableOffset = filePos;
|
|
||||||
for (i = 0, imp = &imports[0]; i < importsCount; i++, imp++) {
|
|
||||||
// write import table entry
|
|
||||||
struct {
|
|
||||||
uint32_t moduleId;
|
|
||||||
uint32_t relocsOffset;
|
|
||||||
} ent;
|
|
||||||
ent.moduleId = imp->moduleId;
|
|
||||||
ent.relocsOffset = imp->relocsOffset;
|
|
||||||
bswap32(&ent.moduleId);
|
|
||||||
bswap32(&ent.relocsOffset);
|
|
||||||
write_checked(fout, relHdr->importTableOffset + i * 8, &ent, sizeof(ent));
|
|
||||||
}
|
|
||||||
relHdr->importTableSize = importsCount * 8;
|
|
||||||
|
|
||||||
// 4. Write REL header.
|
|
||||||
|
|
||||||
// convert to big endian
|
|
||||||
bswap32(&relHdr->moduleId);
|
|
||||||
bswap32(&relHdr->sectionCount);
|
|
||||||
bswap32(&relHdr->sectionTableOffset);
|
|
||||||
bswap32(&relHdr->moduleNameOffset);
|
|
||||||
bswap32(&relHdr->moduleNameSize);
|
|
||||||
bswap32(&relHdr->formatVersion);
|
|
||||||
bswap32(&relHdr->bssSize);
|
|
||||||
bswap32(&relHdr->relocationTableOffset);
|
|
||||||
bswap32(&relHdr->importTableOffset);
|
|
||||||
bswap32(&relHdr->importTableSize);
|
|
||||||
bswap32(&relHdr->prologOffset);
|
|
||||||
bswap32(&relHdr->epilogOffset);
|
|
||||||
bswap32(&relHdr->unresolvedOffset);
|
|
||||||
write_checked(fout, 0, relHdr, sizeof(*relHdr));
|
|
||||||
|
|
||||||
fclose(fout);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int parse_number(const char* str, int* n) {
|
|
||||||
char* end;
|
|
||||||
*n = strtol(str, &end, 0);
|
|
||||||
return end > str && *end == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
int i;
|
|
||||||
int moduleId = -1;
|
|
||||||
int nameOffset = 0;
|
|
||||||
int nameLen = 0;
|
|
||||||
const char* relName = NULL;
|
|
||||||
struct RelHeader relHdr = {0};
|
|
||||||
|
|
||||||
// Read command-line args
|
|
||||||
for (i = 1; i < argc; i++) {
|
|
||||||
if (strcmp(argv[i], "-c") == 0 ||
|
|
||||||
strcmp(argv[i], "--pad-section-count") == 0) {
|
|
||||||
if (i + 1 < argc && parse_number(argv[i + 1], &minSectionCount))
|
|
||||||
i++;
|
|
||||||
else
|
|
||||||
goto usage;
|
|
||||||
} else if (strcmp(argv[i], "-i") == 0 ||
|
|
||||||
strcmp(argv[i], "--module-id") == 0) {
|
|
||||||
if (i + 1 < argc && parse_number(argv[i + 1], &moduleId))
|
|
||||||
i++;
|
|
||||||
else
|
|
||||||
goto usage;
|
|
||||||
} else if (strcmp(argv[i], "-o") == 0 ||
|
|
||||||
strcmp(argv[i], "--name-offset") == 0) {
|
|
||||||
if (i + 1 < argc && parse_number(argv[i + 1], &nameOffset))
|
|
||||||
i++;
|
|
||||||
else
|
|
||||||
goto usage;
|
|
||||||
} else if (strcmp(argv[i], "-l") == 0 ||
|
|
||||||
strcmp(argv[i], "--name-length") == 0) {
|
|
||||||
if (i + 1 < argc && parse_number(argv[i + 1], &nameLen))
|
|
||||||
i++;
|
|
||||||
else
|
|
||||||
goto usage;
|
|
||||||
} else {
|
|
||||||
if (relModule.filename == NULL)
|
|
||||||
relModule.filename = argv[i];
|
|
||||||
else if (dolModule.filename == NULL)
|
|
||||||
dolModule.filename = argv[i];
|
|
||||||
else if (relName == NULL)
|
|
||||||
relName = argv[i];
|
|
||||||
else
|
|
||||||
goto usage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (relModule.filename == NULL || dolModule.filename == NULL ||
|
|
||||||
relName == NULL)
|
|
||||||
goto usage;
|
|
||||||
|
|
||||||
open_module(&relModule);
|
|
||||||
open_module(&dolModule);
|
|
||||||
|
|
||||||
dolModule.moduleId = 0;
|
|
||||||
relModule.moduleId = moduleId;
|
|
||||||
|
|
||||||
module_read_relocs(&relModule);
|
|
||||||
|
|
||||||
// TODO: Read this information from string table
|
|
||||||
relHdr.moduleNameOffset = nameOffset;
|
|
||||||
relHdr.moduleNameSize = nameLen;
|
|
||||||
write_rel_file(&relModule, &relHdr, relName);
|
|
||||||
|
|
||||||
fclose(relModule.file);
|
|
||||||
fclose(dolModule.file);
|
|
||||||
|
|
||||||
free(dolModule.strtab);
|
|
||||||
free(dolModule.symtab);
|
|
||||||
for (i = 0; i < importsCount; i++)
|
|
||||||
free(imports[i].relocs);
|
|
||||||
free(imports);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
usage:
|
|
||||||
fprintf(stderr,
|
|
||||||
"usage: %s reloc_elf static_elf rel_file\n"
|
|
||||||
"options:\n"
|
|
||||||
" -i, --module-id <n> sets the module ID in the rel header "
|
|
||||||
"to <n>\n"
|
|
||||||
" -c, --pad-section-count <n> ensures that the rel will have at "
|
|
||||||
"least <n>\n"
|
|
||||||
" sections\n"
|
|
||||||
" -o, --name-offset <offset> sets the name offset in the rel "
|
|
||||||
"header to\n"
|
|
||||||
" <offset>\n"
|
|
||||||
" -l, --name-length <len> sets the name length in the rel "
|
|
||||||
"header to <len>\n",
|
|
||||||
argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
#! /usr/bin/env python3
|
|
||||||
|
|
||||||
# Written by Ethan Roseman (ethteck)
|
|
||||||
# MIT License
|
|
||||||
# Copyright 2021
|
|
||||||
|
|
||||||
# Modified by EpochFlame
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
# Byte sequence that marks code size
|
|
||||||
CODESIZE_MAGIC = b"\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x34"
|
|
||||||
BLR_BYTE_SEQ = b"\x4E\x80\x00\x20"
|
|
||||||
MTLR_BYTE_SEQ = b"\x7C\x08\x03\xA6"
|
|
||||||
|
|
||||||
# Byte sequence array for branches to link register
|
|
||||||
BLR_BYTE_SEQ_ARRAY = [BLR_BYTE_SEQ,
|
|
||||||
b"\x4D\x80\x00\x20", b"\x4D\x80\x00\x21", b"\x4C\x81\x00\x20", b"\x4C\x81\x00\x21",
|
|
||||||
b"\x4D\x82\x00\x20", b"\x4D\x82\x00\x21", b"\x4C\x80\x00\x20", b"\x4C\x80\x00\x21",
|
|
||||||
b"\x4D\x81\x00\x20", b"\x4D\x81\x00\x21", b"\x4C\x80\x00\x20", b"\x4C\x80\x00\x21",
|
|
||||||
b"\x4C\x82\x00\x20", b"\x4C\x82\x00\x21", b"\x4C\x81\x00\x20", b"\x4C\x81\x00\x21",
|
|
||||||
b"\x4D\x83\x00\x20", b"\x4D\x83\x00\x21", b"\x4C\x83\x00\x20", b"\x4C\x83\x00\x21",
|
|
||||||
b"\x4D\x83\x00\x20", b"\x4D\x83\x00\x21", b"\x4C\x83\x00\x20", b"\x4C\x83\x00\x21"]
|
|
||||||
|
|
||||||
# Example invocation: ./frank.py vanilla.o profile.o output.o
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("vanilla", help="Path to the vanilla object", type=argparse.FileType('rb'))
|
|
||||||
parser.add_argument("profile", help="Path to the profile object", type=argparse.FileType('rb'))
|
|
||||||
parser.add_argument("target", help="Path to the target object (to write)")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
# Read contents into bytearrays and close files
|
|
||||||
vanilla_bytes = args.vanilla.read()
|
|
||||||
args.vanilla.close()
|
|
||||||
profile_bytes = args.profile.read()
|
|
||||||
args.profile.close()
|
|
||||||
|
|
||||||
# Remove byte sequence
|
|
||||||
stripped_bytes = profile_bytes.replace(b"\x48\x00\x00\x01\x60\x00\x00\x00", b"")
|
|
||||||
|
|
||||||
# Find end of code sections in vanilla and stripped bytes
|
|
||||||
code_size_offset = (vanilla_bytes.find(CODESIZE_MAGIC) + 12)
|
|
||||||
code_size_bytes = vanilla_bytes[code_size_offset:code_size_offset+4]
|
|
||||||
code_size = int.from_bytes(code_size_bytes, byteorder='big')
|
|
||||||
|
|
||||||
eoc_offset = 0x34 + code_size
|
|
||||||
|
|
||||||
# Break if the eoc is not found
|
|
||||||
assert(eoc_offset != len(vanilla_bytes))
|
|
||||||
|
|
||||||
# Replace 0x34 - eoc in vanilla with bytes from stripped
|
|
||||||
final_bytes = vanilla_bytes[:0x34] + stripped_bytes[0x34:eoc_offset] + vanilla_bytes[eoc_offset:]
|
|
||||||
|
|
||||||
# Fix branches to link register
|
|
||||||
for seq in BLR_BYTE_SEQ_ARRAY:
|
|
||||||
idx = 0
|
|
||||||
|
|
||||||
while idx < len(vanilla_bytes):
|
|
||||||
found_pos = vanilla_bytes.find(seq, idx)
|
|
||||||
if found_pos == -1:
|
|
||||||
break # break while loop when no targets remain
|
|
||||||
if found_pos % 4 != 0: # check 4-byte alignment
|
|
||||||
idx += 4
|
|
||||||
continue
|
|
||||||
final_bytes = final_bytes[:found_pos] + vanilla_bytes[found_pos:found_pos+4] + final_bytes[found_pos+4:]
|
|
||||||
idx = found_pos + len(seq)
|
|
||||||
|
|
||||||
# Reunify mtlr/blr instructions, shifting intermediary instructions up
|
|
||||||
idx = 0
|
|
||||||
|
|
||||||
while idx < len(final_bytes):
|
|
||||||
# Find mtlr position
|
|
||||||
mtlr_found_pos = final_bytes.find(MTLR_BYTE_SEQ, idx)
|
|
||||||
if mtlr_found_pos == -1:
|
|
||||||
break # break while loop when no targets remain
|
|
||||||
if mtlr_found_pos % 4 != 0: # check 4-byte alignment
|
|
||||||
idx += 4
|
|
||||||
continue
|
|
||||||
# Find paired blr position
|
|
||||||
blr_found_pos = final_bytes.find(BLR_BYTE_SEQ, mtlr_found_pos)
|
|
||||||
if blr_found_pos == -1:
|
|
||||||
break # break while loop when no targets remain
|
|
||||||
if blr_found_pos % 4 != 0: # check 4-byte alignment
|
|
||||||
idx += 4
|
|
||||||
continue
|
|
||||||
if mtlr_found_pos + 4 == blr_found_pos:
|
|
||||||
idx += 4
|
|
||||||
continue # continue if mtlr is followed directly by blr
|
|
||||||
|
|
||||||
final_bytes = final_bytes[:mtlr_found_pos] + final_bytes[mtlr_found_pos+4:blr_found_pos] + final_bytes[mtlr_found_pos:mtlr_found_pos+4] + final_bytes[blr_found_pos:]
|
|
||||||
idx = mtlr_found_pos + len(MTLR_BYTE_SEQ)
|
|
||||||
|
|
||||||
with open(args.target, "wb") as f:
|
|
||||||
f.write(final_bytes)
|
|
|
@ -1,72 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# MIT License
|
|
||||||
# See https://github.com/ethteck/m2ctx/blob/main/m2ctx.py
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
root_dir = os.path.abspath(os.path.join(script_dir, ".."))
|
|
||||||
src_dir = root_dir + "src/"
|
|
||||||
|
|
||||||
CPP_FLAGS = [
|
|
||||||
"-Iinclude",
|
|
||||||
"-Isrc",
|
|
||||||
"-DGEKKO",
|
|
||||||
"-DHW2",
|
|
||||||
"-D__attribute__(...)=",
|
|
||||||
"-D__asm__(...)=",
|
|
||||||
"-ffreestanding",
|
|
||||||
"-DM2CTX",
|
|
||||||
]
|
|
||||||
|
|
||||||
def import_c_file(in_file) -> str:
|
|
||||||
in_file = os.path.relpath(in_file, root_dir)
|
|
||||||
cpp_command = ["gcc", "-E", "-P", "-dM", *CPP_FLAGS, in_file]
|
|
||||||
cpp_command2 = ["gcc", "-E", "-P", *CPP_FLAGS, in_file]
|
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(suffix=".c") as tmp:
|
|
||||||
stock_macros = subprocess.check_output(["gcc", "-E", "-P", "-dM", tmp.name], cwd=root_dir, encoding="utf-8")
|
|
||||||
|
|
||||||
out_text = ""
|
|
||||||
try:
|
|
||||||
out_text += subprocess.check_output(cpp_command, cwd=root_dir, encoding="utf-8")
|
|
||||||
out_text += subprocess.check_output(cpp_command2, cwd=root_dir, encoding="utf-8")
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
print(
|
|
||||||
"Failed to preprocess input file, when running command:\n"
|
|
||||||
+ cpp_command,
|
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if not out_text:
|
|
||||||
print("Output is empty - aborting")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
for line in stock_macros.strip().splitlines():
|
|
||||||
out_text = out_text.replace(line + "\n", "")
|
|
||||||
return out_text
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="""Create a context file which can be used for mips_to_c"""
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"c_file",
|
|
||||||
help="""File from which to create context""",
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
output = import_c_file(args.c_file)
|
|
||||||
|
|
||||||
with open(os.path.join(root_dir, "ctx.c"), "w", encoding="UTF-8") as f:
|
|
||||||
f.write(output)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,496 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
from capstone import *
|
|
||||||
from capstone.ppc import *
|
|
||||||
from elftools.elf.elffile import *
|
|
||||||
from elftools.elf.sections import *
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# addr -> name
|
|
||||||
labels = {}
|
|
||||||
|
|
||||||
# fileOffset -> {addr, type}
|
|
||||||
relocations = {}
|
|
||||||
|
|
||||||
# index -> {offset, flags, length, is_bss, name}
|
|
||||||
sectionInfo = []
|
|
||||||
|
|
||||||
R_PPC_NONE = 0
|
|
||||||
R_PPC_ADDR32 = 1
|
|
||||||
R_PPC_ADDR24 = 2
|
|
||||||
R_PPC_ADDR16_LO = 4
|
|
||||||
R_PPC_ADDR16_HI = 5
|
|
||||||
R_PPC_ADDR16_HA = 6
|
|
||||||
R_PPC_REL24 = 10
|
|
||||||
R_PPC_REL14 = 11
|
|
||||||
R_DOLPHIN_SECTION = 202
|
|
||||||
R_DOLPHIN_END = 203
|
|
||||||
|
|
||||||
relocationTypeNames = {
|
|
||||||
R_PPC_NONE: 'R_PPC_NONE',
|
|
||||||
R_PPC_ADDR32: 'R_PPC_ADDR32',
|
|
||||||
R_PPC_ADDR24: 'R_PPC_ADDR24',
|
|
||||||
R_PPC_ADDR16_LO: 'R_PPC_ADDR16_LO',
|
|
||||||
R_PPC_ADDR16_HI: 'R_PPC_ADDR16_HI',
|
|
||||||
R_PPC_ADDR16_HA: 'R_PPC_ADDR16_HA',
|
|
||||||
R_PPC_REL24: 'R_PPC_REL24',
|
|
||||||
R_PPC_REL14: 'R_PPC_REL14',
|
|
||||||
R_DOLPHIN_SECTION: 'R_DOLPHIN_SECTION',
|
|
||||||
R_DOLPHIN_END: 'R_DOLPHIN_END'
|
|
||||||
}
|
|
||||||
|
|
||||||
def read_u8(offset):
|
|
||||||
return filecontent[offset]
|
|
||||||
|
|
||||||
def read_u16(offset):
|
|
||||||
return (filecontent[offset + 0] << 8) | filecontent[offset + 1]
|
|
||||||
|
|
||||||
def read_u32(offset):
|
|
||||||
return (filecontent[offset + 0] << 24) | (filecontent[offset + 1] << 16) | (filecontent[offset + 2] << 8) | filecontent[offset + 3]
|
|
||||||
|
|
||||||
def add_label(addr, name=None):
|
|
||||||
if addr in labels:
|
|
||||||
return labels[addr]
|
|
||||||
if name == None:
|
|
||||||
name = 'lbl_%08X' % addr
|
|
||||||
labels[addr] = name
|
|
||||||
return name
|
|
||||||
|
|
||||||
with open(sys.argv[1], 'rb') as file:
|
|
||||||
filecontent = bytearray(file.read())
|
|
||||||
|
|
||||||
if len(sys.argv) >= 3:
|
|
||||||
# Why is this so slow?
|
|
||||||
with open(sys.argv[2], 'rb') as f:
|
|
||||||
elf = ELFFile(f)
|
|
||||||
elfsymtab = elf.get_section_by_name('.symtab')
|
|
||||||
for i in range(0, elfsymtab.num_symbols()):
|
|
||||||
sym = elfsymtab.get_symbol(i)
|
|
||||||
if len(sym.name) > 0 and not sym.name[0] in {'.', '@'}:
|
|
||||||
add_label(sym['st_value'], sym.name)
|
|
||||||
|
|
||||||
id = read_u32(0)
|
|
||||||
numSections = read_u32(0x0C)
|
|
||||||
sectionInfoOffset = read_u32(0x10)
|
|
||||||
nameOffset = read_u32(0x14)
|
|
||||||
nameSize = read_u32(0x18)
|
|
||||||
version = read_u32(0x1C)
|
|
||||||
bssSize = read_u32(0x20)
|
|
||||||
relOffset = read_u32(0x24)
|
|
||||||
impOffset = read_u32(0x28)
|
|
||||||
impSize = read_u32(0x2C)
|
|
||||||
prologSection = read_u8(0x30)
|
|
||||||
epilogSection = read_u8(0x31)
|
|
||||||
unresolvedSection = read_u8(0x32)
|
|
||||||
prolog = read_u32(0x34)
|
|
||||||
epilog = read_u32(0x38)
|
|
||||||
unresolved = read_u32(0x3C)
|
|
||||||
|
|
||||||
print("# id: %i" % id)
|
|
||||||
print("# version: %i" % version)
|
|
||||||
print("# nameoffset: 0x%X, size: 0x%X" % (nameOffset, nameSize))
|
|
||||||
print("# section table: 0x%X, size: 0x%X" % (sectionInfoOffset, numSections*8))
|
|
||||||
print("# imp table: 0x%X" % impOffset)
|
|
||||||
print("# relocs offset: 0x%X" % relOffset)
|
|
||||||
print("# _prolog: %i:0x%X" % (prologSection, prolog))
|
|
||||||
print("# _epilog: %i:0x%X" % (epilogSection, epilog))
|
|
||||||
print("# _unresolved: %i:0x%X" % (unresolvedSection, unresolved))
|
|
||||||
print("# num sections: %i" % numSections)
|
|
||||||
print('.include "macros.inc"')
|
|
||||||
|
|
||||||
#print("%i sections:" % numSections)
|
|
||||||
# Read sections
|
|
||||||
for i in range(0, numSections):
|
|
||||||
o = sectionInfoOffset + i * 8
|
|
||||||
section = {
|
|
||||||
'offset': read_u32(o + 0) & ~3,
|
|
||||||
'flags': read_u32(o + 0) & 3,
|
|
||||||
'length': read_u32(o + 4)
|
|
||||||
}
|
|
||||||
if section['offset'] == 0 and section['length'] > 0:
|
|
||||||
section['is_bss'] = True
|
|
||||||
else:
|
|
||||||
section['is_bss'] = False
|
|
||||||
# Hack: if bss, then set file offset to something unique as to not
|
|
||||||
# clash with other symbols
|
|
||||||
if section['is_bss']:
|
|
||||||
section['offset'] = 0x10000000
|
|
||||||
# Determine name
|
|
||||||
if section['is_bss']:
|
|
||||||
section['name'] = '.bss%i' % i
|
|
||||||
elif section['flags'] & 1:
|
|
||||||
section['name'] = '.text%i' % i
|
|
||||||
else:
|
|
||||||
section['name'] = '.data%i' % i
|
|
||||||
sectionInfo.append(section)
|
|
||||||
print("# offset: 0x%08X\tlength: 0x%08X\tflags: %i" %
|
|
||||||
(section['offset'], section['length'], section['flags']))
|
|
||||||
|
|
||||||
|
|
||||||
sectionInfo[1]['name'] = '.text'
|
|
||||||
sectionInfo[2]['name'] = '.ctors'
|
|
||||||
sectionInfo[3]['name'] = '.dtors'
|
|
||||||
sectionInfo[4]['name'] = '.rodata'
|
|
||||||
sectionInfo[5]['name'] = '.data'
|
|
||||||
sectionInfo[6]['name'] = '.bss'
|
|
||||||
|
|
||||||
# Add labels for prologue and epilogue
|
|
||||||
if prologSection != 0:
|
|
||||||
labels[sectionInfo[prologSection]['offset'] + prolog] = '_prolog'
|
|
||||||
if epilogSection != 0:
|
|
||||||
labels[sectionInfo[epilogSection]['offset'] + epilog] = '_epilog'
|
|
||||||
if unresolvedSection != 0:
|
|
||||||
labels[sectionInfo[unresolvedSection]['offset'] + unresolved] = '_unresolved'
|
|
||||||
|
|
||||||
def read_relocation_info(module, o):
|
|
||||||
currSection = None
|
|
||||||
missingSymbols = False
|
|
||||||
while True:
|
|
||||||
offset = read_u16(o + 0)
|
|
||||||
type = read_u8(o + 2)
|
|
||||||
section = read_u8(o + 3)
|
|
||||||
addend = read_u32(o + 4)
|
|
||||||
|
|
||||||
# Get address of symbol and add label
|
|
||||||
symAddr = 0
|
|
||||||
if type == R_DOLPHIN_SECTION: # R_DOLPHIN_SECTION
|
|
||||||
currSection = sectionInfo[section]
|
|
||||||
relocOffset = currSection['offset']
|
|
||||||
if type < 200:
|
|
||||||
if module == 0: # dol
|
|
||||||
symAddr = addend
|
|
||||||
if symAddr not in labels:
|
|
||||||
print('error: symbol for 0x%08X not found' % symAddr)
|
|
||||||
missingSymbols = True
|
|
||||||
else: # rel
|
|
||||||
symAddr = sectionInfo[section]['offset'] + addend
|
|
||||||
labels[symAddr] = 'lbl_%08X' % symAddr
|
|
||||||
|
|
||||||
# Get file offset for relocation
|
|
||||||
relocOffset += offset
|
|
||||||
|
|
||||||
if type < 200:
|
|
||||||
reloc = {
|
|
||||||
'addr': symAddr,
|
|
||||||
'type': type,
|
|
||||||
}
|
|
||||||
relocations[relocOffset] = reloc
|
|
||||||
|
|
||||||
#print(" offset: 0x%04X(+0x%X)\ttype: %s\tsection: %i\tsym_addr: 0x%08X" % (relocOffset, offset, relocationTypeNames[type], section, symAddr))
|
|
||||||
#print(" offset: 0x%04X(+0x%X)\ttype: %s\tsection: %i\tsym_addr: ?" % (relocOffset, offset, relocationTypeNames[type], section))
|
|
||||||
if type == R_DOLPHIN_END:
|
|
||||||
break
|
|
||||||
o += 8
|
|
||||||
if missingSymbols:
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
numImpEntries = impSize / 8
|
|
||||||
#print("%i imports" % numImpEntries)
|
|
||||||
for i in range(0, int(numImpEntries)):
|
|
||||||
o = impOffset + i * 8
|
|
||||||
module = read_u32(o + 0)
|
|
||||||
relocation = read_u32(o + 4)
|
|
||||||
#print("module: %i, offset: 0x%08X" % (module, relocation))
|
|
||||||
read_relocation_info(module, relocation)
|
|
||||||
|
|
||||||
|
|
||||||
cs = Cs(CS_ARCH_PPC, CS_MODE_32 | CS_MODE_BIG_ENDIAN)
|
|
||||||
cs.detail = True
|
|
||||||
cs.imm_unsigned = False
|
|
||||||
|
|
||||||
def get_relocation_for_offset(o):
|
|
||||||
for i in range(o, o + 4):
|
|
||||||
if i in relocations:
|
|
||||||
return relocations[i]
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_label(addr):
|
|
||||||
if addr in labels:
|
|
||||||
return labels[addr]
|
|
||||||
return '0x%08X' % addr
|
|
||||||
|
|
||||||
def print_label(label):
|
|
||||||
if label in ['_prolog', '_epilog', '_unresolved']:
|
|
||||||
label = '.global %s\n%s' % (label, label)
|
|
||||||
print('%s:' % label)
|
|
||||||
|
|
||||||
def sign_extend_16(value):
|
|
||||||
if value > 0 and (value & 0x8000):
|
|
||||||
value -= 0x10000
|
|
||||||
return value
|
|
||||||
|
|
||||||
def disasm_fcmp(inst):
|
|
||||||
crd = (inst & 0x03800000) >> 23
|
|
||||||
a = (inst & 0x001f0000) >> 16
|
|
||||||
b = (inst & 0x0000f800) >> 11
|
|
||||||
return 'fcmpo cr%i, f%i, f%i' % (crd, a, b)
|
|
||||||
|
|
||||||
def disasm_mspr(inst, mode):
|
|
||||||
if (inst & 1):
|
|
||||||
return None
|
|
||||||
d = (inst & 0x03e00000) >> 21
|
|
||||||
a = (inst & 0x001f0000) >> 16
|
|
||||||
b = (inst & 0x0000f800) >>11
|
|
||||||
spr = (b << 5) + a
|
|
||||||
if mode:
|
|
||||||
return 'mtspr 0x%X, r%i' % (spr, d)
|
|
||||||
else:
|
|
||||||
return 'mfspr r%i, 0x%X' % (d, spr)
|
|
||||||
|
|
||||||
def disasm_mcrxr(inst):
|
|
||||||
if (inst & 0x007ff801):
|
|
||||||
return None
|
|
||||||
crd = (inst & 0x03800000) >> 23
|
|
||||||
return 'mcrxr cr%i' % crd
|
|
||||||
|
|
||||||
def disassemble_insn_that_capstone_cant_handle(o, reloc):
|
|
||||||
if reloc:
|
|
||||||
relocComment = '\t;# %s:%s' % (get_label(reloc['addr']), relocationTypeNames[reloc['type']])
|
|
||||||
else:
|
|
||||||
relocComment = ''
|
|
||||||
raw = read_u32(o)
|
|
||||||
asm = None
|
|
||||||
idx = (raw & 0xfc000000) >> 26
|
|
||||||
idx2 = (raw & 0x000007fe) >> 1
|
|
||||||
# mtspr
|
|
||||||
if idx == 31 and idx2 == 467:
|
|
||||||
asm = disasm_mspr(raw, 1)
|
|
||||||
# mfspr
|
|
||||||
elif idx == 31 and idx2 == 339:
|
|
||||||
asm = disasm_mspr(raw, 0)
|
|
||||||
# mcrxr
|
|
||||||
elif idx == 31 and idx2 == 512:
|
|
||||||
asm = disasm_mcrxr(raw)
|
|
||||||
# fcmpo
|
|
||||||
elif idx == 63 and idx2 == 32:
|
|
||||||
asm = disasm_fcmp(raw)
|
|
||||||
# Paired singles
|
|
||||||
elif idx == 4:
|
|
||||||
asm = disasm_ps(raw)
|
|
||||||
elif idx in {56, 57, 60, 61}:
|
|
||||||
asm = disasm_ps_mem(raw, idx)
|
|
||||||
if asm:
|
|
||||||
return asm
|
|
||||||
return '.4byte 0x%08X ;# (error: unknown instruction) %s' % (read_u32(o), relocComment)
|
|
||||||
|
|
||||||
def disassemble_insn(o, reloc):
|
|
||||||
if reloc:
|
|
||||||
relocComment = '\t;# %s:%s' % (get_label(reloc['addr']), relocationTypeNames[reloc['type']])
|
|
||||||
else:
|
|
||||||
relocComment = ''
|
|
||||||
try:
|
|
||||||
insn = next(cs.disasm(filecontent[o : o+4], o))
|
|
||||||
except StopIteration:
|
|
||||||
return disassemble_insn_that_capstone_cant_handle(o, reloc)
|
|
||||||
if reloc:
|
|
||||||
relocType = reloc['type']
|
|
||||||
else:
|
|
||||||
relocType = -1
|
|
||||||
|
|
||||||
# handle relocs label
|
|
||||||
if insn.id in {PPC_INS_BL, PPC_INS_BC} and relocType in {R_PPC_REL24, R_PPC_REL14}:
|
|
||||||
return '%s %s' % (insn.mnemonic, get_label(reloc['addr']))
|
|
||||||
if insn.id == PPC_INS_LIS and relocType == R_PPC_ADDR16_HA:
|
|
||||||
return '%s %s, %s@ha' % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), get_label(reloc['addr']))
|
|
||||||
if insn.id == PPC_INS_LIS and relocType == R_PPC_ADDR16_HI:
|
|
||||||
return '%s %s, %s@h' % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), get_label(reloc['addr']))
|
|
||||||
if insn.id in {PPC_INS_ADDI, PPC_INS_ORI} and relocType == R_PPC_ADDR16_LO:
|
|
||||||
return '%s %s, %s, %s@l' % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.reg_name(insn.operands[1].reg), get_label(reloc['addr']))
|
|
||||||
if insn.id in {
|
|
||||||
PPC_INS_LWZ, PPC_INS_LHZ, PPC_INS_LHA, PPC_INS_LBZ,
|
|
||||||
PPC_INS_LWZU, PPC_INS_LHZU, PPC_INS_LHAU, PPC_INS_LBZU,
|
|
||||||
PPC_INS_LFS, PPC_INS_LFD,
|
|
||||||
PPC_INS_LFSU, PPC_INS_LFDU,
|
|
||||||
PPC_INS_STW, PPC_INS_STH, PPC_INS_STB,
|
|
||||||
PPC_INS_STWU, PPC_INS_STHU, PPC_INS_STBU,
|
|
||||||
PPC_INS_STFS, PPC_INS_STFD,
|
|
||||||
PPC_INS_STFSU, PPC_INS_STFDU} \
|
|
||||||
and relocType == R_PPC_ADDR16_LO:
|
|
||||||
return '%s %s, %s@l(%s)' % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), get_label(reloc['addr']), insn.reg_name(insn.operands[1].mem.base))
|
|
||||||
|
|
||||||
# branch target labels
|
|
||||||
if insn.id in {PPC_INS_B, PPC_INS_BL, PPC_INS_BDZ, PPC_INS_BDNZ, PPC_INS_BC}:
|
|
||||||
if reloc:
|
|
||||||
return '%s %s' % (insn.mnemonic, get_label(reloc['addr']))
|
|
||||||
#add_label(insn.operands[0].imm)
|
|
||||||
#label = labels[insn.operands[0].imm]
|
|
||||||
#if label:
|
|
||||||
# WTF, capstone?
|
|
||||||
if o == 0xAD8C:
|
|
||||||
return '%s lbl_0000ADB0' % insn.mnemonic
|
|
||||||
return '%s %s' % (insn.mnemonic, get_label(insn.operands[0].imm))
|
|
||||||
|
|
||||||
# misc. fixes
|
|
||||||
|
|
||||||
# Sign-extend immediate values because Capstone is an idiot and thinks all immediates are unsigned
|
|
||||||
if insn.id in {PPC_INS_ADDI, PPC_INS_ADDIC, PPC_INS_SUBFIC, PPC_INS_MULLI} and (insn.operands[2].imm & 0x8000):
|
|
||||||
return "%s %s, %s, %i ;# fixed addi" % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.reg_name(insn.operands[1].value.reg), insn.operands[2].imm - 0x10000)
|
|
||||||
if (insn.id == PPC_INS_LI or insn.id == PPC_INS_CMPWI) and (insn.operands[1].imm & 0x8000):
|
|
||||||
return "%s %s, %i" % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.operands[1].imm - 0x10000)
|
|
||||||
# cntlz -> cntlzw
|
|
||||||
if insn.id == PPC_INS_CNTLZW:
|
|
||||||
return "cntlzw %s" % insn.op_str
|
|
||||||
|
|
||||||
return '%s %s%s' % (insn.mnemonic, insn.op_str, relocComment)
|
|
||||||
|
|
||||||
def scan_local_labels(o, size):
|
|
||||||
end = o + size
|
|
||||||
while o < end:
|
|
||||||
reloc = get_relocation_for_offset(o)
|
|
||||||
if reloc:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
insn = next(cs.disasm(filecontent[o:o+4], o))
|
|
||||||
if insn.id in {PPC_INS_B, PPC_INS_BL, PPC_INS_BC, PPC_INS_BDZ, PPC_INS_BDNZ}:
|
|
||||||
for op in insn.operands:
|
|
||||||
if op.type == PPC_OP_IMM:
|
|
||||||
l = add_label(op.imm)
|
|
||||||
#print('adding local label %s(0x%X) from offset 0x%X' % (l, op.imm, o))
|
|
||||||
except StopIteration:
|
|
||||||
pass
|
|
||||||
o += 4
|
|
||||||
#for insn in cs.disasm(filecontent[o:o+size], o):
|
|
||||||
# # branch labels
|
|
||||||
# if insn.id in {PPC_INS_B, PPC_INS_BL, PPC_INS_BC, PPC_INS_BDZ, PPC_INS_BDNZ}:
|
|
||||||
# for op in insn.operands:
|
|
||||||
# if op.type == PPC_OP_IMM:
|
|
||||||
# l = add_label(op.imm)
|
|
||||||
# print('adding local label %s(0x%X) from offset 0x%X' % (l, op.imm, o))
|
|
||||||
|
|
||||||
def dump_code(o, size):
|
|
||||||
scan_local_labels(o, size)
|
|
||||||
end = o + size
|
|
||||||
code = filecontent[o : end]
|
|
||||||
while o < end:
|
|
||||||
if o in labels:
|
|
||||||
print_label(labels[o])
|
|
||||||
asm = disassemble_insn(o, get_relocation_for_offset(o))
|
|
||||||
print('/* %08X %08X */ %s' % (o, read_u32(o), asm))
|
|
||||||
#print('/* %08X */ %s' % (read_u32(o), asm))
|
|
||||||
o += 4
|
|
||||||
if o < end:
|
|
||||||
print('incomplete')
|
|
||||||
|
|
||||||
# returns True if value is 4-byte aligned
|
|
||||||
def is_aligned(num):
|
|
||||||
return num % 4 == 0
|
|
||||||
|
|
||||||
def align(num):
|
|
||||||
return (num + 3) & ~3
|
|
||||||
|
|
||||||
def is_ascii(code):
|
|
||||||
if code >= 0x20 and code <= 0x7E: # normal characters
|
|
||||||
return True
|
|
||||||
if code in [0x09, 0x0A]: # tab, newline
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
# returns True if all elements are zero
|
|
||||||
def is_all_zero(arr):
|
|
||||||
for val in arr:
|
|
||||||
if val != 0:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
# returns string of comma-separated hex bytes
|
|
||||||
def hex_bytes(data):
|
|
||||||
return ', '.join('0x%02X' % n for n in data)
|
|
||||||
|
|
||||||
# reads a string starting at pos
|
|
||||||
def read_string(data, pos):
|
|
||||||
text = ''
|
|
||||||
while pos < len(data) and is_ascii(data[pos]):
|
|
||||||
text += chr(data[pos])
|
|
||||||
pos += 1
|
|
||||||
if pos < len(data) and data[pos] == 0:
|
|
||||||
return text
|
|
||||||
return ''
|
|
||||||
|
|
||||||
# escapes special characters in the string for use in a C string literal
|
|
||||||
def escape_string(text):
|
|
||||||
return text.replace('\\','\\\\').replace('"','\\"').replace('\n','\\n').replace('\t','\\t')
|
|
||||||
|
|
||||||
def output_data_range(secNum, o, end):
|
|
||||||
print(' # 0x%X' % o)
|
|
||||||
if not is_aligned(o):
|
|
||||||
print(' .byte ' + hex_bytes(filecontent[o:align(o)]))
|
|
||||||
o = align(o)
|
|
||||||
while o < (end & ~3):
|
|
||||||
# Try to see if this is a string.
|
|
||||||
string = read_string(filecontent, o)
|
|
||||||
if len(string) >= 4 and secNum == 5: # strings are only in .data
|
|
||||||
strEnd = o + len(string)+1
|
|
||||||
if is_aligned(strEnd) or is_all_zero(filecontent[strEnd : align(strEnd)-strEnd]):
|
|
||||||
print(' .asciz \"%s"' % escape_string(string))
|
|
||||||
if not is_aligned(strEnd):
|
|
||||||
print(' .balign 4')
|
|
||||||
o = align(strEnd)
|
|
||||||
continue
|
|
||||||
# Not a string
|
|
||||||
reloc = get_relocation_for_offset(o)
|
|
||||||
if reloc:
|
|
||||||
type = reloc['type']
|
|
||||||
if type == R_PPC_ADDR32:
|
|
||||||
value = labels[reloc['addr']]
|
|
||||||
else:
|
|
||||||
print('dunno what to do about %s here' % relocationTypeNames[type])
|
|
||||||
else:
|
|
||||||
value = '0x%08X' % read_u32(o)
|
|
||||||
print(' .4byte %s' % value)
|
|
||||||
o += 4
|
|
||||||
if o < end:
|
|
||||||
print(' .byte ' + hex_bytes(filecontent[o:end]))
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def dump_data(secNum, o, size):
|
|
||||||
end = o + size
|
|
||||||
lastPos = o
|
|
||||||
while o < end:
|
|
||||||
if o in labels:
|
|
||||||
if o - lastPos > 0:
|
|
||||||
output_data_range(secNum, lastPos, o)
|
|
||||||
print_label(labels[o])
|
|
||||||
lastPos = o
|
|
||||||
o += 1
|
|
||||||
if o - lastPos > 0:
|
|
||||||
output_data_range(secNum, lastPos, o)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def output_bss_range(start, end):
|
|
||||||
print(' .skip 0x%X' % (end - start))
|
|
||||||
|
|
||||||
def dump_bss(o, size):
|
|
||||||
end = o + size
|
|
||||||
lastPos = o
|
|
||||||
while o < end:
|
|
||||||
if o in labels:
|
|
||||||
if o - lastPos > 0:
|
|
||||||
output_bss_range(lastPos, o)
|
|
||||||
print_label(labels[o])
|
|
||||||
lastPos = o
|
|
||||||
o += 1
|
|
||||||
if o - lastPos > 0:
|
|
||||||
output_bss_range(lastPos, o)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
for i in range(0, numSections):
|
|
||||||
section = sectionInfo[i]
|
|
||||||
if section['offset'] == 0 and section['length'] == 0:
|
|
||||||
continue
|
|
||||||
print('# %i' % i)
|
|
||||||
print('.section %s' % section['name'])
|
|
||||||
if section['is_bss']:
|
|
||||||
# bss section
|
|
||||||
dump_bss(section['offset'], section['length'])
|
|
||||||
elif section['flags'] & 1:
|
|
||||||
# code section
|
|
||||||
dump_code(section['offset'], section['length'])
|
|
||||||
elif section['offset'] != 0:
|
|
||||||
# data section
|
|
||||||
dump_data(i, section['offset'], section['length'])
|
|
||||||
print('')
|
|
Loading…
Reference in New Issue