Skip to content

Instantly share code, notes, and snippets.

@nicolasnoble
Last active June 21, 2020 07:10
Show Gist options
  • Save nicolasnoble/9ffa348314df85ee7a4ab5d2bc519eab to your computer and use it in GitHub Desktop.
Save nicolasnoble/9ffa348314df85ee7a4ab5d2bc519eab to your computer and use it in GitHub Desktop.
#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