mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-25 21:14:59 -07:00
363 lines
15 KiB
C
363 lines
15 KiB
C
// Copyright (c) Epic Games Tools
|
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
|
|
|
internal String8
|
|
pe_name_from_export_parse(PE_ExportParse *exp)
|
|
{
|
|
String8 name;
|
|
if (exp->is_forwarder) {
|
|
name = exp->alias;
|
|
} else if (exp->alias.size) {
|
|
name = exp->alias;
|
|
} else {
|
|
name = exp->name;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
internal U16
|
|
pe_hint_or_ordinal_from_export_parse(PE_ExportParse *exp)
|
|
{
|
|
U16 hint_or_ordinal = max_U16;
|
|
if (exp->import_by == COFF_ImportBy_Ordinal) {
|
|
hint_or_ordinal = exp->ordinal;
|
|
} else if (exp->import_by == COFF_ImportBy_Name) {
|
|
hint_or_ordinal = exp->hint;
|
|
} else {
|
|
NotImplemented;
|
|
}
|
|
return hint_or_ordinal;
|
|
}
|
|
|
|
internal PE_ExportParsePtrArray
|
|
pe_array_from_export_list(Arena *arena, PE_ExportParseList list)
|
|
{
|
|
PE_ExportParsePtrArray result = {0};
|
|
result.v = push_array_no_zero(arena, PE_ExportParse *, list.count);
|
|
for (PE_ExportParseNode *exp = list.first; exp != 0; exp = exp->next) {
|
|
result.v[result.count++] = &exp->data;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal void
|
|
pe_export_parse_list_push_node(PE_ExportParseList *list, PE_ExportParseNode *node)
|
|
{
|
|
SLLQueuePush(list->first, list->last, node);
|
|
list->count += 1;
|
|
}
|
|
|
|
internal PE_ExportParseNode *
|
|
pe_export_parse_list_push(Arena *arena, PE_ExportParseList *list, PE_ExportParse data)
|
|
{
|
|
PE_ExportParseNode *node = push_array(arena, PE_ExportParseNode, 1);
|
|
node->data = data;
|
|
pe_export_parse_list_push_node(list, node);
|
|
return node;
|
|
}
|
|
|
|
internal void
|
|
pe_export_parse_list_concat_in_place(PE_ExportParseList *list, PE_ExportParseList *to_concat)
|
|
{
|
|
if (to_concat->count) {
|
|
if (list->count) {
|
|
list->last->next = to_concat->first;
|
|
list->last = to_concat->last;
|
|
} else {
|
|
list->first = to_concat->first;
|
|
list->last = to_concat->last;
|
|
}
|
|
list->count += to_concat->count;
|
|
MemoryZeroStruct(to_concat);
|
|
}
|
|
}
|
|
|
|
internal int
|
|
pe_named_export_is_before(void *raw_a, void *raw_b)
|
|
{
|
|
PE_ExportParse *a = *(PE_ExportParse **)raw_a;
|
|
PE_ExportParse *b = *(PE_ExportParse **)raw_b;
|
|
int cmp = str8_compar_case_sensitive(&a->name, &b->name);
|
|
return cmp < 0;
|
|
}
|
|
|
|
internal int
|
|
pe_ordinal_export_is_before(void *raw_a, void *raw_b)
|
|
{
|
|
PE_ExportParse *a = raw_a;
|
|
PE_ExportParse *b = raw_b;
|
|
return a->ordinal < b->ordinal;
|
|
}
|
|
|
|
internal PE_FinalizedExports
|
|
pe_finalize_export_list(Arena *arena, PE_ExportParseList export_list)
|
|
{
|
|
PE_ExportParsePtrArray named_exports = {0};
|
|
PE_ExportParsePtrArray ordinal_exports = {0};
|
|
PE_ExportParsePtrArray forwarder_exports = {0};
|
|
{
|
|
// group exports based on flags
|
|
PE_ExportParseList named_exports_list = {0};
|
|
PE_ExportParseList ordinal_exports_list = {0};
|
|
PE_ExportParseList forwarder_exports_list = {0};
|
|
for (PE_ExportParseNode *exp_n = export_list.first, *exp_n_next; exp_n != 0; exp_n = exp_n_next) {
|
|
exp_n_next = exp_n->next;
|
|
if (exp_n->data.is_forwarder) {
|
|
pe_export_parse_list_push_node(&forwarder_exports_list, exp_n);
|
|
} else if (exp_n->data.is_noname_present) {
|
|
AssertAlways(exp_n->data.is_ordinal_assigned);
|
|
pe_export_parse_list_push_node(&ordinal_exports_list, exp_n);
|
|
} else {
|
|
pe_export_parse_list_push_node(&named_exports_list, exp_n);
|
|
}
|
|
}
|
|
|
|
// list -> array
|
|
named_exports = pe_array_from_export_list(arena, named_exports_list);
|
|
forwarder_exports = pe_array_from_export_list(arena, forwarder_exports_list);
|
|
ordinal_exports = pe_array_from_export_list(arena, ordinal_exports_list);
|
|
|
|
// sort exports
|
|
radsort(named_exports.v, named_exports.count, pe_named_export_is_before);
|
|
radsort(ordinal_exports.v, ordinal_exports.count, pe_ordinal_export_is_before);
|
|
radsort(forwarder_exports.v, forwarder_exports.count, pe_named_export_is_before);
|
|
|
|
MemoryZeroStruct(&export_list);
|
|
pe_export_parse_list_concat_in_place(&export_list, &named_exports_list);
|
|
pe_export_parse_list_concat_in_place(&export_list, &forwarder_exports_list);
|
|
pe_export_parse_list_concat_in_place(&export_list, &ordinal_exports_list);
|
|
}
|
|
|
|
// compute max ordinal and used ordinal flag array
|
|
U64 ordinal_low = max_U64;
|
|
B8 *is_ordinal_used = push_array(arena, B8, max_U16);
|
|
for (PE_ExportParseNode *exp_n = export_list.first; exp_n != 0; exp_n = exp_n->next) {
|
|
PE_ExportParse *exp = &exp_n->data;
|
|
if (exp->is_ordinal_assigned) {
|
|
ordinal_low = Min(ordinal_low, exp->ordinal);
|
|
is_ordinal_used[exp->ordinal] = 1;
|
|
}
|
|
}
|
|
if (ordinal_low == max_U64) {
|
|
ordinal_low = 1;
|
|
}
|
|
|
|
// assign omitted ordinals
|
|
{
|
|
U16 last_ordinal = ordinal_low;
|
|
for (U64 exp_idx = 0; exp_idx < named_exports.count; exp_idx += 1) {
|
|
PE_ExportParse *exp = named_exports.v[exp_idx];
|
|
if (!exp->is_ordinal_assigned) {
|
|
for (; last_ordinal < max_U16 && is_ordinal_used[last_ordinal] != 0; last_ordinal += 1);
|
|
exp->ordinal = last_ordinal;
|
|
exp->is_ordinal_assigned = 1;
|
|
is_ordinal_used[last_ordinal] = 1;
|
|
}
|
|
}
|
|
for (U64 exp_idx = 0; exp_idx < forwarder_exports.count; exp_idx += 1) {
|
|
PE_ExportParse *exp = forwarder_exports.v[exp_idx];
|
|
if (!exp->is_ordinal_assigned) {
|
|
for (; last_ordinal < max_U16 && is_ordinal_used[last_ordinal] != 0; last_ordinal += 1);
|
|
exp->ordinal = last_ordinal;
|
|
exp->is_ordinal_assigned = 1;
|
|
is_ordinal_used[last_ordinal] = 1;
|
|
}
|
|
}
|
|
for (U64 exp_idx = 0; exp_idx < ordinal_exports.count; exp_idx += 1) {
|
|
PE_ExportParse *exp = ordinal_exports.v[exp_idx];
|
|
if (!exp->is_ordinal_assigned) {
|
|
exp->ordinal = last_ordinal;
|
|
exp->is_ordinal_assigned = 1;
|
|
is_ordinal_used[last_ordinal] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// assign hints
|
|
{
|
|
U64 hint = 0;
|
|
for (U64 exp_idx = 0; exp_idx < named_exports.count; exp_idx += 1, hint += 1) {
|
|
named_exports.v[exp_idx]->hint = hint;
|
|
}
|
|
for (U64 exp_idx = 0; exp_idx < forwarder_exports.count; exp_idx += 1, hint += 1) {
|
|
forwarder_exports.v[exp_idx]->hint = hint;
|
|
}
|
|
}
|
|
|
|
PE_FinalizedExports result = {0};
|
|
result.ordinal_low = ordinal_low;
|
|
result.named_exports = named_exports;
|
|
result.forwarder_exports = forwarder_exports;
|
|
result.ordinal_exports = ordinal_exports;
|
|
|
|
return result;
|
|
}
|
|
|
|
internal String8
|
|
pe_make_edata_obj(Arena *arena,
|
|
String8 image_name,
|
|
COFF_TimeStamp time_stamp,
|
|
COFF_MachineType machine,
|
|
PE_FinalizedExports finalized_exports)
|
|
{
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
|
|
COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(time_stamp, machine);
|
|
|
|
// push sections
|
|
COFF_ObjSection *voff_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$2"), PE_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_zero());
|
|
COFF_ObjSection *name_voff_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$3"), PE_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align4Bytes, str8_zero());
|
|
COFF_ObjSection *ordinal_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$4"), PE_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, str8_zero());
|
|
COFF_ObjSection *string_table_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$5"), PE_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_zero());
|
|
COFF_ObjSection *image_name_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$6"), PE_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, push_cstr(obj_writer->arena, image_name));
|
|
|
|
ProfBegin("Virtual Offset Table");
|
|
{
|
|
B8 *is_ordinal_bound = push_array(scratch.arena, B8, max_U16);
|
|
|
|
for (U64 arr_idx = 0; arr_idx < ArrayCount(finalized_exports.all); arr_idx += 1) {
|
|
for (U64 exp_idx = 0; exp_idx < finalized_exports.all[arr_idx].count; exp_idx += 1) {
|
|
PE_ExportParse *exp = finalized_exports.all[arr_idx].v[exp_idx];
|
|
if (is_ordinal_bound[exp->ordinal] == 0) {
|
|
// alloc only one slot per ordinal, so it's possible to map ordinal to a virtual offset
|
|
is_ordinal_bound[exp->ordinal] = 1;
|
|
|
|
// create slot for the ordinal virtual offset
|
|
U64 voff_offset = voff_table_sect->data.total_size;
|
|
U32 *voff = push_array(obj_writer->arena, U32, 1);
|
|
str8_list_push(obj_writer->arena, &voff_table_sect->data, str8_struct(voff));
|
|
|
|
COFF_ObjSymbol *exp_symbol;
|
|
if (exp->is_forwarder) {
|
|
U64 forwarder_name_offset = string_table_sect->data.total_size;
|
|
String8 forwarder_name_cstr = push_cstr(obj_writer->arena, exp->name);
|
|
str8_list_push(obj_writer->arena, &string_table_sect->data, forwarder_name_cstr);
|
|
// symbol to the name string
|
|
exp_symbol = coff_obj_writer_push_symbol_static(obj_writer, exp->name, forwarder_name_offset, string_table_sect);
|
|
} else {
|
|
// function or global var symbol
|
|
exp_symbol = coff_obj_writer_push_symbol_undef(obj_writer, exp->name);
|
|
}
|
|
|
|
U16 ordinal_nb = exp->ordinal - finalized_exports.ordinal_low;
|
|
coff_obj_writer_section_push_reloc_voff(obj_writer, voff_table_sect, ordinal_nb*sizeof(U32), exp_symbol);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Named & Forwarder Exports");
|
|
{
|
|
for (U64 arr_idx = 0; arr_idx < ArrayCount(finalized_exports.exports_with_names); arr_idx += 1) {
|
|
PE_ExportParsePtrArray exports = finalized_exports.exports_with_names[arr_idx];
|
|
for (U64 exp_idx = 0; exp_idx < exports.count; exp_idx += 1) {
|
|
PE_ExportParse *exp = exports.v[exp_idx];
|
|
|
|
String8 name = pe_name_from_export_parse(exp);
|
|
|
|
// store symbol name string
|
|
U64 export_name_offset = string_table_sect->data.total_size;
|
|
String8 export_name_cstr = push_cstr(obj_writer->arena, name);
|
|
str8_list_push(obj_writer->arena, &string_table_sect->data, export_name_cstr);
|
|
|
|
// create symbol for the name string
|
|
String8 export_name_symbol_name = push_str8f(obj_writer->arena, "%S", name);
|
|
COFF_ObjSymbol *export_name_symbol = coff_obj_writer_push_symbol_static(obj_writer, export_name_symbol_name, export_name_offset, string_table_sect);
|
|
|
|
// create slot for export virtual offset
|
|
U64 export_name_voff_offset = name_voff_table_sect->data.total_size;
|
|
U8 *export_name_voff = push_array(obj_writer->arena, U8, sizeof(U32));
|
|
str8_list_push(obj_writer->arena, &name_voff_table_sect->data, str8_array(export_name_voff, sizeof(U32)));
|
|
|
|
// write string's virtual offset
|
|
coff_obj_writer_section_push_reloc_voff(obj_writer, name_voff_table_sect, export_name_voff_offset, export_name_symbol);
|
|
|
|
// create and store export's ordinal
|
|
U16 *ordinal = push_array(obj_writer->arena, U16, 1);
|
|
*ordinal = exp->ordinal - finalized_exports.ordinal_low;
|
|
str8_list_push(obj_writer->arena, &ordinal_table_sect->data, str8_struct(ordinal));
|
|
}
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
ProfBegin("Ordinal Exports");
|
|
{
|
|
for (U64 exp_idx = 0; exp_idx < finalized_exports.ordinal_exports.count; exp_idx += 1) {
|
|
// create and store export's ordinal
|
|
PE_ExportParse *exp = finalized_exports.ordinal_exports.v[exp_idx];
|
|
U16 *ordinal = push_array(obj_writer->arena, U16, 1);
|
|
*ordinal = exp->ordinal - finalized_exports.ordinal_low;
|
|
str8_list_push(obj_writer->arena, &ordinal_table_sect->data, str8_struct(ordinal));
|
|
}
|
|
}
|
|
ProfEnd();
|
|
|
|
// fill out export table header
|
|
PE_ExportTableHeader *header = push_array(obj_writer->arena, PE_ExportTableHeader, 1);
|
|
header->time_stamp = time_stamp;
|
|
header->ordinal_base = safe_cast_u16(finalized_exports.ordinal_low);
|
|
header->export_address_table_count = safe_cast_u32(voff_table_sect->data.node_count);
|
|
header->name_pointer_table_count = safe_cast_u32(name_voff_table_sect->data.node_count);
|
|
|
|
// push header field's symbols
|
|
COFF_ObjSymbol *image_name_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_NAME_VOFF"), 0, image_name_sect);
|
|
COFF_ObjSymbol *address_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_ADDRESS_TABLE_VOFF"), 0, voff_table_sect);
|
|
COFF_ObjSymbol *name_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_NAME_POINTER_VOFF"), 0, name_voff_table_sect);
|
|
COFF_ObjSymbol *ordinal_table_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_HEADER_ORDINAL_TABLE_VOFF"), 0, ordinal_table_sect);
|
|
|
|
// push export table header section
|
|
COFF_ObjSection *header_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".edata$1"), PE_EDATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_struct(header));
|
|
coff_obj_writer_push_symbol_static(obj_writer, str8_lit("EXPORT_TABLE_HEADER"), 0, header_sect);
|
|
|
|
// patch export table header
|
|
coff_obj_writer_section_push_reloc_voff(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, name_voff), image_name_symbol);
|
|
coff_obj_writer_section_push_reloc_voff(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, export_address_table_voff), address_table_symbol);
|
|
coff_obj_writer_section_push_reloc_voff(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, name_pointer_table_voff), name_table_symbol);
|
|
coff_obj_writer_section_push_reloc_voff(obj_writer, header_sect, OffsetOf(PE_ExportTableHeader, ordinal_table_voff), ordinal_table_symbol);
|
|
|
|
String8 obj = coff_obj_writer_serialize(arena, obj_writer);
|
|
coff_obj_writer_release(&obj_writer);
|
|
|
|
scratch_end(scratch);
|
|
return obj;
|
|
}
|
|
|
|
internal String8
|
|
pe_make_import_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, String8 debug_symbols, PE_ExportParseList export_list)
|
|
{
|
|
ProfBeginFunction();
|
|
|
|
COFF_LibWriter *lib_writer = coff_lib_writer_alloc();
|
|
|
|
// These objects appear in first three members of any lib that linker produces with /dll.
|
|
// Objects are used by MSVC linker to build import table.
|
|
String8 import_entry_obj = pe_make_import_entry_obj(lib_writer->arena, dll_name, time_stamp, machine, debug_symbols);
|
|
String8 null_import_descriptor_obj = pe_make_null_import_descriptor_obj(lib_writer->arena, time_stamp, machine, debug_symbols);
|
|
String8 null_thunk_data_obj = pe_make_null_thunk_data_obj(lib_writer->arena, dll_name, time_stamp, machine, debug_symbols);
|
|
|
|
// push import table nulls
|
|
coff_lib_writer_push_obj(lib_writer, dll_name, import_entry_obj);
|
|
coff_lib_writer_push_obj(lib_writer, dll_name, null_import_descriptor_obj);
|
|
coff_lib_writer_push_obj(lib_writer, dll_name, null_thunk_data_obj);
|
|
|
|
// push exports
|
|
for (PE_ExportParseNode *exp_n = export_list.first; exp_n != 0; exp_n = exp_n->next) {
|
|
PE_ExportParse *exp = &exp_n->data;
|
|
if (exp->is_private) {
|
|
continue;
|
|
}
|
|
String8 name = pe_name_from_export_parse(exp);
|
|
U16 hint_or_ordinal = pe_hint_or_ordinal_from_export_parse(exp);
|
|
coff_lib_writer_push_import(lib_writer, machine, time_stamp, dll_name, exp->import_by, name, hint_or_ordinal, exp->type);
|
|
}
|
|
|
|
// serialize lib
|
|
String8 lib = coff_lib_writer_serialize(arena, lib_writer, COFF_TimeStamp_Max, 0, /* emit second member: */ 1);
|
|
coff_lib_writer_release(&lib_writer);
|
|
|
|
ProfEnd();
|
|
return lib;
|
|
}
|