Last active
June 21, 2020 07:10
-
-
Save nicolasnoble/9ffa348314df85ee7a4ab5d2bc519eab to your computer and use it in GitHub Desktop.
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
#define NOMINMAX | |
#define _CRT_SECURE_NO_WARNINGS | |
#ifdef _WIN32 | |
#define WIN32LEANANDMEAN | |
#include <windows.h> | |
#endif | |
#include <assert.h> | |
#include <ctype.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <algorithm> | |
#include <filesystem> | |
#include <iostream> | |
#include <limits> | |
#include <string> | |
#include <variant> | |
#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) | |
typedef intptr_t ssize_t; | |
#define SSIZE_MAX INTPTR_MAX | |
#define _SSIZE_T_ | |
#define _SSIZE_T_DEFINED | |
#endif | |
class Slice { | |
public: | |
Slice() {} | |
template <size_t L> | |
Slice(const char (&data)[L]) { | |
borrow(data, L - 1); | |
} | |
Slice(const Slice &other) { copyFrom(other); } | |
Slice(Slice &&other) noexcept { moveFrom(std::move(other)); } | |
Slice(const std::string &str) { m_data = str; } | |
Slice(std::string &&str) { m_data = std::move(str); } | |
std::string asString() const { | |
if (std::holds_alternative<std::string>(m_data)) { | |
return std::get<std::string>(m_data); | |
} | |
return {static_cast<const char *>(data()), size()}; | |
} | |
Slice &operator=(const Slice &other) { | |
copyFrom(other); | |
return *this; | |
} | |
Slice &operator=(Slice &&other) noexcept { | |
moveFrom(std::move(other)); | |
return *this; | |
} | |
void copy(const Slice &other) { | |
if (std::holds_alternative<std::string>(other.m_data)) { | |
m_data = other.m_data; | |
} else { | |
copy(other.data(), other.size()); | |
} | |
} | |
void copy(const std::string &str) { m_data = str; } | |
void copy(const void *data, uint32_t size) { | |
void *dest; | |
if (size < INLINED_SIZE) { | |
m_data = Inlined{size}; | |
dest = std::get<Inlined>(m_data).inlined; | |
} else { | |
m_data = Owned{size, malloc(size)}; | |
dest = std::get<Owned>(m_data).ptr; | |
} | |
memcpy(dest, data, size); | |
} | |
void acquire(std::string &&str) { m_data = std::move(str); } | |
void acquire(void *data, uint32_t size) { | |
m_data = Owned{size, malloc(size)}; | |
std::get<Owned>(m_data).ptr = data; | |
std::get<Owned>(m_data).size = size; | |
} | |
void borrow(const Slice &other, uint32_t from = 0, uint32_t amount = std::numeric_limits<uint32_t>::max()) { | |
const uint8_t *ptr = static_cast<const uint8_t *>(other.data()); | |
uint32_t size = other.size(); | |
if (from >= size) { | |
m_data = std::monostate(); | |
return; | |
} | |
ptr += from; | |
size -= from; | |
borrow(ptr, std::min(amount, size)); | |
} | |
template <size_t L> | |
void borrow(const char (&data)[L]) { | |
m_data = Borrowed{L - 1, data}; | |
} | |
void borrow(const void *data, uint32_t size) { m_data = Borrowed{size, data}; } | |
const void *data() const { | |
if (std::holds_alternative<std::string>(m_data)) { | |
return std::get<std::string>(m_data).data(); | |
} else if (std::holds_alternative<Inlined>(m_data)) { | |
return std::get<Inlined>(m_data).inlined; | |
} else if (std::holds_alternative<Owned>(m_data)) { | |
return std::get<Owned>(m_data).ptr; | |
} else if (std::holds_alternative<Borrowed>(m_data)) { | |
return std::get<Borrowed>(m_data).ptr; | |
} | |
return nullptr; | |
} | |
const uint32_t size() const { | |
if (std::holds_alternative<std::string>(m_data)) { | |
return std::get<std::string>(m_data).size(); | |
} else if (std::holds_alternative<Inlined>(m_data)) { | |
return std::get<Inlined>(m_data).size; | |
} else if (std::holds_alternative<Owned>(m_data)) { | |
return std::get<Owned>(m_data).size; | |
} else if (std::holds_alternative<Borrowed>(m_data)) { | |
return std::get<Borrowed>(m_data).size; | |
} | |
return 0; | |
} | |
private: | |
void copyFrom(const Slice &other) { | |
if (std::holds_alternative<Owned>(other.m_data)) { | |
copy(other.data(), other.size()); | |
} else { | |
m_data = other.m_data; | |
} | |
} | |
void moveFrom(Slice &&other) { | |
m_data = std::move(other.m_data); | |
if (std::holds_alternative<Owned>(other.m_data)) { | |
std::get<Owned>(other.m_data).ptr = nullptr; | |
} | |
other.m_data = std::monostate(); | |
} | |
static constexpr size_t INLINED_SIZE = 28; | |
struct Inlined { | |
uint32_t size; | |
uint8_t inlined[INLINED_SIZE]; | |
}; | |
struct Owned { | |
~Owned() { free(ptr); } | |
uint32_t size; | |
void *ptr; | |
}; | |
struct Borrowed { | |
uint32_t size; | |
const void *ptr; | |
}; | |
std::variant<std::monostate, std::string, Inlined, Owned, Borrowed> m_data; | |
}; | |
class File { | |
public: | |
void close(); | |
ssize_t seek(ssize_t pos, int wheel); | |
ssize_t tell(); | |
void flush(); | |
File(void *data, ssize_t size); | |
File(const std::filesystem::path &filename) : File(filename.u8string()) {} | |
#if defined(__cpp_lib_char8_t) | |
File(const std::u8string &filename) : File(reinterpret_cast<const char *>(filename.c_str())) {} | |
#endif | |
File(const std::string &filename) : File(filename.c_str()) {} | |
File(const char *filename); | |
~File() { close(); } | |
File *dup() { return new File(m_filename); } | |
char *gets(char *s, int size); | |
std::string gets(); | |
template <class T> | |
T read() { | |
T ret = 0; | |
for (int i = 0; i < sizeof(T); i++) { | |
T b = byte(); | |
ret |= (b << (i * 8)); | |
} | |
return ret; | |
} | |
uint8_t byte() { | |
uint8_t r; | |
read(&r, 1); | |
return r; | |
} | |
std::string readString(size_t size) { | |
std::string r; | |
r.reserve(size); | |
for (size_t i = 0; i < size; i++) { | |
r += (char)byte(); | |
} | |
return std::move(r); | |
} | |
ssize_t read(void *dest, ssize_t size); | |
ssize_t write(const void *dest, size_t size); | |
Slice read(ssize_t size) { | |
void *data = malloc(size); | |
read(data, size); | |
Slice slice; | |
slice.acquire(data, size); | |
return std::move(slice); | |
} | |
int getc(); | |
bool failed(); | |
bool eof(); | |
std::filesystem::path filename() { return m_filename; } | |
private: | |
const std::filesystem::path m_filename; | |
static const uint8_t m_internalBuffer; | |
FILE *m_handle = NULL; | |
ssize_t m_ptr = 0; | |
ssize_t m_size = 0; | |
const uint8_t *m_data = NULL; | |
}; | |
const uint8_t File::m_internalBuffer = 0; | |
void File::close() { | |
if (m_handle) fclose(m_handle); | |
m_handle = nullptr; | |
} | |
ssize_t File::seek(ssize_t pos, int wheel) { | |
if (m_handle) return fseek(m_handle, pos, wheel); | |
if (!m_data) return -1; | |
switch (wheel) { | |
case SEEK_SET: | |
m_ptr = pos; | |
break; | |
case SEEK_END: | |
m_ptr = m_size - pos; | |
break; | |
case SEEK_CUR: | |
m_ptr += pos; | |
break; | |
} | |
m_ptr = std::max(std::min(m_ptr, m_size), (ssize_t)0); | |
return m_ptr; | |
} | |
ssize_t File::tell() { | |
if (m_handle) return ftell(m_handle); | |
if (m_data) return m_ptr; | |
return -1; | |
} | |
void File::flush() { | |
if (m_handle) fflush(m_handle); | |
} | |
File::File(void *data, ssize_t size) { | |
if (data) { | |
m_data = static_cast<uint8_t *>(data); | |
} else { | |
assert(size == 1); | |
m_data = &m_internalBuffer; | |
} | |
m_size = size; | |
} | |
#ifdef _WIN32 | |
File::File(const char *filename) : m_filename(filename) { | |
#ifdef UNICODE | |
int needed; | |
LPWSTR str; | |
needed = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); | |
if (needed <= 0) return; | |
str = (LPWSTR)_malloca(needed * sizeof(wchar_t)); | |
MultiByteToWideChar(CP_UTF8, 0, filename, -1, str, needed * sizeof(wchar_t)); | |
m_handle = _wfopen(str, L"rb"); | |
_freea(str); | |
#else | |
m_handle = fopen(filename, "rb"); | |
#endif | |
} | |
#else | |
File::File(const char *filename) : m_filename(filename) { m_handle = fopen(filename, "rb"); } | |
#endif | |
char *File::gets(char *s, int size) { | |
if (m_handle) return fgets(s, size, m_handle); | |
if (!m_data) return nullptr; | |
if (m_size == m_ptr) return nullptr; | |
int c; | |
char *ptr = s; | |
if (!size) return nullptr; | |
size--; | |
while (true) { | |
if (!size) { | |
*ptr = 0; | |
return s; | |
} | |
c = getc(); | |
if ((c == 0) || (c == -1)) { | |
*ptr = 0; | |
return s; | |
} | |
*ptr++ = c; | |
size--; | |
} | |
} | |
std::string File::gets() { | |
int c; | |
std::string ret; | |
while (true) { | |
c = getc(); | |
if ((c == 0) || (c == -1)) { | |
return ret; | |
} | |
ret += c; | |
} | |
} | |
ssize_t File::read(void *dest, ssize_t size) { | |
if (m_handle) return fread(dest, 1, size, m_handle); | |
if (!m_data) return -1; | |
size = std::min(m_size - m_ptr, size); | |
if (size == 0) return -1; | |
memcpy(dest, m_data + m_ptr, size); | |
m_ptr += size; | |
return size; | |
} | |
ssize_t File::write(const void *dest, size_t size) { | |
abort(); | |
return -1; | |
} | |
int File::getc() { | |
if (m_handle) return fgetc(m_handle); | |
if (!m_data) return -1; | |
if (m_size == m_ptr) return -1; | |
return m_data[m_ptr++]; | |
} | |
bool File::failed() { return !m_handle && !m_data; } | |
bool File::eof() { | |
if (m_handle) return feof(m_handle); | |
if (!m_data) return true; | |
return m_size == m_ptr; | |
} | |
void hexdump(Slice &slice) { | |
const uint8_t *buf = (const uint8_t *)slice.data(); | |
int i, j; | |
for (i = 0; i < slice.size(); i += 16) { | |
printf("%06x: ", i); | |
for (j = 0; j < 16; j++) { | |
if (i + j < slice.size()) { | |
printf("%02x ", buf[i + j]); | |
} else { | |
printf(" "); | |
} | |
} | |
printf(" "); | |
for (j = 0; j < 16; j++) { | |
if (i + j < slice.size()) { | |
printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.'); | |
} | |
} | |
printf("\n"); | |
} | |
} | |
enum class PsyqOpcode : uint8_t { | |
END = 0, | |
BYTES = 2, | |
SWITCH = 6, | |
BSS_ALLOC = 8, | |
RELOCATION = 10, | |
SYMBOL = 12, | |
REFERENCE = 14, | |
SECTION = 16, | |
LOCAL = 18, | |
FILE = 28, | |
PROGRAMTYPE = 46, | |
BSS = 48, | |
}; | |
enum class PsyqReloc : uint8_t { | |
REL32 = 16, | |
REL26 = 74, | |
HI16 = 82, | |
LO16 = 84, | |
}; | |
enum class PsyqExprOpcode : uint8_t { | |
VALUE = 0, | |
REFERENCE = 2, | |
SECTION_BASE = 4, | |
SECTION_START = 12, | |
SECTION_END = 22, | |
ADD = 44, | |
SUB = 46, | |
DIV = 50, | |
EXEC = 54, | |
}; | |
std::string readPsyqString(File *file) { return file->readString(file->byte()); } | |
bool readExpression(File *file, int level = 0) { | |
bool ret = true; | |
uint8_t exprOp = file->read<uint8_t>(); | |
printf(" "); | |
for (int i = 0; i < level; i++) printf(" "); | |
switch (exprOp) { | |
case (uint8_t)PsyqExprOpcode::VALUE: { | |
uint32_t value = file->read<uint32_t>(); | |
printf("Value: %08x\n", value); | |
break; | |
} | |
case (uint8_t)PsyqExprOpcode::REFERENCE: { | |
uint16_t reference = file->read<uint16_t>(); | |
printf("Reference: %i\n", reference); | |
break; | |
} | |
case (uint8_t)PsyqExprOpcode::SECTION_BASE: { | |
uint16_t sectionIndex = file->read<uint16_t>(); | |
printf("Base of section %i\n", sectionIndex); | |
break; | |
} | |
case (uint8_t)PsyqExprOpcode::SECTION_START: { | |
uint16_t sectionIndex = file->read<uint16_t>(); | |
printf("Start of section %i\n", sectionIndex); | |
break; | |
} | |
case (uint8_t)PsyqExprOpcode::SECTION_END: { | |
uint16_t sectionIndex = file->read<uint16_t>(); | |
printf("End of section %i\n", sectionIndex); | |
break; | |
} | |
case (uint8_t)PsyqExprOpcode::ADD: { | |
printf("Add:\n"); | |
ret = ret && readExpression(file, level + 1); | |
ret = ret && readExpression(file, level + 1); | |
break; | |
} | |
case (uint8_t)PsyqExprOpcode::SUB: { | |
printf("Sub:\n"); | |
ret = ret && readExpression(file, level + 1); | |
ret = ret && readExpression(file, level + 1); | |
break; | |
} | |
case (uint8_t)PsyqExprOpcode::DIV: { | |
printf("Div:\n"); | |
ret = ret && readExpression(file, level + 1); | |
ret = ret && readExpression(file, level + 1); | |
break; | |
} | |
case (uint8_t)PsyqExprOpcode::EXEC: { | |
printf("Exec:\n"); | |
ret = ret && readExpression(file, level + 1); | |
ret = ret && readExpression(file, level + 1); | |
break; | |
} | |
default: { | |
printf("Unknown! %i\n", exprOp); | |
return false; | |
} | |
} | |
return ret; | |
} | |
int main(int argc, char **argv) { | |
if (argc != 2) return -1; | |
File *file = new File(argv[1]); | |
if (file->failed()) { | |
delete file; | |
return -1; | |
} | |
printf(":: Reading signature.\n"); | |
std::string signature = file->readString(3); | |
if (signature != "LNK") { | |
printf(" --> Wrong signature.\n"); | |
return -1; | |
} | |
printf(" --> Signature ok.\n"); | |
printf(":: Reading version: "); | |
uint8_t version = file->byte(); | |
printf("%02x\n", version); | |
if (version != 2) { | |
printf(" --> Unknown version.\n"); | |
delete file; | |
return -1; | |
} | |
printf(":: Parsing file...\n"); | |
while (!file->eof()) { | |
uint8_t opcode = file->byte(); | |
printf(" :: Read opcode %02x --> ", opcode); | |
switch (opcode) { | |
case (uint8_t)PsyqOpcode::END: { | |
printf("EOF\n"); | |
delete file; | |
return 0; | |
} | |
case (uint8_t)PsyqOpcode::BYTES: { | |
uint16_t size = file->read<uint16_t>(); | |
printf("Bytes (%04x)\n", size); | |
Slice slice = file->read(size); | |
hexdump(slice); | |
break; | |
} | |
case (uint8_t)PsyqOpcode::SWITCH: { | |
uint16_t sectionIndex = file->read<uint16_t>(); | |
printf("Switch to section %i\n", sectionIndex); | |
break; | |
} | |
case (uint8_t)PsyqOpcode::BSS_ALLOC: { | |
uint32_t size = file->read<uint32_t>(); | |
printf("Allocate %i BSS bytes\n", size); | |
break; | |
} | |
case (uint8_t)PsyqOpcode::RELOCATION: { | |
uint8_t relocType = file->read<uint8_t>(); | |
printf("Relocation %i ", relocType); | |
switch (relocType) { | |
case (uint8_t)PsyqReloc::REL32: { | |
printf("(REL32), "); | |
break; | |
} | |
case (uint8_t)PsyqReloc::REL26: { | |
printf("(REL26), "); | |
break; | |
} | |
case (uint8_t)PsyqReloc::HI16: { | |
printf("(HI16), "); | |
break; | |
} | |
case (uint8_t)PsyqReloc::LO16: { | |
printf("(LO16), "); | |
break; | |
} | |
default: { | |
printf("Unknown!\n"); | |
delete file; | |
return -1; | |
} | |
} | |
uint16_t offset = file->read<uint16_t>(); | |
printf("offset %04x, expression: \n", offset); | |
bool okay = readExpression(file); | |
if (!okay) { | |
delete file; | |
return -1; | |
} | |
break; | |
} | |
case (uint8_t)PsyqOpcode::SYMBOL: { | |
uint16_t symbolIndex = file->read<uint16_t>(); | |
uint16_t sectionIndex = file->read<uint16_t>(); | |
uint32_t offset = file->read<uint32_t>(); | |
std::string name = readPsyqString(file); | |
printf("Symbol: id %i, section %i, offset %08x, name %s\n", symbolIndex, sectionIndex, offset, | |
name.c_str()); | |
break; | |
} | |
case (uint8_t)PsyqOpcode::REFERENCE: { | |
uint16_t symbolIndex = file->read<uint16_t>(); | |
std::string name = readPsyqString(file); | |
printf("Reference: symbol %i, name %s\n", symbolIndex, name.c_str()); | |
break; | |
} | |
case (uint8_t)PsyqOpcode::SECTION: { | |
uint16_t symbolIndex = file->read<uint16_t>(); | |
uint16_t group = file->read<uint16_t>(); | |
uint8_t alignment = file->read<uint8_t>(); | |
std::string name = readPsyqString(file); | |
printf("Section: symbol %i, group %i, alignment %i, name %s\n", symbolIndex, group, alignment, | |
name.c_str()); | |
break; | |
} | |
case (uint8_t)PsyqOpcode::LOCAL: { | |
uint16_t symbolIndex = file->read<uint16_t>(); | |
uint32_t offset = file->read<uint32_t>(); | |
std::string name = readPsyqString(file); | |
printf("Local: symbol %i, offset %08x, name %s\n", symbolIndex, offset, name.c_str()); | |
break; | |
} | |
case (uint8_t)PsyqOpcode::FILE: { | |
uint16_t symbolIndex = file->read<uint16_t>(); | |
std::string name = readPsyqString(file); | |
printf("File: symbol %i, name %s\n", symbolIndex, name.c_str()); | |
break; | |
} | |
case (uint8_t)PsyqOpcode::PROGRAMTYPE: { | |
uint8_t type = file->read<uint8_t>(); | |
printf("Program type %i\n", type); | |
if (type != 7) { | |
delete file; | |
return -1; | |
} | |
break; | |
} | |
case (uint8_t)PsyqOpcode::BSS: { | |
uint16_t symbolIndex = file->read<uint16_t>(); | |
uint16_t sectionIndex = file->read<uint16_t>(); | |
uint32_t size = file->read<uint32_t>(); | |
std::string name = readPsyqString(file); | |
printf("BSS section: symbol %i, section %i, size %08x, name %s\n", symbolIndex, sectionIndex, size, | |
name.c_str()); | |
break; | |
} | |
default: { | |
printf("Unknown %i!\n", opcode); | |
delete file; | |
return -1; | |
} | |
} | |
} | |
delete file; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment