helper for writing COFF objs and libs

This commit is contained in:
Nikita Smith
2026-04-22 12:27:25 -07:00
parent 4b7dd960d9
commit 86066018e7
+850 -1
View File
@@ -35,6 +35,790 @@ t_push_debug_s_section(COFF_ObjWriter *obj_writer, String8 data)
return coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), PE_DEBUG_SECTION_FLAGS, data); return coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), PE_DEBUG_SECTION_FLAGS, data);
} }
////////////////////////////////
// string -> COFF
typedef enum T_COFF_TokenKind
{
T_COFF_TokenKind_Null,
T_COFF_TokenKind_EOF,
T_COFF_TokenKind_String,
T_COFF_TokenKind_Number,
T_COFF_TokenKind_Ident,
T_COFF_TokenKind_Punct,
} T_COFF_TokenKind;
typedef struct T_COFF_Token
{
T_COFF_TokenKind kind;
String8 string;
U8 punct;
} T_COFF_Token;
typedef struct T_COFF_Parser
{
String8 source;
U64 off;
T_COFF_Token token;
} T_COFF_Parser;
typedef struct T_COFF_IdNode
{
struct T_COFF_IdNode *next;
String8 id;
void *ptr;
} T_COFF_IdNode;
typedef struct T_COFF_IdMap
{
Arena *arena;
T_COFF_IdNode *first;
T_COFF_IdNode *last;
} T_COFF_IdMap;
typedef struct T_COFF_ObjParseCtx
{
Arena *arena;
COFF_ObjWriter *writer;
T_COFF_IdMap sections;
T_COFF_IdMap symbols;
struct T_COFF_PendingRelocNode *pending_reloc_first;
struct T_COFF_PendingRelocNode *pending_reloc_last;
} T_COFF_ObjParseCtx;
typedef struct T_COFF_PendingRelocNode
{
struct T_COFF_PendingRelocNode *next;
T_COFF_Parser parser;
String8 section_id;
} T_COFF_PendingRelocNode;
internal B32
t_coff_is_ident_start(U8 c)
{
return char_is_alpha(c) || c == '_';
}
internal B32
t_coff_is_ident_continue(U8 c)
{
return char_is_alpha(c) || char_is_digit(c, 10) || c == '_';
}
internal void
t_coff_parser_skip_ws(T_COFF_Parser *parser)
{
for (;;) {
while (parser->off < parser->source.size && char_is_space(parser->source.str[parser->off])) {
parser->off += 1;
}
if (parser->off + 1 < parser->source.size &&
parser->source.str[parser->off] == '/' &&
parser->source.str[parser->off + 1] == '/') {
parser->off += 2;
while (parser->off < parser->source.size) {
U8 c = parser->source.str[parser->off];
parser->off += 1;
if (c == '\n') {
break;
}
}
continue;
}
break;
}
}
internal void
t_coff_parser_next(T_COFF_Parser *parser)
{
t_coff_parser_skip_ws(parser);
if (parser->off >= parser->source.size) {
parser->token.kind = T_COFF_TokenKind_EOF;
parser->token.string = str8_zero();
parser->token.punct = 0;
return;
}
U64 start = parser->off;
U8 c = parser->source.str[parser->off];
if (c == '\'') {
parser->off += 1;
start = parser->off;
while (parser->off < parser->source.size && parser->source.str[parser->off] != '\'') {
parser->off += 1;
}
AssertAlways(parser->off < parser->source.size);
parser->token.kind = T_COFF_TokenKind_String;
parser->token.string = str8_substr(parser->source, rng_1u64(start, parser->off));
parser->token.punct = 0;
parser->off += 1;
return;
}
if (char_is_digit(c, 10)) {
parser->off += 1;
while (parser->off < parser->source.size) {
U8 d = parser->source.str[parser->off];
if (!(char_is_alpha(d) || char_is_digit(d, 10) || d == '_')) {
break;
}
parser->off += 1;
}
parser->token.kind = T_COFF_TokenKind_Number;
parser->token.string = str8_substr(parser->source, rng_1u64(start, parser->off));
parser->token.punct = 0;
return;
}
if (t_coff_is_ident_start(c)) {
parser->off += 1;
while (parser->off < parser->source.size && t_coff_is_ident_continue(parser->source.str[parser->off])) {
parser->off += 1;
}
parser->token.kind = T_COFF_TokenKind_Ident;
parser->token.string = str8_substr(parser->source, rng_1u64(start, parser->off));
parser->token.punct = 0;
return;
}
switch (c) {
case '{': case '}': case '[': case ']': case ':': case ',': {
parser->token.kind = T_COFF_TokenKind_Punct;
parser->token.string = str8_zero();
parser->token.punct = c;
parser->off += 1;
} break;
default: {
InvalidPath;
} break;
}
}
internal B32
t_coff_parser_accept_punct(T_COFF_Parser *parser, U8 punct)
{
if (parser->token.kind == T_COFF_TokenKind_Punct && parser->token.punct == punct) {
t_coff_parser_next(parser);
return 1;
}
return 0;
}
internal void
t_coff_parser_require_punct(T_COFF_Parser *parser, U8 punct)
{
AssertAlways(t_coff_parser_accept_punct(parser, punct));
}
internal String8
t_coff_parser_require_name(T_COFF_Parser *parser)
{
AssertAlways(parser->token.kind == T_COFF_TokenKind_String || parser->token.kind == T_COFF_TokenKind_Ident);
String8 result = parser->token.string;
t_coff_parser_next(parser);
return result;
}
internal U64
t_coff_parser_require_u64(T_COFF_Parser *parser)
{
AssertAlways(parser->token.kind == T_COFF_TokenKind_Number || parser->token.kind == T_COFF_TokenKind_String);
U64 result = 0;
AssertAlways(try_u64_from_str8_c_rules(parser->token.string, &result));
t_coff_parser_next(parser);
return result;
}
internal void
t_coff_parse_object_begin(T_COFF_Parser *parser)
{
t_coff_parser_require_punct(parser, '{');
}
internal B32
t_coff_parse_object_next(T_COFF_Parser *parser, String8 *key_out)
{
if (t_coff_parser_accept_punct(parser, '}')) {
return 0;
}
*key_out = t_coff_parser_require_name(parser);
t_coff_parser_require_punct(parser, ':');
return 1;
}
internal void
t_coff_parse_object_finish_field(T_COFF_Parser *parser)
{
if (!(parser->token.kind == T_COFF_TokenKind_Punct && parser->token.punct == '}')) {
t_coff_parser_require_punct(parser, ',');
}
}
internal void
t_coff_parse_array_begin(T_COFF_Parser *parser)
{
t_coff_parser_require_punct(parser, '[');
}
internal B32
t_coff_parse_array_next(T_COFF_Parser *parser)
{
if (t_coff_parser_accept_punct(parser, ']')) {
return 0;
}
return 1;
}
internal void
t_coff_parse_array_finish_item(T_COFF_Parser *parser)
{
if (!(parser->token.kind == T_COFF_TokenKind_Punct && parser->token.punct == ']')) {
t_coff_parser_require_punct(parser, ',');
}
}
internal void
t_coff_skip_value(T_COFF_Parser *parser)
{
if (parser->token.kind == T_COFF_TokenKind_Punct && parser->token.punct == '{') {
t_coff_parse_object_begin(parser);
for (String8 key = {0}; t_coff_parse_object_next(parser, &key);) {
t_coff_skip_value(parser);
t_coff_parse_object_finish_field(parser);
}
} else if (parser->token.kind == T_COFF_TokenKind_Punct && parser->token.punct == '[') {
t_coff_parse_array_begin(parser);
for (;;) {
if (!t_coff_parse_array_next(parser)) break;
t_coff_skip_value(parser);
t_coff_parse_array_finish_item(parser);
}
} else {
t_coff_parser_next(parser);
}
}
internal void
t_coff_id_map_insert(T_COFF_IdMap *map, String8 id, void *ptr)
{
T_COFF_IdNode *node = push_array(map->arena, T_COFF_IdNode, 1);
node->id = push_str8_copy(map->arena, id);
node->ptr = ptr;
SLLQueuePush(map->first, map->last, node);
}
internal void *
t_coff_id_map_find(T_COFF_IdMap *map, String8 id)
{
for (T_COFF_IdNode *node = map->first; node != 0; node = node->next) {
if (str8_match(node->id, id, 0)) {
return node->ptr;
}
}
return 0;
}
internal String8
t_coff_parse_data_bytes(Arena *arena, T_COFF_Parser *parser)
{
T_COFF_Parser probe = *parser;
U64 byte_count = 0;
t_coff_parse_array_begin(&probe);
for (;;) {
if (!t_coff_parse_array_next(&probe)) {
break;
}
U64 value = t_coff_parser_require_u64(&probe);
AssertAlways(value <= max_U8);
byte_count += 1;
t_coff_parse_array_finish_item(&probe);
}
U8 *buffer = push_array(arena, U8, byte_count);
U64 idx = 0;
t_coff_parse_array_begin(parser);
for (;;) {
if (!t_coff_parse_array_next(parser)) {
break;
}
U64 value = t_coff_parser_require_u64(parser);
AssertAlways(value <= max_U8);
buffer[idx] = (U8)value;
idx += 1;
t_coff_parse_array_finish_item(parser);
}
AssertAlways(idx == byte_count);
return str8(buffer, byte_count);
}
internal COFF_SectionFlags
t_coff_section_flag_from_name(String8 string)
{
if (str8_match(string, str8_lit("type_no_pad"), 0)) return COFF_SectionFlag_TypeNoPad;
if (str8_match(string, str8_lit("cnt_code"), 0)) return COFF_SectionFlag_CntCode;
if (str8_match(string, str8_lit("cnt_init_data"), 0)) return COFF_SectionFlag_CntInitializedData;
if (str8_match(string, str8_lit("cnt_uninit_data"), 0)) return COFF_SectionFlag_CntUninitializedData;
if (str8_match(string, str8_lit("lnk_other"), 0)) return COFF_SectionFlag_LnkOther;
if (str8_match(string, str8_lit("lnk_info"), 0)) return COFF_SectionFlag_LnkInfo;
if (str8_match(string, str8_lit("lnk_remove"), 0)) return COFF_SectionFlag_LnkRemove;
if (str8_match(string, str8_lit("lnk_comdat"), 0)) return COFF_SectionFlag_LnkCOMDAT;
if (str8_match(string, str8_lit("gp_rel"), 0)) return COFF_SectionFlag_GpRel;
if (str8_match(string, str8_lit("mem_16bit"), 0)) return COFF_SectionFlag_Mem16Bit;
if (str8_match(string, str8_lit("mem_locked"), 0)) return COFF_SectionFlag_MemLocked;
if (str8_match(string, str8_lit("mem_preload"), 0)) return COFF_SectionFlag_MemPreload;
if (str8_match(string, str8_lit("align_1"), 0)) return COFF_SectionFlag_Align1Bytes;
if (str8_match(string, str8_lit("align_2"), 0)) return COFF_SectionFlag_Align2Bytes;
if (str8_match(string, str8_lit("align_4"), 0)) return COFF_SectionFlag_Align4Bytes;
if (str8_match(string, str8_lit("align_8"), 0)) return COFF_SectionFlag_Align8Bytes;
if (str8_match(string, str8_lit("align_16"), 0)) return COFF_SectionFlag_Align16Bytes;
if (str8_match(string, str8_lit("align_32"), 0)) return COFF_SectionFlag_Align32Bytes;
if (str8_match(string, str8_lit("align_64"), 0)) return COFF_SectionFlag_Align64Bytes;
if (str8_match(string, str8_lit("align_128"), 0)) return COFF_SectionFlag_Align128Bytes;
if (str8_match(string, str8_lit("align_256"), 0)) return COFF_SectionFlag_Align256Bytes;
if (str8_match(string, str8_lit("align_512"), 0)) return COFF_SectionFlag_Align512Bytes;
if (str8_match(string, str8_lit("align_1024"), 0)) return COFF_SectionFlag_Align1024Bytes;
if (str8_match(string, str8_lit("align_2048"), 0)) return COFF_SectionFlag_Align2048Bytes;
if (str8_match(string, str8_lit("align_4096"), 0)) return COFF_SectionFlag_Align4096Bytes;
if (str8_match(string, str8_lit("align_8192"), 0)) return COFF_SectionFlag_Align8192Bytes;
if (str8_match(string, str8_lit("lnk_nreloc_ovfl"), 0)) return COFF_SectionFlag_LnkNRelocOvfl;
if (str8_match(string, str8_lit("mem_discardable"), 0)) return COFF_SectionFlag_MemDiscardable;
if (str8_match(string, str8_lit("mem_not_cached"), 0)) return COFF_SectionFlag_MemNotCached;
if (str8_match(string, str8_lit("mem_not_paged"), 0)) return COFF_SectionFlag_MemNotPaged;
if (str8_match(string, str8_lit("mem_shared"), 0)) return COFF_SectionFlag_MemShared;
if (str8_match(string, str8_lit("mem_execute"), 0)) return COFF_SectionFlag_MemExecute;
if (str8_match(string, str8_lit("mem_read"), 0)) return COFF_SectionFlag_MemRead;
if (str8_match(string, str8_lit("mem_write"), 0)) return COFF_SectionFlag_MemWrite;
InvalidPath;
return 0;
}
internal COFF_SectionFlags
t_coff_parse_section_flags(T_COFF_Parser *parser)
{
if (parser->token.kind == T_COFF_TokenKind_Number) {
return safe_cast_u32(t_coff_parser_require_u64(parser));
}
if (parser->token.kind == T_COFF_TokenKind_String || parser->token.kind == T_COFF_TokenKind_Ident) {
String8 name = t_coff_parser_require_name(parser);
if (str8_match(name, str8_lit("code"), 0)) return PE_TEXT_SECTION_FLAGS | COFF_SectionFlag_Align1Bytes;
if (str8_match(name, str8_lit("data"), 0)) return PE_DATA_SECTION_FLAGS;
if (str8_match(name, str8_lit("rdata"), 0)) return PE_RDATA_SECTION_FLAGS;
if (str8_match(name, str8_lit("bss"), 0)) return PE_BSS_SECTION_FLAGS;
return t_coff_section_flag_from_name(name);
}
COFF_SectionFlags flags = 0;
t_coff_parse_array_begin(parser);
for (;;) {
if (!t_coff_parse_array_next(parser)) {
break;
}
flags |= t_coff_section_flag_from_name(t_coff_parser_require_name(parser));
t_coff_parse_array_finish_item(parser);
}
return flags;
}
internal COFF_ComdatSelectType
t_coff_comdat_select_from_string(String8 string)
{
if (str8_match(string, str8_lit("null"), 0)) return COFF_ComdatSelect_Null;
if (str8_match(string, str8_lit("no_duplicates"), 0)) return COFF_ComdatSelect_NoDuplicates;
if (str8_match(string, str8_lit("any"), 0)) return COFF_ComdatSelect_Any;
if (str8_match(string, str8_lit("same_size"), 0)) return COFF_ComdatSelect_SameSize;
if (str8_match(string, str8_lit("exact_match"), 0)) return COFF_ComdatSelect_ExactMatch;
if (str8_match(string, str8_lit("associative"), 0)) return COFF_ComdatSelect_Associative;
if (str8_match(string, str8_lit("largest"), 0)) return COFF_ComdatSelect_Largest;
InvalidPath;
return 0;
}
internal COFF_WeakExtType
t_coff_weak_type_from_string(String8 string)
{
if (str8_match(string, str8_lit("null"), 0)) return COFF_WeakExt_Null;
if (str8_match(string, str8_lit("no_library"), 0)) return COFF_WeakExt_NoLibrary;
if (str8_match(string, str8_lit("search_library"), 0)) return COFF_WeakExt_SearchLibrary;
if (str8_match(string, str8_lit("search_alias"), 0)) return COFF_WeakExt_SearchAlias;
if (str8_match(string, str8_lit("anti_dependency"), 0)) return COFF_WeakExt_AntiDependency;
InvalidPath;
return 0;
}
internal COFF_SymStorageClass
t_coff_storage_class_from_string(String8 string)
{
if (str8_match(string, str8_lit("external"), 0)) return COFF_SymStorageClass_External;
if (str8_match(string, str8_lit("static"), 0)) return COFF_SymStorageClass_Static;
if (str8_match(string, str8_lit("section"), 0)) return COFF_SymStorageClass_Section;
if (str8_match(string, str8_lit("weak_external"), 0)) return COFF_SymStorageClass_WeakExternal;
InvalidPath;
return 0;
}
internal COFF_ImportByType
t_coff_import_by_from_string(String8 string)
{
if (str8_match(string, str8_lit("ordinal"), 0)) return COFF_ImportBy_Ordinal;
if (str8_match(string, str8_lit("name"), 0)) return COFF_ImportBy_Name;
if (str8_match(string, str8_lit("name_no_prefix"), 0)) return COFF_ImportBy_NameNoPrefix;
if (str8_match(string, str8_lit("undecorate"), 0)) return COFF_ImportBy_Undecorate;
InvalidPath;
return 0;
}
internal COFF_RelocType
t_coff_x64_reloc_type_from_string(String8 string)
{
if (str8_match(string, str8_lit("abs"), 0)) return COFF_Reloc_X64_Abs;
if (str8_match(string, str8_lit("addr64"), 0)) return COFF_Reloc_X64_Addr64;
if (str8_match(string, str8_lit("addr32"), 0)) return COFF_Reloc_X64_Addr32;
if (str8_match(string, str8_lit("addr32nb"), 0)) return COFF_Reloc_X64_Addr32Nb;
if (str8_match(string, str8_lit("rel"), 0) || str8_match(string, str8_lit("rel32"), 0)) return COFF_Reloc_X64_Rel32;
if (str8_match(string, str8_lit("section"), 0)) return COFF_Reloc_X64_Section;
if (str8_match(string, str8_lit("secrel"), 0)) return COFF_Reloc_X64_SecRel;
if (str8_match(string, str8_lit("token"), 0)) return COFF_Reloc_X64_Token;
if (str8_match(string, str8_lit("pair"), 0)) return COFF_Reloc_X64_Pair;
InvalidPath;
return 0;
}
internal COFF_ObjSection *
t_coff_require_section(T_COFF_IdMap *map, String8 id)
{
COFF_ObjSection *section = t_coff_id_map_find(map, id);
AssertAlways(section != 0);
return section;
}
internal COFF_ObjSymbol *
t_coff_require_symbol(T_COFF_IdMap *map, String8 id)
{
COFF_ObjSymbol *symbol = t_coff_id_map_find(map, id);
AssertAlways(symbol != 0);
return symbol;
}
internal void
t_coff_enqueue_section_relocs(T_COFF_ObjParseCtx *ctx, T_COFF_Parser parser, String8 section_id)
{
T_COFF_PendingRelocNode *node = push_array(ctx->arena, T_COFF_PendingRelocNode, 1);
node->parser = parser;
node->section_id = push_str8_copy(ctx->arena, section_id);
SLLQueuePush(ctx->pending_reloc_first, ctx->pending_reloc_last, node);
}
internal void
t_coff_parse_obj_section(T_COFF_ObjParseCtx *ctx, T_COFF_Parser *parser)
{
String8 id = {0};
String8 name = {0};
String8 data = {0};
COFF_SectionFlags flags = 0;
T_COFF_Parser relocs_parser = {0};
B32 has_relocs = 0;
t_coff_parse_object_begin(parser);
for (String8 key = {0}; t_coff_parse_object_next(parser, &key); t_coff_parse_object_finish_field(parser)) {
if (str8_match(key, str8_lit("id"), 0)) { id = t_coff_parser_require_name(parser); }
else if (str8_match(key, str8_lit("name"), 0)) { name = t_coff_parser_require_name(parser); }
else if (str8_match(key, str8_lit("flags"), 0)) { flags = t_coff_parse_section_flags(parser); }
else if (str8_match(key, str8_lit("data"), 0)) { data = t_coff_parse_data_bytes(ctx->arena, parser); }
else if (str8_match(key, str8_lit("relocs"), 0)) {
relocs_parser = *parser;
t_coff_skip_value(parser);
has_relocs = 1;
}
else { InvalidPath; }
}
// must be defined:
AssertAlways(name.size);
AssertAlways(id.size);
t_coff_id_map_insert(&ctx->sections, id, coff_obj_writer_push_section(ctx->writer, name, flags, data));
if (has_relocs) {
t_coff_enqueue_section_relocs(ctx, relocs_parser, id);
}
}
internal void
t_coff_parse_obj_symbol(T_COFF_ObjParseCtx *ctx, T_COFF_Parser *parser, String8 default_kind)
{
String8 kind = default_kind;
String8 name = {0};
String8 section_id = {0};
String8 head = {0};
String8 associate = {0};
String8 tag = {0};
U32 value = 0;
U32 size = 0;
COFF_ComdatSelectType selection = COFF_ComdatSelect_Null;
COFF_WeakExtType characteristics = COFF_WeakExt_Null;
COFF_SymStorageClass storage_class = COFF_SymStorageClass_External;
t_coff_parse_object_begin(parser);
for (String8 key = {0}; t_coff_parse_object_next(parser, &key);) {
if (str8_match(key, str8_lit("kind"), 0 )) { kind = t_coff_parser_require_name(parser); }
else if (str8_match(key, str8_lit("name"), 0 )) { name = t_coff_parser_require_name(parser); }
else if (str8_match(key, str8_lit("value"), 0 )) { value = safe_cast_u32(t_coff_parser_require_u64(parser)); }
else if (str8_match(key, str8_lit("size"), 0 )) { size = safe_cast_u32(t_coff_parser_require_u64(parser)); }
else if (str8_match(key, str8_lit("section"), 0 )) { section_id = t_coff_parser_require_name(parser); }
else if (str8_match(key, str8_lit("head"), 0 )) { head = t_coff_parser_require_name(parser); }
else if (str8_match(key, str8_lit("associate"), 0 )) { associate = t_coff_parser_require_name(parser); }
else if (str8_match(key, str8_lit("selection"), 0 )) { selection = t_coff_comdat_select_from_string(t_coff_parser_require_name(parser)); }
else if (str8_match(key, str8_lit("characteristics"), 0 )) { characteristics = t_coff_weak_type_from_string(t_coff_parser_require_name(parser)); }
else if (str8_match(key, str8_lit("tag"), 0 )) { tag = t_coff_parser_require_name(parser); }
else if (str8_match(key, str8_lit("storage_class"), 0 )) { storage_class = t_coff_storage_class_from_string(t_coff_parser_require_name(parser)); }
else InvalidPath;
t_coff_parse_object_finish_field(parser);
}
COFF_ObjSymbol *symbol = 0;
if (str8_match(kind, str8_lit("extern_func"), 0 )) { symbol = coff_obj_writer_push_symbol_extern_func(ctx->writer, name, value, t_coff_require_section(&ctx->sections, section_id)); }
else if (str8_match(kind, str8_lit("extern"), 0 )) { symbol = coff_obj_writer_push_symbol_extern (ctx->writer, name, value, t_coff_require_section(&ctx->sections, section_id)); }
else if (str8_match(kind, str8_lit("static"), 0 )) { symbol = coff_obj_writer_push_symbol_static (ctx->writer, name, value, t_coff_require_section(&ctx->sections, section_id)); }
else if (str8_match(kind, str8_lit("secdef"), 0 )) { symbol = coff_obj_writer_push_symbol_secdef (ctx->writer, t_coff_require_section(&ctx->sections, section_id), selection); }
else if (str8_match(kind, str8_lit("associative"), 0 )) { symbol = coff_obj_writer_push_symbol_associative(ctx->writer, t_coff_require_section(&ctx->sections, head), t_coff_require_section(&ctx->sections, associate)); }
else if (str8_match(kind, str8_lit("weak"), 0 )) { symbol = coff_obj_writer_push_symbol_weak (ctx->writer, name, characteristics, t_coff_require_symbol(&ctx->symbols, tag)); }
else if (str8_match(kind, str8_lit("abs"), 0 )) { symbol = coff_obj_writer_push_symbol_abs (ctx->writer, name, value, storage_class); }
else if (str8_match(kind, str8_lit("undef"), 0 )) { symbol = coff_obj_writer_push_symbol_undef (ctx->writer, name); }
else if (str8_match(kind, str8_lit("undef_func"), 0 )) { symbol = coff_obj_writer_push_symbol_undef_func (ctx->writer, name); }
else if (str8_match(kind, str8_lit("undef_sect"), 0 )) { symbol = coff_obj_writer_push_symbol_undef_sect (ctx->writer, name, value); }
else if (str8_match(kind, str8_lit("sect"), 0 )) { symbol = coff_obj_writer_push_symbol_sect (ctx->writer, name, t_coff_require_section(&ctx->sections, section_id)); }
else if (str8_match(kind, str8_lit("common"), 0 )) { symbol = coff_obj_writer_push_symbol_common (ctx->writer, name, size); }
else { InvalidPath; }
if (name.size) {
t_coff_id_map_insert(&ctx->symbols, name, symbol);
}
}
internal void
t_coff_parse_obj_reloc(T_COFF_ObjParseCtx *ctx, T_COFF_Parser *parser, String8 default_kind, String8 implicit_section_id)
{
String8 kind = default_kind;
String8 section_id = implicit_section_id;
String8 symbol_id = {0};
String8 type_name = {0};
U32 apply_off = 0;
t_coff_parse_object_begin(parser);
for (String8 key = {0}; t_coff_parse_object_next(parser, &key); t_coff_parse_object_finish_field(parser)) {
if (str8_match(key, str8_lit("kind"), 0)) { kind = t_coff_parser_require_name(parser); }
else if (str8_match(key, str8_lit("section"), 0)) { section_id = t_coff_parser_require_name(parser); }
else if (str8_match(key, str8_lit("symbol"), 0)) { symbol_id = t_coff_parser_require_name(parser); }
else if (str8_match(key, str8_lit("apply_off"), 0)) { apply_off = safe_cast_u32(t_coff_parser_require_u64(parser)); }
else if (str8_match(key, str8_lit("type"), 0)) { type_name = t_coff_parser_require_name(parser); }
else { InvalidPath; }
}
switch (ctx->writer->machine) {
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: {
COFF_ObjSection *section = t_coff_require_section(&ctx->sections, section_id);
COFF_ObjSymbol *symbol = t_coff_require_symbol(&ctx->symbols, symbol_id);
if (str8_match(kind, str8_lit("generic"), 0) || str8_match(kind, str8_lit("reloc"), 0)) {
coff_obj_writer_section_push_reloc(ctx->writer, section, apply_off, symbol, t_coff_x64_reloc_type_from_string(type_name));
} else if (str8_match(kind, str8_lit("rel32"), 0)) {
coff_obj_writer_section_push_reloc_rel32(ctx->writer, section, apply_off, symbol);
} else if (str8_match(kind, str8_lit("addr32"), 0)) {
coff_obj_writer_section_push_reloc_addr32(ctx->writer, section, apply_off, symbol);
} else if (str8_match(kind, str8_lit("addr"), 0) || str8_match(kind, str8_lit("addr64"), 0)) {
coff_obj_writer_section_push_reloc_addr(ctx->writer, section, apply_off, symbol);
} else if (str8_match(kind, str8_lit("voff"), 0) || str8_match(kind, str8_lit("addr32nb"), 0)) {
coff_obj_writer_section_push_reloc_voff(ctx->writer, section, apply_off, symbol);
} else {
InvalidPath;
}
} break;
}
}
internal void
t_coff_parse_obj_directive(T_COFF_ObjParseCtx *ctx, T_COFF_Parser *parser)
{
String8 value = {0};
if (parser->token.kind == T_COFF_TokenKind_String || parser->token.kind == T_COFF_TokenKind_Ident) {
value = t_coff_parser_require_name(parser);
} else {
t_coff_parse_object_begin(parser);
for (;;) {
String8 key = {0};
if (!t_coff_parse_object_next(parser, &key)) break;
if (str8_match(key, str8_lit("value"), 0)) value = t_coff_parser_require_name(parser);
else InvalidPath;
t_coff_parse_object_finish_field(parser);
}
}
coff_obj_writer_push_directive(ctx->writer, value);
}
internal void
t_coff_parse_obj_sections(T_COFF_ObjParseCtx *ctx, T_COFF_Parser *parser)
{
t_coff_parse_array_begin(parser);
for (;;) {
if (!t_coff_parse_array_next(parser)) break;
t_coff_parse_obj_section(ctx, parser);
t_coff_parse_array_finish_item(parser);
}
}
internal void
t_coff_parse_obj_symbols(T_COFF_ObjParseCtx *ctx, T_COFF_Parser *parser)
{
t_coff_parse_array_begin(parser);
for (;;) {
if (!t_coff_parse_array_next(parser)) break;
t_coff_parse_obj_symbol(ctx, parser, str8_zero());
t_coff_parse_array_finish_item(parser);
}
}
internal void
t_coff_parse_obj_directives(T_COFF_ObjParseCtx *ctx, T_COFF_Parser *parser)
{
t_coff_parse_array_begin(parser);
for (;;) {
if (!t_coff_parse_array_next(parser)) break;
t_coff_parse_obj_directive(ctx, parser);
t_coff_parse_array_finish_item(parser);
}
}
internal String8
t_coff_parse_obj(Arena *arena, T_COFF_Parser *parser)
{
T_COFF_ObjParseCtx ctx = {
.arena = arena,
.writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown),
.sections.arena = arena,
.symbols.arena = arena
};
t_coff_parse_object_begin(parser);
for (String8 key = {0}; t_coff_parse_object_next(parser, &key); t_coff_parse_object_finish_field(parser)) {
if (str8_match(key, str8_lit("machine"), 0)) { ctx.writer->machine = coff_machine_from_string(t_coff_parser_require_name(parser)); }
else if (str8_match(key, str8_lit("time_stamp"), 0)) { ctx.writer->time_stamp = safe_cast_u32(t_coff_parser_require_u64(parser)); }
else if (str8_match(key, str8_lit("sections"), 0)) { t_coff_parse_obj_sections(&ctx, parser); }
else if (str8_match(key, str8_lit("symbols"), 0)) { t_coff_parse_obj_symbols(&ctx, parser); }
else if (str8_match(key, str8_lit("directives"), 0)) { t_coff_parse_obj_directives(&ctx, parser); }
else { InvalidPath; }
}
String8 result = coff_obj_writer_serialize(arena, ctx.writer);
coff_obj_writer_release(&ctx.writer);
return result;
}
internal String8
t_coff_parse_lib(Arena *arena, T_COFF_Parser *parser)
{
COFF_TimeStamp time_stamp = 0;
U16 mode = 0;
B32 emit_second_member = 1;
COFF_LibWriter *writer = coff_lib_writer_alloc();
t_coff_parse_object_begin(parser);
for (String8 key = {0}; t_coff_parse_object_next(parser, &key); t_coff_parse_object_finish_field(parser)) {
if (str8_match(key, str8_lit("time_stamp"), 0)) {
time_stamp = safe_cast_u32(t_coff_parser_require_u64(parser));
} else if (str8_match(key, str8_lit("mode"), 0)) {
mode = safe_cast_u16(t_coff_parser_require_u64(parser));
} else if (str8_match(key, str8_lit("emit_second_member"), 0)) {
emit_second_member = !!t_coff_parser_require_u64(parser);
} else if (str8_match(key, str8_lit("import"), 0)) {
String8 dll_name = {0};
String8 name = {0};
COFF_ImportByType import_by = COFF_ImportBy_Name;
U16 hint_or_ordinal = 0;
COFF_ImportType type = COFF_ImportHeader_Code;
t_coff_parse_object_begin(parser);
for (String8 field = {0}; t_coff_parse_object_next(parser, &field); t_coff_parse_object_finish_field(parser)) {
if (str8_match(field, str8_lit("dll_name"), 0)) { dll_name = t_coff_parser_require_name(parser); }
else if (str8_match(field, str8_lit("name"), 0)) { name = t_coff_parser_require_name(parser); }
else if (str8_match(field, str8_lit("import_by"), 0)) { import_by = t_coff_import_by_from_string(t_coff_parser_require_name(parser)); }
else if (str8_match(field, str8_lit("hint_or_ordinal"), 0)) { hint_or_ordinal = safe_cast_u16(t_coff_parser_require_u64(parser)); }
else if (str8_match(field, str8_lit("type"), 0)) { type = coff_import_header_type_from_string(t_coff_parser_require_name(parser)); }
else { InvalidPath; }
}
coff_lib_writer_push_import(writer, COFF_MachineType_X64, time_stamp, dll_name, import_by, name, hint_or_ordinal, type);
} else if (str8_match(key, str8_lit("obj"), 0)) {
String8 path = {0};
String8 data = {0};
t_coff_parse_object_begin(parser);
for (String8 field = {0}; t_coff_parse_object_next(parser, &field); t_coff_parse_object_finish_field(parser)) {
if (str8_match(field, str8_lit("path"), 0)) path = t_coff_parser_require_name(parser);
else if (str8_match(field, str8_lit("data"), 0)) data = t_coff_parse_obj(arena, parser);
else InvalidPath;
}
coff_lib_writer_push_obj(writer, path, data);
} else {
InvalidPath;
}
}
String8 result = coff_lib_writer_serialize(arena, writer, time_stamp, mode, emit_second_member);
coff_lib_writer_release(&writer);
return result;
}
internal String8
t_coff_parse_root(Arena *arena, T_COFF_Parser *parser)
{
String8 root_name = t_coff_parser_require_name(parser);
t_coff_parser_require_punct(parser, ':');
if (str8_match(root_name, str8_lit("obj"), 0)) { return t_coff_parse_obj(arena, parser); }
if (str8_match(root_name, str8_lit("lib"), 0)) { return t_coff_parse_lib(arena, parser); }
return str8_zero();
}
internal String8
t_coff_from_string(Arena *arena, String8 string)
{
T_COFF_Parser parser = { .source = string };
t_coff_parser_next(&parser);
String8 result = t_coff_parse_root(arena, &parser);
if (parser.token.kind == T_COFF_TokenKind_Punct && parser.token.punct == '}') {
t_coff_parser_next(&parser);
}
AssertAlways(parser.token.kind == T_COFF_TokenKind_EOF);
return result;
}
internal String8
t_coff_lib_from_string(Arena *arena, String8 string)
{
Temp scratch = scratch_begin(&arena, 1);
String8List list = {0};
str8_list_pushf(scratch.arena, &list, "'lib': {");
str8_list_push(scratch.arena, &list, string);
str8_list_pushf(scratch.arena, &list, "}");
String8 wrapped = str8_list_join(scratch.arena, &list, &(StringJoin){ .sep = str8_lit("\n") });
String8 result = t_coff_from_string(arena, wrapped);
scratch_end(scratch);
return result;
}
internal String8
t_coff_obj_from_string(Arena *arena, String8 string)
{
Temp scratch = scratch_begin(&arena, 1);
String8List list = {0};
str8_list_pushf(scratch.arena, &list, "'obj': {");
str8_list_push(scratch.arena, &list, string);
str8_list_pushf(scratch.arena, &list, "}");
String8 wrapped = str8_list_join(scratch.arena, &list, &(StringJoin){ .sep = str8_lit("\n") });
String8 result = t_coff_from_string(arena, wrapped);
scratch_end(scratch);
return result;
}
////////////////////////////////
internal String8 internal String8
t_make_sec_defn_obj(Arena *arena, String8 payload) t_make_sec_defn_obj(Arena *arena, String8 payload)
{ {
@@ -3681,6 +4465,72 @@ TEST(second_member_header)
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
} }
#if 0
TEST(multibyte)
{
String8 a_obj = t_coff_obj_from_string(arena, str8_lit("'machine': 'x64', \n\
'sections': [ \n\
{ \n\
'id': 'text', \n\
'name': '.text', \n\
'flags': 'code', \n\
'data': [ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, \n\
0xff, 0x25, 0x00, 0x00, 0x00, 0x00 ], \n\
'relocs': [ \n\
{ 'kind': 'rel32', 'apply_off': 2, 'symbol': 'foo' }, \n\
], \n\
}, \n\
], \n\
'symbols': [ \n\
{ 'kind': 'extern_func', 'name': 'use_imports', 'value': 0, 'section': 'text' }, \n\
{ 'kind': 'undef', 'name': 'foo' }, \n\
]"));
String8 aa_obj = t_coff_obj_from_string(arena, str8_lit("'machine': 'x64', \n\
'sections': [ \n\
{ \n\
'id': 'text', \n\
'name': '.text', \n\
'flags': 'code', \n\
'data': [ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, \n\
0xff, 0x25, 0x00, 0x00, 0x00, 0x00 ], \n\
'relocs': [ \n\
{ 'kind': 'rel32', 'apply_off': 2, 'symbol': '__imp_foo' }, \n\
], \n\
}, \n\
], \n\
'symbols': [ \n\
{ 'kind': 'extern_func', 'name': 'use_imports', 'value': 0, 'section': 'text' }, \n\
{ 'kind': 'undef', 'name': '__imp_foo' }, \n\
]"));
String8 b_lib = t_coff_lib_from_string(arena, str8_lit("'import': { \n\
'dll_name': 'foo.dll', \n\
'name': 'foo', \n\
'import_by': 'name', \n\
'hint_or_ordinal': 0, \n\
'type': 'code', \n\
}"));
String8 c_lib = t_coff_lib_from_string(arena, str8_lit("'import': { \n\
'dll_name': 'foo.dll', \n\
'name': '__imp_foo', \n\
'import_by': 'name', \n\
'hint_or_ordinal': 0, \n\
'type': 'code', \n\
}"));
T_Ok(t_write_file(str8_lit("a.obj"), a_obj));
T_Ok(t_write_file(str8_lit("aa.obj"), aa_obj));
T_Ok(t_write_file(str8_lit("b.lib"), b_lib));
T_Ok(t_write_file(str8_lit("c.lib"), c_lib));
T_Ok(t_write_entry_obj());
t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.lib c.lib entry.obj /include:use_imports");
T_Ok(g_last_exit_code == 0);
}
#endif
TEST(defer_impl_link_to_second_search_pass) TEST(defer_impl_link_to_second_search_pass)
{ {
String8 imp_ref_obj; String8 imp_ref_obj;
@@ -5994,4 +6844,3 @@ TEST(fold_with_largest_align)
#endif #endif
#undef T_Group #undef T_Group