Browse Source

Add tilde

gingerBill 2 years ago
parent
commit
d7af6de9b9
6 changed files with 1823 additions and 0 deletions
  1. 1101 0
      src/tilde/tb.h
  2. BIN
      src/tilde/tb.lib
  3. 330 0
      src/tilde/tb_coff.h
  4. 170 0
      src/tilde/tb_elf.h
  5. 132 0
      src/tilde/tb_formats.h
  6. 90 0
      src/tilde/tb_x64.h

+ 1101 - 0
src/tilde/tb.h

@@ -0,0 +1,1101 @@
+#ifndef TB_CORE_H
+#define TB_CORE_H
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+// https://semver.org/
+#define TB_VERSION_MAJOR 0
+#define TB_VERSION_MINOR 2
+#define TB_VERSION_PATCH 0
+
+#ifdef __cplusplus
+#define TB_EXTERN extern "C"
+#else
+#define TB_EXTERN
+#endif
+
+#ifdef TB_DLL
+#  ifdef TB_IMPORT_DLL
+#    define TB_API TB_EXTERN __declspec(dllimport)
+#  else
+#    define TB_API TB_EXTERN __declspec(dllexport)
+#  endif
+#else
+#  define TB_API TB_EXTERN
+#endif
+
+// These are flags
+typedef enum TB_ArithmeticBehavior {
+    TB_ARITHMATIC_NSW = 1,
+    TB_ARITHMATIC_NUW = 2,
+} TB_ArithmeticBehavior;
+
+typedef enum TB_DebugFormat {
+    TB_DEBUGFMT_NONE,
+
+    TB_DEBUGFMT_DWARF,
+    TB_DEBUGFMT_CODEVIEW,
+
+    TB_DEBUGFMT_COLINPILLED
+} TB_DebugFormat;
+
+typedef enum TB_Arch {
+    TB_ARCH_UNKNOWN,
+
+    TB_ARCH_X86_64,
+    TB_ARCH_AARCH64, // unsupported but planned
+    TB_ARCH_WASM32,
+} TB_Arch;
+
+typedef enum TB_System {
+    TB_SYSTEM_WINDOWS,
+    TB_SYSTEM_LINUX,
+    TB_SYSTEM_MACOS,
+    TB_SYSTEM_ANDROID, // Not supported yet
+    TB_SYSTEM_WEB,
+
+    TB_SYSTEM_MAX,
+} TB_System;
+
+typedef enum TB_WindowsSubsystem {
+    TB_WIN_SUBSYSTEM_UNKNOWN,
+
+    TB_WIN_SUBSYSTEM_WINDOWS,
+    TB_WIN_SUBSYSTEM_CONSOLE,
+    TB_WIN_SUBSYSTEM_EFI_APP,
+} TB_WindowsSubsystem;
+
+typedef enum TB_ABI {
+    // Used on 64bit Windows platforms
+    TB_ABI_WIN64,
+
+    // Used on Mac, BSD and Linux platforms
+    TB_ABI_SYSTEMV,
+} TB_ABI;
+
+typedef enum TB_OutputFlavor {
+    TB_FLAVOR_OBJECT,     // .o  .obj
+    TB_FLAVOR_ASSEMBLY,   // .s  .asm
+    TB_FLAVOR_SHARED,     // .so .dll
+    TB_FLAVOR_STATIC,     // .a  .lib
+    TB_FLAVOR_EXECUTABLE, //     .exe
+} TB_OutputFlavor;
+
+typedef enum TB_CallingConv {
+    TB_CDECL,
+    TB_STDCALL
+} TB_CallingConv;
+
+typedef enum TB_FeatureSet_X64 {
+    TB_FEATURE_X64_SSE3   = (1u << 0u),
+    TB_FEATURE_X64_SSE41  = (1u << 1u),
+    TB_FEATURE_X64_SSE42  = (1u << 2u),
+
+    TB_FEATURE_X64_POPCNT = (1u << 3u),
+    TB_FEATURE_X64_LZCNT  = (1u << 4u),
+
+    TB_FEATURE_X64_CLMUL  = (1u << 5u),
+    TB_FEATURE_X64_F16C   = (1u << 6u),
+
+    TB_FEATURE_X64_BMI1   = (1u << 7u),
+    TB_FEATURE_X64_BMI2   = (1u << 8u),
+
+    TB_FEATURE_X64_AVX    = (1u << 9u),
+    TB_FEATURE_X64_AVX2   = (1u << 10u),
+} TB_FeatureSet_X64;
+
+typedef struct TB_FeatureSet {
+    TB_FeatureSet_X64 x64;
+} TB_FeatureSet;
+
+typedef enum TB_BranchHint {
+    TB_BRANCH_HINT_NONE,
+    TB_BRANCH_HINT_LIKELY,
+    TB_BRANCH_HINT_UNLIKELY
+} TB_BranchHint;
+
+typedef enum TB_Linkage {
+    TB_LINKAGE_PUBLIC,
+    TB_LINKAGE_PRIVATE
+} TB_Linkage;
+
+typedef enum {
+    TB_COMDAT_NONE,
+
+    TB_COMDAT_MATCH_ANY,
+} TB_ComdatType;
+
+typedef enum TB_MemoryOrder {
+    TB_MEM_ORDER_RELAXED,
+    TB_MEM_ORDER_CONSUME,
+    TB_MEM_ORDER_ACQUIRE,
+    TB_MEM_ORDER_RELEASE,
+    TB_MEM_ORDER_ACQ_REL,
+    TB_MEM_ORDER_SEQ_CST,
+} TB_MemoryOrder;
+
+typedef enum TB_ISelMode {
+    // FastISel
+    TB_ISEL_FAST,
+    TB_ISEL_COMPLEX
+} TB_ISelMode;
+
+typedef enum TB_DataTypeEnum {
+    // Integers, note void is an i0 and bool is an i1
+    //   i(0-2047)
+    TB_INT,
+    // Floating point numbers
+    //   f{32,64}
+    TB_FLOAT,
+    // Pointers
+    //   ptr(0-2047)
+    TB_PTR,
+    // Tuples, these cannot be used in memory ops, just accessed via projections
+    TB_TUPLE,
+    // represents control flow as a kind of data
+    TB_CONTROL,
+} TB_DataTypeEnum;
+
+typedef enum TB_FloatFormat {
+    // IEEE 754 floats
+    TB_FLT_32, TB_FLT_64
+} TB_FloatFormat;
+
+typedef union TB_DataType {
+    struct {
+        uint8_t type;
+        // 2^N where N is the width value.
+        // Only integers and floats can be wide.
+        uint8_t width;
+        // for integers it's the bitwidth
+        uint16_t data;
+    };
+    uint32_t raw;
+} TB_DataType;
+
+// classify data types
+#define TB_IS_VOID_TYPE(x)     ((x).type == TB_INT && (x).data == 0)
+#define TB_IS_BOOL_TYPE(x)     ((x).type == TB_INT && (x).data == 1)
+#define TB_IS_INTEGER_TYPE(x)  ((x).type == TB_INT)
+#define TB_IS_FLOAT_TYPE(x)    ((x).type == TB_FLOAT)
+#define TB_IS_POINTER_TYPE(x)  ((x).type == TB_PTR)
+
+// accessors
+#define TB_GET_INT_BITWIDTH(x) ((x).data)
+#define TB_GET_FLOAT_FORMAT(x) ((x).data)
+#define TB_GET_PTR_ADDRSPACE(x) ((x).data)
+
+typedef enum TB_NodeTypeEnum {
+    TB_NULL = 0,
+
+    TB_RETURN, // fn(r: region, x: data)
+
+    // only one per function
+    TB_START, // fn()
+
+    // regions represent the begining of BBs
+    TB_REGION, // fn(preds: []region)
+
+    // projection
+    TB_PROJ,
+
+    // metadata
+    TB_KEEPALIVE,
+
+    TB_CALL,  // normal call
+    TB_SCALL, // system call
+
+    // Managed ops
+    TB_SAFEPOINT,
+
+    // Memory operations
+    TB_STORE, // fn(r: control, addr: data, src: data)
+    TB_MEMCPY,
+    TB_MEMSET,
+
+    // Atomics
+    TB_ATOMIC_TEST_AND_SET,
+    TB_ATOMIC_CLEAR,
+
+    TB_ATOMIC_LOAD,
+    TB_ATOMIC_XCHG,
+    TB_ATOMIC_ADD,
+    TB_ATOMIC_SUB,
+    TB_ATOMIC_AND,
+    TB_ATOMIC_XOR,
+    TB_ATOMIC_OR,
+
+    TB_ATOMIC_CMPXCHG,
+    TB_DEBUGBREAK,
+
+    // Terminators
+    TB_BRANCH,
+    TB_RET,
+    TB_UNREACHABLE,
+    TB_TRAP,
+
+    TB_POISON,
+
+    // Load
+    TB_LOAD,
+
+    // Pointers
+    TB_LOCAL,
+
+    TB_GET_SYMBOL_ADDRESS,
+
+    TB_MEMBER_ACCESS,
+    TB_ARRAY_ACCESS,
+
+    // Immediates
+    TB_INTEGER_CONST,
+    TB_FLOAT32_CONST,
+    TB_FLOAT64_CONST,
+
+    // Conversions
+    TB_TRUNCATE,
+    TB_FLOAT_EXT,
+    TB_SIGN_EXT,
+    TB_ZERO_EXT,
+    TB_INT2PTR,
+    TB_PTR2INT,
+    TB_UINT2FLOAT,
+    TB_FLOAT2UINT,
+    TB_INT2FLOAT,
+    TB_FLOAT2INT,
+    TB_BITCAST,
+
+    // Select
+    TB_SELECT,
+
+    // Bitmagic
+    TB_BSWAP,
+    TB_CLZ,
+    TB_CTZ,
+    TB_POPCNT,
+
+    // Unary operations
+    TB_NOT,
+    TB_NEG,
+
+    // Integer arithmatic
+    TB_AND,
+    TB_OR,
+    TB_XOR,
+    TB_ADD,
+    TB_SUB,
+    TB_MUL,
+
+    TB_SHL,
+    TB_SHR,
+    TB_SAR,
+    TB_UDIV,
+    TB_SDIV,
+    TB_UMOD,
+    TB_SMOD,
+
+    // Float arithmatic
+    TB_FADD,
+    TB_FSUB,
+    TB_FMUL,
+    TB_FDIV,
+
+    // Comparisons
+    TB_CMP_EQ,
+    TB_CMP_NE,
+    TB_CMP_ULT,
+    TB_CMP_ULE,
+    TB_CMP_SLT,
+    TB_CMP_SLE,
+    TB_CMP_FLT,
+    TB_CMP_FLE,
+
+    // Special ops
+    // does full multiplication (64x64=128 and so on) returning
+    // the low and high values in separate projections
+    TB_MULPAIR,
+
+    // PHI
+    TB_PHI, // fn(r: region, x: []data)
+
+    // NOTE(NeGate): only used internally, if you
+    // see one in normal IR things went wrong in
+    // an optimization pass
+    TB_PASS,
+
+    // variadic
+    TB_VA_START,
+
+    // x86 intrinsics
+    TB_X86INTRIN_RDTSC,
+    TB_X86INTRIN_LDMXCSR,
+    TB_X86INTRIN_STMXCSR,
+    TB_X86INTRIN_SQRT,
+    TB_X86INTRIN_RSQRT,
+} TB_NodeTypeEnum;
+typedef uint8_t TB_NodeType;
+
+#define TB_IS_NODE_SIDE_EFFECT(type) ((type) >= TB_LINE_INFO && (type) <= TB_DEBUGBREAK)
+#define TB_IS_NODE_TERMINATOR(type)  ((type) >= TB_BRANCH && (type) <= TB_TRAP)
+
+typedef int TB_Label;
+
+// just represents some region of bytes, usually in file parsing crap
+typedef struct {
+    size_t length;
+    const uint8_t* data;
+} TB_Slice;
+
+// represents byte counts
+typedef uint32_t TB_CharUnits;
+
+typedef unsigned int TB_FileID;
+
+// SO refers to shared objects which mean either shared libraries (.so or .dll)
+// or executables (.exe or ELF executables)
+typedef enum {
+    // exports to the rest of the shared object
+    TB_EXTERNAL_SO_LOCAL,
+
+    // exports outside of the shared object
+    TB_EXTERNAL_SO_EXPORT,
+} TB_ExternalType;
+
+typedef struct TB_Global            TB_Global;
+typedef struct TB_External          TB_External;
+typedef struct TB_Function          TB_Function;
+
+typedef struct TB_Module            TB_Module;
+typedef struct TB_Attrib            TB_Attrib;
+typedef struct TB_DebugType         TB_DebugType;
+typedef struct TB_ModuleSection     TB_ModuleSection;
+typedef struct TB_FunctionPrototype TB_FunctionPrototype;
+
+// Refers generically to objects within a module
+//
+// TB_Function, TB_Global, and TB_External are all subtypes of TB_Symbol
+// and thus are safely allowed to cast into a symbol for operations.
+typedef struct TB_Symbol {
+    enum TB_SymbolTag {
+        TB_SYMBOL_NONE,
+
+        // symbol is dead now
+        TB_SYMBOL_TOMBSTONE,
+
+        TB_SYMBOL_EXTERNAL,
+        TB_SYMBOL_GLOBAL,
+        TB_SYMBOL_FUNCTION,
+
+        TB_SYMBOL_MAX,
+    } tag;
+
+    // refers to the prev or next symbol with the same tag
+    struct TB_Symbol* prev;
+    struct TB_Symbol* next;
+    char* name;
+
+    // It's kinda a weird circular reference but yea
+    TB_Module* module;
+
+    // helpful for sorting and getting consistent builds
+    uint64_t ordinal;
+
+    union {
+        // if we're JITing then this maps to the address of the symbol
+        void* address;
+        size_t symbol_id;
+    };
+
+    // after this point it's tag-specific storage
+} TB_Symbol;
+
+typedef int TB_Reg;
+
+#define TB_NULL_REG NULL
+
+typedef struct TB_Node TB_Node;
+struct TB_Node {
+    TB_NodeType type;
+    TB_DataType dt;
+    uint16_t input_count; // number of node inputs
+    uint16_t extra_count; // number of bytes for extra operand data
+
+    TB_Attrib* first_attrib;
+    TB_Node** inputs;
+
+    char extra[];
+};
+
+#define TB_KILL_NODE(n) ((n)->type = TB_NULL)
+
+// These are the extra data in specific nodes
+#define TB_NODE_GET_EXTRA(n)         ((void*) n->extra)
+#define TB_NODE_GET_EXTRA_T(n, T)    ((T*) (n)->extra)
+#define TB_NODE_SET_EXTRA(n, T, ...) (*((T*) (n)->extra) = (T){ __VA_ARGS__ })
+
+// this represents switch (many targets), if (one target) and goto (only default) logic.
+typedef struct { // TB_BRANCH
+    // avoid empty structs with flexible members
+    int64_t _;
+    int64_t keys[/* input_count - 1 */];
+} TB_NodeBranch;
+
+typedef struct { // TB_PROJ
+    int index;
+} TB_NodeProj;
+
+typedef struct { // TB_INT
+    uint64_t num_words;
+    uint64_t words[];
+} TB_NodeInt;
+
+typedef struct { // any compare operator
+    TB_DataType cmp_dt;
+} TB_NodeCompare;
+
+typedef struct { // any integer binary operator
+    TB_ArithmeticBehavior ab;
+} TB_NodeBinopInt;
+
+typedef struct { // TB_MULPAIR
+    TB_Node *lo, *hi;
+} TB_NodeMulPair;
+
+typedef struct {
+    TB_CharUnits align;
+    bool is_volatile;
+} TB_NodeMemAccess;
+
+typedef struct {
+    TB_CharUnits size, align;
+} TB_NodeLocal;
+
+typedef struct {
+    TB_FileID file;
+    int line;
+} TB_NodeLine;
+
+typedef struct {
+    float value;
+} TB_NodeFloat32;
+
+typedef struct {
+    double value;
+} TB_NodeFloat64;
+
+typedef struct {
+    int64_t stride;
+} TB_NodeArray;
+
+typedef struct {
+    int64_t offset;
+} TB_NodeMember;
+
+typedef struct {
+    TB_Symbol* sym;
+} TB_NodeSymbol;
+
+typedef struct {
+    TB_MemoryOrder order;
+    TB_MemoryOrder order2;
+} TB_NodeAtomic;
+
+typedef struct {
+    TB_FunctionPrototype* proto;
+    TB_Node* projs[];
+} TB_NodeCall;
+
+typedef struct {
+    uint32_t id;
+} TB_NodeSafepoint;
+
+typedef struct {
+    TB_Node* end;
+
+    size_t succ_count;
+    TB_Node** succ;
+
+    size_t proj_count;
+    TB_Node** projs;
+} TB_NodeRegion;
+
+typedef struct TB_MultiOutput {
+    size_t count;
+    union {
+        // count = 1
+        TB_Node* single;
+        // count > 1
+        TB_Node** multiple;
+    };
+} TB_MultiOutput;
+#define TB_MULTI_OUTPUT(o) ((o).count > 1 ? (o).multiple : &(o).single)
+
+typedef struct {
+    int64_t key;
+    TB_Node* value;
+} TB_SwitchEntry;
+
+typedef struct TB_Loop {
+    // refers to another entry in TB_LoopInfo... unless it's -1
+    ptrdiff_t parent_loop;
+
+    TB_Node* header;
+    TB_Node* backedge;
+} TB_Loop;
+
+typedef struct TB_LoopInfo {
+    size_t count;
+    TB_Loop* loops;
+} TB_LoopInfo;
+
+typedef enum {
+    TB_EXECUTABLE_UNKNOWN,
+    TB_EXECUTABLE_PE,
+    TB_EXECUTABLE_ELF,
+} TB_ExecutableType;
+
+typedef struct {
+    TB_Node* node; // type == TB_SAFEPOINT
+    void* userdata;
+
+    uint32_t ip;    // relative to the function body.
+    uint32_t count; // same as node->input_count
+    int32_t values[];
+} TB_Safepoint;
+
+// *******************************
+// Public macros
+// *******************************
+#ifdef __cplusplus
+
+#define TB_TYPE_TUPLE   TB_DataType{ { TB_TUPLE } }
+#define TB_TYPE_CONTROL TB_DataType{ { TB_CONTROL } }
+#define TB_TYPE_VOID    TB_DataType{ { TB_INT,   0, 0 } }
+#define TB_TYPE_I8      TB_DataType{ { TB_INT,   0, 8 } }
+#define TB_TYPE_I16     TB_DataType{ { TB_INT,   0, 16 } }
+#define TB_TYPE_I32     TB_DataType{ { TB_INT,   0, 32 } }
+#define TB_TYPE_I64     TB_DataType{ { TB_INT,   0, 64 } }
+#define TB_TYPE_F32     TB_DataType{ { TB_FLOAT, 0, TB_FLT_32 } }
+#define TB_TYPE_F64     TB_DataType{ { TB_FLOAT, 0, TB_FLT_64 } }
+#define TB_TYPE_BOOL    TB_DataType{ { TB_INT,   0, 1 } }
+#define TB_TYPE_PTR     TB_DataType{ { TB_PTR,   0, 0 } }
+
+#define TB_TYPE_INTN(N) TB_DataType{ { TB_INT,   0, (N) } }
+#define TB_TYPE_PTRN(N) TB_DataType{ { TB_PTR,   0, (N) } }
+
+#else
+
+#define TB_TYPE_TUPLE   (TB_DataType){ { TB_TUPLE } }
+#define TB_TYPE_CONTROL (TB_DataType){ { TB_CONTROL } }
+#define TB_TYPE_VOID    (TB_DataType){ { TB_INT,   0, 0 } }
+#define TB_TYPE_I8      (TB_DataType){ { TB_INT,   0, 8 } }
+#define TB_TYPE_I16     (TB_DataType){ { TB_INT,   0, 16 } }
+#define TB_TYPE_I32     (TB_DataType){ { TB_INT,   0, 32 } }
+#define TB_TYPE_I64     (TB_DataType){ { TB_INT,   0, 64 } }
+#define TB_TYPE_F32     (TB_DataType){ { TB_FLOAT, 0, TB_FLT_32 } }
+#define TB_TYPE_F64     (TB_DataType){ { TB_FLOAT, 0, TB_FLT_64 } }
+#define TB_TYPE_BOOL    (TB_DataType){ { TB_INT,   0, 1 } }
+#define TB_TYPE_PTR     (TB_DataType){ { TB_PTR,   0, 0 } }
+#define TB_TYPE_INTN(N) (TB_DataType){ { TB_INT,  0, (N) } }
+#define TB_TYPE_PTRN(N) (TB_DataType){ { TB_PTR,  0, (N) } }
+
+#endif
+
+typedef void (*TB_PrintCallback)(void* user_data, const char* fmt, ...);
+
+////////////////////////////////
+// Arena
+////////////////////////////////
+// the goal is to move more things to transparent arenas, for now it's just function
+// IR which is a big one if you're interested in freeing them in whatever organization
+// you please.
+
+// allocations can make no guarentees about being sequential
+// tho it would be greatly appreciated at least to some degree.
+typedef struct TB_Arena TB_Arena;
+struct TB_Arena {
+    // alignment never goes past max_align_t
+    void* (*alloc)(TB_Arena* arena, size_t size, size_t align);
+
+    // clearing but we're not done with it yet, cheap
+    void (*clear)(TB_Arena* arena);
+
+    // frees everything within the arena, potentially expensive
+    void (*free)(TB_Arena* arena);
+};
+
+// allocates in 16MiB chunks and does linear allocation in 'em
+TB_Arena* tb_default_arena(void);
+
+////////////////////////////////
+// Module management
+////////////////////////////////
+// Creates a module with the correct target and settings
+TB_API TB_Module* tb_module_create(TB_Arch arch, TB_System sys, const TB_FeatureSet* features, bool is_jit);
+
+// Creates a module but defaults on the architecture and system based on the host machine
+TB_API TB_Module* tb_module_create_for_host(const TB_FeatureSet* features, bool is_jit);
+
+TB_API size_t tb_module_get_function_count(TB_Module* m);
+
+// Frees all resources for the TB_Module and it's functions, globals and
+// compiled code.
+TB_API void tb_module_destroy(TB_Module* m);
+
+// When targetting windows & thread local storage, you'll need to bind a tls index
+// which is usually just a global that the runtime support has initialized, if you
+// dont and the tls_index is used, it'll crash
+TB_API void tb_module_set_tls_index(TB_Module* m, const char* name);
+
+// You don't need to manually call this unless you want to resolve locations before
+// exporting.
+TB_API void tb_module_layout_sections(TB_Module* m);
+
+////////////////////////////////
+// Exporter
+////////////////////////////////
+
+// this is where the machine code and other relevant pieces go.
+typedef struct TB_FunctionOutput TB_FunctionOutput;
+
+// returns NULL if it fails
+TB_API TB_FunctionOutput* tb_module_compile_function(TB_Module* m, TB_Function* f, TB_ISelMode isel_mode);
+
+////////////////////////////////
+// Exporter
+////////////////////////////////
+// Export buffers are generated in chunks because it's easier, usually the
+// chunks are "massive" (representing some connected piece of the buffer)
+// but they don't have to be.
+typedef struct TB_ExportChunk TB_ExportChunk;
+struct TB_ExportChunk {
+    TB_ExportChunk* next;
+    size_t pos, size;
+    uint8_t data[];
+};
+
+typedef struct {
+    size_t total;
+    TB_ExportChunk *head, *tail;
+} TB_ExportBuffer;
+
+TB_API TB_ExportBuffer tb_module_object_export(TB_Module* m, TB_DebugFormat debug_fmt);
+TB_API bool tb_export_buffer_to_file(TB_ExportBuffer buffer, const char* path);
+TB_API void tb_export_buffer_free(TB_ExportBuffer buffer);
+
+////////////////////////////////
+// Linker exporter
+////////////////////////////////
+// This is used to export shared objects or executables
+typedef struct TB_Linker TB_Linker;
+typedef struct TB_LinkerSection TB_LinkerSection;
+typedef struct TB_LinkerSectionPiece TB_LinkerSectionPiece;
+
+typedef struct {
+    enum {
+        TB_LINKER_MSG_NULL,
+
+        // pragma comment(lib, "blah")
+        TB_LINKER_MSG_IMPORT,
+    } tag;
+    union {
+        // pragma lib request
+        TB_Slice import_path;
+    };
+} TB_LinkerMsg;
+
+TB_API TB_ExecutableType tb_system_executable_format(TB_System s);
+
+TB_API TB_Linker* tb_linker_create(TB_ExecutableType type, TB_Arch arch);
+TB_API TB_ExportBuffer tb_linker_export(TB_Linker* l);
+TB_API void tb_linker_destroy(TB_Linker* l);
+
+TB_API bool tb_linker_get_msg(TB_Linker* l, TB_LinkerMsg* msg);
+
+// windows only
+TB_API void tb_linker_set_subsystem(TB_Linker* l, TB_WindowsSubsystem subsystem);
+
+TB_API void tb_linker_set_entrypoint(TB_Linker* l, const char* name);
+
+// Links compiled module into output
+TB_API void tb_linker_append_module(TB_Linker* l, TB_Module* m);
+
+// Adds object file to output
+TB_API void tb_linker_append_object(TB_Linker* l, TB_Slice obj_name, TB_Slice content);
+
+// Adds static library to output
+//   this can include imports (wrappers for DLL symbols) along with
+//   normal sections.
+TB_API void tb_linker_append_library(TB_Linker* l, TB_Slice ar_name, TB_Slice content);
+
+////////////////////////////////
+// JIT compilation
+////////////////////////////////
+typedef struct TB_JITContext TB_JITContext;
+
+// passing 0 to jit_heap_capacity will default to 4MiB
+TB_API TB_JITContext* tb_module_begin_jit(TB_Module* m, size_t jit_heap_capacity);
+TB_API void* tb_module_apply_function(TB_JITContext* jit, TB_Function* f);
+TB_API void* tb_module_apply_global(TB_JITContext* jit, TB_Global* g);
+// fixes page permissions, applies missing relocations
+TB_API void tb_module_ready_jit(TB_JITContext* jit);
+TB_API void tb_module_end_jit(TB_JITContext* jit);
+
+#define TB_FOR_FUNCTIONS(it, module) for (TB_Function* it = tb_first_function(module); it != NULL; it = tb_next_function(it))
+TB_API TB_Function* tb_first_function(TB_Module* m);
+TB_API TB_Function* tb_next_function(TB_Function* f);
+
+#define TB_FOR_EXTERNALS(it, module) for (TB_External* it = tb_first_external(module); it != NULL; it = tb_next_external(it))
+TB_API TB_External* tb_first_external(TB_Module* m);
+TB_API TB_External* tb_next_external(TB_External* e);
+
+// this is used JIT scenarios to tell the compiler what externals map to
+TB_API TB_ExternalType tb_extern_get_type(TB_External* e);
+TB_Global* tb_extern_transmute(TB_External* e, TB_DebugType* dbg_type, TB_Linkage linkage);
+
+TB_API TB_External* tb_extern_create(TB_Module* m, const char* name, TB_ExternalType type);
+TB_API TB_FileID tb_file_create(TB_Module* m, const char* path);
+
+// Called once you're done with TB operations on a thread (or i guess when it's
+// about to be killed :p), not calling it can only result in leaks on that thread
+// and calling it too early will result in TB potentially reallocating it but there's
+// should be no crashes from this, just potential slowdown or higher than expected memory
+// usage.
+TB_API void tb_free_thread_resources(void);
+
+////////////////////////////////
+// Function Prototypes
+////////////////////////////////
+typedef struct TB_PrototypeParam {
+    TB_DataType dt;
+    TB_DebugType* debug_type;
+
+    // does not apply for returns
+    const char* name;
+} TB_PrototypeParam;
+
+struct TB_FunctionPrototype {
+    // header
+    TB_CallingConv call_conv;
+    uint16_t return_count, param_count;
+    bool has_varargs;
+
+    // params are directly followed by returns
+    TB_PrototypeParam params[];
+};
+#define TB_PROTOTYPE_RETURNS(p) ((p)->params + (p)->param_count)
+
+// creates a function prototype used to define a function's parameters and returns.
+//
+// function prototypes do not get freed individually and last for the entire run
+// of the backend, they can also be reused for multiple functions which have
+// matching signatures.
+TB_API TB_FunctionPrototype* tb_prototype_create(TB_Module* m, TB_CallingConv cc, size_t param_count, const TB_PrototypeParam* params, size_t return_count, const TB_PrototypeParam* returns, bool has_varargs);
+
+////////////////////////////////
+// Globals
+////////////////////////////////
+TB_API TB_Global* tb_global_create(TB_Module* m, const char* name, TB_DebugType* dbg_type, TB_Linkage linkage);
+
+// allocate space for the global
+TB_API void tb_global_set_storage(TB_Module* m, TB_ModuleSection* section, TB_Global* global, size_t size, size_t align, size_t max_objects);
+
+// returns a buffer which the user can fill to then have represented in the initializer
+TB_API void* tb_global_add_region(TB_Module* m, TB_Global* global, size_t offset, size_t size);
+
+// places a relocation for a global at offset, the size of the relocation
+// depends on the pointer size
+TB_API void tb_global_add_symbol_reloc(TB_Module* m, TB_Global* global, size_t offset, const TB_Symbol* symbol);
+
+TB_API TB_ModuleSection* tb_module_get_text(TB_Module* m);
+TB_API TB_ModuleSection* tb_module_get_rdata(TB_Module* m);
+TB_API TB_ModuleSection* tb_module_get_data(TB_Module* m);
+TB_API TB_ModuleSection* tb_module_get_tls(TB_Module* m);
+
+////////////////////////////////
+// Function Attributes
+////////////////////////////////
+// These are parts of a function that describe metadata for instructions
+TB_API void tb_function_attrib_variable(TB_Function* f, TB_Node* n, const char* name, TB_DebugType* type);
+
+////////////////////////////////
+// Debug info Generation
+////////////////////////////////
+TB_API TB_DebugType* tb_debug_get_void(TB_Module* m);
+TB_API TB_DebugType* tb_debug_get_bool(TB_Module* m);
+TB_API TB_DebugType* tb_debug_get_integer(TB_Module* m, bool is_signed, int bits);
+TB_API TB_DebugType* tb_debug_get_float(TB_Module* m, TB_FloatFormat fmt);
+TB_API TB_DebugType* tb_debug_create_ptr(TB_Module* m, TB_DebugType* base);
+TB_API TB_DebugType* tb_debug_create_array(TB_Module* m, TB_DebugType* base, size_t count);
+TB_API TB_DebugType* tb_debug_create_struct(TB_Module* m, const char* tag);
+TB_API TB_DebugType* tb_debug_create_union(TB_Module* m, const char* tag);
+TB_API TB_DebugType* tb_debug_create_field(TB_Module* m, TB_DebugType* type, const char* name, TB_CharUnits offset);
+TB_API void tb_debug_complete_record(TB_DebugType* type, TB_DebugType** members, size_t count, TB_CharUnits size, TB_CharUnits align);
+
+////////////////////////////////
+// IR access
+////////////////////////////////
+// it is an index to the input
+#define TB_FOR_INPUT_IN_NODE(it, parent) for (TB_Node **it = parent->inputs, **__end = it + (parent)->input_count; it != __end; it++)
+
+////////////////////////////////
+// Compiled code introspection
+////////////////////////////////
+// this is relative to the start of the function (the start of the prologue)
+TB_API TB_Safepoint* tb_safepoint_get(TB_Function* f, uint32_t relative_ip);
+
+////////////////////////////////
+// Symbols
+////////////////////////////////
+TB_API bool tb_symbol_is_comdat(const TB_Symbol* s);
+
+// returns NULL if the tag doesn't match
+TB_API TB_Function* tb_symbol_as_function(TB_Symbol* s);
+TB_API TB_External* tb_symbol_as_external(TB_Symbol* s);
+TB_API TB_Global* tb_symbol_as_global(TB_Symbol* s);
+
+////////////////////////////////
+// Function IR Generation
+////////////////////////////////
+// the user_data is expected to be a valid FILE*
+TB_API void tb_default_print_callback(void* user_data, const char* fmt, ...);
+
+TB_API void tb_inst_set_location(TB_Function* f, TB_FileID file, int line);
+
+// this only allows for power of two vector types
+TB_API TB_DataType tb_vector_type(TB_DataTypeEnum type, int width);
+
+// if section is NULL, default to .text
+TB_API TB_Function* tb_function_create(TB_Module* m, const char* name, TB_Linkage linkage, TB_ComdatType comdat);
+
+TB_API void* tb_function_get_jit_pos(TB_Function* f);
+
+TB_API void tb_symbol_bind_ptr(TB_Symbol* s, void* ptr);
+TB_API void tb_symbol_set_name(TB_Symbol* s, const char* name);
+TB_API const char* tb_symbol_get_name(TB_Symbol* s);
+
+// if arena is NULL, defaults to module arena which is freed on tb_free_thread_resources
+TB_API void tb_function_set_prototype(TB_Function* f, TB_FunctionPrototype* p, TB_Arena* arena);
+TB_API TB_FunctionPrototype* tb_function_get_prototype(TB_Function* f);
+
+TB_API void tb_function_print(TB_Function* f, TB_PrintCallback callback, void* user_data);
+
+TB_API void tb_inst_set_control(TB_Function* f, TB_Node* control);
+TB_API TB_Node* tb_inst_get_control(TB_Function* f);
+
+TB_API TB_Node* tb_inst_region(TB_Function* f);
+
+TB_API void tb_inst_unreachable(TB_Function* f);
+TB_API void tb_inst_debugbreak(TB_Function* f);
+TB_API void tb_inst_trap(TB_Function* f);
+TB_API void tb_inst_keep_alive(TB_Function* f, TB_Node* src);
+TB_API TB_Node* tb_inst_poison(TB_Function* f);
+
+TB_API TB_Node* tb_inst_param(TB_Function* f, int param_id);
+TB_API TB_Node* tb_inst_param_addr(TB_Function* f, int param_id);
+
+TB_API TB_Node* tb_inst_fpxt(TB_Function* f, TB_Node* src, TB_DataType dt);
+TB_API TB_Node* tb_inst_sxt(TB_Function* f, TB_Node* src, TB_DataType dt);
+TB_API TB_Node* tb_inst_zxt(TB_Function* f, TB_Node* src, TB_DataType dt);
+TB_API TB_Node* tb_inst_trunc(TB_Function* f, TB_Node* src, TB_DataType dt);
+TB_API TB_Node* tb_inst_int2ptr(TB_Function* f, TB_Node* src);
+TB_API TB_Node* tb_inst_ptr2int(TB_Function* f, TB_Node* src, TB_DataType dt);
+TB_API TB_Node* tb_inst_int2float(TB_Function* f, TB_Node* src, TB_DataType dt, bool is_signed);
+TB_API TB_Node* tb_inst_float2int(TB_Function* f, TB_Node* src, TB_DataType dt, bool is_signed);
+TB_API TB_Node* tb_inst_bitcast(TB_Function* f, TB_Node* src, TB_DataType dt);
+
+TB_API TB_Node* tb_inst_local(TB_Function* f, uint32_t size, TB_CharUnits align);
+TB_API TB_Node* tb_inst_load(TB_Function* f, TB_DataType dt, TB_Node* addr, TB_CharUnits align, bool is_volatile);
+TB_API void tb_inst_store(TB_Function* f, TB_DataType dt, TB_Node* addr, TB_Node* val, TB_CharUnits align, bool is_volatile);
+
+TB_API TB_Node* tb_inst_bool(TB_Function* f, bool imm);
+TB_API TB_Node* tb_inst_ptr(TB_Function* f, uint64_t imm);
+TB_API TB_Node* tb_inst_sint(TB_Function* f, TB_DataType dt, int64_t imm);
+TB_API TB_Node* tb_inst_uint(TB_Function* f, TB_DataType dt, uint64_t imm);
+TB_API TB_Node* tb_inst_float32(TB_Function* f, float imm);
+TB_API TB_Node* tb_inst_float64(TB_Function* f, double imm);
+TB_API TB_Node* tb_inst_cstring(TB_Function* f, const char* str);
+TB_API TB_Node* tb_inst_string(TB_Function* f, size_t len, const char* str);
+
+// Broadcasts 'val' across 'count' elements starting 'dst'
+TB_API void tb_inst_memset(TB_Function* f, TB_Node* dst, TB_Node* val, TB_Node* count, TB_CharUnits align, bool is_volatile);
+
+// performs a copy of 'count' elements from one memory location to another
+// both locations cannot overlap.
+TB_API void tb_inst_memcpy(TB_Function* f, TB_Node* dst, TB_Node* src, TB_Node* count, TB_CharUnits align, bool is_volatile);
+
+// result = base + (index * stride)
+TB_API TB_Node* tb_inst_array_access(TB_Function* f, TB_Node* base, TB_Node* index, int64_t stride);
+
+// result = base + offset
+// where base is a pointer
+TB_API TB_Node* tb_inst_member_access(TB_Function* f, TB_Node* base, int64_t offset);
+
+TB_API TB_Node* tb_inst_get_symbol_address(TB_Function* f, TB_Symbol* target);
+
+// Performs a conditional select between two values, if the operation is
+// performed wide then the cond is expected to be the same type as a and b where
+// the condition is resolved as true if the MSB (per component) is 1.
+//
+// result = cond ? a : b
+// a, b must match in type
+TB_API TB_Node* tb_inst_select(TB_Function* f, TB_Node* cond, TB_Node* a, TB_Node* b);
+
+// Integer arithmatic
+TB_API TB_Node* tb_inst_add(TB_Function* f, TB_Node* a, TB_Node* b, TB_ArithmeticBehavior arith_behavior);
+TB_API TB_Node* tb_inst_sub(TB_Function* f, TB_Node* a, TB_Node* b, TB_ArithmeticBehavior arith_behavior);
+TB_API TB_Node* tb_inst_mul(TB_Function* f, TB_Node* a, TB_Node* b, TB_ArithmeticBehavior arith_behavior);
+TB_API TB_Node* tb_inst_div(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness);
+TB_API TB_Node* tb_inst_mod(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness);
+
+// Bitmagic operations
+TB_API TB_Node* tb_inst_bswap(TB_Function* f, TB_Node* n);
+TB_API TB_Node* tb_inst_clz(TB_Function* f, TB_Node* n);
+TB_API TB_Node* tb_inst_ctz(TB_Function* f, TB_Node* n);
+TB_API TB_Node* tb_inst_popcount(TB_Function* f, TB_Node* n);
+
+// Bitwise operations
+TB_API TB_Node* tb_inst_not(TB_Function* f, TB_Node* n);
+TB_API TB_Node* tb_inst_neg(TB_Function* f, TB_Node* n);
+TB_API TB_Node* tb_inst_and(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_or(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_xor(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_sar(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_shl(TB_Function* f, TB_Node* a, TB_Node* b, TB_ArithmeticBehavior arith_behavior);
+TB_API TB_Node* tb_inst_shr(TB_Function* f, TB_Node* a, TB_Node* b);
+
+// Atomics
+// By default you can use TB_MEM_ORDER_SEQ_CST for the memory order to get
+// correct but possibly slower results on certain platforms (those with relaxed
+// memory models).
+TB_API TB_Node* tb_inst_atomic_test_and_set(TB_Function* f, TB_Node* addr, TB_MemoryOrder order);
+TB_API TB_Node* tb_inst_atomic_clear(TB_Function* f, TB_Node* addr, TB_MemoryOrder order);
+
+// Must be aligned to the natural alignment of dt
+TB_API TB_Node* tb_inst_atomic_load(TB_Function* f, TB_Node* addr, TB_DataType dt, TB_MemoryOrder order);
+
+// All atomic operations here return the old value and the operations are
+// performed in the same data type as 'src' with alignment of 'addr' being
+// the natural alignment of 'src'
+TB_API TB_Node* tb_inst_atomic_xchg(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order);
+TB_API TB_Node* tb_inst_atomic_add(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order);
+TB_API TB_Node* tb_inst_atomic_sub(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order);
+TB_API TB_Node* tb_inst_atomic_and(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order);
+TB_API TB_Node* tb_inst_atomic_xor(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order);
+TB_API TB_Node* tb_inst_atomic_or(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order);
+
+// returns old_value from *addr
+TB_API TB_Node* tb_inst_atomic_cmpxchg(TB_Function* f, TB_Node* addr, TB_Node* expected, TB_Node* desired, TB_MemoryOrder succ, TB_MemoryOrder fail);
+
+// Float math
+TB_API TB_Node* tb_inst_fadd(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_fsub(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_fmul(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_fdiv(TB_Function* f, TB_Node* a, TB_Node* b);
+
+// Comparisons
+TB_API TB_Node* tb_inst_cmp_eq(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_cmp_ne(TB_Function* f, TB_Node* a, TB_Node* b);
+
+TB_API TB_Node* tb_inst_cmp_ilt(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness);
+TB_API TB_Node* tb_inst_cmp_ile(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness);
+TB_API TB_Node* tb_inst_cmp_igt(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness);
+TB_API TB_Node* tb_inst_cmp_ige(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness);
+
+TB_API TB_Node* tb_inst_cmp_flt(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_cmp_fle(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_cmp_fgt(TB_Function* f, TB_Node* a, TB_Node* b);
+TB_API TB_Node* tb_inst_cmp_fge(TB_Function* f, TB_Node* a, TB_Node* b);
+
+// General intrinsics
+TB_API TB_Node* tb_inst_va_start(TB_Function* f, TB_Node* a);
+
+// x86 Intrinsics
+TB_API TB_Node* tb_inst_x86_rdtsc(TB_Function* f);
+TB_API TB_Node* tb_inst_x86_ldmxcsr(TB_Function* f, TB_Node* a);
+TB_API TB_Node* tb_inst_x86_stmxcsr(TB_Function* f);
+TB_API TB_Node* tb_inst_x86_sqrt(TB_Function* f, TB_Node* a);
+TB_API TB_Node* tb_inst_x86_rsqrt(TB_Function* f, TB_Node* a);
+
+// Control flow
+TB_API TB_Node* tb_inst_syscall(TB_Function* f, TB_DataType dt, TB_Node* syscall_num, size_t param_count, TB_Node** params);
+TB_API TB_MultiOutput tb_inst_call(TB_Function* f, TB_FunctionPrototype* proto, TB_Node* target, size_t param_count, TB_Node** params);
+
+// Managed
+TB_API TB_Node* tb_inst_safepoint(TB_Function* f, size_t param_count, TB_Node** params);
+
+TB_API TB_Node* tb_inst_incomplete_phi(TB_Function* f, TB_DataType dt, TB_Node* region, size_t preds);
+TB_API bool tb_inst_add_phi_operand(TB_Function* f, TB_Node* phi, TB_Node* region, TB_Node* val);
+TB_API void tb_inst_set_phis_to_region(TB_Function* f, TB_Node* region, size_t phi_count, TB_Node** phis);
+
+TB_API TB_Node* tb_inst_phi2(TB_Function* f, TB_Node* region, TB_Node* a, TB_Node* b);
+TB_API void tb_inst_goto(TB_Function* f, TB_Node* target);
+TB_API void tb_inst_if(TB_Function* f, TB_Node* cond, TB_Node* true_case, TB_Node* false_case);
+TB_API void tb_inst_branch(TB_Function* f, TB_DataType dt, TB_Node* key, TB_Node* default_case, size_t entry_count, const TB_SwitchEntry* keys);
+
+TB_API void tb_inst_ret(TB_Function* f, size_t count, TB_Node** values);
+
+////////////////////////////////
+// Transformation pass library
+////////////////////////////////
+typedef struct TB_OptQueue TB_OptQueue;
+
+TB_API bool tb_optqueue_mark(TB_OptQueue* restrict queue, TB_Node* n, bool mark_kids);
+TB_API void tb_optqueue_kill(TB_OptQueue* restrict queue, TB_Node* n);
+TB_API void tb_optqueue_fill_all(TB_OptQueue* restrict queue, TB_Node* n);
+
+typedef struct TB_Pass {
+    // it's either a module-level pass or function-level
+    bool is_module;
+    const char* name;
+
+    union {
+        bool(*func_run)(TB_Function* f, TB_OptQueue* queue);
+        bool(*mod_run)(TB_Module* m);
+    };
+} TB_Pass;
+
+typedef struct {
+    bool module_level;
+    uint32_t start, end;
+} TB_Passes;
+
+typedef struct TB_PassManager {
+    size_t count;
+    const TB_Pass* passes;
+} TB_PassManager;
+
+// each iteration the user can take the function sequence and apply
+// it to the functions (or module if module_level is true)
+#define TB_DO_PASSES(it, pm, mod) for (TB_Passes it = { 0 }; tb_passes_iter(pm, mod, &it);)
+TB_API bool tb_passes_iter(TB_PassManager* manager, TB_Module* m, TB_Passes* passes);
+
+// Applies optimizations to the entire module
+TB_API void tb_module_optimize(TB_Module* m, size_t pass_count, const TB_Pass* passes[]);
+
+// Applies a set of function level passes onto a function
+TB_API void tb_function_apply_passes(TB_PassManager* manager, TB_Passes passes, TB_Function* f, TB_Arena* arena);
+TB_API void tb_module_apply_passes(TB_PassManager* manager, TB_Passes passes, TB_Module* m, TB_Arena* arena);
+
+TB_API TB_Pass tb_opt_mem2reg(void);
+TB_API TB_Pass tb_opt_identity(void);
+
+////////////////////////////////
+// IR access
+////////////////////////////////
+TB_API const char* tb_node_get_name(TB_Node* n);
+
+TB_API TB_Node* tb_get_parent_region(TB_Node* n);
+TB_API bool tb_has_effects(TB_Node* n);
+
+TB_API bool tb_node_is_constant_non_zero(TB_Node* n);
+TB_API bool tb_node_is_constant_zero(TB_Node* n);
+
+#endif /* TB_CORE_H */

BIN
src/tilde/tb.lib


+ 330 - 0
src/tilde/tb_coff.h

@@ -0,0 +1,330 @@
+// PE/COFF is the executable/object format used by Microsoft.
+#ifndef TB_COFF_H
+#define TB_COFF_H
+
+#include "tb_formats.h"
+
+#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000
+
+#define IMAGE_SYM_CLASS_EXTERNAL      0x0002
+#define IMAGE_SYM_CLASS_STATIC        0x0003
+#define IMAGE_SYM_CLASS_LABEL         0x0006
+#define IMAGE_SYM_CLASS_FILE          0x0067
+#define IMAGE_SYM_CLASS_SECTION       0x0068
+#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 0x0069
+
+#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
+
+#define IMAGE_REL_AMD64_ADDR64   0x0001
+#define IMAGE_REL_AMD64_ADDR32   0x0002
+#define IMAGE_REL_AMD64_ADDR32NB 0x0003
+#define IMAGE_REL_AMD64_REL32    0x0004
+#define IMAGE_REL_AMD64_REL32_1  0x0005
+#define IMAGE_REL_AMD64_REL32_2  0x0006
+#define IMAGE_REL_AMD64_REL32_3  0x0007
+#define IMAGE_REL_AMD64_REL32_4  0x0008
+#define IMAGE_REL_AMD64_REL32_5  0x0009
+#define IMAGE_REL_AMD64_SECTION  0x000A
+#define IMAGE_REL_AMD64_SECREL   0x000B
+
+#define IMAGE_SCN_LNK_REMOVE      0x00000800
+#define IMAGE_SCN_LNK_COMDAT      0x00001000
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
+#define IMAGE_SCN_MEM_EXECUTE     0x20000000
+#define IMAGE_SCN_MEM_READ        0x40000000
+#define IMAGE_SCN_MEM_WRITE       0x80000000
+
+#define IMAGE_SCN_CNT_CODE                   0x00000020  /* Section contains code. */
+#define IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  /* Section contains initialized data. */
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA     0x00000080  /* Section contains uninitialized data. */
+
+#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
+#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
+#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
+#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
+#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
+#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
+#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
+#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
+#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
+#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
+#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
+#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
+#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
+#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
+#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor
+
+#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2
+#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3
+#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10
+
+typedef enum {
+    TB_COFF_SECTION_NO_PAD      = 0x00000008,
+    TB_COFF_SECTION_CODE        = 0x00000020,
+    TB_COFF_SECTION_INIT        = 0x00000040,
+    TB_COFF_SECTION_UNINIT      = 0x00000080,
+    TB_COFF_SECTION_OTHER       = 0x00000100,
+    TB_COFF_SECTION_INFO        = 0x00000200,
+    TB_COFF_SECTION_REMOVE      = 0x00000800,
+    TB_COFF_SECTION_COMDAT      = 0x00001000,
+
+    // this is actually a 4bit field
+    TB_COFF_SECTION_ALIGN       = 0x00F00000,
+
+    // if we have more than 65535 relocations we do this
+    TB_COFF_SECTION_RELOC_OVR   = 0x00F00000,
+
+    // memory flags
+    TB_COFF_SECTION_DISCARDABLE = 0x02000000,
+    TB_COFF_SECTION_NOT_CACHED  = 0x04000000,
+    TB_COFF_SECTION_NOT_PAGED   = 0x08000000,
+    TB_COFF_SECTION_SHARED      = 0x10000000,
+    TB_COFF_SECTION_EXECUTE     = 0x20000000,
+    TB_COFF_SECTION_READ        = 0x40000000,
+    TB_COFF_SECTION_WRITE       = 0x80000000,
+} TB_COFF_SectionFlags;
+
+typedef struct TB_COFF_Parser {
+    // inputs
+    TB_Slice name, file;
+
+    // results
+    size_t section_count;
+    size_t symbol_table, symbol_count;
+
+    // private
+    TB_Slice string_table;
+} TB_COFF_Parser;
+
+// fills the parser with results from the COFF header
+bool tb_coff_parse_init(TB_COFF_Parser* restrict parser);
+bool tb_coff_parse_section(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSection* out_sec);
+
+// how many symbols does this one symbol take up (basically 1 + aux symbols).
+// returns 0 if error.
+size_t tb_coff_parse_symbol(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSymbol* restrict out_sym);
+
+#endif // TB_COFF_H
+
+#ifdef TB_COFF_IMPL
+#include <common.h>
+
+#pragma pack(push, 2)
+typedef struct COFF_SectionHeader {
+    char name[8];
+    union {
+        uint32_t physical_address;
+        uint32_t virtual_size;
+    } misc;
+    uint32_t virtual_address;
+    uint32_t raw_data_size;
+    uint32_t raw_data_pos;
+    uint32_t pointer_to_reloc;
+    uint32_t pointer_to_lineno;
+    uint16_t num_reloc;
+    uint16_t num_lineno;
+    uint32_t characteristics;
+} COFF_SectionHeader;
+
+typedef struct COFF_FileHeader {
+    uint16_t machine;
+    uint16_t section_count;
+    uint32_t timestamp;
+    uint32_t symbol_table;
+    uint32_t symbol_count;
+    uint16_t optional_header_size;
+    uint16_t flags;
+} COFF_FileHeader;
+
+typedef struct COFF_Symbol {
+    union {
+        uint8_t  short_name[8];
+        uint32_t long_name[2];
+    };
+    uint32_t value;
+    int16_t  section_number;
+    uint16_t type;
+    uint8_t  storage_class;
+    uint8_t  aux_symbols_count;
+} COFF_Symbol;
+
+typedef struct COFF_ImageReloc {
+    union {
+        uint32_t VirtualAddress;
+        uint32_t RelocCount;
+    };
+    uint32_t SymbolTableIndex;
+    uint16_t Type;
+} COFF_ImageReloc;
+#pragma pack(pop)
+
+// sanity checks
+static_assert(sizeof(COFF_SectionHeader) == 40, "COFF Section header size != 40 bytes");
+static_assert(sizeof(COFF_ImageReloc) == 10,    "COFF Image Relocation size != 10 bytes");
+static_assert(sizeof(COFF_FileHeader) == 20,    "COFF File header size != 20 bytes");
+static_assert(sizeof(COFF_Symbol) == 18,        "COFF Symbol size != 18 bytes");
+
+bool tb_coff_parse_init(TB_COFF_Parser* restrict parser) {
+    TB_Slice file = parser->file;
+
+    if (file.length < sizeof(COFF_FileHeader)) return false;
+    COFF_FileHeader* header = (COFF_FileHeader*) &parser->file.data[0];
+
+    // locate string table (it spans until the end of the file)
+    size_t string_table_pos = header->symbol_table + (header->symbol_count * sizeof(COFF_Symbol));
+    if (file.length < string_table_pos) return false;
+
+    parser->symbol_count = header->symbol_count;
+    parser->symbol_table = header->symbol_table;
+    parser->section_count = header->section_count;
+    parser->string_table = (TB_Slice){
+        .length = file.length - string_table_pos,
+        .data   = &file.data[string_table_pos]
+    };
+
+    return true;
+}
+
+static long long tb__parse_decimal_int(size_t n, const char* str) {
+    const char* end = &str[n];
+
+    int result = 0;
+    while (str != end) {
+        if (*str < '0' || *str > '9') break;
+
+        result *= 10;
+        result += *str - '0';
+        str++;
+    }
+
+    return result;
+}
+
+bool tb_coff_parse_section(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSection* restrict out_sec) {
+    TB_Slice file = parser->file;
+    size_t section_offset = sizeof(COFF_FileHeader) + (i * sizeof(COFF_SectionHeader));
+
+    if (file.length < section_offset + sizeof(COFF_SectionHeader)) {
+        return false;
+    }
+
+    COFF_SectionHeader* sec = (COFF_SectionHeader*) &file.data[section_offset];
+    *out_sec = (TB_ObjectSection) { .flags = sec->characteristics };
+
+    // Parse string table name stuff
+    if (sec->name[0] == '/') {
+        // string table access
+        int offset = tb__parse_decimal_int(7, &sec->name[1]);
+        if (file.length > offset) {
+            return false;
+        }
+
+        const uint8_t* data = &parser->string_table.data[offset];
+        out_sec->name = (TB_Slice){ strlen((const char*) data), data };
+    } else {
+        // normal inplace string
+        size_t len = strlen(sec->name);
+        out_sec->name = (TB_Slice){ len, (uint8_t*) sec->name };
+    }
+
+    // Parse relocations
+    if (sec->num_reloc > 0) {
+        out_sec->relocation_count = sec->num_reloc;
+        COFF_ImageReloc* src_relocs = (COFF_ImageReloc*) &file.data[sec->pointer_to_reloc];
+
+        TB_ObjectReloc* dst_relocs = tb_platform_heap_alloc(sec->num_reloc * sizeof(TB_ObjectReloc));
+        FOREACH_N(j, 0, sec->num_reloc) {
+            dst_relocs[j] = (TB_ObjectReloc){ 0 };
+            switch (src_relocs[j].Type) {
+                case IMAGE_REL_AMD64_ADDR32NB: dst_relocs[j].type = TB_OBJECT_RELOC_ADDR32NB; break;
+                case IMAGE_REL_AMD64_ADDR32:   dst_relocs[j].type = TB_OBJECT_RELOC_ADDR32; break;
+                case IMAGE_REL_AMD64_ADDR64:   dst_relocs[j].type = TB_OBJECT_RELOC_ADDR64; break;
+                case IMAGE_REL_AMD64_SECREL:   dst_relocs[j].type = TB_OBJECT_RELOC_SECREL; break;
+                case IMAGE_REL_AMD64_SECTION:  dst_relocs[j].type = TB_OBJECT_RELOC_SECTION; break;
+
+                case IMAGE_REL_AMD64_REL32:
+                case IMAGE_REL_AMD64_REL32_1:
+                case IMAGE_REL_AMD64_REL32_2:
+                case IMAGE_REL_AMD64_REL32_3:
+                case IMAGE_REL_AMD64_REL32_4:
+                case IMAGE_REL_AMD64_REL32_5:
+                dst_relocs[j].type = TB_OBJECT_RELOC_REL32;
+                break;
+
+                default: tb_todo();
+            }
+
+            if (src_relocs[j].Type >= IMAGE_REL_AMD64_REL32 && src_relocs[j].Type <= IMAGE_REL_AMD64_REL32_5) {
+                dst_relocs[j].addend = 4 + (src_relocs[j].Type - IMAGE_REL_AMD64_REL32);
+            }
+
+            dst_relocs[j].symbol_index = src_relocs[j].SymbolTableIndex;
+            dst_relocs[j].virtual_address = src_relocs[j].VirtualAddress;
+        }
+
+        out_sec->relocations = dst_relocs;
+    }
+
+    // Parse virtual region
+    out_sec->virtual_address = sec->virtual_address;
+    out_sec->virtual_size = sec->misc.virtual_size;
+
+    // Read raw data (if applies)
+    if (sec->raw_data_size) {
+        assert(sec->raw_data_pos + sec->raw_data_size < file.length);
+        out_sec->raw_data = (TB_Slice){ sec->raw_data_size, &file.data[sec->raw_data_pos] };
+    }
+
+    return true;
+}
+
+TB_ObjectSymbolType classify_symbol_type(uint16_t st_class) {
+    switch (st_class) {
+        case 2:    return TB_OBJECT_SYMBOL_EXTERN;
+        case 3:    return TB_OBJECT_SYMBOL_STATIC;
+        case 6:    return TB_OBJECT_SYMBOL_STATIC;
+        case 0x68: return TB_OBJECT_SYMBOL_SECTION;
+        case 0x69: return TB_OBJECT_SYMBOL_WEAK_EXTERN;
+        default: return TB_OBJECT_SYMBOL_UNKNOWN;
+    }
+}
+
+size_t tb_coff_parse_symbol(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSymbol* restrict out_sym) {
+    TB_Slice file = parser->file;
+    size_t symbol_offset = parser->symbol_table + (i * sizeof(COFF_Symbol));
+
+    if (file.length < symbol_offset + sizeof(COFF_Symbol)) {
+        return 0;
+    }
+
+    COFF_Symbol* sym = (COFF_Symbol*) &file.data[symbol_offset];
+    *out_sym = (TB_ObjectSymbol) {
+        .ordinal = i,
+        .type = classify_symbol_type(sym->storage_class),
+        .section_num = sym->section_number,
+        .value = sym->value
+    };
+
+    // Parse string table name stuff
+    if (sym->long_name[0] == 0) {
+        // string table access (read a cstring)
+        // TODO(NeGate): bounds check this
+        const uint8_t* data = &parser->string_table.data[sym->long_name[1]];
+        out_sym->name = (TB_Slice){ strlen((const char*) data), data };
+    } else {
+        // normal inplace string
+        size_t len = strlen((const char*) sym->short_name);
+        out_sym->name = (TB_Slice){ len, sym->short_name };
+    }
+
+    // TODO(NeGate): Process aux symbols
+    if (sym->aux_symbols_count) {
+        out_sym->extra = &sym[1];
+
+        // FOREACH_N(j, 0, sym->aux_symbols_count) {}
+    }
+
+    return sym->aux_symbols_count + 1;
+}
+
+#endif // TB_COFF_IMPL

+ 170 - 0
src/tilde/tb_elf.h

@@ -0,0 +1,170 @@
+#ifndef TB_ELF_H
+#define TB_ELF_H
+
+#include <stdint.h>
+
+#define TB_EI_MAG0       0
+#define TB_EI_MAG1       1
+#define TB_EI_MAG2       2
+#define TB_EI_MAG3       3
+#define TB_EI_CLASS      4  /* Class of machine. */
+#define TB_EI_DATA       5  /* Data format. */
+#define TB_EI_VERSION    6  /* ELF format version. */
+#define TB_EI_OSABI      7  /* Operating system / ABI identification */
+#define TB_EI_ABIVERSION 8  /* ABI version */
+#define TB_OLD_EI_BRAND  8  /* Start of architecture identification. */
+#define TB_EI_PAD        9  /* Start of padding (per SVR4 ABI). */
+#define TB_EI_NIDENT     16 /* Size of e_ident array. */
+
+/* Values for e_type. */
+#define TB_ET_NONE   0      /* Unknown type. */
+#define TB_ET_REL    1      /* Relocatable. */
+#define TB_ET_EXEC   2      /* Executable. */
+#define TB_ET_DYN    3      /* Shared object. */
+#define TB_ET_CORE   4      /* Core file. */
+#define TB_ET_LOOS   0xfe00 /* First operating system specific. */
+#define TB_ET_HIOS   0xfeff /* Last operating system-specific. */
+#define TB_ET_LOPROC 0xff00 /* First processor-specific. */
+#define TB_ET_HIPROC 0xffff /* Last processor-specific. */
+
+/* Values for e_machine. */
+#define TB_EM_NONE    0   /* Unknown machine. */
+#define TB_EM_X86_64  62  /* Advanced Micro Devices x86-64 */
+#define TB_EM_AARCH64 183 /* AArch64 (64-bit ARM) */
+
+/* sh_type */
+#define TB_SHT_NULL     0 /* inactive */
+#define TB_SHT_PROGBITS 1 /* program defined information */
+#define TB_SHT_SYMTAB   2 /* symbol table section */
+#define TB_SHT_STRTAB   3 /* string table section */
+#define TB_SHT_RELA     4 /* relocation section with addends */
+#define TB_SHT_NOBITS   8 /* no space section */
+
+/* Flags for sh_flags. */
+#define TB_SHF_WRITE            0x1        /* Section contains writable data. */
+#define TB_SHF_ALLOC            0x2        /* Section occupies memory. */
+#define TB_SHF_EXECINSTR        0x4        /* Section contains instructions. */
+#define TB_SHF_MERGE            0x10       /* Section may be merged. */
+#define TB_SHF_STRINGS          0x20       /* Section contains strings. */
+#define TB_SHF_INFO_LINK        0x40       /* sh_info holds section index. */
+#define TB_SHF_LINK_ORDER       0x80       /* Special ordering requirements. */
+#define TB_SHF_OS_NONCONFORMING 0x100      /* OS-specific processing required. */
+#define TB_SHF_GROUP            0x200      /* Member of section group. */
+#define TB_SHF_TLS              0x400      /* Section contains TLS data. */
+#define TB_SHF_MASKOS           0x0ff00000 /* OS-specific semantics. */
+#define TB_SHF_MASKPROC         0xf0000000 /* Processor-specific semantics. */
+
+/* Values for p_flags. */
+#define TB_PF_X		0x1        /* Executable. */
+#define TB_PF_W		0x2        /* Writable. */
+#define TB_PF_R		0x4        /* Readable. */
+#define TB_PF_MASKOS   0x0ff00000 /* Operating system-specific. */
+#define TB_PF_MASKPROC 0xf0000000 /* Processor-specific. */
+
+/* Values for p_type. */
+#define TB_PT_NULL      0	/* Unused entry. */
+#define TB_PT_LOAD      1	/* Loadable segment. */
+#define TB_PT_DYNAMIC   2	/* Dynamic linking information segment. */
+#define TB_PT_INTERP    3	/* Pathname of interpreter. */
+#define TB_PT_NOTE      4	/* Auxiliary information. */
+#define TB_PT_SHLIB     5	/* Reserved (not used). */
+#define TB_PT_PHDR      6	/* Location of program header itself. */
+#define TB_PT_TLS       7	/* Thread local storage segment */
+
+/* Values for relocation */
+typedef enum {
+    TB_ELF_X86_64_NONE     = 0,
+    TB_ELF_X86_64_64       = 1,
+    TB_ELF_X86_64_PC32     = 2,
+    TB_ELF_X86_64_GOT32    = 3,
+    TB_ELF_X86_64_PLT32    = 4,
+    TB_ELF_X86_64_GOTPCREL = 9,
+} TB_ELF_RelocType;
+
+// ST_TYPE
+#define TB_ELF64_STT_NOTYPE  0
+#define TB_ELF64_STT_OBJECT  1
+#define TB_ELF64_STT_FUNC    2
+#define TB_ELF64_STT_SECTION 3
+
+// ST_INFO
+#define TB_ELF64_STB_LOCAL  0
+#define TB_ELF64_STB_GLOBAL 1
+#define TB_ELF64_STB_WEAK   2
+
+/* Macros for accessing the fields of st_info. */
+#define TB_ELF64_ST_BIND(info) ((info) >> 4)
+#define TB_ELF64_ST_TYPE(info) ((info) & 0xf)
+
+#define TB_ELF64_ST_INFO(b, t) (((b) << 4) | ((t) & 0xF))
+
+#define TB_ELF64_R_SYM(i)     ((i) >> 32u)
+#define TB_ELF64_R_TYPE(i)    ((i)&0xffffffffULL)
+#define TB_ELF64_R_INFO(s, t) (((uint64_t)(s) << 32ULL) + ((uint64_t)(t) & 0xffffffffULL))
+
+// http://web.mit.edu/freebsd/head/sys/sys/elf64.h
+// https://cirosantilli.com/elf-hello-world#minimal-elf-file
+// https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
+typedef struct {
+    uint8_t  ident[16];
+    uint16_t type;
+    uint16_t machine;
+    uint32_t version;
+    uint64_t entry;
+    uint64_t phoff;
+    uint64_t shoff;
+    uint32_t flags;
+    uint16_t ehsize;
+    uint16_t phentsize;
+    uint16_t phnum;
+    uint16_t shentsize;
+    uint16_t shnum;
+    uint16_t shstrndx;
+} TB_Elf64_Ehdr;
+
+typedef struct {
+    uint32_t name;
+    uint32_t type;
+    uint64_t flags;
+    uint64_t addr;
+    uint64_t offset;
+    uint64_t size;
+    uint32_t link;
+    uint32_t info;
+    uint64_t addralign;
+    uint64_t entsize;
+} TB_Elf64_Shdr;
+
+// Segment header for ELF64.
+typedef struct {
+    uint32_t type;   // Type of segment
+    uint32_t flags;  // Segment flags
+    uint64_t offset; // File offset where segment is located, in bytes
+    uint64_t vaddr;  // Virtual address of beginning of segment
+    uint64_t paddr;  // Physical addr of beginning of segment (OS-specific)
+    uint64_t filesz; // Num. of bytes in file image of segment (may be zero)
+    uint64_t memsz;  // Num. of bytes in mem image of segment (may be zero)
+    uint64_t align;  // Segment alignment constraint
+} TB_Elf64_Phdr;
+
+typedef struct {
+    uint32_t name;
+    uint8_t  info;
+    uint8_t  other;
+    uint16_t shndx;
+    uint64_t value;
+    uint64_t size;
+} TB_Elf64_Sym;
+
+typedef struct {
+    uint64_t offset;
+    uint64_t info;
+    int64_t  addend;
+} TB_Elf64_Rela;
+
+typedef struct {
+    uint64_t offset;
+    uint64_t info;
+} TB_Elf64_Rel;
+
+#endif /* TB_ELF_H */

+ 132 - 0
src/tilde/tb_formats.h

@@ -0,0 +1,132 @@
+// This handles the generalized executable/object format parsing stuff
+#ifndef TB_OBJECT_H
+#define TB_OBJECT_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+typedef enum {
+    TB_OBJECT_RELOC_NONE, // how?
+
+    // Target independent
+    TB_OBJECT_RELOC_ADDR32,
+    TB_OBJECT_RELOC_ADDR64, // unsupported on 32bit platforms
+    TB_OBJECT_RELOC_SECREL,
+    TB_OBJECT_RELOC_SECTION,
+
+    // COFF only
+    TB_OBJECT_RELOC_ADDR32NB, // Relative virtual address
+
+    // x64 only
+    TB_OBJECT_RELOC_REL32,    // relative 32bit displacement
+
+    // Aarch64 only
+    TB_OBJECT_RELOC_BRANCH26, // 26bit displacement for B and BL instructions
+    TB_OBJECT_RELOC_REL21,    // for ADR instructions
+
+    // TODO(NeGate): fill in the rest of this later
+} TB_ObjectRelocType;
+
+typedef struct {
+    TB_ObjectRelocType type;
+    uint32_t symbol_index;
+    size_t virtual_address;
+    size_t addend;
+} TB_ObjectReloc;
+
+typedef enum {
+    TB_OBJECT_SYMBOL_UNKNOWN,
+    TB_OBJECT_SYMBOL_EXTERN,      // exported
+    TB_OBJECT_SYMBOL_WEAK_EXTERN, // weak
+    TB_OBJECT_SYMBOL_IMPORT,      // forward decl
+    TB_OBJECT_SYMBOL_STATIC,      // local
+    TB_OBJECT_SYMBOL_SECTION,     // local
+} TB_ObjectSymbolType;
+
+typedef struct {
+    TB_ObjectSymbolType type;
+    int section_num;
+
+    uint32_t ordinal;
+    uint32_t value;
+
+    TB_Slice name;
+
+    // for COFF, this is the auxillary
+    void* extra;
+
+    // this is zeroed out by the loader and left for the user to do crap with
+    void* user_data;
+} TB_ObjectSymbol;
+
+typedef struct {
+    TB_Slice name;
+    uint32_t flags;
+
+    size_t virtual_address;
+    size_t virtual_size;
+
+    // You can have a virtual size without having a raw
+    // data size, that's how the BSS section works
+    TB_Slice raw_data;
+
+    size_t relocation_count;
+    TB_ObjectReloc* relocations;
+
+    // this is zeroed out by the loader and left for the user to do crap with
+    void* user_data;
+} TB_ObjectSection;
+
+typedef enum {
+    TB_OBJECT_FILE_UNKNOWN,
+
+    TB_OBJECT_FILE_COFF,
+    TB_OBJECT_FILE_ELF64
+} TB_ObjectFileType;
+
+typedef struct {
+    TB_ObjectFileType type;
+    TB_Arch           arch;
+
+    TB_Slice          name;
+    TB_Slice          ar_name;
+
+    size_t           symbol_count;
+    TB_ObjectSymbol* symbols;
+
+    size_t           section_count;
+    TB_ObjectSection sections[];
+} TB_ObjectFile;
+
+////////////////////////////////
+// Archive parser
+////////////////////////////////
+typedef struct {
+    TB_Slice name;
+
+    // if import_name is empty, we're dealing with an object file
+    TB_Slice import_name;
+    uint16_t ordinal;
+
+    TB_Slice content;
+} TB_ArchiveEntry;
+
+typedef struct {
+    TB_Slice file;
+    size_t pos;
+
+    size_t member_count;
+    uint32_t* members;
+
+    size_t symbol_count;
+    uint16_t* symbols;
+
+    TB_Slice strtbl;
+} TB_ArchiveFileParser;
+
+// We do this to parse the header
+bool tb_archive_parse(TB_Slice file, TB_ArchiveFileParser* restrict out_parser);
+// After that we can enumerate any symbol entries to resolve imports
+size_t tb_archive_parse_entries(TB_ArchiveFileParser* restrict parser, size_t i, size_t count, TB_ArchiveEntry* out_entry);
+
+#endif // TB_OBJECT_H

+ 90 - 0
src/tilde/tb_x64.h

@@ -0,0 +1,90 @@
+#ifndef TB_X64_H
+#define TB_X64_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef enum {
+    // uses xmm registers for the reg array
+    TB_X86_INSTR_XMMREG = (1u << 0u),
+
+    // r/m is a memory operand
+    TB_X86_INSTR_USE_MEMOP = (1u << 1u),
+
+    // r/m is a rip-relative address (TB_X86_INSTR_USE_MEMOP is always set when this is set)
+    TB_X86_INSTR_USE_RIPMEM = (1u << 2u),
+
+    // LOCK prefix is present
+    TB_X86_INSTR_LOCK = (1u << 3u),
+
+    // uses a signed immediate
+    TB_X86_INSTR_IMMEDIATE = (1u << 4u),
+
+    // absolute means it's using the 64bit immediate (cannot be applied while a memory operand is active)
+    TB_X86_INSTR_ABSOLUTE = (1u << 5u),
+
+    // set if the r/m can be found on the right hand side
+    TB_X86_INSTR_DIRECTION = (1u << 6u),
+
+    // uses the second data type because the instruction is weird like MOVSX or MOVZX
+    TB_X86_INSTR_TWO_DATA_TYPES = (1u << 7u)
+} TB_X86_InstFlags;
+
+typedef enum {
+    TB_X86_SEGMENT_DEFAULT = 0,
+
+    TB_X86_SEGMENT_ES, TB_X86_SEGMENT_CS,
+    TB_X86_SEGMENT_SS, TB_X86_SEGMENT_DS,
+    TB_X86_SEGMENT_GS, TB_X86_SEGMENT_FS,
+} TB_X86_Segment;
+
+typedef enum {
+    TB_X86_TYPE_NONE = 0,
+
+    TB_X86_TYPE_BYTE,    // 1
+    TB_X86_TYPE_WORD,    // 2
+    TB_X86_TYPE_DWORD,   // 4
+    TB_X86_TYPE_QWORD,   // 8
+
+    TB_X86_TYPE_PBYTE,   // int8 x 16 = 16
+    TB_X86_TYPE_PWORD,   // int16 x 8 = 16
+    TB_X86_TYPE_PDWORD,  // int32 x 4 = 16
+    TB_X86_TYPE_PQWORD,  // int64 x 2 = 16
+
+    TB_X86_TYPE_SSE_SS,  // float32 x 1 = 4
+    TB_X86_TYPE_SSE_SD,  // float64 x 1 = 8
+    TB_X86_TYPE_SSE_PS,  // float32 x 4 = 16
+    TB_X86_TYPE_SSE_PD,  // float64 x 2 = 16
+
+    TB_X86_TYPE_XMMWORD, // the generic idea of them
+} TB_X86_DataType;
+
+typedef struct {
+    int16_t type;
+
+    // registers (there's 4 max taking up 4bit slots each)
+    uint16_t regs;
+    uint8_t flags;
+
+    // bitpacking amirite
+    TB_X86_DataType data_type  : 4;
+    TB_X86_DataType data_type2 : 4;
+    TB_X86_Segment segment     : 4;
+    uint8_t length             : 4;
+
+    // memory operand
+    //   X86_INSTR_USE_MEMOP
+    int32_t disp;
+
+    // immediate operand
+    //   imm for INSTR_IMMEDIATE
+    //   abs for INSTR_ABSOLUTE
+    union {
+        int32_t  imm;
+        uint64_t abs;
+    };
+} TB_X86_Inst;
+
+TB_X86_Inst tb_x86_disasm(size_t length, const uint8_t data[length]);
+
+#endif /* TB_X64_H */