Created
September 6, 2018 14:46
-
-
Save VelocityRa/c259e580b8b5c5c38a7d446da6df286e to your computer and use it in GitHub Desktop.
Vita3K WIP code for newly discovered relocation entry types
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Vita3K emulator project | |
// Copyright (C) 2018 Vita3K team | |
// | |
// This program is free software; you can redistribute it and/or modify | |
// it under the terms of the GNU General Public License as published by | |
// the Free Software Foundation; either version 2 of the License, or | |
// (at your option) any later version. | |
// | |
// This program is distributed in the hope that it will be useful, | |
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
// GNU General Public License for more details. | |
// | |
// You should have received a copy of the GNU General Public License aFORMAT0 | |
// with this program; if not, write to the Free Software Foundation, Inc., | |
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
#include <kernel/relocation.h> | |
#include <util/log.h> | |
#include <cassert> | |
#include <cstring> | |
#include <iostream> | |
static constexpr bool LOG_RELOCATIONS = true; | |
enum Code { | |
None = 0, | |
Abs32 = 2, | |
Rel32 = 3, | |
Abs8 = 8, | |
ThumbCall = 10, | |
Call = 28, | |
Jump24 = 29, | |
Target1 = 38, | |
V4BX = 40, | |
Target2 = 41, | |
Prel31 = 42, | |
MovwAbsNc = 43, | |
MovtAbs = 44, | |
ThumbMovwAbsNc = 47, | |
ThumbMovtAbs = 48 | |
}; | |
// Common type so we can static_cast between unknown/known formats | |
struct Entry {}; | |
struct EntryFormatUnknown : Entry { | |
uint8_t type : 4; | |
}; | |
struct EntryFormat0 : Entry { | |
uint32_t type : 4; | |
uint32_t symbol_segment : 4; | |
uint32_t code : 8; | |
uint32_t data_segment : 4; | |
uint32_t code2 : 8; | |
uint32_t dist2 : 4; | |
uint32_t addend; | |
uint32_t offset; | |
}; | |
struct EntryFormat1 : Entry { | |
uint32_t type : 4; | |
uint32_t symbol_segment : 4; | |
uint32_t code : 8; | |
uint32_t data_segment : 4; | |
uint32_t offset_lo : 12; | |
uint32_t offset_hi : 10; | |
uint32_t addend : 22; | |
}; | |
struct EntryFormat2 : Entry { | |
uint32_t type : 4; | |
uint32_t symbol_segment : 4; | |
uint32_t code : 8; | |
uint32_t offset : 16; | |
uint32_t addend; | |
}; | |
struct EntryFormat3 : Entry { | |
uint32_t type : 4; | |
uint32_t symbol_segment : 4; | |
uint32_t mode : 1; // ARM = 0, THUMB = 1 | |
uint32_t offset : 18; | |
uint32_t dist : 5; | |
uint32_t addend : 22; | |
}; | |
struct EntryFormat4 : Entry { | |
uint32_t type : 4; | |
uint32_t offset : 23; | |
uint32_t dist : 5; | |
}; | |
struct EntryFormat5 : Entry { | |
uint32_t type : 4; | |
uint32_t dist1 : 9; | |
uint32_t dist2 : 5; | |
uint32_t dist3 : 9; | |
uint32_t dist4 : 5; | |
}; | |
struct EntryFormat6 : Entry { | |
uint32_t type : 4; | |
uint32_t offset : 28; | |
}; | |
struct EntryFormat7 : Entry { | |
uint32_t type : 4; | |
uint32_t offset1 : 7; | |
uint32_t offset2 : 7; | |
uint32_t offset3 : 7; | |
uint32_t offset4 : 7; | |
}; | |
struct EntryFormat8 : Entry { | |
uint32_t type : 4; | |
uint32_t offset1 : 4; | |
uint32_t offset2 : 4; | |
uint32_t offset3 : 4; | |
uint32_t offset4 : 4; | |
uint32_t offset5 : 4; | |
uint32_t offset6 : 4; | |
uint32_t offset7 : 4; | |
}; | |
struct EntryFormat9 : Entry { | |
uint32_t type : 4; | |
uint32_t offset1 : 2; | |
uint32_t offset2 : 2; | |
uint32_t offset3 : 2; | |
uint32_t offset4 : 2; | |
uint32_t offset5 : 2; | |
uint32_t offset6 : 2; | |
uint32_t offset7 : 2; | |
uint32_t offset8 : 2; | |
uint32_t offset9 : 2; | |
uint32_t offset10 : 2; | |
uint32_t offset11 : 2; | |
uint32_t offset12 : 2; | |
uint32_t offset13 : 2; | |
uint32_t offset14 : 2; | |
}; | |
static_assert(sizeof(EntryFormat0) == 12, "Entry has incorrect size."); | |
static_assert(sizeof(EntryFormat1) == 8, "Entry has incorrect size."); | |
static_assert(sizeof(EntryFormat3) == 8, "Entry has incorrect size."); | |
static_assert(sizeof(EntryFormat4) == 4, "Entry has incorrect size."); | |
static_assert(sizeof(EntryFormat7) == 4, "Entry has incorrect size."); | |
static_assert(sizeof(EntryFormat8) == 4, "Entry has incorrect size."); | |
static_assert(sizeof(EntryFormat9) == 4, "Entry has incorrect size."); | |
static void write(void *data, uint32_t value) { | |
memcpy(data, &value, sizeof(value)); | |
} | |
static void write_masked(void *data, uint32_t symbol, uint32_t mask) { | |
write(data, symbol & mask); | |
} | |
static void write_thumb_call(void *data, uint32_t symbol) { | |
// This is cribbed from UVLoader, but I used bitfields to get rid of some shifting and masking. | |
struct Upper { | |
uint16_t imm10 : 10; | |
uint16_t sign : 1; | |
uint16_t ignored : 5; | |
}; | |
struct Lower { | |
uint16_t imm11 : 11; | |
uint16_t j2 : 1; | |
uint16_t unknown : 1; | |
uint16_t j1 : 1; | |
uint16_t unknown2 : 2; | |
}; | |
struct Pair { | |
Upper upper; | |
Lower lower; | |
}; | |
static_assert(sizeof(Pair) == 4, "Incorrect size."); | |
Pair *const pair = static_cast<Pair *>(data); | |
pair->lower.imm11 = symbol >> 1; | |
pair->upper.imm10 = symbol >> 12; | |
pair->upper.sign = symbol >> 24; | |
pair->lower.j2 = pair->upper.sign ^ ((~symbol) >> 22); | |
pair->lower.j1 = pair->upper.sign ^ ((~symbol) >> 23); | |
} | |
static void write_mov_abs(void *data, uint16_t symbol) { | |
struct Instruction { | |
uint32_t imm12 : 12; | |
uint32_t ignored1 : 4; | |
uint32_t imm4 : 4; | |
uint32_t ignored2 : 12; | |
}; | |
static_assert(sizeof(Instruction) == 4, "Incorrect size."); | |
Instruction *const instruction = static_cast<Instruction *>(data); | |
instruction->imm12 = symbol; | |
instruction->imm4 = symbol >> 12; | |
} | |
static void write_thumb_mov_abs(void *data, uint16_t symbol) { | |
// This is cribbed from UVLoader, but I used bitfields to get rid of some shifting and masking. | |
struct Upper { | |
uint16_t imm4 : 4; | |
uint16_t ignored1 : 6; | |
uint16_t i : 1; | |
uint16_t ignored2 : 5; | |
}; | |
struct Lower { | |
uint16_t imm8 : 8; | |
uint16_t ignored1 : 4; | |
uint16_t imm3 : 3; | |
uint16_t ignored2 : 1; | |
}; | |
struct Pair { | |
Upper upper; | |
Lower lower; | |
}; | |
static_assert(sizeof(Pair) == 4, "Incorrect size."); | |
Pair *const pair = static_cast<Pair *>(data); | |
pair->lower.imm8 = symbol; | |
pair->lower.imm3 = symbol >> 8; | |
pair->upper.i = symbol >> 11; | |
pair->upper.imm4 = symbol >> 12; | |
} | |
static bool relocate_entry(void *data, Code code, uint32_t s, uint32_t a, uint32_t p) { | |
switch (code) { | |
case None: | |
case V4BX: // Untested. | |
return true; | |
case Abs32: | |
case Target1: | |
write(data, s + a); | |
return true; | |
case Abs8: | |
write_masked(data, s + a, 0xff); | |
return true; | |
case Rel32: | |
case Target2: | |
write(data, s + a - p); | |
return true; | |
case Prel31: | |
write_masked(data, s + a - p, INT32_MAX); | |
return true; | |
case ThumbCall: | |
write_thumb_call(data, s + a - p); | |
return true; | |
case Call: | |
case Jump24: | |
write_masked(data, (s + a - p) >> 2, 0xffffff); | |
return true; | |
case MovwAbsNc: | |
write_mov_abs(data, s + a); | |
return true; | |
case MovtAbs: | |
write_mov_abs(data, (s + a) >> 16); | |
return true; | |
case ThumbMovwAbsNc: | |
write_thumb_mov_abs(data, s + a); | |
return true; | |
case ThumbMovtAbs: | |
write_thumb_mov_abs(data, (s + a) >> 16); | |
return true; | |
} | |
LOG_WARN("Unhandled relocation code {}.", code); | |
return false; | |
} | |
bool relocate(const void *entries, size_t size, const SegmentAddresses &segments, const MemState &mem) { | |
const void *const end = static_cast<const uint8_t *>(entries) + size; | |
const Entry *entry = static_cast<const Entry *>(entries); | |
if (LOG_RELOCATIONS) { | |
LOG_DEBUG("Relocating patch of size: {}, # of segments: {}", log_hex(size), segments.size()); | |
for (const auto seg : segments) | |
LOG_DEBUG(" Segment: {} -> {}", seg.first, log_hex(seg.second.address())); | |
} | |
// initialized in format 1 and 2 | |
Address g_addr = 0, | |
g_offset = 0, | |
g_patchseg = 0; | |
// initiliazed in format 0, 1, 2, and 3 | |
Address g_saddr = 0, | |
g_addend = 0, | |
g_type = 0, | |
g_type2 = 0; | |
const EntryFormatUnknown *generic_entry = nullptr; | |
while (entry < end) { | |
generic_entry = static_cast<const EntryFormatUnknown *>(entry); | |
switch (generic_entry->type) { | |
case 0: { | |
const EntryFormat0 *const FORMAT0_entry = static_cast<const EntryFormat0 *>(entry); | |
const auto symbol_start_it = segments.find(FORMAT0_entry->symbol_segment); | |
const Ptr<void> symbol_start = symbol_start_it->second; | |
const Address s = (FORMAT0_entry->symbol_segment == 0xf) ? 0 : symbol_start.address(); | |
const auto data_segment_it = segments.find(FORMAT0_entry->data_segment); | |
if (data_segment_it != segments.end()) { | |
const Ptr<void> data_segment = data_segment_it->second; | |
const Address p = data_segment.address() + FORMAT0_entry->offset; | |
const Address a = FORMAT0_entry->addend; | |
LOG_DEBUG_IF(LOG_RELOCATIONS, "[FORMAT0]: code: {}, sym_seg: {}, sym_start: {}, s: {}, data_seg: {}, p: {}, a: {}", FORMAT0_entry->code, FORMAT0_entry->symbol_segment, log_hex(symbol_start.address()), log_hex(s), log_hex(data_segment.address()), log_hex(p), log_hex(a)); | |
if (!relocate_entry(Ptr<uint32_t>(p).get(mem), static_cast<Code>(FORMAT0_entry->code), s, a, p)) { | |
return false; | |
} | |
if (FORMAT0_entry->code2 != 0) { | |
LOG_DEBUG_IF(LOG_RELOCATIONS, "[FORMAT0/2]: code: {}, sym_seg: {}, sym_start: {}, s: {}, data_seg: {}, p: {}, a: {}", FORMAT0_entry->code2, FORMAT0_entry->symbol_segment, log_hex(symbol_start.address()), log_hex(s), log_hex(data_segment.address()), log_hex(p + (FORMAT0_entry->dist2 * 2)), log_hex(a)); | |
if (!relocate_entry(Ptr<uint32_t>(p + (FORMAT0_entry->dist2 * 2)).get(mem), static_cast<Code>(FORMAT0_entry->code2), s, a, p)) { | |
return false; | |
} | |
} | |
g_addr = data_segment.address(); | |
g_offset = FORMAT0_entry->offset; | |
g_patchseg = FORMAT0_entry->data_segment; | |
g_saddr = s; | |
g_addend = a; | |
g_type = FORMAT0_entry->code; | |
g_type2 = FORMAT0_entry->code2; | |
} else { | |
LOG_DEBUG_IF(LOG_RELOCATIONS, "[FORMAT0]: code: {}, sym_seg: {}, sym_start: {}, s: {}", FORMAT0_entry->code, FORMAT0_entry->symbol_segment, log_hex(symbol_start.address()), log_hex(s)); | |
LOG_WARN("Segment {} not found for FORMAT0 relocation with code {}, skipping", FORMAT0_entry->data_segment, FORMAT0_entry->code); | |
} | |
break; | |
} | |
case 1: { | |
const EntryFormat1 *const short_entry = static_cast<const EntryFormat1 *>(entry); | |
const auto symbol_start_it = segments.find(short_entry->symbol_segment); | |
const Ptr<void> symbol_start = symbol_start_it->second; | |
const Address s = (short_entry->symbol_segment == 0xf) ? 0 : symbol_start.address(); | |
const auto data_segment_it = segments.find(short_entry->data_segment); | |
if (data_segment_it != segments.end()) { | |
const Ptr<void> data_segment = data_segment_it->second; | |
const Address offset = short_entry->offset_lo | (short_entry->offset_hi << 12); | |
const Address p = data_segment.address() + offset; | |
const Address a = short_entry->addend; | |
LOG_DEBUG_IF(LOG_RELOCATIONS, "[FORMAT1]: code: {}, sym_seg: {}, sym_start: {}, s: {}, data_seg: {}, offset: {}, p: {}, a: {}", short_entry->code, short_entry->symbol_segment, log_hex(symbol_start.address()), log_hex(s), log_hex(data_segment.address()), log_hex(offset), log_hex(p), log_hex(a)); | |
if (!relocate_entry(Ptr<uint32_t>(p).get(mem), static_cast<Code>(short_entry->code), s, a, p)) { | |
return false; | |
} | |
g_addr = data_segment.address(); | |
g_offset = offset; | |
g_patchseg = short_entry->data_segment; | |
g_saddr = s; | |
g_addend = a; | |
g_type = short_entry->code; | |
g_type2 = 0; | |
} else { | |
LOG_DEBUG_IF(LOG_RELOCATIONS, "[FORMAT1/ERROR]: code: {}, sym_seg: {}, sym_start: {}, s: {}", short_entry->code, short_entry->symbol_segment, log_hex(symbol_start.address()), log_hex(s)); | |
LOG_WARN("Segment {} not found for short relocation with code {}, skipping", short_entry->data_segment, short_entry->code); | |
} | |
break; | |
} | |
case 3: { | |
// TODO: | |
const EntryFormat3 *const format3_entry = static_cast<const EntryFormat3 *>(entry); | |
LOG_WARN_IF(LOG_RELOCATIONS, "[FORMAT3/UNIMP]: sym_seg: {}, mode: {} ({}), offset: {}, dist: {}, addend: {}", log_hex(format3_entry->symbol_segment), format3_entry->mode, format3_entry->mode ? "THUMB" : "ARM", log_hex(format3_entry->offset), log_hex(format3_entry->dist), log_hex(format3_entry->addend)); | |
break; | |
} | |
case 4: { | |
// TODO: | |
const EntryFormat4 *const format4_entry = static_cast<const EntryFormat4 *>(entry); | |
LOG_WARN_IF(LOG_RELOCATIONS, "[FORMAT4/UNIMP]: offset: {}, dist: {}", log_hex(format4_entry->offset), log_hex(format4_entry->dist)); | |
break; | |
} | |
case 7: { | |
const EntryFormat7 *const format7_entry = static_cast<const EntryFormat7 *>(entry); | |
LOG_DEBUG_IF(LOG_RELOCATIONS, "[FORMAT7]: offset1: {}, offset2: {}, offset3: {}, offset4: {}", log_hex(format7_entry->offset1), log_hex(format7_entry->offset2), log_hex(format7_entry->offset3), log_hex(format7_entry->offset4)); | |
g_offset += format7_entry->offset1 * sizeof(uint32_t); | |
g_type2 = 0; | |
g_type = Abs32; | |
{ | |
const Address s = g_saddr; | |
const Address p = g_addr + g_offset; | |
const Address a = g_offset; | |
if (!relocate_entry(Ptr<uint32_t>(p).get(mem), static_cast<Code>(g_type), s, a, p)) { | |
return false; | |
} | |
} | |
if (format7_entry->offset2) { | |
g_offset += format7_entry->offset2 * sizeof(uint32_t); | |
g_type2 = 0; | |
g_type = Abs32; | |
{ | |
const Address s = g_saddr; | |
const Address p = g_addr + g_offset; | |
const Address a = g_offset; | |
if (!relocate_entry(Ptr<uint32_t>(p).get(mem), static_cast<Code>(g_type), s, a, p)) { | |
return false; | |
} | |
} | |
if (format7_entry->offset3) { | |
g_offset += format7_entry->offset3 * sizeof(uint32_t); | |
g_type2 = 0; | |
g_type = Abs32; | |
{ | |
const Address s = g_saddr; | |
const Address p = g_addr + g_offset; | |
const Address a = g_offset; | |
if (!relocate_entry(Ptr<uint32_t>(p).get(mem), static_cast<Code>(g_type), s, a, p)) { | |
return false; | |
} | |
} | |
if (format7_entry->offset4) { | |
g_offset += format7_entry->offset4 * sizeof(uint32_t); | |
g_type2 = 0; | |
g_type = Abs32; | |
{ | |
const Address s = g_saddr; | |
const Address p = g_addr + g_offset; | |
const Address a = g_offset; | |
if (!relocate_entry(Ptr<uint32_t>(p).get(mem), static_cast<Code>(g_type), s, a, p)) { | |
return false; | |
} | |
} | |
} | |
} | |
} | |
break; | |
} | |
case 8: { | |
// TODO: | |
const EntryFormat8 *const format8_entry = static_cast<const EntryFormat8 *>(entry); | |
LOG_WARN_IF(LOG_RELOCATIONS, "[FORMAT8/UNIMP]: offset1: {}, offset2: {}, offset3: {}, offset4: {}, offset5: {}, offset6: {}, offset7: {},", log_hex(format8_entry->offset1), log_hex(format8_entry->offset2), log_hex(format8_entry->offset3), log_hex(format8_entry->offset4), log_hex(format8_entry->offset5), log_hex(format8_entry->offset6), log_hex(format8_entry->offset7)); | |
break; | |
} | |
case 9: { | |
// TODO: | |
const EntryFormat9 *const format9_entry = static_cast<const EntryFormat9 *>(entry); | |
LOG_WARN_IF(LOG_RELOCATIONS, "[FORMAT9/UNIMP]: offset1: {}, offset2: {}, offset3: {}, offset4: {}, offset5: {}, offset6: {}, offset7: {}, offset8: {}, offset9: {}, offset10: {}, offset11: {}, offset12: {}, offset13: {}, offset14: {}", log_hex(format9_entry->offset1), log_hex(format9_entry->offset2), log_hex(format9_entry->offset3), log_hex(format9_entry->offset4), log_hex(format9_entry->offset5), log_hex(format9_entry->offset6), log_hex(format9_entry->offset7), log_hex(format9_entry->offset8), log_hex(format9_entry->offset9), log_hex(format9_entry->offset10), log_hex(format9_entry->offset11), log_hex(format9_entry->offset12), log_hex(format9_entry->offset13), log_hex(format9_entry->offset14)); | |
break; | |
} | |
default: { | |
LOG_WARN("Unknown relocation entry type {} ", generic_entry->type); | |
return false; | |
} | |
} | |
switch (generic_entry->type) { | |
case 0: | |
entry = static_cast<const EntryFormat0 *>(entry) + 1; | |
break; | |
case 1: | |
entry = static_cast<const EntryFormat1 *>(entry) + 1; | |
break; | |
case 3: | |
entry = static_cast<const EntryFormat3 *>(entry) + 1; | |
break; | |
case 4: | |
entry = static_cast<const EntryFormat4 *>(entry) + 1; | |
break; | |
case 7: | |
entry = static_cast<const EntryFormat7 *>(entry) + 1; | |
break; | |
case 8: | |
entry = static_cast<const EntryFormat8 *>(entry) + 1; | |
break; | |
case 9: | |
entry = static_cast<const EntryFormat9 *>(entry) + 1; | |
break; | |
} | |
} | |
return true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment