mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-11-14 18:20:11 -08:00
implement LINK-compatible map output
This commit is contained in:
1 parent
da00fa9627
commit
ad8fe1c61e
4 files changed
+272
-191
No files matched your search
+197
-181
@@ -6106,216 +6106,232 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT
|
||||
return image_ctx;
|
||||
}
|
||||
|
||||
internal PairU32 *
|
||||
lnk_obj_section_numbers_from_section(Arena *arena, U64 objs_count, LNK_Obj **objs, LNK_Section *sect, LNK_Config *config, U64 *obj_section_numbers_count_out)
|
||||
typedef struct LNK_MapSection
|
||||
{
|
||||
U64 max_contribs = 0;
|
||||
for (LNK_SectionContribChunk *chunk = sect->contribs.first; chunk != 0; chunk = chunk->next) {
|
||||
max_contribs += chunk->count;
|
||||
}
|
||||
String8 name;
|
||||
U32 section_number;
|
||||
U32 off;
|
||||
U32 end;
|
||||
B32 is_code;
|
||||
} LNK_MapSection;
|
||||
|
||||
U64 obj_section_numbers_count = 0;
|
||||
PairU32 *obj_section_numbers = push_array(arena, PairU32, max_contribs);
|
||||
for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) {
|
||||
LNK_Obj *obj = objs[obj_idx];
|
||||
String8 string_table = str8_substr(obj->coff.data, obj->coff.header.string_table_range);
|
||||
for LNK_EachCoffSection(it, obj) {
|
||||
COFF_SectionHeader *section_header = it.v.header;
|
||||
String8 full_section_name = coff_name_from_section_header(string_table, section_header);
|
||||
String8 section_name, section_postfix;
|
||||
coff_parse_section_name(full_section_name, §ion_name, §ion_postfix);
|
||||
|
||||
if (*it.v.flags & COFF_SectionFlag_LnkRemove) { continue; }
|
||||
if (section_header->fsize == 0) { continue; }
|
||||
if (lnk_is_section_removed(config, section_name)) { continue; }
|
||||
|
||||
if (sect->voff <= section_header->voff && section_header->voff < sect->voff + sect->vsize) {
|
||||
Assert(obj_section_numbers_count < max_contribs);
|
||||
obj_section_numbers[obj_section_numbers_count].v0 = obj_idx;
|
||||
obj_section_numbers[obj_section_numbers_count].v1 = it.v.section_number;
|
||||
obj_section_numbers_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
U64 pop_size = (max_contribs - obj_section_numbers_count) * sizeof(obj_section_numbers[0]);
|
||||
arena_pop(arena, pop_size);
|
||||
|
||||
*obj_section_numbers_count_out = obj_section_numbers_count;
|
||||
|
||||
return obj_section_numbers;
|
||||
}
|
||||
|
||||
internal COFF_SectionHeader *
|
||||
lnk_coff_section_header_from_obj_section_number_pair(LNK_Obj **objs, PairU32 p)
|
||||
typedef struct LNK_MapSymbol
|
||||
{
|
||||
return lnk_coff_section_header_from_section_number(objs[p.v0], p.v1);
|
||||
}
|
||||
String8 name;
|
||||
String8 source;
|
||||
U64 va;
|
||||
U32 section_number;
|
||||
U32 off;
|
||||
B32 is_func;
|
||||
} LNK_MapSymbol;
|
||||
|
||||
global LNK_Obj **g_rad_map_objs;
|
||||
internal U32
|
||||
lnk_map_image_section_number_from_voff(COFF_SectionHeader **section_table, U64 section_count, U64 voff)
|
||||
{
|
||||
for (U32 section_number = 1; section_number <= section_count; section_number += 1) {
|
||||
COFF_SectionHeader *header = section_table[section_number];
|
||||
if (voff == header->voff) { return section_number; }
|
||||
}
|
||||
for (U32 section_number = 1; section_number <= section_count; section_number += 1) {
|
||||
COFF_SectionHeader *header = section_table[section_number];
|
||||
U64 section_size = Max(header->vsize, header->fsize);
|
||||
if (header->voff < voff && voff <= header->voff + section_size) { return section_number; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal int
|
||||
lnk_obj_section_number_is_before(void *raw_a, void *raw_b)
|
||||
lnk_map_section_is_before(void *raw_a, void *raw_b)
|
||||
{
|
||||
PairU32 *a = raw_a, *b = raw_b;
|
||||
COFF_SectionHeader *section_header_a = lnk_coff_section_header_from_obj_section_number_pair(g_rad_map_objs, *a);
|
||||
COFF_SectionHeader *section_header_b = lnk_coff_section_header_from_obj_section_number_pair(g_rad_map_objs, *b);
|
||||
return section_header_a->voff < section_header_b->voff;
|
||||
LNK_MapSection *a = raw_a, *b = raw_b;
|
||||
if (a->section_number != b->section_number) { return a->section_number < b->section_number; }
|
||||
if (a->off != b->off) { return a->off < b->off; }
|
||||
return str8_is_before_case_sensitive(&a->name, &b->name);
|
||||
}
|
||||
|
||||
internal U64
|
||||
lnk_pair_u32_nearest_section(PairU32 *arr, U64 count, LNK_Obj **objs, U32 voff)
|
||||
internal int
|
||||
lnk_map_symbol_is_before(void *raw_a, void *raw_b)
|
||||
{
|
||||
U64 result = max_U64;
|
||||
LNK_MapSymbol *a = *(LNK_MapSymbol **)raw_a, *b = *(LNK_MapSymbol **)raw_b;
|
||||
if (a->va != b->va) { return a->va < b->va; }
|
||||
if (a->section_number != b->section_number) { return a->section_number < b->section_number; }
|
||||
if (a->off != b->off) { return a->off < b->off; }
|
||||
return str8_is_before_case_sensitive(&a->name, &b->name);
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
COFF_SectionHeader *first = lnk_coff_section_header_from_obj_section_number_pair(objs, arr[0]);
|
||||
if (first->voff == voff) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
COFF_SectionHeader *last = lnk_coff_section_header_from_obj_section_number_pair(objs, arr[count-1]);
|
||||
if (last->voff <= voff) {
|
||||
return count - 1;
|
||||
}
|
||||
|
||||
if (first->voff <= voff && voff < last->voff + last->vsize) {
|
||||
U64 l = 0;
|
||||
U64 r = count - 1;
|
||||
for (; l <= r; ) {
|
||||
U64 m = l + (r - l) / 2;
|
||||
COFF_SectionHeader *s = lnk_coff_section_header_from_obj_section_number_pair(objs, arr[m]);
|
||||
if (s->voff == voff) {
|
||||
return m;
|
||||
} else if (s->voff < voff) {
|
||||
l = m + 1;
|
||||
} else {
|
||||
r = m - 1;
|
||||
}
|
||||
}
|
||||
result = l - 1;
|
||||
}
|
||||
internal String8
|
||||
lnk_map_source_from_obj(Arena *arena, LNK_Obj *obj)
|
||||
{
|
||||
if (obj == 0 || str8_starts_with(obj->path, str8_lit("*"))) {
|
||||
return str8_lit("<linker-defined>");
|
||||
}
|
||||
String8 obj_name = str8_skip_last_slash(obj->path);
|
||||
LNK_Lib *lib = lnk_obj_get_lib(obj);
|
||||
if (lib) {
|
||||
String8 lib_name = str8_chop_last_dot(str8_skip_last_slash(lib->path));
|
||||
return push_str8f(arena, "%S:%S", lib_name, obj_name);
|
||||
}
|
||||
return obj_name;
|
||||
}
|
||||
|
||||
return result;
|
||||
internal void
|
||||
lnk_map_push_symbol_line(Arena *arena, String8List *map, LNK_MapSymbol *symbol)
|
||||
{
|
||||
String8 function_marker = symbol->is_func ? str8_lit("f") : str8_lit(" ");
|
||||
str8_list_pushf(arena, map, " %04x:%08x %-26S %016llx %S %S\r\n",
|
||||
symbol->section_number, symbol->off, symbol->name, symbol->va, function_marker, symbol->source);
|
||||
}
|
||||
|
||||
internal String8List
|
||||
lnk_build_rad_map(Arena *arena, String8 image_data, LNK_Config *config, U64 objs_count, LNK_Obj **objs, U64 libs_count, LNK_Lib **libs, LNK_SectionTable *sectab)
|
||||
lnk_build_map(Arena *arena, String8 image_data, LNK_Config *config, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, image_data);
|
||||
COFF_SectionHeader **image_section_table = coff_section_table_from_data(scratch.arena, image_data, pe.section_table_range);
|
||||
String8List map = {0};
|
||||
|
||||
String8List map = {0};
|
||||
String8 image_name = str8_chop_last_dot(str8_skip_last_slash(config->out_path));
|
||||
DateTime universal_time = date_time_from_unix_time(config->time_stamp);
|
||||
DateTime local_time = local_from_universal_time(&universal_time);
|
||||
String8 week_day = string_from_week_day(local_time.week_day);
|
||||
String8 month = string_from_month(local_time.month);
|
||||
|
||||
ProfBegin("SECTIONS");
|
||||
str8_list_pushf(arena, &map, "# SECTIONS\n");
|
||||
for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) {
|
||||
LNK_Section *sect = §_n->data;
|
||||
str8_list_pushf(arena, &map, " %S\r\n\r\n", image_name);
|
||||
str8_list_pushf(arena, &map, " Timestamp is %08x (%S %S %02u %02u:%02u:%02u %04u)\r\n\r\n",
|
||||
config->time_stamp, week_day, month, local_time.day, local_time.hour, local_time.min, local_time.sec, local_time.year);
|
||||
str8_list_pushf(arena, &map, " Preferred load address is %016llx\r\n\r\n", pe.image_base);
|
||||
str8_list_pushf(arena, &map, " Start Length Name Class\r\n");
|
||||
|
||||
str8_list_pushf(arena, &map, "%S\n", sect->name);
|
||||
str8_list_pushf(arena, &map, "%-4s %-8s %-8s %-8s %-8s %-16s %-4s %s\n", "No.", "VirtOff", "VirtSize", "FileOff", "FileSize", "Blake3", "Algn", "SC");
|
||||
|
||||
U64 obj_section_numbers_count = 0;
|
||||
PairU32 *obj_section_numbers = lnk_obj_section_numbers_from_section(scratch.arena, objs_count, objs, sect, config, &obj_section_numbers_count);
|
||||
g_rad_map_objs = objs;
|
||||
radsort(obj_section_numbers, obj_section_numbers_count, lnk_obj_section_number_is_before);
|
||||
|
||||
U64 global_sc_idx = 0;
|
||||
for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) {
|
||||
for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1, global_sc_idx += 1) {
|
||||
Temp temp = temp_begin(scratch.arena);
|
||||
LNK_SectionContrib *sc = sc_chunk->v[sc_idx];
|
||||
|
||||
U64 file_off = image_section_table[sc->u.sect_idx+1]->foff + sc->u.off;
|
||||
U64 virt_off = image_section_table[sc->u.sect_idx+1]->voff + sc->u.off;
|
||||
U64 virt_size = lnk_size_from_section_contrib(sc);
|
||||
U64 file_size = lnk_size_from_section_contrib(sc);
|
||||
String8 sc_data = str8_substr(image_data, rng_1u64(file_off, file_off + virt_size));
|
||||
|
||||
LNK_Obj *obj = 0;
|
||||
U32 section_number = 0;
|
||||
U64 obj_section_number_idx = lnk_pair_u32_nearest_section(obj_section_numbers, obj_section_numbers_count, objs, virt_off);
|
||||
if (obj_section_number_idx < obj_section_numbers_count) {
|
||||
obj = objs[obj_section_numbers[obj_section_number_idx].v0];
|
||||
section_number = obj_section_numbers[obj_section_number_idx].v1;
|
||||
}
|
||||
|
||||
U128 sc_hash = {0};
|
||||
if (~sect->flags & COFF_SectionFlag_CntUninitializedData) {
|
||||
blake3_hasher hasher; blake3_hasher_init(&hasher);
|
||||
blake3_hasher_update(&hasher, sc_data.str, sc_data.size);
|
||||
blake3_hasher_finalize(&hasher, (U8 *)&sc_hash, sizeof(sc_hash));
|
||||
}
|
||||
|
||||
String8 sc_idx_str = push_str8f(temp.arena, "%4llx", global_sc_idx);
|
||||
String8 virt_size_str = push_str8f(temp.arena, "%08x", virt_size);
|
||||
String8 sc_hash_str = (~sect->flags & COFF_SectionFlag_CntUninitializedData) ? push_str8f(temp.arena, "%08x%08x", sc_hash.u64[0], sc_hash.u64[1]) : str8_lit("--------");
|
||||
String8 file_off_str = (~sect->flags & COFF_SectionFlag_CntUninitializedData) ? push_str8f(temp.arena, "%08x", file_off) : str8_lit("--------");
|
||||
String8 file_size_str = (~sect->flags & COFF_SectionFlag_CntUninitializedData) ? push_str8f(temp.arena, "%08x", file_size) : str8_lit("--------");
|
||||
String8 virt_off_str = push_str8f(temp.arena, "%08x", virt_off);
|
||||
String8 align_str = push_str8f(temp.arena, "%4x", sc->align);
|
||||
String8 contrib_str;
|
||||
{
|
||||
String8List source_list = {0};
|
||||
if (obj) {
|
||||
COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number);
|
||||
String8 string_table = str8_substr(obj->coff.data, obj->coff.header.string_table_range);
|
||||
String8 section_name = coff_name_from_section_header(string_table, section_header);
|
||||
LNK_Lib *lib = lnk_obj_get_lib(obj);
|
||||
if (lib) {
|
||||
String8 lib_name = str8_chop_last_dot(str8_skip_last_slash(lib->path));
|
||||
String8 obj_name = str8_skip_last_slash(obj->path);
|
||||
str8_list_pushf(temp.arena, &source_list, "%S(%S) SECT%X (%S)", lib_name, obj_name, section_number, section_name);
|
||||
} else {
|
||||
str8_list_pushf(temp.arena, &source_list, "%S SECT%X (%S)", obj->path, section_number, section_name);
|
||||
}
|
||||
} else {
|
||||
str8_list_pushf(temp.arena, &source_list, "<no_loc>");
|
||||
}
|
||||
contrib_str = str8_list_join(temp.arena, &source_list, &(StringJoin){.sep=str8_lit(" ")});
|
||||
}
|
||||
|
||||
str8_list_pushf(arena, &map, "%S %S %S %S %S %S %S %S\n", sc_idx_str, virt_off_str, virt_size_str, file_off_str, file_size_str, sc_hash_str, align_str, contrib_str);
|
||||
|
||||
temp_end(temp);
|
||||
}
|
||||
}
|
||||
str8_list_pushf(arena, &map, "\n");
|
||||
}
|
||||
ProfEnd();
|
||||
|
||||
str8_list_pushf(arena, &map, "# DEBUG\n");
|
||||
for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) {
|
||||
U64 section_cap = 0;
|
||||
for EachIndex(obj_idx, objs_count) { section_cap += objs[obj_idx]->coff.sections.count_no_null; }
|
||||
LNK_MapSection *sections = push_array(scratch.arena, LNK_MapSection, section_cap);
|
||||
U64 section_count = 0;
|
||||
HashMap *section_maps = push_array(scratch.arena, HashMap, pe.section_count + 1);
|
||||
for EachIndex(obj_idx, objs_count) {
|
||||
LNK_Obj *obj = objs[obj_idx];
|
||||
for LNK_EachCoffSection(it, obj) {
|
||||
COFF_SectionFlags section_flags = *it.v.flags;
|
||||
if (~section_flags & COFF_SectionFlag_LnkRemove && section_flags & LNK_SECTION_FLAG_DEBUG) {
|
||||
LNK_Lib *lib = lnk_obj_get_lib(obj);
|
||||
if (lib) {
|
||||
String8 lib_name = str8_chop_last_dot(str8_skip_last_slash(lib->path));
|
||||
String8 obj_name = str8_skip_last_slash(obj->path);
|
||||
str8_list_pushf(arena, &map, "%S(%S) SECT%X\n", lib_name, obj_name, it.v.section_number);
|
||||
} else {
|
||||
str8_list_pushf(arena, &map, "%S SECT%X\n", obj->path, it.v.section_number);
|
||||
}
|
||||
COFF_SectionFlags flags = *it.v.flags;
|
||||
if (flags & (COFF_SectionFlag_LnkRemove | COFF_SectionFlag_LnkInfo | LNK_SECTION_FLAG_DEBUG)) { continue; }
|
||||
|
||||
U32 image_section_number = lnk_map_image_section_number_from_voff(image_section_table, pe.section_count, it.v.header->voff);
|
||||
if (image_section_number == 0) { continue; }
|
||||
|
||||
String8 name = lnk_obj_section_name_from_section_number(obj, it.v.section_number);
|
||||
LNK_MapSection *section = hash_map_search_string_raw(§ion_maps[image_section_number], name);
|
||||
U32 off = safe_cast_u32(it.v.header->voff - image_section_table[image_section_number]->voff);
|
||||
U32 end = off + it.v.header->vsize;
|
||||
if (section == 0) {
|
||||
Assert(section_count < section_cap);
|
||||
section = §ions[section_count++];
|
||||
*section = (LNK_MapSection){ name, image_section_number, off, end, !!(flags & COFF_SectionFlag_CntCode) };
|
||||
hash_map_push_string_raw(scratch.arena, §ion_maps[image_section_number], name, section);
|
||||
} else {
|
||||
section->off = Min(section->off, off);
|
||||
section->end = Max(section->end, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
str8_list_pushf(arena, &map, "\n");
|
||||
if (section_count > 1) { radsort(sections, section_count, lnk_map_section_is_before); }
|
||||
for EachIndex(i, section_count) {
|
||||
LNK_MapSection *section = §ions[i];
|
||||
str8_list_pushf(arena, &map, " %04x:%08x %08xH %-22S %s\r\n",
|
||||
section->section_number, section->off, section->end - section->off,
|
||||
section->name, section->is_code ? "CODE" : "DATA");
|
||||
}
|
||||
|
||||
ProfBegin("LIBS");
|
||||
if (libs_count) {
|
||||
str8_list_pushf(arena, &map, "# LIBS\n");
|
||||
for EachIndex(i, libs_count) {
|
||||
str8_list_pushf(arena, &map, "%S\n", libs[i]->path);
|
||||
str8_list_pushf(arena, &map, "\r\n Address Publics by Value Rva+Base Lib:Object\r\n\r\n");
|
||||
|
||||
U64 public_cap = 0;
|
||||
for EachIndex(worker_idx, symtab->arena->count) {
|
||||
for EachNode(chunk, LNK_SymbolHashTrieChunk, symtab->chunks[worker_idx].first) { public_cap += chunk->count; }
|
||||
}
|
||||
LNK_MapSymbol *publics = push_array(scratch.arena, LNK_MapSymbol, public_cap);
|
||||
U64 public_count = 0;
|
||||
for EachIndex(worker_idx, symtab->arena->count) {
|
||||
for EachNode(chunk, LNK_SymbolHashTrieChunk, symtab->chunks[worker_idx].first) {
|
||||
for EachIndex(i, chunk->count) {
|
||||
LNK_Symbol *symbol = chunk->v[i].symbol;
|
||||
if (symbol == 0 || str8_match(symbol->name, str8_lit(LNK_NULL_SYMBOL), 0) || str8_match(symbol->name, str8_lit(LNK_IMPORT_STUB), 0)) { continue; }
|
||||
|
||||
LNK_ObjSymbolRef ref = lnk_ref_from_symbol(symbol);
|
||||
COFF_ParsedSymbol parsed = lnk_parsed_from_symbol(symbol);
|
||||
COFF_SymbolValueInterpType interp = coff_interp_from_parsed_symbol(parsed);
|
||||
LNK_MapSymbol map_symbol = { .name = symbol->name, .is_func = COFF_SymbolType_IsFunc(parsed.type) };
|
||||
|
||||
if (interp == COFF_SymbolValueInterp_Regular) {
|
||||
if (parsed.section_number == lnk_obj_get_removed_section_number(ref.obj) || parsed.section_number == 0 || parsed.section_number > pe.section_count) { continue; }
|
||||
map_symbol.section_number = parsed.section_number;
|
||||
map_symbol.off = safe_cast_u32(parsed.value);
|
||||
map_symbol.va = pe.image_base + image_section_table[parsed.section_number]->voff + parsed.value;
|
||||
|
||||
COFF_ParsedSymbol original = coff_parse_symbol_no_name(ref.obj->coff.header, lnk_coff_symbol_table_from_obj(ref.obj), ref.symbol_idx);
|
||||
COFF_SymbolValueInterpType original_interp = coff_interp_from_parsed_symbol(original);
|
||||
map_symbol.source = original_interp == COFF_SymbolValueInterp_Common ? str8_lit("<common>") : lnk_map_source_from_obj(scratch.arena, ref.obj);
|
||||
} else if (interp == COFF_SymbolValueInterp_Abs) {
|
||||
map_symbol.off = safe_cast_u32(parsed.value);
|
||||
if (str8_match(symbol->name, str8_lit("__ImageBase"), 0)) {
|
||||
map_symbol.va = pe.image_base;
|
||||
map_symbol.source = str8_lit("<linker-defined>");
|
||||
} else {
|
||||
map_symbol.va = parsed.value;
|
||||
map_symbol.source = str8_lit("<absolute>");
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
Assert(public_count < public_cap);
|
||||
publics[public_count++] = map_symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
ProfEnd();
|
||||
|
||||
LNK_MapSymbol **public_ptrs = push_array_no_zero(scratch.arena, LNK_MapSymbol *, public_count);
|
||||
for EachIndex(i, public_count) { public_ptrs[i] = &publics[i]; }
|
||||
if (public_count > 1) { radsort(public_ptrs, public_count, lnk_map_symbol_is_before); }
|
||||
for EachIndex(i, public_count) { lnk_map_push_symbol_line(arena, &map, public_ptrs[i]); }
|
||||
|
||||
U32 entry_section_number = lnk_map_image_section_number_from_voff(image_section_table, pe.section_count, pe.entry_point);
|
||||
U32 entry_section_off = entry_section_number ? pe.entry_point - image_section_table[entry_section_number]->voff : pe.entry_point;
|
||||
str8_list_pushf(arena, &map, "\r\n entry point at %04x:%08x\r\n\r\n Static symbols\r\n\r\n", entry_section_number, entry_section_off);
|
||||
|
||||
U64 static_cap = 0;
|
||||
for EachIndex(obj_idx, objs_count) { static_cap += objs[obj_idx]->coff.symbols.count; }
|
||||
LNK_MapSymbol *statics = push_array(scratch.arena, LNK_MapSymbol, static_cap);
|
||||
U64 static_count = 0;
|
||||
for EachIndex(obj_idx, objs_count) {
|
||||
LNK_Obj *obj = objs[obj_idx];
|
||||
String8 string_table = lnk_coff_string_table_from_obj(obj);
|
||||
String8 symbol_table = lnk_coff_symbol_table_from_obj(obj);
|
||||
for LNK_EachCoffSymbol(it, obj) {
|
||||
COFF_ParsedSymbol original = coff_parse_symbol(obj->coff.header, string_table, symbol_table, safe_cast_u32(it.symbol_idx));
|
||||
if (original.storage_class != COFF_SymStorageClass_Static || coff_interp_from_parsed_symbol(original) != COFF_SymbolValueInterp_Regular) { continue; }
|
||||
if (str8_starts_with(original.name, str8_lit("$pdata$"))) { continue; }
|
||||
if (original.section_number == 0 || original.section_number > obj->coff.sections.count_no_null) { continue; }
|
||||
if (*lnk_obj_section_from_section_number(obj, original.section_number).flags & (COFF_SectionFlag_LnkInfo | LNK_SECTION_FLAG_DEBUG)) { continue; }
|
||||
if (original.type.v == 0 && original.value == 0 && original.aux_symbol_count > 0 &&
|
||||
str8_match(original.name, lnk_obj_section_name_from_section_number(obj, original.section_number), 0)) { continue; }
|
||||
|
||||
COFF_ParsedSymbol parsed = it.v;
|
||||
if (coff_interp_from_parsed_symbol(parsed) != COFF_SymbolValueInterp_Regular || parsed.section_number == 0 || parsed.section_number > pe.section_count) { continue; }
|
||||
|
||||
Assert(static_count < static_cap);
|
||||
statics[static_count++] = (LNK_MapSymbol){
|
||||
.name = original.name,
|
||||
.source = lnk_map_source_from_obj(scratch.arena, obj),
|
||||
.va = pe.image_base + image_section_table[parsed.section_number]->voff + parsed.value,
|
||||
.section_number = parsed.section_number,
|
||||
.off = safe_cast_u32(parsed.value),
|
||||
.is_func = COFF_SymbolType_IsFunc(parsed.type),
|
||||
};
|
||||
}
|
||||
}
|
||||
LNK_MapSymbol **static_ptrs = push_array_no_zero(scratch.arena, LNK_MapSymbol *, static_count);
|
||||
for EachIndex(i, static_count) { static_ptrs[i] = &statics[i]; }
|
||||
if (static_count > 1) { radsort(static_ptrs, static_count, lnk_map_symbol_is_before); }
|
||||
for EachIndex(i, static_count) { lnk_map_push_symbol_line(arena, &map, static_ptrs[i]); }
|
||||
|
||||
scratch_end(scratch);
|
||||
ProfEnd();
|
||||
return map;
|
||||
@@ -6463,11 +6479,11 @@ lnk_run_linker(TP_Context *tp, TP_Arena *arena, LNK_Config *config)
|
||||
lnk_background_file_writer_begin(pdb_writer.file_writer);
|
||||
|
||||
//
|
||||
// RAD Map
|
||||
// Map
|
||||
//
|
||||
if (config->rad_chunk_map == LNK_SwitchState_Yes) {
|
||||
String8List rad_map = lnk_build_rad_map(scratch.arena, image_ctx.image_data, config, objs_count, objs, libs_count, libs, image_ctx.sectab);
|
||||
lnk_write_data_list_to_file_path(config->rad_chunk_map_name, config->temp_rad_chunk_map_name, rad_map);
|
||||
if (config->map == LNK_SwitchState_Yes) {
|
||||
String8List map = lnk_build_map(scratch.arena, image_ctx.image_data, config, symtab, objs_count, objs);
|
||||
lnk_write_data_list_to_file_path(config->map_name, config->temp_map_name, map);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
+35
-6
@@ -40,6 +40,7 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] =
|
||||
{ LNK_CmdSwitch_Lib, 0, LNK_CmdValueKind_Null, "LIB", "", "Turn linker into lib.exe." },
|
||||
{ LNK_CmdSwitch_LibPath, 0, LNK_CmdValueKind_Scalar, "LIBPATH", ":DIR", "Add DIR for the linker to search for libraries." },
|
||||
{ LNK_CmdSwitch_Machine, 0, LNK_CmdValueKind_Scalar, "MACHINE", ":{X64|X86}", "Image target platform." },
|
||||
{ LNK_CmdSwitch_Map, 0, LNK_CmdValueKind_Scalar, "MAP", "[:FILENAME]", "Create a linker map file." },
|
||||
{ LNK_CmdSwitch_Manifest, 0, LNK_CmdValueKind_List, "MANIFEST", "[:{EMBED[,ID=#]|NO]", "Controls whether the linker should create a side manifest." },
|
||||
{ LNK_CmdSwitch_ManifestDependency, 1, LNK_CmdValueKind_Scalar, "MANIFESTDEPENDENCY", ":\"manifest dependency XML string\"", "Add a manifest dependency." },
|
||||
{ LNK_CmdSwitch_ManifestFile, 0, LNK_CmdValueKind_Scalar, "MANIFESTFILE", ":FILENAME", "Specifies a manifest file." },
|
||||
@@ -72,7 +73,6 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] =
|
||||
{ LNK_CmdSwitch_Rad_BuildImpLib, 0, LNK_CmdValueKind_Scalar, "RAD_BUILD_IMPLIB", "[:NO]", "Build import library." },
|
||||
{ LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, 0, LNK_CmdValueKind_Scalar, "RAD_CHECK_UNUSED_DELAY_LOAD_DLL", "[:NO]", "Check for unused delay load dlls." },
|
||||
{ LNK_CmdSwitch_Rad_DataDirCount, 0, LNK_CmdValueKind_Scalar, "RAD_DATA_DIR_COUNT", ":#", "Internal default for PE optional header data directory count." },
|
||||
{ LNK_CmdSwitch_Rad_Map, 0, LNK_CmdValueKind_Scalar, "RAD_MAP", ":FILENAME", "Emit file with the output image's layout description." },
|
||||
{ LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols, 0, LNK_CmdValueKind_Scalar, "RAD_MAP_LINES_FOR_UNRESOLVED_SYMBOLS", "[:NO]", "Use debug info to print source file location for unresolved symbol" },
|
||||
{ LNK_CmdSwitch_Rad_MemoryMapFiles, 0, LNK_CmdValueKind_Scalar, "RAD_MEMORY_MAP_FILES", "[:{NO|READ_ONLY|READ_WRITE}]", "When enabled, files are memory-mapped instead of being read entirely on request." },
|
||||
{ LNK_CmdSwitch_Rad_BootMode, 0, LNK_CmdValueKind_Scalar, "RAD_BOOT_MODE", "[:LINKER|TYPE_SERVER]", "Overrides default boot program." },
|
||||
@@ -1175,8 +1175,8 @@ lnk_print_help(void)
|
||||
internal void
|
||||
lnk_apply_write_temp_files(Arena *arena, LNK_Config *config)
|
||||
{
|
||||
if (config->rad_chunk_map_name.size) {
|
||||
config->temp_rad_chunk_map_name = push_str8f(arena, "%S.tmp%x", config->rad_chunk_map_name, config->time_stamp);
|
||||
if (config->map_name.size) {
|
||||
config->temp_map_name = push_str8f(arena, "%S.tmp%x", config->map_name, config->time_stamp);
|
||||
}
|
||||
if (config->out_path.size) {
|
||||
config->temp_out_path = push_str8f(arena, "%S.tmp%x", config->out_path, config->time_stamp);
|
||||
@@ -1982,9 +1982,11 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8 val
|
||||
}
|
||||
} break;
|
||||
|
||||
case LNK_CmdSwitch_Rad_Map: {
|
||||
lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value, &config->rad_chunk_map_name);
|
||||
config->rad_chunk_map = LNK_SwitchState_Yes;
|
||||
case LNK_CmdSwitch_Map: {
|
||||
config->map = LNK_SwitchState_Yes;
|
||||
if (value.size) {
|
||||
lnk_cmd_switch_parse_string_copy(config->arena, obj, cmd_switch, value, &config->map_name);
|
||||
}
|
||||
} break;
|
||||
|
||||
case LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols: {
|
||||
@@ -3082,6 +3084,11 @@ lnk_config_init(U64 argc, char **argv)
|
||||
config->pdb_name = path_replace_file_extension(scratch.arena, config->out_path, str8_lit("pdb"));
|
||||
}
|
||||
|
||||
// handle empty /MAP
|
||||
if (config->map == LNK_SwitchState_Yes && !config->map_name.size) {
|
||||
config->map_name = path_replace_file_extension(scratch.arena, config->out_path, str8_lit("map"));
|
||||
}
|
||||
|
||||
// handle empty /RAD_DEBUG_NAME
|
||||
if (!lnk_cmd_line_has_switch(cmd_line, LNK_CmdSwitch_Rad_DebugName)) {
|
||||
config->rad_debug_name = path_replace_file_extension(scratch.arena, config->out_path, str8_lit("rdi"));
|
||||
@@ -3100,10 +3107,32 @@ lnk_config_init(U64 argc, char **argv)
|
||||
// convert to full paths
|
||||
config->out_path = full_path_from_path(arena, config->out_path);
|
||||
config->pdb_name = full_path_from_path(arena, config->pdb_name);
|
||||
if (config->map_name.size) {
|
||||
config->map_name = full_path_from_path(arena, config->map_name);
|
||||
}
|
||||
config->rad_debug_name = full_path_from_path(arena, config->rad_debug_name);
|
||||
config->imp_lib_name = full_path_from_path(arena, config->imp_lib_name);
|
||||
config->manifest_name = full_path_from_path(arena, config->manifest_name);
|
||||
|
||||
// reject output path aliases before background writers are launched
|
||||
if (config->map_name.size) {
|
||||
String8 output_paths[] = {
|
||||
config->out_path,
|
||||
config->pdb_name,
|
||||
config->rad_debug_name,
|
||||
config->imp_lib_name,
|
||||
config->manifest_name,
|
||||
};
|
||||
for EachElement(i, output_paths) {
|
||||
if (output_paths[i].size && path_match_normalized(config->map_name, output_paths[i])) {
|
||||
lnk_error(LNK_Error_Cmdl, "/MAP output path %S conflicts with another linker output", config->map_name);
|
||||
config->map = LNK_SwitchState_No;
|
||||
config->map_name = str8_zero();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// push linker env vars
|
||||
{
|
||||
struct { String8 key, value; } key_value_str8_table[] = {
|
||||
|
||||
@@ -73,6 +73,7 @@ typedef enum
|
||||
LNK_CmdSwitch_Lib,
|
||||
LNK_CmdSwitch_LibPath,
|
||||
LNK_CmdSwitch_Machine,
|
||||
LNK_CmdSwitch_Map,
|
||||
LNK_CmdSwitch_Manifest,
|
||||
LNK_CmdSwitch_ManifestDependency,
|
||||
LNK_CmdSwitch_ManifestFile,
|
||||
@@ -121,7 +122,6 @@ typedef enum
|
||||
LNK_CmdSwitch_Rad_LinkVer,
|
||||
LNK_CmdSwitch_Rad_Log,
|
||||
LNK_CmdSwitch_Rad_Logo,
|
||||
LNK_CmdSwitch_Rad_Map,
|
||||
LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols,
|
||||
LNK_CmdSwitch_Rad_MemoryMapFiles,
|
||||
LNK_CmdSwitch_Rad_Mode,
|
||||
@@ -388,8 +388,8 @@ typedef struct LNK_Config
|
||||
String8 manifest_ui_access;
|
||||
String8List manifest_dependency_list;
|
||||
LNK_SwitchState rad_debug;
|
||||
LNK_SwitchState rad_chunk_map;
|
||||
String8 rad_chunk_map_name;
|
||||
LNK_SwitchState map;
|
||||
String8 map_name;
|
||||
String8 rad_debug_name;
|
||||
String8 rad_debug_alt_path;
|
||||
LNK_IncludeSymbolList include_symbol_list;
|
||||
@@ -403,7 +403,7 @@ typedef struct LNK_Config
|
||||
String8 temp_out_path;
|
||||
String8 temp_pdb_name;
|
||||
String8 temp_rad_debug_name;
|
||||
String8 temp_rad_chunk_map_name;
|
||||
String8 temp_map_name;
|
||||
String8 temp_type_server_name;
|
||||
String8 delay_load_helper_name;
|
||||
String8List remove_sections;
|
||||
|
||||
@@ -571,6 +571,42 @@ TEST(simple_link_test)
|
||||
T_Ok(opt->loader_flags == 0);
|
||||
}
|
||||
|
||||
TEST(map)
|
||||
{
|
||||
T_Ok(t_write_def_obj("map.obj", (T_COFF_DefObj){
|
||||
.machine = T_COFF_DefSetMachine(X64),
|
||||
.sections = (T_COFF_DefSection[]){
|
||||
{ "text", ".text", str8_lit_comp("\xC3"), .flags = "rx:code" },
|
||||
{0}
|
||||
},
|
||||
.symbols = (T_COFF_DefSymbol[]){
|
||||
T_COFF_DefSymbol_Extern("map_entry", "text", 0),
|
||||
T_COFF_DefSymbol_Static("map_local", "text", 0),
|
||||
{0}
|
||||
}
|
||||
}));
|
||||
|
||||
t_invoke_linkerf("/entry:map_entry /subsystem:console /out:mapped.exe /map:explicit.map map.obj");
|
||||
T_Ok(g_last_exit_code == 0);
|
||||
|
||||
String8 map = t_read_file(arena, str8_lit("explicit.map"));
|
||||
T_Ok(str8_find_needle(map, 0, str8_lit(" Timestamp is "), 0) < map.size);
|
||||
T_Ok(str8_find_needle(map, 0, str8_lit(" Preferred load address is "), 0) < map.size);
|
||||
T_Ok(str8_find_needle(map, 0, str8_lit(" Start Length Name Class"), 0) < map.size);
|
||||
T_Ok(str8_find_needle(map, 0, str8_lit(" Publics by Value"), 0) < map.size);
|
||||
T_Ok(str8_find_needle(map, 0, str8_lit("map_entry"), 0) < map.size);
|
||||
T_Ok(str8_find_needle(map, 0, str8_lit(" entry point at"), 0) < map.size);
|
||||
T_Ok(str8_find_needle(map, 0, str8_lit(" Static symbols"), 0) < map.size);
|
||||
T_Ok(str8_find_needle(map, 0, str8_lit("map_local"), 0) < map.size);
|
||||
|
||||
t_invoke_linkerf("/entry:map_entry /subsystem:console /out:default.exe /map map.obj");
|
||||
T_Ok(g_last_exit_code == 0);
|
||||
T_Ok(t_read_file(arena, str8_lit("default.map")).size > 0);
|
||||
|
||||
t_invoke_linkerf("/entry:map_entry /subsystem:console /out:collision.exe /map:collision.exe map.obj");
|
||||
T_Ok(g_last_exit_code != 0);
|
||||
}
|
||||
|
||||
|
||||
TEST(out_of_bounds_section_number)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user