COFF lib writer

This commit is contained in:
Nikita Smith
2025-05-08 11:09:32 -07:00
committed by Ryan Fleury
parent ccf204d2f1
commit 120829095b
4 changed files with 470 additions and 4 deletions
+26 -2
View File
@@ -302,9 +302,9 @@ coff_ordinal_data_from_hint(Arena *arena, COFF_MachineType machine, U16 hint)
internal String8
coff_make_import_header_by_name(Arena *arena,
String8 dll_name,
COFF_MachineType machine,
COFF_TimeStamp time_stamp,
String8 dll_name,
String8 name,
U16 hint,
COFF_ImportType type)
@@ -346,9 +346,9 @@ coff_make_import_header_by_name(Arena *arena,
internal String8
coff_make_import_header_by_ordinal(Arena *arena,
String8 dll_name,
COFF_MachineType machine,
COFF_TimeStamp time_stamp,
String8 dll_name,
U16 ordinal,
COFF_ImportType type)
{
@@ -930,3 +930,27 @@ coff_import_header_type_from_string(String8 name)
}
return COFF_ImportType_Invalid;
}
internal String8
coff_make_lib_member_header(Arena *arena, String8 name, COFF_TimeStamp time_stamp, U16 user_id, U16 group_id, U16 mode, U32 size)
{
Assert(name.size < 16);
Assert(user_id < 10000);
Assert(group_id < 10000);
Assert(mode < 10000);
Assert(size < 1000000000);
Temp scratch = scratch_begin(&arena, 1);
String8List list = {0};
str8_list_pushf(scratch.arena, &list, "%-16.*s", str8_varg(name));
str8_list_pushf(scratch.arena, &list, "%-12u", time_stamp);
str8_list_pushf(scratch.arena, &list, "%-6u", user_id);
str8_list_pushf(scratch.arena, &list, "%-6u", group_id);
str8_list_pushf(scratch.arena, &list, "%-8u", mode);
str8_list_pushf(scratch.arena, &list, "%-10u", size);
str8_list_pushf(scratch.arena, &list, "`\n");
String8 result = str8_list_join(arena, &list, 0);
Assert(result.size == sizeof(COFF_ArchiveMemberHeader));
scratch_end(scratch);
return result;
}
+1 -2
View File
@@ -612,8 +612,6 @@ internal U64 coff_make_ordinal64(U16 hint);
internal String8 coff_ordinal_data_from_hint(Arena *arena, COFF_MachineType machine, U16 hint);
internal String8 coff_make_import_lookup (Arena *arena, U16 hint, String8 name);
internal String8 coff_make_import_header_by_name (Arena *arena, String8 dll_name, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 name, U16 hint, COFF_ImportType type);
internal String8 coff_make_import_header_by_ordinal(Arena *arena, String8 dll_name, COFF_MachineType machine, COFF_TimeStamp time_stamp, U16 ordinal, COFF_ImportType type);
////////////////////////////////
// Misc
@@ -647,5 +645,6 @@ internal String8 coff_string_from_reloc(COFF_MachineType machine, COFF_RelocType
internal COFF_MachineType coff_machine_from_string(String8 string);
internal COFF_ImportType coff_import_header_type_from_string(String8 name);
internal String8 coff_make_lib_member_header(Arena *arena, String8 name, COFF_TimeStamp time_stamp, U16 user_id, U16 group_id, U16 mode, U32 size);
#endif // COFF_H
+374
View File
@@ -0,0 +1,374 @@
// Copyright (c) 2025 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal COFF_LibWriterSymbolNode *
coff_lib_writer_symbol_list_push(Arena *arena, COFF_LibWriterSymbolList *list, COFF_LibWriterSymbol symbol)
{
COFF_LibWriterSymbolNode *node = push_array_no_zero(arena, COFF_LibWriterSymbolNode, 1);
node->next = 0;
node->data = symbol;
SLLQueuePush(list->first, list->last, node);
list->count += 1;
return node;
}
internal COFF_LibWriterMemberNode *
coff_lib_writer_member_list_push(Arena *arena, COFF_LibWriterMemberList *list, COFF_LibWriterMember member)
{
COFF_LibWriterMemberNode *node = push_array_no_zero(arena, COFF_LibWriterMemberNode, 1);
node->next = 0;
node->data = member;
SLLQueuePush(list->first, list->last, node);
list->count += 1;
return node;
}
internal COFF_LibWriterSymbol *
coff_lib_writer_symbol_array_from_list(Arena *arena, COFF_LibWriterSymbolList list)
{
COFF_LibWriterSymbol *arr = push_array_no_zero(arena, COFF_LibWriterSymbol, list.count + 2);
COFF_LibWriterSymbol *ptr = arr + 1;
for (COFF_LibWriterSymbolNode *i = list.first; i != 0; i = i->next, ptr += 1) {
ptr->name = push_str8_copy(arena, i->data.name);
ptr->member_idx = i->data.member_idx;
}
MemoryZeroStruct(&arr[0]);
MemoryZeroStruct(&arr[list.count+1]);
return arr;
}
internal COFF_LibWriterMember *
coff_lib_writer_member_array_from_list(Arena *arena, COFF_LibWriterMemberList list)
{
COFF_LibWriterMember *arr = push_array_no_zero(arena, COFF_LibWriterMember, list.count);
COFF_LibWriterMember *ptr = arr;
for (COFF_LibWriterMemberNode *i = list.first; i != 0; i = i->next, ptr += 1) {
ptr->name = push_str8_copy(arena, i->data.name);
ptr->data = push_str8_copy(arena, i->data.data);
}
return arr;
}
internal int
coff_lib_writer_symbol_name_compar(const void *raw_a, const void *raw_b)
{
const COFF_LibWriterSymbol *sa = raw_a;
const COFF_LibWriterSymbol *sb = raw_b;
return str8_compar_case_sensitive(&sa->name, &sb->name);
}
internal int
coff_lib_writer_symbol_is_before(void *raw_a, void *raw_b)
{
int compar = coff_lib_writer_symbol_name_compar(raw_a, raw_b);
return compar < 0;
}
internal void
coff_lib_writer_symbol_array_sort(COFF_LibWriterSymbol *arr, U64 count)
{
Assert(count >= 2);
radsort(arr + 1, count - 2, coff_lib_writer_symbol_is_before);
}
internal COFF_LibWriter *
coff_lib_writer_alloc(void)
{
Arena *arena = arena_alloc();
COFF_LibWriter *writer = push_array(arena, COFF_LibWriter, 1);
writer->arena = arena;
return writer;
}
internal void
coff_lib_writer_release(COFF_LibWriter **writer_ptr)
{
arena_release((*writer_ptr)->arena);
*writer_ptr = 0;
}
internal void
coff_lib_writer_push_obj(COFF_LibWriter *writer, String8 obj_path, String8 obj_data)
{
U64 member_idx = writer->member_list.count;
// push obj member
COFF_LibWriterMember member = {0};
member.name = obj_path;
member.data = obj_data;
coff_lib_writer_member_list_push(writer->arena, &writer->member_list, member);
// push external symbols
{
COFF_FileHeaderInfo obj_header = coff_file_header_info_from_data(obj_data);
String8 string_table = str8_substr(obj_data, obj_header.string_table_range);
String8 symbol_table = str8_substr(obj_data, obj_header.symbol_table_range);
COFF_ParsedSymbol symbol;
for (U64 symbol_idx = 0; symbol_idx < obj_header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) {
void *symbol_ptr;
if (obj_header.is_big_obj) {
symbol_ptr = &((COFF_Symbol32 *)symbol_table.str)[symbol_idx];
symbol = coff_parse_symbol32(string_table, symbol_ptr);
} else {
symbol_ptr = &((COFF_Symbol16 *)symbol_table.str)[symbol_idx];
symbol = coff_parse_symbol16(string_table, symbol_ptr);
}
COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class);
if (interp == COFF_SymbolValueInterp_Regular) {
if (symbol.storage_class == COFF_SymStorageClass_External) {
COFF_LibWriterSymbol lib_symbol = {0};
lib_symbol.name = symbol.name;
lib_symbol.member_idx = member_idx;
coff_lib_writer_symbol_list_push(writer->arena, &writer->symbol_list, lib_symbol);
}
}
}
}
}
internal void
coff_lib_writer_push_export(COFF_LibWriter *writer, String8 raw_import_header)
{
U64 member_idx = writer->member_list.count;
COFF_ParsedArchiveImportHeader import_header = coff_archive_import_from_data(raw_import_header);
// push import member
COFF_LibWriterMember member = {0};
member.name = import_header.dll_name;
member.data = raw_import_header;
coff_lib_writer_member_list_push(writer->arena, &writer->member_list, member);
switch (import_header.type) {
case COFF_ImportHeader_Code: {
COFF_LibWriterSymbol def_symbol = {0};
def_symbol.name = push_str8_copy(writer->arena, import_header.func_name);
def_symbol.member_idx = member_idx;
coff_lib_writer_symbol_list_push(writer->arena, &writer->symbol_list, def_symbol);
} break;
case COFF_ImportHeader_Data: {
COFF_LibWriterSymbol imp_symbol = {0};
imp_symbol.name = push_str8f(writer->arena, "__imp_%S", import_header.func_name);
imp_symbol.member_idx = member_idx;
coff_lib_writer_symbol_list_push(writer->arena, &writer->symbol_list, imp_symbol);
} break;
case COFF_ImportHeader_Const: {
NotImplemented;
} break;
default: { InvalidPath; } break;
}
}
internal void
coff_lib_writer_push_export_by_ordinal(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportType import_type, U16 ordinal)
{
String8 import_header = coff_make_import_header_by_ordinal(lib_writer->arena, machine, time_stamp, dll_name, ordinal, import_type);
coff_lib_writer_push_export(lib_writer, import_header);
}
internal void
coff_lib_writer_push_export_by_name(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportType import_type, String8 name, U16 hint)
{
String8 import_header = coff_make_import_header_by_name(lib_writer->arena, machine, time_stamp, dll_name, name, hint, import_type);
coff_lib_writer_push_export(lib_writer, import_header);
}
internal String8List
coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeStamp time_stamp, U16 mode, B32 emit_second_member)
{
Temp scratch = scratch_begin(&arena, 1);
// symbol & member lists -> arrays
U64 symbols_count;
COFF_LibWriterSymbol *symbols;
U64 member_count;
COFF_LibWriterMember *member_array;
{
U64 symbols_count_with_null = lib_writer->symbol_list.count + 2;
COFF_LibWriterSymbol *symbols_with_null = coff_lib_writer_symbol_array_from_list(scratch.arena, lib_writer->symbol_list);
coff_lib_writer_symbol_array_sort(symbols_with_null, symbols_count_with_null);
symbols_count = symbols_count_with_null - 2;
symbols = symbols_with_null + 1;
member_count = lib_writer->member_list.count;
member_array = coff_lib_writer_member_array_from_list(scratch.arena, lib_writer->member_list);
}
// serialize members
U64 *member_offsets = push_array_no_zero(scratch.arena, U64, member_count);
String8List long_names_list = {0};
String8List member_data_list = {0};
{
HashTable *name_ht = hash_table_init(scratch.arena, 1024);
for (U64 member_idx = 0; member_idx < member_count; member_idx += 1) {
COFF_LibWriterMember *member = &member_array[member_idx];
// make member name
String8 name;
U64 name_with_slash_size = member->name.size + 1;
if (name_with_slash_size > COFF_Archive_MaxShortNameSize) {
// have we seen this member name before?
KeyValuePair *is_present = hash_table_search_string(name_ht, member->name);
if (is_present) {
name = is_present->value_string;
} else {
name = push_str8f(scratch.arena, "/%u", long_names_list.total_size);
str8_list_pushf(scratch.arena, &long_names_list, "%S/\n", member->name);
hash_table_push_string_string(scratch.arena, name_ht, member->name, name);
}
} else {
name = push_str8f(scratch.arena, "%S/", member->name);
}
member_offsets[member_idx] = member_data_list.total_size;
String8 member_data = member->data;
String8 member_header = coff_make_lib_member_header(arena, name, time_stamp, 0, 0, mode, member_data.size);
str8_list_push(arena, &member_data_list, member_header);
str8_list_push(arena, &member_data_list, member_data);
{
U64 pad_size = AlignPadPow2(member_data_list.total_size, COFF_Archive_MemberAlign);
U8 *pad = push_array(arena, U8, pad_size);
str8_list_push(arena, &member_data_list, str8(pad, pad_size));
}
}
}
// long names member
if (long_names_list.total_size) {
String8 header = coff_make_lib_member_header(arena, str8_lit("//"), time_stamp, 0, 0, mode, long_names_list.total_size);
String8 data = str8_list_join(arena, &long_names_list, 0);
U64 member_offset = member_data_list.total_size + data.size + header.size;
{
U64 pad_size = AlignPadPow2(member_offset, COFF_Archive_MemberAlign);
U8 *pad = push_array(arena, U8, pad_size);
str8_list_push_front(arena, &member_data_list, str8(pad, pad_size));
}
str8_list_push_front(arena, &member_data_list, data);
str8_list_push_front(arena, &member_data_list, header);
}
// compute size for symbol string table
U32 name_buffer_size = 0;
for (COFF_LibWriterSymbol *ptr = &symbols[0], *opl = ptr + symbols_count; ptr < opl; ptr += 1) {
name_buffer_size += ptr->name.size;
name_buffer_size += 1; // null
}
// write symbol name buffer
U8 *name_buffer = push_array_no_zero(scratch.arena, U8, name_buffer_size);
{
U64 name_cursor = 0;
for (COFF_LibWriterSymbol *ptr = &symbols[0], *opl = ptr + symbols_count; ptr < opl; ptr += 1) {
MemoryCopy(name_buffer + name_cursor, ptr->name.str, ptr->name.size);
name_buffer[name_cursor + ptr->name.size] = '\0';
name_cursor += ptr->name.size + 1;
}
}
U64 members_base_offset;
{
U64 sizeof_first_header = sizeof(COFF_ArchiveMemberHeader) + sizeof(U32) + sizeof(U32) * symbols_count + name_buffer_size;
U64 sizeof_second_header = sizeof(COFF_ArchiveMemberHeader) + sizeof(U32) + sizeof(U32) * member_count + sizeof(U32) + sizeof(U16) * symbols_count + name_buffer_size;
U64 sizeof_long_names = sizeof(COFF_ArchiveMemberHeader) + long_names_list.total_size;
sizeof_first_header = AlignPow2(sizeof_first_header, COFF_Archive_MemberAlign);
sizeof_second_header = AlignPow2(sizeof_second_header, COFF_Archive_MemberAlign);
sizeof_long_names = AlignPow2(sizeof_long_names, COFF_Archive_MemberAlign);
members_base_offset = sizeof(g_coff_archive_sig);
members_base_offset += sizeof_first_header;
if (emit_second_member) {
members_base_offset += sizeof_second_header;
}
if (long_names_list.total_size) {
members_base_offset += sizeof_long_names;
}
}
// second linker member
if (emit_second_member) {
U32 member_count32 = safe_cast_u32(member_count);
U32 symbol_count32 = safe_cast_u32(symbols_count);
U32 *member_off32_arr = push_array_no_zero(scratch.arena, U32, member_count);
U16 *member_idx16_arr = push_array_no_zero(scratch.arena, U16, symbols_count);
// write member offset array
for (U64 member_idx = 0; member_idx < member_count; member_idx += 1) {
U64 member_offset = members_base_offset + member_offsets[member_idx];
U32 member_off32 = safe_cast_u32(member_offset);
member_off32_arr[member_idx] = member_off32;
}
// write member offset indices for each symbol
for (U64 symbol_idx = 0; symbol_idx < symbols_count; symbol_idx += 1) {
// member offset indices are 1-based
U64 member_idx = symbols[symbol_idx].member_idx + 1;
U16 member_idx16 = safe_cast_u16(member_idx);
member_idx16_arr[symbol_idx] = member_idx16;
}
// layout second member data
String8List second_member_data_list = {0};
str8_list_push(scratch.arena, &second_member_data_list, str8_struct(&member_count32));
str8_list_push(scratch.arena, &second_member_data_list, str8_array(member_off32_arr, member_count));
str8_list_push(scratch.arena, &second_member_data_list, str8_struct(&symbol_count32));
str8_list_push(scratch.arena, &second_member_data_list, str8_array(member_idx16_arr, symbols_count));
str8_list_push(scratch.arena, &second_member_data_list, str8(name_buffer, name_buffer_size));
String8 member_data = str8_list_join(arena, &second_member_data_list, 0);
String8 member_header = coff_make_lib_member_header(arena, str8_lit("/"), time_stamp, 0, 0, mode, member_data.size);
U64 member_offset = member_data_list.total_size + member_data.size + member_header.size;
{
U64 pad_size = AlignPadPow2(member_offset, COFF_Archive_MemberAlign);
U8 *pad = push_array(arena, U8, pad_size);
str8_list_push_front(arena, &member_data_list, str8(pad, pad_size));
}
str8_list_push_front(arena, &member_data_list, member_data);
str8_list_push_front(arena, &member_data_list, member_header);
}
// first linker member (obsolete, but kept for compatability reasons)
{
U32 symbol_count_be = from_be_u32(symbols_count);
U32 *member_off32_arr = push_array_no_zero(scratch.arena, U32, symbols_count);
for (U64 symbol_idx = 0; symbol_idx < symbols_count; symbol_idx += 1) {
COFF_LibWriterSymbol *symbol = &symbols[symbol_idx];
// write big endian member offset
U64 member_offset = members_base_offset + member_offsets[symbol->member_idx];
U32 member_off32 = from_be_u32(safe_cast_u32(member_offset));
member_off32_arr[symbol_idx] = member_off32;
}
// layout first member data
String8List first_member_data_list = {0};
str8_list_push(scratch.arena, &first_member_data_list, str8_struct(&symbol_count_be));
str8_list_push(scratch.arena, &first_member_data_list, str8_array(member_off32_arr, symbols_count));
str8_list_push(scratch.arena, &first_member_data_list, str8(name_buffer, name_buffer_size));
String8 member_data = str8_list_join(arena, &first_member_data_list, 0);
String8 member_header = coff_make_lib_member_header(arena, str8_lit("/"), time_stamp, 0, 0, mode, member_data.size);
U64 member_offset = sizeof(g_coff_archive_sig) + member_header.size + member_data.size;
{
U64 pad_size = AlignPadPow2(member_offset, COFF_Archive_MemberAlign);
U8 *pad = push_array(arena, U8, pad_size);
str8_list_push_front(arena, &member_data_list, str8(pad, pad_size));
}
str8_list_push_front(arena, &member_data_list, member_data);
str8_list_push_front(arena, &member_data_list, member_header);
}
// archive signature
str8_list_push_front(arena, &member_data_list, str8_struct(&g_coff_archive_sig));
scratch_end(scratch);
return member_data_list;
}
+69
View File
@@ -0,0 +1,69 @@
// Copyright (c) 2025 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef COFF_LIB_WRITER_H
#define COFF_LIB_WRITER_H
typedef struct COFF_LibWriterMember
{
String8 name;
String8 data;
} COFF_LibWriterMember;
typedef struct COFF_LibWriterMemberNode
{
struct COFF_LibWriterMemberNode *next;
COFF_LibWriterMember data;
} COFF_LibWriterMemberNode;
typedef struct COFF_LibWriterMemberList
{
U64 count;
COFF_LibWriterMemberNode *first;
COFF_LibWriterMemberNode *last;
} COFF_LibWriterMemberList;
typedef struct COFF_LibWriterSymbol
{
String8 name;
U64 member_idx;
} COFF_LibWriterSymbol;
typedef struct COFF_LibWriterSymbolNode
{
struct COFF_LibWriterSymbolNode *next;
COFF_LibWriterSymbol data;
} COFF_LibWriterSymbolNode;
typedef struct COFF_LibWriterSymbolList
{
U64 count;
COFF_LibWriterSymbolNode *first;
COFF_LibWriterSymbolNode *last;
} COFF_LibWriterSymbolList;
typedef struct COFF_LibWriter
{
Arena *arena;
COFF_LibWriterMemberList member_list;
COFF_LibWriterSymbolList symbol_list;
} COFF_LibWriter;
////////////////////////////////
internal COFF_LibWriterSymbolNode * coff_lib_writer_symbol_list_push(Arena *arena, COFF_LibWriterSymbolList *list, COFF_LibWriterSymbol symbol);
internal COFF_LibWriterMemberNode * coff_lib_writer_member_list_push(Arena *arena, COFF_LibWriterMemberList *list, COFF_LibWriterMember member);
internal COFF_LibWriterSymbol * coff_lib_writer_symbol_array_from_list(Arena *arena, COFF_LibWriterSymbolList list);
internal COFF_LibWriterMember * coff_lib_writer_member_array_from_list(Arena *arena, COFF_LibWriterMemberList list);
internal void coff_lib_writer_symbol_array_sort(COFF_LibWriterSymbol *arr, U64 count);
internal COFF_LibWriter * coff_lib_writer_alloc(void);
internal void coff_lib_writer_release(COFF_LibWriter **writer_ptr);
internal void coff_lib_writer_push_obj(COFF_LibWriter *writer, String8 obj_path, String8 obj_data);
internal void coff_lib_writer_push_export(COFF_LibWriter *writer, String8 raw_import_header);
internal void coff_lib_writer_push_export_by_ordinal(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportType import_type, U16 ordinal);
internal void coff_lib_writer_push_export_by_name(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportType import_type, String8 name, U16 hint);
internal String8List coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeStamp time_stamp, U16 mode, B32 emit_second_member);
#endif // COFF_LIB_WRITER_H