Skip to content

Instantly share code, notes, and snippets.

@VelocityRa
Created September 6, 2018 14:46
Show Gist options
  • Save VelocityRa/c259e580b8b5c5c38a7d446da6df286e to your computer and use it in GitHub Desktop.
Save VelocityRa/c259e580b8b5c5c38a7d446da6df286e to your computer and use it in GitHub Desktop.
Vita3K WIP code for newly discovered relocation entry types
// 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