This commit is contained in:
Nikita Smith
2024-10-15 17:25:22 -07:00
parent 0cf553e39a
commit 932df7bf68
127 changed files with 76232 additions and 0 deletions
+1
View File
@@ -104,6 +104,7 @@ if "%rdi_from_pdb%"=="1" set didbuild=1 && %compile% ..\src\rdi_fr
if "%rdi_from_dwarf%"=="1" set didbuild=1 && %compile% ..\src\rdi_from_dwarf\rdi_from_dwarf.c %compile_link% %out%rdi_from_dwarf.exe || exit /b 1
if "%rdi_dump%"=="1" set didbuild=1 && %compile% ..\src\rdi_dump\rdi_dump_main.c %compile_link% %out%rdi_dump.exe || exit /b 1
if "%rdi_breakpad_from_pdb%"=="1" set didbuild=1 && %compile% ..\src\rdi_breakpad_from_pdb\rdi_breakpad_from_pdb_main.c %compile_link% %out%rdi_breakpad_from_pdb.exe || exit /b 1
if "%radlink%"=="1" set didbuild=1 && %compile% ..\src\linker\lnk.c %compile_link% %out%radlink.exe || exit /b 1
if "%tester%"=="1" set didbuild=1 && %compile% ..\src\tester\tester_main.c %compile_link% %out%tester.exe || exit /b 1
if "%ryan_scratch%"=="1" set didbuild=1 && %compile% ..\src\scratch\ryan_scratch.c %compile_link% %out%ryan_scratch.exe || exit /b 1
if "%mule_main%"=="1" set didbuild=1 && del vc*.pdb mule*.pdb && %compile_release% %only_compile% ..\src\mule\mule_inline.cpp && %compile_release% %only_compile% ..\src\mule\mule_o2.cpp && %compile_debug% %EHsc% ..\src\mule\mule_main.cpp ..\src\mule\mule_c.c mule_inline.obj mule_o2.obj %compile_link% %no_aslr% %out%mule_main.exe || exit /b 1
+88
View File
@@ -0,0 +1,88 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal String8
push_cstr(Arena *arena, String8 str)
{
U64 buffer_size = str.size + 1;
U8 *buffer = push_array_no_zero(arena, U8, buffer_size);
MemoryCopy(buffer, str.str, str.size);
buffer[str.size] = 0;
String8 result = str8(buffer, buffer_size);
return result;
}
internal U32 *
push_u32(Arena *arena, U32 value)
{
U32 *result = push_array_no_zero(arena, U32, 1);
*result = value;
return result;
}
internal U64 *
push_u64(Arena *arena, U64 value)
{
U64 *result = push_array_no_zero(arena, U64, 1);
*result = value;
return result;
}
internal U32 *
push_array_copy_u32(Arena *arena, U32 *v, U64 count)
{
U32 *result = push_array_no_zero(arena, U32, count);
MemoryCopyTyped(result, v, count);
return result;
}
internal U64 *
push_array_copy_u64(Arena *arena, U64 *v, U64 count)
{
U64 *result = push_array_no_zero(arena, U64, count);
MemoryCopyTyped(result, v, count);
return result;
}
internal U64 **
push_matrix_u64(Arena *arena, U64 rows, U64 columns)
{
U64 **result = push_array_no_zero(arena, U64 *, rows);
for (U64 row_idx = 0; row_idx < rows; row_idx += 1) {
result[row_idx] = push_array(arena, U64, columns);
}
return result;
}
internal Arena **
alloc_fixed_size_arena_array(Arena *arena, U64 count, U64 res, U64 cmt)
{
U64 data_size = sizeof(count) + sizeof(Arena *) * count;
U8 *data = push_array_no_zero(arena, U8, data_size);
U64 *count_ptr = (U64 *)data;
Arena **arr = (Arena **)(count_ptr + 1);
*count_ptr = count;
ArenaParams params = {0};
params.reserve_size = res;
params.commit_size = cmt;
for (U64 i = 0; i < count; i += 1) {
Arena *fixed_arena = arena_alloc_(&params);
arr[i] = fixed_arena;
}
return arr;
}
internal void
release_arena_array(Arena **arr)
{
U64 *count_ptr = (U64 *)arr - 1;
U64 count = *count_ptr;
for (U64 i = 0; i < count; i += 1) {
arena_release(arr[i]);
arr[i] = 0;
}
}
+15
View File
@@ -0,0 +1,15 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
internal U32 * push_u32(Arena *arena, U32 value);
internal U64 * push_u64(Arena *arena, U64 value);
internal U32 * push_array_copy_u32(Arena *arena, U32 *v, U64 count);
internal U64 * push_array_copy_u64(Arena *arena, U64 *v, U64 count);
internal U64 ** push_matrix_u64(Arena *arena, U64 rows, U64 columns);
internal String8 push_cstr(Arena *arena, String8 str);
internal Arena ** alloc_fixed_size_arena_array(Arena *arena, U64 count, U64 res, U64 cmt);
internal void release_arena_array(Arena **arr);
+230
View File
@@ -0,0 +1,230 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal U64
void_list_count_nodes(VoidNode *head)
{
U64 node_count = 0;
for (VoidNode *curr = head; curr != 0; curr = curr->next) {
++node_count;
}
return node_count;
}
internal void
void_node_concat(VoidNode **head, VoidNode *node)
{
Assert(*head != node);
node->next = *head;
*head = node;
}
internal void
void_node_concat_atomic(VoidNode **head, VoidNode *node)
{
Assert(*head != node);
node->next = ins_atomic_ptr_eval_assign(head, node);
}
internal U64Node *
u64_list_push(Arena *arena, U64List *list, U64 data)
{
U64Node *n = push_array(arena, U64Node, 1);
n->next = 0;
n->data = data;
SLLQueuePush(list->first, list->last, n);
++list->count;
return n;
}
internal void
u64_list_concat_in_place(U64List *list, U64List *to_concat)
{
SLLConcatInPlace(list, to_concat);
}
internal U64Array
u64_array_from_list(Arena *arena, U64List *list)
{
U64Array result;
result.count = 0;
result.v = push_array(arena, U64, list->count);
for (U64Node *n = list->first; n != NULL; n = n->next) {
result.v[result.count++] = n->data;
}
return result;
}
internal void
u32_array_sort(U64 count, U32 *v)
{
radsort(v, count, u32_is_before);
}
internal void
u32_pair_radix_sort(U64 count, PairU32 *arr)
{
Temp scratch = scratch_begin(0,0);
PairU32 *temp = push_array(scratch.arena, PairU32, count);
const U64 bit_count0 = 11;
const U64 bit_count1 = 11;
const U64 bit_count2 = 10;
U32 *count0 = push_array(scratch.arena, U32, (1 << bit_count0));
U32 *count1 = push_array(scratch.arena, U32, (1 << bit_count1));
U32 *count2 = push_array(scratch.arena, U32, (1 << bit_count2));
for (U64 i = 0; i < count; ++i) {
U32 digit0 = (arr[i].v0 >> 0 ) % (1 << bit_count0);
U32 digit1 = (arr[i].v0 >> bit_count0) % (1 << bit_count1);
U32 digit2 = (arr[i].v0 >> bit_count1) % (1 << bit_count2);
++count0[digit0];
++count1[digit1];
++count2[digit2];
}
counts_to_offsets_array_u32((1 << bit_count0), count0);
counts_to_offsets_array_u32((1 << bit_count1), count1);
counts_to_offsets_array_u32((1 << bit_count2), count2);
for (U64 i = 0; i < count; ++i) {
U32 digit0 = (arr[i].v0 >> 0) % (1 << bit_count0);
temp[count0[digit0]++] = arr[i];
}
for (U64 i = 0; i < count; ++i) {
U32 digit1 = (temp[i].v0 >> bit_count0) % (1 << bit_count1);
arr[count1[digit1]++] = temp[i];
}
for (U64 i = 0; i < count; ++i) {
U32 digit2 = (arr[i].v0 >> bit_count1) % (1 << bit_count2);
temp[count2[digit2]++] = arr[i];
}
MemoryCopyTyped(arr, temp, count);
scratch_end(scratch);
}
internal B32
u32_array_compare(U32Array a, U32Array b)
{
B32 are_equal = 0;
if (a.count == b.count) {
int cmp = MemoryCompare(a.v, b.v, sizeof(a.v[0]) * a.count);
are_equal = (cmp == 0);
}
return are_equal;
}
internal U64Array
u64_array_remove_duplicates(Arena *arena, U64Array in)
{
U64Array result;
result.count = 0;
result.v = push_array(arena, U64, in.count);
for (U64 i = 1; i < in.count; ++i) {
B32 is_unique = in.v[i - 1] != in.v[i];
if (is_unique) {
result.v[result.count++] = in.v[i - 1];
}
}
if (in.count > 0 && result.count > 0) {
B32 is_unique = result.v[result.count - 1] != in.v[in.count - 1];
if (is_unique) {
result.v[result.count++] = in.v[in.count - 1];
}
}
U64 slack_size = (in.count - result.count) * sizeof(result.v[0]);
arena_pop(arena, slack_size);
return result;
}
internal U64
sum_array_u64(U64 count, U64 *v)
{
U64 result = 0;
for (U64 i = 0; i < count; i += 1) {
result += v[i];
}
return result;
}
internal U64
sum_matrix_u64(U64 rows, U64 cols, U64 **v)
{
U64 result = 0;
for (U64 i = 0; i < rows; ++i) {
result += sum_array_u64(cols, v[i]);
}
return result;
}
internal U64
max_array_u64(U64 count, U64 *v)
{
U64 result = 0;
for (U64 i = 0; i < count; i += 1) {
result = Max(v[i], result);
}
return result;
}
internal U64
min_array_u64(U64 count, U64 *v)
{
U64 result = max_U64;
for (U64 i = 0; i < count; i += 1) {
result = Min(v[i], result);
}
return result;
}
internal void
counts_to_offsets_array_u32(U64 count, U32 *arr)
{
U32 next_offset = 0;
for (U64 i = 0; i < count; i += 1) {
U32 current_offset = next_offset;
next_offset += arr[i];
arr[i] = current_offset;
}
}
internal void
counts_to_offsets_array_u64(U64 count, U64 *arr)
{
U64 next_offset = 0;
for (U64 i = 0; i < count; i += 1) {
U64 current_offset = next_offset;
next_offset += arr[i];
arr[i] = current_offset;
}
}
internal U32 *
offsets_from_counts_array_u32(Arena *arena, U32 *v, U64 count)
{
U32 *result = push_array_copy_u32(arena, v, count);
counts_to_offsets_array_u32(count, result);
return result;
}
internal U64 *
offsets_from_counts_array_u64(Arena *arena, U64 *v, U64 count)
{
U64 *result = push_array_copy_u64(arena, v, count);
counts_to_offsets_array_u64(count, result);
return result;
}
+63
View File
@@ -0,0 +1,63 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef struct U32Array
{
U64 count;
U32 *v;
} U32Array;
typedef struct U64Array
{
U64 count;
U64 *v;
} U64Array;
typedef struct
{
U64 count;
U128 *v;
} U128Array;
typedef struct U64Node
{
struct U64Node *next;
U64 data;
} U64Node;
typedef struct U64List
{
U64 count;
U64Node *first;
U64Node *last;
} U64List;
typedef struct VoidNode
{
struct VoidNode *next;
void *v;
} VoidNode;
////////////////////////////////
internal U64Node * u64_list_push(Arena *arena, U64List *list, U64 data);
internal void u64_list_concat_in_place(U64List *list, U64List *to_concat);
internal U64Array u64_array_from_list(Arena *arena, U64List *list);
internal U64Array u64_array_remove_duplicates(Arena *arena, U64Array in);
internal void u32_array_sort(U64 count, U32 *v);
internal B32 u32_array_compare(U32Array a, U32Array b);
internal U64 sum_array_u64(U64 count, U64 *v);
internal U64 max_array_u64(U64 count, U64 *v);
internal U64 min_array_u64(U64 count, U64 *v);
internal void counts_to_offsets_array_u32(U64 count, U32 *arr);
internal void counts_to_offsets_array_u64(U64 count, U64 *arr);
internal U32 * offsets_from_counts_array_u32(Arena *arena, U32 *v, U64 count);
internal U64 * offsets_from_counts_array_u64(Arena *arena, U64 *v, U64 count);
+276
View File
@@ -0,0 +1,276 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal U32Array
bit_array_init32(Arena *arena, U64 word_count)
{
U32Array result;
result.count = CeilIntegerDiv(word_count, 32);
result.v = push_array(arena, U32, word_count);
return result;
}
internal U64
bit_array_scan_left_to_right32(U32Array bit_array, U64 lo, U64 hi, B32 state)
{
Assert(lo < bit_array.count*32);
Assert(hi <= bit_array.count*32);
Assert(lo <= hi);
Assert(state == 0 || state == 1);
U64 word_lo = lo / 32;
U64 word_hi = CeilIntegerDiv(hi, 32) - 1;
U64 word_idx = word_lo;
U64 bit_idx = 0;
U64 scan_count = hi - lo;
if (scan_count < 32) {
U64 bit_lo = lo % 32;
U64 bit_hi = hi % 32;
U64 word = bit_array.v[word_idx];
word ^= state - 1;
word &= (1U << bit_hi) - (1U << bit_lo);
if (word) {
bit_idx = ctz32(word);
goto exit;
}
} else {
U32 first_word = bit_array.v[word_idx];
first_word ^= state - 1;
first_word &= ~0u << (lo % 32);
if (first_word) {
bit_idx = ctz32(first_word);
goto exit;
}
for (word_idx += 1; word_idx < word_hi; word_idx += 1) {
U32 word = bit_array.v[word_idx];
word ^= state - 1;
if (word != 0) {
bit_idx = ctz32(word);
goto exit;
}
}
U64 bit_hi = hi - (word_idx * 32);
U32 last_word = bit_array.v[word_idx];
last_word ^= state - 1;
last_word &= (1 << bit_hi) - 1;
if (last_word) {
bit_idx = ctz32(last_word);
goto exit;
}
}
word_idx = 0;
bit_idx = max_U32;
exit:;
U64 result = word_idx * 32 + bit_idx;
return result;
}
internal U64
bit_array_scan_right_to_left32(U32Array bit_array, U64 lo, U64 hi, B32 state)
{
Assert(lo <= hi);
Assert(state == 0 || state == 1);
S64 word_lo = lo / 32;
S64 word_hi = CeilIntegerDiv(hi, 32) - 1;
S64 word_idx = word_hi;
S64 bit_idx = -1;
U64 scan_count = hi - lo;
if (scan_count < 32) {
S64 bit_lo = lo % 32;
S64 bit_hi = bit_lo + scan_count;
U32 word = bit_array.v[word_idx];
for (bit_idx = bit_hi; bit_idx >= bit_lo; bit_idx -= 1) {
U32 bit = ExtractBit(word, bit_idx);
if (bit == state) {
goto exit;
}
}
} else {
U32 last_word = bit_array.v[word_idx];
S64 bit_hi = hi % 32;
for (bit_idx = bit_hi; bit_idx >= 0; bit_idx -= 1) {
U32 bit = ExtractBit(last_word, bit_idx);
if (bit == state) {
goto exit;
}
}
for (word_idx -= 1; word_idx > word_lo; word_idx -= 1) {
U32 word = bit_array.v[word_idx];
for (bit_idx = 32 - 1; bit_idx >= 0; bit_idx -= 1) {
U32 bit = ExtractBit(word, bit_idx);
if (bit == state) {
goto exit;
}
}
}
U32 first_word = bit_array.v[word_idx];
S64 bit_lo = lo % 32;
for (bit_idx = 32 - 1; bit_idx >= bit_lo; bit_idx -= 1) {
U32 bit = ExtractBit(first_word, bit_idx);
if (bit == state) {
goto exit;
}
}
}
word_idx = 0;
bit_idx = max_U32;
exit:;
S64 result_s64 = word_idx * 32 + bit_idx;
U64 result_u64 = (U64)result_s64;
return result_u64;
}
internal Rng1U64
bit_array_scan_left_to_right32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count)
{
Rng1U64 result = rng_1u64(max_U64, max_U64);
U64 curr_count = 0, rover = lo;
while (curr_count < in_row_count) {
rover = bit_array_scan_left_to_right32(bit_array, rover, hi, state);
// no more bits in range
if (rover >= hi) {
break;
}
// set first match
if (result.v[0] == max_U64) {
result = rng_1u64(rover, rover);
continue;
}
// reset on non-contiguous range
B32 is_bit_index_not_adjoined = (result.v[0] + 1 < rover);
if (is_bit_index_not_adjoined) {
curr_count = 0;
result = rng_1u64(max_U64, max_U64);
continue;
}
// advance
result.v[1] = rover;
curr_count -= 1;
}
// did we allocate enough bits?
if (curr_count != in_row_count) {
result = rng_1u64(max_U64, max_U64);
}
return result;
}
internal Rng1U64
bit_array_scan_right_to_left32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count)
{
Rng1U64 result = rng_1u64(max_U64, max_U64);
U64 curr_count = 0, rover = lo;
while (curr_count < in_row_count) {
rover = bit_array_scan_right_to_left32(bit_array, lo, rover, state);
// no more bits in range
if (rover >= hi) {
break;
}
// set first match
if (result.v[0] == max_U64) {
result = rng_1u64(rover, rover);
continue;
}
// reset on non-contiguous range
B32 is_bit_index_not_adjoined = (result.v[0] + 1 < rover);
if (is_bit_index_not_adjoined) {
curr_count = 0;
result = rng_1u64(max_U64, max_U64);
continue;
}
// advance
result.v[0] = rover;
curr_count -= 1;
}
// did we allocate enough bits?
if (curr_count != in_row_count) {
result = rng_1u64(max_U64, max_U64);
}
return result;
}
internal U64
bit_array_find_next_unset_bit32(U32Array bit_array)
{
U64 result = bit_array_scan_left_to_right32(bit_array, 0, bit_array.count*32, 0);
return result;
}
internal U64
bit_array_find_next_set_bit32(U32Array bit_array)
{
U64 result = bit_array_scan_left_to_right32(bit_array, 0, bit_array.count*32, 1);
return result;
}
internal void
bit_array_set_bit32(U32Array bit_array, U64 idx, B32 state)
{
Assert(idx < bit_array.count*32);
U64 word_idx = idx / 32;
U64 bit_idx = idx % 32;
if (state) {
bit_array.v[word_idx] |= (1 << bit_idx);
} else {
bit_array.v[word_idx] &= ~(1 << bit_idx);
}
}
internal void
bit_array_set_bit_range32(U32Array bit_array, Rng1U64 range, B32 state)
{
for (U64 idx = range.min ; idx < range.max; idx += 1) {
bit_array_set_bit32(bit_array, idx, state);
}
}
internal U32
bit_array_get_bit32(U32Array bit_array, U64 idx)
{
Assert(idx < bit_array.count*32);
U64 word_idx = idx / 32;
U64 bit_idx = idx % 32;
U32 bit = (bit_array.v[word_idx] & (1 << bit_idx)) >> bit_idx;
return bit;
}
internal B32
bit_array_is_bit_set(U32Array bit_arr, U64 bit_pos)
{
U64 word_idx = bit_pos / 32;
Assert(word_idx < bit_arr.count);
U32 word = bit_arr.v[word_idx];
U64 bit_idx = bit_pos % 32;
B32 is_set = !!(word & (1 << bit_idx));
return is_set;
}
+18
View File
@@ -0,0 +1,18 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
internal U32Array bit_array_init32(Arena *arena, U64 word_count);
internal U64 bit_array_scan_left_to_right32(U32Array bit_array, U64 lo, U64 hi, B32 state);
internal U64 bit_array_scan_right_to_left32(U32Array bit_array, U64 lo, U64 hi, B32 state);
internal Rng1U64 bit_array_scan_left_to_right32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count);
internal Rng1U64 bit_array_scan_right_to_left32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count);
internal B32 byte_scan_right_to_left(U8 *start, U8 *opl, U8 byte, U64 *offset_out);
internal U64 bit_array_find_next_unset_bit32(U32Array bit_array);
internal U64 bit_array_find_next_set_bit32(U32Array bit_array);
internal void bit_array_set_bit32(U32Array bit_array, U64 idx, B32 state);
internal void bit_array_set_bit_range32(U32Array bit_array, Rng1U64 range, B32 state);
internal U32 bit_array_get_bit32(U32Array bit_array, U64 idx);
+102
View File
@@ -0,0 +1,102 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmacro-redefined"
#elif defined(_MSC_VER)
#pragma warning (push, 0)
#endif
#include "../third_party_ext/blake3/c/blake3_portable.c"
#if defined(_M_AMD64) || defined(__x86_64__)
#define round_fn sse2_round_fn
#define compress_pre sse2_compress_pre
#include "../third_party_ext/blake3/c/blake3_sse2.c"
#define loadu sse41_loadu
#define storeu sse41_storeu
#define addv sse41_addv
#define xorv sse41_xorv
#define set1 sse41_set1
#define set4 sse41_set4
#define rot16 sse41_rot16
#define rot12 sse41_rot12
#define rot8 sse41_rot8
#define rot7 sse41_rot7
#define g1 sse41_g1
#define g2 sse41_g2
#define diagonalize sse41_diagonalize
#define undiagonalize sse41_undiagonalize
#define compress_pre sse41_compress_pre
#define round_fn sse41_round_fn
#define transpose_vecs sse41_transpose_vecs
#define transpose_msg_vecs sse41_transpose_msg_vecs
#define load_counters sse41_load_counters
#if defined(__clang__)
#pragma clang attribute push(__attribute__((target("sse4.1"))), apply_to=function)
#endif
#include "../third_party_ext/blake3/c/blake3_sse41.c"
#if defined(__clang__)
#pragma clang attribute pop
#endif
#define loadu avx2_loadu
#define storeu avx2_storeu
#define addv avx2_addv
#define xorv avx2_xorv
#define set1 avx2_set1
#define rot7 avx2_rot7
#define rot8 avx2_rot8
#define rot12 avx2_rot12
#define rot16 avx2_rot16
#define round_fn avx2_round_fn
#define transpose_vecs avx2_transpose_vecs
#define transpose_msg_vecs avx2_transpose_msg_vecs
#define load_counters avx2_load_counters
#if defined(__clang__)
#pragma clang attribute push(__attribute__((target("avx2"))), apply_to=function)
#endif
#include "../third_party_ext/blake3/c/blake3_avx2.c"
#if defined(__clang__)
#pragma clang attribute pop
#endif
#define set4 avx512_set4
#define g1 avx512_g1
#define g2 avx512_g2
#define diagonalize avx512_diagonalize
#define undiagonalize avx512_undiagonalize
#define compress_pre avx512_compress_pre
#define transpose_vecs avx512_transpose_vecs
#define transpose_msg_vecs avx512_transpose_msg_vecs
#define load_counters avx512_load_counters
#if defined(__clang__)
#pragma clang attribute push(__attribute__((target("avx512f,avx512vl"))), apply_to=function)
#endif
#include "../third_party_ext/blake3/c/blake3_avx512.c"
#if defined(__clang__)
#pragma clang attribute pop
#endif
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#include "../third_party_ext/blake3/c/blake3_neon.c"
#endif
#include "../third_party_ext/blake3/c/blake3_dispatch.c"
#include "../third_party_ext/blake3/c/blake3.c"
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(_MSC_VER)
#pragma warning (pop, 0)
#endif
+41
View File
@@ -0,0 +1,41 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
#if defined(__clang__) && defined(__x86_64__)
# if defined(__IMMINTRIN_H)
# error "include this header before immintrin.h / x86intrin.h / intrin.h"
# endif
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wreserved-macro-identifier"
# pragma push_macro("__AVX__")
# pragma push_macro("__AVX2__")
# pragma push_macro("__SSE4_1__")
# pragma push_macro("__AVX512F__")
# pragma push_macro("__AVX512VL__")
# define __AVX__ 1
# define __AVX2__ 1
# define __SSE4_1__ 1
# define __AVX512F__ 1
# define __AVX512VL__ 1
# include <immintrin.h>
# pragma pop_macro("__AVX512VL__")
# pragma pop_macro("__AVX512F__")
# pragma pop_macro("__SSE4_1__")
# pragma pop_macro("__AVX2__")
# pragma pop_macro("__AVX__")
# pragma clang diagnostic pop
#endif
#include "../third_party_ext/blake3/c/blake3.h"
static void
blake3(void* out, size_t outlen, void* in, size_t inlen)
{
blake3_hasher hasher;
blake3_hasher_init(&hasher);
blake3_hasher_update(&hasher, in, inlen);
blake3_hasher_finalize(&hasher, (uint8_t*)out, outlen);
}
+13
View File
@@ -0,0 +1,13 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "../third_party_ext/blake3/blake3_portable.c"
#if defined(__aarch64__) || defined(_M_ARM64)
#include "../third_party_ext/blake3/blake3_neon.c"
#endif
#include "../third_party_ext/blake3/blake3_dispatch.c"
#include "../third_party_ext/blake3/blake3.c"
#pragma comment (lib, "blake3")
+18
View File
@@ -0,0 +1,18 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_BLAKE3_H
#define BASE_BLAKE3_H
#include "../third_party_ext/blake3/blake3.h"
static void
blake3(void* out, size_t outlen, void* in, size_t inlen)
{
blake3_hasher hasher;
blake3_hasher_init(&hasher);
blake3_hasher_update(&hasher, in, inlen);
blake3_hasher_finalize(&hasher, (uint8_t*)out, outlen);
}
#endif // BASE_BLAKE3_H
+227
View File
@@ -0,0 +1,227 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal U16
safe_cast_u16x(U64 x)
{
AssertAlways(x <= max_U16);
return (U16)x;
}
////////////////////////////////
internal U64
u128_mod64(U128 a, U64 b)
{
return a.u64[1] % b;
}
////////////////////////////////
internal Version
make_version(U64 major, U64 minor)
{
Version version;
version.major = major;
version.minor = minor;
return version;
}
internal int
version_compar(Version a, Version b)
{
int cmp = 0;
if (a.major < b.major) {
cmp = -1;
} else if (a.major > b.major) {
cmp = +1;
} else if (a.major == b.major) {
if (a.minor < b.minor) {
cmp = -1;
} else if (a.minor > b.minor) {
cmp = +1;
}
}
return cmp;
}
////////////////////////////////
internal ISectOff
isect_off(U32 isect, U32 off)
{
ISectOff result = { isect, off };
return result;
}
////////////////////////////////
internal int
u16_compar(const void *raw_a, const void *raw_b)
{
U16 a = *(U16*)raw_a;
U16 b = *(U16*)raw_b;
int result = a < b ? -1 :
a > b ? +1 :
0;
return result;
}
internal int
u32_compar(const void *raw_a, const void *raw_b)
{
U32 a = *(U32*)raw_a;
U32 b = *(U32*)raw_b;
int result = a < b ? -1 :
a > b ? +1 :
0;
return result;
}
internal int
u64_compar(const void *raw_a, const void *raw_b)
{
U64 a = *(const U64*)raw_a;
U64 b = *(const U64*)raw_b;
int result = a < b ? -1 : a > b ? +1 : 0;
return result;
}
internal int
u64_compar_inv(const void *raw_a, const void *raw_b)
{
U64 a = *(const U64*)raw_a;
U64 b = *(const U64*)raw_b;
int result = a < b ? +1 : a > b ? -1 : 0;
return result;
}
internal int
u16_compar_is_before(void *raw_a, void *raw_b)
{
U16 *a = (U16 *)raw_a;
U16 *b = (U16 *)raw_b;
int is_before = *a < *b;
return is_before;
}
internal int
u32_compar_is_before(void *raw_a, void *raw_b)
{
U32 *a = (U32 *)raw_a;
U32 *b = (U32 *)raw_b;
int is_before = *a < *b;
return is_before;
}
internal int
u64_compar_is_before(void *raw_a, void *raw_b)
{
U64 *a = (U64 *)raw_a;
U64 *b = (U64 *)raw_b;
int is_before = *a < *b;
return is_before;
}
internal int
u8_is_before(void *raw_a, void *raw_b)
{
U8 *a = (U8 *) raw_a;
U8 *b = (U8 *) raw_b;
return *a < *b;
}
internal int
u16_is_before(void *raw_a, void *raw_b)
{
U16 *a = (U16 *) raw_a;
U16 *b = (U16 *) raw_b;
return *a < *b;
}
internal int
u32_is_before(void *raw_a, void *raw_b)
{
U32 *a = (U32 *) raw_a;
U32 *b = (U32 *) raw_b;
return *a < *b;
}
internal int
u64_is_before(void *raw_a, void *raw_b)
{
U64 *a = (U64 *) raw_a;
U64 *b = (U64 *) raw_b;
return *a < *b;
}
internal int
pair_u32_is_before_v0(void *raw_a, void *raw_b)
{
PairU32 *a = raw_a;
PairU32 *b = raw_b;
return a->v0 < b->v0;
}
internal int
pair_u32_is_before(void *raw_a, void *raw_b)
{
PairU32 *a = raw_a;
PairU32 *b = raw_b;
return a->v1 < b->v1;
}
internal int
pair_u64_is_before_v0(void *raw_a, void *raw_b)
{
PairU64 *a = raw_a;
PairU64 *b = raw_b;
return a->v0 < b->v0;
}
internal int
pair_u64_is_before_v1(void *raw_a, void *raw_b)
{
PairU64 *a = raw_a;
PairU64 *b = raw_b;
return a->v1 < b->v1;
}
internal int
pair_u32_compar_v0(const void *raw_a, const void *raw_b)
{
const PairU32 *a = raw_a;
const PairU32 *b = raw_b;
return u32_compar(&a->v0, &b->v0);
}
internal int
pair_u64_compar_v0(const void *raw_a, const void *raw_b)
{
const PairU64 *a = raw_a;
const PairU64 *b = raw_b;
return u64_compar(&a->v0, &b->v0);
}
internal int
pair_u64_compar_v1(const void *raw_a, const void *raw_b)
{
const PairU64 *a = raw_a;
const PairU64 *b = raw_b;
return u64_compar(&a->v1, &b->v1);
}
////////////////////////////////
internal void
str8_list_concat_in_place_array(String8List *list, String8List *arr, U64 count)
{
for (U64 i = 0; i < count; ++i) {
str8_list_concat_in_place(list, &arr[i]);
}
}
+199
View File
@@ -0,0 +1,199 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
#if COMPILER_MSVC
# define COMPILER_STRING "MSVC"
#elif COMPILER_CLANG
# define COMPILER_STRING "Clang"
#elif COMPILER_GCC
# define COMPILER_STRING "GCC"
#else
# error "undefined compiler string"
#endif
#if BUILD_DEBUG
# define BUILD_MODE_STRING "Debug"
#else
# define BUILD_MODE_STRING "Release"
#endif
////////////////////////////////
#define BitExtract(x, count, shift) (((x) >> (shift)) & ((1 << (count)) - 1))
////////////////////////////////
#if OS_WINDOWS
# define ins_atomic_ptr_eval_cond_assign(x,k,c) InterlockedCompareExchangePointer((volatile PVOID *)(x),(k),(c))
# define ins_atomic_u32_add_eval(x,c) InterlockedAdd((volatile LONG *)(x), c)
# define ins_atomic_u32_inc_eval(x) InterlockedIncrement((volatile LONG *)x)
#else
# error "atomics are not defined for this system"
#endif
////////////////////////////////
// Linked List Helpers
#define DLLConcatInPlace(list, to_concat) do { \
if ((to_concat)->count) { \
if ((list)->count) { \
(list)->last->next = (to_concat)->first; \
(to_concat)->first->prev = (list)->last; \
(list)->last = (to_concat)->last; \
} else { \
(list)->first = (to_concat)->first; \
(list)->last = (to_concat)->last; \
} \
(list)->count += (to_concat)->count; \
MemoryZeroStruct(to_concat); \
} \
} while (0)
#define DLLConcatInPlaceArray(list, to_concat_arr, count) for (U64 i = 0; i < (count); i += 1) { DLLConcatInPlace(list, &(to_concat_arr)[i]); }
#define SLLQueuePushCount(list, node) do { \
SLLQueuePush((list)->first, (list)->last, node); \
++(list)->count; \
} while (0)
#define SLLConcatInPlaceNoCount(list, to_concat) do { \
if ((to_concat)->first) { \
if ((list)->first) { \
(list)->last->next = (to_concat)->first; \
(list)->last = (to_concat)->last; \
} else { \
(list)->first = (to_concat)->first; \
(list)->last = (to_concat)->last; \
} \
MemoryZeroStruct(to_concat); \
} \
} while (0)
#define SLLConcatInPlace(list, to_concat) do { \
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); \
} \
} while (0)
#define SLLConcatInPlaceArray(list, to_concat_arr, count) for (U64 i = 0; i < (count); ++i) { SLLConcatInPlace(list, &(to_concat_arr)[i]); }
#define SLLConcatInPlaceChunkList(list, to_concat, chunk_type) do { \
if ((list)->last != 0) { \
U64 base_cursor = (list)->last->base + (list)->last->count; \
for (chunk_type *c = (to_concat)->first; c != 0; c = c->next) { \
c->base = base_cursor; \
base_cursor += c->count; \
} \
} \
SLLConcatInPlace(list, to_concat); \
} while (0)
#define SLLConcatInPlaceChunkListArray(list, to_concat_arr, type, count) for (U64 i = 0; i < (count); ++i) { SLLConcatInPlaceChunkList(list, &(to_concat_arr)[i], type); }
#define SLLChunkListPush(_arena, _list, _cap, _value_type) do { \
if ((_list)->last == 0 || (_list)->last->count >= (_list)->last->cap) { \
_value_type##Chunk *new_chunk = push_array(_arena, _value_type##Chunk, 1); \
new_chunk->v = push_array(_arena, _value_type, _cap); \
new_chunk->cap = _cap; \
new_chunk->base = (_list)->last ? (_list)->last->base + (_list)->last->cap : 0; \
SLLQueuePushCount(_list, new_chunk); \
} \
_value_type *v = &(_list)->last->v[(_list)->last->count++]; \
v->chunk = (_list)->last; \
} while (0)
#define SLLChunkListPushZero(_arena, _list, _cap, _value_type) do { \
SLLChunkListPush(_arena, _list, _cap, _value_type); \
MemoryZeroStruct(SLLChunkListLastItem(_list)); \
SLLChunkListLastItem(_list)->chunk = (_list)->last; \
} while(0)
#define SLLChunkListLastItem(_list) (&(_list)->last->v[(_list)->last->count - 1])
////////////////////////////////
#define MemoryIsZeroStruct(p) memory_is_zero(p, sizeof(*p))
////////////////////////////////
#if ARCH_LITTLE_ENDIAN
# define BE_U32(x) bswap_u32(x)
#else
# define BE_U32(x) (x)
#endif
////////////////////////////////
typedef struct
{
U64 major;
U64 minor;
} Version;
////////////////////////////////
typedef struct ISectOff
{
U32 isect;
U32 off;
} ISectOff;
////////////////////////////////
typedef struct PairU32
{
U32 v0;
U32 v1;
} PairU32;
typedef struct PairU64
{
U64 v0;
U64 v1;
} PairU64;
////////////////////////////////
internal U16 safe_cast_u16x(U64 x);
////////////////////////////////
internal U64 u128_mod64(U128 a, U64 b);
////////////////////////////////
internal Version make_version(U64 major, U64 minor);
internal int version_compar(Version a, Version b);
////////////////////////////////
internal ISectOff isect_off(U32 isect, U32 off);
////////////////////////////////
internal int u16_compar(const void *raw_a, const void *raw_b);
internal int u32_compar(const void *raw_a, const void *raw_b);
internal int u64_compar(const void *raw_a, const void *raw_b);
internal int u8_is_before(void *raw_a, void *raw_b);
internal int u16_is_before(void *raw_a, void *raw_b);
internal int u32_is_before(void *raw_a, void *raw_b);
internal int u64_is_before(void *raw_a, void *raw_b);
internal int pair_u32_is_before_v0(void *raw_a, void *raw_b);
internal int pair_u32_is_before_v1(void *raw_a, void *raw_b);
internal int pair_u64_is_before_v0(void *raw_a, void *raw_b);
internal int pair_u64_is_before_v1(void *raw_a, void *raw_b);
////////////////////////////////
internal void str8_list_concat_in_place_array(String8List *list, String8List *arr, U64 count);
+72
View File
@@ -0,0 +1,72 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal U32
update_crc32(U32 crc, U8 *ptr, U64 size)
{
// CRC-32 algo borrowed from stb.h
local_persist U32 crc_table[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535,
0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd,
0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d,
0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac,
0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab,
0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb,
0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea,
0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce,
0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409,
0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739,
0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268,
0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0,
0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8,
0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703,
0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7,
0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae,
0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6,
0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d,
0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5,
0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
#if 0
for (U32 i = 0; i < 256; ++i) {
U32 s = i;
for (U32 j = 0; j < 8; ++j) {
s = (s >> 1) ^ (s & 1 ? 0xedb88320 : 0);
}
crc_table[i] = s;
}
#endif
crc = ~crc;
for (U32 i = 0; i < size; ++i) {
crc = (crc >> 8) ^ crc_table[(ptr[i] ^ crc) & 0xff];
}
return ~crc;
}
internal U32
crc32_from_string(String8 string)
{
return update_crc32(0, string.str, string.size);
}
+8
View File
@@ -0,0 +1,8 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
internal U32 update_crc32(U32 crc, U8 *ptr, U64 size);
internal U32 crc32_from_string(String8 string);
+12
View File
@@ -0,0 +1,12 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "base_core.c"
#include "base_strings.c"
#include "base_arena.c"
#include "base_math.c"
#include "base_arrays.c"
#include "base_bit_array.c"
#include "base_crc32.c"
#include "base_md5.c"
+15
View File
@@ -0,0 +1,15 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
#include "base_core.h"
#include "base_strings.h"
#include "base_arena.h"
#include "base_math.h"
#include "base_arrays.h"
#include "base_blake3.h"
#include "base_bit_array.h"
#include "base_crc32.h"
#include "base_md5.h"
+19
View File
@@ -0,0 +1,19 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal void
rng_1u64_list_push_node(Rng1U64List *list, Rng1U64Node *node)
{
SLLQueuePush(list->first, list->last, node);
++list->count;
}
internal Rng1U64Node *
rng_1u64_list_push(Arena *arena, Rng1U64List *list, Rng1U64 range)
{
Rng1U64Node *node = push_array(arena, Rng1U64Node, 1);
node->v = range;
rng_1u64_list_push_node(list, node);
return node;
}
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef struct Rng1U64Node
{
struct Rng1U64Node *next;
Rng1U64 v;
} Rng1U64Node;
typedef struct Rng1U64List
{
U64 count;
Rng1U64Node *first;
Rng1U64Node *last;
} Rng1U64List;
////////////////////////////////
internal void rng_1u64_list_push_node(Rng1U64List *list, Rng1U64Node *node);
internal Rng1U64Node * rng_1u64_list_push(Arena *arena, Rng1U64List *list, Rng1U64 range);
+12
View File
@@ -0,0 +1,12 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal MD5Hash
md5_hash_from_string(String8 data)
{
MD5_CTX ctx; MD5_Init(&ctx);
MD5_Update(&ctx, (void*)data.str, safe_cast_u32(data.size));
MD5Hash hash; MD5_Final((unsigned char*)&hash, &ctx);
return hash;
}
+12
View File
@@ -0,0 +1,12 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef struct MD5Hash
{
U8 value[16];
} MD5Hash;
internal MD5Hash md5_hash_from_string(String8 data);
+248
View File
@@ -0,0 +1,248 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
global read_only String8 g_null_string;
internal String8
str8_cstring_capped_reverse(void *start, void *cap)
{
char *ptr = cap;
while (ptr > (char *)start) {
--ptr;
if (*ptr == '\0') break;
}
U64 null_offset = (U64)(ptr - (char *) start);
String8 result = str8((U8 *) start, null_offset);
return result;
}
internal U64
str8_find_needle_reverse(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags)
{
for (S64 i = string.size - start_pos - needle.size; i >= 0; --i) {
String8 haystack = str8_substr(string, rng_1u64(i, i + needle.size));
if (str8_match(haystack, needle, flags)) {
return (U64)i + needle.size;
}
}
return 0;
}
internal int
str8_compar(String8 a, String8 b, B32 ignore_case)
{
int cmp = 0;
U64 size = Min(a.size, b.size);
if (ignore_case) {
for (U64 i = 0; i < size; ++i) {
U8 la = char_to_lower(a.str[i]);
U8 lb = char_to_lower(b.str[i]);
if (la < lb) {
cmp = -1;
break;
} else if (la > lb) {
cmp = +1;
break;
}
}
} else {
for (U64 i = 0; i < size; ++i) {
if (a.str[i] < b.str[i]) {
cmp = -1;
break;
} else if (a.str[i] > b.str[i]) {
cmp = +1;
break;
}
}
}
if (cmp == 0) {
// shorter prefix must precede longer prefixes
if (a.size > b.size) {
cmp = +1;
} else if (b.size > a.size) {
cmp = -1;
}
}
return cmp;
}
internal int
str8_compar_ignore_case(const void *a, const void *b)
{
return str8_compar(*(String8*)a, *(String8*)b, 1);
}
internal int
str8_compar_case_sensetive(const void *a, const void *b)
{
return str8_compar(*(String8*)a, *(String8*)b, 0);
}
internal int
str8_is_before_case_sensetive(const void *a, const void *b)
{
int cmp = str8_compar_case_sensetive(a, b);
return cmp < 0;
}
internal String8Node *
str8_list_push_raw(Arena *arena, String8List *list, void *data_ptr, U64 data_size)
{
String8 data = str8((U8 *)data_ptr, data_size);
String8Node *node = str8_list_push(arena, list, data);
return node;
}
internal U64
str8_list_push_pad(Arena *arena, String8List *list, U64 offset, U64 align)
{
U64 pad_size = AlignPow2(offset, align) - offset;
U8 *pad = push_array(arena, U8, pad_size);
MemorySet(pad, 0, pad_size);
str8_list_push(arena, list, str8(pad, pad_size));
return pad_size;
}
internal U64
str8_list_push_pad_front(Arena *arena, String8List *list, U64 offset, U64 align)
{
U64 pad_size = AlignPow2(offset, align) - offset;
U8 *pad = push_array(arena, U8, pad_size);
MemorySet(pad, 0, pad_size);
str8_list_push_front(arena, list, str8(pad, pad_size));
return pad_size;
}
internal String8List
str8_list_arr_concat(String8List *v, U64 count)
{
String8List result = {0};
for (U64 i = 0; i < count; i += 1) {
str8_list_concat_in_place(&result, &v[i]);
}
return result;
}
internal String8Node *
str8_list_push_many(Arena *arena, String8List *list, U64 count)
{
String8Node *arr = push_array(arena, String8Node, count);
for (U64 i = 0; i < count; ++i) {
str8_list_push_node(list, arr + i);
}
return arr;
}
internal String8
str8_from_bits_u32(Arena *arena, U32 x)
{
U8 c0 = 'a' + ((x >> 28) & 0xf);
U8 c1 = 'a' + ((x >> 24) & 0xf);
U8 c2 = 'a' + ((x >> 20) & 0xf);
U8 c3 = 'a' + ((x >> 16) & 0xf);
U8 c4 = 'a' + ((x >> 12) & 0xf);
U8 c5 = 'a' + ((x >> 8) & 0xf);
U8 c6 = 'a' + ((x >> 4) & 0xf);
U8 c7 = 'a' + ((x >> 0) & 0xf);
String8 result = push_str8f(arena, "%c%c%c%c%c%c%c%c", c0, c1, c2, c3, c4, c5, c6, c7);
return result;
}
internal String8
str8_from_bits_u64(Arena *arena, U64 x)
{
U8 c0 = 'a' + ((x >> 60) & 0xf);
U8 c1 = 'a' + ((x >> 56) & 0xf);
U8 c2 = 'a' + ((x >> 52) & 0xf);
U8 c3 = 'a' + ((x >> 48) & 0xf);
U8 c4 = 'a' + ((x >> 44) & 0xf);
U8 c5 = 'a' + ((x >> 40) & 0xf);
U8 c6 = 'a' + ((x >> 36) & 0xf);
U8 c7 = 'a' + ((x >> 32) & 0xf);
U8 c8 = 'a' + ((x >> 28) & 0xf);
U8 c9 = 'a' + ((x >> 24) & 0xf);
U8 ca = 'a' + ((x >> 20) & 0xf);
U8 cb = 'a' + ((x >> 16) & 0xf);
U8 cc = 'a' + ((x >> 12) & 0xf);
U8 cd = 'a' + ((x >> 8) & 0xf);
U8 ce = 'a' + ((x >> 4) & 0xf);
U8 cf = 'a' + ((x >> 0) & 0xf);
String8 result = push_str8f(arena,
"%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf);
return result;
}
internal String8Node *
str8_list_pop_front(String8List *list)
{
String8Node *node = 0;
if (list->node_count) {
node = list->first;
Assert(list->total_size >= list->first->string.size);
list->node_count -= 1;
list->total_size -= list->first->string.size;
SLLQueuePop(list->first, list->last);
}
return node;
}
internal String8
str8_from_memory_size2(Arena *arena, U64 size)
{
String8 result;
if (size < KB(1)) {
result = push_str8f(arena, "%llu Bytes", size);
} else if (size < MB(1)) {
result = push_str8f(arena, "%llu.%02llu KiB", size / KB(1), ((size * 100) / KB(1)) % 100);
} else if (size < GB(1)) {
result = push_str8f(arena, "%llu.%02llu MiB", size / MB(1), ((size * 100) / MB(1)) % 100);
} else if (size < TB(1)) {
result = push_str8f(arena, "%llu.%02llu GiB", size / GB(1), ((size * 100) / GB(1)) % 100);
} else {
result = push_str8f(arena, "%llu.%02llu TiB", size / TB(1), ((size * 100) / TB(1)) % 100);
}
return result;
}
internal String8
str8_from_count(Arena *arena, U64 count)
{
String8 result;
if (count < 1000) {
result = push_str8f(arena, "%llu", count);
} else if (count < 1000000) {
U64 frac = ((count * 100) / 1000) % 100;
if (frac) {
result = push_str8f(arena, "%llu.%02lluK", count / 1000, frac);
} else {
result = push_str8f(arena, "%lluK", count / 1000);
}
} else if (count < 1000000000) {
U64 frac = ((count * 100) / 1000000) % 100;
if (frac) {
result = push_str8f(arena, "%llu.%02lluM", count / 1000000, frac);
} else {
result = push_str8f(arena, "%lluM", count / 1000000);
}
} else {
U64 frac = ((count * 100) * 1000000000) % 100;
if (frac) {
result = push_str8f(arena, "%llu.%02lluB", count / 1000000000, frac);
} else {
result = push_str8f(arena, "%lluB", count / 1000000000, frac);
}
}
return result;
}
internal U64
hash_from_str8(String8 string)
{
XXH64_hash_t hash64 = XXH3_64bits(string.str, string.size);
return hash64;
}
+33
View File
@@ -0,0 +1,33 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
#define MemoryCopyStr8(dst, s) MemoryCopy(dst, (s).str, (s).size)
internal String8 str8_cstring_capped_reverse(void *start, void *cap);
internal U64 str8_find_needle_reverse(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags);
internal int str8_compar(String8 a, String8 b, B32 ignore_case);
internal int str8_compar_ignore_case(const void *a, const void *b);
internal int str8_compar_case_sensetive(const void *a, const void *b);
#define str8_list_push_struct(a,l,d) str8_list_push_raw(a, l, d, sizeof(*d))
internal String8Node * str8_list_push_raw(Arena *arena, String8List *list, void *data_ptr, U64 data_size);
internal U64 str8_list_push_pad(Arena *arena, String8List *list, U64 offset, U64 align);
internal U64 str8_list_push_pad_front(Arena *arena, String8List *list, U64 offset, U64 align);
internal String8List str8_list_arr_concat(String8List *v, U64 count);
internal String8Node * str8_list_push_many(Arena *arena, String8List *list, U64 count);
internal String8 str8_from_bits_u32(Arena *arena, U32 x);
internal String8 str8_from_bits_u64(Arena *arena, U64 x);
// TODO: remove
internal String8Node * str8_list_pop_front(String8List *list);
internal String8 str8_from_memory_size2(Arena *arena, U64 size);
internal String8 str8_from_count(Arena *arena, U64 count);
internal U64 hash_from_str8(String8 string);
File diff suppressed because it is too large Load Diff
+571
View File
@@ -0,0 +1,571 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
#define CV_MinComplexTypeIndex 0x1000
////////////////////////////////
// Aligns
#define CV_LeafAlign 4
#define CV_SymbolAlign 1
#define CV_C13SubSectionAlign 4
#define CV_FileCheckSumsAlign 4
////////////////////////////////
//- Symbol and Leaf Headers
#define CV_LeafSize_Max max_U16
typedef U16 CV_LeafSize;
#define CV_SymSize_Max max_U16
typedef U16 CV_SymSize;
typedef struct CV_LeafHeader
{
CV_LeafSize size;
CV_LeafKind kind;
} CV_LeafHeader;
typedef struct CV_SymbolHeader
{
CV_SymSize size;
CV_SymKind kind;
} CV_SymbolHeader;
////////////////////////////////
// Type Index Helpers
typedef enum CV_TypeIndexSource
{
CV_TypeIndexSource_NULL,
CV_TypeIndexSource_TPI,
CV_TypeIndexSource_IPI,
CV_TypeIndexSource_COUNT
} CV_TypeIndexSource;
typedef struct CV_TypeIndexInfo
{
struct CV_TypeIndexInfo *next;
U64 offset;
CV_TypeIndexSource source;
} CV_TypeIndexInfo;
typedef struct CV_TypeIndexInfoList
{
U64 count;
CV_TypeIndexInfo *first;
CV_TypeIndexInfo *last;
} CV_TypeIndexInfoList;
typedef struct CV_TypeIndexArray
{
U32 count;
CV_TypeIndex *v;
} CV_TypeIndexArray;
//- $$Symbols
typedef struct CV_Symbol
{
CV_SymKind kind;
U64 offset;
String8 data;
} CV_Symbol;
typedef struct CV_SymbolNode
{
struct CV_SymbolNode *next;
struct CV_SymbolNode *prev;
CV_Symbol data;
} CV_SymbolNode;
typedef struct CV_SymbolPtrNode
{
struct CV_SymbolPtrNode *next;
CV_Symbol *data;
} CV_SymbolPtrNode;
typedef struct CV_SymbolList
{
U64 count;
CV_Signature signature;
CV_SymbolNode *first;
CV_SymbolNode *last;
} CV_SymbolList;
typedef struct CV_SymbolListArray
{
U64 count;
CV_SymbolList *v;
} CV_SymbolListArray;
typedef struct CV_SymbolPtrArray
{
U64 count;
CV_SymbolNode **v;
} CV_SymbolPtrArray;
typedef struct CV_Scope
{
struct CV_ScopeList *children;
struct CV_Scope *next;
struct CV_Scope *prev;
CV_Symbol symbol;
} CV_Scope;
typedef struct CV_ScopeList
{
CV_Scope *first;
CV_Scope *last;
} CV_ScopeList;
typedef struct CV_ScopeFrame
{
struct CV_ScopeFrame *next;
CV_ScopeList *list;
CV_Scope *curr;
U64 symbol_off;
U32 *parent_off_ptr;
U32 *end_off_ptr;
} CV_ScopeFrame;
//- $$FileChksms
typedef struct CV_Checksum
{
CV_C13Checksum *header;
String8 value;
} CV_Checksum;
typedef struct CV_ChecksumNode
{
struct CV_ChecksumNode *next;
CV_Checksum data;
} CV_ChecksumNode;
typedef struct CV_ChecksumList
{
U64 count;
CV_ChecksumNode *first;
CV_ChecksumNode *last;
} CV_ChecksumList;
//- $$Lines
typedef struct CV_LineArray
{
U32 file_off;
U64 line_count;
U64 col_count;
U64 *voffs; // [line_count + 1]
U32 *line_nums; // [line_count]
U16 *col_nums; // [line_count * 2]
} CV_LineArray;
typedef struct CV_File
{
U32 file_off;
CV_LineArray lines;
} CV_File;
typedef struct CV_C13LinesHeader
{
U64 sec_idx;
U64 sec_off_lo;
U64 sec_off_hi;
U64 file_off;
U64 line_count;
U64 col_count;
U64 line_array_off;
U64 col_array_off;
} CV_C13LinesHeader;
typedef struct CV_C13LinesHeaderNode
{
struct CV_C13LinesHeaderNode *next;
CV_C13LinesHeader v;
} CV_C13LinesHeaderNode;
typedef struct CV_C13LinesHeaderList
{
CV_C13LinesHeaderNode *first;
CV_C13LinesHeaderNode *last;
U64 count;
} CV_C13LinesHeaderList;
////////////////////////////////
typedef struct CV_UDTInfo
{
String8 name;
String8 unique_name;
CV_TypeProps props;
} CV_UDTInfo;
typedef struct CV_TypeServerInfo
{
String8 name;
COFF_Guid sig;
U32 age;
} CV_TypeServerInfo;
typedef struct CV_PrecompInfo
{
CV_TypeIndex start_index;
U32 sig;
U32 leaf_count;
String8 obj_name;
} CV_PrecompInfo;
typedef struct CV_ObjInfo
{
U32 sig;
String8 name;
} CV_ObjInfo;
////////////////////////////////
// Accels
typedef struct CV_Line
{
U64 voff;
U32 file_off;
U32 line_num;
U16 col_num;
} CV_Line;
typedef struct CV_LinesAccel
{
U64 map_count;
CV_Line *map;
} CV_LinesAccel;
typedef struct CV_InlineeLinesAccel
{
U64 bucket_count;
U64 bucket_max;
CV_C13InlineeLinesParsed **buckets;
} CV_InlineeLinesAccel;
typedef struct CV_InlineBinaryAnnotsParsed
{
U64 lines_count;
CV_LineArray *lines;
Rng1U64List code_ranges;
} CV_InlineBinaryAnnotsParsed;
typedef struct CV_C13InlineeLinesParsedList
{
CV_C13InlineeLinesParsedNode *first;
CV_C13InlineeLinesParsedNode *last;
U64 count;
} CV_C13InlineeLinesParsedList;
////////////////////////////////
typedef U32 CV_C13SubSectionIdxKind;
enum
{
CV_C13SubSectionIdxKind_NULL,
#define X(N,c) CV_C13SubSectionIdxKind_##N,
CV_C13SubSectionKindXList(X)
#undef X
CV_C13SubSectionIdxKind_COUNT
};
typedef struct CV_C13SubSectionList
{
CV_C13SubSectionNode *first;
CV_C13SubSectionNode *last;
U64 count;
} CV_C13SubSectionList;
////////////////////////////////
typedef struct CV_DebugS
{
String8List data_list[CV_C13SubSectionIdxKind_COUNT];
} CV_DebugS;
typedef struct CV_DebugT
{
U64 size;
U64 count;
U8 **v;
} CV_DebugT;
////////////////////////////////
//~ Leaf Helpers
typedef struct CV_Leaf
{
CV_LeafKind kind;
String8 data;
} CV_Leaf;
typedef struct CV_LeafNode
{
struct CV_LeafNode *next;
CV_Leaf data;
} CV_LeafNode;
typedef struct CV_LeafList
{
U64 count;
CV_LeafNode *first;
CV_LeafNode *last;
} CV_LeafList;
////////////////////////////////
//~ String Hash Table
typedef struct CV_StringTableRange
{
struct CV_StringTableRange *next;
Rng1U64 range;
U64 debug_s_idx;
} CV_StringTableRange;
typedef struct CV_StringBucket
{
String8 string;
union {
struct {
U32 idx0;
U32 idx1;
};
U64 offset;
} u;
} CV_StringBucket;
typedef struct CV_StringHashTable
{
U64 total_string_size;
U64 total_insert_count;
U64 bucket_cap;
CV_StringBucket **buckets;
} CV_StringHashTable;
typedef struct CV_StringHashTableResult
{
U64 string_count;
CV_StringBucket **buckets;
} CV_StringHashTableResult;
////////////////////////////////
//~ Task Contexts
typedef struct
{
U64 cap;
union {
CV_SymbolNode ***buckets;
CV_SymbolNode **deref_buckets;
} u;
Rng1U64 *ranges;
CV_SymbolNode **symbols;
} CV_SymbolDeduperTask;
typedef struct
{
CV_SymbolList *list_arr;
Rng1U64 *list_range_arr;
U64 *symbol_base_arr;
CV_SymbolNode **symbol_arr;
} CV_SymbolListSyncer;
typedef struct
{
CV_DebugS *arr;
CV_StringTableRange **range_lists;
U64 *string_counts;
U64 bucket_cap;
CV_StringBucket **buckets;
U64 total_string_size;
U64 total_insert_count;
} CV_DedupStringTablesTask;
typedef struct
{
U8 *buffer;
Rng1U64 *ranges;
CV_StringBucket **buckets;
} CV_PackStringHashTableTask;
typedef struct
{
CV_DebugT debug_t;
Rng1U64 *ranges;
String8List *lists;
String8Node *nodes;
} CV_Str8ListFromDebugT;
////////////////////////////////
// Type Index Helpers
internal CV_TypeIndexInfo * cv_symbol_type_index_info_push(Arena *arena, CV_TypeIndexInfoList *list, CV_TypeIndexSource source, U64 offset);
internal CV_TypeIndexInfoList cv_get_symbol_type_index_offsets(Arena *arena, CV_SymKind kind, String8 data);
internal CV_TypeIndexInfoList cv_get_leaf_type_index_offsets(Arena *arena, CV_LeafKind leaf_kind, String8 data);
internal CV_TypeIndexInfoList cv_get_inlinee_type_index_offsets(Arena *arena, String8 raw_data);
internal String8Array cv_get_data_around_type_indices(Arena *arena, CV_TypeIndexInfoList ti_list, String8 data);
internal CV_TypeIndexSource cv_type_index_source_from_leaf_kind(CV_LeafKind leaf_kind);
////////////////////////////////
internal U64 cv_name_offset_from_symbol(CV_SymKind kind, String8 data);
internal String8 cv_name_from_symbol (CV_SymKind kind, String8 data);
internal String8 cv_name_from_udt_info (CV_UDTInfo udt_info);
internal B32 cv_is_udt_name_anon (String8 name);
internal B32 cv_is_udt (CV_LeafKind kind);
internal B32 cv_is_global_symbol (CV_SymKind kind);
internal B32 cv_is_typedef (CV_SymKind kind);
internal B32 cv_is_scope_symbol (CV_SymKind kind);
internal B32 cv_is_end_symbol (CV_SymKind kind);
internal B32 cv_is_leaf_type_server(CV_LeafKind kind);
internal B32 cv_is_leaf_pch (CV_LeafKind kind);
internal CV_ObjInfo cv_obj_info_from_symbol(CV_Symbol symbol);
internal CV_TypeServerInfo cv_type_server_info_from_leaf(CV_Leaf leaf);
internal CV_PrecompInfo cv_precomp_info_from_leaf(CV_Leaf leaf);
internal B32 cv_is_reg_sp(CV_Arch arch, CV_Reg reg);
////////////////////////////////
//~ Leaf Helpers
internal U64 cv_compute_leaf_record_size(String8 data, U64 align);
internal U64 cv_serialize_leaf_to_buffer(U8 *buffer, U64 buffer_cursor, U64 buffer_size, CV_LeafKind kind, String8 data, U64 align);
internal String8 cv_serialize_leaf_ex(Arena *arena, CV_LeafKind kind, String8 data, U64 align);
internal String8 cv_serialize_leaf(Arena *arena, CV_Leaf *leaf, U64 align);
internal CV_Leaf cv_make_leaf(Arena *arena, CV_LeafKind kind, String8 data);
internal U64 cv_deserial_leaf(String8 raw_data, U64 off, U64 align, CV_Leaf *leaf_out);
internal CV_Leaf cv_leaf_from_string(String8 raw_data);
////////////////////////////////
//~ Symbol Helpers
internal U64 cv_compute_symbol_record_size(CV_Symbol *symbol, U64 align);
internal U64 cv_serialize_symbol_to_buffer(U8 *buffer, U64 buffer_cursor, U64 buffer_size, CV_Symbol *symbol, U64 align);
internal String8 cv_serialize_symbol(Arena *arena, CV_Symbol *symbol, U64 align);
internal String8 cv_make_symbol(Arena *arena, CV_SymKind kind, String8 data);
internal String8 cv_make_obj_name(Arena *arena, String8 obj_path, U32 sig);
internal String8 cv_make_comp3(Arena *arena,
CV_Compile3Flags flags, CV_Language lang, CV_Arch arch,
U16 ver_fe_major, U16 ver_fe_minor, U16 ver_fe_build, U16 ver_feqfe,
U16 ver_major, U16 ver_minor, U16 ver_build, U16 ver_qfe,
String8 version_string);
internal String8 cv_make_envblock(Arena *arena, String8List string_list);
internal CV_Symbol cv_make_proc_ref(Arena *arena, CV_ModIndex imod, U32 stream_offset, String8 name, B32 is_local);
internal CV_Symbol cv_make_pub32(Arena *arena, CV_Pub32Flags flags, U32 off, U16 isect, String8 name);
internal CV_SymbolList cv_make_proc_refs(Arena *arena, CV_ModIndex imod, CV_SymbolList symbol_list);
////////////////////////////////
// .debug$S Helpers
internal CV_DebugS cv_parse_debug_s_c13(Arena *arena, String8 raw_debug_s);
internal CV_DebugS cv_parse_debug_s_c13_list(Arena *arena, String8List raw_debug_s);
internal CV_DebugS cv_parse_debug_s(Arena *arena, String8 raw_debug_s);
internal void cv_debug_s_concat_in_place(CV_DebugS *dst, CV_DebugS *src);
internal String8List cv_data_c13_from_debug_s(Arena *arena, CV_DebugS *debug_s, B32 write_sig);
internal CV_C13SubSectionIdxKind cv_c13_sub_section_idx_from_kind(CV_C13SubSectionKind kind);
internal String8List * cv_sub_section_ptr_from_debug_s(CV_DebugS *debug_s, CV_C13SubSectionKind kind);
internal String8List cv_sub_section_from_debug_s(CV_DebugS debug_s, CV_C13SubSectionKind kind);
internal String8 cv_string_table_from_debug_s(CV_DebugS debug_s);
internal String8 cv_file_chksms_from_debug_s(CV_DebugS debug_s);
////////////////////////////////
//~ .debug$T helpers
internal CV_DebugT cv_debug_t_from_data_arr(Arena *arena, String8Array data_arr, U64 align);
internal CV_DebugT cv_debug_t_from_data(Arena *arena, String8 data, U64 align);
internal CV_Leaf cv_debug_t_get_leaf(CV_DebugT debug_t, U64 leaf_idx);
internal String8 cv_debug_t_get_raw_leaf(CV_DebugT debug_t, U64 leaf_idx);
internal CV_LeafHeader * cv_debug_t_get_leaf_header(CV_DebugT debug_t, U64 leaf_idx);
internal B32 cv_debug_t_is_pch(CV_DebugT debug_t);
internal B32 cv_debug_t_is_type_server(CV_DebugT debug_t);
internal U64 cv_debug_t_array_count_leaves(U64 count, CV_DebugT *arr);
internal String8List cv_str8_list_from_debug_t_parallel(TP_Context *tp, Arena *arena, CV_DebugT types);
// $$Symbols
internal void cv_parse_symbol_sub_section(Arena *arena, CV_SymbolList *list, U64 offset_base, String8 data, U64 align);
internal void cv_symbol_list_push_node(CV_SymbolList *list, CV_SymbolNode *node);
internal CV_SymbolNode * cv_symbol_list_push(Arena *arena, CV_SymbolList *list);
internal CV_SymbolNode * cv_symbol_list_push_data(Arena *arena, CV_SymbolList *list, CV_SymKind kind, String8 data);
internal CV_SymbolNode * cv_symbol_list_push_many(Arena *arena, CV_SymbolList *list, U64 count);
internal void cv_symbol_list_remove_node(CV_SymbolList *list, CV_SymbolNode *node);
internal void cv_symbol_list_concat_in_place(CV_SymbolList *list, CV_SymbolList *to_concat);
internal void cv_symbol_list_concat_in_place_arr(CV_SymbolList *list, U64 count, CV_SymbolList *to_concat);
internal U64 cv_symbol_list_arr_get_count(U64 count, CV_SymbolList *list_arr);
internal String8List cv_data_from_symbol_list(Arena *arena, CV_SymbolList symbol_list, U64 align);
internal CV_SymbolList cv_global_scope_symbols_from_list(Arena *arena, CV_SymbolList list);
internal CV_ScopeList cv_symbol_tree_from_symbol_list(Arena *arena, CV_SymbolList list);
internal CV_SymbolList cv_build_symbol_tree(Arena *arena, CV_ScopeList symbol_tree, U64 symbol_base, U64 align);
internal CV_SymbolPtrArray cv_symbol_ptr_array_from_list(Arena *arena, TP_Context *tp, U64 count, CV_SymbolList *symbol_list_arr);
// $$FileChksms
#define CV_MAP_STRING_TO_OFFSET_FUNC(name) U64 name(void *ud, String8 string)
typedef CV_MAP_STRING_TO_OFFSET_FUNC(CV_MapStringToOffsetFunc);
//internal String8 cv_c13_file_chksms_from_sub_sections(String8 c13_data, CV_C13Parsed *ss);
internal void cv_c13_patch_string_offsets_in_checksum_list(CV_ChecksumList checksum_list, String8 string_data, U64 string_data_base_offset, CV_StringHashTable string_ht);
internal String8List cv_c13_collect_source_file_names(Arena *arena, CV_ChecksumList checksum_list, String8 string_data);
// $$Lines
internal CV_C13LinesHeaderList cv_c13_lines_from_sub_sections(Arena *arena, String8 c13_data, Rng1U64 ss_range);
internal CV_LineArray cv_c13_line_array_from_data(Arena *arena, String8 c13_data, U64 sec_base, CV_C13LinesHeader parsed_lines);
// $$InlineeLines
internal CV_C13InlineeLinesParsedList cv_c13_inlinee_lines_from_sub_sections(Arena *arena, String8List raw_inlinee_lines);
internal CV_InlineBinaryAnnotsParsed cv_c13_parse_inline_binary_annots(Arena *arena, U64 parent_voff, CV_C13InlineeLinesParsed *inlinee_parsed, String8 binary_annots);
// $$FrameData
internal void cv_c13_patch_checksum_offsets_in_frame_data_list(String8List frame_data, U32 checksum_rebase);
////////////////////////////////
// $$Lines Accel
internal void cv_make_c13_files(Arena *arena, String8 c13_data, CV_C13SubSectionList lines, U64 *file_count_out, CV_C13File **files_out);
internal CV_LinesAccel * cv_make_lines_accel(Arena *arena, U64 lines_count, CV_LineArray *lines);
internal CV_Line * cv_line_from_voff(CV_LinesAccel *accel, U64 voff, U64 *out_line_count);
////////////////////////////////
// $$InlineeLines Accel
internal U64 cv_c13_inlinee_lines_accel_hash(void *buffer, U64 size);
internal B32 cv_c13_inlinee_lines_accel_push(CV_InlineeLinesAccel *accel, CV_C13InlineeLinesParsed *parsed);
internal CV_C13InlineeLinesParsed * cv_c13_inlinee_lines_accel_find(CV_InlineeLinesAccel *accel, CV_ItemId inlinee);
internal CV_InlineeLinesAccel * cv_c13_make_inlinee_lines_accel(Arena *arena, CV_C13InlineeLinesParsedList sub_sects);
////////////////////////////////
// String Hash Table
internal U64 cv_string_hash_table_hash(String8 string);
internal CV_StringHashTable cv_dedup_string_tables(TP_Arena *arena, TP_Context *tp, U64 count, CV_DebugS *arr);
internal CV_StringHashTableResult cv_serialize_string_hash_table(Arena *arena, TP_Context *tp, CV_StringHashTable string_ht);
internal String8 cv_pack_string_hash_table(Arena *arena, TP_Context *tp, CV_StringHashTable string_ht);
////////////////////////////////
internal CV_EncodedFramePtrReg cv_pick_fp_encoding(CV_SymFrameproc *frameproc, B32 is_local_param);
internal CV_Reg cv_decode_fp_reg(CV_Arch arch, CV_EncodedFramePtrReg encoded_reg);
internal Rng1U64List cv_make_defined_range_list_from_gaps(Arena *arena, Rng1U64 defrange, CV_LvarAddrGap *gaps, U64 gap_count);
////////////////////////////////
internal U64 cv_size_from_reg_x86(CV_Reg reg);
internal U64 cv_size_from_reg_x64(CV_Reg reg);
internal U64 cv_size_from_reg(CV_Arch arch, CV_Reg reg);
////////////////////////////////
internal CV_Arch cv_arch_from_coff_machine(COFF_MachineType machine);
internal String8 cv_string_from_type_index_source(CV_TypeIndexSource ti_source);
+270
View File
@@ -0,0 +1,270 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal void
bucket_list_concat_in_place(BucketList *list, BucketList *to_concat)
{
SLLConcatInPlaceNoCount(list, to_concat);
}
internal BucketNode *
bucket_list_pop(BucketList *list)
{
BucketNode *result = list->first;
SLLQueuePop(list->first, list->last);
return result;
}
////////////////////////////////
internal U64
hash_table_hasher(String8 string)
{
XXH64_hash_t hash64 = XXH3_64bits(string.str, string.size);
return hash64;
}
internal HashTable *
hash_table_init(Arena *arena, U64 cap)
{
HashTable *ht = push_array(arena, HashTable, 1);
ht->cap = cap;
ht->buckets = push_array(arena, BucketList, cap);
return ht;
}
internal void
hash_table_purge(HashTable *ht)
{
// reset key count
ht->count = 0;
// concat buckets
for (U64 ibucket = 0; ibucket < ht->cap; ++ibucket) {
bucket_list_concat_in_place(&ht->free_buckets, &ht->buckets[ibucket]);
}
}
internal BucketNode *
hash_table_push(Arena *arena, HashTable *ht, U64 hash, KeyValuePair v)
{
BucketNode *node;
if (ht->free_buckets.first != 0) {
node = bucket_list_pop(&ht->free_buckets);
} else {
node = push_array(arena, BucketNode, 1);
}
node->next = 0;
node->v = v;
U64 ibucket = hash % ht->cap;
SLLQueuePush(ht->buckets[ibucket].first, ht->buckets[ibucket].last, node);
++ht->count;
return node;
}
internal BucketNode *
hash_table_push_string_string(Arena *arena, HashTable *ht, String8 key, String8 value)
{
U64 hash = hash_table_hasher(key);
return hash_table_push(arena, ht, hash, (KeyValuePair){ .key_string = key, .value_string = value });
}
internal BucketNode *
hash_table_push_string_raw(Arena *arena, HashTable *ht, String8 key, void *value)
{
U64 hash = hash_table_hasher(key);
return hash_table_push(arena, ht, hash, (KeyValuePair){ .key_string = key, .value_raw = value });
}
internal BucketNode *
hash_table_push_string_u64(Arena *arena, HashTable *ht, String8 key, U64 value)
{
U64 hash = hash_table_hasher(key);
return hash_table_push(arena, ht, hash, (KeyValuePair){.key_string = key, .value_u64 = value });
}
internal BucketNode *
hash_table_push_u32_raw(Arena *arena, HashTable *ht, U32 key, void *value)
{
U64 hash = hash_table_hasher(str8_struct(&key));
return hash_table_push(arena, ht, hash, (KeyValuePair){ .key_u32 = key, .value_raw = value });
}
internal BucketNode *
hash_table_push_u32_string(Arena *arena, HashTable *ht, U32 key, String8 value)
{
U64 hash = hash_table_hasher(str8_struct(&key));
return hash_table_push(arena, ht, hash, (KeyValuePair){ .key_u32 = key, .value_string = value });
}
internal BucketNode *
hash_table_push_u64_raw(Arena *arena, HashTable *ht, U64 key, void *value)
{
U64 hash = hash_table_hasher(str8_struct(&key));
return hash_table_push(arena, ht, hash, (KeyValuePair){ .key_u64 = key, .value_raw = value });
}
internal BucketNode *
hash_table_push_u64_string(Arena *arena, HashTable *ht, U64 key, String8 value)
{
U64 hash = hash_table_hasher(str8_struct(&key));
return hash_table_push(arena, ht, hash, (KeyValuePair){ .key_u64 = key, .value_string = value });
}
internal BucketNode *
hash_table_push_u64_u64(Arena *arena, HashTable *ht, U64 key, U64 value)
{
U64 hash = hash_table_hasher(str8_struct(&key));
return hash_table_push(arena, ht, hash, (KeyValuePair){ .key_u64 = key, .value_u64 = value });
}
internal BucketNode *
hash_table_push_path_string(Arena *arena, HashTable *ht, String8 path, String8 value)
{
String8 path_canon = path_canon_from_regular_path(arena, path);
return hash_table_push_string_string(arena, ht, path_canon, value);
}
internal BucketNode *
hash_table_push_path_u64(Arena *arena, HashTable *ht, String8 path, U64 value)
{
String8 path_canon = path_canon_from_regular_path(arena, path);
U64 hash = hash_table_hasher(path_canon);
return hash_table_push(arena, ht, hash, (KeyValuePair){ .key_string = path_canon, .value_u64 = value });
}
internal BucketNode *
hash_table_push_path_raw(Arena *arena, HashTable *ht, String8 path, void *value)
{
String8 path_canon = path_canon_from_regular_path(arena, path);
U64 hash = hash_table_hasher(path_canon);
return hash_table_push(arena, ht, hash, (KeyValuePair){ .key_string = path_canon, .value_raw = value });
}
////////////////////////////////
internal KeyValuePair *
hash_table_search_string(HashTable *ht, String8 key_string)
{
U64 hash = hash_table_hasher(key_string);
U64 ibucket = hash % ht->cap;
BucketList *bucket = ht->buckets + ibucket;
for (BucketNode *n = bucket->first; n != 0; n = n->next) {
if (str8_match(n->v.key_string, key_string, 0)) {
return &n->v;
}
}
return 0;
}
internal KeyValuePair *
hash_table_search_u32(HashTable *ht, U32 key_u32)
{
U64 hash = hash_table_hasher(str8_struct(&key_u32));
U64 ibucket = hash % ht->cap;
BucketList *bucket = ht->buckets + ibucket;
for (BucketNode *n = bucket->first; n != 0; n = n->next) {
if (n->v.key_u32 == key_u32) {
return &n->v;
}
}
return 0;
}
internal KeyValuePair *
hash_table_search_u64(HashTable *ht, U64 key_u64)
{
U64 hash = hash_table_hasher(str8_struct(&key_u64));
U64 ibucket = hash % ht->cap;
BucketList *bucket = ht->buckets + ibucket;
for (BucketNode *n = bucket->first; n != 0; n = n->next) {
if (n->v.key_u64 == key_u64) {
return &n->v;
}
}
return 0;
}
internal KeyValuePair *
hash_table_search_path(HashTable *ht, String8 path)
{
Temp scratch = scratch_begin(0,0);
String8 path_canon = path;
path_canon = lower_from_str8(scratch.arena, path_canon);
path_canon = path_convert_slashes(scratch.arena, path_canon, PathStyle_UnixAbsolute);
KeyValuePair *result = hash_table_search_string(ht, path_canon);
scratch_end(scratch);
return result;
}
internal B32
hash_table_search_path_u64(HashTable *ht, String8 key, U64 *value_out)
{
KeyValuePair *result = hash_table_search_path(ht, key);
if (result != 0) {
if (value_out != 0) {
*value_out = result->value_u64;
}
return 1;
}
return 0;
}
internal B32
hash_table_search_string_u64(HashTable *ht, String8 key, U64 *value_out)
{
KeyValuePair *result = hash_table_search_string(ht, key);
if (result != 0) {
if (value_out != 0) {
*value_out = result->value_u64;
}
return 1;
}
return 0;
}
////////////////////////////////
internal int
key_value_pair_is_before_u32(void *raw_a, void *raw_b)
{
KeyValuePair *a = raw_a;
KeyValuePair *b = raw_b;
return a->key_u32 < b->key_u32;
}
internal int
key_value_pair_is_before_u64(void *raw_a, void *raw_b)
{
KeyValuePair *a = raw_a;
KeyValuePair *b = raw_b;
return a->key_u64 < b->key_u64;
}
internal KeyValuePair *
key_value_pairs_from_hash_table(Arena *arena, HashTable *ht)
{
KeyValuePair *pairs = push_array_no_zero(arena, KeyValuePair, ht->count);
for (U64 bucket_idx = 0, cursor = 0; bucket_idx < ht->cap; ++bucket_idx) {
for (BucketNode *n = ht->buckets[bucket_idx].first; n != 0; n = n->next) {
Assert(cursor < ht->count);
pairs[cursor++] = n->v;
}
}
return pairs;
}
internal void
sort_key_value_pairs_as_u32(KeyValuePair *pairs, U64 count)
{
radsort(pairs, count, key_value_pair_is_before_u32);
}
internal void
sort_key_value_pairs_as_u64(KeyValuePair *pairs, U64 count)
{
radsort(pairs, count, key_value_pair_is_before_u64);
}
+81
View File
@@ -0,0 +1,81 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef struct KeyValuePair
{
union {
String8 key_string;
U32 key_u32;
U64 key_u64;
};
union {
String8 value_string;
void *value_raw;
U32 value_u32;
U64 value_u64;
};
} KeyValuePair;
typedef struct BucketNode
{
struct BucketNode *next;
KeyValuePair v;
} BucketNode;
typedef struct BucketList
{
BucketNode *first;
BucketNode *last;
} BucketList;
typedef struct HashTable
{
U64 count;
U64 cap;
BucketList *buckets;
BucketList free_buckets;
} HashTable;
////////////////////////////////
//- bucket list helpers
internal void bucket_list_concat_in_place(BucketList *list, BucketList *to_concat);
internal BucketNode * bucket_list_pop(BucketList *list);
//- main
internal U64 hash_table_hasher(String8 string);
internal HashTable * hash_table_init(Arena *arena, U64 cap);
internal void hash_table_purge(HashTable *ht);
//- push
internal BucketNode * hash_table_push (Arena *arena, HashTable *ht, U64 hash, KeyValuePair v);
internal BucketNode * hash_table_push_u32_string (Arena *arena, HashTable *ht, U32 key, String8 value);
internal BucketNode * hash_table_push_u64_string (Arena *arena, HashTable *ht, U64 key, String8 value);
internal BucketNode * hash_table_push_string_string(Arena *arena, HashTable *ht, String8 key, String8 value);
internal BucketNode * hash_table_push_path_string (Arena *arena, HashTable *ht, String8 key, String8 value);
internal BucketNode * hash_table_push_u32_raw (Arena *arena, HashTable *ht, U32 key, void *value);
internal BucketNode * hash_table_push_u64_raw (Arena *arena, HashTable *ht, U64 key, void *value);
internal BucketNode * hash_table_push_path_raw (Arena *arena, HashTable *ht, String8 path, void *value);
internal BucketNode * hash_table_push_path_u64 (Arena *arena, HashTable *ht, String8 path, U64 value);
internal BucketNode * hash_table_push_u64_u64 (Arena *arena, HashTable *ht, U64 key, U64 value);
//- search
internal KeyValuePair * hash_table_search_string (HashTable *ht, String8 string);
internal KeyValuePair * hash_table_search_u32 (HashTable *ht, U32 key);
internal KeyValuePair * hash_table_search_u64 (HashTable *ht, U64 key);
internal KeyValuePair * hash_table_search_path (HashTable *ht, String8 path);
internal B32 hash_table_search_path_u64(HashTable *ht, String8 key, U64 *value_out);
//- key-value helpers
internal KeyValuePair * key_value_pairs_from_hash_table(Arena *arena, HashTable *ht);
internal void sort_key_value_pairs_as_u32(KeyValuePair *pairs, U64 count);
internal void sort_key_value_pairs_as_u64(KeyValuePair *pairs, U64 count);
+4580
View File
File diff suppressed because it is too large Load Diff
+298
View File
@@ -0,0 +1,298 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
#define LNK_NULL_SYMBOL_NAME "NULL"
#define LNK_TEXT_SYMBOL_NAME "TEXT"
#define LNK_DATA_SYMBOL_NAME "DATA"
#define LNK_RDATA_SYMBOL_NAME "RDATA"
#define LNK_BSS_SYMBOL_NAME "BSS"
#define LNK_XDATA_SYMBOL_NAME "XDATA"
#define LNK_PDATA_SYMBOL_NAME "PDATA"
#define LNK_BASE_RELOC_SYMBOL_NAME "BASE_RELOC"
#define LNK_EDATA_SYMBOL_NAME "EDATA"
#define LNK_DEBUG_DIR_SYMBOL_NAME "DEDIR"
#define LNK_DEBUG_DATA_SYMBOL_NAME "DEDAT"
#define LNK_CV_DIR_SYMBOL_NAME "CV_DIR"
#define LNK_CV_HEADER_PDB70_SYMBOL_NAME "CV_HEADER_PDB70"
#define LNK_CV_HEADER_RDI_SYMBOL_NAME "CV_HEADER_RDI"
#define LNK_CV_HEADER_GUID_SYMBOL_NAME "CV_HEADER_GUID"
#define LNK_RSRC_SYMBOL_NAME "RSRC"
#define LNK_DEBUG_SYMBOL_NAME "DEBUG"
#define LNK_GFIDS_SYMBOL_NAME "GFIDS"
#define LNK_GIATS_SYMBOL_NAME "GIATS"
#define LNK_GLJMP_SYMBOL_NAME "GLJMP"
#define LNK_GEHCONT_SYMBOL_NAME "GEHCONT"
#define LNK_IMPORT_NAME_TABLE_SYMBOL_NAME "IMPORT_STR"
#define LNK_IMPORT_DLL_TABLE_SYMBOL_NAME "IDATA"
#define LNK_IMPORT_ILT_SYMBOL_NAME "ILT"
#define LNK_IMPORT_IAT_SYMBOL_NAME "IAT"
#define LNK_IMPORT_JMP_SYMBOL_NAME "IMPORT_THUNKS"
#define LNK_DELAYED_IMPORT_DLL_TABLE_SYMBOL_NAME "DELAYED_IMPORT_DLL_TABLE"
#define LNK_DELAYED_IMPORT_HANDLE_TABLE_SYMBOL_NAME "DELAYED_IMPORT_HANDLE_TABLE"
#define LNK_DELAYED_IMPORT_INT_SYMBOL_NAME "DELAYED_IMPORT_INT"
#define LNK_DELAYED_IMPORT_IAT_SYMBOL_NAME "DELAYED_IMPORT_IAT"
#define LNK_DELAYED_IMPORT_ILT_SYMBOL_NAME "DELAYED_IMPORT_ILT"
#define LNK_DELAYED_IMPORT_BIAT_SYMBOL_NAME "DELAYED_IMPORT_BIAT"
#define LNK_DELAYED_IMPORT_UIAT_SYMBOL_NAME "DELAYED_IMPORT_UIAT"
#define LNK_DELAYED_IMPORT_CODE_SYMBOL_NAME "DELAYED_IMPORT_CODE"
#define LNK_WIN32_HEADER_SYMBOL_NAME "WIN32_HEADER"
#define LNK_DOS_SYMBOL_NAME "DOS"
#define LNK_NT_HEADERS_SYMBOL_NAME "NT_HEADERS"
#define LNK_PE_MAGIC_CONTAINER_SYMBOL_NAME "PE_MAGIC_CONTAINER"
#define LNK_COFF_FILE_HEADER_CONTAINER_SYMBOL_NAME "COFF_FILE_HEADER_CONTAINER"
#define LNK_PE_OPT_HEADER_CONTAINER_SYMBOL_NAME "PE_OPTIONAL_HEADER_CONTAINER"
#define LNK_COFF_SECTION_HEADER_CONTAINER_SYMBOL_NAME "COFF_SECTION_HEADER_CONTAINER"
#define LNK_DOS_HEADER_SYMBOL_NAME "DOS_HEADER"
#define LNK_DOS_PROGRAM_SYMBOL_NAME "DOS_PROGRAM"
#define LNK_PE_MAGIC_SYMBOL_NAME "PE_MAGIC"
#define LNK_COFF_HEADER_SYMBOL_NAME "COFF_HEADER"
#define LNK_PE_DIRECTORY_ARRAY_SYMBOL_NAME "PE_DIRECTORY_ARRAY"
#define LNK_PE_DIRECTORY_COUNT_SYMBOL_NAME "PE_DIRECTORY_COUNT"
#define LNK_PE_OPT_HEADER_SYMBOL_NAME "PE_OPTIONAL_HEADER"
#define LNK_COFF_SECT_HEADER_ARRAY_SYMBOL_NAME "COFF_SECT_HEADER_ARRAY"
#define LNK_COFF_SECT_HEADER_COUNT_SYMBOL_NAME "COFF_SECT_HEADER_COUNT"
#define LNK_PE_CHECKSUM_SYMBOL_NAME "PE_CHECKSUM"
// _tls_used is a special section in CRT which has format of
// PE_TLSHeader32 or PE_TLSHeader64, according to machine type.
#define LNK_TLS_SYMBOL_NAME "_tls_used"
// _load_config_used points to SYMS_PeLoadConfig32/SYMS_PeLoadConfig64
// and symbols below are used to patch patricual fields of the struct.
#define LNK_LOAD_CONFIG_SYMBOL_NAME "_load_config_used"
#define LNK_ENCLAVE_CONFIG_SYMBOL_NAME "__enclave_config"
#define LNK_GUARD_FLAGS_SYMBOL_NAME "__guard_flags"
#define LNK_GUARD_FIDS_TABLE_SYMBOL_NAME "__guard_fids_table"
#define LNK_GUARD_FIDS_COUNT_SYMBOL_NAME "__guard_fids_count"
#define LNK_GUARD_IAT_TABLE_SYMBOL_NAME "__guard_iat_table"
#define LNK_GUARD_IAT_COUNT_SYMBOL_NAME "__guard_iat_count"
#define LNK_GUARD_LONGJMP_TABLE_SYMBOL_NAME "__guard_longjmp_table"
#define LNK_GUARD_LONGJMP_COUNT_SYMBOL_NAME "__guard_longjmp_count"
#define LNK_GUARD_EHCONT_TABLE_SYMBOL_NAME "__guard_eh_cont_table"
#define LNK_GUARD_EHCONT_COUNT_SYMBOL_NAME "__guard_eh_cont_count"
// x86 load config fields
#define LNK_SAFE_SE_HANDLER_TABLE_SYMBOL_NAME "__safe_se_handler_table"
#define LNK_SAFE_SE_HANDLER_COUNT_SYMBOL_NAME "__safe_se_handler_count"
// load symbols from delayimp.lib
#define LNK_DELAY_LOAD_HELPER2_SYMBOL_NAME "__delayLoadHelper2"
#define LNK_DELAY_LOAD_HELPER2_X86_SYMBOL_NAME "___delayLoadHelper2@8"
#define LNK_TEXT_SECTION_FLAGS (COFF_SectionFlag_CNT_CODE|COFF_SectionFlag_MEM_EXECUTE|COFF_SectionFlag_MEM_READ)
#define LNK_DATA_SECTION_FLAGS (COFF_SectionFlag_CNT_INITIALIZED_DATA|COFF_SectionFlag_MEM_READ|COFF_SectionFlag_MEM_WRITE)
#define LNK_RDATA_SECTION_FLAGS (COFF_SectionFlag_CNT_INITIALIZED_DATA|COFF_SectionFlag_MEM_READ)
#define LNK_BSS_SECTION_FLAGS (COFF_SectionFlag_CNT_UNINITIALIZED_DATA|COFF_SectionFlag_MEM_READ|COFF_SectionFlag_MEM_WRITE)
#define LNK_IDATA_SECTION_FLAGS LNK_DATA_SECTION_FLAGS
#define LNK_DEBUG_DIR_SECTION_FLAGS LNK_DATA_SECTION_FLAGS
#define LNK_RSRC_SECTION_FLAGS LNK_DATA_SECTION_FLAGS
#define LNK_XDATA_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS
#define LNK_PDATA_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS
#define LNK_EDATA_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS
#define LNK_GFIDS_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS
#define LNK_GIATS_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS
#define LNK_GLJMP_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS
#define LNK_GEHCONT_SECTION_FLAGS LNK_RDATA_SECTION_FLAGS
#define LNK_RELOC_SECTION_FLAGS (LNK_RDATA_SECTION_FLAGS | COFF_SectionFlag_MEM_DISCARDABLE)
#define LNK_DEBUG_SECTION_FLAGS (LNK_RDATA_SECTION_FLAGS | COFF_SectionFlag_MEM_DISCARDABLE)
////////////////////////////////
typedef enum
{
LNK_InputSource_CmdLine, // specified on command line
LNK_InputSource_Default, // specified through defaultlib switch
LNK_InputSource_Obj, // refrenced from objects
LNK_InputSource_Count
} LNK_InputSourceType;
typedef String8Node LNK_InputLib;
typedef String8List LNK_InputLibList;
typedef struct LNK_InputImport
{
COFF_ImportHeader import_header;
struct LNK_InputImport *next;
} LNK_InputImport;
typedef struct LNK_InputImportList
{
U64 count;
LNK_InputImport *first;
LNK_InputImport *last;
} LNK_InputImportList;
////////////////////////////////
typedef struct LNK_BaseRelocPage
{
U64 voff;
U64List entries;
} LNK_BaseRelocPage;
typedef struct LNK_BaseRelocPageNode
{
struct LNK_BaseRelocPageNode *next;
LNK_BaseRelocPage v;
} LNK_BaseRelocPageNode;
typedef struct LNK_BaseRelocPageList
{
U64 count;
LNK_BaseRelocPageNode *first;
LNK_BaseRelocPageNode *last;
} LNK_BaseRelocPageList;
typedef struct LNK_BaseRelocPageArray
{
U64 count;
LNK_BaseRelocPage *v;
} LNK_BaseRelocPageArray;
typedef struct
{
U64 page_size;
LNK_Section **sect_id_map;
LNK_Reloc **reloc_arr;
Rng1U64 *range_arr;
LNK_BaseRelocPageList *list_arr;
HashTable **page_ht_arr;
} LNK_BaseRelocTask;
typedef struct
{
Rng1U64 *ranges;
U64 page_size;
LNK_Section **sect_id_map;
LNK_BaseRelocPageList *list_arr;
LNK_Obj **obj_arr;
HashTable **page_ht_arr;
} LNK_ObjBaseRelocTask;
typedef struct
{
Rng1U64 *range_arr;
LNK_LibNode *lib_arr;
LNK_Symbol **symbol_arr_arr;
} LNK_LazyIniter;
typedef struct
{
LNK_InputObjList input_obj_list;
LNK_InputImportList input_import_list;
LNK_SymbolList unresolved_symbol_list;
} LNK_SymbolFinderResult;
typedef struct
{
PathStyle path_style;
LNK_SymbolTable *symtab;
LNK_SymbolNodeArray lookup_node_arr;
LNK_SymbolFinderResult *result_arr;
Rng1U64 *range_arr;
} LNK_SymbolFinder;
typedef struct
{
LNK_SymbolTable *symtab;
LNK_SectionTable *st;
LNK_Section **sect_id_map;
U64 base_addr;
LNK_Section **sect_arr;
Rng1U64 *range_arr;
} LNK_SectionRelocPatcher;
typedef struct
{
LNK_SymbolTable *symtab;
LNK_SectionTable *st;
LNK_Section **sect_id_map;
U64 base_addr;
LNK_Obj **obj_arr;
} LNK_ObjRelocPatcher;
typedef struct
{
String8 path;
String8 data;
} LNK_WriteThreadContext;
typedef struct
{
String8 data;
Rng1U64 *ranges;
U128 *hashes;
} LNK_Blake3Hasher;
////////////////////////////////
internal LNK_InputImport * lnk_input_import_list_push(Arena *arena, LNK_InputImportList *list);
internal void lnk_input_import_list_concat_in_place(LNK_InputImportList *list, LNK_InputImportList *to_concat);
internal LNK_InputImport ** lnk_input_import_arr_from_list(Arena *arena, LNK_InputImportList list);
internal LNK_InputImportList lnk_list_from_input_import_arr(LNK_InputImport **arr, U64 count);
////////////////////////////////
// Helpers
internal void lnk_write_data_list_to_file_path(String8 path, String8List list);
internal void lnk_write_data_to_file_path(String8 path, String8 data);
internal String8 lnk_make_full_path(Arena *arena, String8 work_dir, PathStyle system_path_style, String8 path);
internal String8 lnk_get_lib_name(String8 path);
internal B32 lnk_is_lib_disallowed(HashTable *disallow_lib_ht, String8 path);
internal B32 lnk_is_lib_loaded(HashTable *default_lib_ht, HashTable *loaded_lib_ht, LNK_InputSourceType input_source, String8 lib_path);
internal void lnk_push_disallow_lib(Arena *arena, HashTable *disallow_lib_ht, String8 path);
internal void lnk_push_loaded_lib(Arena *arena, HashTable *default_lib_ht, HashTable *loaded_lib_ht, String8 path);
////////////////////////////////
// Manifest
internal String8List lnk_make_linker_manifest(Arena *arena, B32 manifest_uac, String8 manifest_level, String8 manifest_ui_access, String8List manifest_dependency_list);
internal String8 lnk_merge_manifest_files(Arena *arena, String8 mt_path, String8 manifest_name, String8List manifest_path_list);
internal String8 lnk_res_from_data(Arena *arena, String8 data);
////////////////////////////////
// Resources
internal void lnk_serialize_pe_resource_tree(LNK_SectionTable *st, LNK_SymbolTable *symtab, PE_ResourceDir *root_dir);
internal void lnk_add_resource_debug_s(LNK_SectionTable *st, LNK_SymbolTable *symtab, String8 obj_path, String8 cwd_path, String8 exe_path, CV_Arch arch, String8List res_file_list, MD5Hash *res_hash_array);
internal String8 lnk_make_res_obj(TP_Context *tp, Arena *arena, PE_ResourceDir *root_dir, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 path, String8 cwd_path, String8 exe_path, String8List res_file_list, MD5Hash *res_hash_array);
internal String8 lnk_obj_from_res_file_list(TP_Context *tp, Arena *arena, LNK_SectionTable *st, LNK_SymbolTable *symtab, String8List res_file_list, String8List res_path_list, COFF_MachineType machine, U32 time_stamp, String8 work_dir, PathStyle system_path_style, String8 obj_name);
////////////////////////////////
// Debug
internal String8 lnk_make_linker_coff_obj(TP_Context *tp, Arena *arena, COFF_TimeStamp time_stamp, COFF_MachineType machine, String8 cwd_path, String8 exe_path, String8 pdb_path, String8 cmd_line, String8 obj_name);
////////////////////////////////
// Win32 Image Helpers
internal void lnk_build_debug_pdb(LNK_SectionTable *st, LNK_SymbolTable *symtab, LNK_Section *debug_sect, LNK_Chunk *debug_dir_array_chunk, COFF_TimeStamp time_stamp, OS_Guid guid, U32 age, String8 pdb_path);
internal void lnk_build_debug_rdi(LNK_SectionTable *st, LNK_SymbolTable *symtab, LNK_Section *debug_sect, LNK_Chunk *debug_dir_array_chunk, COFF_TimeStamp time_stamp, OS_Guid guid, String8 rdi_path);
internal void lnk_build_guard_tables(TP_Context *tp, LNK_SectionTable *st, LNK_SymbolTable *symtab, LNK_ExportTable *exptab, LNK_ObjList obj_list, COFF_MachineType machine, String8 entry_point_name, LNK_GuardFlags guard_flags, B32 emit_suppress_flag);
internal void lnk_build_base_relocs(TP_Context *tp, TP_Arena *tp_arena, LNK_SectionTable *st, LNK_SymbolTable *symtab, COFF_MachineType machine, U64 page_size, LNK_ObjList obj_list);
internal LNK_Chunk * lnk_build_dos_header(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent_chunk);
internal LNK_Chunk * lnk_build_pe_magic(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent);
internal LNK_Chunk * lnk_build_coff_file_header(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent, COFF_MachineType machine, COFF_TimeStamp time_stamp, PE_ImageFileCharacteristics file_characteristics);
internal LNK_Chunk * lnk_build_pe_optional_header_x64(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent, COFF_MachineType machine, U64 base_addr, U64 sect_align, U64 file_align, Version linker_ver, Version os_ver, Version image_ver, Version subsystem_ver, PE_WindowsSubsystem subsystem, PE_DllCharacteristics dll_characteristics, U64 stack_reserve, U64 stack_commit, U64 heap_reserve, U64 heap_commit, String8 entry_point_name, LNK_SectionArray sect_arr);
internal LNK_Chunk * lnk_build_pe_directories(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent);
internal LNK_Chunk * lnk_build_coff_section_table(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent_chunk, LNK_SectionArray sect_arr);
internal LNK_Chunk * lnk_build_win32_image_header(LNK_SymbolTable *symtab, LNK_Section *header_sect, LNK_Chunk *parent_chunk, LNK_Config *config, LNK_SectionArray sect_arr);
////////////////////////////////
// Relocs
internal void lnk_patch_relocs(TP_Context *tp, LNK_SymbolTable *symtab, LNK_SectionTable *st, U64 base_addr);
internal void lnk_apply_reloc(U64 base_addr, U64 virt_align, U64 file_align, LNK_Section **sect_id_map, LNK_SymbolTable *symtab, String8 chunk_data, LNK_Reloc *reloc);
////////////////////////////////
internal void lnk_log_size_breakdown(LNK_SectionTable *st, LNK_SymbolTable *symtab);
internal void lnk_log_link_stats(LNK_ObjList obj_list, LNK_LibList *lib_index, LNK_SectionTable *st);
internal void lnk_log_timers(void);
+264
View File
@@ -0,0 +1,264 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal LNK_BaseRelocPageArray
lnk_base_reloc_page_array_from_list(Arena* arena, LNK_BaseRelocPageList list)
{
LNK_BaseRelocPageArray result = {0};
result.count = 0;
result.v = push_array_no_zero(arena, LNK_BaseRelocPage, list.count);
for (LNK_BaseRelocPageNode* n = list.first; n != 0; n = n->next) {
result.v[result.count++] = n->v;
}
Assert(result.count == list.count);
return result;
}
internal void
lnk_emit_base_reloc_info(Arena *arena,
LNK_Section **sect_id_map,
U64 page_size,
HashTable *page_ht,
LNK_BaseRelocPageList *page_list,
LNK_Reloc *reloc)
{
B32 is_addr = (reloc->type == LNK_Reloc_ADDR_64 || reloc->type == LNK_Reloc_ADDR_32);
if (is_addr) {
U64 reloc_voff = lnk_virt_off_from_reloc(sect_id_map, reloc);
U64 page_voff = AlignDownPow2(reloc_voff, page_size);
LNK_BaseRelocPageNode *page;
{
String8 raw_page;
B32 is_page_present = hash_table_search_u64(page_ht, page_voff, &raw_page);
if (is_page_present) {
page = *(LNK_BaseRelocPageNode **) raw_page.str;
} else {
// fill out page
page = push_array(arena, LNK_BaseRelocPageNode, 1);
page->v.voff = page_voff;
// push page
SLLQueuePush(page_list->first, page_list->last, page);
page_list->count += 1;
// register page voff
hash_table_push_u64(arena, page_ht, page_voff, str8_struct(&page));
}
}
u64_list_push(arena, &page->v.entries, reloc_voff);
}
}
internal
THREAD_POOL_TASK_FUNC(lnk_emit_base_relocs_from_reloc_array_task)
{
LNK_BaseRelocTask *task = raw_task;
Rng1U64 range = task->range_arr[task_id];
LNK_BaseRelocPageList *page_list = &task->list_arr[task_id];
HashTable *page_ht = task->page_ht_arr[task_id];
for (U64 reloc_idx = range.min; reloc_idx < range.max; reloc_idx += 1) {
LNK_Reloc *reloc = task->reloc_arr[reloc_idx];
lnk_emit_base_reloc_info(arena, task->sect_id_map, task->page_size, page_ht, page_list, reloc);
}
}
internal
THREAD_POOL_TASK_FUNC(lnk_emit_base_relocs_from_objs_task)
{
LNK_ObjBaseRelocTask *task = raw_task;
LNK_Obj *obj = task->obj_arr[task_id];
LNK_BaseRelocPageList *page_list = &task->list_arr[worker_id];
HashTable *page_ht = task->page_ht_arr[worker_id];
for (U64 sect_idx = 0; sect_idx < obj->sect_count; sect_idx += 1) {
B32 is_live = !lnk_chunk_is_discarded(&obj->chunk_arr[sect_idx]);
if (is_live) {
LNK_RelocList reloc_list = obj->sect_reloc_list_arr[sect_idx];
for (LNK_Reloc *reloc = reloc_list.first; reloc != 0; reloc = reloc->next) {
lnk_emit_base_reloc_info(arena,
task->sect_id_map,
task->page_size,
page_ht,
page_list,
reloc);
}
}
}
}
int
lnk_base_reloc_page_compar(void *raw_a, void *raw_b)
{
LNK_BaseRelocPage* a = raw_a;
LNK_BaseRelocPage* b = raw_b;
int is_before = a->voff < b->voff;
return is_before;
}
internal void
lnk_base_reloc_page_array_sort(LNK_BaseRelocPageArray arr)
{
ProfBeginFunction();
radsort(arr.v, arr.count, lnk_base_reloc_page_compar);
ProfEnd();
}
internal void
lnk_build_base_relocs(TP_Context *tp,
LNK_SectionTable *st,
LNK_SymbolTable *symtab,
COFF_MachineType machine,
U64 page_size,
LNK_ObjList obj_list)
{
ProfBeginFunction();
TP_Arena *arena = g_file_arena;
TP_Temp temp = tp_temp_begin(arena);
lnk_section_table_build_data(st, machine);
lnk_section_table_assign_virtual_offsets(st);
LNK_Section **sect_id_map = lnk_sect_id_map_from_section_table(arena->v[0], st);
LNK_BaseRelocPageList *page_list_arr = push_array(arena->v[0], LNK_BaseRelocPageList, tp->worker_count);
HashTable **page_ht_arr = push_array_no_zero(arena->v[0], HashTable *, tp->worker_count);
for (U64 i = 0; i < tp->worker_count; ++i) {
page_ht_arr[i] = hash_table_init(arena->v[0], 1024);
}
// emit pages from relocs defined in section table
ProfBegin("Emit Relocs From Section Table");
for (LNK_SectionNode *sect_node = st->list.first; sect_node != 0; sect_node = sect_node->next) {
LNK_BaseRelocTask task = {0};
task.page_size = page_size;
task.sect_id_map = sect_id_map;
task.list_arr = page_list_arr;
task.page_ht_arr = page_ht_arr;
task.reloc_arr = lnk_reloc_array_from_list(arena->v[0], sect_node->data.reloc_list);
task.range_arr = tp_divide_work(arena->v[0], sect_node->data.reloc_list.count, tp->worker_count);
tp_for_parallel(tp, arena, tp->worker_count, lnk_emit_base_relocs_from_reloc_array_task, &task);
}
ProfEnd();
// emit pages from relocs defined in objs
ProfBegin("Emit Relocs From Objs");
{
LNK_ObjBaseRelocTask task = {0};
task.page_size = page_size;
task.sect_id_map = sect_id_map;
task.page_ht_arr = page_ht_arr;
task.list_arr = page_list_arr;
task.obj_arr = lnk_obj_arr_from_list(arena->v[0], obj_list);
tp_for_parallel(tp, arena, obj_list.count, lnk_emit_base_relocs_from_objs_task, &task);
}
ProfEnd();
// merge page lists
ProfBegin("Merge Worker Page Lists");
HashTable *main_ht = page_ht_arr[0];
LNK_BaseRelocPageList *main_page_list = &page_list_arr[0];
for (U64 list_idx = 1; list_idx < tp->worker_count; ++list_idx) {
LNK_BaseRelocPageList src = page_list_arr[list_idx];
for (LNK_BaseRelocPageNode *src_page = src.first, *src_next; src_page != 0; src_page = src_next) {
src_next = src_page->next;
String8 raw_page;
B32 is_page_present = hash_table_search(main_ht, str8_struct(&src_page->v.voff), &raw_page);
if (is_page_present) {
// page exists concat voffs
Assert(raw_page.size == sizeof(LNK_BaseRelocPageNode));
LNK_BaseRelocPageNode *page = (LNK_BaseRelocPageNode *) raw_page.str;
Assert(page != src_page);
u64_list_concat_in_place(&page->v.entries, &src_page->v.entries);
} else {
// push page to main list
SLLQueuePush(main_page_list->first, main_page_list->last, src_page);
main_page_list->count += 1;
// store lookup voff
hash_table_push_nocopy(arena->v[0], main_ht, str8_struct(&src_page->v.voff), str8_struct(src_page));
}
}
}
ProfEnd();
// push storage for section
LNK_Section *base_reloc_sect = lnk_section_table_push(st, str8_lit(".reloc"), LNK_RELOC_SECTION_FLAGS);
LNK_Symbol *base_reloc_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_lit(LNK_BASE_RELOC_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, base_reloc_sect->root, 0, 0, 0);
lnk_symbol_table_push(symtab, base_reloc_symbol);
ProfBegin("Page List -> Array");
LNK_BaseRelocPageArray page_arr = lnk_base_reloc_page_array_from_list(base_reloc_sect->arena, *main_page_list);
ProfEnd();
ProfBegin("Sort Pages on VOFF");
lnk_base_reloc_page_array_sort(page_arr);
ProfEnd();
HashTable *voff_ht = hash_table_init(arena->v[0], page_size);
ProfBegin("Serialize Pages");
for (U64 page_idx = 0; page_idx < page_arr.count; ++page_idx) {
LNK_BaseRelocPage *page = &page_arr.v[page_idx];
// push buffer
U64 buf_align = sizeof(U32);
U64 buf_size = AlignPow2(sizeof(U32)*2 + sizeof(U16)*page->entries.count, buf_align);
U8 *buf = push_array_no_zero(base_reloc_sect->arena, U8, buf_size);
// setup pointers into buffer
U32 *page_voff_ptr = (U32*)buf;
U32 *block_size_ptr = page_voff_ptr + 1;
U16 *reloc_arr_base = (U16*)(block_size_ptr + 1);
U16 *reloc_arr_ptr = reloc_arr_base;
// write reloc array
for (U64Node *i = page->entries.first; i != 0; i = i->next) {
// was base reloc entry made?
if (hash_table_search_u64(voff_ht, i->data, 0)) {
continue;
}
hash_table_push_u64(arena->v[0], voff_ht, i->data, str8(0,0));
// write entry
U64 rel_off = i->data - page->voff;
Assert(rel_off <= page_size);
*reloc_arr_ptr++ = PE_BaseRelocMake(PE_BaseRelocKind_DIR64, rel_off);
}
// write pad
U64 pad_reloc_count = AlignPadPow2(page->entries.count, sizeof(reloc_arr_ptr[0]));
MemoryZeroTyped(reloc_arr_ptr, pad_reloc_count); // fill pad with PE_BaseRelocKind_ABSOLUTE
reloc_arr_ptr += pad_reloc_count;
// compute block size
U64 reloc_arr_size = (U64)((U8*)reloc_arr_ptr - (U8*)reloc_arr_base);
U64 block_size = sizeof(*page_voff_ptr) + sizeof(*block_size_ptr) + reloc_arr_size;
// write header
*page_voff_ptr = safe_cast_u32(page->voff);
*block_size_ptr = safe_cast_u32(block_size);
Assert(*block_size_ptr <= buf_size);
// push page chunk
lnk_section_push_chunk_raw(base_reloc_sect, base_reloc_sect->root, buf, block_size, str8(0,0));
// purge voffs for next run
hash_table_purge(voff_ht);
}
ProfEnd();
tp_temp_end(temp);
ProfEnd();
}
+56
View File
@@ -0,0 +1,56 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef struct LNK_BaseRelocPage
{
U64 voff;
U64List entries;
} LNK_BaseRelocPage;
typedef struct LNK_BaseRelocPageNode
{
struct LNK_BaseRelocPageNode *next;
LNK_BaseRelocPage v;
} LNK_BaseRelocPageNode;
typedef struct LNK_BaseRelocPageList
{
U64 count;
LNK_BaseRelocPageNode *first;
LNK_BaseRelocPageNode *last;
} LNK_BaseRelocPageList;
typedef struct LNK_BaseRelocPageArray
{
U64 count;
LNK_BaseRelocPage *v;
} LNK_BaseRelocPageArray;
typedef struct
{
U64 page_size;
LNK_Section **sect_id_map;
LNK_Reloc **reloc_arr;
Rng1U64 *range_arr;
LNK_BaseRelocPageList *list_arr;
HashTable **page_ht_arr;
} LNK_BaseRelocTask;
typedef struct
{
U64 page_size;
LNK_Section **sect_id_map;
LNK_BaseRelocPageList *list_arr;
LNK_Obj **obj_arr;
HashTable **page_ht_arr;
} LNK_ObjBaseRelocTask;
////////////////////////////////
internal LNK_BaseRelocPageArray lnk_base_reloc_page_array_from_list(Arena* arena, LNK_BaseRelocPageList list);
internal void lnk_emit_base_reloc_info(Arena *arena, LNK_Section **sect_id_map, U64 page_size, HashTable *page_ht, LNK_BaseRelocPageList *page_list, LNK_Reloc *reloc);
internal void lnk_base_reloc_page_array_sort(LNK_BaseRelocPageArray arr);
internal void lnk_build_base_relocs(TP_Context *tp, LNK_SectionTable *st, LNK_SymbolTable *symtab, COFF_MachineType machine, U64 page_size, LNK_ObjList obj_list);
+755
View File
@@ -0,0 +1,755 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
global read_only LNK_Chunk g_null_chunk = { 0, 0, /* is_discarded: */ 1 };
global read_only LNK_Chunk *g_null_chunk_ptr = &g_null_chunk;
internal LNK_ChunkRef
lnk_chunk_ref(U64 sect_id, U64 chunk_id)
{
LNK_ChunkRef ref = {0};
ref.sect_id = sect_id;
ref.chunk_id = chunk_id;
return ref;
}
internal B32
lnk_chunk_ref_is_equal(LNK_ChunkRef a, LNK_ChunkRef b)
{
B32 is_equal = a.sect_id == b.sect_id && a.chunk_id == b.chunk_id;
return is_equal;
}
internal LNK_ChunkNode *
lnk_chunk_list_push(Arena *arena, LNK_ChunkList *list, LNK_Chunk *chunk)
{
LNK_ChunkNode *node = push_array_no_zero(arena, LNK_ChunkNode, 1);
node->next = 0;
node->data = chunk;
SLLQueuePush(list->first, list->last, node);
++list->count;
return node;
}
internal void
lnk_chunk_list_concat_in_place(LNK_ChunkList *list, LNK_ChunkList *to_concat)
{
SLLConcatInPlace(list, to_concat);
}
internal void
lnk_chunk_list_concat_in_place_arr(LNK_ChunkList *list, LNK_ChunkList *arr, U64 count)
{
SLLConcatInPlaceArray(list, arr, count);
}
internal LNK_ChunkList **
lnk_make_chunk_list_arr_arr(Arena *arena, U64 slot_count, U64 per_count)
{
LNK_ChunkList **arr_arr = push_array_no_zero(arena, LNK_ChunkList *, slot_count);
for (U64 i = 0; i < slot_count; i += 1) {
arr_arr[i] = push_array(arena, LNK_ChunkList, per_count);
}
return arr_arr;
}
internal int
lnk_chunk_sort_index_is_before(void *raw_a, void *raw_b)
{
// Grouped Sections (PE Format)
// "All contributions with the same object-section name are allocated contiguously in the image,
// and the blocks of contributions are sorted in lexical order by object-section name."
LNK_ChunkPtr *a = raw_a;
LNK_ChunkPtr *b = raw_b;
// sort on section postfix
int cmp = str8_compar_case_sensetive(&(*a)->sort_idx, &(*b)->sort_idx);
// sort on obj position on command line
if (cmp == 0) {
cmp = u64_compar(&(*a)->input_idx, &(*b)->input_idx);
}
int is_before = cmp < 0;
return is_before;
}
internal void
lnk_chunk_array_sort(LNK_ChunkArray arr)
{
radsort(arr.v, arr.count, lnk_chunk_sort_index_is_before);
}
internal LNK_ChunkManager *
lnk_chunk_manager_alloc(Arena *arena, U64 id, U64 align)
{
ProfBeginFunction();
LNK_ChunkList temp_list = {0};
LNK_Chunk temp_chunk = {0};
temp_chunk.ref = lnk_chunk_ref(id, 0);
temp_chunk.align = align;
temp_chunk.type = LNK_Chunk_List;
temp_chunk.u.list = &temp_list;
LNK_ChunkManager *cman = push_array_no_zero(arena, LNK_ChunkManager, 1);
cman->total_chunk_count = 1; // null chunk
cman->root = 0;
cman->root = lnk_chunk_push_list(arena, cman, &temp_chunk, str8(0,0));
cman->root->align = align;
ProfEnd();
return cman;
}
internal LNK_Chunk *
lnk_chunk_push_(Arena *arena, LNK_Chunk *parent, U64 chunk_id, String8 sort_index)
{
ProfBeginFunction();
Assert(parent->type == LNK_Chunk_List);
LNK_ChunkList *list = parent->u.list;
LNK_Chunk *chunk = push_array_no_zero(arena, LNK_Chunk, 1);
chunk->ref = lnk_chunk_ref(parent->ref.sect_id, chunk_id);
chunk->align = 1;
chunk->is_discarded = 0;
chunk->sort_chunk = 1;
chunk->type = LNK_Chunk_Null;
chunk->sort_idx = push_str8_copy(arena, sort_index);
chunk->input_idx = list->count;
chunk->flags = 0;
chunk->associate = 0;
lnk_chunk_list_push(arena, list, chunk);
ProfEnd();
return chunk;
}
internal LNK_Chunk *
lnk_chunk_push(Arena *arena, LNK_ChunkManager *cman, LNK_Chunk *parent, String8 sort_index)
{
U64 chunk_id = cman->total_chunk_count;
++cman->total_chunk_count;
LNK_Chunk *chunk = lnk_chunk_push_(arena, parent, chunk_id, sort_index);
return chunk;
}
internal LNK_Chunk *
lnk_chunk_push_leaf(Arena *arena, LNK_ChunkManager *cman, LNK_Chunk *parent, String8 sort_index, void *raw_ptr, U64 raw_size)
{
LNK_Chunk *chunk = lnk_chunk_push(arena, cman, parent, sort_index);
chunk->type = LNK_Chunk_Leaf;
chunk->u.leaf = str8((U8 *)raw_ptr, raw_size);
return chunk;
}
internal LNK_Chunk *
lnk_chunk_push_list(Arena *arena, LNK_ChunkManager *cman, LNK_Chunk *parent, String8 sort_index)
{
LNK_Chunk *chunk = lnk_chunk_push(arena, cman, parent, sort_index);
chunk->type = LNK_Chunk_List;
chunk->u.list = push_array(arena, LNK_ChunkList, 1);
return chunk;
}
internal LNK_ChunkNode *
lnk_chunk_deep_copy(Arena *arena, LNK_Chunk *chunk)
{
ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 1);
LNK_ChunkNode *dst_root_node = push_array_no_zero(arena, LNK_ChunkNode, 1);
LNK_ChunkNode *src_root_node = push_array_no_zero(scratch.arena, LNK_ChunkNode, 1);
src_root_node->next = 0;
src_root_node->data = chunk;
struct Stack {
struct Stack *next;
LNK_ChunkNode *src_node;
LNK_ChunkNode *dst_node;
};
struct Stack *stack = push_array_no_zero(scratch.arena, struct Stack, 1);
stack->next = 0;
stack->src_node = src_root_node;
stack->dst_node = dst_root_node;
while (stack) {
while (stack->src_node) {
LNK_Chunk *src = stack->src_node->data;
LNK_Chunk *dst = stack->dst_node->data;
stack->src_node = stack->src_node->next;
stack->dst_node = stack->dst_node->next;
dst->ref = src->ref;
dst->align = src->align;
dst->sort_idx = push_str8_copy(arena, src->sort_idx);
dst->type = src->type;
dst->flags = src->flags;
lnk_chunk_set_debugf(arena, dst, "%S", src->debug);
switch (src->type) {
case LNK_Chunk_Null: break;
case LNK_Chunk_Leaf: {
B32 is_bss = src->u.leaf.str == 0;
if (is_bss) {
dst->u.leaf = src->u.leaf;
} else {
dst->u.leaf = push_str8_copy(arena, src->u.leaf);
}
} break;
case LNK_Chunk_List: {
LNK_ChunkNode *chain = 0;
LNK_ChunkNode *curr = 0;
if (src->u.list->count > 0) {
chain = push_array(arena, LNK_ChunkNode, src->u.list->count);
curr = chain;
for (U64 i = 1; i < src->u.list->count; ++i) {
curr->next = &chain[i];
curr = curr->next;
}
curr->next = 0;
}
dst->u.list = push_array_no_zero(arena, LNK_ChunkList, 1);
dst->u.list->count = src->u.list->count;
dst->u.list->first = chain;
dst->u.list->last = curr;
struct Stack *frame = push_array_no_zero(scratch.arena, struct Stack, 1);
frame->next = 0;
frame->src_node = src->u.list->first;
frame->dst_node = dst->u.list->first;
SLLStackPush(stack, frame);
} break;
default: InvalidPath; break;
}
}
SLLStackPop(stack);
}
scratch_end(scratch);
ProfEnd();
return dst_root_node;
}
internal LNK_ChunkNode *
lnk_merge_chunks(Arena *arena, LNK_ChunkManager *dst_cman, LNK_Chunk *dst, LNK_Chunk *src, U64 *id_map_out, U64 id_map_max)
{
ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 0);
Assert(src->ref.sect_id != dst->ref.sect_id);
Assert(dst->type == LNK_Chunk_List);
Assert(src->type != LNK_Chunk_Null);
LNK_ChunkNode *src_node = push_array(arena, LNK_ChunkNode, 1);
src_node->data = src;
struct Stack {
struct Stack *next;
LNK_ChunkNode *node;
};
struct Stack *stack = push_array_no_zero(scratch.arena, struct Stack, 1);
stack->next = 0;
stack->node = src_node;
while (stack) {
while (stack->node) {
LNK_Chunk *chunk = stack->node->data;
// advance node
stack->node = stack->node->next;
// allocate id
U64 new_id = dst_cman->total_chunk_count++;
// write id map
Assert(chunk->ref.chunk_id < id_map_max);
id_map_out[chunk->ref.chunk_id] = new_id;
// update id
chunk->ref = lnk_chunk_ref(dst->ref.sect_id, new_id);
// recurse down on lists
if (chunk->type == LNK_Chunk_List) {
struct Stack *frame = push_array_no_zero(scratch.arena, struct Stack, 1);
frame->next = 0;
frame->node = chunk->u.list->first;
SLLStackPush(stack, frame);
}
}
// reached end of chunk list, pop frame
SLLStackPop(stack);
}
// move source root copy to destination section
LNK_ChunkList *list = dst->u.list;
++list->count;
SLLQueuePush(list->first, list->last, src_node);
scratch_end(scratch);
ProfEnd();
return src_node;
}
internal void
lnk_chunk_associate(Arena *arena, LNK_Chunk *head, LNK_Chunk *chunk)
{
// for simplicity we don't support multiple associations,
// but it's possible to craft symbol table with multiple associations
Assert(!chunk->associate);
chunk->associate = head;
}
internal B32
lnk_chunk_is_discarded(LNK_Chunk *chunk)
{
B32 is_discarded = chunk->is_discarded;
LNK_Chunk *curr = chunk->associate;
while (!is_discarded && curr) {
is_discarded = curr->is_discarded;
curr = curr->associate;
}
return is_discarded;
}
internal U64
lnk_chunk_get_size(LNK_Chunk *chunk)
{
U64 result = 0;
switch (chunk->type) {
case LNK_Chunk_Null: break;
case LNK_Chunk_Leaf: {
result = chunk->u.leaf.size;
} break;
case LNK_Chunk_LeafArray:
case LNK_Chunk_List: {
Assert(!"TODO: list size");
} break;
}
return result;
}
internal U64
lnk_chunk_list_get_node_count(LNK_Chunk *chunk)
{
Assert(chunk->type == LNK_Chunk_List);
return chunk->u.list->count;
}
internal void
lnk_chunk_op_list_push_node(LNK_ChunkOpList *list, LNK_ChunkOp *op)
{
SLLQueuePush(list->first, list->last, op);
}
internal LNK_ChunkOp *
lnk_push_chunk_op_begin(Arena *arena, U64 chunk_id)
{
LNK_ChunkOp *begin_op = push_array_no_zero(arena, LNK_ChunkOp, 1);
begin_op->next = 0;
begin_op->type = LNK_ChunkOp_Begin;
begin_op->u.chunk_id = chunk_id;
return begin_op;
}
internal LNK_ChunkOp *
lnk_push_chunk_op_end_virt(Arena *arena)
{
LNK_ChunkOp *end_virt_op = push_array_no_zero(arena, LNK_ChunkOp, 1);
end_virt_op->next = 0;
end_virt_op->type = LNK_ChunkOp_EndVirt;
return end_virt_op;
}
internal LNK_ChunkOp *
lnk_push_chunk_op_end_file(Arena *arena)
{
LNK_ChunkOp *end_op = push_array_no_zero(arena, LNK_ChunkOp, 1);
end_op->next = 0;
end_op->type = LNK_ChunkOp_End;
return end_op;
}
internal LNK_ChunkOp *
lnk_push_chunk_op_align(Arena *arena, U64 align, U64 val)
{
LNK_ChunkOp *align_op = push_array_no_zero(arena, LNK_ChunkOp, 1);
align_op->next = 0;
align_op->type = LNK_ChunkOp_Align;
align_op->u.align.x = align;
align_op->u.align.val = val;
return align_op;
}
internal LNK_ChunkOp *
lnk_push_chunk_op_write(Arena *arena, String8 string)
{
LNK_ChunkOp *write_op = push_array_no_zero(arena, LNK_ChunkOp, 1);
write_op->next = 0;
write_op->type = LNK_ChunkOp_WriteString;
write_op->u.string = string;
return write_op;
}
internal LNK_ChunkOpList
lnk_op_list_from_chunk(Arena *arena, LNK_Chunk *root, U64 total_chunk_count, U8 align_byte)
{
struct Stack {
struct Stack *next;
LNK_ChunkArray chunk_array;
U64 ichunk;
};
ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 1);
// setup stack
struct Stack *stack = push_array(scratch.arena, struct Stack, 1);
stack->chunk_array.count = 1;
stack->chunk_array.v = &root;
// setup output list
LNK_ChunkOpList list = {0};
list.total_chunk_count = total_chunk_count;
list.first = list.last = 0;
// write null
LNK_ChunkOp *null_begin_op = lnk_push_chunk_op_begin(arena, 0);
LNK_ChunkOp *null_end_virt_op = lnk_push_chunk_op_end_virt(arena);
LNK_ChunkOp *null_end_file_op = lnk_push_chunk_op_end_file(arena);;
lnk_chunk_op_list_push_node(&list, null_begin_op);
lnk_chunk_op_list_push_node(&list, null_end_virt_op);
lnk_chunk_op_list_push_node(&list, null_end_file_op);
// traverse chunks from root
while (stack) {
while (stack->ichunk < stack->chunk_array.count) {
LNK_Chunk *chunk = stack->chunk_array.v[stack->ichunk++];
// skip unused chunks
if (lnk_chunk_is_discarded(chunk)) {
continue;
}
switch (chunk->type) {
case LNK_Chunk_Leaf: {
// align start in its own begin/end block so align bytes don't contribute to chunk size
LNK_ChunkOp *pad_begin_op = lnk_push_chunk_op_begin(arena, list.total_chunk_count++);
LNK_ChunkOp *pad_align_op = lnk_push_chunk_op_align(arena, chunk->align, align_byte);
LNK_ChunkOp *pad_end_file_op = lnk_push_chunk_op_end_file(arena);
lnk_chunk_op_list_push_node(&list, pad_begin_op);
lnk_chunk_op_list_push_node(&list, pad_align_op);
lnk_chunk_op_list_push_node(&list, pad_end_file_op);
// write leaf
LNK_ChunkOp *leaf_begin_op = lnk_push_chunk_op_begin(arena, chunk->ref.chunk_id);
LNK_ChunkOp *leaf_write_op = lnk_push_chunk_op_write(arena, chunk->u.leaf);
LNK_ChunkOp *leaf_align_op = lnk_push_chunk_op_align(arena, chunk->align, align_byte);
LNK_ChunkOp *leaf_end_virt_op = lnk_push_chunk_op_end_virt(arena);
LNK_ChunkOp *leaf_end_file_op = lnk_push_chunk_op_end_file(arena);
#if LNK_DUMP_CHUNK_LAYOUT
leaf_write_op->chunk = chunk;
#endif
lnk_chunk_op_list_push_node(&list, leaf_begin_op);
lnk_chunk_op_list_push_node(&list, leaf_write_op);
lnk_chunk_op_list_push_node(&list, leaf_align_op);
lnk_chunk_op_list_push_node(&list, leaf_end_virt_op);
lnk_chunk_op_list_push_node(&list, leaf_end_file_op);
} break;
case LNK_Chunk_LeafArray: {
LNK_ChunkOp *begin_op = lnk_push_chunk_op_begin(arena, chunk->ref.chunk_id);
LNK_ChunkOp *align_op = lnk_push_chunk_op_align(arena, chunk->align, align_byte);
lnk_chunk_op_list_push_node(&list, begin_op);
lnk_chunk_op_list_push_node(&list, align_op);
if (chunk->sort_chunk) {
lnk_chunk_array_sort(*chunk->u.arr);
}
struct Stack *frame = push_array_no_zero(scratch.arena, struct Stack, 1);
frame->next = 0;
frame->chunk_array = *chunk->u.arr;
frame->ichunk = 0;
SLLStackPush(stack, frame);
} goto yeild;
case LNK_Chunk_List: {
// balance ops at :end_chunk_series
LNK_ChunkOp *begin_op = lnk_push_chunk_op_begin(arena, chunk->ref.chunk_id);
LNK_ChunkOp *align_op = lnk_push_chunk_op_align(arena, chunk->align, align_byte);
lnk_chunk_op_list_push_node(&list, begin_op);
lnk_chunk_op_list_push_node(&list, align_op);
// chunk list -> chunk array
LNK_ChunkArray chunk_array = {0};
chunk_array.v = push_array_no_zero(scratch.arena, LNK_ChunkPtr, chunk->u.list->count);
for (LNK_ChunkNode *cptr = chunk->u.list->first; cptr != 0; cptr = cptr->next) {
chunk_array.v[chunk_array.count++] = cptr->data;
}
if (chunk->sort_chunk) {
lnk_chunk_array_sort(chunk_array);
}
// recurse into list chunk
struct Stack *frame = push_array_no_zero(scratch.arena, struct Stack, 1);
frame->next = 0;
frame->chunk_array = chunk_array;
frame->ichunk = 0;
SLLStackPush(stack, frame);
} goto yeild;
case LNK_Chunk_Null: { /* ignore */ } break;
}
}
// terminate series
if (stack->next) {
struct Stack *prev = stack->next;
Assert(prev->ichunk > 0);
// :end_chunk_series
LNK_ChunkOp *end_virt_op = lnk_push_chunk_op_end_virt(arena);
LNK_ChunkOp *align_op = lnk_push_chunk_op_align(arena, prev->chunk_array.v[prev->ichunk - 1]->align, align_byte);
LNK_ChunkOp *end_op = lnk_push_chunk_op_end_file(arena);
lnk_chunk_op_list_push_node(&list, end_virt_op);
lnk_chunk_op_list_push_node(&list, align_op);
lnk_chunk_op_list_push_node(&list, end_op);
}
// move to next frame
SLLStackPop(stack);
yeild:;
}
scratch_end(scratch);
ProfEnd();
return list;
}
internal LNK_ChunkLayout
lnk_chunk_layout_from_op_list(Arena *arena, LNK_ChunkOpList op_list, B32 is_data_inited)
{
ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 1);
// setup stack
struct Stack {
struct Stack *next;
U64 chunk_id;
U64 cursor;
} *stack = 0;
// setup state
U64 cursor = 0;
String8List data_list = {0};
// setup output
U64 *chunk_off_array = push_array_no_zero(arena, U64, op_list.total_chunk_count);
U64 *chunk_file_size_array = push_array_no_zero(arena, U64, op_list.total_chunk_count);
U64 *chunk_virt_size_array = push_array_no_zero(arena, U64, op_list.total_chunk_count);
// debug stomp so discarded chunks map to invalid offset
#if LNK_PARANOID
MemorySet(chunk_off_array, 0xFF, sizeof(*chunk_off_array) * op_list.total_chunk_count);
MemorySet(chunk_file_size_array, 0xFF, sizeof(*chunk_file_size_array) * op_list.total_chunk_count);
MemorySet(chunk_virt_size_array, 0xFF, sizeof(*chunk_virt_size_array) * op_list.total_chunk_count);
#endif
// execute opcodes
for (LNK_ChunkOp *op = op_list.first; op != NULL; op = op->next) {
switch (op->type) {
case LNK_ChunkOp_Null: break;
case LNK_ChunkOp_Begin: {
struct Stack *frame = push_array(scratch.arena, struct Stack, 1);
frame->chunk_id = op->u.chunk_id;
frame->cursor = cursor;
SLLStackPush(stack, frame);
chunk_off_array[stack->chunk_id] = stack->cursor;
} break;
case LNK_ChunkOp_End: {
chunk_file_size_array[stack->chunk_id] = cursor - stack->cursor;
SLLStackPop(stack);
} break;
case LNK_ChunkOp_EndVirt: {
chunk_virt_size_array[stack->chunk_id] = cursor - stack->cursor;
} break;
case LNK_ChunkOp_Align: {
Assert(IsPow2(op->u.align.x));
U64 size = AlignPow2(cursor, op->u.align.x) - cursor;
String8 string;
string.size = size;
string.str = push_array_no_zero(arena, U8, string.size);
MemorySet(string.str, op->u.align.val, string.size);
op->type = LNK_ChunkOp_WriteString;
op->u.string = string;
} // fall-through
case LNK_ChunkOp_WriteString: {
if (is_data_inited) {
// we allow chunks to have null for str for regions in the image that are zeroed out.
if (op->u.string.str == 0) {
op->u.string.str = push_array(arena, U8, op->u.string.size);
}
str8_list_push(scratch.arena, &data_list, op->u.string);
}
#if LNK_DUMP_CHUNK_LAYOUT
if (op->chunk) {
fprintf(g_layout_file, "[%.*s] %llX %.*s\n", str8_varg(op->chunk->sort_idx), op->chunk->input_idx, str8_varg(op->chunk->debug));
}
#endif
// advance
cursor += op->u.string.size;
} break;
}
}
// are begin/end series opcodes balanced?
Assert(stack == 0);
// fill out result
LNK_ChunkLayout layout = {0};
layout.data = str8_list_join(arena, &data_list, 0);
layout.chunk_off_array = chunk_off_array;
layout.chunk_file_size_array = chunk_file_size_array;
layout.chunk_virt_size_array = chunk_virt_size_array;
scratch_end(scratch);
ProfEnd();
return layout;
}
internal LNK_ChunkLayout
lnk_build_chunk_layout(Arena *arena, LNK_ChunkManager *cman, COFF_SectionFlags flags, U8 align_byte)
{
ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 1);
// should we write data for chunks?
B32 is_data_inited = !!(~flags & COFF_SectionFlag_CNT_UNINITIALIZED_DATA);
// build layout
LNK_ChunkOpList op_list = lnk_op_list_from_chunk(scratch.arena, cman->root, cman->total_chunk_count, align_byte);
LNK_ChunkLayout layout = lnk_chunk_layout_from_op_list(arena, op_list, is_data_inited);
scratch_end(scratch);
ProfEnd();
return layout;
}
internal B32
lnk_visit_chunks_(U64 sect_id, LNK_Chunk *chunk, LNK_ChunkVisitorSig *cb, void *ud)
{
// visit chunk
B32 is_done = cb(sect_id, chunk, ud);
if (is_done) {
return is_done;
}
switch (chunk->type) {
case LNK_Chunk_Null:
case LNK_Chunk_Leaf: {
// reached leaf
} break;
case LNK_Chunk_LeafArray: {
for (U64 idx = 0; idx < chunk->u.arr->count; idx += 1) {
is_done = lnk_visit_chunks_(sect_id, chunk->u.arr->v[idx], cb, ud);
if (is_done) {
break;
}
}
} break;
case LNK_Chunk_List: {
for (LNK_ChunkNode *i = chunk->u.list->first; i != 0; i = i->next) {
is_done = lnk_visit_chunks_(sect_id, i->data, cb, ud);
if (is_done) {
break;
}
}
} break;
}
return is_done;
}
internal void
lnk_visit_chunks(U64 sect_id, LNK_Chunk *chunk, LNK_ChunkVisitorSig *cb, void *ud)
{
lnk_visit_chunks_(sect_id, chunk, cb, ud);
}
LNK_CHUNK_VISITOR_SIG(lnk_save_chunk_ptr)
{
LNK_Chunk **id_map = (LNK_Chunk **)ud;
if (!chunk->is_discarded) {
id_map[chunk->ref.chunk_id] = chunk;
}
return 0;
}
internal LNK_ChunkPtr *
lnk_make_chunk_id_map(Arena *arena, LNK_ChunkManager *cman)
{
LNK_ChunkPtr *chunk_id_map = push_array_no_zero(arena, LNK_ChunkPtr, cman->total_chunk_count + 1);
lnk_visit_chunks(0, cman->root, lnk_save_chunk_ptr, chunk_id_map);
LNK_Chunk *null_chunk = push_array(arena, LNK_Chunk, 1);
null_chunk->is_discarded = 1;
chunk_id_map[0] = null_chunk;
return chunk_id_map;
}
internal LNK_ChunkNode *
lnk_chunk_ptr_list_reserve(Arena *arena, LNK_ChunkList *list, U64 count)
{
LNK_ChunkNode *arr = 0;
if (count) {
arr = push_array(arena, LNK_ChunkNode, count);
LNK_Chunk *chunk_arr = push_array(arena, LNK_Chunk, count);
for (U64 i = 0; i < count; i += 1) {
arr[i].data = &chunk_arr[i];
SLLQueuePush(list->first, list->last, &arr[i]);
}
list->count += count;
}
return arr;
}
internal String8Array
lnk_data_arr_from_chunk_ptr_list(Arena *arena, LNK_ChunkList list)
{
String8Array arr = {0};
arr.v = push_array(arena, String8, list.count);
for (LNK_ChunkNode *n = list.first; n != 0; n = n->next) {
LNK_ChunkPtr c = n->data;
Assert(c->type == LNK_Chunk_Leaf);
arr.v[arr.count] = c->u.leaf;
arr.count += 1;
}
return arr;
}
internal String8Array *
lnk_data_arr_from_chunk_ptr_list_arr(Arena *arena, LNK_ChunkList *list_arr, U64 count)
{
String8Array *result = push_array(arena, String8Array, count);
for (U64 i = 0; i < count; i += 1) {
result[i] = lnk_data_arr_from_chunk_ptr_list(arena, list_arr[i]);
}
return result;
}
+164
View File
@@ -0,0 +1,164 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
////////////////////////////////
#define LNK_DEBUG_CHUNKS 0
#define LNK_DUMP_CHUNK_LAYOUT 0
////////////////////////////////
typedef struct LNK_ChunkRef
{
U64 sect_id;
U64 chunk_id;
} LNK_ChunkRef;
typedef enum
{
LNK_Chunk_Null,
LNK_Chunk_Leaf,
LNK_Chunk_LeafArray,
LNK_Chunk_List,
} LNK_ChunkType;
typedef struct LNK_Chunk
{
LNK_ChunkRef ref;
LNK_ChunkType type;
U64 align;
B32 is_discarded;
B32 sort_chunk;
String8 sort_idx;
U64 input_idx;
COFF_SectionFlags flags;
struct LNK_Chunk *associate;
union {
String8 leaf;
struct LNK_ChunkList *list;
struct LNK_ChunkArray *arr;
} u;
#if LNK_DEBUG_CHUNKS
String8 debug;
#endif
} LNK_Chunk, * LNK_ChunkPtr;
typedef struct LNK_ChunkNode
{
struct LNK_ChunkNode *next;
LNK_ChunkPtr data;
} LNK_ChunkNode;
typedef struct LNK_ChunkArray
{
U64 count;
LNK_ChunkPtr *v;
} LNK_ChunkArray;
typedef struct LNK_ChunkList
{
U64 count;
LNK_ChunkNode *first;
LNK_ChunkNode *last;
} LNK_ChunkList;
typedef enum LNK_ChunkOpType
{
LNK_ChunkOp_Null,
LNK_ChunkOp_WriteString,
LNK_ChunkOp_Align,
LNK_ChunkOp_Begin,
LNK_ChunkOp_End,
LNK_ChunkOp_EndVirt,
} LNK_ChunkOpType;
typedef struct LNK_ChunkOp
{
struct LNK_ChunkOp *next;
LNK_ChunkOpType type;
union {
String8 string;
U64 chunk_id;
struct {
U64 val;
U64 x;
} align;
LNK_Chunk *leaf;
} u;
#if LNK_DUMP_CHUNK_LAYOUT
LNK_Chunk *chunk;
#endif
} LNK_ChunkOp;
typedef struct LNK_ChunkOpList
{
U64 total_chunk_count;
LNK_ChunkOp *first;
LNK_ChunkOp *last;
} LNK_ChunkOpList;
typedef struct LNK_ChunkLayout
{
String8 data;
U64 *chunk_off_array;
U64 *chunk_file_size_array;
U64 *chunk_virt_size_array;
} LNK_ChunkLayout;
typedef struct LNK_ChunkManager
{
LNK_Chunk *root;
U64 total_chunk_count;
} LNK_ChunkManager;
extern LNK_Chunk g_null_chunk;
extern LNK_Chunk *g_null_chunk_ptr;
internal LNK_ChunkRef lnk_chunk_ref(U64 sect_id, U64 chunk_id);
internal B32 lnk_chunk_ref_is_equal(LNK_ChunkRef a, LNK_ChunkRef b);
internal LNK_ChunkNode * lnk_chunk_list_push(Arena *arena, LNK_ChunkList *list, LNK_Chunk *chunk);
internal void lnk_chunk_list_concat_in_place(LNK_ChunkList *list, LNK_ChunkList *to_concat);
internal void lnk_chunk_list_concat_in_place_arr(LNK_ChunkList *list, LNK_ChunkList *arr, U64 count);
internal LNK_ChunkList ** lnk_make_chunk_list_arr_arr(Arena *arena, U64 slot_count, U64 per_count);
internal void lnk_chunk_array_sort(LNK_ChunkArray arr);
internal LNK_ChunkManager * lnk_chunk_manager_alloc(Arena *arena, U64 id, U64 align);
internal LNK_Chunk * lnk_chunk_push(Arena *arena, LNK_ChunkManager *cman, LNK_Chunk *parent, String8 sort_index);
internal LNK_Chunk * lnk_chunk_push_leaf(Arena *arena, LNK_ChunkManager *cman, LNK_Chunk *parent, String8 sort_index, void *raw_ptr, U64 raw_size);
internal LNK_Chunk * lnk_chunk_push_list(Arena *arena, LNK_ChunkManager *cman, LNK_Chunk *parent, String8 sort_index);
internal LNK_ChunkNode * lnk_chunk_deep_copy(Arena *arena, LNK_Chunk *chunk);
internal LNK_ChunkNode * lnk_merge_chunks(Arena *arena, LNK_ChunkManager *dst_cman, LNK_Chunk *dst, LNK_Chunk *src, U64 *id_map_out, U64 id_map_max);
internal void lnk_chunk_associate(Arena *arena, LNK_Chunk *head, LNK_Chunk *associate);
internal B32 lnk_chunk_is_discarded(LNK_Chunk *chunk);
internal U64 lnk_chunk_get_size(LNK_Chunk *chunk);
internal U64 lnk_chunk_list_get_node_count(LNK_Chunk *chunk);
internal void lnk_chunk_op_list_push_node(LNK_ChunkOpList *list, LNK_ChunkOp *op);
internal LNK_ChunkOp * lnk_push_chunk_op_begin(Arena *arena, U64 chunk_id);
internal LNK_ChunkOp * lnk_push_chunk_op_end_virt(Arena *arena);
internal LNK_ChunkOp * lnk_push_chunk_op_end_file(Arena *arena);
internal LNK_ChunkOp * lnk_push_chunk_op_align(Arena *arena, U64 align, U64 val);
internal LNK_ChunkOp * lnk_push_chunk_op_write(Arena *arena, String8 string);
internal LNK_ChunkOpList lnk_op_list_from_chunk(Arena *arena, LNK_Chunk *root, U64 total_chunk_count, U8 align_byte);
internal LNK_ChunkLayout lnk_chunk_layout_from_op_list(Arena *arena, LNK_ChunkOpList op_list, B32 is_data_inited);
internal LNK_ChunkLayout lnk_build_chunk_layout(Arena *arena, LNK_ChunkManager *cman, COFF_SectionFlags flags, U8 align_byte);
#define LNK_CHUNK_VISITOR_SIG(name) B32 name(U64 sect_id, LNK_Chunk *chunk, void *ud)
typedef LNK_CHUNK_VISITOR_SIG(LNK_ChunkVisitorSig);
internal void lnk_visit_chunks(U64 sect_id, LNK_Chunk *root, LNK_ChunkVisitorSig *cb, void *ud);
internal LNK_ChunkNode * lnk_chunk_ptr_list_reserve(Arena *arena, LNK_ChunkList *list, U64 count);
internal String8Array lnk_data_arr_from_chunk_ptr_list(Arena *arena, LNK_ChunkList list);
internal String8Array * lnk_data_arr_from_chunk_ptr_list_arr(Arena *arena, LNK_ChunkList *list_arr, U64 count);
#if LNK_DEBUG_CHUNKS
#define lnk_chunk_set_debugf(a, c, f, ...) do { (c)->debug = push_str8f((a), f, __VA_ARGS__); } while(0)
#else
#define lnk_chunk_set_debugf(a, c, f, ...) (void)(c)
#endif
+274
View File
@@ -0,0 +1,274 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal String8List
lnk_arg_list_parse_windows_rules(Arena *arena, String8 string)
{
Temp scratch = scratch_begin(&arena, 1);
String8List list = {0};
U8 *ptr = string.str;
U8 *opl = string.str + string.size;
while (ptr < opl) {
// skip white space and new lines
for (;;) {
U64 size = (U64)(opl - ptr);
UnicodeDecode uni = utf8_decode(ptr, size);
if (uni.codepoint != ' ' && uni.codepoint != '\n' && uni.codepoint != '\r') {
break;
}
ptr += uni.inc;
}
if (*ptr == '\0') {
break;
}
String8List token_builder = {0};
U8 *anchor = ptr;
while (ptr < opl) {
UnicodeDecode uni;
uni = utf8_decode(ptr, (U64)(opl-ptr));
if (uni.codepoint == '\0' || uni.codepoint == '\n' || uni.codepoint == '\r' || uni.codepoint == ' ') {
break;
}
// handle string and strip quotes
if (uni.codepoint == '"') {
String8 text_before_quote = str8(anchor, (U64)(ptr - anchor));
str8_list_push(scratch.arena, &token_builder, text_before_quote);
// advance past starting quote
ptr += uni.inc;
anchor = ptr;
U8 *quote_end = ptr;
while (ptr < opl) {
uni = utf8_decode(ptr, (U64)(opl - ptr));
ptr += uni.inc;
// skip escape char
if (uni.codepoint == '\\') {
uni = utf8_decode(ptr, (U64)(opl - ptr));
ptr += uni.inc;
} else if (uni.codepoint == '"' || uni.codepoint == '\0') {
break; // found matching quote char
}
quote_end = ptr;
}
String8 text_inside_quotes = str8(anchor, (U64)(quote_end - anchor));
str8_list_push(scratch.arena, &token_builder, text_inside_quotes);
anchor = ptr;
} else {
ptr += uni.inc;
}
}
// push remaining text
String8 text = str8(anchor, (U64)(ptr - anchor));
str8_list_push(scratch.arena, &token_builder, text);
// push token
String8 token = str8_list_join(arena, &token_builder, NULL);
if (token.size) {
str8_list_push(arena, &list, token);
}
}
scratch_end(scratch);
return list;
}
internal void
lnk_cmd_line_push_option_node(LNK_CmdLine *cmd_line, LNK_CmdOption *opt)
{
SLLQueuePush(cmd_line->first_option, cmd_line->last_option, opt);
cmd_line->option_count += 1;
}
internal LNK_CmdOption *
lnk_cmd_line_push_option_list(Arena *arena, LNK_CmdLine *cmd_line, String8 string, String8List value_strings)
{
// fill out node
LNK_CmdOption *opt = push_array_no_zero(arena, LNK_CmdOption, 1);
opt->next = 0;
opt->string = string;
opt->value_strings = value_strings;
// push node
lnk_cmd_line_push_option_node(cmd_line, opt);
return opt;
}
internal LNK_CmdOption *
lnk_cmd_line_push_option_string(Arena *arena, LNK_CmdLine *cmd_line, String8 string, String8 value)
{
String8List value_list = str8_split_by_string_chars(arena, value, str8_lit(","), StringSplitFlag_KeepEmpties);
LNK_CmdOption *opt = lnk_cmd_line_push_option_list(arena, cmd_line, string, value_list);
return opt;
}
internal LNK_CmdOption *
lnk_cmd_line_push_option(Arena *arena, LNK_CmdLine *cmd_line, char *string, char *value)
{
return lnk_cmd_line_push_option_string(arena, cmd_line, str8_cstring(string), str8_cstring(value));
}
internal LNK_CmdOption *
lnk_cmd_line_push_option_if_not_present(Arena *arena, LNK_CmdLine *cmd_line, char *string, char *value)
{
if (!lnk_cmd_line_has_option(*cmd_line, string)) {
return lnk_cmd_line_push_option(arena, cmd_line, string, value);
}
return 0;
}
internal LNK_CmdLine
lnk_cmd_line_parse_windows_rules(Arena *arena, String8List arg_list)
{
Temp scratch = scratch_begin(&arena, 1);
LNK_CmdLine cmd_line = {0};
for (String8Node *arg_node = arg_list.first; arg_node != 0; arg_node = arg_node->next) {
String8 arg = arg_node->string;
B32 is_option = str8_match(str8_lit("/"), arg, StringMatchFlag_RightSideSloppy) ||
str8_match(str8_lit("-"), arg, StringMatchFlag_RightSideSloppy);
if (is_option) {
U64 param_start_pos = str8_find_needle(arg, 0, str8_lit(":"), 0);
String8 option_name = str8_chop(arg, arg.size - param_start_pos);
// remove '/' or '-' from option name
option_name = str8_skip(option_name, 1);
// skip ':'
String8 value_string = str8_skip(arg, param_start_pos + 1);
// make value list
String8List value_list = str8_split_by_string_chars(arena, value_string, str8_lit(","), 0);
// push command
lnk_cmd_line_push_option_list(arena, &cmd_line, option_name, value_list);
} else {
str8_list_push(arena, &cmd_line.input_list, arg);
}
}
scratch_end(scratch);
return cmd_line;
}
internal LNK_CmdOption *
lnk_cmd_line_option_from_string(LNK_CmdLine cmd_line, String8 string)
{
LNK_CmdOption *opt;
for (opt = cmd_line.first_option; opt != NULL; opt = opt->next) {
if (str8_match(string, opt->string, StringMatchFlag_CaseInsensitive)) {
break;
}
}
return opt;
}
internal B32
lnk_cmd_line_has_option_string(LNK_CmdLine cmd_line, String8 string)
{
LNK_CmdOption *opt = lnk_cmd_line_option_from_string(cmd_line, string);
B32 has_option = (opt != 0);
return has_option;
}
internal B32
lnk_cmd_line_has_option(LNK_CmdLine cmd_line, char *string)
{
return lnk_cmd_line_has_option_string(cmd_line, str8_cstring(string));
}
internal String8List
lnk_unwrap_rsp(Arena *arena, String8List arg_list)
{
Temp scratch = scratch_begin(&arena, 1);
String8List result = {0};
for (String8Node *curr = arg_list.first; curr != 0; curr = curr->next) {
B32 is_rsp = str8_match(str8_lit("@"), curr->string, StringMatchFlag_RightSideSloppy);
if (is_rsp) {
// remove "@"
String8 name = str8_skip(curr->string, 1);
if (os_file_path_exists(name)) {
// read rsp from disk
String8 file = os_data_from_file_path(scratch.arena, name);
// parse rsp
String8List rsp_args = lnk_arg_list_parse_windows_rules(scratch.arena, file);
// handle case where rsp references another rsp
String8List list = lnk_unwrap_rsp(arena, rsp_args);
// push arguments from rsp
list = str8_list_copy(arena, &list);
str8_list_concat_in_place(&result, &list);
} else {
lnk_error(LNK_Error_Cmdl, "unable to find rsp: %S", name);
}
} else {
// push regular argument
String8 str = push_str8_copy(arena, curr->string);
str8_list_push(arena, &result, str);
}
}
scratch_end(scratch);
return result;
}
internal String8List
lnk_data_from_cmd_line(Arena *arena, LNK_CmdLine cmd_line)
{
String8List result = {0};
for (LNK_CmdOption *opt = cmd_line.first_option; opt != 0; opt = opt->next) {
// separate directives
if (opt != cmd_line.first_option) {
str8_list_pushf(arena, &result, " ");
}
// push new directive
str8_list_pushf(arena, &result, "/%.*s", str8_varg(opt->string));
// do we have arguments?
if (opt->value_strings.node_count > 0) {
str8_list_pushf(arena, &result, ":");
for (String8Node *value_node = opt->value_strings.first; value_node != 0; value_node = value_node->next) {
// separate arguments
if (value_node != opt->value_strings.first) {
str8_list_pushf(arena, &result, ",");
}
// push argument
B32 has_spaces = str8_find_needle(value_node->string, 0, str8_lit(" "), StringMatchFlag_CaseInsensitive) < value_node->string.size;
if (has_spaces) {
str8_list_pushf(arena, &result, "\"%.*s\"", str8_varg(value_node->string));
} else {
str8_list_pushf(arena, &result, "%.*s", str8_varg(value_node->string));
}
}
}
}
// append inputs
for (String8Node *input_node = cmd_line.input_list.first; input_node != 0; input_node = input_node->next) {
if (input_node != cmd_line.input_list.first) {
str8_list_pushf(arena, &result, " ");
}
str8_list_pushf(arena, &result, "\"%.*s\"", str8_varg(input_node->string));
}
return result;
}
+33
View File
@@ -0,0 +1,33 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef struct LNK_CmdOption
{
struct LNK_CmdOption *next;
String8 string;
String8List value_strings;
} LNK_CmdOption;
typedef struct LNK_CmdLine
{
U64 option_count;
LNK_CmdOption *first_option;
LNK_CmdOption *last_option;
String8List input_list;
} LNK_CmdLine;
internal String8List lnk_arg_list_parse_windows_rules(Arena *arena, String8 string);
internal LNK_CmdLine lnk_cmd_line_parse_windows_rules(Arena *arena, String8List arg_list);
internal LNK_CmdOption * lnk_cmd_line_option_from_string(LNK_CmdLine cmd_line, String8 string);
internal B32 lnk_cmd_line_has_option_string(LNK_CmdLine cmd_line, String8 string);
internal B32 lnk_cmd_line_has_option(LNK_CmdLine cmd_line, char *string);
internal LNK_CmdOption * lnk_cmd_line_push_option(Arena *arena, LNK_CmdLine *cmd_line, char *string, char *value);
internal LNK_CmdOption * lnk_cmd_line_push_option_if_not_present(Arena *arena, LNK_CmdLine *cmd_line, char *string, char *value);
internal String8List lnk_unwrap_rsp(Arena *arena, String8List arg_list);
internal String8List lnk_data_from_cmd_line(Arena *arena, LNK_CmdLine cmd_line);
File diff suppressed because it is too large Load Diff
+480
View File
@@ -0,0 +1,480 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef enum
{
LNK_CmdSwitch_Null,
LNK_CmdSwitch_NotImplemented,
LNK_CmdSwitch_Deprecated,
LNK_CmdSwitch_Align,
LNK_CmdSwitch_AllowBind,
LNK_CmdSwitch_AllowIsolation,
LNK_CmdSwitch_AlternateName,
LNK_CmdSwitch_AppContainer,
LNK_CmdSwitch_Base,
LNK_CmdSwitch_Debug,
LNK_CmdSwitch_DefaultLib,
LNK_CmdSwitch_Delay,
LNK_CmdSwitch_DelayLoad,
LNK_CmdSwitch_Dll,
LNK_CmdSwitch_DynamicBase,
LNK_CmdSwitch_Entry,
LNK_CmdSwitch_FastFail,
LNK_CmdSwitch_FileAlign,
LNK_CmdSwitch_Fixed,
LNK_CmdSwitch_FunctionPadMin,
LNK_CmdSwitch_Heap,
LNK_CmdSwitch_HighEntropyVa,
LNK_CmdSwitch_Ignore,
LNK_CmdSwitch_ImpLib,
LNK_CmdSwitch_Include,
LNK_CmdSwitch_Incremental,
LNK_CmdSwitch_LargeAddressAware,
LNK_CmdSwitch_LibPath,
LNK_CmdSwitch_Machine,
LNK_CmdSwitch_Manifest,
LNK_CmdSwitch_ManifestDependency,
LNK_CmdSwitch_ManifestFile,
LNK_CmdSwitch_ManifestInput,
LNK_CmdSwitch_ManifestUac,
LNK_CmdSwitch_Natvis,
LNK_CmdSwitch_NoDefaultLib,
LNK_CmdSwitch_NoLogo,
LNK_CmdSwitch_NxCompat,
LNK_CmdSwitch_Opt,
LNK_CmdSwitch_Out,
LNK_CmdSwitch_Pdb,
LNK_CmdSwitch_PdbPageSize,
LNK_CmdSwitch_Stack,
LNK_CmdSwitch_SubSystem,
LNK_CmdSwitch_Time,
LNK_CmdSwitch_TsAware,
// -- NOT Implemented:
LNK_CmdSwitch_AssemblyDebug,
LNK_CmdSwitch_AssemblyLinkResource,
LNK_CmdSwitch_AssemblyModule,
LNK_CmdSwitch_AssemblyResource,
LNK_CmdSwitch_ClrImageType,
LNK_CmdSwitch_ClrLoaderOptimization,
LNK_CmdSwitch_ClrSupportLastError,
LNK_CmdSwitch_ClrThreadAttribute,
LNK_CmdSwitch_ClrRunManagedCodeCheck,
LNK_CmdSwitch_ClrUnmanagedCheck,
LNK_CmdSwitch_Def,
LNK_CmdSwitch_DelaySign,
LNK_CmdSwitch_DependentLoadFlag,
LNK_CmdSwitch_Driver,
LNK_CmdSwitch_DisallowLib,
LNK_CmdSwitch_EmitVolatileMetadata,
LNK_CmdSwitch_ErrorReport,
LNK_CmdSwitch_Export,
LNK_CmdSwitch_ExportAdmin,
LNK_CmdSwitch_FastGenProfile,
LNK_CmdSwitch_Force,
LNK_CmdSwitch_Guard,
LNK_CmdSwitch_GenProfile,
LNK_CmdSwitch_IdlOut,
LNK_CmdSwitch_IgnoreIdl,
LNK_CmdSwitch_Ilk,
LNK_CmdSwitch_IntegrityCheck,
LNK_CmdSwitch_Kernel,
LNK_CmdSwitch_KeyContainer,
LNK_CmdSwitch_KeyFile,
LNK_CmdSwitch_LinkerRepro,
LNK_CmdSwitch_LinkerReproTarget,
LNK_CmdSwitch_Ltcg,
LNK_CmdSwitch_LtcgOut,
LNK_CmdSwitch_Map,
LNK_CmdSwitch_MapInfo,
LNK_CmdSwitch_Merge,
LNK_CmdSwitch_Midl,
LNK_CmdSwitch_NoAssembly,
LNK_CmdSwitch_NoEntry,
LNK_CmdSwitch_NoExp,
LNK_CmdSwitch_NoImpLib,
LNK_CmdSwitch_Order,
LNK_CmdSwitch_PdbStripped,
LNK_CmdSwitch_Profile,
LNK_CmdSwitch_Release,
LNK_CmdSwitch_SafeSeh,
LNK_CmdSwitch_Section,
LNK_CmdSwitch_SourceLink,
LNK_CmdSwitch_Stub,
LNK_CmdSwitch_SwapRun,
LNK_CmdSwitch_TlbId,
LNK_CmdSwitch_UserProfile,
LNK_CmdSwitch_Verbose,
LNK_CmdSwitch_Version,
LNK_CmdSwitch_Winmd,
LNK_CmdSwitch_WinmdDelaySign,
LNK_CmdSwitch_WinmdKeyContainer,
LNK_CmdSwitch_WinmdKeyFile,
LNK_CmdSwitch_WholeArchive,
LNK_CmdSwitch_Wx,
LNK_CmdSwitch_Rad_Age,
LNK_CmdSwitch_Rad_BuildInfo,
LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll,
LNK_CmdSwitch_Rad_Debug,
LNK_CmdSwitch_Rad_DebugName,
LNK_CmdSwitch_Rad_DelayBind,
LNK_CmdSwitch_Rad_DeleteManifest,
LNK_CmdSwitch_Rad_DoMerge,
LNK_CmdSwitch_Rad_EnvLib,
LNK_CmdSwitch_Rad_Exe,
LNK_CmdSwitch_Rad_Guid,
LNK_CmdSwitch_Rad_LargePages,
LNK_CmdSwitch_Rad_LinkVer,
LNK_CmdSwitch_Rad_Log,
LNK_CmdSwitch_Rad_Logo,
LNK_CmdSwitch_Rad_MtPath,
LNK_CmdSwitch_Rad_OsVer,
LNK_CmdSwitch_Rad_PageSize,
LNK_CmdSwitch_Rad_PathStyle,
LNK_CmdSwitch_Rad_SectVirtOff,
LNK_CmdSwitch_Rad_SuppressError,
LNK_CmdSwitch_Rad_SymbolTableCapDefined,
LNK_CmdSwitch_Rad_SymbolTableCapInternal,
LNK_CmdSwitch_Rad_SymbolTableCapWeak,
LNK_CmdSwitch_Rad_SymbolTableCapLib,
LNK_CmdSwitch_Rad_TargetOs,
LNK_CmdSwitch_Rad_TimeStamp,
LNK_CmdSwitch_Rad_Version,
LNK_CmdSwitch_Rad_Workers,
LNK_CmdSwitch_Help,
LNK_CmdSwitch_Count
} LNK_CmdSwitchType;
typedef enum
{
LNK_SwitchState_Null,
LNK_SwitchState_No,
LNK_SwitchState_Yes
} LNK_SwitchState;
typedef enum
{
LNK_Input_Null,
LNK_Input_Obj,
LNK_Input_Lib,
LNK_Input_Res,
LNK_Input_Manifest,
LNK_Input_Count
} LNK_InputType;
enum
{
LNK_ConfigFlag_Fixed = (1 << 0),
LNK_ConfigFlag_Merge = (1 << 1),
LNK_ConfigFlag_EnvLib = (1 << 2),
LNK_ConfigFlag_DelayUnload = (1 << 3),
LNK_ConfigFlag_DelayBind = (1 << 4),
LNK_ConfigFlag_CheckUnusedDelayLoadDll = (1 << 5),
LNK_ConfigFlag_NoTsAware = (1 << 6),
LNK_ConfigFlag_WriteImageChecksum = (1 << 8),
LNK_ConfigFlag_ManifestEmbed = (1 << 9),
};
typedef U64 LNK_ConfigFlags;
typedef enum
{
LNK_DebugMode_Null,
LNK_DebugMode_None,
LNK_DebugMode_FastLink,
LNK_DebugMode_GHash,
LNK_DebugMode_Full,
} LNK_DebugMode;
enum
{
LNK_Guard_None = 0,
LNK_Guard_Cf = (1 << 0),
LNK_Guard_LongJmp = (1 << 1),
LNK_Guard_EhCont = (1 << 2),
LNK_Guard_All = LNK_Guard_Cf | LNK_Guard_LongJmp | LNK_Guard_EhCont
};
typedef U32 LNK_GuardFlags;
typedef enum
{
LNK_ManifestOpt_Null,
LNK_ManifestOpt_Embed,
LNK_ManifestOpt_No
} LNK_ManifestOpt;
typedef struct LNK_AltNameList
{
String8List from_list;
String8List to_list;
} LNK_AltNameList;
typedef enum
{
LNK_DebugInfoGuid_Null,
Lnk_DebugInfoGuid_ImageBlake3,
} LNK_DebugInfoGuidType;
typedef struct LNK_Config
{
LNK_ConfigFlags flags;
LNK_DebugMode debug_mode;
LNK_SwitchState opt_ref;
LNK_SwitchState opt_icf;
LNK_SwitchState opt_lbr;
U64 opt_iter_count;
LNK_GuardFlags guard_flags;
LNK_DebugInfoGuidType guid_type;
OS_Guid guid;
COFF_TimeStamp time_stamp;
U32 age;
U64 section_virt_off;
U64 file_align;
U64 sect_align;
U64 stack_reserve;
U64 stack_commit;
U64 heap_reserve;
U64 heap_commit;
U64 user_base_addr;
U64 max_image_size;
U64 page_size;
U64 pdb_page_size;
U64 worker_count;
U64 function_pad_min;
U64 manifest_resource_id;
Version link_ver;
Version os_ver;
Version image_ver;
OperatingSystem target_os;
COFF_MachineType machine;
PE_WindowsSubsystem subsystem;
Version subsystem_ver;
PE_ImageFileCharacteristics file_characteristics;
PE_DllCharacteristics dll_characteristics;
String8 user_entry_point_name;
String8 entry_point_name;
String8List lib_dir_list;
PathStyle path_style;
LNK_ManifestOpt manifest_opt;
String8 work_dir;
String8 image_name;
String8 imp_lib_name;
String8List raw_cmd_line;
String8 pdb_name;
String8 mt_path;
String8List input_list[LNK_Input_Count];
String8List input_default_lib_list;
String8List disallow_lib_list;
String8List delay_load_dll_list;
String8List natvis_list;
LNK_SwitchState delete_manifest;
String8 manifest_name;
B32 manifest_uac;
String8 manifest_level;
String8 manifest_ui_access;
String8List manifest_dependency_list;
LNK_SwitchState rad_debug;
String8 rad_debug_name;
String8List include_symbol_list;
LNK_AltNameList alt_name_list;
U64 symbol_table_cap_defined;
U64 symbol_table_cap_internal;
U64 symbol_table_cap_weak;
U64 symbol_table_cap_lib;
} LNK_Config;
typedef enum
{
LNK_ParseU64Flag_CheckUnder32bit = (1 << 0),
LNK_ParseU64Flag_CheckPow2 = (1 << 1),
} LNK_ParseU64Flags;
////////////////////////////////
typedef enum
{
LNK_MsErrorCode_Lnk1000 = 1000,
LNK_MsErrorCode_Lnk1103 = 1103,
LNK_MsErrorCode_Lnk1104 = 1104,
LNK_MsErrorCode_Lnk1106 = 1106,
LNK_MsErrorCode_Lnk1107 = 1107,
LNK_MsErrorCode_Lnk1112 = 1112,
LNK_MsErrorCode_Lnk1113 = 1113,
LNK_MsErrorCode_Lnk1120 = 1120,
LNK_MsErrorCode_Lnk1123 = 1123,
LNK_MsErrorCode_Lnk1127 = 1127,
LNK_MsErrorCode_Lnk1136 = 1136,
LNK_MsErrorCode_Lnk1140 = 1140,
LNK_MsErrorCode_Lnk1141 = 1141,
LNK_MsErrorCode_Lnk1143 = 1143,
LNK_MsErrorCode_Lnk1152 = 1152,
LNK_MsErrorCode_Lnk1158 = 1158,
LNK_MsErrorCode_Lnk1164 = 1164,
LNK_MsErrorCode_Lnk1166 = 1166,
LNK_MsErrorCode_Lnk1168 = 1168,
LNK_MsErrorCode_Lnk1169 = 1169,
LNK_MsErrorCode_Lnk1170 = 1170,
LNK_MsErrorCode_Lnk1179 = 1179,
LNK_MsErrorCode_Lnk1181 = 1181,
LNK_MsErrorCode_Lnk1189 = 1189,
LNK_MsErrorCode_Lnk1196 = 1196,
LNK_MsErrorCode_Lnk1200 = 1200,
LNK_MsErrorCode_Lnk1201 = 1201,
LNK_MsErrorCode_Lnk1211 = 1211,
LNK_MsErrorCode_Lnk1215 = 1215,
LNK_MsErrorCode_Lnk1218 = 1218,
LNK_MsErrorCode_Lnk1221 = 1221,
LNK_MsErrorCode_Lnk1223 = 1223,
LNK_MsErrorCode_Lnk1224 = 1224,
LNK_MsErrorCode_Lnk1237 = 1237,
LNK_MsErrorCode_Lnk1240 = 1240,
LNK_MsErrorCode_Lnk1241 = 1241,
LNK_MsErrorCode_Lnk1245 = 1245,
LNK_MsErrorCode_Lnk1248 = 1248,
LNK_MsErrorCode_Lnk1256 = 1256,
LNK_MsErrorCode_Lnk1264 = 1264,
LNK_MsErrorCode_Lnk1277 = 1277,
LNK_MsErrorCode_Lnk1282 = 1282,
LNK_MsErrorCode_Lnk1287 = 1287,
LNK_MsErrorCode_Lnk1296 = 1296,
LNK_MsErrorCode_Lnk1301 = 1301,
LNK_MsErrorCode_Lnk1302 = 1302,
LNK_MsErrorCode_Lnk1306 = 1306,
LNK_MsErrorCode_Lnk1309 = 1309,
LNK_MsErrorCode_Lnk1312 = 1312,
LNK_MsErrorCode_Lnk1313 = 1313,
LNK_MsErrorCode_Lnk1314 = 1314,
LNK_MsErrorCode_Lnk1318 = 1318,
LNK_MsErrorCode_Lnk1332 = 1332,
LNK_MsErrorCode_Lnk1352 = 1352,
LNK_MsErrorCode_Lnk1561 = 1561,
LNK_MsErrorCode_Lnk2001 = 2001,
LNK_MsErrorCode_Lnk2004 = 2004,
LNK_MsErrorCode_Lnk2005 = 2005,
LNK_MsErrorCode_Lnk2008 = 2008,
LNK_MsErrorCode_Lnk2011 = 2011,
LNK_MsErrorCode_Lnk2013 = 2013,
LNK_MsErrorCode_Lnk2017 = 2017,
LNK_MsErrorCode_Lnk2019 = 2019,
LNK_MsErrorCode_Lnk2020 = 2020,
LNK_MsErrorCode_Lnk2022 = 2022,
LNK_MsErrorCode_Lnk2023 = 2023,
LNK_MsErrorCode_Lnk2026 = 2026,
LNK_MsErrorCode_Lnk2027 = 2027,
LNK_MsErrorCode_Lnk2031 = 2031,
LNK_MsErrorCode_Lnk2033 = 2033,
LNK_MsErrorCode_Lnk2038 = 2038,
LNK_MsErrorCode_Lnk2039 = 2039,
LNK_MsWarningCode_Lnk4001 = 4001,
LNK_MsWarningCode_Lnk4002 = 4002,
LNK_MsWarningCode_Lnk4006 = 4006,
LNK_MsWarningCode_Lnk4010 = 4010,
LNK_MsWarningCode_Lnk4014 = 4014,
LNK_MsWarningCode_Lnk4020 = 4020,
LNK_MsWarningCode_Lnk4022 = 4022,
LNK_MsWarningCode_Lnk4039 = 4039,
LNK_MsWarningCode_Lnk4044 = 4044,
LNK_MsWarningCode_Lnk4049 = 4049,
LNK_MsWarningCode_Lnk4065 = 4065,
LNK_MsWarningCode_Lnk4070 = 4070,
LNK_MsWarningCode_Lnk4071 = 4071,
LNK_MsWarningCode_Lnk4073 = 4073,
LNK_MsWarningCode_Lnk4075 = 4075,
LNK_MsWarningCode_Lnk4076 = 4076,
LNK_MsWarningCode_SectionFlagsConflict = 4078,
LNK_MsWarningCode_Lnk4086 = 4086,
LNK_MsWarningCode_Lnk4092 = 4092,
LNK_MsWarningCode_Lnk4096 = 4096,
LNK_MsWarningCode_Lnk4098 = 4098,
LNK_MsWarningCode_MissingExternalTypeServer = 4099,
LNK_MsWarningCode_Lnk4102 = 4102,
LNK_MsWarningCode_Lnk4104 = 4104,
LNK_MsWarningCode_Lnk4105 = 4105,
LNK_MsWarningCode_Lnk4194 = 4194,
LNK_MsWarningCode_Lnk4197 = 4197,
LNK_MsWarningCode_UnsuedDelayLoadDll = 4199,
LNK_MsWarningCode_Lnk4200 = 4200,
LNK_MsWarningCode_Lnk4204 = 4204,
LNK_MsWarningCode_Lnk4205 = 4205,
LNK_MsWarningCode_Lnk4206 = 4206,
LNK_MsWarningCode_Lnk4210 = 4210,
LNK_MsWarningCode_Lnk4216 = 4216,
LNK_MsWarningCode_Lnk4217 = 4217,
LNK_MsWarningCode_Lnk4219 = 4219,
LNK_MsWarningCode_Lnk4220 = 4220,
LNK_MsWarningCode_Lnk4221 = 4221,
LNK_MsWarningCode_Lnk4222 = 4222,
LNK_MsWarningCode_Lnk4224 = 4224,
LNK_MsWarningCode_Lnk4227 = 4227,
LNK_MsWarningCode_Lnk4229 = 4229,
LNK_MsWarningCode_Lnk4237 = 4237,
LNK_MsWarningCode_Lnk4247 = 4247,
LNK_MsWarningCode_Lnk4248 = 4248,
LNK_MsWarningCode_Lnk4253 = 4253,
LNK_MsWarningCode_Lnk4254 = 4254,
LNK_MsWarningCode_Lnk4286 = 4286,
} LNK_MsErrorCode;
////////////////////////////////
// Enum <-> String
internal String8 lnk_string_cmd_switch_type(LNK_CmdSwitchType type);
internal LNK_CmdSwitchType lnk_cmd_switch_from_string(String8 string);
internal LNK_InputType lnk_input_type_from_string(String8 string);
internal LNK_DebugMode lnk_debug_mode_from_string(String8 string);
////////////////////////////////
// Command Line Helpers
internal LNK_CmdOption * lnk_cmd_line_push_option_if_not_presentf(Arena *arena, LNK_CmdLine *cmd_line, LNK_CmdSwitchType cmd_switch_type, char *param_fmt, ...);
internal LNK_CmdOption * lnk_cmd_line_push_optionf(Arena *arena, LNK_CmdLine *cmd_line, LNK_CmdSwitchType cmd_switch_type, char *param_fmt, ...);
internal B32 lnk_cmd_line_has_switch(LNK_CmdLine cmd_line, LNK_CmdSwitchType cmd_switch_type);
////////////////////////////////
// Errors
internal void lnk_error_cmd_switch(LNK_ErrorCode code, LNK_CmdSwitchType cmd_switch, char *fmt, ...);
internal void lnk_error_cmd_switch_invalid_param_count(LNK_ErrorCode code, LNK_CmdSwitchType cmd_switch);
internal void lnk_error_cmd_switch_invalid_param(LNK_ErrorCode code, LNK_CmdSwitchType cmd_switch, String8 param);
////////////////////////////////
// Getters
internal U64 lnk_get_base_addr(LNK_Config *config);
internal Version lnk_get_default_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine);
internal Version lnk_get_min_subsystem_version(PE_WindowsSubsystem subsystem, COFF_MachineType machine);
internal String8 lnk_get_mt_path(Arena *arena);
internal B32 lnk_do_debug_info(LNK_Config *config);
////////////////////////////////
// Specialized Parsers
internal B32 lnk_cmd_switch_parse_version(String8List value_strings, LNK_CmdSwitchType cmd_switch, Version *ver_out);
internal B32 lnk_cmd_switch_parse_tuple(String8List value_strings, LNK_CmdSwitchType cmd_switch, Rng1U64 *tuple_out);
internal B32 lnk_cmd_switch_parse_u64(String8List value_strings, LNK_CmdSwitchType cmd_switch, U64 *value_out, LNK_ParseU64Flags flags);
internal B32 lnk_cmd_switch_parse_u32(String8List value_strings, LNK_CmdSwitchType cmd_switch, U32 *value_out, LNK_ParseU64Flags flags);
internal B32 lnk_cmd_switch_parse_flag(String8List value_strings, LNK_CmdSwitchType cmd_switch, LNK_SwitchState *value_out);
internal void lnk_cmd_switch_set_flag_inv_16(String8List value_strings, LNK_CmdSwitchType cmd_switch, U16 *flags, U16 bits);
internal void lnk_cmd_switch_set_flag_inv_64(String8List value_strings, LNK_CmdSwitchType cmd_switch, U64 *flags, U64 bits);
internal void lnk_cmd_switch_set_flag_16(String8List value_strings, LNK_CmdSwitchType cmd_switch, U16 *flags, U16 bits);
internal void lnk_cmd_switch_set_flag_32(String8List value_strings, LNK_CmdSwitchType cmd_switch, U32 *flags, U32 bits);
internal void lnk_cmd_switch_set_flag_64(String8List value_strings, LNK_CmdSwitchType cmd_switch, U64 *flags, U64 bits);
internal B32 lnk_cmd_switch_parse_string(String8List value_strings, LNK_CmdSwitchType cmd_switch, String8 *string_out);
internal void lnk_cmd_switch_parse_string_copy(Arena *arena, String8List value_strings, LNK_CmdSwitchType cmd_switch, String8 *string_out);
internal B32 lnk_parse_alt_name_directive(Arena *arena, String8 input, LNK_AltNameList *list_out);
internal String8 * lnk_parse_alt_name_directive_list(Arena *arena, String8List list, LNK_AltNameList *list_out);
////////////////////////////////
internal LNK_Config * lnk_config_from_raw_cmd_line(Arena *arena, String8List raw_cmd_line);
internal LNK_Config * lnk_build_config(Arena *arena, int argc, char **argv);
File diff suppressed because it is too large Load Diff
+603
View File
@@ -0,0 +1,603 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
////////////////////////////////
typedef struct LNK_PchInfo
{
CV_TypeIndex ti_lo;
CV_TypeIndex ti_hi;
U64 debug_p_obj_idx;
} LNK_PchInfo;
typedef struct LNK_CodeViewSymbolsInput
{
U64 obj_idx;
CV_SymbolList *symbol_list;
String8 raw_symbols;
} LNK_CodeViewSymbolsInput;
typedef struct LNK_CodeViewInput
{
U64 count;
U64 internal_count;
U64 external_count;
U64 type_server_count;
String8 *type_server_path_arr; // [type_server_count]
String8 *type_server_data_arr; // [type_server_count]
U64List *ts_to_obj_arr; // [type_server_count]
LNK_Obj *obj_arr; // [count]
LNK_PchInfo *pch_arr; // [count]
CV_DebugS *debug_s_arr; // [count]
CV_DebugT *debug_p_arr; // [count]
CV_DebugT *debug_t_arr; // [count]
CV_DebugT *merged_debug_t_p_arr; // [count]
U64 total_symbol_input_count;
LNK_CodeViewSymbolsInput *symbol_inputs; // [total_symbol_input_count]
CV_SymbolListArray *parsed_symbols; // [count]
LNK_Obj *internal_obj_arr; // [internal_count]
CV_DebugS *internal_debug_s_arr; // [internal_count]
CV_DebugT *internal_debug_t_arr; // [internal_count]
CV_DebugT *internal_debug_p_arr; // [internal_count]
U64 internal_total_symbol_input_count;
LNK_CodeViewSymbolsInput *internal_symbol_inputs; // [internal_total_symbol_input_count]
CV_SymbolListArray *internal_parsed_symbols; // [internal_count]
LNK_Obj *external_obj_arr; // [external_count]
CV_DebugS *external_debug_s_arr; // [external_count]
CV_DebugT *external_debug_t_arr; // [external_count]
CV_DebugT *external_debug_p_arr; // [external_count]
U64 external_total_symbol_input_count;
LNK_CodeViewSymbolsInput *external_symbol_inputs; // [exteranl_total_symbol_input_count]
CV_SymbolListArray *external_parsed_symbols; // [external_count]
Rng1U64 **external_ti_ranges; // [type_server_count]
CV_DebugT **external_leaves; // [type_server_count]
U64 *external_obj_to_ts_idx_arr; // [external_count]
Rng1U64 external_obj_range;
} LNK_CodeViewInput;
////////////////////////////////
typedef enum
{
LNK_LeafLocType_Internal,
LNK_LeafLocType_External,
LNK_LeafLocType_Count
} LNK_LeafLocType;
#define LNK_LeafRefFlag_LocIdxExternal (1 << 31)
#define LNK_LeafRefFlag_LeafIdxIPI (1 << 31)
typedef struct
{
U32 enc_loc_idx;
U32 enc_leaf_idx;
} LNK_LeafRef;
typedef struct LNK_LeafRange
{
struct LNK_LeafRange *next;
Rng1U64 range;
CV_DebugT *debug_t;
} LNK_LeafRange;
typedef struct LNK_LeafRangeList
{
U64 count;
LNK_LeafRange *first;
LNK_LeafRange *last;
} LNK_LeafRangeList;
typedef struct
{
LNK_LeafRef leaf_ref;
CV_TypeIndex type_index;
} LNK_LeafBucket;
typedef struct
{
U64 count;
LNK_LeafBucket **v;
} LNK_LeafBucketArray;
typedef struct
{
U64 cap;
LNK_LeafBucket **bucket_arr;
} LNK_LeafHashTable;
typedef union
{
struct {
U128Array **internal_hashes;
U128Array **external_hashes;
};
U128Array **v[CV_TypeIndexSource_COUNT];
} LNK_LeafHashes;
////////////////////////////////
typedef struct
{
LNK_Obj **obj_arr;
LNK_ChunkList *sect_list_arr;
CV_DebugS *debug_s_arr;
} LNK_ParseDebugSTaskData;
typedef struct
{
LNK_Obj **obj_arr;
String8Array *data_arr_arr;
} LNK_CheckDebugTSigTaskData;
typedef struct
{
LNK_Obj **obj_arr;
String8Array *data_arr_arr;
CV_DebugT *debug_t_arr;
} LNK_ParseDebugTTaskData;
typedef struct
{
String8 *path_arr;
String8 *msf_data_arr;
Rng1U64 **external_ti_ranges;
CV_DebugT **external_leaves;
B8 *is_corrupted;
} LNK_GetExternalLeavesTask;
////////////////////////////////
typedef struct
{
LNK_LeafRangeList *leaf_ranges_per_task;
U64 **count_arr_arr;
} LNK_CountPerSourceLeafTask;
typedef struct
{
LNK_CodeViewInput *input;
LNK_LeafHashes *hashes;
Arena **fixed_arenas;
CV_DebugT *debug_t_arr;
} LNK_LeafHasherTask;
typedef struct
{
LNK_CodeViewInput *input;
LNK_LeafHashes *hashes;
LNK_LeafHashTable *leaf_ht_arr;
CV_DebugT *debug_t_arr;
} LNK_LeafDedupInternal;
typedef struct
{
LNK_CodeViewInput *input;
LNK_LeafHashes *hashes;
LNK_LeafHashTable *leaf_ht_arr;
CV_TypeIndexSource dedup_ti_source;
} LNK_LeafDedupExternal;
typedef struct
{
LNK_LeafHashTable *ht;
U64 *count_arr;
Rng1U64 *range_arr;
U64 *offset_arr;
LNK_LeafBucketArray result;
} LNK_GetPresentBucketsTask;
typedef struct
{
U64 loc_idx_bit_count_0;
U64 loc_idx_bit_count_1;
U64 loc_idx_bit_count_2;
U64 counts_max;
U32 **counts_arr;
Rng1U64 *ranges;
LNK_LeafBucket **dst;
LNK_LeafBucket **src;
U64 loc_idx_max;
U64 pass_idx;
} LNK_LeafRadixSortTask;
typedef struct
{
U32 *counts;
U32 *offsets;
LNK_LeafBucket **dst;
LNK_LeafBucket **src;
Rng1U64 *ranges;
} LNK_LeafLocRadixSortTask;
typedef struct
{
Rng1U64 *range_arr;
CV_TypeIndex min_type_index;
LNK_LeafBucketArray bucket_arr;
} LNK_AssignTypeIndicesTask;
typedef struct
{
LNK_CodeViewInput *input;
LNK_LeafBucket **bucket_arr;
U8 **raw_leaf_arr;
Rng1U64 *range_arr;
} LNK_UnbucketRawLeavesTask;
typedef struct
{
LNK_CodeViewInput *input;
LNK_LeafHashes *hashes;
LNK_LeafHashTable *leaf_ht_arr;
CV_SymbolList *symbol_list_arr;
Arena **arena_arr;
} LNK_PatchSymbolTypesTask;
typedef struct
{
LNK_CodeViewInput *input;
LNK_LeafHashes *hashes;
LNK_LeafHashTable *leaf_ht_arr;
CV_DebugS *debug_s_arr;
} LNK_PatchInlinesTask;
typedef struct
{
LNK_CodeViewInput *input;
LNK_LeafHashes *hashes;
LNK_LeafHashTable *leaf_ht_arr;
LNK_LeafBucket **bucket_arr;
Rng1U64 *range_arr;
Arena **fixed_arena_arr;
} LNK_PatchLeavesTask;
////////////////////////////////
typedef struct
{
String8List *data_list_arr;
} LNK_ProcessedCodeViewC11Data;
typedef struct
{
String8List *data_list_arr;
String8List *source_file_names_list_arr;
} LNK_ProcessedCodeViewC13Data;
typedef struct
{
LNK_CodeViewSymbolsInput *inputs;
} LNK_ParseCVSymbolsTaskData;
typedef struct
{
U64 total_symbol_input_count;
LNK_CodeViewSymbolsInput *symbol_inputs;
CV_SymbolListArray *parsed_symbols;
PDB_DbiModule **mod_arr;
String8List *symbol_data_arr;
CV_SymbolList *gsi_list_arr;
} LNK_ProcessSymDataTaskData;
typedef struct
{
CV_DebugS *debug_s_arr;
MSF_Context *msf;
PDB_DbiModule **dbi_mod_arr;
String8List *c13_data_arr;
String8List *source_file_names_list_arr;
U64 string_data_base_offset;
CV_StringHashTable string_ht;
} LNK_ProcessC13DataTask;
typedef struct
{
MSF_Context *msf;
PDB_DbiModule **mod_arr;
String8List *symbol_data_arr;
String8List *c11_data_list_arr;
String8List *c13_data_list_arr;
String8List *globrefs_arr;
} LNK_WriteModuleDataTask;
typedef struct
{
LNK_Obj *obj_arr;
LNK_Section **sect_id_map;
PDB_DbiModule **mod_arr;
PDB_DbiSectionContribList *sc_list;
} LNK_PushDbiSecContribTaskData;
typedef struct
{
U32Array *hash_arr_arr;
CV_SymbolList *list_arr;
} LNK_HashCVSymbolListTask;
typedef struct
{
U64 *hash_arr;
CV_SymbolNode **arr;
Rng1U64 *range_arr;
} LNK_CvSymbolPtrArrayHasher;
typedef struct
{
LNK_Section **sect_id_map;
LNK_SymbolScopeIndex scope_idx;
LNK_SymbolList **bucket_arr;
CV_SymbolList *pub_list_arr;
Rng1U64 *symbol_ranges;
PDB_GsiContext *gsi;
CV_SymbolPtrArray symbols;
U32 *hashes;
} LNK_BuildPublicSymbolsTaskData;
typedef struct
{
CV_TypeIndex ipi_min_type_index;
CV_DebugT ipi_types;
LNK_CodeViewSymbolsInput *symbol_inputs;
CV_SymbolListArray *parsed_symbols;
} LNK_PostProcessCvSymbolsTask;
typedef struct
{
Rng1U64 *range_arr;
CV_SymbolPtrNode **bucket_arr;
CV_SymbolPtrNode **out_arr;
U64 *out_count_arr;
} LNK_GsiDeduper;
typedef struct
{
Rng1U64 *range_arr;
CV_SymbolPtrNode **bucket_arr;
U64 *symbol_base_arr;
CV_SymbolNode **symbol_arr;
} LNK_GsiUnbucket;
////////////////////////////////
// RAD Debug Info
typedef struct
{
String8 name;
U64 leaf_idx;
} LNK_UDTNameBucket;
typedef struct
{
CV_DebugT debug_t;
Rng1U64 *ranges;
U64 buckets_cap;
LNK_UDTNameBucket **buckets;
} LNK_BuildUDTNameHashTableTask;
typedef struct
{
CV_DebugT debug_t;
CV_TypeIndex ti_lo;
Rng1U64 *ranges;
U64 udt_name_buckets_cap;
LNK_UDTNameBucket **udt_name_buckets;
CV_TypeIndex *fwdmap;
} LNK_BuildUDTFwdMapTask;
////////////////////////////////
typedef struct
{
CV_DebugT *types;
U64 type_cap;
U64 udt_cap;
RDIB_TypeRef variadic_type_ref;
Rng1U64 *itype_ranges;
U64 udt_name_bucket_cap;
LNK_UDTNameBucket **udt_name_buckets;
RDIB_Type **tpi_itype_map;
RDIB_TypeChunkList *rdib_types_lists;
RDIB_TypeChunkList *rdib_types_struct_lists;
RDIB_TypeChunkList *rdib_types_union_lists;
RDIB_TypeChunkList *rdib_types_enum_lists;
RDIB_TypeChunkList *rdib_types_params_lists;
RDIB_TypeChunkList *rdib_types_udt_members_lists;
RDIB_TypeChunkList *rdib_types_enum_members_lists;
RDIB_UDTMemberChunkList *rdib_udt_members_lists;
RDIB_UDTMemberChunkList *rdib_enum_members_lists;
Rng1U64 *ranges;
} LNK_ConvertTypesToRDI;
typedef struct
{
U64 obj_idx;
RDIB_SourceFile *src_file;
} LNK_SourceFileBucket;
typedef struct
{
LNK_Obj *obj_arr;
CV_DebugS *debug_s_arr;
U64 total_src_file_count;
LNK_SourceFileBucket **src_file_buckets;
U64 src_file_buckets_cap;
} LNK_ConvertSourceFilesToRDITask;
typedef struct
{
CV_Arch arch;
CV_Language language;
String8 compiler_name;
} LNK_CodeViewCompilerInfo;
typedef struct
{
LNK_SectionArray image_sects;
LNK_Section **sect_id_map;
LNK_Obj *obj_arr;
CV_DebugS *debug_s_arr;
CV_DebugT ipi;
LNK_CodeViewSymbolsInput *symbol_inputs;
CV_SymbolListArray *parsed_symbols;
Rng1U64 ipi_itype_range;
Rng1U64 tpi_itype_range;
RDIB_Type **tpi_itype_map;
U64 src_file_buckets_cap;
LNK_SourceFileBucket **src_file_buckets;
LNK_UDTNameBucket **udt_name_buckets;
U64 line_table_cap;
U64 udt_name_buckets_cap;
U64 src_file_chunk_cap;
U64 symbol_chunk_cap;
U64 unit_chunk_cap;
U64 inline_site_cap;
RDIB_LineTable *null_line_table;
HashTable *extern_symbol_voff_ht;
LNK_CodeViewCompilerInfo *comp_info_arr;
CV_InlineeLinesAccel **inlinee_lines_accel_arr;
RDIB_InlineSiteChunk **inline_site_chunks;
// output
RDIB_UnitChunk *units;
RDIB_VariableChunkList *locals;
RDIB_ScopeChunkList *scopes;
RDIB_VariableChunkList *extern_gvars;
RDIB_VariableChunkList *static_gvars;
RDIB_VariableChunkList *extern_tvars;
RDIB_VariableChunkList *static_tvars;
RDIB_ProcedureChunkList *extern_procs;
RDIB_ProcedureChunkList *static_procs;
RDIB_InlineSiteChunkList *inline_sites;
RDIB_LineTableChunkList *line_tables;
} LNK_ConvertUnitToRDITask;
////////////////////////////////
// CodeView
internal CV_DebugS * lnk_parse_debug_s_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, LNK_ChunkList *sect_list_arr);
internal CV_DebugT * lnk_parse_debug_t_sections(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, LNK_ChunkList *debug_t_list_arr);
internal CV_SymbolList * lnk_cv_symbol_list_arr_from_debug_s_arr(TP_Context *tp, TP_Arena *arena, U64 obj_count, CV_DebugS *debug_s_arr);
internal LNK_PchInfo * lnk_setup_pch(Arena *arena, U64 obj_count, LNK_Obj *obj_arr, CV_DebugT *debug_t_arr, CV_DebugT *debug_p_arr, CV_SymbolListArray *parsed_symbols);
internal LNK_CodeViewInput lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, String8List lib_dir_list, LNK_ObjList obj_list);
internal LNK_LeafRef lnk_leaf_ref(U32 idx, U32 leaf_idx);
internal LNK_LeafRef lnk_obj_leaf_ref(U32 obj_idx, U32 leaf_idx);
internal LNK_LeafRef lnk_ts_leaf_ref(CV_TypeIndexSource ti_source, U32 ts_idx, U32 leaf_idx);
internal int lnk_leaf_ref_compare(LNK_LeafRef a, LNK_LeafRef b);
internal LNK_LeafLocType lnk_loc_type_from_leaf_ref(LNK_LeafRef leaf_ref);
internal LNK_LeafLocType lnk_loc_type_from_obj_idx(LNK_CodeViewInput *input, U64 obj_idx);
internal U64 lnk_loc_idx_from_obj_idx(LNK_CodeViewInput *input, U64 obj_idx);
internal CV_TypeIndex lnk_ti_lo_from_loc(LNK_CodeViewInput *input, LNK_LeafLocType loc_type, U64 loc_idx, CV_TypeIndexSource ti_source);
internal CV_TypeIndex lnk_ti_lo_from_leaf_ref(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref);
internal String8 lnk_data_from_leaf_ref(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref);
internal CV_Leaf lnk_cv_leaf_from_leaf_ref(LNK_CodeViewInput *input, LNK_LeafRef leaf_ref);
internal U128 lnk_hash_from_leaf_ref(LNK_LeafHashes *hashes, LNK_LeafRef leaf_ref);
internal LNK_LeafRef lnk_leaf_ref_from_loc_idx_and_ti(LNK_CodeViewInput *input, LNK_LeafLocType loc_type, CV_TypeIndexSource ti_source, U64 loc_idx, CV_TypeIndex obj_ti);
internal B32 lnk_match_leaf_ref(LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafRef a, LNK_LeafRef b);
internal B32 lnk_match_leaf_ref_deep(Arena *arena, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafRef a, LNK_LeafRef b);
internal U128 lnk_hash_cv_leaf(Arena *arena, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafLocType loc_type, U32 loc_idx, Rng1U64 *ti_ranges, CV_TypeIndex curr_ti, CV_Leaf leaf, CV_TypeIndexInfoList ti_info_list);
internal void lnk_hash_cv_leaf_deep(Arena *arena, LNK_CodeViewInput *input, Rng1U64 *ti_ranges, CV_DebugT *leaves, LNK_LeafHashes *hashes, LNK_LeafLocType loc_type, U32 loc_idx, CV_TypeIndexInfoList ti_info_list, String8 data);
internal LNK_LeafBucket * lnk_leaf_hash_table_insert_or_update(LNK_LeafHashTable *leaf_ht, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, U128 hash, LNK_LeafBucket *new_bucket);
internal LNK_LeafBucket * lnk_leaf_hash_table_search(LNK_LeafHashTable *ht, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafRef leaf_ref);
internal void lnk_cv_debug_t_count_leaves_per_source(TP_Context *tp, U64 count, CV_DebugT *debug_t_arr, U64 *per_source_count_arr);
internal void lnk_hash_debug_t_arr(TP_Context *tp, Arena *arena, U64 obj_count, CV_DebugT *debug_t_arr, U128Array *hash_arr_arr);
internal LNK_LeafBucketArray lnk_present_bucket_array_from_leaf_hash_table(TP_Context *tp, Arena *arena, LNK_LeafHashTable *ht);
internal void lnk_leaf_bucket_array_sort_radix_subset_parallel(TP_Context *tp, U64 bucket_count, U64 loc_idx_max, LNK_LeafBucket **dst, LNK_LeafBucket **src);
internal void lnk_leaf_bucket_array_sort_radix_parallel(TP_Context *tp, LNK_LeafBucketArray arr, U64 obj_count, U64 type_server_count);
internal void lnk_assign_type_indices(TP_Context *tp, LNK_LeafBucketArray bucket_arr, CV_TypeIndex min_type_index);
internal void lnk_patch_symbols(TP_Context *tp, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafHashTable *leaf_ht_arr);
internal void lnk_patch_inlines(TP_Context *tp, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafHashTable *leaf_ht_arr, U64 obj_count, CV_DebugS *debug_s_arr);
internal void lnk_patch_leaves(TP_Context *tp, LNK_CodeViewInput *input, LNK_LeafHashes *hashes, LNK_LeafHashTable *leaf_ht_arr, LNK_LeafBucketArray bucket_arr);
internal String8Node * lnk_copy_raw_leaf_arr_to_type_server(TP_Context *tp, CV_DebugT types, PDB_TypeServer *type_server);
internal CV_DebugT * lnk_import_types(TP_Context *tp, TP_Arena *tp_temp, LNK_CodeViewInput *input);
////////////////////////////////
// RAD Debug info
internal U64 lnk_udt_name_hash_table_hash(String8 string);
internal LNK_UDTNameBucket ** lnk_udt_name_hash_table_from_debug_t(TP_Context *tp, TP_Arena *arena, CV_DebugT debug_t, U64 *buckets_cap_out);
internal LNK_UDTNameBucket * lnk_udt_name_hash_table_lookup(LNK_UDTNameBucket **buckets, U64 cap, String8 name);
internal CV_TypeIndex * lnk_build_udt_fwdmap(TP_Context *tp, Arena *arena, CV_DebugT debug_t, CV_TypeIndex ti_lo, LNK_UDTNameBucket **udt_name_buckets, U64 udt_name_buckets_cap);
internal RDIB_TypeRef lnk_rdib_type_from_itype(LNK_ConvertTypesToRDI *task, CV_TypeIndex itype);
internal RDI_MemberKind lnk_rdib_method_kind_from_cv_prop(CV_MethodProp prop);
internal LNK_SourceFileBucket * lnk_src_file_hash_table_hash(String8 file_path, CV_C13ChecksumKind checksum_kind, String8 checksum_bytes);
internal LNK_SourceFileBucket * lnk_src_file_hash_table_lookup_slot(LNK_SourceFileBucket **src_file_buckets, U64 src_file_buckets_cap, U64 hash, String8 file_path, CV_C13ChecksumKind checksum_kind, String8 checksum_bytes);
internal String8List lnk_build_rad_debug_info(TP_Context *tp,
TP_Arena *tp_arena,
OperatingSystem os,
RDI_Arch arch,
String8 image_name,
String8 image_data,
LNK_SectionArray image_sects,
LNK_Section **sect_id_map,
U64 obj_count,
LNK_Obj *obj_arr,
CV_DebugS *debug_s_arr,
U64 total_symbol_input_count,
LNK_CodeViewSymbolsInput *symbol_inputs,
CV_SymbolListArray *parsed_symbols,
CV_DebugT types[CV_TypeIndexSource_COUNT]);
////////////////////////////////
// PDB
internal LNK_ProcessedCodeViewC11Data lnk_process_c11_data(TP_Context *tp, TP_Arena *arena, U64 obj_count, CV_DebugS *debug_s_arr, U64 string_data_base_offset, CV_StringHashTable string_ht, MSF_Context *msf, PDB_DbiModule **mod_arr);
internal LNK_ProcessedCodeViewC13Data lnk_process_c13_data(TP_Context *tp, TP_Arena *arena, U64 obj_count, CV_DebugS *debug_s_arr, U64 string_data_base_offset, CV_StringHashTable string_ht, MSF_Context *msf, PDB_DbiModule **mod_arr);
internal U64 * lnk_hash_cv_symbol_ptr_arr(TP_Context *tp, Arena *arena, CV_SymbolPtrArray arr);
internal CV_SymbolPtrArray lnk_dedup_gsi_symbols(TP_Context *tp, Arena *arena, PDB_GsiContext *gsi, U64 obj_count, CV_SymbolList *symbol_list_arr);
internal void lnk_build_pdb_public_symbols(TP_Context *tp,
TP_Arena *arena,
LNK_SymbolTable *symtab,
LNK_Section **sect_id_map,
PDB_PsiContext *psi,
LNK_SymbolScopeIndex scope_idx);
internal String8List lnk_build_pdb(TP_Context *tp,
TP_Arena *tp_arena,
OS_Guid guid,
COFF_MachineType machine,
COFF_TimeStamp time_stamp,
U32 age,
U64 page_size,
String8 pdb_name,
String8List lib_dir_list,
String8List natvis_list,
LNK_SymbolTable *symtab,
LNK_Section **sect_id_map,
U64 obj_count,
LNK_Obj *obj_arr,
CV_DebugS *debug_s_arr,
U64 total_symbol_input_count,
LNK_CodeViewSymbolsInput *symbol_inputs,
CV_SymbolListArray *parsed_symbols,
CV_DebugT types[CV_TypeIndexSource_COUNT]);
////////////////////////////////
// RAD Debug Info
internal U64 lnk_udt_name_hash_table_hash(String8 string);
internal LNK_UDTNameBucket ** lnk_udt_name_hash_table_from_debug_t(TP_Context *tp, TP_Arena *arena, CV_DebugT debug_t, U64 *buckets_cap_out);
internal LNK_UDTNameBucket * lnk_udt_name_hash_table_lookup(LNK_UDTNameBucket **buckets, U64 cap, String8 name);
internal CV_TypeIndex * lnk_build_udt_fwdmap(TP_Context *tp,
Arena *arena,
CV_DebugT debug_t,
CV_TypeIndex ti_lo,
LNK_UDTNameBucket **udt_name_buckets,
U64 udt_name_buckets_cap);
internal void lnk_init_rdib_itype_map(Arena *arena, RDI_Arch arch, RDIB_Type **itype_map, RDIB_TypeChunkList *rdib_types_list);
internal RDIB_TypeRef lnk_rdib_type_from_itype(LNK_ConvertTypesToRDI *task, CV_TypeIndex itype);
internal RDI_MemberKind lnk_rdib_method_kind_from_cv_prop(CV_MethodProp prop);
+185
View File
@@ -0,0 +1,185 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal void
lnk_alt_name_list_concat_in_place(LNK_AltNameList *list, LNK_AltNameList *to_concat)
{
str8_list_concat_in_place(&list->from_list, &to_concat->from_list);
str8_list_concat_in_place(&list->to_list, &to_concat->to_list);
}
internal LNK_MergeDirectiveNode *
lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective data)
{
LNK_MergeDirectiveNode *node = push_array_no_zero(arena, LNK_MergeDirectiveNode, 1);
node->data = data;
node->next = 0;
SLLQueuePush(list->first, list->last, node);
++list->count;
return node;
}
////////////////////////////////
internal void
lnk_parse_directives(Arena *arena, LNK_DirectiveInfo *directive_info, String8 buffer, String8 obj_path)
{
Temp scratch = scratch_begin(&arena, 1);
String8 unparsed_directives = buffer;
{
static const U8 BOM_SIG[] = { 0xEF, 0xBB, 0xBF };
B32 is_bom = MemoryMatch(buffer.str, &BOM_SIG[0], sizeof(BOM_SIG));
if (is_bom) {
unparsed_directives = str8_zero();
lnk_not_implemented("TODO: support for BOM encoding");
}
static const U8 ASCII_SIG[] = { 0x20, 0x20, 0x20 };
B32 is_ascii = MemoryMatch(buffer.str, &ASCII_SIG[0], sizeof(ASCII_SIG));
if (is_ascii) {
unparsed_directives = str8_skip(buffer, sizeof(ASCII_SIG));
}
}
String8List arg_list = lnk_arg_list_parse_windows_rules(scratch.arena, unparsed_directives);
LNK_CmdLine cmd_line = lnk_cmd_line_parse_windows_rules(scratch.arena, arg_list);
for (LNK_CmdOption *opt = cmd_line.first_option; opt != 0; opt = opt->next) {
static struct {
LNK_DirectiveKind kind;
String8 name;
} directive_table[LNK_Directive_Count] = {
{ LNK_Directive_Null, str8_lit_comp("") },
{ LNK_Directive_DefaultLib, str8_lit_comp("defaultlib") },
{ LNK_Directive_Export, str8_lit_comp("export" ) },
{ LNK_Directive_Include, str8_lit_comp("include") },
{ LNK_Directive_ManifestDependency, str8_lit_comp("manifestdependency") },
{ LNK_Directive_Merge, str8_lit_comp("merge") },
{ LNK_Directive_Section, str8_lit_comp("section") },
{ LNK_Directive_AlternateName, str8_lit_comp("alternatename") },
{ LNK_Directive_GuardSym, str8_lit_comp("guardsym") },
{ LNK_Directive_DisallowLib, str8_lit_comp("disallowlib") },
{ LNK_Directive_FailIfMismatch, str8_lit_comp("failifmismatch") },
{ LNK_Directive_EditAndContinue, str8_lit_comp("editandcontinue") },
{ LNK_Directive_ThrowingNew, str8_lit_comp("throwingnew") },
};
LNK_DirectiveKind kind = LNK_Directive_Null;
for (U64 i = 0; i < ArrayCount(directive_table); ++i) {
if (str8_match(directive_table[i].name, opt->string, StringMatchFlag_CaseInsensitive)) {
kind = directive_table[i].kind;
break;
}
}
if (kind == LNK_Directive_Null) {
lnk_error(LNK_Warning_UnknownDirective, "%S: unknown directive \"%S\"", obj_path, opt->string);
}
LNK_Directive *directive = push_array_no_zero(arena, LNK_Directive, 1);
directive->next = 0;
directive->id = push_str8_copy(arena, opt->string);
directive->value_list = str8_list_copy(arena, &opt->value_strings);
LNK_DirectiveList *directive_list = &directive_info->v[kind];
SLLQueuePush(directive_list->first, directive_list->last, directive);
++directive_list->count;
}
scratch_end(scratch);
}
internal String8List
lnk_parse_default_lib_directive(Arena *arena, LNK_DirectiveList *dir_list)
{
ProfBeginFunction();
String8List default_libs = {0};
for (LNK_Directive *dir = dir_list->first; dir != 0; dir = dir->next) {
for (String8Node *i = dir->value_list.first; i != 0; i = i->next) {
String8 lib_path = i->string;
// is there lib extension?
String8 ext = str8_skip_last_dot(lib_path);
if (ext.size == lib_path.size) { // TODO: fix string_extension_from_path, if there is no extension it should return zero
lib_path = push_str8f(arena, "%S.lib", lib_path);
} else {
lib_path = push_str8_copy(arena, lib_path);
}
str8_list_push(arena, &default_libs, lib_path);
}
}
ProfEnd();
return default_libs;
}
internal LNK_ExportParse *
lnk_parse_export_direcive(Arena *arena, LNK_ExportParseList *list, String8List value_list, LNK_Obj *obj)
{
ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 1);
LNK_ExportParse *parse = 0;
// parse directive
String8 name = str8(0,0);
String8 alias = str8(0,0);
String8 type = coff_string_from_import_header_type(COFF_ImportHeaderType_CODE);
if (value_list.node_count > 0) {
String8List dir_split = str8_split_by_string_chars(scratch.arena, value_list.first->string, str8_lit("="), 0);
B32 is_export_valid = value_list.node_count <= 2 && value_list.node_count > 0;
if (is_export_valid) {
if (dir_split.node_count > 0) {
name = dir_split.last->string;
}
if (dir_split.node_count == 2) {
alias = dir_split.first->string;
}
if (value_list.node_count == 2) {
type = value_list.last->string;
}
}
}
// prase error check
if (name.size == 0) {
String8 dir = str8_list_join(scratch.arena, &value_list, 0);
lnk_error_obj(LNK_Error_IllData, obj, "invalid export directive \"%S\"", dir);
goto exit;
}
parse = push_array_no_zero(arena, LNK_ExportParse, 1);
parse->next = 0;
parse->name = name;
parse->alias = alias;
parse->type = type;
SLLQueuePush(list->first, list->last, parse);
++list->count;
exit:;
scratch_end(scratch);
ProfEnd();
return parse;
}
internal B32
lnk_parse_merge_directive(String8 string, LNK_MergeDirective *out)
{
Temp scratch = scratch_begin(0, 0);
B32 is_parse_ok = 0;
String8List list = str8_split_by_string_chars(scratch.arena, string, str8_lit("="), 0);
if (list.node_count == 2) {
out->src = list.first->string;
out->dst = list.last->string;
is_parse_ok = 1;
}
scratch_end(scratch);
return is_parse_ok;
}
+89
View File
@@ -0,0 +1,89 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef struct LNK_Directive
{
struct LNK_Directive *next;
String8 id;
String8List value_list;
} LNK_Directive;
typedef struct LNK_DirectiveList
{
U64 count;
LNK_Directive *first;
LNK_Directive *last;
} LNK_DirectiveList;
typedef struct LNK_ExportParse
{
struct LNK_ExportParse *next;
String8 name;
String8 alias;
String8 type;
} LNK_ExportParse;
typedef struct LNK_ExportParseList
{
U64 count;
LNK_ExportParse *first;
LNK_ExportParse *last;
} LNK_ExportParseList;
typedef struct LNK_MergeDirective
{
String8 src;
String8 dst;
} LNK_MergeDirective;
typedef struct LNK_MergeDirectiveNode
{
struct LNK_MergeDirectiveNode *next;
LNK_MergeDirective data;
} LNK_MergeDirectiveNode;
typedef struct LNK_MergeDirectiveList
{
U64 count;
LNK_MergeDirectiveNode *first;
LNK_MergeDirectiveNode *last;
} LNK_MergeDirectiveList;
typedef enum
{
LNK_Directive_Null,
LNK_Directive_DefaultLib,
LNK_Directive_Export,
LNK_Directive_Include,
LNK_Directive_ManifestDependency,
LNK_Directive_Merge,
LNK_Directive_Section,
LNK_Directive_AlternateName,
LNK_Directive_GuardSym,
LNK_Directive_DisallowLib,
LNK_Directive_FailIfMismatch,
LNK_Directive_EditAndContinue,
LNK_Directive_ThrowingNew,
LNK_Directive_Count
} LNK_DirectiveKind;
typedef struct LNK_DirectiveInfo
{
LNK_DirectiveList v[LNK_Directive_Count];
} LNK_DirectiveInfo;
////////////////////////////////
internal void lnk_alt_name_list_concat_in_place(LNK_AltNameList *list, LNK_AltNameList *to_concat);
internal LNK_MergeDirectiveNode * lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective data);
////////////////////////////////
internal void lnk_parse_directives(Arena *arena, LNK_DirectiveInfo *directive_info, String8 buffer, String8 obj_path);
internal String8List lnk_parse_default_lib_directive(Arena *arena, LNK_DirectiveList *dir_list);
internal B32 lnk_parse_merge_directive(String8 directive, LNK_MergeDirective *out);
+121
View File
@@ -0,0 +1,121 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
static LNK_ErrorMode g_error_mode_arr[LNK_Error_Count];
static LNK_ErrorCodeStatus g_error_code_status_arr[LNK_Error_Count];
static B32 g_log_status[LNK_Log_Count];
internal void
lnk_exit(int code)
{
exit(code);
}
internal void
lnk_init_error_handler(void)
{
for (int i = LNK_Error_StopFirst; i < LNK_Error_StopLast; ++i) {
g_error_mode_arr[i] = LNK_ErrorMode_Stop;
}
for (int i = LNK_Error_First; i < LNK_Error_Last; ++i) {
g_error_mode_arr[i] = LNK_ErrorMode_Continue;
}
for (int i = LNK_Warning_First; i < LNK_Warning_Last; ++i) {
g_error_mode_arr[i] = LNK_ErrorMode_Warn;
}
}
internal String8
lnk_string_from_error_mode(LNK_ErrorMode mode)
{
switch (mode) {
case LNK_ErrorMode_Ignore: return str8_lit("Ignore");
case LNK_ErrorMode_Continue: return str8_lit("Error");
case LNK_ErrorMode_Stop: return str8_lit("Error");
case LNK_ErrorMode_Warn: return str8_lit("Warning");
}
return str8_zero();
}
internal void
lnk_errorfv(LNK_ErrorCode code, char *fmt, va_list args)
{
if (g_error_mode_arr[code] == LNK_ErrorMode_Ignore) {
return;
}
if (lnk_is_error_code_ignored(code)) {
return;
}
Temp scratch = scratch_begin(0,0);
String8 message = push_str8fv(scratch.arena, fmt, args);
String8 string = push_str8f(scratch.arena, "%S(%03d): %S\n", lnk_string_from_error_mode(g_error_mode_arr[code]), code, message);
fprintf(stderr, "%.*s", str8_varg(string));
scratch_end(scratch);
if (g_error_mode_arr[code] == LNK_ErrorMode_Stop) {
lnk_exit(code);
}
}
internal void
lnk_error(LNK_ErrorCode code, char *fmt, ...)
{
va_list args;
va_start(args, fmt);
lnk_errorfv(code, fmt, args);
va_end(args);
}
internal void
lnk_supplement_error(char *fmt, ...)
{
va_list args;
va_start(args, fmt);
Temp scratch = scratch_begin(0,0);
String8 string = push_str8fv(scratch.arena, fmt, args);
fprintf(stderr, "\t");
fprintf(stderr, "%.*s", str8_varg(string));
fprintf(stderr, "\n");
va_end(args);
scratch_end(scratch);
}
internal void
lnk_supplement_error_list(String8List list)
{
for (String8Node *node = list.first; node != 0; node = node->next) {
lnk_supplement_error("%.*s", str8_varg(node->string));
}
}
internal void
lnk_suppress_error(LNK_ErrorCode code)
{
g_error_code_status_arr[code] = LNK_ErrorCodeStatus_Ignore;
}
internal LNK_ErrorCodeStatus
lnk_get_error_code_status(LNK_ErrorCode code)
{
return g_error_code_status_arr[code];
}
internal void
lnk_internal_error(LNK_InternalError code, char *file, int line, char *fmt, ...)
{
Temp scratch = scratch_begin(0,0);
va_list args;
va_start(args, fmt);
String8 issue = push_str8fv(scratch.arena, fmt, args);
fprintf(stderr, "internal error #%03d in %s:%u\n", code, file, line);
fprintf(stderr, "\t%.*s\n", str8_varg(issue));
va_end(args);
scratch_end(scratch);
}
+120
View File
@@ -0,0 +1,120 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef enum
{
LNK_Error_Null,
LNK_Error_StopFirst,
LNK_Error_Cmdl,
LNK_Error_EndprecompNotFound,
LNK_Error_EntryPoint,
LNK_Error_ExternalTypeServerConflict,
LNK_Error_FileNotFound,
LNK_Error_IllData,
LNK_Error_IllExport,
LNK_Error_IncomatibleCmdOptions,
LNK_Error_IncompatibleObj,
LNK_Error_InvalidPrecompLeafCount,
LNK_Error_InvalidStartIndex,
LNK_Error_NoAccess,
LNK_Error_NoSubsystem,
LNK_Error_OutOfExportOrdinals,
LNK_Error_PrecompObjNotFound,
LNK_Error_PrecompSigMismatch,
LNK_Error_Telemetry,
LNK_Error_UnsupportedMachine,
LNK_Error_Mt,
LNK_Error_UnableToSerializeMsf,
LNK_Error_StopLast,
LNK_Error_First,
LNK_Error_InvalidPath,
LNK_Error_AlreadyDefinedSymbol,
LNK_Error_AlternateNameConflict,
LNK_Error_CvPrecomp,
LNK_Error_MultiplyDefinedSymbol,
LNK_Error_Natvis,
LNK_Error_TooManyFiles,
LNK_Error_UndefinedSymbol,
LNK_Error_UnresolvedSymbol,
LNK_Error_UnableToOpenTypeServer,
LNK_Error_UnexpectedCodePath,
LNK_Error_CvIllSymbolData,
LNK_Error_IllegalAlternateNameRedifine,
LNK_Error_InvalidTypeIndex,
LNK_Error_Last,
LNK_Warning_First,
LNK_Warning_AmbiguousMerge,
LNK_Warning_AtypicalStartIndex,
LNK_Warning_Cmdl,
LNK_Warning_Directive,
LNK_Warning_DuplicateObjPath,
LNK_Warning_ExternalTypeServerAgeMismatch,
LNK_Warning_FileNotFound,
LNK_Warning_IllData,
LNK_Warning_IllExport,
LNK_Warning_InvalidNatvisFileExt,
LNK_Warning_LargePages,
LNK_Warning_LargePagesNotEnabled,
LNK_Warning_MissingExternalTypeServer,
LNK_Warning_MultipleDebugTAndDebugP,
LNK_Warning_MultipleExternalTypeServers,
LNK_Warning_MultipleLibMatch,
LNK_Warning_MultiplyDefinedImport,
LNK_Warning_Natvis,
LNK_Warning_PrecompObjSymbolsNotFound,
LNK_Warning_SectionFlagsConflict,
LNK_Warning_Subsystem,
LNK_Warning_UnknownDirective,
LNK_Warning_UnresolvedComdat,
LNK_Warning_UnusedDelayLoadDll,
LNK_Warning_LongSectionName,
LNK_Warning_UnknownSwitch,
LNK_Warning_Last,
LNK_Error_Count
} LNK_ErrorCode;
typedef enum
{
LNK_ErrorMode_Ignore,
LNK_ErrorMode_Stop,
LNK_ErrorMode_Continue,
LNK_ErrorMode_Warn,
} LNK_ErrorMode;
typedef enum
{
LNK_InternalError_Null,
LNK_InternalError_NotImplemented,
LNK_InternalError_InvalidPath,
LNK_InternalError_IncompleteSwitch,
LNK_InternalError_OutOfMemory
} LNK_InternalError;
typedef enum
{
LNK_ErrorCodeStatus_Active,
LNK_ErrorCodeStatus_Ignore,
} LNK_ErrorCodeStatus;
internal void lnk_init_error_handler(void);
internal void lnk_errorfv(LNK_ErrorCode code, char *fmt, va_list args);
internal void lnk_error(LNK_ErrorCode code, char *fmt, ...);
internal void lnk_supplement_error(char *fmt, ...);
internal void lnk_supplement_error_list(String8List list);
internal void lnk_suppress_error(LNK_ErrorCode code);
#define lnk_is_error_code_active(code) (lnk_get_error_code_status(code) == LNK_ErrorCodeStatus_Active)
#define lnk_is_error_code_ignored(code) (lnk_get_error_code_status(code) == LNK_ErrorCodeStatus_Ignore)
internal LNK_ErrorCodeStatus lnk_get_error_code_status(LNK_ErrorCode code);
internal void lnk_internal_error(LNK_InternalError code, char *file, int line, char *fmt, ...);
#define lnk_invalid_path(fmt, ...) lnk_internal_error(LNK_InternalError_InvalidPath, __FILE__, __LINE__, fmt, __VA_ARGS__)
#define lnk_not_implemented(fmt, ...) lnk_internal_error(LNK_InternalError_NotImplemented, __FILE__, __LINE__, fmt, __VA_ARGS__)
#define lnk_incomplete_switch(fmt, ...) lnk_internal_error(LNK_InternalError_IncompleteSwitch, __FILE__, __LINE__, fmt, __VA_ARGS__)
+302
View File
@@ -0,0 +1,302 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
int
lnk_export_name_compar(const void *a_, const void *b_)
{
const LNK_Export *a = (const LNK_Export *)a_;
const LNK_Export *b = (const LNK_Export *)b_;
return str8_compar_case_sensetive(&a->name, &b->name);
}
int
lnk_export_ordinal_compar(const void *a_, const void *b_)
{
const LNK_Export *a = (const LNK_Export *)a_;
const LNK_Export *b = (const LNK_Export *)b_;
int cmp = u16_compar(&a->ordinal, &b->ordinal);
return cmp;
}
internal LNK_ExportTable *
lnk_export_table_alloc(void)
{
ProfBeginFunction();
Arena *arena = arena_alloc();
LNK_ExportTable *exptab = push_array(arena, LNK_ExportTable, 1);
exptab->arena = arena;
exptab->voff_size = sizeof(U32);
exptab->max_ordinal = max_U16;
exptab->is_ordinal_used = push_array(arena, B8, exptab->max_ordinal);
ProfEnd();
return exptab;
}
internal void
lnk_export_table_release(LNK_ExportTable **exptab_ptr)
{
ProfBeginFunction();
arena_release((*exptab_ptr)->arena);
*exptab_ptr = NULL;
ProfEnd();
}
internal LNK_Export *
lnk_export_table_search(LNK_ExportTable *exptab, String8 name)
{
for (LNK_Export *exp = exptab->name_export_list.first; exp != NULL; exp = exp->next) {
if (str8_match(exp->name, name, 0)) {
return exp;
}
}
return NULL;
}
internal LNK_Export *
lnk_export_table_push_export(LNK_ExportTable *exptab, LNK_SymbolTable *symtab, LNK_ExportParse *exp_parse)
{
LNK_Export *exp = 0;
// get export symbol
LNK_Symbol *symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Main, exp_parse->name);
if (symbol == 0) {
lnk_error(LNK_Warning_IllExport, "symbol \"%S\" for export doesn't exist", exp_parse->name);
goto exit;
}
symbol = lnk_resolve_symbol(symtab, symbol);
if (!LNK_Symbol_IsDefined(symbol->type)) {
lnk_error(LNK_Warning_IllExport, "unable to resolve symbol \"%S\" for export", exp_parse->name);
goto exit;
}
LNK_DefinedSymbol *def = &symbol->u.defined;
// NOTE: It is possible to export a global variable as CODE
// with following snippet:
// int global_bar = 0;
// #pragma comment(linker, "/export:global_bar")
// for some reason MSVC and LLD don't check symbol type and default
// to CODE instead of DATA. But if you try export global variable with:
// #pragma comment(linker, "/export:global_bar,CODE")
// MSVC and LLD issue an error. For compatibility sake we do the same thing too.
COFF_ImportHeaderType type = coff_import_header_type_from_string(exp_parse->type);
switch (type) {
case COFF_ImportHeaderType_CODE: {
B32 is_export_data = !(def->flags & (LNK_DefinedSymbolFlag_IsFunc|LNK_DefinedSymbolFlag_IsThunk));
if (is_export_data) {
lnk_error(LNK_Error_IllExport, "export \"%S\" is DATA but has specifier CODE", exp_parse->name);
}
} break;
case COFF_ImportHeaderType_DATA: {
B32 is_export_code = !!(def->flags & (LNK_DefinedSymbolFlag_IsFunc|LNK_DefinedSymbolFlag_IsThunk));
if (is_export_code) {
lnk_error(LNK_Error_IllExport, "export \"%S\" is CODE but has specifier DATA", exp_parse->name);
}
} break;
case COFF_ImportHeaderType_CONST: {
lnk_not_implemented("TODO: COFF_ImportHeaderType_CONST");
} break;
default: {
if (exp_parse->type.size) {
lnk_error(LNK_Error_IllExport, "invalid type \"%S\" for export \"%S\"", exp_parse->type, exp_parse->name);
}
} break;
}
// error check multiple def
exp = lnk_export_table_search(exptab, exp_parse->alias);
if (exp) {
if (exp->type != type) {
lnk_error(LNK_Warning_IllExport, "trying to rexport symbol \"%S\"", exp_parse->alias);
}
goto exit;
}
exp = lnk_export_table_search(exptab, exp_parse->name);
if (exp) {
if (exp->type != type) {
lnk_error(LNK_Warning_IllExport, "multiple export definition for \"%S\"", exp_parse->name);
}
goto exit;
}
// find free ordinal
U16 ordinal;
for (ordinal = 0; ordinal < exptab->max_ordinal; ++ordinal) {
if (!exptab->is_ordinal_used[ordinal]) {
exptab->is_ordinal_used[ordinal] = 1;
break;
}
}
// ordinal alloc error check
if (ordinal >= exptab->max_ordinal) {
lnk_error(LNK_Error_OutOfExportOrdinals, "reached export limit of %u, discarding export %S", exptab->max_ordinal, exp_parse->name);
goto exit;
}
// fill out export
exp = push_array_no_zero(exptab->arena, LNK_Export, 1);
exp->next = 0;
exp->name = push_str8_copy(exptab->arena, exp_parse->alias.size > 0 ? exp_parse->alias : exp_parse->name);
exp->symbol = symbol;
exp->id = exptab->name_export_list.count;
exp->ordinal = ordinal;
exp->type = type;
exp->is_private = 0; // exports through directives are public
// push node
SLLQueuePush(exptab->name_export_list.first, exptab->name_export_list.last, exp);
exptab->name_export_list.count += 1;
exit:;
return exp;
}
internal LNK_ExportArray
lnk_export_array_from_list(Arena *arena, LNK_ExportList list)
{
ProfBeginFunction();
LNK_ExportArray arr;
arr.count = 0;
arr.v = push_array_no_zero(arena, LNK_Export, list.count);
for (LNK_Export *exp = list.first; exp != NULL; exp = exp->next) {
arr.v[arr.count++] = *exp;
}
ProfEnd();
return arr;
}
internal void
lnk_build_edata(LNK_ExportTable *exptab, LNK_SectionTable *st, LNK_SymbolTable *symtab, String8 image_name, COFF_MachineType machine)
{
ProfBeginFunction();
Temp scratch = scratch_begin(0, 0);
// is export table empty?
if (exptab->name_export_list.count == 0 && exptab->noname_export_list.count == 0) {
goto exit;
}
// compute ordinal bounds
U64 ordinal_low;
for (ordinal_low = 0; ordinal_low < exptab->max_ordinal; ++ordinal_low) {
if (exptab->is_ordinal_used[ordinal_low]) {
break;
}
}
U64 ordinal_high;
for (ordinal_high = exptab->max_ordinal - 1; ordinal_high > 0; --ordinal_high) {
if (exptab->is_ordinal_used[ordinal_high]) {
break;
}
}
LNK_Section *edata = lnk_section_table_search(st, str8_lit(".edata"));
// push header
PE_ExportTable *header = push_array(edata->arena, PE_ExportTable, 1);
header->ordinal_base = safe_cast_u16(ordinal_low + 1);
header->export_address_table_count = safe_cast_u32(exptab->name_export_list.count + exptab->noname_export_list.count);
header->name_pointer_table_count = safe_cast_u32(exptab->name_export_list.count);
String8 header_data = str8((U8*)header, sizeof(*header));
String8 image_name_cstr = push_cstr(edata->arena, str8_skip_last_slash(image_name));
// push edata chunks
LNK_Chunk *header_chunk = lnk_section_push_chunk_data(edata, edata->root, header_data, str8_lit("a"));
LNK_Chunk *voff_table_chunk = lnk_section_push_chunk_list(edata, edata->root, str8_lit("b"));
LNK_Chunk *name_voff_table_chunk = lnk_section_push_chunk_list(edata, edata->root, str8_lit("c"));
LNK_Chunk *ordinal_table_chunk = lnk_section_push_chunk_list(edata, edata->root, str8_lit("d"));
LNK_Chunk *string_buffer_chunk = lnk_section_push_chunk_list(edata, edata->root, str8_lit("e"));
LNK_Chunk *image_name_chunk = lnk_section_push_chunk_data(edata, string_buffer_chunk, image_name_cstr, str8(0,0));
lnk_chunk_set_debugf(edata->arena, header_chunk, "EXPORT_HEADER");
lnk_chunk_set_debugf(edata->arena, voff_table_chunk, "EXPORT_ADDRESS_TABLE");
lnk_chunk_set_debugf(edata->arena, name_voff_table_chunk, "EXPORT_NAME_VOFF_TABLE");
lnk_chunk_set_debugf(edata->arena, ordinal_table_chunk, "EXPORT_ORDINAL_TABLE");
lnk_chunk_set_debugf(edata->arena, string_buffer_chunk, "EXPORT_STRING_BUFFER");
lnk_chunk_set_debugf(edata->arena, image_name_chunk, "EXPORT_IMAGE_NAME");
LNK_Symbol *image_name_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_lit("export_table.name_voff"), LNK_DefinedSymbolVisibility_Internal, 0, image_name_chunk, 0, 0, 0);
LNK_Symbol *address_table_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_lit("export_table.export_address_table_voff"), LNK_DefinedSymbolVisibility_Internal, 0, voff_table_chunk, 0, 0, 0);
LNK_Symbol *name_table_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_lit("export_table.name_pointer_table_voff"), LNK_DefinedSymbolVisibility_Internal, 0, name_voff_table_chunk, 0, 0, 0);
LNK_Symbol *ordinal_table_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_lit("export_table.ordinal_table_voff"), LNK_DefinedSymbolVisibility_Internal, 0, ordinal_table_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, image_name_symbol);
lnk_symbol_table_push(symtab, address_table_symbol);
lnk_symbol_table_push(symtab, name_table_symbol);
lnk_symbol_table_push(symtab, ordinal_table_symbol);
// patch header fields
lnk_section_push_reloc(edata, header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ExportTable, name_voff), image_name_symbol);
lnk_section_push_reloc(edata, header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ExportTable, export_address_table_voff), address_table_symbol);
lnk_section_push_reloc(edata, header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ExportTable, name_pointer_table_voff), name_table_symbol);
lnk_section_push_reloc(edata, header_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ExportTable, ordinal_table_voff), ordinal_table_symbol);
// reserve virtual offset chunks
LNK_Chunk **ordinal_voff_map = push_array(scratch.arena, LNK_Chunk *, exptab->max_ordinal);
for (U32 i = ordinal_low; i <= ordinal_high; i += 1) {
String8 sort_index = str8_from_bits_u32(edata->arena, i);
LNK_Chunk *voff_chunk = lnk_section_push_chunk_bss(edata, voff_table_chunk, exptab->voff_size, sort_index);
ordinal_voff_map[i] = voff_chunk;
}
B8 *is_ordinal_bound = push_array(scratch.arena, B8, exptab->max_ordinal);
LNK_ExportList *exp_list_arr[] = { &exptab->name_export_list, &exptab->noname_export_list };
for (LNK_ExportList *list_ptr = exp_list_arr[0], *list_opl = list_ptr + ArrayCount(exp_list_arr);
list_ptr < list_opl;
list_ptr += 1) {
for (LNK_Export *exp = list_ptr->first; exp != 0; exp = exp->next) {
String8 name_cstr = push_cstr(edata->arena, exp->name);
// push name string
LNK_Chunk *name_chunk = lnk_section_push_chunk_data(edata, string_buffer_chunk, name_cstr, str8(0,0));
lnk_chunk_set_debugf(edata->arena, name_chunk, "export: %S", name_cstr);
// push name symbol
String8 name_export_name = push_str8f(symtab->arena, "export.%S", name_cstr);
LNK_Symbol *name_symbol = lnk_make_defined_symbol_chunk(symtab->arena, name_export_name, LNK_DefinedSymbolVisibility_Internal, 0, name_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, name_symbol);
// name voff
LNK_Chunk *voff_chunk = lnk_section_push_chunk_bss(edata, name_voff_table_chunk, exptab->voff_size, /* export table must be sorted lexically: */ name_cstr);
lnk_chunk_set_debugf(edata->arena, voff_chunk, "voff for export name %S", name_cstr);
// link reloc with name symbol
lnk_section_push_reloc(edata, voff_chunk, LNK_Reloc_VIRT_OFF_32, 0, name_symbol);
// make ordinal relative
U16 *ordinal_ptr = push_array(edata->arena, U16, 1);
*ordinal_ptr = (exp->ordinal - ordinal_low);
// ordinal
LNK_Chunk *ordinal_chunk = lnk_section_push_chunk_raw(edata, ordinal_table_chunk, ordinal_ptr, sizeof(*ordinal_ptr), /* ordinal table is parallel to the name table: */ name_cstr);
lnk_chunk_set_debugf(edata->arena, ordinal_chunk, "ordinal %u for %S", exp->ordinal, exp->name);
// (ordinal - ordinal_low) -> export virtual offset
if ( ! is_ordinal_bound[exp->ordinal]) {
is_ordinal_bound[exp->ordinal] = 1;
LNK_Chunk *export_func_voff_chunk = ordinal_voff_map[exp->ordinal];
lnk_section_push_reloc(edata, export_func_voff_chunk, LNK_Reloc_VIRT_OFF_32, 0, exp->symbol);
}
}
}
exit:;
scratch_end(scratch);
ProfEnd();
}
internal void
lnk_collect_exports_from_obj_directives(LNK_ExportTable *exptab, LNK_ObjList obj_list, LNK_SymbolTable *symtab)
{
ProfBeginFunction();
for (LNK_ObjNode *obj_node = obj_list.first; obj_node != 0; obj_node = obj_node->next) {
for (LNK_ExportParse *exp_parse = obj_node->data.export_parse.first; exp_parse != 0; exp_parse = exp_parse->next) {
lnk_export_table_push_export(exptab, symtab, exp_parse);
}
}
ProfEnd();
}
+46
View File
@@ -0,0 +1,46 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef struct LNK_Export
{
struct LNK_Export *next;
String8 name;
LNK_Symbol *symbol;
U32 id;
U16 ordinal;
COFF_ImportHeaderType type;
B32 is_private;
} LNK_Export;
typedef struct LNK_ExportList
{
U64 count;
LNK_Export *first;
LNK_Export *last;
} LNK_ExportList;
typedef struct LNK_ExportArray
{
U64 count;
LNK_Export *v;
} LNK_ExportArray;
typedef struct LNK_ExportTable
{
Arena *arena;
LNK_ExportList name_export_list;
LNK_ExportList noname_export_list;
U64 voff_size;
U64 max_ordinal;
B8 *is_ordinal_used;
} LNK_ExportTable;
internal LNK_ExportTable * lnk_export_table_alloc(void);
internal void lnk_export_table_release(LNK_ExportTable **exptab_ptr);
internal LNK_Export * lnk_export_table_search(LNK_ExportTable *exptab, String8 name);
internal void lnk_collect_exports_from_def_files(LNK_ExportTable *exptab, String8List path_list);
internal void lnk_build_edata(LNK_ExportTable *exptab, LNK_SectionTable *st, LNK_SymbolTable *symtab, String8 image_name, COFF_MachineType machine);
internal void lnk_collect_exports_from_obj_directives(LNK_ExportTable *exptab, LNK_ObjList obj_list, LNK_SymbolTable *symtab);
+802
View File
@@ -0,0 +1,802 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal LNK_ImportTable *
lnk_import_table_alloc_regular(LNK_SectionTable *st, LNK_SymbolTable *symtab, COFF_MachineType machine)
{
ProfBeginFunction();
LNK_Section *data_sect = lnk_section_table_push(st, str8_lit(".idata"), LNK_IDATA_SECTION_FLAGS);
LNK_Section *code_sect = lnk_section_table_search(st, str8_lit(".text"));
LNK_Chunk *dll_table_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *int_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *iat_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *ilt_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *code_chunk = lnk_section_push_chunk_list(code_sect, code_sect->root, str8_zero());
LNK_Chunk *null_dll_import = lnk_section_push_chunk_data(data_sect, dll_table_chunk, str8(0, sizeof(PE_ImportEntry)), str8_lit("zzzzz"));
lnk_chunk_set_debugf(data_sect->arena, null_dll_import, "DLL_DIRECTORY_TERMINATOR");
LNK_Symbol *dll_table_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_cstring(LNK_IMPORT_DLL_TABLE_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, dll_table_chunk, 0, 0, 0);
LNK_Symbol *int_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_cstring(LNK_IMPORT_NAME_TABLE_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, int_chunk , 0, 0, 0);
LNK_Symbol *iat_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_cstring(LNK_IMPORT_IAT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, iat_chunk , 0, 0, 0);
LNK_Symbol *ilt_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_cstring(LNK_IMPORT_ILT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, ilt_chunk , 0, 0, 0);
LNK_Symbol *code_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_cstring(LNK_IMPORT_JMP_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, code_chunk , 0, 0, 0);
lnk_symbol_table_push(symtab, dll_table_symbol);
lnk_symbol_table_push(symtab, int_symbol);
lnk_symbol_table_push(symtab, iat_symbol);
lnk_symbol_table_push(symtab, ilt_symbol);
lnk_symbol_table_push(symtab, code_symbol);
Arena *arena = arena_alloc();
LNK_ImportTable *imptab = push_array(arena, LNK_ImportTable, 1);
imptab->machine = machine;
imptab->arena = arena;
imptab->data_sect = data_sect;
imptab->code_sect = code_sect;
imptab->dll_table_chunk = dll_table_chunk;
imptab->int_chunk = int_chunk;
imptab->iat_chunk = iat_chunk;
imptab->ilt_chunk = ilt_chunk;
imptab->code_chunk = code_chunk;
imptab->dll_ht = hash_table_init(arena, LNK_IMPORT_DLL_HASH_TABLE_BUCKET_COUNT);
ProfEnd();
return imptab;
}
internal LNK_ImportTable *
lnk_import_table_alloc_delayed(LNK_SectionTable *st, LNK_SymbolTable *symtab, COFF_MachineType machine, B32 is_unloadable, B32 is_bindable)
{
ProfBeginFunction();
LNK_Section *data_sect = lnk_section_table_push(st, str8_lit(".didat"), LNK_DEBUG_DIR_SECTION_FLAGS);
LNK_Section *code_sect = lnk_section_table_search(st, str8_lit(".text"));
LNK_Chunk *dll_table_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *int_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *handle_table_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *iat_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *ilt_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *biat_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *uiat_chunk = lnk_section_push_chunk_list(data_sect, data_sect->root, str8_zero());
LNK_Chunk *code_chunk = lnk_section_push_chunk_list(code_sect, code_sect->root, str8_zero());
LNK_Chunk *null_dll_import = lnk_section_push_chunk_data(data_sect, dll_table_chunk, str8(0, sizeof(PE_DelayedImportEntry)), str8_lit("~0"));
lnk_chunk_set_debugf(data_sect->arena, null_dll_import, "DLL_DIRECTORY_TERMINATOR");
if (is_unloadable) {
U64 import_size = coff_word_size_from_machine(machine);
LNK_Chunk *null_uiat_chunk = lnk_section_push_chunk_bss(data_sect, uiat_chunk, import_size, str8_lit("~1"));
lnk_chunk_set_debugf(data_sect->arena, null_uiat_chunk, "UIAT_TERMINATOR");
}
if (is_bindable) {
U64 import_size = coff_word_size_from_machine(machine);
LNK_Chunk *null_biat_chunk = lnk_section_push_chunk_bss(data_sect, biat_chunk, import_size, str8_lit("~2"));
lnk_chunk_set_debugf(data_sect->arena, null_biat_chunk, "BIAT_TERMINATOR");
}
LNK_Symbol *dll_table_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_cstring(LNK_DELAYED_IMPORT_DLL_TABLE_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, dll_table_chunk , 0, 0, 0);
LNK_Symbol *int_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_cstring(LNK_DELAYED_IMPORT_INT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, int_chunk , 0, 0, 0);
LNK_Symbol *handle_table_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_cstring(LNK_DELAYED_IMPORT_HANDLE_TABLE_SYMBOL_NAME), LNK_DefinedSymbolVisibility_Internal, 0, handle_table_chunk, 0, 0, 0);
LNK_Symbol *iat_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_cstring(LNK_DELAYED_IMPORT_IAT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, iat_chunk , 0, 0, 0);
LNK_Symbol *ilt_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_cstring(LNK_DELAYED_IMPORT_ILT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, ilt_chunk , 0, 0, 0);
LNK_Symbol *biat_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_cstring(LNK_DELAYED_IMPORT_BIAT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, biat_chunk , 0, 0, 0);
LNK_Symbol *uiat_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_cstring(LNK_DELAYED_IMPORT_UIAT_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, uiat_chunk , 0, 0, 0);
LNK_Symbol *code_symbol = lnk_make_defined_symbol_chunk(symtab->arena, str8_cstring(LNK_DELAYED_IMPORT_CODE_SYMBOL_NAME) , LNK_DefinedSymbolVisibility_Internal, 0, code_chunk , 0, 0, 0);
lnk_symbol_table_push(symtab, dll_table_symbol);
lnk_symbol_table_push(symtab, int_symbol);
lnk_symbol_table_push(symtab, handle_table_symbol);
lnk_symbol_table_push(symtab, iat_symbol);
lnk_symbol_table_push(symtab, ilt_symbol);
lnk_symbol_table_push(symtab, biat_symbol);
lnk_symbol_table_push(symtab, uiat_symbol);
lnk_symbol_table_push(symtab, code_symbol);
LNK_ImportTableFlags flags = 0;
if (is_unloadable) {
flags |= LNK_ImportTableFlag_EmitUiat;
}
if (is_bindable) {
flags |= LNK_ImportTableFlag_EmitBiat;
}
Arena *arena = arena_alloc();
LNK_ImportTable *imptab = push_array(arena, LNK_ImportTable, 1);
imptab->arena = arena;
imptab->machine = machine;
imptab->data_sect = data_sect;
imptab->code_sect = code_sect;
imptab->dll_table_chunk = dll_table_chunk;
imptab->int_chunk = int_chunk;
imptab->handle_table_chunk = handle_table_chunk;
imptab->iat_chunk = iat_chunk;
imptab->ilt_chunk = ilt_chunk;
imptab->biat_chunk = biat_chunk;
imptab->uiat_chunk = uiat_chunk;
imptab->code_chunk = code_chunk;
imptab->flags = flags;
imptab->dll_ht = hash_table_init(arena, LNK_IMPORT_FUNC_HASH_TABLE_BUCKET_COUNT);
ProfEnd();
return imptab;
}
internal void
lnk_import_table_release(LNK_ImportTable **imptab_ptr)
{
ProfBeginFunction();
arena_release((*imptab_ptr)->arena);
*imptab_ptr = 0;
ProfEnd();
}
internal BucketNode *
lnk_import_table_push_dll_node(LNK_ImportTable *imptab, LNK_ImportDLL *dll)
{
// update list
SLLQueuePush(imptab->first_dll, imptab->last_dll, dll);
// update name -> dll hash table
return hash_table_push_path_raw(imptab->arena, imptab->dll_ht, dll->name, dll);
}
internal BucketNode *
lnk_import_table_push_func_node(LNK_ImportTable *imptab, LNK_ImportDLL *dll, LNK_ImportFunc *func)
{
// update list
SLLQueuePush(dll->first_func, dll->last_func, func);
// update name -> func hash table
return hash_table_push_string_raw(imptab->arena, dll->func_ht, func->name, func);
}
internal LNK_ImportDLL *
lnk_import_table_search_dll(LNK_ImportTable *imptab, String8 name)
{
KeyValuePair *kv = hash_table_search_path(imptab->dll_ht, name);
if (kv) {
Assert(kv->value_raw);
return kv->value_raw;
}
return 0;
}
internal LNK_ImportFunc *
lnk_import_table_search_func(LNK_ImportDLL *dll, String8 name)
{
KeyValuePair *kv = hash_table_search_string(dll->func_ht, name);
if (kv) {
Assert(kv->value_raw);
return kv->value_raw;
}
return 0;
}
internal LNK_ImportDLL *
lnk_import_table_push_dll_regular(LNK_ImportTable *imptab, LNK_SymbolTable *symtab, String8 dll_name, COFF_MachineType machine)
{
ProfBeginFunction();
// TODO: error handle
Assert(imptab->machine == machine);
LNK_Section *data_sect = imptab->data_sect;
LNK_Section *code_sect = imptab->code_sect;
LNK_Chunk *int_table_chunk = lnk_section_push_chunk_list(data_sect, imptab->int_chunk, str8_zero());
LNK_Chunk *ilt_table_chunk = lnk_section_push_chunk_list(data_sect, imptab->ilt_chunk, str8_zero());
LNK_Chunk *iat_table_chunk = lnk_section_push_chunk_list(data_sect, imptab->iat_chunk, str8_zero());
LNK_Chunk *code_table_chunk = lnk_section_push_chunk_list(code_sect, imptab->code_chunk, str8_zero());
String8 ilt_symbol_name = push_str8f(symtab->arena, "%S.lookup_table_voff", dll_name);
LNK_Symbol *ilt_symbol = lnk_make_defined_symbol_chunk(symtab->arena, ilt_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, ilt_table_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, ilt_symbol);
String8 iat_symbol_name = push_str8f(symtab->arena, "%S.import_addr_table_voff", dll_name);
LNK_Symbol *iat_symbol = lnk_make_defined_symbol_chunk(symtab->arena, iat_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, iat_table_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, iat_symbol);
String8 dll_name_cstr = push_cstr(data_sect->arena, dll_name);
LNK_Chunk *dll_name_chunk = lnk_section_push_chunk_data(data_sect, int_table_chunk, dll_name_cstr, str8_zero());
lnk_chunk_set_debugf(data_sect->arena, dll_name_chunk, "DLL name chunk (%S)", dll_name);
String8 dll_name_voff_name = push_str8f(symtab->arena, "%S.name_voff", dll_name);
LNK_Symbol *dll_name_voff_symbol = lnk_make_defined_symbol_chunk(symtab->arena, dll_name_voff_name, LNK_DefinedSymbolVisibility_Internal, 0, dll_name_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, dll_name_voff_symbol);
// chunk for dll directory entry
PE_ImportEntry *dir = push_array(imptab->arena, PE_ImportEntry, 1);
LNK_Chunk *dll_chunk = lnk_section_push_chunk_data(data_sect, imptab->dll_table_chunk, str8_struct(dir), str8_zero());
lnk_chunk_set_debugf(data_sect->arena, dll_chunk, "DLL Directory for %S", dll_name);
// patch dll import fields
lnk_section_push_reloc(data_sect, dll_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ImportEntry, lookup_table_voff), ilt_symbol);
lnk_section_push_reloc(data_sect, dll_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ImportEntry, name_voff), dll_name_voff_symbol);
lnk_section_push_reloc(data_sect, dll_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_ImportEntry, import_addr_table_voff), iat_symbol);
U64 import_size = coff_word_size_from_machine(machine);
// null entry to terminate import lookup table array
LNK_Chunk *null_ilt_chunk = lnk_section_push_chunk_data(data_sect, ilt_table_chunk, str8(0, import_size), str8_lit("zzzzzz"));
lnk_chunk_set_debugf(data_sect->arena, null_ilt_chunk, "%S: ILT terminator", dll_name);
// null entry to terminate import address table array
LNK_Chunk *null_iat_chunk = lnk_section_push_chunk_data(data_sect, iat_table_chunk, str8(0, import_size), str8_lit("zzzzzz"));
lnk_chunk_set_debugf(data_sect->arena, null_iat_chunk, "%S: IAT terminator", dll_name);
// push to list
LNK_ImportDLL *dll = push_array(imptab->arena, LNK_ImportDLL, 1);
dll->name = push_str8_copy(imptab->arena, dll_name);
dll->dll_chunk = dll_chunk;
dll->int_table_chunk = int_table_chunk;
dll->ilt_table_chunk = ilt_table_chunk;
dll->iat_table_chunk = iat_table_chunk;
dll->code_table_chunk = code_table_chunk;
dll->machine = machine;
dll->func_ht = hash_table_init(imptab->arena, LNK_IMPORT_FUNC_HASH_TABLE_BUCKET_COUNT);
lnk_import_table_push_dll_node(imptab, dll);
ProfEnd();
return dll;
}
internal LNK_ImportDLL *
lnk_import_table_push_dll_delayed(LNK_ImportTable *imptab, LNK_SymbolTable *symtab, String8 dll_name, COFF_MachineType machine)
{
ProfBeginFunction();
Assert(imptab->machine == machine);
U64 handle_size = coff_word_size_from_machine(machine);
U64 import_size = coff_word_size_from_machine(machine);
// shortcuts
LNK_Section *data_sect = imptab->data_sect;
LNK_Section *code_sect = imptab->code_sect;
// init DLL entry
PE_DelayedImportEntry *imp_desc = push_array(data_sect->arena, PE_DelayedImportEntry, 1);
imp_desc->attributes = 1;
imp_desc->name_voff = 0; // relocated
imp_desc->module_handle_voff = 0; // relocated
imp_desc->iat_voff = 0; // relocated
imp_desc->name_table_voff = 0; // relocated
imp_desc->bound_table_voff = 0; // relocated
imp_desc->unload_table_voff = 0; // relocated
imp_desc->time_stamp = 0;
// emit entry chunk
String8 imp_desc_data = str8_struct(imp_desc);
LNK_Chunk *imp_desc_chunk = lnk_section_push_chunk_data(data_sect, imptab->dll_table_chunk, imp_desc_data, str8_zero());
// emit entry symbol
String8 imp_desc_name = push_str8f(symtab->arena, "__DELAY_IMPORT_DESCRIPTOR_%S", dll_name);
LNK_Symbol *imp_desc_symbol = lnk_make_defined_symbol_chunk(symtab->arena, imp_desc_name, LNK_DefinedSymbolVisibility_Extern, 0, imp_desc_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, imp_desc_symbol);
// emit string table chunk
String8 int_table_chunk_debug = push_str8f(data_sect->arena, "delayed.%S.int", dll_name);
LNK_Chunk *int_table_chunk = lnk_section_push_chunk_list(data_sect, imptab->int_chunk, int_table_chunk_debug);
String8 int_table_symbol_name = push_str8f(symtab->arena, "delayed.%S.int", dll_name);
LNK_Symbol *int_table_symbol = lnk_make_defined_symbol_chunk(symtab->arena, int_table_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, int_table_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, int_table_symbol);
LNK_Chunk *null_string_chunk = lnk_section_push_chunk_list(data_sect, int_table_chunk, str8_lit("zzzzz"));
lnk_chunk_set_debugf(data_sect->arena, null_string_chunk, "string table null");
// emit DLL name chunk
String8 name_chunk_data = push_cstr(data_sect->arena, dll_name);
LNK_Chunk *name_chunk = lnk_section_push_chunk_data(data_sect, int_table_chunk, name_chunk_data, str8_zero());
String8 name_symbol_name = push_str8f(symtab->arena, "delayed.%S.name", dll_name);
LNK_Symbol *name_symbol = lnk_make_defined_symbol_chunk(symtab->arena, name_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, name_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, name_symbol);
// patch DLL name voff
lnk_section_push_reloc(data_sect, imp_desc_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_DelayedImportEntry, name_voff), name_symbol);
// emit DLL handle chunk
LNK_Chunk *handle_chunk = lnk_section_push_chunk_bss(data_sect, imptab->handle_table_chunk, handle_size, str8_zero());
String8 handle_name = push_str8f(symtab->arena, "delayed.%S.handle", dll_name);
LNK_Symbol *handle_symbol = lnk_make_defined_symbol_chunk(symtab->arena, handle_name, LNK_DefinedSymbolVisibility_Internal, 0, handle_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, handle_symbol);
// patch DLL handle voff
lnk_section_push_reloc(data_sect, imp_desc_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_DelayedImportEntry, module_handle_voff), handle_symbol);
// emit IAT chunk
LNK_Chunk *iat_table_chunk = lnk_section_push_chunk_list(data_sect, imptab->iat_chunk, str8_zero());
String8 iat_table_name = push_str8f(symtab->arena, "delayed.%S.iat", dll_name);
LNK_Symbol *iat_table_symbol = lnk_make_defined_symbol_chunk(symtab->arena, iat_table_name, LNK_DefinedSymbolVisibility_Internal, 0, iat_table_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, iat_table_symbol);
LNK_Chunk *null_iat_chunk = lnk_section_push_chunk_bss(data_sect, iat_table_chunk, import_size, str8_lit("zzzzzz"));
lnk_chunk_set_debugf(data_sect->arena, null_iat_chunk, "%S: IAT terminator", dll_name);
// emit ILT chunk
LNK_Chunk *ilt_table_chunk = lnk_section_push_chunk_list(data_sect, imptab->ilt_chunk, str8_zero());
LNK_Chunk *null_ilt_chunk = lnk_section_push_chunk_bss(data_sect, ilt_table_chunk, import_size, str8_lit("zzzzzz"));
lnk_chunk_set_debugf(data_sect->arena, null_ilt_chunk, "%S: ILT terminator", dll_name);
String8 ilt_table_name = push_str8f(symtab->arena, "delayed.%S.ilt", dll_name);
LNK_Symbol *ilt_table_symbol = lnk_make_defined_symbol_chunk(symtab->arena, ilt_table_name, LNK_DefinedSymbolVisibility_Extern, 0, ilt_table_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, ilt_table_symbol);
// patch import address table voff
lnk_section_push_reloc(data_sect, imp_desc_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_DelayedImportEntry, iat_voff), iat_table_symbol);
// patch string table voff
lnk_section_push_reloc(data_sect, imp_desc_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_DelayedImportEntry, name_table_voff), ilt_table_symbol);
// emit bound table chunk
LNK_Chunk *biat_chunk = 0;
if (imptab->flags & LNK_ImportTableFlag_EmitBiat) {
biat_chunk = lnk_section_push_chunk_list(data_sect, imptab->biat_chunk, str8_zero());
String8 biat_symbol_name = push_str8f(symtab->arena, "delayed.%S.BIAT", dll_name);
LNK_Symbol *biat_symbol = lnk_make_defined_symbol_chunk(symtab->arena, biat_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, biat_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, biat_symbol);
// patch BIAT field off
lnk_section_push_reloc(data_sect, imp_desc_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_DelayedImportEntry, bound_table_voff), biat_symbol);
}
// emit unload table chunk
LNK_Chunk *uiat_chunk = NULL;
if (imptab->flags & LNK_ImportTableFlag_EmitUiat) {
uiat_chunk = lnk_section_push_chunk_list(data_sect, imptab->uiat_chunk, str8_zero());
String8 uiat_symbol_name = push_str8f(symtab->arena, "delayed.%S.UIAT", dll_name);
LNK_Symbol *uiat_symbol = lnk_make_defined_symbol_chunk(symtab->arena, uiat_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, uiat_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, uiat_symbol);
// patch UIAT field voff
lnk_section_push_reloc(data_sect, imp_desc_chunk, LNK_Reloc_VIRT_OFF_32, OffsetOf(PE_DelayedImportEntry, unload_table_voff), uiat_symbol);
}
// emit chunk for DLL thunk/load code
LNK_Chunk *code_chunk = lnk_section_push_chunk_list(code_sect, imptab->code_chunk, str8_zero());
lnk_chunk_set_debugf(code_sect->arena, code_chunk, "code for %S", dll_name);
// emit tail merge
LNK_Chunk *tail_merge_chunk = 0;
switch (machine) {
case COFF_MachineType_X64: {
LNK_Symbol *delay_load_helper_symbol = lnk_make_undefined_symbol(symtab->arena, str8_lit(LNK_DELAY_LOAD_HELPER2_SYMBOL_NAME), LNK_SymbolScopeFlag_Main);
tail_merge_chunk = lnk_emit_tail_merge_thunk_x64(code_sect, code_chunk, imp_desc_symbol, delay_load_helper_symbol);
} break;
default: {
lnk_not_implemented("TODO: __tailMerge for %S", coff_string_from_machine_type(machine));
} break;
}
// fill out result
LNK_ImportDLL *dll = push_array(imptab->arena, LNK_ImportDLL, 1);
dll->dll_chunk = imp_desc_chunk;
dll->int_table_chunk = int_table_chunk;
dll->iat_table_chunk = iat_table_chunk;
dll->ilt_table_chunk = ilt_table_chunk;
dll->biat_table_chunk = biat_chunk;
dll->uiat_table_chunk = uiat_chunk;
dll->code_table_chunk = code_chunk;
dll->tail_merge_symbol = lnk_emit_tail_merge_symbol(symtab, tail_merge_chunk, dll_name);
dll->name = push_str8_copy(imptab->arena, dll_name);
dll->machine = machine;
dll->func_ht = hash_table_init(imptab->arena, LNK_IMPORT_FUNC_HASH_TABLE_BUCKET_COUNT);
lnk_import_table_push_dll_node(imptab, dll);
ProfEnd();
return dll;
}
internal LNK_ImportFunc *
lnk_import_table_push_func_regular(LNK_ImportTable *imptab, LNK_SymbolTable *symtab, LNK_ImportDLL *dll, COFF_ImportHeader *header)
{
ProfBeginFunction();
Assert(header->machine == dll->machine); // TODO: error handling
LNK_Section *data_sect = imptab->data_sect;
LNK_Section *code_sect = imptab->code_sect;
LNK_Chunk *int_table_chunk = dll->int_table_chunk;
LNK_Chunk *ilt_table_chunk = dll->ilt_table_chunk;
LNK_Chunk *iat_table_chunk = dll->iat_table_chunk;
LNK_Chunk *code_table_chunk = dll->code_table_chunk;
LNK_Chunk *ilt_chunk = g_null_chunk_ptr;
LNK_Chunk *iat_chunk = g_null_chunk_ptr;
U64 import_size = coff_word_size_from_machine(dll->machine);
// generate sort index (optional)
String8 sort_index = str8_from_bits_u32(data_sect->arena, header->hint);
switch (header->name_type) {
case COFF_ImportHeaderNameType_ORDINAL: {
String8 ordinal_data = lnk_ordinal_data_from_hint(data_sect->arena, dll->machine, header->hint);
ilt_chunk = lnk_section_push_chunk_data(data_sect, ilt_table_chunk, ordinal_data, sort_index);
iat_chunk = lnk_section_push_chunk_data(data_sect, iat_table_chunk, ordinal_data, sort_index);
// associate chunks
lnk_section_associate_chunks(data_sect, iat_chunk, ilt_chunk);
} break;
case COFF_ImportHeaderNameType_NAME: {
// put together name look up entry
String8 int_data = coff_make_import_lookup(data_sect->arena, header->hint, header->func_name);
LNK_Chunk *int_chunk = lnk_section_push_chunk_data(data_sect, int_table_chunk, int_data, str8_zero());
// create symbol for lookup chunk
String8 int_symbol_name = push_str8f(symtab->arena, "regular.%S.%S.name", dll->name, header->func_name);
LNK_Symbol *int_symbol = lnk_make_defined_symbol_chunk(symtab->arena, int_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, int_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, int_symbol);
// in the file IAT mirrors ILT, dynamic linker later overwrites it with imported function addresses.
ilt_chunk = lnk_section_push_chunk_bss(data_sect, ilt_table_chunk, import_size, sort_index);
iat_chunk = lnk_section_push_chunk_bss(data_sect, iat_table_chunk, import_size, sort_index);
lnk_chunk_set_debugf(data_sect->arena, ilt_chunk, "ILT entry for %S.%S", dll->name, header->func_name);
lnk_chunk_set_debugf(data_sect->arena, iat_chunk, "IAT entry for %S.%S", dll->name, header->func_name);
// associate chunks
lnk_section_associate_chunks(data_sect, iat_chunk, ilt_chunk);
lnk_section_associate_chunks(data_sect, iat_chunk, int_chunk);
// patch IAT and ILT
lnk_section_push_reloc(data_sect, ilt_chunk, LNK_Reloc_VIRT_OFF_32, 0, int_symbol);
lnk_section_push_reloc(data_sect, iat_chunk, LNK_Reloc_VIRT_OFF_32, 0, int_symbol);
} break;
case COFF_ImportHeaderNameType_UNDECORATE: {
lnk_not_implemented("TODO: COFF_ImportHeaderNameType_UNDECORATE");
} break;
case COFF_ImportHeaderNameType_NAME_NOPREFIX: {
lnk_not_implemented("TODO: COFF_ImportHeaderNameType_NAME_NOPREFIX");
} break;
}
String8 ilt_symbol_name = push_str8f(symtab->arena, "regular.%S.%S.ilt", dll->name, header->func_name);
String8 iat_symbol_name = push_str8f(symtab->arena, "__imp_%S", header->func_name);
LNK_Symbol *ilt_symbol = lnk_make_defined_symbol_chunk(symtab->arena, ilt_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, ilt_chunk, 0, 0, 0);
LNK_Symbol *iat_symbol = lnk_make_defined_symbol_chunk(symtab->arena, iat_symbol_name, LNK_DefinedSymbolVisibility_Extern, 0, iat_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, ilt_symbol);
lnk_symbol_table_push(symtab, iat_symbol);
// generate thunks
LNK_Symbol *jmp_thunk_symbol = g_null_symbol_ptr;
if (header->type == COFF_ImportHeaderType_CODE) {
switch (dll->machine) {
case COFF_MachineType_X64: {
// generate jump thunk
LNK_Chunk *jmp_thunk_chunk = lnk_emit_indirect_jump_thunk_x64(code_sect, code_table_chunk, iat_symbol);
lnk_section_associate_chunks(data_sect, iat_chunk, jmp_thunk_chunk);
// push jump thunk symbol
String8 jmp_thunk_symbol_name = push_str8_copy(symtab->arena, header->func_name);
jmp_thunk_symbol = lnk_emit_jmp_thunk_symbol(symtab, jmp_thunk_chunk, jmp_thunk_symbol_name);
lnk_symbol_set_debugf(symtab->arena, jmp_thunk_symbol, "x64 jmp thunk %S.%S", dll->name, header->func_name);
} break;
default: lnk_not_implemented("TODO: support for machine 0x%X", dll->machine); break;
}
}
// fill out import
LNK_ImportFunc *func = push_array(imptab->arena, LNK_ImportFunc, 1);
func->name = push_str8_copy(imptab->arena, header->func_name);
func->thunk_symbol_name = push_str8_copy(imptab->arena, jmp_thunk_symbol->name);
func->iat_symbol_name = push_str8_copy(imptab->arena, iat_symbol->name);
lnk_import_table_push_func_node(imptab, dll, func);
ProfEnd();
return func;
}
internal LNK_ImportFunc *
lnk_import_table_push_func_delayed(LNK_ImportTable *imptab, LNK_SymbolTable *symtab, LNK_ImportDLL *dll, COFF_ImportHeader *header)
{
ProfBeginFunction();
Assert(dll->machine == header->machine); // TODO: error handle
U64 import_size = coff_word_size_from_machine(dll->machine);
LNK_Section *data_sect = imptab->data_sect;
LNK_Section *code_sect = imptab->code_sect;
LNK_Chunk *int_table_chunk = dll->int_table_chunk;
LNK_Chunk *ilt_table_chunk = dll->ilt_table_chunk;
LNK_Chunk *iat_table_chunk = dll->iat_table_chunk;
LNK_Chunk *biat_table_chunk = dll->biat_table_chunk;
LNK_Chunk *uiat_table_chunk = dll->uiat_table_chunk;
LNK_Chunk *code_table_chunk = dll->code_table_chunk;
LNK_Chunk *ilt_chunk = g_null_chunk_ptr;
LNK_Chunk *iat_chunk = g_null_chunk_ptr;
LNK_Chunk *uiat_chunk = g_null_chunk_ptr;
LNK_Chunk *biat_chunk = g_null_chunk_ptr;
LNK_Symbol *int_symbol = 0;
// generate sort index (optional)
String8 sort_index = str8_from_bits_u32(data_sect->arena, header->hint);
// generate thunks
LNK_Symbol *jmp_thunk_symbol = g_null_symbol_ptr;
LNK_Symbol *load_thunk_symbol = g_null_symbol_ptr;
LNK_Chunk *jmp_thunk_chunk = 0;
LNK_Chunk *load_thunk_chunk = 0;
if (header->type == COFF_ImportHeaderType_CODE) {
switch (dll->machine) {
case COFF_MachineType_X64: {
String8 iat_symbol_name = push_str8f(symtab->arena, "__imp_%S", header->func_name);
LNK_Symbol *iat_symbol = lnk_make_undefined_symbol(symtab->arena, iat_symbol_name, LNK_SymbolScopeFlag_Main);
lnk_symbol_set_debugf(symtab->arena, iat_symbol, "x64 IAT (delayed) %S.%S", dll->name, header->func_name);
// emit jmp thunk chunk
jmp_thunk_chunk = lnk_emit_indirect_jump_thunk_x64(code_sect, code_table_chunk, iat_symbol);
jmp_thunk_symbol = lnk_emit_jmp_thunk_symbol(symtab, jmp_thunk_chunk, header->func_name);
lnk_symbol_set_debugf(symtab->arena, jmp_thunk_symbol, "x64 jmp thunk (delayed) %S.%S", dll->name, header->func_name);
// emit load thunk
load_thunk_chunk = lnk_emit_load_thunk_x64(code_sect, code_table_chunk, iat_symbol, dll->tail_merge_symbol);
load_thunk_symbol = lnk_emit_load_thunk_symbol(symtab, load_thunk_chunk, header->func_name);
lnk_symbol_set_debugf(symtab->arena, load_thunk_symbol, "x64 load thunk (delayed) %S.%S", dll->name, header->func_name);
} break;
default: lnk_not_implemented("TODO: support for machine 0x%X", dll->machine); break;
}
}
switch (header->name_type) {
case COFF_ImportHeaderNameType_ORDINAL: {
String8 ordinal_data = lnk_ordinal_data_from_hint(data_sect->arena, dll->machine, header->hint);
Assert(ordinal_data.size == import_size);
ilt_chunk = lnk_section_push_chunk_data(data_sect, ilt_table_chunk, ordinal_data, sort_index);
iat_chunk = lnk_section_push_chunk_bss(data_sect, iat_table_chunk, import_size, sort_index);
lnk_section_push_reloc(data_sect, iat_chunk, LNK_Reloc_ADDR_64, 0, load_thunk_symbol);
lnk_section_associate_chunks(data_sect, iat_chunk, ilt_chunk);
if (imptab->flags & LNK_ImportTableFlag_EmitBiat) {
biat_chunk = lnk_section_push_chunk_bss(data_sect, biat_table_chunk, import_size, sort_index);
lnk_section_push_reloc(data_sect, biat_chunk, LNK_Reloc_ADDR_64, 0, load_thunk_symbol);
lnk_section_associate_chunks(data_sect, iat_chunk, biat_chunk);
}
if (imptab->flags & LNK_ImportTableFlag_EmitUiat) {
uiat_chunk = lnk_section_push_chunk_bss(data_sect, uiat_table_chunk, import_size, sort_index);
lnk_section_push_reloc(data_sect, uiat_chunk, LNK_Reloc_ADDR_64, 0, load_thunk_symbol);
lnk_section_associate_chunks(data_sect, iat_chunk, uiat_chunk);
}
} break;
case COFF_ImportHeaderNameType_NAME: {
// put together name look up entry
String8 int_data = coff_make_import_lookup(data_sect->arena, header->hint, header->func_name);
LNK_Chunk *int_chunk = lnk_section_push_chunk_data(data_sect, int_table_chunk, int_data, str8_zero());
// create symbol for lookup chunk
String8 int_symbol_name = push_str8f(symtab->arena, "%S.%S.name.delayed", dll->name, header->func_name);
int_symbol = lnk_make_defined_symbol_chunk(symtab->arena, int_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, int_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, int_symbol);
// dynamic linker patches this voff on DLL load event
ilt_chunk = lnk_section_push_chunk_bss(data_sect, ilt_table_chunk, import_size, sort_index);
lnk_chunk_set_debugf(data_sect->arena, ilt_chunk, "ILT entry (delayed) %S.%S", dll->name, header->func_name);
// patch-in ILT with import voff
lnk_section_push_reloc(data_sect, ilt_chunk, LNK_Reloc_VIRT_OFF_32, 0, int_symbol);
// in the file IAT mirrors ILT, dynamic linker later overwrites it with imported function addresses.
iat_chunk = lnk_section_push_chunk_bss(data_sect, iat_table_chunk, import_size, sort_index);
lnk_chunk_set_debugf(data_sect->arena, iat_chunk, "IAT entre (delayed) %S.%S", dll->name, header->func_name);
// associate chunks
lnk_section_associate_chunks(data_sect, iat_chunk, ilt_chunk);
lnk_section_associate_chunks(data_sect, iat_chunk, int_chunk);
// patch-in thunk address
lnk_section_push_reloc(data_sect, iat_chunk, LNK_Reloc_ADDR_64, 0, load_thunk_symbol);
if (imptab->flags & LNK_ImportTableFlag_EmitBiat) {
biat_chunk = lnk_section_push_chunk_bss(data_sect, biat_table_chunk, import_size, sort_index);
lnk_chunk_set_debugf(data_sect->arena, biat_chunk, "%S.biat.%S (delayed)", dll->name, header->func_name);
// patch-in thunk address
lnk_section_push_reloc(data_sect, biat_chunk, LNK_Reloc_ADDR_64, 0, load_thunk_symbol);
}
if (imptab->flags & LNK_ImportTableFlag_EmitUiat) {
uiat_chunk = lnk_section_push_chunk_bss(data_sect, uiat_table_chunk, import_size, sort_index);
lnk_chunk_set_debugf(data_sect->arena, uiat_chunk, "%S.uiat.%S (delayed)", dll->name, header->func_name);
// patch-in thunk address
lnk_section_push_reloc(data_sect, uiat_chunk, LNK_Reloc_ADDR_64, 0, load_thunk_symbol);
}
} break;
case COFF_ImportHeaderNameType_UNDECORATE: {
lnk_not_implemented("TODO: COFF_ImportHeaderNameType_UNDECORATE");
} break;
case COFF_ImportHeaderNameType_NAME_NOPREFIX: {
lnk_not_implemented("TODO: COFF_ImportHeaderNameType_NAME_NOPREFIX");
} break;
}
if (jmp_thunk_chunk) {
lnk_section_associate_chunks(data_sect, iat_chunk, jmp_thunk_chunk);
}
if (load_thunk_chunk) {
lnk_section_associate_chunks(data_sect, iat_chunk, load_thunk_chunk);
}
String8 iat_symbol_name = push_str8f(symtab->arena, "__imp_%S", header->func_name);
LNK_Symbol *iat_symbol = lnk_make_defined_symbol_chunk(symtab->arena, iat_symbol_name, LNK_DefinedSymbolVisibility_Extern, 0, iat_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, iat_symbol);
String8 ilt_symbol_name = push_str8f(symtab->arena, "%S.%S.ilt.delayed", dll->name, header->func_name);
LNK_Symbol *ilt_symbol = lnk_make_defined_symbol_chunk(symtab->arena, ilt_symbol_name, LNK_DefinedSymbolVisibility_Internal, 0, ilt_chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, ilt_symbol);
// fill out import
LNK_ImportFunc *func = push_array(imptab->arena, LNK_ImportFunc, 1);
func->name = push_str8_copy(imptab->arena, header->func_name);
func->thunk_symbol_name = push_str8_copy(imptab->arena, jmp_thunk_symbol->name);
func->iat_symbol_name = push_str8_copy(imptab->arena, iat_symbol->name);
lnk_import_table_push_func_node(imptab, dll, func);
ProfEnd();
return func;
}
internal String8
lnk_ordinal_data_from_hint(Arena *arena, COFF_MachineType machine, U16 hint)
{
String8 ordinal_data = str8_zero();
switch (machine) {
case COFF_MachineType_X64: {
U64 *ordinal = push_array(arena, U64, 1);
*ordinal = coff_make_ordinal_64(hint);
ordinal_data = str8_struct(ordinal);
} break;
case COFF_MachineType_X86: {
U32 *ordinal = push_array(arena, U32, 1);
*ordinal = coff_make_ordinal_32(hint);
ordinal_data = str8_struct(ordinal);
} break;
default: lnk_not_implemented("TODO: support for machine 0x%x", machine);
}
return ordinal_data;
}
internal LNK_Chunk *
lnk_emit_indirect_jump_thunk_x64(LNK_Section *sect, LNK_Chunk *parent, LNK_Symbol *addr_ptr)
{
ProfBeginFunction();
static U8 thunk[] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 }; // jmp [__imp_<FUNC_NAME>]
// emit chunk
String8 jmp_data = push_str8_copy(sect->arena, str8_array_fixed(thunk));
LNK_Chunk *jmp_chunk = lnk_section_push_chunk_data(sect, parent, jmp_data, str8_zero());
// patch thunk with imports address
static const U64 JMP_OPERAND_OFFSET = 2;
lnk_section_push_reloc(sect, jmp_chunk, LNK_Reloc_REL32, JMP_OPERAND_OFFSET, addr_ptr);
ProfEnd();
return jmp_chunk;
}
internal LNK_Chunk *
lnk_emit_load_thunk_x64(LNK_Section *sect, LNK_Chunk *parent, LNK_Symbol *imp_addr_ptr, LNK_Symbol *tail_merge)
{
ProfBeginFunction();
static U8 load_thunk[] = {
0x48, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00, // lea rax, [__imp_<FUNC_NAME>]
0xE9, 0x00, 0x00, 0x00, 0x00 // jmp __tailMerge_<DLL_NAME>
};
// emit load thunk chunk
String8 load_thunk_data = push_str8_copy(sect->arena, str8_array_fixed(load_thunk));
LNK_Chunk *load_thunk_chunk = lnk_section_push_chunk_data(sect, parent, load_thunk_data, str8_zero());
// patch lea with IAT entry
static const U64 LEA_OPERAND_OFFSET = 3;
lnk_section_push_reloc(sect, load_thunk_chunk, LNK_Reloc_REL32, LEA_OPERAND_OFFSET, imp_addr_ptr);
// patch jmp __tailMerge_<DLL_NAME>
static const U64 JMP_OPERAND_OFFSET = 8;
lnk_section_push_reloc(sect, load_thunk_chunk, LNK_Reloc_REL32, JMP_OPERAND_OFFSET, tail_merge);
ProfEnd();
return load_thunk_chunk;
}
internal LNK_Chunk *
lnk_emit_tail_merge_thunk_x64(LNK_Section *sect, LNK_Chunk *parent, LNK_Symbol *dll_import_descriptor, LNK_Symbol *delay_load_helper)
{
ProfBeginFunction();
static U8 tail_merge[] = {
0x48, 0x89, 0x4C, 0x24, 0x08, // mov qword ptr [rsp+8],rcx
0x48, 0x89, 0x54, 0x24, 0x10, // mov qword ptr [rsp+10h],rdx
0x4C, 0x89, 0x44, 0x24, 0x18, // mov qword ptr [rsp+18h],r8
0x4C, 0x89, 0x4C, 0x24, 0x20, // mov qword ptr [rsp+20h],r9
0x48, 0x83, 0xEC, 0x68, // sub rsp,68h
0x66, 0x0F, 0x7F, 0x44, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h],xmm0
0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h],xmm1
0x66, 0x0F, 0x7F, 0x54, 0x24, 0x40, // movdqa xmmword ptr [rsp+40h],xmm2
0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x50, // movdqa xmmword ptr [rsp+50h],xmm3
0x48, 0x8B, 0xD0, // mov rdx,rax
0x48, 0x8D, 0x0D, 0x00, 0x00, 0x00, 0x00, // lea rcx,[__DELAY_IMPORT_DESCRIPTOR_<DLL_NAME>]
0xE8, 0x00, 0x00, 0x00, 0x00, // call __delayLoadHelper2
0x66, 0x0F, 0x6F, 0x44, 0x24, 0x20, // movdqa xmm0,xmmword ptr [rsp+20h]
0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x30, // movdqa xmm1,xmmword ptr [rsp+30h]
0x66, 0x0F, 0x6F, 0x54, 0x24, 0x40, // movdqa xmm2,xmmword ptr [rsp+40h]
0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x50, // movdqa xmm3,xmmword ptr [rsp+50h]
0x48, 0x8B, 0x4C, 0x24, 0x70, // mov rcx,qword ptr [rsp+70h]
0x48, 0x8B, 0x54, 0x24, 0x78, // mov rdx,qword ptr [rsp+78h]
0x4C, 0x8B, 0x84, 0x24, 0x80, 0x00, 0x00, 0x00, // mov r8,qword ptr [rsp+80h]
0x4C, 0x8B, 0x8C, 0x24, 0x88, 0x00, 0x00, 0x00, // mov r9,qword ptr [rsp+88h]
0x48, 0x83, 0xC4, 0x68, // add rsp,68h
0xFF, 0xE0, // jmp rax
};
// emit tail merge chunk
String8 tail_merge_data = push_str8_copy(sect->arena, str8_array_fixed(tail_merge));
LNK_Chunk *tail_merge_chunk = lnk_section_push_chunk_data(sect, parent, tail_merge_data, str8_zero());
// patch lea __DELAY_IMPORT_DESCRIPTOR_<DLL_NAME>
static const U64 LEA_OPERAND_OFFSET = 54;
lnk_section_push_reloc(sect, tail_merge_chunk, LNK_Reloc_REL32, LEA_OPERAND_OFFSET, dll_import_descriptor);
// patch call __delayLoadHelper2
static const U64 CALL_OPERAND_OFFSET = 59;
lnk_section_push_reloc(sect, tail_merge_chunk, LNK_Reloc_REL32, CALL_OPERAND_OFFSET, delay_load_helper);
ProfEnd();
return tail_merge_chunk;
}
internal LNK_Symbol *
lnk_emit_load_thunk_symbol(LNK_SymbolTable *symtab, LNK_Chunk *chunk, String8 func_name)
{
ProfBeginFunction();
// emit load thunk symbol
String8 load_thunk_name = push_str8f(symtab->arena, "__imp_load_%S", func_name);
LNK_Symbol *load_thunk_symbol = lnk_make_defined_symbol_chunk(symtab->arena, load_thunk_name, LNK_DefinedSymbolVisibility_Extern, LNK_DefinedSymbolFlag_IsFunc|LNK_DefinedSymbolFlag_IsThunk, chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, load_thunk_symbol);
ProfEnd();
return load_thunk_symbol;
}
internal LNK_Symbol *
lnk_emit_jmp_thunk_symbol(LNK_SymbolTable *symtab, LNK_Chunk *chunk, String8 func_name)
{
ProfBeginFunction();
String8 jmp_thunk_name = push_str8f(symtab->arena, "%S", func_name);
LNK_Symbol *jmp_thunk_symbol = lnk_make_defined_symbol_chunk(symtab->arena, jmp_thunk_name, LNK_DefinedSymbolVisibility_Extern, LNK_DefinedSymbolFlag_IsFunc|LNK_DefinedSymbolFlag_IsThunk, chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, jmp_thunk_symbol);
ProfEnd();
return jmp_thunk_symbol;
}
internal LNK_Symbol *
lnk_emit_tail_merge_symbol(LNK_SymbolTable *symtab, LNK_Chunk *chunk, String8 func_name)
{
ProfBeginFunction();
String8 tail_merge_name = push_str8f(symtab->arena, "__tailMerge_%S", func_name);
LNK_Symbol *tail_merge_symbol = lnk_make_defined_symbol_chunk(symtab->arena, tail_merge_name, LNK_DefinedSymbolVisibility_Extern, LNK_DefinedSymbolFlag_IsFunc|LNK_DefinedSymbolFlag_IsThunk, chunk, 0, 0, 0);
lnk_symbol_table_push(symtab, tail_merge_symbol);
ProfEnd();
return tail_merge_symbol;
}
+81
View File
@@ -0,0 +1,81 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
#define LNK_IMPORT_DLL_HASH_TABLE_BUCKET_COUNT 512
#define LNK_IMPORT_FUNC_HASH_TABLE_BUCKET_COUNT 2048
typedef struct LNK_ImportFunc
{
struct LNK_ImportFunc *next;
String8 name;
String8 thunk_symbol_name;
String8 iat_symbol_name;
} LNK_ImportFunc;
typedef struct LNK_ImportDLL
{
struct LNK_ImportDLL *next;
struct LNK_ImportFunc *first_func;
struct LNK_ImportFunc *last_func;
LNK_Chunk *dll_chunk;
LNK_Chunk *int_table_chunk;
LNK_Chunk *ilt_table_chunk;
LNK_Chunk *iat_table_chunk;
LNK_Chunk *biat_table_chunk;
LNK_Chunk *uiat_table_chunk;
LNK_Chunk *code_table_chunk;
LNK_Symbol *tail_merge_symbol;
String8 name;
COFF_MachineType machine;
HashTable *func_ht;
} LNK_ImportDLL;
enum
{
LNK_ImportTableFlag_EmitBiat = (1 << 0),
LNK_ImportTableFlag_EmitUiat = (1 << 1),
};
typedef U32 LNK_ImportTableFlags;
typedef struct LNK_ImportTable
{
Arena *arena;
COFF_MachineType machine;
LNK_ImportDLL *first_dll;
LNK_ImportDLL *last_dll;
LNK_Section *data_sect;
LNK_Section *code_sect;
LNK_Chunk *dll_table_chunk;
LNK_Chunk *int_chunk;
LNK_Chunk *handle_table_chunk;
LNK_Chunk *iat_chunk;
LNK_Chunk *ilt_chunk;
LNK_Chunk *biat_chunk;
LNK_Chunk *uiat_chunk;
LNK_Chunk *code_chunk;
LNK_ImportTableFlags flags;
HashTable *dll_ht;
} LNK_ImportTable;
internal LNK_ImportTable * lnk_import_table_alloc_regular(LNK_SectionTable *st, LNK_SymbolTable *symtab, COFF_MachineType machine);
internal LNK_ImportTable * lnk_import_table_alloc_delayed(LNK_SectionTable *st, LNK_SymbolTable *symtab, COFF_MachineType machine, B32 is_unloadable, B32 is_bindable);
internal void lnk_import_table_release(LNK_ImportTable **imptab);
internal LNK_ImportDLL * lnk_import_table_push_dll_regular(LNK_ImportTable *imptab, LNK_SymbolTable *symtab, String8 dll_name, COFF_MachineType machine);
internal LNK_ImportDLL * lnk_import_table_push_dll_delayed(LNK_ImportTable *imptab, LNK_SymbolTable *symtab, String8 dll_name, COFF_MachineType machine);
internal LNK_ImportFunc * lnk_import_table_push_func_regular(LNK_ImportTable *imptab, LNK_SymbolTable *symtab, LNK_ImportDLL *dll, COFF_ImportHeader *header);
internal LNK_ImportFunc * lnk_import_table_push_func_delayed(LNK_ImportTable *imptab, LNK_SymbolTable *symtab, LNK_ImportDLL *dll, COFF_ImportHeader *header);
internal LNK_ImportDLL * lnk_import_table_search_dll(LNK_ImportTable *imptab, String8 name);
internal LNK_ImportFunc * lnk_import_table_search_func(LNK_ImportDLL *dll, String8 name);
internal String8 lnk_ordinal_data_from_hint(Arena *arena, COFF_MachineType machine, U16 hint);
internal LNK_Chunk * lnk_emit_indirect_jump_thunk_x64(LNK_Section *sect, LNK_Chunk *parent, LNK_Symbol *addr_ptr);
internal LNK_Chunk * lnk_emit_load_thunk_x64(LNK_Section *sect, LNK_Chunk *parent, LNK_Symbol *imp_addr_ptr, LNK_Symbol *tail_merge);
internal LNK_Chunk * lnk_emit_tail_merge_thunk_x64(LNK_Section *sect, LNK_Chunk *parent, LNK_Symbol *dll_import_descriptor, LNK_Symbol *delay_load_helper);
internal LNK_Symbol * lnk_emit_load_thunk_symbol(LNK_SymbolTable *symtab, LNK_Chunk *chunk, String8 func_name);
internal LNK_Symbol * lnk_emit_jmp_thunk_symbol(LNK_SymbolTable *symtab, LNK_Chunk *chunk, String8 func_name);
internal LNK_Symbol * lnk_emit_tail_merge_symbol(LNK_SymbolTable *symtab, LNK_Chunk *chunk, String8 func_name);
+941
View File
@@ -0,0 +1,941 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal LNK_LibNode *
lnk_lib_list_reserve(Arena *arena, LNK_LibList *list, U64 count)
{
LNK_LibNode *arr = 0;
if (count) {
arr = push_array(arena, LNK_LibNode, count);
for (LNK_LibNode *ptr = arr, *opl = arr + count; ptr < opl; ++ptr) {
SLLQueuePush(list->first, list->last, ptr);
}
list->count += count;
}
return arr;
}
internal LNK_LibMemberNode *
lnk_lib_member_list_push(Arena *arena, LNK_LibMemberList *list, LNK_LibMember member)
{
LNK_LibMemberNode *n = push_array_no_zero(arena, LNK_LibMemberNode, 1);
n->next = 0;
n->data = member;
SLLQueuePush(list->first, list->last, n);
++list->count;
return n;
}
internal LNK_LibMember *
lnk_lib_member_array_from_list(Arena *arena, LNK_LibMemberList list)
{
ProfBeginFunction();
LNK_LibMember *arr = push_array_no_zero(arena, LNK_LibMember, list.count);
LNK_LibMember *ptr = arr;
for (LNK_LibMemberNode *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);
}
ProfEnd();
return arr;
}
internal LNK_LibSymbolNode *
lnk_lib_symbol_list_push(Arena *arena, LNK_LibSymbolList *list, LNK_LibSymbol symbol)
{
LNK_LibSymbolNode *n = push_array_no_zero(arena, LNK_LibSymbolNode, 1);
n->next = 0;
n->data = symbol;
SLLQueuePush(list->first, list->last, n);
++list->count;
return n;
}
internal LNK_LibSymbol *
lnk_lib_symbol_array_from_list(Arena *arena, LNK_LibSymbolList list)
{
LNK_LibSymbol *arr = push_array_no_zero(arena, LNK_LibSymbol, list.count + 2);
LNK_LibSymbol *ptr = arr + 1;
for (LNK_LibSymbolNode *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;
}
int
lnk_lib_symbol_name_compar(const void *raw_a, const void *raw_b)
{
const LNK_Symbol *sa = (const LNK_Symbol *)raw_a;
const LNK_Symbol *sb = (const LNK_Symbol *)raw_b;
return str8_compar_case_sensetive(&sa->name, &sb->name);
}
int
lnk_lib_symbol_name_compar_is_before(void *raw_a, void *raw_b)
{
int compar = lnk_lib_symbol_name_compar(raw_a, raw_b);
int is_before = compar < 0;
return is_before;
}
internal void
lnk_lib_symbol_array_sort(LNK_LibSymbol *arr, U64 count)
{
Assert(count >= 2);
radsort(arr + 1, count - 2, lnk_lib_symbol_name_compar_is_before);
}
////////////////////////////////
internal LNK_Lib
lnk_lib_from_data(Arena *arena, String8 data, String8 path)
{
U64 symbol_count;
String8 string_table;
U32 *member_off_arr;
// is data archive?
COFF_ArchiveType type = coff_archive_type_from_data(data);
if (type == COFF_Archive_Null) {
lnk_not_implemented("TODO: data is not archive");
}
COFF_ArchiveParse parse = coff_archive_parse_from_data(arena, data);
// try to init library from optional second member
if (parse.second_member.member_count) {
COFF_ArchiveSecondMember second_member = parse.second_member;
Assert(second_member.symbol_count == second_member.symbol_indices.size / sizeof(U16));
Assert(second_member.member_count == second_member.member_offsets.size / sizeof(U32));
symbol_count = second_member.symbol_count;
string_table = second_member.string_table;
member_off_arr = push_array_no_zero(arena, U32, symbol_count);
// decompress member offsets
U32 *comp_off_arr = (U32*)second_member.member_offsets.str;
U16 *off_number_arr = (U16*)second_member.symbol_indices.str;
for (U64 symbol_idx = 0; symbol_idx < symbol_count; symbol_idx += 1) {
U16 off_number = off_number_arr[symbol_idx];
if (0 < off_number && off_number <= second_member.member_count) {
member_off_arr[symbol_idx] = comp_off_arr[off_number - 1];
} else {
// TODO: log bad offset
member_off_arr[symbol_idx] = max_U32;
}
}
}
// first member is deprecated however tools emit it for compatibility reasons
// and lld-link with /DLL emits only first member
else if (parse.first_member.symbol_count) {
COFF_ArchiveFirstMember first_member = parse.first_member;
Assert(first_member.symbol_count == first_member.member_offsets.size / sizeof(U32));
symbol_count = first_member.symbol_count;
string_table = first_member.string_table;
member_off_arr = (U32*)first_member.member_offsets.str;
// convert big endian offsets
for (U32 offset_idx = 0; offset_idx < symbol_count; offset_idx += 1) {
member_off_arr[offset_idx] = BE_U32(member_off_arr[offset_idx]);
}
} else {
symbol_count = 0;
string_table = str8(0,0);
member_off_arr = 0;
}
// parse string table
String8List symbol_name_list = str8_split_by_string_chars(arena, string_table, str8_lit("\0"), StringSplitFlag_KeepEmpties);
Assert(symbol_name_list.node_count >= symbol_count);
symbol_count = Min(symbol_count, symbol_name_list.node_count);
// init lib
LNK_Lib lib = {0};
lib.path = push_str8_copy(arena, path);
lib.data = data;
lib.type = type;
lib.symbol_count = symbol_count;
lib.member_off_arr = member_off_arr;
lib.symbol_name_list = symbol_name_list;
lib.long_names = parse.long_names;
return lib;
}
THREAD_POOL_TASK_FUNC(lnk_lib_initer)
{
LNK_LibIniter *task = raw_task;
LNK_LibNode *lib_node = task->node_arr + task_id;
LNK_Lib *lib = &lib_node->data;
String8 data = task->data_arr[task_id];
String8 path = task->path_arr[task_id];
*lib = lnk_lib_from_data(arena, data, path);
}
internal LNK_LibNodeArray
lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, String8Array data_arr, String8Array path_arr)
{
Assert(data_arr.count == path_arr.count);
U64 lib_count = data_arr.count;
LNK_LibIniter lib_initer = {0};
lib_initer.node_arr = lnk_lib_list_reserve(arena->v[0], list, lib_count);
lib_initer.data_arr = data_arr.v;
lib_initer.path_arr = path_arr.v;
tp_for_parallel(tp, arena, lib_count, lnk_lib_initer, &lib_initer);
LNK_LibNodeArray arr;
arr.count = lib_count;
arr.v = lib_initer.node_arr;
return arr;
}
#if 0
internal LNK_LibNode *
lnk_lib_list_push(Arena *arena, LNK_LibList *list, String8 data, String8 path)
{
ProfBeginFunction();
TP_Arena pool_arena = {0};
pool_arena.count = 1;
pool_arena.v = &arena;
String8Array data_arr = {0};
data_arr.count = 1;
data_arr.v = &data;
String8Array path_arr = {0};
path_arr.count = 1;
path_arr.v = &path;
LNK_LibNodeArray node_arr = lnk_lib_list_push_parallel(&pool_arena, list, data_arr, path_arr);
ProfEnd();
return node_arr.v;
}
#endif
////////////////////////////////
internal LNK_LibWriter *
lnk_lib_writer_alloc(void)
{
Arena *arena = arena_alloc();
LNK_LibWriter *writer = push_array(arena, LNK_LibWriter, 1);
writer->arena = arena;
return writer;
}
internal void
lnk_lib_writer_release(LNK_LibWriter **writer_ptr)
{
arena_release((*writer_ptr)->arena);
*writer_ptr = 0;
}
internal void
lnk_lib_writer_push_obj(LNK_LibWriter *writer, LNK_Obj *obj)
{
ProfBeginFunction();
U64 member_idx = writer->member_list.count;
// push obj member
LNK_LibMember member = {0};
member.name = obj->path;
member.data = obj->data;
lnk_lib_member_list_push(writer->arena, &writer->member_list, member);
// push external symbols
for (LNK_SymbolNode *node = obj->symbol_list.first; node != 0; node = node->next) {
LNK_Symbol *symbol = node->data;
B32 is_extern = symbol->type == LNK_Symbol_DefinedExtern;
if (is_extern) {
LNK_LibSymbol lib_symbol = {0};
lib_symbol.name = symbol->name;
lib_symbol.member_idx = member_idx;
lnk_lib_symbol_list_push(writer->arena, &writer->symbol_list, lib_symbol);
}
}
ProfEnd();
}
internal void
lnk_lib_writer_push_export(LNK_LibWriter *writer, COFF_MachineType machine, U64 time_stamp, String8 dll_name, LNK_Export *exp)
{
ProfBeginFunction();
U64 member_idx = writer->member_list.count;
// make import header
String8 import_data;
if (exp->name.size) {
U16 hint = safe_cast_u16(exp->id);
import_data = coff_make_import_header_by_name(writer->arena, dll_name, machine, time_stamp, exp->name, hint, exp->type);
} else {
U16 ordinal = safe_cast_u16(exp->id);
import_data = coff_make_import_header_by_ordinal(writer->arena, dll_name, machine, time_stamp, ordinal, exp->type);
}
// push import member
LNK_LibMember member = {0};
member.name = dll_name;
member.data = import_data;
lnk_lib_member_list_push(writer->arena, &writer->member_list, member);
switch (exp->type) {
case COFF_ImportHeaderType_CODE: {
LNK_LibSymbol def_symbol = {0};
def_symbol.name = push_str8_copy(writer->arena, exp->name);
def_symbol.member_idx = member_idx;
lnk_lib_symbol_list_push(writer->arena, &writer->symbol_list, def_symbol);
}
case COFF_ImportHeaderType_DATA: {
LNK_LibSymbol imp_symbol = {0};
imp_symbol.name = push_str8f(writer->arena, "__imp_%S", exp->name);
imp_symbol.member_idx = member_idx;
lnk_lib_symbol_list_push(writer->arena, &writer->symbol_list, imp_symbol);
} break;
case COFF_ImportHeaderType_CONST: {
NotImplemented;
} break;
default: InvalidPath;
}
ProfEnd();
}
internal LNK_LibBuild
lnk_lib_build_from_writer(Arena *arena, LNK_LibWriter *writer)
{
ProfBeginFunction();
LNK_LibBuild lib = {0};
lib.symbol_count = writer->symbol_list.count + 2;
lib.member_count = writer->member_list.count;
lib.symbol_array = lnk_lib_symbol_array_from_list(arena, writer->symbol_list);
lib.member_array = lnk_lib_member_array_from_list(arena, writer->member_list);
lnk_lib_symbol_array_sort(lib.symbol_array, lib.symbol_count);
ProfEnd();
return lib;
}
internal String8List
lnk_coff_archive_from_lib_build(Arena *arena, LNK_LibBuild *lib, B32 emit_second_member, COFF_TimeStamp time_stamp, U32 mode)
{
ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 1);
U64 symbol_count = lib->symbol_count - 2;
LNK_LibSymbol *symbol_arr = lib->symbol_array + 1;
HashTable *name_ht = hash_table_init(scratch.arena, 1024);
U64 *member_off_arr = push_array_no_zero(scratch.arena, U64, lib->member_count);
String8List long_names_list = {0};
String8List member_data_list = {0};
for (U64 member_idx = 0; member_idx < lib->member_count; member_idx += 1) {
LNK_LibMember *member = &lib->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_MAX_SHORT_NAME_SIZE) {
// 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_off_arr[member_idx] = member_data_list.total_size;
String8 member_data = member->data;
String8 member_header = lnk_build_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);
str8_list_push_pad(arena, &member_data_list, member_data_list.total_size, COFF_ARCHIVE_ALIGN);
}
// long names member
if (long_names_list.total_size > 0) {
String8 header = lnk_build_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;
str8_list_push_pad_front(arena, &member_data_list, member_offset, COFF_ARCHIVE_ALIGN);
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 (LNK_LibSymbol *ptr = &symbol_arr[0], *opl = ptr + symbol_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 (LNK_LibSymbol *ptr = &symbol_arr[0], *opl = ptr + symbol_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 member_base_off;
{
U64 sizeof_first_header = COFF_ARCHIVE_MEMBER_HEADER_SIZE + sizeof(U32) + sizeof(U32) * symbol_count + name_buffer_size;
U64 sizeof_second_header = COFF_ARCHIVE_MEMBER_HEADER_SIZE + sizeof(U32) + sizeof(U32) * lib->member_count + sizeof(U32) + sizeof(U16) * symbol_count + name_buffer_size;
U64 sizeof_long_names = COFF_ARCHIVE_MEMBER_HEADER_SIZE + long_names_list.total_size;
sizeof_first_header = AlignPow2(sizeof_first_header, COFF_ARCHIVE_ALIGN);
sizeof_second_header = AlignPow2(sizeof_second_header, COFF_ARCHIVE_ALIGN);
sizeof_long_names = AlignPow2(sizeof_long_names, COFF_ARCHIVE_ALIGN);
member_base_off = sizeof(g_coff_archive_sig);
member_base_off += sizeof_first_header;
if (emit_second_member) {
member_base_off += sizeof_second_header;
}
if (long_names_list.total_size > 0) {
member_base_off += sizeof_long_names;
}
}
// second linker member
if (emit_second_member) {
U32 member_count32 = safe_cast_u32(lib->member_count);
U32 symbol_count32 = safe_cast_u32(symbol_count);
U32 *member_off32_arr = push_array_no_zero(scratch.arena, U32, lib->member_count);
U16 *member_idx16_arr = push_array_no_zero(scratch.arena, U16, symbol_count);
// write member offset array
for (U64 member_idx = 0; member_idx < lib->member_count; member_idx += 1) {
U64 member_off = member_base_off + member_off_arr[member_idx];
U32 member_off32 = safe_cast_u32(member_off);
member_off32_arr[member_idx] = member_off32;
}
// write member offset indices for each symbol
for (U64 symbol_idx = 0; symbol_idx < symbol_count; symbol_idx += 1) {
// member offset indices are 1-based
U64 member_idx = symbol_arr[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, lib->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, symbol_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 = lnk_build_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;
str8_list_push_pad_front(arena, &member_data_list, member_offset, COFF_ARCHIVE_ALIGN);
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 = BE_U32(symbol_count);
U32 *member_off32_arr = push_array_no_zero(scratch.arena, U32, symbol_count);
for (U64 symbol_idx = 0; symbol_idx < symbol_count; symbol_idx += 1) {
LNK_LibSymbol *symbol = &symbol_arr[symbol_idx];
// write big endian member offset
U64 member_off = member_base_off + member_off_arr[symbol->member_idx];
U32 member_off32 = BE_U32(safe_cast_u32(member_off));
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, symbol_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 = lnk_build_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;
str8_list_push_pad_front(arena, &member_data_list, member_offset, COFF_ARCHIVE_ALIGN);
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);
ProfEnd();
return member_data_list;
}
////////////////////////////////
internal LNK_LibBuild
lnk_build_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, LNK_ObjList obj_list, LNK_ExportTable *exptab)
{
ProfBeginFunction();
LNK_LibWriter *writer = lnk_lib_writer_alloc();
for (LNK_ObjNode *obj_node = obj_list.first; obj_node != 0; obj_node = obj_node->next) {
lnk_lib_writer_push_obj(writer, &obj_node->data);
}
for (LNK_Export *exp = exptab->name_export_list.first; exp != 0; exp = exp->next) {
lnk_lib_writer_push_export(writer, machine, time_stamp, dll_name, exp);
}
LNK_LibBuild lib = lnk_lib_build_from_writer(arena, writer);
lnk_lib_writer_release(&writer);
ProfEnd();
return lib;
}
internal String8List
lnk_build_import_entry_obj(Arena *arena, String8 dll_name, COFF_MachineType machine)
{
ProfBeginFunction();
Assert(machine == COFF_MachineType_X64);
Assert(str8_match(str8_lit("dll"), str8_skip_last_dot(dll_name), StringMatchFlag_CaseInsensitive|StringMatchFlag_RightSideSloppy));
String8List list = {0};
COFF_Header *coff_header = push_array(arena, COFF_Header, 1);
coff_header->machine = machine;
str8_list_push(arena, &list, str8_struct(coff_header));
coff_header->section_count = 2;
COFF_SectionHeader *coff_sect_header_array = push_array(arena, COFF_SectionHeader, coff_header->section_count);
str8_list_push(arena, &list, str8_array(coff_sect_header_array, coff_header->section_count));
PE_ImportEntry *import_entry = push_array(arena, PE_ImportEntry, 1);
U64 import_entry_off = list.total_size;
str8_list_push(arena, &list, str8_struct(import_entry));
String8 dll_name_cstr = push_cstr(arena, dll_name);
U64 dll_name_off = list.total_size;
str8_list_push(arena, &list, dll_name_cstr);
U32 import_entry_reloc_count = 3;
COFF_Reloc *import_entry_reloc_array = push_array(arena, COFF_Reloc, import_entry_reloc_count);
U64 import_entry_reloc_off = list.total_size;
str8_list_push(arena, &list, str8_array(import_entry_reloc_array, import_entry_reloc_count));
coff_header->symbol_count = 7;
COFF_Symbol16 *symbol_array = push_array(arena, COFF_Symbol16, coff_header->symbol_count);
coff_header->symbol_table_foff = safe_cast_u32(list.total_size);
str8_list_push(arena, &list, str8_array(symbol_array, coff_header->symbol_count));
U64 string_table_base = list.total_size;
U32 *string_table_size_ptr = push_array(arena, U32, 1);
str8_list_push(arena, &list, str8_struct(string_table_size_ptr));
// PE_ImportEntry
{
COFF_SectionHeader *sect = &coff_sect_header_array[0];
sect->name[0] = '.';
sect->name[1] = 'i';
sect->name[2] = 'd';
sect->name[3] = 'a';
sect->name[4] = 't';
sect->name[5] = 'a';
sect->name[6] = '$';
sect->name[7] = '2';
sect->fsize = sizeof(PE_ImportEntry);
sect->foff = import_entry_off;
sect->reloc_count = import_entry_reloc_count;
sect->relocs_foff = import_entry_reloc_off;
sect->flags = COFF_SectionFlag_CNT_INITIALIZED_DATA|(COFF_SectionAlign_4BYTES << COFF_SectionFlag_ALIGN_SHIFT)|COFF_SectionFlag_MEM_READ|COFF_SectionFlag_MEM_WRITE;
}
{
COFF_Reloc *lookup_table_voff_reloc = &import_entry_reloc_array[0];
lookup_table_voff_reloc->apply_off = OffsetOf(PE_ImportEntry, lookup_table_voff);
lookup_table_voff_reloc->isymbol = 3;
lookup_table_voff_reloc->type = COFF_RelocTypeX64_ADDR32NB;
COFF_Reloc *name_voff_reloc = &import_entry_reloc_array[1];
name_voff_reloc->apply_off = OffsetOf(PE_ImportEntry, name_voff);
name_voff_reloc->isymbol = 2;
name_voff_reloc->type = COFF_RelocTypeX64_ADDR32NB;
COFF_Reloc *import_addr_table_voff = &import_entry_reloc_array[2];
import_addr_table_voff->apply_off = OffsetOf(PE_ImportEntry, import_addr_table_voff);
import_addr_table_voff->isymbol = 4;
import_addr_table_voff->type = COFF_RelocTypeX64_ADDR32NB;
}
// dll name
{
COFF_SectionHeader *sect = &coff_sect_header_array[1];
sect->name[0] = '.';
sect->name[1] = 'i';
sect->name[2] = 'd';
sect->name[3] = 'a';
sect->name[4] = 't';
sect->name[5] = 'a';
sect->name[6] = '$';
sect->name[7] = '6';
sect->fsize = dll_name_cstr.size;
sect->foff = dll_name_off;
sect->flags = COFF_SectionFlag_CNT_INITIALIZED_DATA|(COFF_SectionAlign_2BYTES << COFF_SectionFlag_ALIGN_SHIFT)|COFF_SectionFlag_MEM_READ|COFF_SectionFlag_MEM_WRITE;
}
// import descriptor
{
String8 dll_name_no_ext = str8_substr(dll_name, r1u64(0, dll_name.size - 4));
String8 symbol_name = push_str8f(arena, "__IMPORT_DESCRIPTOR_%S", dll_name_no_ext);
U64 symbol_name_off = (list.total_size - string_table_base);
str8_list_push(arena, &list, push_cstr(arena, symbol_name));
COFF_Symbol16 *symbol = &symbol_array[0];
symbol->name.long_name.zeroes = 0;
symbol->name.long_name.string_table_offset = symbol_name_off;
symbol->section_number = 1;
symbol->storage_class = COFF_SymStorageClass_EXTERNAL;
}
// .idata$2
{
COFF_Symbol16 *symbol = &symbol_array[1];
symbol->name.short_name[0] = '.';
symbol->name.short_name[1] = 'i';
symbol->name.short_name[2] = 'd';
symbol->name.short_name[3] = 'a';
symbol->name.short_name[4] = 't';
symbol->name.short_name[5] = 'a';
symbol->name.short_name[6] = '$';
symbol->name.short_name[7] = '2';
symbol->section_number = 1;
symbol->storage_class = COFF_SymStorageClass_SECTION;
}
// .idata$6
{
COFF_Symbol16 *symbol = &symbol_array[2];
symbol->name.short_name[0] = '.';
symbol->name.short_name[1] = 'i';
symbol->name.short_name[2] = 'd';
symbol->name.short_name[3] = 'a';
symbol->name.short_name[4] = 't';
symbol->name.short_name[5] = 'a';
symbol->name.short_name[6] = '$';
symbol->name.short_name[7] = '6';
symbol->section_number = 2;
symbol->storage_class = COFF_SymStorageClass_STATIC;
}
// .idata$4
{
COFF_Symbol16 *symbol = &symbol_array[3];
symbol->name.short_name[0] = '.';
symbol->name.short_name[1] = 'i';
symbol->name.short_name[2] = 'd';
symbol->name.short_name[3] = 'a';
symbol->name.short_name[4] = 't';
symbol->name.short_name[5] = 'a';
symbol->name.short_name[6] = '$';
symbol->name.short_name[7] = '4';
symbol->section_number = COFF_SYMBOL_UNDEFINED_SECTION;
symbol->storage_class = COFF_SymStorageClass_SECTION;
}
// .idata$5
{
COFF_Symbol16 *symbol = &symbol_array[4];
symbol->name.short_name[0] = '.';
symbol->name.short_name[1] = 'i';
symbol->name.short_name[2] = 'd';
symbol->name.short_name[3] = 'a';
symbol->name.short_name[4] = 't';
symbol->name.short_name[5] = 'a';
symbol->name.short_name[6] = '$';
symbol->name.short_name[7] = '5';
symbol->section_number = COFF_SYMBOL_UNDEFINED_SECTION;
symbol->storage_class = COFF_SymStorageClass_SECTION;
}
// __NULL_IMPORT_DESCRIPTOR
{
U64 symbol_name_off = (list.total_size - string_table_base);
str8_list_push(arena, &list, push_cstr(arena, str8_lit("__NULL_IMPORT_DESCRIPTOR")));
COFF_Symbol16 *symbol = &symbol_array[5];
symbol->name.long_name.zeroes = 0;
symbol->name.long_name.string_table_offset = symbol_name_off;
symbol->section_number = COFF_SYMBOL_UNDEFINED_SECTION;
symbol->storage_class = COFF_SymStorageClass_EXTERNAL;
}
// NULL_THUNK_DATA
{
String8 dll_name_no_ext = str8_substr(dll_name, r1u64(0, dll_name.size - 4));
String8 symbol_name = push_str8f(arena, "\x7f%S_NULL_THUNK_DATA", dll_name_no_ext);
U64 symbol_name_off = (list.total_size - string_table_base);
str8_list_push(arena, &list, push_cstr(arena, symbol_name));
COFF_Symbol16 *symbol = &symbol_array[6];
symbol->name.long_name.zeroes = 0;
symbol->name.long_name.string_table_offset = symbol_name_off;
symbol->section_number = COFF_SYMBOL_UNDEFINED_SECTION;
symbol->storage_class = COFF_SymStorageClass_EXTERNAL;
}
// update string table size
*string_table_size_ptr = (list.total_size - string_table_base);
ProfEnd();
return list;
}
internal String8List
lnk_build_null_import_descriptor_obj(Arena *arena, COFF_MachineType machine)
{
ProfBeginFunction();
String8List list = {0};
COFF_Header *coff_header = push_array(arena, COFF_Header, 1);
coff_header->machine = machine;
str8_list_push(arena, &list, str8_struct(coff_header));
coff_header->section_count = 1;
COFF_SectionHeader *coff_sect_header_array = push_array(arena, COFF_SectionHeader, coff_header->section_count);
str8_list_push(arena, &list, str8_array(coff_sect_header_array, coff_header->section_count));
U64 null_import_data_size = 20;
U8 *null_import_data = push_array(arena, U8, null_import_data_size);
U64 null_import_data_off = list.total_size;
str8_list_push(arena, &list, str8(null_import_data, null_import_data_size));
coff_header->symbol_count = 1;
COFF_Symbol16 *symbol_array = push_array(arena, COFF_Symbol16, coff_header->symbol_count);
coff_header->symbol_table_foff = safe_cast_u32(list.total_size);
str8_list_push(arena, &list, str8_array(symbol_array, coff_header->symbol_count));
U64 string_table_base = list.total_size;
U32 *string_table_size_ptr = push_array(arena, U32, 1);
str8_list_push(arena, &list, str8_struct(string_table_size_ptr));
{
COFF_SectionHeader *sect = &coff_sect_header_array[0];
sect->name[0] = '.';
sect->name[1] = 'i';
sect->name[2] = 'd';
sect->name[3] = 'a';
sect->name[4] = 't';
sect->name[5] = 'a';
sect->name[6] = '$';
sect->name[7] = '3';
sect->fsize = null_import_data_size;
sect->foff = null_import_data_off;
sect->flags = COFF_SectionFlag_CNT_INITIALIZED_DATA|(COFF_SectionAlign_4BYTES << COFF_SectionFlag_ALIGN_SHIFT)|COFF_SectionFlag_MEM_READ|COFF_SectionFlag_MEM_WRITE;
}
{
U64 symbol_name_off = list.total_size - string_table_base;
str8_list_push(arena, &list, push_cstr(arena, str8_lit("__NULL_IMPORT_DESCRIPTOR")));
COFF_Symbol16 *symbol = &symbol_array[0];
symbol->name.long_name.zeroes = 0;
symbol->name.long_name.string_table_offset = symbol_name_off;
symbol->section_number = 1;
symbol->storage_class = COFF_SymStorageClass_EXTERNAL;
}
// update string table size
*string_table_size_ptr = (list.total_size - string_table_base);
ProfEnd();
return list;
}
internal String8List
lnk_build_null_thunk_data_obj(Arena *arena, String8 dll_name, COFF_MachineType machine)
{
ProfBeginFunction();
Assert(str8_match(str8_lit("dll"), str8_skip_last_dot(dll_name), StringMatchFlag_CaseInsensitive|StringMatchFlag_RightSideSloppy));
String8List list = {0};
COFF_Header *coff_header = push_array(arena, COFF_Header, 1);
coff_header->machine = machine;
str8_list_push(arena, &list, str8_struct(coff_header));
coff_header->section_count = 2;
COFF_SectionHeader *coff_sect_header_array = push_array(arena, COFF_SectionHeader, coff_header->section_count);
str8_list_push(arena, &list, str8_array(coff_sect_header_array, coff_header->section_count));
U64 lookup_entry_data_size = 8;
U8 *lookup_entry_data = push_array(arena, U8, lookup_entry_data_size);
U64 lookup_entry_data_off = list.total_size;
str8_list_push(arena, &list, str8(lookup_entry_data, lookup_entry_data_size));
U64 null_thunk_data_size = 8;
U8 *null_thunk_data = push_array(arena, U8, null_thunk_data_size);
U64 null_thunk_data_off = list.total_size;
str8_list_push(arena, &list, str8(null_thunk_data, null_thunk_data_size));
coff_header->symbol_count = 1;
COFF_Symbol16 *symbol_array = push_array(arena, COFF_Symbol16, coff_header->symbol_count);
coff_header->symbol_table_foff = safe_cast_u32(list.total_size);
str8_list_push(arena, &list, str8_array(symbol_array, coff_header->symbol_count));
U64 string_table_base = list.total_size;
U32 *string_table_size_ptr = push_array(arena, U32, 1);
str8_list_push(arena, &list, str8_struct(string_table_size_ptr));
{
COFF_SectionHeader *sect = &coff_sect_header_array[0];
sect->name[0] = '.';
sect->name[1] = 'i';
sect->name[2] = 'd';
sect->name[3] = 'a';
sect->name[4] = 't';
sect->name[5] = 'a';
sect->name[6] = '$';
sect->name[7] = '5';
sect->fsize = lookup_entry_data_size;
sect->foff = lookup_entry_data_off;
sect->flags = COFF_SectionFlag_CNT_INITIALIZED_DATA|(COFF_SectionAlign_8BYTES << COFF_SectionFlag_ALIGN_SHIFT)|COFF_SectionFlag_MEM_READ|COFF_SectionFlag_MEM_WRITE;
}
{
COFF_SectionHeader *sect = &coff_sect_header_array[1];
sect->name[0] = '.';
sect->name[1] = 'i';
sect->name[2] = 'd';
sect->name[3] = 'a';
sect->name[4] = 't';
sect->name[5] = 'a';
sect->name[6] = '$';
sect->name[7] = '4';
sect->fsize = null_thunk_data_size;
sect->foff = null_thunk_data_off;
sect->flags = COFF_SectionFlag_CNT_INITIALIZED_DATA|(COFF_SectionAlign_8BYTES << COFF_SectionFlag_ALIGN_SHIFT)|COFF_SectionFlag_MEM_READ|COFF_SectionFlag_MEM_WRITE;
}
{
String8 dll_name_no_ext = str8_substr(dll_name, r1u64(0, dll_name.size - 4));
String8 symbol_name = push_str8f(arena, "\x7f%S_NULL_THUNK_DATA", dll_name_no_ext);
U64 symbol_name_off = list.total_size - string_table_base;
str8_list_push(arena, &list, push_cstr(arena, symbol_name));
COFF_Symbol16 *symbol = &symbol_array[0];
symbol->name.long_name.zeroes = 0;
symbol->name.long_name.string_table_offset = symbol_name_off;
symbol->section_number = 1;
symbol->storage_class = COFF_SymStorageClass_EXTERNAL;
}
// update string table size
*string_table_size_ptr = (list.total_size - string_table_base);
ProfEnd();
return list;
}
internal String8
lnk_build_lib_member_header(Arena *arena, String8 name, COFF_TimeStamp time_stamp, U16 user_id, U16 group_id, U16 mode, U32 size)
{
ProfBeginFunction();
Assert(name.size < 16);
Assert(user_id < 10000);
Assert(group_id < 10000);
Assert(mode < 10000);
Assert(size < 1000000000);
//U64 sizeof_member_header = /* name */ 16 + /* time */ 12 + /* user_id */ 6 + /* group id */ 6 + /* mode */ 8 + /* size */ 10;
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 == COFF_ARCHIVE_MEMBER_HEADER_SIZE);
scratch_end(scratch);
ProfEnd();
return result;
}
internal String8List
lnk_build_import_lib(TP_Context *tp, TP_Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 lib_name, String8 dll_name, LNK_ExportTable *exptab)
{
ProfBeginFunction();
Temp scratch = scratch_begin(arena->v, arena->count);
dll_name = str8_skip_last_slash(dll_name);
// 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.
String8List import_obj_array[3];
import_obj_array[0] = lnk_build_import_entry_obj(scratch.arena, dll_name, machine);
import_obj_array[1] = lnk_build_null_import_descriptor_obj(scratch.arena, machine);
import_obj_array[2] = lnk_build_null_thunk_data_obj(scratch.arena, dll_name, machine);
// build input list
LNK_InputObjList input_obj_list = {0};
for (U64 i = 0; i < ArrayCount(import_obj_array); ++i) {
LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list);
input->data = str8_list_join(scratch.arena, &import_obj_array[i], 0);
input->path = dll_name;
input->lib_path = lib_name;
}
LNK_InputObj **inputs = lnk_array_from_input_obj_list(scratch.arena, input_obj_list);
LNK_SectionTable *st = lnk_section_table_alloc(0,0,0);
LNK_ObjList obj_list = {0};
lnk_obj_list_push_parallel(tp, arena, &obj_list, st, input_obj_list.count, inputs);
LNK_LibBuild import_lib = lnk_build_lib(scratch.arena, machine, time_stamp, dll_name, obj_list, exptab);
B32 emit_second_member = 0; // MSVC linker refuses to link with lib that has the second member.
String8List coff_archive_data = lnk_coff_archive_from_lib_build(arena->v[0], &import_lib, emit_second_member, time_stamp, /* -rw-r--r-- */ 644);
// cleanup memory
lnk_section_table_release(&st);
scratch_end(scratch);
ProfEnd();
return coff_archive_data;
}
+134
View File
@@ -0,0 +1,134 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef struct LNK_Lib
{
String8 path;
String8 data;
COFF_ArchiveType type;
U32 symbol_count;
U32 * member_off_arr;
String8List symbol_name_list;
String8 long_names;
} LNK_Lib;
typedef struct LNK_LibNode
{
struct LNK_LibNode *next;
LNK_Lib data;
} LNK_LibNode;
typedef struct LNK_LibNodeArray
{
U64 count;
LNK_LibNode *v;
} LNK_LibNodeArray;
typedef struct LNK_LibList
{
U64 count;
struct LNK_LibNode *first;
struct LNK_LibNode *last;
} LNK_LibList;
////////////////////////////////
typedef struct LNK_LibMember
{
String8 name;
String8 data;
} LNK_LibMember;
typedef struct LNK_LibMemberNode
{
struct LNK_LibMemberNode *next;
LNK_LibMember data;
} LNK_LibMemberNode;
typedef struct LNK_LibMemberList
{
U64 count;
LNK_LibMemberNode *first;
LNK_LibMemberNode *last;
} LNK_LibMemberList;
typedef struct LNK_LibSymbol
{
String8 name;
U64 member_idx;
} LNK_LibSymbol;
typedef struct LNK_LibSymbolNode
{
struct LNK_LibSymbolNode *next;
LNK_LibSymbol data;
} LNK_LibSymbolNode;
typedef struct LNK_LibSymbolList
{
U64 count;
LNK_LibSymbolNode *first;
LNK_LibSymbolNode *last;
} LNK_LibSymbolList;
typedef struct LNK_LibWriter
{
Arena *arena;
LNK_LibMemberList member_list;
LNK_LibSymbolList symbol_list;
} LNK_LibWriter;
typedef struct LNK_LibBuild
{
U64 symbol_count;
U64 member_count;
LNK_LibSymbol *symbol_array;
LNK_LibMember *member_array;
} LNK_LibBuild;
////////////////////////////////
typedef struct
{
LNK_LibNode *node_arr;
String8 *data_arr;
String8 *path_arr;
} LNK_LibIniter;
////////////////////////////////
internal LNK_LibNode * lnk_lib_list_reserve(Arena *arena, LNK_LibList *list, U64 count);
internal LNK_LibMemberNode * lnk_lib_member_list_push(Arena *arena, LNK_LibMemberList *list, LNK_LibMember member);
internal LNK_LibMember * lnk_lib_member_array_from_list(Arena *arena, LNK_LibMemberList list);
internal LNK_LibSymbolNode * lnk_lib_symbol_list_push(Arena *arena, LNK_LibSymbolList *list, LNK_LibSymbol symbol);
internal LNK_LibSymbol * lnk_lib_symbol_array_from_list(Arena *arena, LNK_LibSymbolList list);
internal void lnk_lib_symbol_array_sort(LNK_LibSymbol *arr, U64 count);
////////////////////////////////
internal LNK_Lib lnk_lib_from_data(Arena *arena, String8 data, String8 path);
internal LNK_LibNodeArray lnk_lib_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_LibList *list, String8Array data_arr, String8Array path_arr);
internal LNK_LibNode * lnk_lib_list_push(Arena *arena, LNK_LibList *list, String8 data, String8 path);
////////////////////////////////
internal LNK_LibWriter * lnk_lib_writer_alloc(void);
internal void lnk_lib_writer_release(LNK_LibWriter **writer_ptr);
internal void lnk_lib_writer_push_obj(LNK_LibWriter *writer, LNK_Obj *obj);
internal void lnk_lib_writer_push_export(LNK_LibWriter *writer, COFF_MachineType machine, U64 time_stamp, String8 dll_name, LNK_Export *exp);
internal LNK_LibBuild lnk_lib_build_from_writer(Arena *arena, LNK_LibWriter *writer);
internal String8List lnk_coff_archive_from_lib_build(Arena *arena, LNK_LibBuild *lib, B32 emit_second_member, COFF_TimeStamp time_stamp, U32 mode);
////////////////////////////////
internal LNK_LibBuild lnk_build_lib(Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, LNK_ObjList obj_list, LNK_ExportTable *exptab);
internal String8List lnk_build_import_entry_obj(Arena *arena, String8 dll_name, COFF_MachineType machine);
internal String8List lnk_build_null_import_descriptor_obj(Arena *arena, COFF_MachineType machine);
internal String8List lnk_build_null_thunk_data_obj(Arena *arena, String8 dll_name, COFF_MachineType machine);
internal String8 lnk_build_lib_member_header(Arena *arena, String8 name, COFF_TimeStamp time_stamp, U16 user_id, U16 group_id, U16 mode, U32 size);
internal String8List lnk_build_import_lib(TP_Context *tp, TP_Arena *arena, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 lib_name, String8 dll_name, LNK_ExportTable *exptab);
+59
View File
@@ -0,0 +1,59 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal void
lnk_set_log_status(LNK_LogType type, B32 is_enabled)
{
g_log_status[type] = is_enabled;
}
internal B32
lnk_get_log_status(LNK_LogType type)
{
B32 status = g_log_status[type];
return status;
}
internal void
lnk_log(LNK_LogType type, char *fmt, ...)
{
B32 is_log_enabled = g_log_status[type];
if (is_log_enabled) {
Temp scratch = scratch_begin(0,0);
va_list args;
va_start(args, fmt);
String8 string = push_str8fv(scratch.arena, fmt, args);
fprintf(stdout, "%.*s\n", str8_varg(string));
va_end(args);
scratch_end(scratch);
}
}
internal LNK_LogType
lnk_log_type_from_string(String8 string)
{
static struct {
char *name;
LNK_LogType type;
} map[] = {
"Null", LNK_Log_Null,
"Debug", LNK_Log_Debug,
"InputObj", LNK_Log_InputObj,
"InputLib", LNK_Log_InputLib,
"IO", LNK_Log_IO,
"SizeBreakdown", LNK_Log_SizeBreakdown,
"LinkStats", LNK_Log_LinkStats,
"Timers", LNK_Log_Timers,
};
Assert(ArrayCount(map) == LNK_Log_Count);
for (U64 i = 0; i < ArrayCount(map); ++i) {
if (str8_match(str8_cstring(map[i].name), string, StringMatchFlag_CaseInsensitive)) {
return map[i].type;
}
}
return LNK_Log_Null;
}
+24
View File
@@ -0,0 +1,24 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef enum
{
LNK_Log_Null,
LNK_Log_Debug,
LNK_Log_InputObj,
LNK_Log_InputLib,
LNK_Log_IO,
LNK_Log_SizeBreakdown,
LNK_Log_LinkStats,
LNK_Log_Timers,
LNK_Log_Count
} LNK_LogType;
internal void set_log_level(LNK_LogType type, B32 is_enabled);
internal B32 lnk_get_log_status(LNK_LogType type);
internal void lnk_log(LNK_LogType type, char *fmt, ...);
internal LNK_LogType lnk_log_type_from_string(String8 string);
+940
View File
@@ -0,0 +1,940 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
internal void
lnk_error_obj(LNK_ErrorCode code, LNK_Obj *obj, char *fmt, ...)
{
Temp scratch = scratch_begin(0, 0);
va_list args;
va_start(args, fmt);
String8 text = push_str8fv(scratch.arena, fmt, args);
if (obj->lib_path.size) {
lnk_error(code, "%S(%S): %S", obj->lib_path, obj->path, text);
} else {
lnk_error(code, "%S: %S", obj->path, text);
}
va_end(args);
scratch_end(scratch);
}
////////////////////////////////
internal void
lnk_input_obj_list_push_node(LNK_InputObjList *list, LNK_InputObj *node)
{
SLLQueuePush(list->first, list->last, node);
++list->count;
}
internal LNK_InputObj *
lnk_input_obj_list_push(Arena *arena, LNK_InputObjList *list)
{
LNK_InputObj *node = push_array(arena, LNK_InputObj, 1);
lnk_input_obj_list_push_node(list, node);
return node;
}
internal LNK_InputObj **
lnk_array_from_input_obj_list(Arena *arena, LNK_InputObjList list)
{
LNK_InputObj **result = push_array_no_zero(arena, LNK_InputObj *, list.count);
U64 i = 0;
for (LNK_InputObj *n = list.first; n != 0; n = n->next, ++i) {
Assert(i < list.count);
result[i] = n;
}
return result;
}
internal void
lnk_input_obj_list_concat_in_place(LNK_InputObjList *list, LNK_InputObjList *to_concat)
{
SLLConcatInPlace(list, to_concat);
}
internal int
lnk_input_obj_compar(const void *raw_a, const void *raw_b)
{
const LNK_InputObj **a = (const LNK_InputObj **) raw_a;
const LNK_InputObj **b = (const LNK_InputObj **) raw_b;
int cmp = str8_compar_case_sensetive(&(*a)->path, &(*b)->path);
return cmp;
}
internal int
lnk_input_obj_compar_is_before(void *raw_a, void *raw_b)
{
LNK_InputObj **a = raw_a;
LNK_InputObj **b = raw_b;
int cmp = str8_compar_case_sensetive(&(*a)->path, &(*b)->path);
int is_before = cmp < 0;
return is_before;
}
internal LNK_InputObjList
lnk_list_from_input_obj_arr(LNK_InputObj **arr, U64 count)
{
LNK_InputObjList list = {0};
for (U64 i = 0; i < count; ++i) {
SLLQueuePush(list.first, list.last, arr[i]);
++list.count;
}
return list;
}
internal LNK_InputObjList
lnk_input_obj_list_from_string_list(Arena *arena, String8List list)
{
LNK_InputObjList input_list = {0};
for (String8Node *path = list.first; path != 0; path = path->next) {
LNK_InputObj *input = lnk_input_obj_list_push(arena, &input_list);
input->is_thin = 1;
input->dedup_id = path->string;
input->path = path->string;
}
return input_list;
}
////////////////////////////////
internal LNK_Obj **
lnk_obj_arr_from_list(Arena *arena, LNK_ObjList list)
{
LNK_Obj **arr = push_array_no_zero(arena, LNK_Obj *, list.count);
U64 idx = 0;
for (LNK_ObjNode *node = list.first; node != 0; node = node->next, ++idx) {
arr[idx] = &node->data;
}
return arr;
}
internal LNK_ObjNodeArray
lnk_obj_list_reserve(Arena *arena, LNK_ObjList *list, U64 count)
{
LNK_ObjNodeArray arr = {0};
if (count) {
arr.count = count;
arr.v = push_array(arena, LNK_ObjNode, count);
for (LNK_ObjNode *ptr = arr.v, *opl = arr.v + arr.count; ptr < opl; ++ptr) {
SLLQueuePush(list->first, list->last, ptr);
}
list->count += count;
} else {
MemoryZeroStruct(&arr);
}
return arr;
}
internal LNK_ChunkList
lnk_obj_search_chunks(Arena *arena, LNK_Obj *obj, String8 name, String8 postfix, B32 collect_discarded)
{
LNK_ChunkList list = {0};
for (U64 sect_idx = 0; sect_idx < obj->chunk_count; ++sect_idx) {
String8 obj_sect_name = obj->sect_name_arr[sect_idx];
String8 obj_sect_sort = obj->sect_sort_arr[sect_idx];
B32 is_match = str8_match(obj_sect_name, name, 0) &&
str8_match(obj_sect_sort, postfix, 0);
if (is_match) {
LNK_ChunkPtr chunk = &obj->chunk_arr[sect_idx];
if (!collect_discarded && lnk_chunk_is_discarded(chunk)) {
continue;
}
LNK_ChunkNode *node = push_array_no_zero(arena, LNK_ChunkNode, 1);
node->next = 0;
node->data = chunk;
SLLQueuePush(list.first, list.last, node);
++list.count;
}
}
return list;
}
internal
THREAD_POOL_TASK_FUNC(lnk_collect_obj_chunks_task)
{
U64 obj_idx = task_id;
LNK_CollectObjChunksTaskData *task = raw_task;
LNK_Obj *obj = task->obj_arr[obj_idx];
LNK_ChunkList *list_ptr = &task->list_arr[obj_idx];
*list_ptr = lnk_obj_search_chunks(arena, obj, task->name, task->postfix, task->collect_discarded);
}
internal LNK_ChunkList *
lnk_collect_obj_chunks(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8 name, String8 postfix, B32 collect_discarded)
{
LNK_CollectObjChunksTaskData task_data = {0};
task_data.obj_arr = obj_arr;
task_data.name = name;
task_data.postfix = postfix;
task_data.list_arr = push_array_no_zero(arena->v[0], LNK_ChunkList, obj_count);
task_data.collect_discarded = collect_discarded;
tp_for_parallel(tp, arena, obj_count, lnk_collect_obj_chunks_task, &task_data);
return task_data.list_arr;
}
internal
THREAD_POOL_TASK_FUNC(lnk_symbol_collector)
{
LNK_SymbolCollector *task = raw_task;
Rng1U64 range = task->range_arr[task_id];
LNK_SymbolList *list = &task->out_arr[task_id];
for (U64 obj_idx = range.min; obj_idx < range.max; ++obj_idx) {
LNK_Obj *obj = &task->in_arr.v[obj_idx].data;
for (LNK_SymbolNode *node = obj->symbol_list.first; node != 0; node = node->next) {
if (node->data->type == task->type) {
lnk_symbol_list_push(arena, list, node->data);
}
}
}
}
internal LNK_SymbolList
lnk_run_symbol_collector(TP_Context *tp, TP_Arena *arena, LNK_ObjNodeArray arr, LNK_SymbolType symbol_type)
{
ProfBeginFunction();
Temp scratch = scratch_begin(0,0);
LNK_SymbolCollector task_data;
task_data.type = symbol_type;
task_data.range_arr = tp_divide_work(scratch.arena, arr.count, tp->worker_count);
task_data.in_arr = arr;
task_data.out_arr = push_array(scratch.arena, LNK_SymbolList, tp->worker_count);
tp_for_parallel(tp, arena, tp->worker_count, lnk_symbol_collector, &task_data);
LNK_SymbolList list = {0};
for (U64 ithread = 0; ithread < tp->worker_count; ++ithread) {
lnk_symbol_list_concat_in_place(&list, &task_data.out_arr[ithread]);
}
scratch_end(scratch);
ProfEnd();
return list;
}
internal
THREAD_POOL_TASK_FUNC(lnk_default_lib_collector)
{
LNK_DefaultLibCollector *task = raw_task;
Rng1U64 range = task->range_arr[task_id];
String8List *result = &task->out_arr[task_id];
for (U64 obj_idx = range.min; obj_idx < range.max; obj_idx += 1) {
LNK_Obj *obj = &task->in_arr.v[obj_idx].data;
String8List list = lnk_parse_default_lib_directive(arena, &obj->directive_info.v[LNK_Directive_DefaultLib]);
str8_list_concat_in_place(result, &list);
}
}
internal LNK_InputLibList
lnk_collect_default_lib_obj_arr(TP_Context *tp, TP_Arena *arena, LNK_ObjNodeArray arr)
{
Temp scratch = scratch_begin(0,0);
LNK_DefaultLibCollector task_data;
task_data.range_arr = tp_divide_work(scratch.arena, arr.count, tp->worker_count);
task_data.in_arr = arr;
task_data.out_arr = push_array(scratch.arena, LNK_InputLibList, tp->worker_count);
tp_for_parallel(tp, arena, tp->worker_count, lnk_default_lib_collector, &task_data);
String8List result = str8_list_arr_concat(task_data.out_arr, tp->worker_count);
scratch_end(scratch);
return result;
}
internal
THREAD_POOL_TASK_FUNC(lnk_manifest_dependency_collector)
{
LNK_ManifestDependencyCollector *task = raw_task;
Rng1U64 range = task->range_arr[task_id];
String8List *list = &task->out_arr[task_id];
LNK_Obj **obj_ptr = &task->in_arr[range.min];
LNK_Obj **obj_opl = &task->in_arr[range.max];
for (; obj_ptr < obj_opl; obj_ptr += 1) {
LNK_Obj *obj = *obj_ptr;
LNK_DirectiveList *dirs = &obj->directive_info.v[LNK_Directive_ManifestDependency];
for (LNK_Directive *dir = dirs->first; dir != 0; dir = dir->next) {
String8List dep = str8_list_copy(arena, &dir->value_list);
str8_list_concat_in_place(list, &dep);
}
}
}
internal String8List
lnk_collect_manifest_dependency_list(TP_Context *tp, TP_Arena *arena, LNK_Obj **obj_arr, U64 obj_count)
{
Temp scratch = scratch_begin(0,0);
LNK_ManifestDependencyCollector task_data = {0};
task_data.in_arr = obj_arr;
task_data.out_arr = push_array(scratch.arena, String8List, tp->worker_count);
task_data.range_arr = tp_divide_work(scratch.arena, obj_count, tp->worker_count);
tp_for_parallel(tp, arena, tp->worker_count, lnk_manifest_dependency_collector, &task_data);
String8List result = str8_list_arr_concat(task_data.out_arr, tp->worker_count);
scratch_end(scratch);
return result;
}
internal void
lnk_sect_defn_list_push_node(LNK_SectDefnList *list, LNK_SectDefn *node)
{
SLLQueuePush(list->first, list->last, node);
++list->count;
}
internal LNK_SectDefn *
lnk_sect_defn_list_push(Arena *arena, LNK_SectDefnList *list, LNK_Obj *obj, String8 name, U64 idx, COFF_SectionFlags flags)
{
LNK_SectDefn *node = push_array_no_zero(arena, LNK_SectDefn, 1);
node->next = 0;
node->obj = obj;
node->name = name;
node->idx = idx;
node->flags = flags;
lnk_sect_defn_list_push_node(list, node);
return node;
}
internal void
lnk_sect_defn_list_concat_in_place(LNK_SectDefnList *list, LNK_SectDefnList *to_concat)
{
SLLConcatInPlace(list, to_concat);
}
internal void
lnk_sect_defn_list_concat_in_place_arr(LNK_SectDefnList *list, LNK_SectDefnList *to_concat_arr, U64 count)
{
SLLConcatInPlaceArray(list, to_concat_arr, count);
}
internal
THREAD_POOL_TASK_FUNC(lnk_obj_initer)
{
Temp scratch = scratch_begin(&arena, 1);
LNK_ObjIniter *task = raw_task;
LNK_InputObj *input = task->inputs[task_id];
U64 obj_idx = task->obj_id_base + task_id;
LNK_ObjNode *obj_node = task->obj_node_arr + task_id;
LNK_Obj *obj = &obj_node->data;
//Assert(coff_data.size > 0);
// cache path, we need it for error reports and debug stuff
String8 cached_path = push_str8_copy(arena, input->path);
String8 cached_lib_path = push_str8_copy(arena, input->lib_path);
// parse coff obj
COFF_HeaderInfo coff_info = coff_header_info_from_data(input->data);
COFF_SectionHeader *coff_sect_arr = (COFF_SectionHeader *)(input->data.str + coff_info.section_array_off);
COFF_Symbol32Array coff_symbols = coff_symbol_array_from_data(scratch.arena, input->data, coff_info.symbol_off, coff_info.symbol_count, coff_info.symbol_size);
// handle machines we dont support
if (coff_info.machine != COFF_MachineType_UNKNOWN &&
coff_info.machine != COFF_MachineType_X64) {
lnk_error(LNK_Error_UnsupportedMachine, "%S: %S machine is supported", input->path, coff_string_from_machine_type(coff_info.machine));
}
U64 chunk_count = 0;
chunk_count += coff_info.section_count_no_null;
chunk_count += 1; // :common_block
String8 *sect_name_arr = push_array_no_zero(arena, String8, chunk_count);
String8 *sect_sort_arr = push_array_no_zero(arena, String8, chunk_count);
LNK_Chunk *chunk_arr = push_array_no_zero(arena, LNK_Chunk, chunk_count);
// init section name and postfix array
for (U64 sect_idx = 0; sect_idx < coff_info.section_count_no_null; sect_idx += 1) {
COFF_SectionHeader *coff_sect = &coff_sect_arr[sect_idx];
// read name
String8 sect_name = coff_section_header_get_name(coff_sect, input->data, coff_info.string_table_off);
// parse section name
String8 name, postfix;
coff_parse_section_name(sect_name, &name, &postfix);
// fill out
sect_name_arr[sect_idx] = name;
sect_sort_arr[sect_idx] = postfix;
}
// :common_block
U64 common_block_idx = chunk_count - 1;
sect_name_arr[common_block_idx] = str8_lit(".bss");
sect_sort_arr[common_block_idx] = str8_lit("~");
for (U64 sect_idx = 0; sect_idx < coff_info.section_count_no_null; sect_idx += 1) {
COFF_SectionHeader *coff_sect = &coff_sect_arr[sect_idx];
String8 data;
if (coff_sect->flags & COFF_SectionFlag_CNT_UNINITIALIZED_DATA) {
data = str8(0, coff_sect->fsize);
} else {
data = str8(input->data.str + coff_sect->foff, coff_sect->fsize);
}
LNK_Chunk *chunk = &chunk_arr[sect_idx];
chunk->ref = lnk_chunk_ref(0,0); // :chunk_ref_assign
chunk->align = coff_align_size_from_section_flags(coff_sect->flags);
chunk->is_discarded = !!(coff_sect->flags & COFF_SectionFlag_LNK_REMOVE);
chunk->sort_chunk = 1;
chunk->type = LNK_Chunk_Leaf;
chunk->sort_idx = sect_sort_arr[sect_idx];
chunk->input_idx = LNK_MakeChunkInputIdx(obj_idx, sect_idx);
chunk->flags = coff_sect->flags;
chunk->associate = 0;
chunk->u.leaf = data;
lnk_chunk_set_debugf(arena, chunk, "%S: name: %S, isect: 0x%llX", path, sect_name_arr[sect_idx], sect_idx);
}
// :common_block
LNK_Chunk *master_common_block = &chunk_arr[common_block_idx];
master_common_block->ref = lnk_chunk_ref(0,0); // :chunk_ref_assign
master_common_block->align = 1;
master_common_block->is_discarded = 0;
master_common_block->sort_chunk = 0;
master_common_block->type = LNK_Chunk_List;
master_common_block->sort_idx = sect_sort_arr[common_block_idx];
master_common_block->input_idx = LNK_MakeChunkInputIdx(obj_idx, common_block_idx);
master_common_block->flags = LNK_BSS_SECTION_FLAGS;
master_common_block->associate = 0;
master_common_block->u.list = push_array(arena, LNK_ChunkList, 1);
lnk_chunk_set_debugf(arena, master_common_block, "%S: master common block", path);
// convert from coff
LNK_SymbolArray symbol_arr = lnk_symbol_array_from_coff(arena, input->data, cached_path, coff_info.string_table_off, coff_info.section_count_no_null, coff_sect_arr, coff_symbols, chunk_arr, master_common_block);
LNK_SymbolList symbol_list = lnk_symbol_list_from_array(arena, symbol_arr);
LNK_RelocList *reloc_list_arr = lnk_reloc_list_array_from_coff(arena, coff_info.machine, input->data, coff_info.section_count_no_null, coff_sect_arr, chunk_arr, symbol_arr);
// fill out obj
obj->data = input->data;
obj->path = cached_path;
obj->lib_path = cached_lib_path;
obj->machine = coff_info.machine;
obj->chunk_count = chunk_count;
obj->sect_count = coff_info.section_count_no_null;
obj->sect_name_arr = sect_name_arr;
obj->sect_sort_arr = sect_sort_arr;
obj->chunk_arr = chunk_arr;
obj->symbol_list = symbol_list;
obj->sect_reloc_list_arr = reloc_list_arr;
obj->directive_info = lnk_init_directives(arena, cached_path, coff_info.section_count_no_null, sect_name_arr, chunk_arr);
// parse exports
LNK_ExportParseList export_parse = {0};
for (LNK_Directive *dir = obj->directive_info.v[LNK_Directive_Export].first; dir != 0; dir = dir->next) {
lnk_parse_export_direcive(arena, &obj->export_parse, dir->value_list, obj);
}
// push /export symbols
for (LNK_ExportParse *exp = export_parse.first; exp != 0; exp = exp->next) {
LNK_Symbol *symbol = lnk_make_undefined_symbol(arena, exp->name, LNK_SymbolScopeFlag_Main);
lnk_symbol_list_push(arena, &obj->symbol_list, symbol);
}
// push /include symbols
for (LNK_Directive *dir = obj->directive_info.v[LNK_Directive_Include].first; dir != 0; dir = dir->next) {
str8_list_concat_in_place(&obj->include_symbol_list, &dir->value_list);
}
// parse /alternatename
for (LNK_Directive *dir = obj->directive_info.v[LNK_Directive_AlternateName].first; dir != 0; dir = dir->next) {
String8 *invalid_string = lnk_parse_alt_name_directive_list(arena, dir->value_list, &obj->alt_name_list);
if (invalid_string != 0) {
lnk_error_obj(LNK_Error_Cmdl, obj, "invalid syntax \"%S\", expected format \"FROM=TO\"", *invalid_string);
}
}
scratch_end(scratch);
}
internal
THREAD_POOL_TASK_FUNC(lnk_obj_new_sect_scanner)
{
LNK_ObjNewSectScanner *task = raw_task;
Rng1U64 range = task->range_arr[task_id];
HashTable *ht = hash_table_init(arena, 128);
for (U64 obj_idx = range.min; obj_idx < range.max; obj_idx += 1) {
LNK_Obj *obj = &task->obj_node_arr[obj_idx].data;
for (U64 chunk_idx = 0; chunk_idx < obj->chunk_count; chunk_idx += 1) {
String8 sect_name = obj->sect_name_arr[chunk_idx];
COFF_SectionFlags sect_flags = obj->chunk_arr[chunk_idx].flags & ~COFF_SectionFlags_LNK_FLAGS;
KeyValuePair *is_present = hash_table_search_string(ht, sect_name);
if (is_present) {
if (lnk_is_error_code_active(LNK_Warning_SectionFlagsConflict)) {
LNK_SectDefn *defn = is_present->value_raw;
if (defn->flags != sect_flags) {
lnk_sect_defn_list_push(arena, &task->defn_arr[task_id], obj, sect_name, chunk_idx, sect_flags);
}
}
} else {
LNK_SectDefn *defn = lnk_sect_defn_list_push(arena, &task->defn_arr[task_id], obj, sect_name, chunk_idx, sect_flags);
hash_table_push_string_raw(arena, ht, sect_name, defn);
}
}
}
}
LNK_CHUNK_VISITOR_SIG(lnk_chunk_get_count_cb)
{
U64 *counter = (U64 *)ud;
*counter += 1;
return 0;
}
internal
THREAD_POOL_TASK_FUNC(lnk_chunk_counter)
{
U64 obj_idx = task_id;
LNK_ChunkCounter *task = raw_task;
LNK_Obj *obj = &task->obj_arr[obj_idx].data;
for (U64 chunk_idx = 0; chunk_idx < obj->chunk_count; chunk_idx += 1) {
String8 name = obj->sect_name_arr[chunk_idx];
LNK_Chunk *chunk = &obj->chunk_arr[chunk_idx];
LNK_Section *sect = lnk_section_table_search(task->st, name);
U64 count = 0;
lnk_visit_chunks(0, chunk, lnk_chunk_get_count_cb, &count);
task->chunk_count_arr_arr[sect->id][obj_idx] += count;
}
}
internal
LNK_CHUNK_VISITOR_SIG(lnk_chunk_ref_assign)
{
LNK_ChunkRefAssign *ctx = ud;
// alloc chunk id
U64 chunk_id = ctx->chunk_id_arr_arr[sect_id][ctx->obj_idx];
ctx->chunk_id_arr_arr[sect_id][ctx->obj_idx] += 1;
// set chunk ref
chunk->ref = lnk_chunk_ref(sect_id, chunk_id);
// keep visiting chunks
return 0;
}
internal
THREAD_POOL_TASK_FUNC(lnk_chunk_ref_assigner)
{
LNK_ChunkRefAssigner *task = raw_task;
Rng1U64 range = task->range_arr[task_id];
for (U64 obj_idx = range.min; obj_idx < range.max; obj_idx += 1) {
LNK_Obj *obj = &task->obj_arr[obj_idx].data;
for (U64 chunk_idx = 0; chunk_idx < obj->chunk_count; chunk_idx += 1) {
String8 name = obj->sect_name_arr[chunk_idx];
String8 sort = obj->sect_sort_arr[chunk_idx];
LNK_Chunk *chunk = &obj->chunk_arr[chunk_idx];
// :find_chunk_section
LNK_Section *sect = lnk_section_table_search(task->st, name);
Assert(sect);
// :chunk_ref_assign
LNK_ChunkRefAssign ctx;
ctx.cman = sect->cman;
ctx.chunk_id_arr_arr = task->chunk_id_arr_arr;
ctx.obj_idx = obj_idx;
lnk_visit_chunks(sect->id, chunk, lnk_chunk_ref_assign, &ctx);
// push to section chunk list
LNK_ChunkList **chunk_list_arr_arr = sort.size ? task->chunk_list_arr_arr : task->nosort_chunk_list_arr_arr;
lnk_chunk_list_push(arena, &chunk_list_arr_arr[sect->id][task_id], chunk);
}
}
}
internal LNK_ObjNodeArray
lnk_obj_list_push_parallel(TP_Context *tp, TP_Arena *arena, LNK_ObjList *obj_list, LNK_SectionTable *st, U64 input_count, LNK_InputObj **inputs)
{
ProfBeginFunction();
Temp scratch = scratch_begin(arena->v, arena->count);
U64 obj_id_base = obj_list->count;
LNK_ObjNodeArray obj_arr = lnk_obj_list_reserve(arena->v[0], obj_list, input_count);
ProfBegin("Obj Initer");
{
LNK_ObjIniter task = {0};
task.inputs = inputs;
task.obj_id_base = obj_id_base;
task.obj_node_arr = obj_arr.v;
tp_for_parallel(tp, arena, input_count, lnk_obj_initer, &task);
}
ProfEnd();
if (st) {
ProfBegin("Section Table Update");
{
TP_Temp temp = tp_temp_begin(arena);
LNK_ObjNewSectScanner task;
task.range_arr = tp_divide_work(arena->v[0], obj_arr.count, tp->worker_count);
task.obj_node_arr = obj_arr.v;
task.defn_arr = push_array(arena->v[0], LNK_SectDefnList, tp->worker_count);
task.conf_arr = push_array(arena->v[0], LNK_SectDefnList, tp->worker_count);
tp_for_parallel(tp, arena, tp->worker_count, lnk_obj_new_sect_scanner, &task);
LNK_SectDefnList defn_list = {0};
LNK_SectDefnList conf_list = {0};
lnk_sect_defn_list_concat_in_place_arr(&defn_list, task.defn_arr, tp->worker_count);
lnk_sect_defn_list_concat_in_place_arr(&conf_list, task.conf_arr, tp->worker_count);
HashTable *ht = hash_table_init(arena->v[0], 128);
for (LNK_SectionNode *sect_node = st->list.first; sect_node != 0; sect_node = sect_node->next) {
LNK_Section *sect = &sect_node->data;
hash_table_push_string_u64(arena->v[0], ht, sect->name, sect->flags);
}
LNK_SectDefnList new_list = {0};
for (LNK_SectDefn *curr = defn_list.first, *next; curr != 0; curr = next) {
next = curr->next;
curr->next = 0;
KeyValuePair *is_present = hash_table_search_string(ht, curr->name);
if (is_present) {
if (lnk_is_error_code_active(LNK_Warning_SectionFlagsConflict)) {
COFF_SectionFlags flags = is_present->value_u64;
if (flags != curr->flags) {
lnk_sect_defn_list_push_node(&conf_list, curr);
} else {
// section is present or is in new_list
}
}
} else {
lnk_sect_defn_list_push_node(&new_list, curr);
hash_table_push_string_u64(arena->v[0], ht, curr->name, curr->flags);
}
}
for (LNK_SectDefn *defn = conf_list.first; defn != 0; defn = defn->next) {
KeyValuePair *is_present = hash_table_search_string(ht, defn->name);
if (!is_present) {
InvalidPath;
}
U64 sect_number = (defn->idx + 1);
COFF_SectionFlags expected_flags = is_present->value_u64;
String8 expected_flags_str = coff_string_from_section_flags(scratch.arena, expected_flags);
String8 current_flags_str = coff_string_from_section_flags(scratch.arena, defn->flags);
lnk_error_obj(LNK_Warning_SectionFlagsConflict, defn->obj, "detected section flags conflict in %S(No. %X); expected {%S} but got {%S}", defn->name, sect_number, expected_flags_str, current_flags_str);
}
// push new sections for :find_chunk_section
for (LNK_SectDefn *curr = new_list.first; curr != 0; curr = curr->next) {
lnk_section_table_push(st, curr->name, curr->flags & ~COFF_SectionFlags_LNK_FLAGS);
}
tp_temp_end(temp);
}
ProfEnd();
ProfBegin("Count Chunks Per Section");
U64 **chunk_id_arr_arr;
{
U64 **chunk_count_arr_arr = push_array_no_zero(scratch.arena, U64 *, st->id_max);
for (U64 sect_idx = 0; sect_idx < st->id_max; sect_idx += 1) {
chunk_count_arr_arr[sect_idx] = push_array(scratch.arena, U64, obj_arr.count);
}
LNK_ChunkCounter task;
task.st = st;
task.obj_arr = obj_arr.v;
task.chunk_count_arr_arr = chunk_count_arr_arr;
tp_for_parallel(tp, 0, obj_arr.count, lnk_chunk_counter, &task);
chunk_id_arr_arr = chunk_count_arr_arr;
for (U64 sect_idx = 1; sect_idx < st->id_max; sect_idx += 1) {
LNK_Section *sect = lnk_section_table_search_id(st, sect_idx);
if (!sect) continue;
for (U64 obj_idx = 0; obj_idx < obj_arr.count; obj_idx += 1) {
U64 chunk_id_base = sect->cman->total_chunk_count;
sect->cman->total_chunk_count += chunk_count_arr_arr[sect_idx][obj_idx];
chunk_id_arr_arr[sect_idx][obj_idx] = chunk_id_base;
}
}
}
ProfEnd();
ProfBegin("Assign Chunk Refs");
{
LNK_ChunkRefAssigner task;
task.st = st;
task.range_arr = tp_divide_work(scratch.arena, obj_arr.count, tp->worker_count);
task.chunk_id_arr_arr = chunk_id_arr_arr;
task.obj_arr = obj_arr.v;
task.nosort_chunk_list_arr_arr = lnk_make_chunk_list_arr_arr(scratch.arena, st->id_max, tp->worker_count);
task.chunk_list_arr_arr = lnk_make_chunk_list_arr_arr(scratch.arena, st->id_max, tp->worker_count);
tp_for_parallel(tp, arena, tp->worker_count, lnk_chunk_ref_assigner, &task);
// merge chunks
for (LNK_SectionNode *sect_node = st->list.first; sect_node != 0; sect_node = sect_node->next) {
LNK_Section *sect = &sect_node->data;
lnk_chunk_list_concat_in_place_arr(sect->nosort_chunk->u.list, task.nosort_chunk_list_arr_arr[sect->id], tp->worker_count);
lnk_chunk_list_concat_in_place_arr(sect->root->u.list, task.chunk_list_arr_arr[sect->id], tp->worker_count);
}
}
ProfEnd();
}
ProfEnd();
scratch_end(scratch);
return obj_arr;
}
internal LNK_SymbolArray
lnk_symbol_array_from_coff(Arena *arena,
String8 coff_data,
String8 obj_path,
U64 string_table_off,
U64 sect_count,
COFF_SectionHeader *coff_sect_arr,
COFF_Symbol32Array coff_symbols,
LNK_Chunk *chunk_arr,
LNK_Chunk *master_common_block)
{
LNK_SymbolList weak_symbol_list = {0};
LNK_SymbolArray symbol_array = {0};
symbol_array.count = coff_symbols.count;
symbol_array.v = push_array(arena, LNK_Symbol, symbol_array.count);
for (U64 symbol_idx = 0; symbol_idx < coff_symbols.count; symbol_idx += 1) {
COFF_Symbol32 *coff_symbol = &coff_symbols.v[symbol_idx];
LNK_Symbol *symbol = &symbol_array.v[symbol_idx];
lnk_symbol_set_debug(symbol, obj_path);
String8 name = coff_read_symbol_name(coff_data, string_table_off, &coff_symbol->name);
// TODO: we convert 16-bit symbols and copy them to arena; symbols with short names
// are stored in the symbol itself and becuase converted symbols are pushed to scratch
// that memory is discarded after obj is processed
name = push_str8_copy(arena, name);
COFF_SymbolValueInterpType interp = coff_interp_symbol(coff_symbol);
switch (interp) {
case COFF_SymbolValueInterp_REGULAR: {
if (coff_symbol->section_number == 0 || coff_symbol->section_number > sect_count) {
lnk_error(LNK_Error_IllData, "%S: out ouf bounds section index in symbol \"%S (%u)\"", obj_path, name, coff_symbol->section_number);
break;
}
LNK_DefinedSymbolVisibility visibility = LNK_DefinedSymbolVisibility_Static;
if (coff_symbol->storage_class == COFF_SymStorageClass_EXTERNAL) {
visibility = LNK_DefinedSymbolVisibility_Extern;
}
LNK_DefinedSymbolFlags flags = 0;
if (coff_symbol->type.u.lsb == COFF_SymType_NULL && coff_symbol->type.u.msb == COFF_SymDType_FUNC) {
flags |= LNK_DefinedSymbolFlag_IsFunc;
}
COFF_ComdatSelectType selection = COFF_ComdatSelectType_ANY;
U64 check_sum = 0;
{
B32 is_comdat = !!(coff_sect_arr[coff_symbol->section_number - 1].flags & COFF_SectionFlag_LNK_COMDAT);
B32 has_static_def = is_comdat &&
coff_symbol->value == 0 &&
coff_symbol->type.u.lsb == COFF_SymType_NULL &&
coff_symbol->storage_class == COFF_SymStorageClass_STATIC &&
coff_symbol->aux_symbol_count == 1;
if (has_static_def) {
COFF_SymbolSecDef *secdef = (COFF_SymbolSecDef *)(coff_symbol + 1);
selection = secdef->selection;
check_sum = secdef->check_sum;
if (secdef->selection == COFF_ComdatSelectType_ASSOCIATIVE) {
LNK_Chunk *head_chunk = &chunk_arr[secdef->number - 1];
LNK_Chunk *associate_chunk = &chunk_arr[coff_symbol->section_number - 1];
lnk_chunk_associate(arena, head_chunk, associate_chunk);
}
}
}
LNK_Chunk *chunk = &chunk_arr[coff_symbol->section_number - 1];
U64 offset = coff_symbol->value;
lnk_init_defined_symbol_chunk(symbol, name, visibility, flags, chunk, offset, selection, check_sum);
} break;
case COFF_SymbolValueInterp_UNDEFINED: {
lnk_init_undefined_symbol(symbol, name, LNK_SymbolScopeFlag_Main);
} break;
case COFF_SymbolValueInterp_COMMON: {
// :common_block
LNK_Chunk *chunk = push_array_no_zero(arena, LNK_Chunk, 1);
chunk->ref = lnk_chunk_ref(0,0); // :chunk_ref_assign
chunk->align = 1;
chunk->is_discarded = 0;
chunk->sort_chunk = 1;
chunk->type = LNK_Chunk_Leaf;
chunk->sort_idx = str8(0,0);
chunk->input_idx = LNK_MakeChunkInputIdx(0, lnk_chunk_list_get_node_count(master_common_block));
chunk->flags = LNK_BSS_SECTION_FLAGS;
chunk->associate = 0;
chunk->u.leaf = str8(0, coff_symbol->value);
lnk_chunk_set_debugf(arena, chunk, "common block %S", name);
lnk_chunk_list_push(arena, master_common_block->u.list, chunk);
LNK_DefinedSymbolVisibility visibility = LNK_DefinedSymbolVisibility_Extern;
LNK_DefinedSymbolFlags flags = 0;
if (coff_symbol->type.u.lsb == COFF_SymType_NULL && coff_symbol->type.u.msb == COFF_SymDType_FUNC) {
flags |= LNK_DefinedSymbolFlag_IsFunc;
}
lnk_init_defined_symbol_chunk(symbol, name, visibility, flags, chunk, 0, COFF_ComdatSelectType_LARGEST, 0);
} break;
case COFF_SymbolValueInterp_WEAK: {
if (coff_symbol->aux_symbol_count == 0 || symbol_idx + 1 >= coff_symbols.count) {
lnk_error(LNK_Error_IllData, "%S: Weak symbol \"%S (%u)\" must at least one aux symbol", obj_path, name, symbol_idx);
break;
}
COFF_SymbolWeakExt *weak_ext = (COFF_SymbolWeakExt*)(coff_symbol + 1);
if (weak_ext->tag_index >= symbol_array.count) {
lnk_error(LNK_Error_IllData, "%S: Weak symbol \"%S (%u)\" points to out of bounds symbol", obj_path, name, symbol_idx);
break;
}
#if 0
if (symbol_array.v[weak_ext->tag_index] == NULL) {
lnk_error(LNK_ERROR_ILL_DATA, "%S: Weak symbol \"%S (%u)\" tags auxiliary symbol %u",
obj_path, name, symbol_idx, weak_ext->tag_index);
break;
}
#endif
lnk_init_weak_symbol(symbol, name, weak_ext->characteristics, &symbol_array.v[weak_ext->tag_index]);
lnk_symbol_list_push(arena, &weak_symbol_list, symbol);
} break;
case COFF_SymbolValueInterp_ABS: {
// Never code or data, synthetic symbol. COFF spec says bits in value are used
// as flags in symbol @feat.00, other symbols like @comp.id and @vol.md are undocumented.
// LLVM uses undocumented mask 0x4800 on @feat.00 to tell if object was compiled with /guard:cf.
LNK_DefinedSymbolVisibility visibility = LNK_DefinedSymbolVisibility_Static;
if (coff_symbol->storage_class == COFF_SymStorageClass_EXTERNAL) {
visibility = LNK_DefinedSymbolVisibility_Extern;
}
lnk_init_defined_symbol_va(symbol, name, visibility, 0, coff_symbol->value);
} break;
case COFF_SymbolValueInterp_DEBUG: {
// ignore
} break;
}
// skip aux symbols
symbol_idx += coff_symbol->aux_symbol_count;
}
return symbol_array;
}
internal LNK_RelocList *
lnk_reloc_list_array_from_coff(Arena *arena, COFF_MachineType machine, String8 coff_data, U64 sect_count, COFF_SectionHeader *coff_sect_arr, LNK_Chunk *chunk_arr, LNK_SymbolArray symbol_array)
{
LNK_RelocList *reloc_list_arr = push_array_no_zero(arena, LNK_RelocList, sect_count);
for (U64 sect_idx = 0; sect_idx < sect_count; sect_idx += 1) {
COFF_SectionHeader *coff_header = &coff_sect_arr[sect_idx];
COFF_RelocInfo coff_reloc_info = coff_reloc_info_from_section_header(coff_data, coff_header);
COFF_Reloc *coff_reloc_v = (COFF_Reloc *)(coff_data.str + coff_reloc_info.array_off);
LNK_Chunk *sect_chunk = &chunk_arr[sect_idx];
reloc_list_arr[sect_idx] = lnk_reloc_list_from_coff_reloc_array(arena, machine, sect_chunk, symbol_array, coff_reloc_v, coff_reloc_info.count);
}
return reloc_list_arr;
}
internal LNK_DirectiveInfo
lnk_init_directives(Arena *arena, String8 obj_path, U64 chunk_count, String8 *sect_name_arr, LNK_Chunk *chunk_arr)
{
LNK_DirectiveInfo directive_info = {0};
for (U64 chunk_idx = 0; chunk_idx < chunk_count; chunk_idx += 1) {
String8 sect_name = sect_name_arr[chunk_idx];
LNK_Chunk *sect_chunk = &chunk_arr[chunk_idx];
Assert(sect_chunk->type == LNK_Chunk_Leaf);
if (!str8_match(sect_name, str8_lit(".drectve"), 0)) {
continue;
}
if (sect_chunk->u.leaf.size < 3) {
lnk_error(LNK_Warning_IllData, "%S: can't parse %S", obj_path, sect_name);
continue;
}
if (~sect_chunk->flags & COFF_SectionFlag_LNK_INFO) {
lnk_error(LNK_Warning_IllData, "%S: %S missing COFF_SectionFlag_LNK_INFO.", obj_path, sect_name);
}
// TODO: warn if section has relocations
lnk_parse_directives(arena, &directive_info, sect_chunk->u.leaf, obj_path);
int bad_vs = 0; (void)bad_vs;
}
return directive_info;
}
internal COFF_FeatFlags
lnk_obj_get_features(LNK_Obj *obj)
{
COFF_FeatFlags result = 0;
LNK_Symbol *sym = lnk_symbol_list_search(obj->symbol_list, str8_lit("@feat.00"), 0);
if (sym) {
Assert(LNK_Symbol_IsDefined(sym->type));
Assert(sym->u.defined.value_type == LNK_DefinedSymbolValue_VA);
result = sym->u.defined.u.va;
}
return result;
}
internal U32
lnk_obj_get_comp_id(LNK_Obj *obj)
{
U32 result = 0;
LNK_Symbol *sym = lnk_symbol_list_search(obj->symbol_list, str8_lit("@comp.id"), 0);
if (sym) {
Assert(LNK_Symbol_IsDefined(sym->type));
Assert(sym->u.defined.value_type == LNK_DefinedSymbolValue_VA);
result = sym->u.defined.u.va;
}
return result;
}
internal U32
lnk_obj_get_vol_md(LNK_Obj *obj)
{
U32 result = 0;
LNK_Symbol *sym = lnk_symbol_list_search(obj->symbol_list, str8_lit("@vol.md"), 0);
if (sym) {
Assert(LNK_Symbol_IsDefined(sym->type));
Assert(sym->u.defined.value_type == LNK_DefinedSymbolValue_VA);
result = sym->u.defined.u.va;
}
return result;
}
+190
View File
@@ -0,0 +1,190 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
////////////////////////////////
typedef struct LNK_InputObj
{
struct LNK_InputObj *next;
B32 is_thin;
B32 has_disk_read_failed;
String8 dedup_id;
String8 path;
String8 data;
String8 lib_path;
} LNK_InputObj;
typedef struct LNK_InputObjList
{
U64 count;
LNK_InputObj *first;
LNK_InputObj *last;
} LNK_InputObjList;
////////////////////////////////
#define LNK_MakeChunkInputIdx(obj_idx, sect_idx) (((U64)(obj_idx) << 32) | (U64)((sect_idx) & max_U32))
typedef struct LNK_Obj
{
String8 data;
String8 path;
String8 lib_path;
U64 common_symbol_size;
COFF_MachineType machine;
U64 chunk_count;
U64 sect_count;
String8 *sect_name_arr;
String8 *sect_sort_arr;
LNK_RelocList *sect_reloc_list_arr;
LNK_Chunk *chunk_arr;
LNK_SymbolList symbol_list;
LNK_DirectiveInfo directive_info;
LNK_ExportParseList export_parse;
String8List include_symbol_list;
LNK_AltNameList alt_name_list;
} LNK_Obj;
typedef struct LNK_ObjNode
{
struct LNK_ObjNode *next;
LNK_Obj data;
} LNK_ObjNode;
typedef struct LNK_ObjList
{
U64 count;
LNK_ObjNode *first;
LNK_ObjNode *last;
} LNK_ObjList;
typedef struct LNK_ObjNodeArray
{
U64 count;
LNK_ObjNode *v;
} LNK_ObjNodeArray;
////////////////////////////////
typedef struct LNK_SectDefn
{
struct LNK_SectDefn *next;
LNK_Obj *obj;
String8 name;
COFF_SectionFlags flags;
U64 idx;
} LNK_SectDefn;
typedef struct
{
U64 count;
LNK_SectDefn *first;
LNK_SectDefn *last;
} LNK_SectDefnList;
typedef struct
{
LNK_InputObj **inputs;
LNK_ObjNode *obj_node_arr;
U64 obj_id_base;
LNK_SectDefnList *defn_arr;
LNK_SectionTable *st;
} LNK_ObjIniter;
typedef struct
{
Rng1U64 *range_arr;
LNK_ObjNode *obj_node_arr;
LNK_SectDefnList *defn_arr;
LNK_SectDefnList *conf_arr;
} LNK_ObjNewSectScanner;
typedef struct
{
LNK_SectionTable *st;
LNK_ObjNode *obj_arr;
U64 **chunk_count_arr_arr;
} LNK_ChunkCounter;
typedef struct
{
LNK_ChunkManager *cman;
U64 **chunk_id_arr_arr;
U64 obj_idx;
} LNK_ChunkRefAssign;
typedef struct
{
LNK_SectionTable *st;
Rng1U64 *range_arr;
U64 **chunk_id_arr_arr;
LNK_ObjNode *obj_arr;
LNK_ChunkList **nosort_chunk_list_arr_arr;
LNK_ChunkList **chunk_list_arr_arr;
} LNK_ChunkRefAssigner;
typedef struct
{
LNK_SymbolType type;
LNK_ObjNodeArray in_arr;
LNK_SymbolList *out_arr;
Rng1U64 *range_arr;
} LNK_SymbolCollector;
typedef struct
{
LNK_Obj **obj_arr;
String8 name;
String8 postfix;
B32 collect_discarded;
LNK_ChunkList *list_arr;
} LNK_CollectObjChunksTaskData;
typedef struct
{
Rng1U64 *range_arr;
LNK_ObjNodeArray in_arr;
String8List *out_arr;
} LNK_DefaultLibCollector;
typedef struct
{
LNK_Obj **in_arr;
String8List *out_arr;
Rng1U64 *range_arr;
} LNK_ManifestDependencyCollector;
////////////////////////////////
internal void lnk_error_obj(LNK_ErrorCode code, LNK_Obj *obj, char *fmt, ...);
////////////////////////////////
internal void lnk_input_obj_list_push_node(LNK_InputObjList *list, LNK_InputObj *node);
internal void lnk_input_obj_list_concat_in_place(LNK_InputObjList *list, LNK_InputObjList *to_concat);
internal LNK_InputObj * lnk_input_obj_list_push(Arena *arena, LNK_InputObjList *list);
internal LNK_InputObj ** lnk_array_from_input_obj_list(Arena *arena, LNK_InputObjList list);
internal LNK_InputObjList lnk_input_obj_list_from_string_list(Arena *arena, String8List list);
internal LNK_InputObjList lnk_list_from_input_obj_arr(LNK_InputObj **arr, U64 count);
////////////////////////////////
internal LNK_InputObjList lnk_input_obj_list_from_string_list(Arena *arena, String8List list);
internal LNK_Obj ** lnk_obj_arr_from_list(Arena *arena, LNK_ObjList list);
internal LNK_ObjNodeArray lnk_obj_list_reserve(Arena *arena, LNK_ObjList *list, U64 count);
internal LNK_ChunkList * lnk_collect_obj_chunks(TP_Context *tp, TP_Arena *arena, U64 obj_count, LNK_Obj **obj_arr, String8 name, String8 postfix, B32 collect_discarded);
internal LNK_ObjNodeArray lnk_obj_list_push_parallel(TP_Context *tp, TP_Arena *tp_arena, LNK_ObjList *obj_list, LNK_SectionTable *st, U64 input_count, LNK_InputObj **inputs);
internal LNK_Chunk * lnk_sect_chunk_array_from_coff(Arena *arena, U64 obj_id, String8 obj_path, String8 coff_data, U64 sect_count, COFF_SectionHeader *coff_sect_arr, String8 *sect_name_arr, String8 *sect_postfix_arr);
internal LNK_SymbolArray lnk_symbol_array_from_coff(Arena *arena, String8 coff_data, String8 obj_path, U64 string_table_off, U64 sect_count, COFF_SectionHeader *coff_sect_arr, COFF_Symbol32Array coff_symbols, LNK_Chunk *chunk_arr, LNK_Chunk *master_common_block);
internal LNK_RelocList lnk_reloc_list_from_coff_reloc_array(Arena *arena, COFF_MachineType machine, LNK_Chunk *chunk, LNK_SymbolArray symbol_array, COFF_Reloc *reloc_v, U64 reloc_count);
internal LNK_RelocList * lnk_reloc_list_array_from_coff(Arena *arena, COFF_MachineType machine, String8 coff_data, U64 sect_count, COFF_SectionHeader *coff_sect_arr, LNK_Chunk *sect_chunk_arr, LNK_SymbolArray symbol_array);
internal LNK_DirectiveInfo lnk_init_directives(Arena *arena, String8 obj_path, U64 chunk_count, String8 *sect_name_arr, LNK_Chunk *chunk_arr);
internal U32 lnk_obj_get_features(LNK_Obj *obj);
internal U32 lnk_obj_get_comp_id(LNK_Obj *obj);
internal U32 lnk_obj_get_vol_md(LNK_Obj *obj);
+153
View File
@@ -0,0 +1,153 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal LNK_Reloc *
lnk_reloc_list_reserve(Arena *arena, LNK_RelocList *list, U64 count)
{
LNK_Reloc *arr = NULL;
if (count) {
arr = push_array(arena, LNK_Reloc, count);
for (LNK_Reloc *ptr = arr, *opl = arr + count; ptr < opl; ++ptr) {
SLLQueuePush(list->first, list->last, ptr);
}
list->count += count;
}
return arr;
}
internal LNK_Reloc *
lnk_reloc_list_push(Arena *arena, LNK_RelocList *list)
{
LNK_Reloc *node = push_array(arena, LNK_Reloc, 1);
SLLQueuePush(list->first, list->last, node);
list->count += 1;
return node;
}
internal LNK_RelocList
lnk_reloc_list_copy(Arena *arena, LNK_RelocList *list)
{
LNK_RelocList result = {0};
for (LNK_Reloc *n = list->first; n != NULL; n = n->next) {
LNK_Reloc *r = lnk_reloc_list_push(arena, &result);
r->chunk = n->chunk;
r->type = n->type;
r->apply_off = n->apply_off;
r->symbol = n->symbol;
}
return result;
}
internal void
lnk_reloc_list_concat_in_place(LNK_RelocList *list, LNK_RelocList *to_concat)
{
SLLConcatInPlace(list, to_concat);
}
internal void
lnk_reloc_list_concat_in_place_arr(LNK_RelocList *list, LNK_RelocList *arr, U64 count)
{
SLLConcatInPlaceArray(list, arr, count);
}
internal LNK_RelocList **
lnk_make_reloc_list_arr_arr(Arena *arena, U64 slot_count, U64 per_count)
{
LNK_RelocList **arr_arr = push_array_no_zero(arena, LNK_RelocList *, slot_count);
for (U64 i = 0; i < slot_count; i += 1) {
arr_arr[i] = push_array(arena, LNK_RelocList, per_count);
}
return arr_arr;
}
internal LNK_RelocList
lnk_reloc_list_from_coff_reloc_array(Arena *arena, COFF_MachineType machine, LNK_Chunk *chunk, LNK_SymbolArray symbol_array, COFF_Reloc *reloc_v, U64 reloc_count)
{
LNK_RelocList reloc_list = {0};
LNK_Reloc *reloc_arr = lnk_reloc_list_reserve(arena, &reloc_list, reloc_count);
LNK_Reloc *reloc_ptr = reloc_arr;
LNK_Reloc *reloc_opl = reloc_arr + reloc_count;
COFF_Reloc *coff_reloc_ptr = reloc_v;
for (; reloc_ptr < reloc_opl; reloc_ptr += 1, coff_reloc_ptr += 1) {
Assert(coff_reloc_ptr->isymbol < symbol_array.count);
reloc_ptr->chunk = chunk;
reloc_ptr->type = lnk_ext_reloc_type_from_coff(machine, coff_reloc_ptr->type);
reloc_ptr->apply_off = coff_reloc_ptr->apply_off;
reloc_ptr->symbol = symbol_array.v + coff_reloc_ptr->isymbol;
}
return reloc_list;
}
internal LNK_Reloc **
lnk_reloc_array_from_list(Arena *arena, LNK_RelocList list)
{
LNK_Reloc **arr = push_array_no_zero(arena, LNK_Reloc *, list.count);
U64 count = 0;
for (LNK_Reloc *node = list.first; node != 0; node = node->next) {
Assert(count < list.count);
arr[count++] = node;
}
return arr;
}
internal LNK_RelocType
lnk_ext_reloc_type_from_coff(COFF_MachineType machine, U32 type)
{
LNK_RelocType result = LNK_Reloc_NULL;
switch (machine) {
case COFF_MachineType_UNKNOWN: break;
case COFF_MachineType_X64: {
switch (type) {
case COFF_RelocTypeX64_ABS: result = LNK_Reloc_NULL; break;
case COFF_RelocTypeX64_ADDR64: result = LNK_Reloc_ADDR_64; break;
case COFF_RelocTypeX64_ADDR32: result = LNK_Reloc_ADDR_32; break;
case COFF_RelocTypeX64_ADDR32NB: result = LNK_Reloc_VIRT_OFF_32; break;
case COFF_RelocTypeX64_REL32: result = LNK_Reloc_REL32; break;
case COFF_RelocTypeX64_REL32_1: result = LNK_Reloc_REL32_1; break;
case COFF_RelocTypeX64_REL32_2: result = LNK_Reloc_REL32_2; break;
case COFF_RelocTypeX64_REL32_3: result = LNK_Reloc_REL32_3; break;
case COFF_RelocTypeX64_REL32_4: result = LNK_Reloc_REL32_4; break;
case COFF_RelocTypeX64_REL32_5: result = LNK_Reloc_REL32_5; break;
case COFF_RelocTypeX64_SECTION: result = LNK_Reloc_SECT_IDX; break;
case COFF_RelocTypeX64_SECREL: result = LNK_Reloc_SECT_REL; break;
case COFF_RelocTypeX64_SECREL7: lnk_not_implemented("TODO: COFF_RelocTypeX64_SECREL7"); break;
case COFF_RelocTypeX64_TOKEN: lnk_not_implemented("TODO: COFF_RelocTypeX64_TOKEN"); break;
case COFF_RelocTypeX64_SREL32: lnk_not_implemented("TODO: COFF_RelocTypeX64_SREL32"); break;
case COFF_RelocTypeX64_PAIR: lnk_not_implemented("TODO: COFF_RelocTypeX64_PAIR"); break;
case COFF_RelocTypeX64_SSPAN32: lnk_not_implemented("TODO: COFF_RelocTypeX64_SSPAN32"); break;
default: lnk_invalid_path("unknown relocation type 0x%X", type);
}
} break;
default: lnk_not_implemented("TODO: define remap for coff reloc types"); break;
}
return result;
}
internal U32
lnk_ext_reloc_type_to_coff(COFF_MachineType machine, LNK_RelocType type)
{
U32 result = 0;
switch (machine) {
case COFF_MachineType_X64: {
switch (type) {
case LNK_Reloc_NULL: result = COFF_RelocTypeX64_ABS; break;
case LNK_Reloc_ADDR_64: result = COFF_RelocTypeX64_ADDR64; break;
case LNK_Reloc_ADDR_32: result = COFF_RelocTypeX64_ADDR32; break;
case LNK_Reloc_VIRT_OFF_32: result = COFF_RelocTypeX64_ADDR32NB; break;
case LNK_Reloc_REL32: result = COFF_RelocTypeX64_REL32; break;
case LNK_Reloc_REL32_1: result = COFF_RelocTypeX64_REL32_1; break;
case LNK_Reloc_REL32_2: result = COFF_RelocTypeX64_REL32_2; break;
case LNK_Reloc_REL32_3: result = COFF_RelocTypeX64_REL32_3; break;
case LNK_Reloc_REL32_4: result = COFF_RelocTypeX64_REL32_4; break;
case LNK_Reloc_REL32_5: result = COFF_RelocTypeX64_REL32_5; break;
case LNK_Reloc_SECT_IDX: result = COFF_RelocTypeX64_SECTION; break;
case LNK_Reloc_SECT_REL: result = COFF_RelocTypeX64_SECREL; break;
default: InvalidPath;
}
} break;
default: lnk_not_implemented("TODO: support for machine 0x%X", machine); break;
}
return result;
}
+56
View File
@@ -0,0 +1,56 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef enum
{
LNK_Reloc_NULL,
LNK_Reloc_ADDR_16,
LNK_Reloc_ADDR_32,
LNK_Reloc_ADDR_64,
LNK_Reloc_CHUNK_SIZE_FILE_16,
LNK_Reloc_CHUNK_SIZE_FILE_32,
LNK_Reloc_CHUNK_SIZE_VIRT_32,
LNK_Reloc_FILE_ALIGN_32,
LNK_Reloc_FILE_OFF_15,
LNK_Reloc_FILE_OFF_32,
LNK_Reloc_FILE_OFF_64,
LNK_Reloc_REL32,
LNK_Reloc_REL32_1,
LNK_Reloc_REL32_2,
LNK_Reloc_REL32_3,
LNK_Reloc_REL32_4,
LNK_Reloc_REL32_5,
LNK_Reloc_SECT_REL,
LNK_Reloc_SECT_IDX,
LNK_Reloc_VIRT_ALIGN_32,
LNK_Reloc_VIRT_OFF_32,
} LNK_RelocType;
typedef struct LNK_Reloc
{
struct LNK_Reloc *next;
LNK_Chunk *chunk;
LNK_RelocType type;
U64 apply_off;
struct LNK_Symbol *symbol;
} LNK_Reloc;
typedef struct LNK_RelocList
{
U64 count;
LNK_Reloc *first;
LNK_Reloc *last;
} LNK_RelocList;
internal LNK_Reloc * lnk_reloc_list_reserve(Arena *arena, LNK_RelocList *list, U64 count);
internal LNK_Reloc * lnk_reloc_list_push(Arena *arena, LNK_RelocList *list);
internal LNK_RelocList lnk_reloc_list_copy(Arena *arena, LNK_RelocList *list);
internal void lnk_reloc_list_concat_in_place(LNK_RelocList *list, LNK_RelocList *to_concat);
internal void lnk_reloc_list_concat_in_place_arr(LNK_RelocList *list, LNK_RelocList *arr, U64 count);
internal LNK_RelocList ** lnk_make_reloc_list_arr_arr(Arena *arena, U64 slot_count, U64 per_count);
internal LNK_Reloc ** lnk_reloc_array_from_list(Arena *arena, LNK_RelocList list);
internal LNK_RelocType lnk_ext_reloc_type_from_coff(COFF_MachineType machine, U32 type);
internal U32 lnk_ext_reloc_type_to_coff(COFF_MachineType machine, LNK_RelocType type);
+855
View File
@@ -0,0 +1,855 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal LNK_SectionNode *
lnk_section_list_remove(LNK_SectionList *list, String8 name)
{
LNK_SectionNode *section = lnk_section_list_search_node(list, name);
if (list->count > 0) {
if (list->first == section) {
list->first = list->first->next;
list->count -= 1;
if (list->last == section) {
list->last = NULL;
}
} else {
for (LNK_SectionNode *curr = list->first, *prev = NULL; curr != NULL; prev = curr, curr = curr->next) {
if (curr == section) {
prev->next = curr->next;
list->count -= 1;
if (list->last == curr) {
list->last = prev;
}
break;
}
}
}
}
return section;
}
internal LNK_SectionNode *
lnk_section_list_search_node(LNK_SectionList *list, String8 name)
{
LNK_SectionNode *node;
for (node = list->first; node != 0; node = node->next) {
if (str8_match(node->data.name, name, 0)) {
break;
}
}
return node;
}
internal LNK_Section *
lnk_section_list_search(LNK_SectionList *list, String8 name)
{
LNK_SectionNode *node = lnk_section_list_search_node(list, name);
return node != NULL ? &node->data : NULL;
}
internal LNK_SectionArray
lnk_section_array_from_list(Arena *arena, LNK_SectionList list)
{
LNK_SectionArray result;
result.count = 0;
result.v = push_array_no_zero(arena, LNK_Section, list.count);
for (LNK_SectionNode *node = list.first; node != 0; node = node->next) {
result.v[result.count] = node->data;
result.count += 1;
}
return result;
}
internal LNK_SectionPtrArray
lnk_section_ptr_array_from_list(Arena *arena, LNK_SectionList list)
{
LNK_SectionPtrArray result;
result.count = 0;
result.v = push_array_no_zero(arena, LNK_Section *, list.count);
for (LNK_SectionNode *node = list.first; node != 0; node = node->next) {
result.v[result.count] = &node->data;
result.count += 1;
}
return result;
}
internal String8
lnk_make_section_sort_index(Arena *arena, String8 name, COFF_SectionFlags flags, U64 section_index)
{
ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 1);
// pack sections with run-time data closer
String8List sort_index_list = {0};
if (flags & COFF_SectionFlag_MEM_DISCARDABLE) {
str8_list_pushf(scratch.arena, &sort_index_list, "b");
} else {
str8_list_pushf(scratch.arena, &sort_index_list, "a");
}
if (str8_match(name, str8_lit(".null"), 0)) {
// null section always first
str8_list_pushf(scratch.arena, &sort_index_list, "a");
} else if (str8_match(name, str8_lit(".rsrc"), 0)) {
// section with resource data must be last because during runtime windows might append pages
str8_list_pushf(scratch.arena, &sort_index_list, "c");
} else {
str8_list_pushf(scratch.arena, &sort_index_list, "b");
}
// sort sections based on the contents
if (flags & COFF_SectionFlag_CNT_CODE) {
str8_list_pushf(scratch.arena, &sort_index_list, "a");
if (str8_match(name, str8_lit(".text"), 0)) {
str8_list_pushf(scratch.arena, &sort_index_list, "a");
} else {
str8_list_pushf(scratch.arena, &sort_index_list, "b");
}
} else if (flags & COFF_SectionFlag_CNT_INITIALIZED_DATA) {
str8_list_pushf(scratch.arena, &sort_index_list, "b");
if (str8_match(name, str8_lit(".data"), 0)) {
str8_list_pushf(scratch.arena, &sort_index_list, "a");
} else if (str8_match(name, str8_lit(".rdata"), 0)) {
str8_list_pushf(scratch.arena, &sort_index_list, "b");
} else if (str8_match(name, str8_lit(".tls"), 0)) {
str8_list_pushf(scratch.arena, &sort_index_list, "c");
} else {
str8_list_pushf(scratch.arena, &sort_index_list, "d");
}
} else if (flags & COFF_SectionFlag_CNT_UNINITIALIZED_DATA) {
str8_list_pushf(scratch.arena, &sort_index_list, "c");
} else {
str8_list_pushf(scratch.arena, &sort_index_list, "d");
}
// sort sections based on read/write access so final section layout looks cleaner
if (flags & COFF_SectionFlag_MEM_READ && ~flags & COFF_SectionFlag_MEM_WRITE) {
str8_list_pushf(scratch.arena, &sort_index_list, "a");
} else {
str8_list_pushf(scratch.arena, &sort_index_list, "b");
}
String8 order_index = str8_from_bits_u32(scratch.arena, safe_cast_u32(section_index));
str8_list_push(scratch.arena, &sort_index_list, order_index);
String8 result = str8_list_join(arena, &sort_index_list, 0);
scratch_end(scratch);
ProfEnd();
return result;
}
internal void
lnk_section_associate_chunks(LNK_Section *sect, LNK_Chunk *head, LNK_Chunk *associate)
{
lnk_chunk_associate(sect->arena, head, associate);
}
internal LNK_Chunk *
lnk_section_push_chunk_raw(LNK_Section *sect, LNK_Chunk *parent, void *raw_ptr, U64 raw_size, String8 sort_index)
{
return lnk_chunk_push_leaf(sect->arena, sect->cman, parent, sort_index, raw_ptr, raw_size);
}
internal LNK_Chunk *
lnk_section_push_chunk_data(LNK_Section *sect, LNK_Chunk *parent, String8 data, String8 sort_index)
{
return lnk_section_push_chunk_raw(sect, parent, data.str, data.size, sort_index);
}
internal LNK_Chunk *
lnk_section_push_chunk_u32(LNK_Section *sect, LNK_Chunk *parent, U32 value, String8 sort_index)
{
U32 *ptr = push_array_no_zero(sect->arena, U32, 1);
*ptr = value;
return lnk_section_push_chunk_raw(sect, parent, ptr, sizeof(*ptr), sort_index);
}
internal LNK_Chunk *
lnk_section_push_chunk_u64(LNK_Section *sect, LNK_Chunk *parent, U32 value, String8 sort_index)
{
U64 *ptr = push_array_no_zero(sect->arena, U64, 1);
*ptr = value;
return lnk_section_push_chunk_raw(sect, parent, ptr, sizeof(*ptr), sort_index);
}
internal LNK_Chunk *
lnk_section_push_chunk_bss(LNK_Section *sect, LNK_Chunk *parent, U64 size, String8 sort_index)
{
return lnk_section_push_chunk_raw(sect, parent, 0, size, sort_index);
}
internal LNK_Chunk *
lnk_section_push_chunk_list(LNK_Section *sect, LNK_Chunk *parent, String8 sort_index)
{
return lnk_chunk_push_list(sect->arena, sect->cman, parent, sort_index);
}
internal LNK_Reloc *
lnk_section_push_reloc(LNK_Section *sect, LNK_Chunk *chunk, LNK_RelocType type, U64 apply_off, LNK_Symbol *symbol)
{
Assert(symbol);
LNK_Reloc *reloc = lnk_reloc_list_push(sect->arena, &sect->reloc_list);
reloc->chunk = chunk;
reloc->type = type;
reloc->apply_off = apply_off;
reloc->symbol = symbol;
return reloc;
}
internal LNK_Reloc *
lnk_section_push_reloc_undefined(LNK_Section *sect, LNK_Chunk *chunk, LNK_RelocType type, U64 apply_off, String8 undefined_symbol_name, LNK_SymbolScopeFlags scope_flags)
{
LNK_Symbol *symbol = lnk_make_undefined_symbol(sect->arena, undefined_symbol_name, scope_flags);
LNK_Reloc *reloc = lnk_section_push_reloc(sect, chunk, type, apply_off, symbol);
return reloc;
}
internal void
lnk_section_merge(LNK_Section *dst, LNK_Section *src)
{
ProfBeginFunction();
// set merge info
src->is_merged = 1;
src->merge_sect_id = dst->id;
src->id_map = push_array_no_zero(src->arena, U64, src->cman->total_chunk_count);
// put source root in a wrapper list so it has unique sort index otherwise
// after we merge sections sort indices might conflict
LNK_Chunk *src_root_wrapper = lnk_section_push_chunk_list(dst, dst->cman->root, str8(0,0));
// merge roots
lnk_merge_chunks(dst->arena, dst->cman, src_root_wrapper, src->cman->root, src->id_map, src->cman->total_chunk_count);
// copy relocations
lnk_reloc_list_concat_in_place(&dst->reloc_list, &src->reloc_list);
ProfEnd();
}
internal U8
lnk_code_align_byte_from_machine(COFF_MachineType machine)
{
U8 align_byte = 0;
switch (machine) {
case COFF_MachineType_X64:
case COFF_MachineType_X86: {
align_byte = 0xCC;
} break;
default: {
lnk_not_implemented("TODO: set align value for machine %S", coff_string_from_machine_type(machine));
} break;
}
return align_byte;
}
internal void
lnk_section_build_data(LNK_Section *sect, COFF_MachineType machine)
{
if (sect->is_loose && sect->has_layout) {
// get value for align data fill
U8 align_byte = 0;
B32 is_code = !!(sect->flags & COFF_SectionFlag_CNT_CODE);
if (is_code) {
align_byte = lnk_code_align_byte_from_machine(machine);
}
sect->layout = lnk_build_chunk_layout(sect->arena, sect->cman, sect->flags, align_byte);
sect->is_loose = 0;
}
}
internal LNK_SectionTable *
lnk_section_table_alloc(U64 section_virt_off, U64 sect_align, U64 file_align)
{
ProfBeginFunction();
Arena *arena = arena_alloc();
LNK_SectionTable *st = push_array(arena, LNK_SectionTable, 1);
st->arena = arena;
st->section_virt_off = section_virt_off;
st->sect_align = sect_align;
st->file_align = file_align;
ProfEnd();
return st;
}
internal void
lnk_section_table_release(LNK_SectionTable **st_ptr)
{
ProfBeginFunction();
LNK_SectionTable *st = *st_ptr;
arena_release(st->arena);
*st_ptr = NULL;
ProfEnd();
}
internal LNK_Section *
lnk_section_table_push(LNK_SectionTable *st, String8 name, COFF_SectionFlags flags)
{
ProfBeginFunction();
LNK_SectionList *sect_list = &st->list;
LNK_SectionNode *sect_node = push_array(st->arena, LNK_SectionNode, 1);
String8 sort_index = lnk_make_section_sort_index(st->arena, name, flags, st->id_max);
B32 found = 0;
for (LNK_SectionNode *curr = sect_list->first, *prev = NULL; curr != NULL; prev = curr, curr = curr->next) {
LNK_Section *sect = &curr->data;
int cmp = str8_compar_case_sensetive(&sort_index, &sect->sort_index);
if (cmp < 0) {
if (prev == NULL) {
SLLQueuePushFront(sect_list->first, sect_list->last, sect_node);
} else {
prev->next = sect_node;
sect_node->next = curr;
}
found = 1;
break;
}
}
if (!found) {
SLLQueuePush(sect_list->first, sect_list->last, sect_node);
}
sect_list->count += 1;
U64 sect_id = st->id_max;
st->id_max += 1;
LNK_Section *sect = &sect_node->data;
sect->arena = arena_alloc();
sect->id = sect_id;
sect->name = push_str8_copy(sect->arena, name);
sect->sort_index = sort_index;
sect->flags = flags;
sect->cman = lnk_chunk_manager_alloc(sect->arena, sect_id, st->file_align);
sect->root = sect->cman->root;
sect->nosort_chunk = lnk_chunk_push_list(sect->arena, sect->cman, sect->root, str8(0,0));
sect->nosort_chunk->sort_chunk = 0;
sect->emit_header = 1;
sect->has_layout = 1;
sect->is_loose = 1;
lnk_chunk_set_debugf(sect->arena, sect->root, "root chunk for %S", name);
ProfEnd();
return sect;
}
internal LNK_Section *
lnk_section_table_push_null(LNK_SectionTable *st)
{
LNK_SectionList *list = &st->list;
SLLQueuePushFront(list->first, list->last, st->null_sect);
list->count += 1;
return &st->null_sect->data;
}
LNK_CHUNK_VISITOR_SIG(lnk_chunk_has_leaf)
{
B32 stop = 0;
if (chunk->type == LNK_Chunk_Leaf) {
B32 has_data = !lnk_chunk_is_discarded(chunk) && chunk->u.leaf.size > 0;
if (has_data) {
B32 *no_data = (B32*)ud;
*no_data = 0;
stop = 1;
}
}
return stop;
}
LNK_CHUNK_VISITOR_SIG(lnk_chunk_mark_discarded)
{
chunk->is_discarded = 1;
B32 stop = 0;
return stop;
}
internal void
lnk_section_table_remove(LNK_SectionTable *st, LNK_SymbolTable *symtab, String8 name)
{
ProfBeginFunction();
// remove node from list
LNK_SectionNode *sect_node = lnk_section_list_remove(&st->list, name);
LNK_Section *sect = &sect_node->data;
// remove symbol for section root chunk
lnk_symbol_table_remove(symtab, LNK_SymbolScopeIndex_Internal, sect->symbol_name);
// mark chunks as discarded
lnk_visit_chunks(sect->id, sect->root, lnk_chunk_mark_discarded, NULL);
// push to empties
SLLQueuePush(st->empties_list.first, st->empties_list.last, sect_node);
st->empties_list.count += 1;
ProfEnd();
}
internal LNK_Section *
lnk_section_table_search(LNK_SectionTable *st, String8 name)
{
return lnk_section_list_search(&st->list, name);
}
internal LNK_Section *
lnk_section_table_search_id(LNK_SectionTable *st, U64 id)
{
for (LNK_SectionNode *node = st->list.first; node != NULL; node = node->next) {
if (node->data.id == id) {
return &node->data;
}
}
return NULL;
}
internal void
lnk_section_table_merge(LNK_SectionTable *st, LNK_MergeDirectiveList merge_list)
{
ProfBeginFunction();
Temp scratch = scratch_begin(0, 0);
LNK_Section **src_dst = push_array(scratch.arena, LNK_Section *, st->id_max);
for (LNK_MergeDirectiveNode *merge_node = merge_list.first; merge_node != NULL; merge_node = merge_node->next) {
LNK_MergeDirective *merge = &merge_node->data;
// are we trying to merge section that was already merged?
LNK_Section *merge_sect = lnk_section_list_search(&st->merge_list, merge->src);
if (merge_sect) {
LNK_Section *dst = src_dst[merge_sect->id];
B32 is_ambiguous_merge = !str8_match(dst->name, merge->dst, 0);
if (is_ambiguous_merge) {
lnk_error(LNK_Warning_AmbiguousMerge, "Detected ambiguous section merge:");
lnk_supplement_error("%S => %S (Merged)", merge_sect->name, dst->name);
lnk_supplement_error("%S => %S", merge_sect->name, merge->dst);
}
continue;
}
// find source seciton
LNK_Section *src = lnk_section_table_search(st, merge->src);
if (src == NULL) {
lnk_error(LNK_Warning_IllData, "Can't find section \"%S\" to merge with \"%S\"", merge->src, merge->dst);
// TODO: supplement obj path if applicable
continue;
}
// handle case where destination section doesn't exist
LNK_Section *dst = lnk_section_table_search(st, merge->dst);
if (dst == NULL) {
src->name = push_str8_copy(src->arena, merge->dst);
src_dst[src->id] = src;
continue;
}
// update map
src_dst[src->id] = dst;
// merge section with destination
lnk_section_merge(dst, src);
// remove from output section list
LNK_SectionNode *src_node = lnk_section_list_remove(&st->list, src->name);
// push section to merged list
SLLQueuePush(st->merge_list.first, st->merge_list.last, src_node);
st->merge_list.count += 1;
}
scratch_end(scratch);
ProfEnd();
}
internal void
lnk_section_table_remove_empties(LNK_SectionTable *st, LNK_SymbolTable *symtab)
{
ProfBeginFunction();
Temp scratch = scratch_begin(0, 0);
String8List name_list = {0};
for (LNK_SectionNode *sect_node = st->list.first; sect_node != NULL; sect_node = sect_node->next) {
LNK_Section *sect = &sect_node->data;
B32 no_data = 1;
lnk_visit_chunks(sect->id, sect->root, lnk_chunk_has_leaf, (void*)&no_data);
if (no_data) {
String8 name = push_str8_copy(scratch.arena, sect->name);
str8_list_push(scratch.arena, &name_list, name);
}
}
for (String8Node *name = name_list.first; name != NULL; name = name->next) {
lnk_section_table_remove(st, symtab, name->string);
}
scratch_end(scratch);
ProfEnd();
}
internal LNK_SectionArray
lnk_section_table_get_output_sections(Arena *arena, LNK_SectionTable *st)
{
LNK_SectionArray result = {0};
result.count = 0;
result.v = push_array(arena, LNK_Section, st->list.count);
for (LNK_SectionNode *sect_node = st->list.first; sect_node != 0; sect_node = sect_node->next) {
if (sect_node->data.emit_header && sect_node->data.has_layout) {
Assert(result.count < st->list.count);
result.v[result.count] = sect_node->data;
result.count += 1;
}
}
U64 unused_entry_count = st->list.count - result.count;
arena_pop(arena, unused_entry_count * sizeof(result.v[0]));
return result;
}
internal
THREAD_POOL_TASK_FUNC(lnk_section_data_builder)
{
LNK_SectionDataBuilder *task = raw_task;
Rng1U64 range = task->range_arr[task_id];
for (U64 sect_idx = range.min; sect_idx < range.max; ++sect_idx) {
lnk_section_build_data(task->sect_arr[sect_idx], task->machine);
}
}
internal void
lnk_section_table_build_data(TP_Context *tp, LNK_SectionTable *st, COFF_MachineType machine)
{
ProfBeginFunction();
Temp scratch = scratch_begin(0, 0);
LNK_SectionPtrArray sect_arr = lnk_section_ptr_array_from_list(scratch.arena, st->list);
LNK_SectionDataBuilder task = {0};
task.machine = machine;
task.range_arr = tp_divide_work(scratch.arena, sect_arr.count, tp->worker_count);
task.sect_arr = sect_arr.v;
tp_for_parallel(tp, 0, tp->worker_count, lnk_section_data_builder, &task);
scratch_end(scratch);
ProfEnd();
}
internal void
lnk_section_table_assign_virtual_offsets(LNK_SectionTable *st)
{
ProfBeginFunction();
Temp scratch = scratch_begin(0, 0);
LNK_Section **sect_id_map = lnk_sect_id_map_from_section_table(scratch.arena, st);
U64 cursor = st->section_virt_off;
Assert(cursor >= 0x1000);
for (LNK_SectionNode *sect_node = st->list.first; sect_node != NULL; sect_node = sect_node->next) {
if (sect_node == st->null_sect) continue;
LNK_Section *sect = &sect_node->data;
if (!sect->has_layout) continue;
sect->virt_off = cursor;
U64 sect_size = lnk_virt_size_from_chunk_ref(sect_id_map, sect->root->ref);
cursor += sect_size;
cursor = AlignPow2(cursor, st->sect_align);
}
scratch_end(scratch);
ProfEnd();
}
internal void
lnk_section_table_assign_file_offsets(LNK_SectionTable *st)
{
ProfBeginFunction();
U64 cursor = 0;
for (LNK_SectionNode *sect_node = st->list.first; sect_node != NULL; sect_node = sect_node->next) {
LNK_Section *sect = &sect_node->data;
if (sect->flags & COFF_SectionFlag_CNT_UNINITIALIZED_DATA) {
continue;
}
if (!sect->has_layout) continue;
sect->file_off = cursor;
U64 root_size = sect->layout.chunk_file_size_array[sect->root->ref.chunk_id];
cursor += root_size;
}
ProfEnd();
}
internal void
lnk_section_table_assign_indices(LNK_SectionTable *st)
{
ProfBeginFunction();
U64 isect = 0;
for (LNK_SectionNode *sect_node = st->list.first; sect_node != NULL; sect_node = sect_node->next) {
LNK_Section *sect = &sect_node->data;
if (sect->emit_header) {
sect->isect = isect++;
}
}
ProfEnd();
}
internal String8
lnk_section_table_serialize(Arena *arena, LNK_SectionTable *st)
{
ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 1);
String8List image_list = {0};
for (LNK_SectionNode *sect_node = st->list.first; sect_node != NULL; sect_node = sect_node->next) {
LNK_Section *sect = &sect_node->data;
str8_list_push(scratch.arena, &image_list, sect->layout.data);
}
String8 result = str8_list_join(arena, &image_list, NULL);
scratch_end(scratch);
ProfEnd();
return result;
}
internal LNK_ChunkPtr **
lnk_chunk_id_map_from_section_table(Arena *arena, LNK_SectionTable *st)
{
ProfBeginFunction();
LNK_ChunkPtr **chunk_id_map = push_array(arena, LNK_ChunkPtr *, st->id_max);
for (LNK_SectionNode *node = st->list.first; node != 0; node = node->next) {
LNK_Section *sect = &node->data;
chunk_id_map[sect->id] = lnk_make_chunk_id_map(arena, sect->cman);
}
if (st->list.first->data.id != 0) {
chunk_id_map[0] = push_array(arena, LNK_ChunkPtr, 1);
chunk_id_map[0][0] = g_null_chunk_ptr;
}
ProfEnd();
return chunk_id_map;
}
internal LNK_Section **
lnk_sect_id_map_from_section_table(Arena *arena, LNK_SectionTable *st)
{
ProfBeginFunction();
LNK_Section **map = push_array(arena, LNK_Section *, st->id_max);
LNK_SectionList *list_arr[] = { &st->list, &st->merge_list, &st->empties_list };
for (U64 list_idx = 0; list_idx < ArrayCount(list_arr); ++list_idx) {
for (LNK_SectionNode *sect_node = list_arr[list_idx]->first; sect_node != NULL; sect_node = sect_node->next) {
LNK_Section *sect = &sect_node->data;
Assert(sect->id < st->id_max);
Assert(map[sect->id] == NULL);
map[sect->id] = sect;
}
}
if (map[0] == NULL) {
LNK_Section *sect = push_array(arena, LNK_Section, 1);
sect->layout.chunk_off_array = push_array(arena, U64, 1);
sect->layout.chunk_file_size_array = push_array(arena, U64, 1);
sect->layout.chunk_virt_size_array = push_array(arena, U64, 1);
map[0] = sect;
}
ProfEnd();
return map;
}
internal LNK_ChunkRef
lnk_get_final_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref)
{
LNK_ChunkRef final_chunk_ref = chunk_ref;
if (sect_id_map[chunk_ref.sect_id]->is_merged) {
final_chunk_ref.sect_id = sect_id_map[chunk_ref.sect_id]->merge_sect_id;
final_chunk_ref.chunk_id = sect_id_map[chunk_ref.sect_id]->id_map[chunk_ref.chunk_id];
// we don't support sections that were merged more than once.
Assert(!sect_id_map[final_chunk_ref.sect_id]->is_merged);
}
return final_chunk_ref;
}
internal LNK_Section *
lnk_sect_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef input_chunk_ref)
{
LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, input_chunk_ref);
LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id];
return sect;
}
internal LNK_Chunk *
lnk_chunk_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkPtr **chunk_id_map, LNK_ChunkRef chunk_ref)
{
LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref);
LNK_Chunk *chunk = chunk_id_map[final_chunk_ref.sect_id][final_chunk_ref.chunk_id];
return chunk;
}
internal U64
lnk_isect_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref)
{
LNK_Section *sect = lnk_sect_from_chunk_ref(sect_id_map, chunk_ref);
U64 isect = sect->isect;
return isect;
}
internal U64
lnk_off_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref)
{
LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref);
LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id];
U64 off = sect->layout.chunk_off_array[final_chunk_ref.chunk_id];
return off;
}
internal U64
lnk_virt_off_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref)
{
LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref);
LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id];
U64 off = sect->layout.chunk_off_array[final_chunk_ref.chunk_id];
U64 virt_off = off + sect->virt_off;
return virt_off;
}
internal U64
lnk_file_off_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref)
{
LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref);
LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id];
U64 off = sect->layout.chunk_off_array[final_chunk_ref.chunk_id];
U64 file_off = off + sect->file_off;
return file_off;
}
internal U64
lnk_virt_size_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref)
{
LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref);
LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id];
U64 virt_size = sect->layout.chunk_virt_size_array[final_chunk_ref.chunk_id];
return virt_size;
}
internal U64
lnk_file_size_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref)
{
LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref);
LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id];
U64 file_size = sect->layout.chunk_file_size_array[final_chunk_ref.chunk_id];
return file_size;
}
internal String8
lnk_data_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref)
{
LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref);
LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id];
U64 chunk_off = lnk_off_from_chunk_ref(sect_id_map, chunk_ref);
U64 chunk_size = lnk_file_size_from_chunk_ref(sect_id_map, chunk_ref);
String8 chunk_data = str8_substr(sect->layout.data, r1u64(chunk_off, chunk_off + chunk_size));
return chunk_data;
}
internal String8
lnk_data_from_chunk_ref_no_pad(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref)
{
LNK_ChunkRef final_chunk_ref = lnk_get_final_chunk_ref(sect_id_map, chunk_ref);
LNK_Section *sect = sect_id_map[final_chunk_ref.sect_id];
U64 chunk_off = lnk_off_from_chunk_ref(sect_id_map, chunk_ref);
U64 chunk_size = lnk_virt_size_from_chunk_ref(sect_id_map, chunk_ref);
String8 chunk_data = str8_substr(sect->layout.data, r1u64(chunk_off, chunk_off + chunk_size));
return chunk_data;
}
internal ISectOff
lnk_sc_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref)
{
ISectOff sc = {0};
sc.isect = lnk_isect_from_chunk_ref(sect_id_map, chunk_ref);
sc.off = lnk_off_from_chunk_ref(sect_id_map, chunk_ref);
return sc;
}
internal U64
lnk_virt_off_from_reloc(LNK_Section **sect_id_map, LNK_Reloc *reloc)
{
U64 virt_off = lnk_virt_off_from_chunk_ref(sect_id_map, reloc->chunk->ref);
virt_off += reloc->apply_off;
return virt_off;
}
internal U64
lnk_isect_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol)
{
Assert(LNK_Symbol_IsDefined(symbol->type));
LNK_ChunkRef symbol_chunk_ref = symbol->u.defined.u.chunk->ref;
U64 symbol_isect = lnk_isect_from_chunk_ref(sect_id_map, symbol_chunk_ref);
return symbol_isect;
}
internal U64
lnk_sect_off_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol)
{
Assert(LNK_Symbol_IsDefined(symbol->type));
LNK_ChunkRef symbol_chunk_ref = symbol->u.defined.u.chunk->ref;
U64 chunk_off = lnk_off_from_chunk_ref(sect_id_map, symbol_chunk_ref);
U64 symbol_off = chunk_off + symbol->u.defined.u.chunk_offset;
return symbol_off;
}
internal U64
lnk_virt_off_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol)
{
Assert(LNK_Symbol_IsDefined(symbol->type));
LNK_ChunkRef symbol_chunk_ref = symbol->u.defined.u.chunk->ref;
U64 chunk_voff = lnk_virt_off_from_chunk_ref(sect_id_map, symbol_chunk_ref);
U64 symbol_voff = chunk_voff + symbol->u.defined.u.chunk_offset;
return symbol_voff;
}
internal U64
lnk_file_off_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol)
{
Assert(LNK_Symbol_IsDefined(symbol->type));
LNK_ChunkRef symbol_chunk_ref = symbol->u.defined.u.chunk->ref;
U64 chunk_foff = lnk_file_off_from_chunk_ref(sect_id_map, symbol_chunk_ref);
U64 symbol_foff = chunk_foff + symbol->u.defined.u.chunk_offset;
return symbol_foff;
}
internal U64
lnk_virt_size_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol)
{
Assert(LNK_Symbol_IsDefined(symbol->type));
U64 symbol_chunk_virt_size = lnk_virt_size_from_chunk_ref(sect_id_map, symbol->u.defined.u.chunk->ref);
return symbol_chunk_virt_size;
}
internal U64
lnk_file_size_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol)
{
Assert(LNK_Symbol_IsDefined(symbol->type));
U64 symbol_chunk_file_size = lnk_file_size_from_chunk_ref(sect_id_map, symbol->u.defined.u.chunk->ref);
return symbol_chunk_file_size;
}
#if LNK_DEBUG_CHUNKS
internal void
lnk_dump_chunks(LNK_SectionTable *st)
{
Temp scratch = scratch_begin(0, 0);
LNK_ChunkPtr **chunk_id_map = lnk_chunk_id_map_from_section_table(scratch.arena, st);
LNK_Section **sect_id_map = lnk_sect_id_map_from_section_table(scratch.arena, st);
for (U64 sect_id = 0; sect_id < st->id_max; ++sect_id) {
LNK_Section *sect = sect_id_map[sect_id];
if (!sect) continue;
if (sect->is_merged) continue;
if (str8_match(sect->name, str8_lit(".text"), 0)) {
for (U64 chunk_id = 0; chunk_id < sect->cman->total_chunk_count; ++chunk_id) {
LNK_ChunkRef chunk_ref = { sect_id, chunk_id };
LNK_Chunk *chunk = lnk_chunk_from_chunk_ref(sect_id_map, chunk_id_map, chunk_ref);
U64 chunk_foff = sect->file_off + sect->layout.chunk_off_array[chunk_id];
printf("%llu {%04llX,%04llX} 0x%08llX %.*s\n", chunk_foff, sect_id, chunk_id, chunk_foff, str8_varg(chunk->debug));
}
}
}
scratch_end(scratch);
}
#endif
+151
View File
@@ -0,0 +1,151 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef struct LNK_Section
{
Arena *arena;
U64 id;
String8 name;
String8 symbol_name;
COFF_SectionFlags flags;
String8 sort_index;
LNK_ChunkManager *cman;
LNK_Chunk *root;
// overwhelming number of chunks don't have sort index and grouping
// them speeds up sort step
LNK_Chunk *nosort_chunk;
LNK_RelocList reloc_list;
B32 emit_header; // TODO: this is a hack to make reloc serializer work in resource converter
B32 has_layout;
B32 is_loose;
B32 is_merged;
U64 merge_sect_id;
U64 *id_map;
U64 isect;
U64 virt_off;
U64 file_off;
LNK_ChunkLayout layout;
} LNK_Section;
typedef struct LNK_SectionNode
{
struct LNK_SectionNode *next;
LNK_Section data;
} LNK_SectionNode;
typedef struct LNK_SectionList
{
U64 count;
LNK_SectionNode *first;
LNK_SectionNode *last;
} LNK_SectionList;
typedef struct LNK_SectionArray
{
U64 count;
LNK_Section *v;
} LNK_SectionArray;
typedef struct LNK_SectionPtrArray
{
U64 count;
LNK_Section **v;
} LNK_SectionPtrArray;
typedef struct LNK_SectionTable
{
Arena *arena;
U64 section_virt_off;
U64 sect_align;
U64 file_align;
U64 id_max;
LNK_SectionList list;
LNK_SectionList merge_list;
LNK_SectionList empties_list;
LNK_SectionNode *null_sect;
} LNK_SectionTable;
////////////////////////////////
typedef struct
{
COFF_MachineType machine;
Rng1U64 *range_arr;
LNK_Section **sect_arr;
} LNK_SectionDataBuilder;
////////////////////////////////
internal LNK_SectionNode * lnk_section_list_remove(LNK_SectionList *list, String8 name);
internal LNK_SectionNode * lnk_section_list_search_node(LNK_SectionList *list, String8 name);
internal LNK_Section * lnk_section_list_search(LNK_SectionList *list, String8 name);
internal LNK_SectionArray lnk_section_array_from_list(Arena *arena, LNK_SectionList list);
internal LNK_SectionPtrArray lnk_section_ptr_array_from_list(Arena *arena, LNK_SectionList list);
internal void lnk_section_associate_chunks(LNK_Section *sect, LNK_Chunk *head, LNK_Chunk *associate);
internal LNK_Reloc * lnk_section_push_reloc(LNK_Section *sect, LNK_Chunk *chunk, LNK_RelocType type, U64 apply_off, LNK_Symbol *symbol);
internal LNK_Reloc * lnk_section_push_reloc_undefined(LNK_Section *sect, LNK_Chunk *chunk, LNK_RelocType type, U64 apply_off, String8 undefined_symbol_name, LNK_SymbolScopeFlags scope_flags);
internal void lnk_section_merge(LNK_Section *dst, LNK_Section *src);
internal void lnk_section_build_data(LNK_Section *sect, COFF_MachineType machine);
internal String8 lnk_make_section_sort_index(Arena *arena, String8 name, COFF_SectionFlags flags, U64 section_index);
internal LNK_Chunk * lnk_section_push_chunk_raw(LNK_Section *sect, LNK_Chunk *parent, void *data_ptr, U64 data_size, String8 sort_index);
internal LNK_Chunk * lnk_section_push_chunk_data(LNK_Section *sect, LNK_Chunk *parent, String8 data, String8 sort_index);
internal LNK_Chunk * lnk_section_push_chunk_u32(LNK_Section *sect, LNK_Chunk *parent, U32 value, String8 sort_index);
internal LNK_Chunk * lnk_section_push_chunk_u64(LNK_Section *sect, LNK_Chunk *parent, U32 value, String8 sort_index);
internal LNK_Chunk * lnk_section_push_chunk_bss(LNK_Section *sect, LNK_Chunk *parent, U64 size, String8 sort_index);
internal LNK_Chunk * lnk_section_push_chunk_list(LNK_Section *sect, LNK_Chunk *parent, String8 sort_index);
internal LNK_SectionTable * lnk_section_table_alloc(U64 section_virt_off, U64 sect_align, U64 file_align);
internal void lnk_section_table_release(LNK_SectionTable **st_ptr);
internal LNK_Section * lnk_section_table_push(LNK_SectionTable *st, String8 name, COFF_SectionFlags flags);
internal LNK_Section * lnk_section_table_push_null(LNK_SectionTable *st);
internal void lnk_section_table_remove(LNK_SectionTable *st, LNK_SymbolTable *symtab, String8 name);
internal LNK_Section * lnk_section_table_search(LNK_SectionTable *st, String8 name);
internal LNK_Section * lnk_section_table_search_id(LNK_SectionTable *st, U64 id);
internal void lnk_section_table_merge(LNK_SectionTable *st, LNK_MergeDirectiveList merge_list);
internal void lnk_section_table_remove_empties(LNK_SectionTable *st, LNK_SymbolTable *symtab);
internal void lnk_section_table_build_data(TP_Context *tp, LNK_SectionTable *st, COFF_MachineType machine);
internal void lnk_section_table_assign_virtual_offsets(LNK_SectionTable *st);
internal void lnk_section_table_assign_file_offsets(LNK_SectionTable *st);
internal void lnk_section_table_assign_indices(LNK_SectionTable *st);
internal String8 lnk_section_table_serialize(Arena *arena, LNK_SectionTable *st);
internal LNK_ChunkPtr ** lnk_chunk_id_map_from_section_table(Arena *arena, LNK_SectionTable *st);
internal LNK_Section ** lnk_sect_id_map_from_section_table(Arena *arena, LNK_SectionTable *st);
internal LNK_ChunkRef lnk_get_final_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref);
internal LNK_Section * lnk_sect_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref);
internal LNK_Chunk * lnk_chunk_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkPtr **chunk_id_map, LNK_ChunkRef chunk_ref);
internal U64 lnk_isect_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref);
internal U64 lnk_off_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref);
internal U64 lnk_virt_off_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref);
internal U64 lnk_file_off_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref);
internal U64 lnk_virt_size_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref);
internal U64 lnk_file_size_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref);
internal String8 lnk_data_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref);
internal String8 lnk_data_from_chunk_ref_no_pad(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref);
internal ISectOff lnk_sc_from_chunk_ref(LNK_Section **sect_id_map, LNK_ChunkRef chunk_ref);
internal U64 lnk_virt_off_from_reloc(LNK_Section **sect_id_map, LNK_Reloc *reloc);
internal U64 lnk_isect_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol);
internal U64 lnk_sect_off_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol);
internal U64 lnk_virt_off_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol);
internal U64 lnk_file_off_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol);
internal U64 lnk_virt_size_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol);
internal U64 lnk_file_size_from_symbol(LNK_Section **sect_id_map, LNK_Symbol *symbol);
#if LNK_DEBUG_CHUNKS
internal void lnk_dump_chunks(LNK_SectionTable *st);
#endif
+832
View File
@@ -0,0 +1,832 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
global read_only LNK_Symbol g_null_symbol = { str8_lit_comp("NULL"), LNK_Symbol_DefinedStatic };
global read_only LNK_Symbol *g_null_symbol_ptr = &g_null_symbol;
internal void
lnk_init_symbol(LNK_Symbol *symbol, String8 name, LNK_SymbolType type)
{
symbol->name = name;
symbol->type = type;
}
internal void
lnk_init_defined_symbol(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags)
{
switch (visibility) {
case LNK_DefinedSymbolVisibility_Static: lnk_init_symbol(symbol, name, LNK_Symbol_DefinedStatic); break;
case LNK_DefinedSymbolVisibility_Extern: lnk_init_symbol(symbol, name, LNK_Symbol_DefinedExtern); break;
case LNK_DefinedSymbolVisibility_Internal: lnk_init_symbol(symbol, name, LNK_Symbol_DefinedInternal); break;
}
LNK_DefinedSymbol *def = &symbol->u.defined;
def->flags = flags;
def->value_type = LNK_DefinedSymbolValue_Null;
}
internal void
lnk_init_defined_symbol_chunk(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, LNK_Chunk *chunk, U64 offset, COFF_ComdatSelectType selection, U32 check_sum)
{
lnk_init_defined_symbol(symbol, name, visibility, flags);
LNK_DefinedSymbol *def = &symbol->u.defined;
def->value_type = LNK_DefinedSymbolValue_Chunk;
def->u.chunk = chunk;
def->u.chunk_offset = offset;
def->u.check_sum = check_sum;
def->u.selection = selection;
}
internal void
lnk_init_defined_symbol_va(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, U64 va)
{
lnk_init_defined_symbol(symbol, name, visibility, flags);
LNK_DefinedSymbol *def = &symbol->u.defined;
def->value_type = LNK_DefinedSymbolValue_VA;
def->u.va = va;
}
internal void
lnk_init_undefined_symbol(LNK_Symbol *symbol, String8 name, LNK_SymbolScopeFlags scope_flags)
{
lnk_init_symbol(symbol, name, LNK_Symbol_Undefined);
symbol->u.undefined.scope_flags = scope_flags;
}
internal void
lnk_init_weak_symbol(LNK_Symbol *symbol, String8 name, COFF_WeakExtType lookup, LNK_Symbol *fallback)
{
lnk_init_symbol(symbol, name, LNK_Symbol_Weak);
symbol->u.weak.scope_flags = LNK_SymbolScopeFlag_Defined;
symbol->u.weak.lookup_type = lookup;
symbol->u.weak.fallback_symbol = fallback;
}
internal void
lnk_init_lazy_symbol(LNK_Symbol *symbol, String8 name, LNK_Lib *lib, U64 member_offset)
{
lnk_init_symbol(symbol, name, LNK_Symbol_Lazy);
symbol->u.lazy.lib = lib;
symbol->u.lazy.member_offset = member_offset;
}
internal LNK_Symbol *
lnk_make_defined_symbol(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags)
{
LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1);
lnk_init_defined_symbol(symbol, name, visibility, flags);
return symbol;
}
internal LNK_Symbol *
lnk_make_defined_symbol_chunk(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, LNK_Chunk *chunk, U64 offset, COFF_ComdatSelectType selection, U32 check_sum)
{
LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1);
lnk_init_defined_symbol_chunk(symbol, name, visibility, flags, chunk, offset, selection, check_sum);
return symbol;
}
internal LNK_Symbol *
lnk_make_defined_symbol_va(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, U64 va)
{
LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1);
lnk_init_defined_symbol_va(symbol, name, visibility, flags, va);
return symbol;
}
internal LNK_Symbol *
lnk_make_undefined_symbol(Arena *arena, String8 name, LNK_SymbolScopeFlags flags)
{
LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1);
lnk_init_undefined_symbol(symbol, name, flags);
return symbol;
}
internal LNK_Symbol *
lnk_make_weak_symbol(Arena *arena, String8 name, COFF_WeakExtType lookup, LNK_Symbol *fallback)
{
LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1);
lnk_init_weak_symbol(symbol, name, lookup, fallback);
return symbol;
}
internal LNK_Symbol *
lnk_make_lazy_symbol(Arena *arena, String8 name, LNK_Lib *lib, U64 member_offset)
{
LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1);
lnk_init_lazy_symbol(symbol, name, lib, member_offset);
return symbol;
}
internal LNK_Chunk *
lnk_defined_symbol_get_chunk(LNK_DefinedSymbol *symbol)
{
if (symbol->value_type == LNK_DefinedSymbolValue_Chunk) {
return symbol->u.chunk;
}
return 0;
}
internal void
lnk_symbol_list_push_node(LNK_SymbolList *list, LNK_SymbolNode *node)
{
DLLPushBack(list->first, list->last, node);
list->count += 1;
}
internal LNK_SymbolNode *
lnk_symbol_list_push(Arena *arena, LNK_SymbolList *list, LNK_Symbol *symbol)
{
LNK_SymbolNode *node = push_array(arena, LNK_SymbolNode, 1);
node->data = symbol;
lnk_symbol_list_push_node(list, node);
return node;
}
internal void
lnk_symbol_list_push_list(LNK_SymbolList *list, LNK_SymbolList *to_push)
{
if (to_push->count) {
if (list->count) {
list->last->next = to_push->first;
to_push->first->prev = list->last;
list->last = to_push->last;
list->count += to_push->count;
} else {
*list = *to_push;
}
MemoryZeroStruct(to_push);
}
}
internal void
lnk_symbol_list_insert_after(LNK_SymbolList *list, LNK_SymbolNode *node, LNK_SymbolNode *insert)
{
DLLInsert(list->first, list->last, node, insert);
list->count += 1;
}
internal LNK_SymbolNode *
lnk_symbol_list_pop_node(LNK_SymbolList *list)
{
LNK_SymbolNode *node = 0;
if (list->count) {
node = list->first;
DLLRemove(list->first, list->last, node);
node->next = 0;
node->prev = 0;
list->count -= 1;
}
return node;
}
internal LNK_Symbol *
lnk_symbol_list_pop(LNK_SymbolList *list)
{
LNK_SymbolNode *node = lnk_symbol_list_pop_node(list);
return node ? node->data : 0;
}
internal void
lnk_symbol_list_remove(LNK_SymbolList *list, LNK_SymbolNode *node)
{
Assert(list->count > 0);
list->count -= 1;
DLLRemove(list->first, list->last, node);
node->next = 0;
node->prev = 0;
}
internal void
lnk_symbol_list_concat_in_place(LNK_SymbolList *list, LNK_SymbolList *to_concat)
{
DLLConcatInPlace(list, to_concat);
}
internal LNK_SymbolList
lnk_symbol_list_copy(Arena *arena, LNK_SymbolList list)
{
LNK_SymbolList result = {0};
LNK_SymbolNode *node_arr = push_array_no_zero(arena, LNK_SymbolNode, list.count);
for (LNK_SymbolNode *i = list.first; i != 0; i = i->next) {
Assert(result.count < list.count);
LNK_SymbolNode *n = &node_arr[result.count++];
n->data = i->data;
SLLQueuePush(result.first, result.last, n);
}
return result;
}
internal LNK_SymbolNode *
lnk_symbol_list_search_node(LNK_SymbolList list, String8 name, StringMatchFlags flags)
{
for (LNK_SymbolNode *node = list.first; node != 0; node = node->next) {
if (str8_match(node->data->name, name, flags)) {
return node;
}
}
return 0;
}
internal LNK_Symbol *
lnk_symbol_list_search(LNK_SymbolList list, String8 name, StringMatchFlags flags)
{
LNK_SymbolNode *node = lnk_symbol_list_search_node(list, name, flags);
return node ? node->data : 0;
}
internal LNK_SymbolList
lnk_symbol_list_from_array(Arena *arena, LNK_SymbolArray arr)
{
LNK_SymbolList list = {0};
LNK_SymbolNode *node_arr = push_array_no_zero(arena, LNK_SymbolNode, arr.count);
for (U64 i = 0; i < arr.count; i += 1) {
LNK_SymbolNode *node = &node_arr[i];
node->prev = node->next = 0;
node->data = &arr.v[i];
lnk_symbol_list_push_node(&list, node);
}
return list;
}
internal LNK_SymbolNodeArray
lnk_symbol_node_array_from_list(Arena *arena, LNK_SymbolList list)
{
LNK_SymbolNodeArray result = {0};
result.count = 0;
result.v = push_array_no_zero(arena, LNK_SymbolNode *, list.count);
for (LNK_SymbolNode *i = list.first; i != 0; i = i->next, ++result.count) {
result.v[result.count] = i;
}
return result;
}
internal
THREAD_POOL_TASK_FUNC(lnk_symbol_node_ptr_hasher)
{
LNK_SymbolNodePtrHasher *hasher = raw_task;
Rng1U64 range = hasher->range_arr[task_id];
for (U64 symbol_idx = range.min; symbol_idx < range.max; symbol_idx += 1) {
LNK_SymbolNode *symbol_node = hasher->input_arr[symbol_idx];
symbol_node->hash = lnk_symbol_table_hash(symbol_node->data->name);
}
}
internal void
lnk_symbol_node_ptr_array_hash(TP_Context *tp, LNK_SymbolNode **arr, U64 count)
{
Temp scratch = scratch_begin(0, 0);
LNK_SymbolNodePtrHasher hasher = {0};
hasher.input_arr = arr;
hasher.range_arr = tp_divide_work(scratch.arena, count, tp->worker_count);
tp_for_parallel(tp, 0, tp->worker_count, lnk_symbol_node_ptr_hasher, &hasher);
scratch_end(scratch);
}
internal
THREAD_POOL_TASK_FUNC(lnk_symbol_node_hasher)
{
LNK_SymbolNodeHasher *hasher = raw_task;
Rng1U64 range = hasher->range_arr[task_id];
for (U64 symbol_idx = range.min; symbol_idx < range.max; symbol_idx += 1) {
LNK_SymbolNode *symbol_node = &hasher->input_arr[symbol_idx];
symbol_node->hash = lnk_symbol_table_hash(symbol_node->data->name);
}
}
internal void
lnk_symbol_node_array_hash(TP_Context *tp, LNK_SymbolNode *arr, U64 count)
{
Temp scratch = scratch_begin(0, 0);
LNK_SymbolNodeHasher hasher = {0};
hasher.input_arr = arr;
hasher.range_arr = tp_divide_work(scratch.arena, count, tp->worker_count);
tp_for_parallel(tp, 0, tp->worker_count, lnk_symbol_node_hasher, &hasher);
scratch_end(scratch);
}
internal LNK_SymbolArray
lnk_symbol_array_from_list(Arena *arena, LNK_SymbolList list)
{
LNK_SymbolArray arr = {0};
arr.count = 0;
arr.v = push_array_no_zero(arena, LNK_Symbol, list.count);
for (LNK_SymbolNode *node = list.first; node != 0; node = node->next) {
arr.v[arr.count++] = *node->data;
}
return arr;
}
internal LNK_Symbol *
lnk_symbol_array_search(LNK_SymbolArray symarr, String8 name, StringMatchFlags flags)
{
for (U64 isym = 0; isym < symarr.count; ++isym) {
LNK_Symbol *sym = &symarr.v[isym];
if (str8_match(sym->name, name, flags)) {
return sym;
}
}
return 0;
}
internal
THREAD_POOL_TASK_FUNC(lnk_symbol_name_hasher)
{
LNK_SymbolNameHasher *task = raw_task;
Rng1U64 range = task->range_arr[task_id];
for (U64 symbol_idx = range.min; symbol_idx < range.max; symbol_idx += 1) {
LNK_Symbol *symbol = &task->symbol_arr[symbol_idx];
task->hash_arr[symbol_idx] = lnk_symbol_table_hash(symbol->name);
}
}
internal U64 *
lnk_symbol_array_hash(TP_Context *tp, Arena *arena, LNK_Symbol *arr, U64 count)
{
Temp scratch = scratch_begin(&arena, 1);
U64 stride = CeilIntegerDiv(count, tp->worker_count);
Rng1U64 *range_arr = push_array_no_zero(scratch.arena, Rng1U64, tp->worker_count);
for (U64 thread_idx = 0; thread_idx < tp->worker_count; thread_idx += 1) {
Rng1U64 *range = &range_arr[thread_idx];
range->min = Min(count, stride * thread_idx);
range->max = Min(count, range->min + stride);
}
LNK_SymbolNameHasher hasher_ctx = {0};
hasher_ctx.symbol_arr = arr;
hasher_ctx.range_arr = range_arr;
hasher_ctx.hash_arr = push_array_no_zero(arena, U64, count);
tp_for_parallel(tp, 0, tp->worker_count, lnk_symbol_name_hasher, &hasher_ctx);
scratch_end(scratch);
return hasher_ctx.hash_arr;
}
internal LNK_SymbolTable *
lnk_symbol_table_alloc(void)
{
return lnk_symbol_table_alloc_ex(0x1000, 0x100, 0x500, 0x1000);
}
internal LNK_SymbolTable *
lnk_symbol_table_alloc_ex(U64 defined_cap, U64 internal_cap, U64 weak_cap, U64 lib_cap)
{
ProfBeginDynamic("Alloc Symbol Table [Defined: 0x%llx, Internal: 0x%llx, Weak: 0x%llx, Lib: 0x%llx]", defined_cap, internal_cap, weak_cap, lib_cap);
Arena *arena = arena_alloc();
LNK_SymbolTable *symtab = push_array(arena, LNK_SymbolTable, 1);
symtab->arena = arena;
symtab->bucket_count[LNK_SymbolScopeIndex_Defined] = defined_cap;
symtab->bucket_count[LNK_SymbolScopeIndex_Internal] = internal_cap;
symtab->bucket_count[LNK_SymbolScopeIndex_Weak] = weak_cap;
symtab->bucket_count[LNK_SymbolScopeIndex_Lib] = lib_cap;
for (U64 iscope = 0; iscope < ArrayCount(symtab->buckets); ++iscope) {
symtab->buckets[iscope] = push_array(symtab->arena, LNK_SymbolList, symtab->bucket_count[iscope]);
}
ProfEnd();
return symtab;
}
internal void
lnk_symbol_table_release(LNK_SymbolTable **symtab)
{
ProfBeginFunction();
arena_release((*symtab)->arena);
*symtab = 0;
ProfEnd();
}
internal U64
lnk_symbol_table_hash(String8 string)
{
return hash_from_str8(string);
}
internal LNK_SymbolNode *
lnk_symbol_table_search_bucket(LNK_SymbolTable *symtab, LNK_SymbolScopeIndex scope_idx, U64 bucket_idx, String8 name, U64 hash)
{
for (LNK_SymbolNode *node = symtab->buckets[scope_idx][bucket_idx].first; node != 0; node = node->next) {
if (hash == node->hash && str8_match(node->data->name, name, 0)) {
return node;
}
}
return 0;
}
internal LNK_SymbolNode *
lnk_symbol_table_search_node_hash(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope_flags, String8 name, U64 hash)
{
while (scope_flags) {
LNK_SymbolScopeIndex scope_idx = ctz64(scope_flags);
scope_flags &= scope_flags - 1;
U64 bucket_idx = hash % symtab->bucket_count[scope_idx];
LNK_SymbolNode *node = lnk_symbol_table_search_bucket(symtab, scope_idx, bucket_idx, name, hash);
if (node) return node;
}
return 0;
}
internal LNK_SymbolNode *
lnk_symbol_table_search_node(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope_flags, String8 name)
{
U64 hash = lnk_symbol_table_hash(name);
return lnk_symbol_table_search_node_hash(symtab, scope_flags, name, hash);
}
internal LNK_Symbol *
lnk_symbol_table_search(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope_flags, String8 name)
{
LNK_SymbolNode *node = lnk_symbol_table_search_node(symtab, scope_flags, name);
return node ? node->data : 0;
}
internal LNK_Symbol *
lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope_flags, char *fmt, ...)
{
Temp scratch = scratch_begin(0, 0);
va_list args;
va_start(args, fmt);
String8 name = push_str8fv(scratch.arena, fmt, args);
va_end(args);
LNK_Symbol *symbol = lnk_symbol_table_search(symtab, scope_flags, name);
scratch_end(scratch);
return symbol;
}
internal void
lnk_symbol_table_remove(LNK_SymbolTable *symtab, LNK_SymbolScopeIndex scope, String8 name)
{
U64 hash = lnk_symbol_table_hash(name);
U64 ibucket = hash % symtab->bucket_count[scope];
for (;;) {
LNK_SymbolNode *node = lnk_symbol_table_search_bucket(symtab, scope, ibucket, name, hash);
if (!node) {
break;
}
LNK_SymbolList *bucket = &symtab->buckets[scope][ibucket];
DLLRemove(bucket->first, bucket->last, node);
bucket->count -= 1;
}
}
internal LNK_SymbolList *
lnk_symbol_table_bucket_from_hash(LNK_SymbolTable *symtab, LNK_SymbolScopeIndex scope_idx, U64 hash)
{
U64 bucket_idx = hash % symtab->bucket_count[scope_idx];
LNK_SymbolList *bucket = &symtab->buckets[scope_idx][bucket_idx];
return bucket;
}
internal void
lnk_symbol_table_push_(LNK_SymbolTable *symtab, LNK_SymbolScopeIndex scope_idx, LNK_SymbolNode *node, U64 hash)
{
LNK_SymbolList *bucket = lnk_symbol_table_bucket_from_hash(symtab, scope_idx, hash);
node->hash = hash;
lnk_symbol_list_push_node(bucket, node);
}
internal void
lnk_symbol_table_push_node_hash(LNK_SymbolTable *symtab, LNK_SymbolNode *node, U64 hash)
{
switch (node->data->type) {
case LNK_Symbol_Null: break;
case LNK_Symbol_DefinedExtern: {
lnk_symbol_table_push_(symtab, LNK_SymbolScopeIndex_Defined, node, hash);
} break;
case LNK_Symbol_DefinedInternal: {
lnk_symbol_table_push_(symtab, LNK_SymbolScopeIndex_Internal, node, hash);
} break;
case LNK_Symbol_Weak: {
LNK_SymbolNode *is_strong_defn_present = lnk_symbol_table_search_node(symtab, LNK_SymbolScopeFlag_Defined, node->data->name);
if (is_strong_defn_present) {
break;
}
LNK_SymbolNode *is_weak_present = lnk_symbol_table_search_node(symtab, LNK_SymbolScopeFlag_Weak, node->data->name);
if (is_weak_present) {
B32 is_fallback_same = str8_match(is_weak_present->data->u.weak.fallback_symbol->name, node->data->u.weak.fallback_symbol->name, 0);
if (!is_fallback_same) {
lnk_error(LNK_Error_MultiplyDefinedSymbol, "Weak symbol %S conflict detected, symbol defined in:", node->data->name);
lnk_supplement_error("%S", node->data->debug);
lnk_supplement_error("%S", is_weak_present->data->debug);
}
break;
}
lnk_symbol_table_push_(symtab, LNK_SymbolScopeIndex_Weak, node, hash);
} break;
case LNK_Symbol_Lazy: {
lnk_symbol_table_push_(symtab, LNK_SymbolScopeIndex_Lib, node, hash);
} break;
// symbols not supported
case LNK_Symbol_Undefined:
case LNK_Symbol_DefinedStatic: {
InvalidPath;
} break;
}
}
internal void
lnk_symbol_table_push_node(LNK_SymbolTable *symtab, LNK_SymbolNode *node)
{
U64 hash = lnk_symbol_table_hash(node->data->name);
lnk_symbol_table_push_node_hash(symtab, node, hash);
}
internal LNK_SymbolNode *
lnk_symbol_table_push(LNK_SymbolTable *symtab, LNK_Symbol *symbol)
{
LNK_SymbolNode *node = push_array(symtab->arena, LNK_SymbolNode, 1);
node->data = symbol;
lnk_symbol_table_push_node(symtab, node);
return node;
}
internal
THREAD_POOL_TASK_FUNC(lnk_lazy_symbol_inserter)
{
LNK_LazySymbolInserter *task = raw_task;
LNK_SymbolTable *symtab = task->symtab;
Rng1U64 range = task->range_arr[task_id];
for (U64 bucket_idx = range.min; bucket_idx < range.max; bucket_idx += 1) {
LNK_SymbolList *bucket = &task->bucket_arr[bucket_idx];
for (LNK_SymbolNode *curr = bucket->first, *next; curr != 0; curr = next) {
next = curr->next;
lnk_symbol_table_push_(symtab, LNK_SymbolScopeIndex_Lib, curr, curr->hash);
}
}
}
internal void
lnk_symbol_table_push_lazy_arr(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Symbol *arr, U64 count)
{
Temp scratch = scratch_begin(0,0);
ProfBegin("Push Symbol Nodes");
LNK_SymbolNode *node_arr = push_array_no_zero(symtab->arena, LNK_SymbolNode, count);
for (U64 symbol_idx = 0; symbol_idx < count; symbol_idx += 1) {
LNK_SymbolNode *node = &node_arr[symbol_idx];
node->prev = node->next = 0;
node->data = &arr[symbol_idx];
}
ProfEnd();
ProfBegin("Hash Symbol Names");
lnk_symbol_node_array_hash(tp, node_arr, count);
ProfEnd();
ProfBegin("Populate Buckets");
LNK_SymbolList *bucket_arr = push_array(scratch.arena, LNK_SymbolList, symtab->bucket_count[LNK_SymbolScopeIndex_Lib]);
for (U64 symbol_idx = 0; symbol_idx < count; symbol_idx += 1) {
LNK_SymbolNode *symbol_node = &node_arr[symbol_idx];
U64 bucket_idx = symbol_node->hash % symtab->bucket_count[LNK_SymbolScopeIndex_Lib];
lnk_symbol_list_push_node(&bucket_arr[bucket_idx], symbol_node);
}
ProfEnd();
ProfBegin("Update Symbol Table");
LNK_LazySymbolInserter symbol_inserter;
symbol_inserter.symtab = symtab;
symbol_inserter.bucket_arr = bucket_arr;
symbol_inserter.range_arr = tp_divide_work(scratch.arena, symtab->bucket_count[LNK_SymbolScopeIndex_Lib], tp->worker_count);
tp_for_parallel(tp, 0, tp->worker_count, lnk_lazy_symbol_inserter, &symbol_inserter);
ProfEnd();
scratch_end(scratch);
}
internal void
lnk_symbol_table_push_list(LNK_SymbolTable *symtab, LNK_SymbolList *list)
{
ProfBeginFunction();
MemoryZeroStruct(list);
ProfEnd();
}
internal LNK_Symbol *
lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_Symbol *resolve_symbol)
{
LNK_Symbol *symbol = resolve_symbol;
B32 run_resolver;
do {
run_resolver = 0;
switch (symbol->type) {
case LNK_Symbol_Null: break;
case LNK_Symbol_Undefined: {
LNK_UndefinedSymbol *undef_symbol = &symbol->u.undefined;
LNK_Symbol *def = lnk_symbol_table_search(symtab, undef_symbol->scope_flags, symbol->name);
if (def) {
symbol = def;
run_resolver = 1;
}
} break;
case LNK_Symbol_Weak: {
LNK_WeakSymbol *weak = &symbol->u.weak;
LNK_Symbol *def = lnk_symbol_table_search(symtab, weak->scope_flags, symbol->name);
if (def) {
Assert(LNK_Symbol_IsDefined(def->type));
symbol = def;
} else {
symbol = symbol->u.weak.fallback_symbol;
}
run_resolver = 1;
} break;
case LNK_Symbol_DefinedStatic:
case LNK_Symbol_DefinedExtern:
case LNK_Symbol_DefinedInternal: {
/* resolved */
} break;
default: NotImplemented;
}
} while (run_resolver);
return symbol;
}
internal LNK_SymbolList
lnk_pop_comdat_chain(LNK_SymbolList *bucket, LNK_SymbolNode **cursor)
{
LNK_SymbolList chain_list = {0};
LNK_SymbolNode *leader_node = *cursor;
*cursor = (*cursor)->next;
lnk_symbol_list_remove(bucket, leader_node);
lnk_symbol_list_push_node(&chain_list, leader_node);
while (*cursor) {
LNK_SymbolNode *next = (*cursor)->next;
// symbols with identical names are stored in order
if (!str8_match(leader_node->data->name, (*cursor)->data->name, 0)) {
break;
}
// move node to chain list
lnk_symbol_list_remove(bucket, *cursor);
lnk_symbol_list_push_node(&chain_list, *cursor);
// advance
*cursor = next;
}
return chain_list;
}
internal LNK_SymbolNode *
lnk_fold_comdat_chain(LNK_SymbolList chain_list)
{
LNK_SymbolNode *lead_node = chain_list.first;
if (LNK_Symbol_IsDefined(lead_node->data->type)) {
LNK_Symbol *lead = lead_node->data;
if (lead->u.defined.value_type != LNK_DefinedSymbolValue_Chunk && chain_list.count > 1) {
lnk_error(LNK_Error_MultiplyDefinedSymbol, "Unable to perfrom COMDAT fold on symbol %S, symbol must reference a section, defined in %S",
lead->name, lead->debug);
return 0;
}
}
for (LNK_SymbolNode *curr_node = lead_node->next; curr_node != 0; curr_node = curr_node->next) {
Assert(LNK_Symbol_IsDefined(lead_node->data->type));
Assert(LNK_Symbol_IsDefined(curr_node->data->type));
LNK_DefinedSymbol *lead_defined = &lead_node->data->u.defined;
LNK_DefinedSymbol *curr_defined = &curr_node->data->u.defined;
if (curr_defined->value_type != LNK_DefinedSymbolValue_Chunk) {
lnk_error(LNK_Error_MultiplyDefinedSymbol, "Unable to perfrom COMDAT fold on symbol %S, symbol must reference a section, defined in %S",
curr_node->data->name, curr_node->data->debug);
return 0;
}
// There is no mentioning of this rule in PE spec, but according to comment from lld-link in 'handleComdatSelection':
// "cl.exe picks "any" for vftabels when building with /GR- and "largest" when building /GR.". However,
// chromium links '__src_ucrt_dll_is_in_use' from MSVCRT which is not a vftable but still requires selection override.
if ((curr_defined->u.selection == COFF_ComdatSelectType_ANY && lead_defined->u.selection == COFF_ComdatSelectType_LARGEST) ||
(curr_defined->u.selection == COFF_ComdatSelectType_LARGEST && lead_defined->u.selection == COFF_ComdatSelectType_ANY)) {
lead_defined->u.selection = COFF_ComdatSelectType_LARGEST;
curr_defined->u.selection = COFF_ComdatSelectType_LARGEST;
}
// COMDATs must have same selection rule
if (lead_defined->u.selection != curr_defined->u.selection) {
String8 curr_selection_str = coff_string_from_comdat_select_type(curr_defined->u.selection);
String8 lead_selection_str = coff_string_from_comdat_select_type(lead_defined->u.selection);
lnk_error(LNK_Warning_UnresolvedComdat,
"COMDAT selection conflict detected in symbol %S defined in %S (%S), leader selection %S from %S",
curr_node->data->name, curr_node->data->debug, curr_selection_str, lead_selection_str, lead_node->data->debug);
return 0;
}
switch (curr_defined->u.selection) {
case COFF_ComdatSelectType_NULL:
case COFF_ComdatSelectType_ANY: {
// both COMDATs are valid but to get smaller exe pick smallest
LNK_Chunk *lead_chunk = lead_defined->u.chunk;
LNK_Chunk *curr_chunk = curr_defined->u.chunk;
U64 lead_chunk_size = lnk_chunk_get_size(lead_chunk);
U64 curr_chunk_size = lnk_chunk_get_size(curr_chunk);
if (curr_chunk_size < lead_chunk_size) {
lead_node = curr_node;
}
} break;
case COFF_ComdatSelectType_NODUPLICATES: {
lnk_error(LNK_Error_MultiplyDefinedSymbol, "%S: error: multiply defined symbol %S in %S.", curr_node->data->debug, curr_node->data->name, lead_node->data->debug);
} break;
case COFF_ComdatSelectType_SAME_SIZE: {
LNK_Chunk *lead_chunk = lead_defined->u.chunk;
LNK_Chunk *curr_chunk = curr_defined->u.chunk;
U64 lead_chunk_size = lnk_chunk_get_size(lead_chunk);
U64 curr_chunk_size = lnk_chunk_get_size(curr_chunk);
B32 is_same_size = (lead_chunk_size == curr_chunk_size);
if (!is_same_size) {
lnk_error(LNK_Error_MultiplyDefinedSymbol, "%S: error: multiply defined symbol %S in %S.", curr_node->data->debug, curr_node->data->name, lead_node->data->debug);
}
} break;
case COFF_ComdatSelectType_EXACT_MATCH: {
B32 is_exact_match = (lead_defined->u.check_sum == curr_defined->u.check_sum);
if (!is_exact_match) {
lnk_error(LNK_Error_MultiplyDefinedSymbol, "%S: error: multiply defined symbol %S in %S.", curr_node->data->debug, curr_node->data->name, lead_node->data->debug);
}
} break;
case COFF_ComdatSelectType_LARGEST: {
LNK_Chunk *lead_chunk = lead_defined->u.chunk;
LNK_Chunk *curr_chunk = curr_defined->u.chunk;
U64 lead_chunk_size = lnk_chunk_get_size(lead_chunk);
U64 curr_chunk_size = lnk_chunk_get_size(curr_chunk);
if (lead_chunk_size > curr_chunk_size) {
lead_node = curr_node;
}
} break;
case COFF_ComdatSelectType_ASSOCIATIVE: {
// ignore
} break;
}
}
// rewire chunks so they point to COMDAT leader
for (LNK_SymbolNode *curr_node = chain_list.first; curr_node != 0; curr_node = curr_node->next) {
if (curr_node == lead_node) {
continue;
}
LNK_DefinedSymbol *curr_defined = &curr_node->data->u.defined;
LNK_Chunk *curr_chunk = curr_defined->u.chunk;
// copy offset because after folding COMDATS we might end
// up with larger sized chunk and, for instance, a vftable
// might have a function pointer preceeding lead symbol
curr_defined->u.chunk = lead_node->data->u.defined.u.chunk;
curr_defined->u.chunk_offset = lead_node->data->u.defined.u.chunk_offset;
// discard chunk from output
curr_chunk->is_discarded = 1;
// static symbols that are not part of obj's symbol table might point to discarded chunk
curr_chunk->ref = lead_node->data->u.defined.u.chunk->ref;
}
return lead_node;
}
internal
THREAD_POOL_TASK_FUNC(lnk_comdat_folder)
{
LNK_ComdatFolder *task = raw_task;
LNK_SymbolTable *symtab = task->symtab;
Rng1U64 range = task->range_arr[task_id];
for (U64 bucket_idx = range.min; bucket_idx < range.max; ++bucket_idx) {
LNK_SymbolList *bucket = &symtab->buckets[LNK_SymbolScopeIndex_Defined][bucket_idx];
LNK_SymbolList leader_list = {0};
LNK_SymbolNode *curr = bucket->first;
while (curr) {
LNK_SymbolList chain_list = lnk_pop_comdat_chain(bucket, &curr);
LNK_SymbolNode *leader_node = lnk_fold_comdat_chain(chain_list);
if (leader_node) {
lnk_symbol_list_push_node(&leader_list, leader_node);
}
}
Assert(bucket->count == 0);
*bucket = leader_list;
}
}
internal void
lnk_fold_comdat_chunks(TP_Context *tp, LNK_SymbolTable *symtab)
{
ProfBeginFunction();
Temp scratch = scratch_begin(0, 0);
LNK_ComdatFolder folder = {0};
folder.symtab = symtab;
folder.range_arr = tp_divide_work(scratch.arena, symtab->bucket_count[LNK_SymbolScopeIndex_Defined], tp->worker_count);
tp_for_parallel(tp, 0, tp->worker_count, lnk_comdat_folder, &folder);
scratch_end(scratch);
ProfEnd();
}
+253
View File
@@ -0,0 +1,253 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef enum
{
LNK_SymbolScopeIndex_Defined,
LNK_SymbolScopeIndex_Internal, // symbols defined by linker
LNK_SymbolScopeIndex_Weak,
LNK_SymbolScopeIndex_Lib,
LNK_SymbolScopeIndex_Count
} LNK_SymbolScopeIndex;
enum
{
LNK_SymbolScopeFlag_Defined = 1,
LNK_SymbolScopeFlag_Internal = 2,
LNK_SymbolScopeFlag_Weak = 4,
LNK_SymbolScopeFlag_Lib = 8,
LNK_SymbolScopeFlag_Main = LNK_SymbolScopeFlag_Defined | LNK_SymbolScopeFlag_Weak,
LNK_SymbolScopeFlag_All = LNK_SymbolScopeFlag_Defined | LNK_SymbolScopeFlag_Weak | LNK_SymbolScopeFlag_Lib | LNK_SymbolScopeFlag_Internal
};
typedef U64 LNK_SymbolScopeFlags;
typedef enum
{
LNK_DefinedSymbolVisibility_Static,
LNK_DefinedSymbolVisibility_Extern,
LNK_DefinedSymbolVisibility_Internal,
} LNK_DefinedSymbolVisibility;
enum
{
LNK_DefinedSymbolFlag_IsFunc = (1 << 0),
LNK_DefinedSymbolFlag_IsThunk = (1 << 1),
};
typedef U64 LNK_DefinedSymbolFlags;
typedef enum
{
LNK_DefinedSymbolValue_Null,
LNK_DefinedSymbolValue_Chunk,
LNK_DefinedSymbolValue_VA
} LNK_DefinedSymbolValueType;
typedef struct LNK_DefinedSymbol
{
LNK_DefinedSymbolFlags flags;
LNK_DefinedSymbolValueType value_type;
union {
struct {
LNK_Chunk *chunk;
U64 chunk_offset;
U32 check_sum;
COFF_ComdatSelectType selection;
};
U64 va;
} u;
} LNK_DefinedSymbol;
typedef struct LNK_WeakSymbol
{
LNK_SymbolScopeFlags scope_flags;
COFF_WeakExtType lookup_type;
struct LNK_Symbol *fallback_symbol;
} LNK_WeakSymbol;
typedef struct LNK_UndefinedSymbol
{
LNK_SymbolScopeFlags scope_flags;
} LNK_UndefinedSymbol;
typedef struct LNK_LazySymbol
{
struct LNK_Lib *lib;
U64 member_offset;
} LNK_LazySymbol;
#define LNK_Symbol_IsDefined(type) ((type) == LNK_Symbol_DefinedStatic || (type) == LNK_Symbol_DefinedExtern || (type) == LNK_Symbol_DefinedInternal)
typedef enum
{
LNK_Symbol_Null,
LNK_Symbol_DefinedStatic,
LNK_Symbol_DefinedExtern,
LNK_Symbol_DefinedInternal,
LNK_Symbol_Weak,
LNK_Symbol_Lazy,
LNK_Symbol_Undefined,
} LNK_SymbolType;
#define LNK_DEBUG_SYMBOLS 1
typedef struct LNK_Symbol
{
String8 name;
LNK_SymbolType type;
union {
LNK_DefinedSymbol defined;
LNK_WeakSymbol weak;
LNK_UndefinedSymbol undefined;
LNK_LazySymbol lazy;
} u;
#if LNK_DEBUG_SYMBOLS
String8 debug;
#endif
} LNK_Symbol;
typedef struct LNK_SymbolNode
{
struct LNK_SymbolNode *next;
struct LNK_SymbolNode *prev;
U64 hash;
LNK_Symbol *data;
} LNK_SymbolNode;
typedef struct LNK_SymbolList
{
U64 count;
LNK_SymbolNode *first;
LNK_SymbolNode *last;
} LNK_SymbolList;
typedef struct LNK_SymbolNodeArray
{
U64 count;
LNK_SymbolNode **v;
} LNK_SymbolNodeArray;
typedef struct LNK_SymbolArray
{
U64 count;
LNK_Symbol *v;
} LNK_SymbolArray;
typedef struct LNK_SymbolTable
{
Arena *arena;
U64 bucket_count[LNK_SymbolScopeIndex_Count];
LNK_SymbolList *buckets[LNK_SymbolScopeIndex_Count];
} LNK_SymbolTable;
////////////////////////////////
// parallel for wrappers
typedef struct
{
LNK_Symbol *symbol_arr;
Rng1U64 *range_arr;
U64 *hash_arr;
} LNK_SymbolNameHasher;
typedef struct
{
LNK_SymbolNode **input_arr;
Rng1U64 *range_arr;
} LNK_SymbolNodePtrHasher;
typedef struct
{
LNK_SymbolNode *input_arr;
Rng1U64 *range_arr;
} LNK_SymbolNodeHasher;
typedef struct
{
LNK_SymbolTable *symtab;
LNK_SymbolList *bucket_arr;
Rng1U64 *range_arr;
} LNK_DefinedSymbolInserter;
typedef struct
{
LNK_SymbolTable *symtab;
LNK_SymbolList *bucket_arr;
Rng1U64 *range_arr;
} LNK_LazySymbolInserter;
typedef struct
{
LNK_SymbolTable *symtab;
Rng1U64 *range_arr;
} LNK_ComdatFolder;
////////////////////////////////
extern LNK_Symbol g_null_symbol;
extern LNK_Symbol *g_null_symbol_ptr;
internal void lnk_init_symbol(LNK_Symbol *symbol, String8 name, LNK_SymbolType type);
internal void lnk_init_defined_symbol(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags);
internal void lnk_init_defined_symbol_chunk(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, LNK_Chunk *chunk, U64 offset, COFF_ComdatSelectType selection, U32 check_sum);
internal void lnk_init_defined_symbol_va(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, U64 va);
internal void lnk_init_undefined_symbol(LNK_Symbol *symbol, String8 name, LNK_SymbolScopeFlags scope_flags);
internal void lnk_init_weak_symbol(LNK_Symbol *symbol, String8 name, COFF_WeakExtType lookup, LNK_Symbol *fallback);
internal LNK_Symbol * lnk_make_defined_symbol(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags);
internal LNK_Symbol * lnk_make_defined_symbol_chunk(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, LNK_Chunk *chunk, U64 offset, COFF_ComdatSelectType selection, U32 check_sum);
internal LNK_Symbol * lnk_make_defined_symbol_va(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, U64 va);
internal LNK_Symbol * lnk_make_undefined_symbol(Arena *arena, String8 name, LNK_SymbolScopeFlags scope_flags);
internal LNK_Symbol * lnk_make_weak_symbol(Arena *arena, String8 name, COFF_WeakExtType lookup, LNK_Symbol *fallback);
internal LNK_Symbol * lnk_make_lazy_symbol(Arena *arena, String8 name, struct LNK_Lib *lib, U64 member_offset);
#if LNK_DEBUG_SYMBOLS
#define lnk_symbol_set_debugf(a, s, fmt, ...) do { (s)->debug = push_str8f((a), fmt, __VA_ARGS__); } while (0)
#define lnk_symbol_set_debug(s, str) do { (s)->debug = str; } while (0)
#else
#define lnk_symbol_set_debugf(...)
#define lnk_symbol_set_debug(...)
#endif
internal LNK_Chunk * lnk_defined_symbol_get_chunk(LNK_DefinedSymbol *symbol);
internal void lnk_symbol_update_chunk_ref(LNK_Symbol *symbol, U64 src_sect_id, U64 dst_sect_id, U64 *id_map, U64 id_count);
internal void lnk_symbol_list_push_node(LNK_SymbolList *list, LNK_SymbolNode *node);
internal LNK_SymbolNode * lnk_symbol_list_push(Arena *arena, LNK_SymbolList *list, LNK_Symbol *symbol);
internal void lnk_symbol_list_push_list(LNK_SymbolList *list, LNK_SymbolList *to_push);
internal void lnk_symbol_list_insert_after(LNK_SymbolList *list, LNK_SymbolNode *node, LNK_SymbolNode *insert);
internal LNK_SymbolNode * lnk_symbol_list_pop_node(LNK_SymbolList *list);
internal LNK_Symbol * lnk_symbol_list_pop(LNK_SymbolList *list);
internal void lnk_symbol_list_remove(LNK_SymbolList *list, LNK_SymbolNode *node);
internal void lnk_symbol_list_concat_in_place(LNK_SymbolList *list, LNK_SymbolList *to_concat);
internal LNK_SymbolNodeArray lnk_symbol_node_array_from_list(Arena *arena, LNK_SymbolList list);
internal LNK_SymbolList lnk_symbol_list_from_array(Arena *arena, LNK_SymbolArray arr);
internal LNK_SymbolNodeArray lnk_symbol_node_array_from_list(Arena *arena, LNK_SymbolList list);
internal LNK_SymbolArray lnk_symbol_array_from_list(Arena *arena, LNK_SymbolList list);
internal LNK_Symbol * lnk_symbol_array_search(LNK_SymbolArray symarr, String8 name, StringMatchFlags flags);
internal U64 * lnk_symbol_array_hash(TP_Context *tp, Arena *arena, LNK_Symbol *arr, U64 count);
internal LNK_SymbolTable * lnk_symbol_table_alloc(void);
internal LNK_SymbolTable * lnk_symbol_table_alloc_ex(U64 defined_cap, U64 internal_cap, U64 weak_cap, U64 lib_cap);
internal void lnk_symbol_table_release(LNK_SymbolTable **symtab);
internal U64 lnk_symbol_table_hash(String8 string);
internal LNK_SymbolNode * lnk_symbol_table_search_bucket(LNK_SymbolTable *symtab, LNK_SymbolScopeIndex scope_idx, U64 bucket_idx, String8 name, U64 hash);
internal LNK_SymbolNode * lnk_symbol_table_search_node_hash(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope_flags, String8 name, U64 hash);
internal LNK_SymbolNode * lnk_symbol_table_search_node(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope, String8 name);
internal LNK_Symbol * lnk_symbol_table_search(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope_flags, String8 name);
internal LNK_Symbol * lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope_flags, char *fmt, ...);
internal void lnk_symbol_table_push_node_hash(LNK_SymbolTable *symtab, LNK_SymbolNode *node, U64 hash);
internal void lnk_symbol_table_push_node(LNK_SymbolTable *symtab, LNK_SymbolNode *node);
internal LNK_SymbolNode * lnk_symbol_table_push(LNK_SymbolTable *symtab, LNK_Symbol *symbol);
internal void lnk_symbol_table_push_lazy_arr(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Symbol *arr, U64 count);
internal void lnk_symbol_table_remove(LNK_SymbolTable *symtab, LNK_SymbolScopeIndex scope, String8 name);
internal void lnk_symbol_table_replace(LNK_SymbolTable *symtab, LNK_SymbolScopeIndex iscope, LNK_Symbol *symbol);
internal LNK_Symbol * lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_Symbol *resolve_symbol);
internal LNK_SymbolList lnk_pop_comdat_chain(LNK_SymbolList *bucket, LNK_SymbolNode **cursor);
internal LNK_SymbolNode * lnk_fold_comdat_chain(LNK_SymbolList chain_list);
internal void lnk_fold_comdat_chunks(TP_Context *tp, LNK_SymbolTable *symtab);
+31
View File
@@ -0,0 +1,31 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
global LNK_Timer g_timers[LNK_Timer_Count];
internal void
lnk_timer_begin(LNK_TimerType timer)
{
g_timers[timer].begin = os_now_microseconds();
}
internal void
lnk_timer_end(LNK_TimerType timer)
{
g_timers[timer].end = os_now_microseconds();
}
internal String8
lnk_string_from_timer_type(LNK_TimerType type)
{
switch (type) {
case LNK_Timer_Image: return str8_lit("Image");
case LNK_Timer_Pdb: return str8_lit("PDB");
case LNK_Timer_Rdi: return str8_lit("RDI");
case LNK_Timer_Lib: return str8_lit("Lib");
case LNK_Timer_Debug: return str8_lit("Debug");
default: InvalidPath;
}
return str8_zero();
}
+24
View File
@@ -0,0 +1,24 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
typedef enum LNK_TimerType
{
LNK_Timer_Image,
LNK_Timer_Pdb,
LNK_Timer_Rdi,
LNK_Timer_Lib,
LNK_Timer_Debug,
LNK_Timer_Count
} LNK_TimerType;
typedef struct LNK_Timer
{
U64 begin;
U64 end;
} LNK_Timer;
internal void lnk_timer_begin(LNK_TimerType timer);
internal void lnk_timer_end(LNK_TimerType timer);
+188
View File
@@ -0,0 +1,188 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal
THREAD_POOL_TASK_FUNC(os_data_size_from_file_path_task)
{
OS_DataSizeFromFilePathTask *task = raw_task;
String8 path = task->path_arr.v[task_id];
OS_Handle handle = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead, path);
FileProperties props = os_properties_from_file(handle);
task->handle_arr[task_id] = handle;
task->size_arr[task_id] = props.size;
}
internal
THREAD_POOL_TASK_FUNC(os_data_from_file_path_task)
{
OS_DataFromFilePathTask *task = raw_task;
OS_Handle handle = task->handle_arr[task_id];
U64 size = task->size_arr[task_id];
U8 *buffer = task->buffer + task->off_arr[task_id];
U64 read_size = os_file_read(handle, rng_1u64(0, size), buffer);
Assert(read_size == size);
task->data_arr.v[task_id] = str8(buffer, read_size);
os_file_close(handle);
}
internal String8Array
os_data_from_file_path_parallel(TP_Context *tp, Arena *arena, String8Array path_arr)
{
Temp scratch = scratch_begin(&arena,1);
OS_Handle *handle_arr = push_array_no_zero(scratch.arena, OS_Handle, path_arr.count);
U64 *size_arr = push_array_no_zero(scratch.arena, U64, path_arr.count);
U64 *off_arr = push_array_no_zero(scratch.arena, U64, path_arr.count);
// open handles and get sizes
OS_DataSizeFromFilePathTask sizer;
sizer.path_arr = path_arr;
sizer.size_arr = size_arr;
sizer.handle_arr = handle_arr;
tp_for_parallel(tp, 0, path_arr.count, os_data_size_from_file_path_task, &sizer);
// compute file buffer size
U64 total_data_size = sum_array_u64(path_arr.count, sizer.size_arr);
// assign offsets into file buffer
MemoryCopyTyped(off_arr, sizer.size_arr, path_arr.count);
counts_to_offsets_array_u64(path_arr.count, off_arr);
// read files and close handles
OS_DataFromFilePathTask reader;
reader.data_arr = str8_array_reserve(arena, path_arr.count);
reader.handle_arr = handle_arr;
reader.size_arr = size_arr;;
reader.off_arr = off_arr;
reader.buffer = push_array_no_zero(arena, U8, total_data_size);
tp_for_parallel(tp, 0, path_arr.count, os_data_from_file_path_task, &reader);
String8Array result = {0};
result.count = path_arr.count;
result.v = reader.data_arr.v;
scratch_end(scratch);
return result;
}
internal String8List
os_file_search(Arena *arena, String8List dir_list, String8 file_path)
{
ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 1);
String8List match_list; MemoryZeroStruct(&match_list);
if (os_file_path_exists(file_path)) {
String8 str = push_str8_copy(arena, file_path);
str8_list_push(arena, &match_list, str);
}
PathStyle file_path_style = path_style_from_str8(file_path);
B32 is_relative = file_path_style != PathStyle_WindowsAbsolute &&
file_path_style != PathStyle_UnixAbsolute;
if (is_relative) {
for (String8Node *i = dir_list.first; i != 0; i = i->next) {
String8List path_list = {0};
str8_list_push(scratch.arena, &path_list, i->string);
str8_list_push(scratch.arena, &path_list, file_path);
String8 path = str8_path_list_join_by_style(scratch.arena, &path_list, PathStyle_SystemAbsolute);
B32 file_exists = os_file_path_exists(path);
if (file_exists) {
B32 is_unique = 1;
OS_FileID file_id = os_id_from_file_path(path);
for (String8Node *k = match_list.first; k != 0; k = k->next) {
OS_FileID test_id = os_id_from_file_path(k->string);
int cmp = os_file_id_compare(test_id, file_id) != 0;
if (cmp == 0) {
is_unique = 0;
break;
}
}
if (is_unique) {
String8 str = push_str8_copy(arena, path);
str8_list_push(arena, &match_list, str);
}
}
}
}
scratch_end(scratch);
ProfEnd();
return match_list;
}
static struct
{
String8 string;
OperatingSystem os;
} g_os_map[] = {
{ str8_lit_comp("windows"), OperatingSystem_Windows, },
{ str8_lit_comp("linux"), OperatingSystem_Linux, },
{ str8_lit_comp("mac"), OperatingSystem_Mac, },
};
internal OperatingSystem
operating_system_from_string(String8 string)
{
for (U64 i = 0; i < ArrayCount(g_os_map); ++i) {
if (str8_match(g_os_map[i].string, string, StringMatchFlag_CaseInsensitive)) {
return g_os_map[i].os;
}
}
return OperatingSystem_Null;
}
internal B32
os_try_guid_from_string(String8 string, OS_Guid *guid_out)
{
Temp scratch = scratch_begin(0,0);
B32 is_parsed = 0;
String8List list = str8_split_by_string_chars(scratch.arena, string, str8_lit("-"), StringSplitFlag_KeepEmpties);
if (list.node_count == 5) {
String8 data1_str = list.first->string;
String8 data2_str = list.first->next->string;
String8 data3_str = list.first->next->next->string;
String8 data4_hi_str = list.first->next->next->next->string;
String8 data4_lo_str = list.first->next->next->next->next->string;
if (str8_is_integer(data1_str, 16) &&
str8_is_integer(data2_str, 16) &&
str8_is_integer(data3_str, 16) &&
str8_is_integer(data4_hi_str, 16) &&
str8_is_integer(data4_lo_str, 16)) {
U64 data1 = u64_from_str8(data1_str, 16);
U64 data2 = u64_from_str8(data2_str, 16);
U64 data3 = u64_from_str8(data3_str, 16);
U64 data4_hi = u64_from_str8(data4_hi_str, 16);
U64 data4_lo = u64_from_str8(data4_lo_str, 16);
if (data1 <= max_U32 &&
data2 <= max_U16 &&
data3 <= max_U16 &&
data4_hi <= max_U16 &&
data4_lo <= 0xffffffffffff) {
guid_out->data1 = (U32)data1;
guid_out->data2 = (U16)data2;
guid_out->data3 = (U16)data3;
U64 data4 = (data4_hi << 48) | data4_lo;
MemoryCopy(&guid_out->data4[0], &data4, sizeof(data4));
is_parsed = 1;
}
}
}
scratch_end(scratch);
return is_parsed;
}
internal OS_Guid
os_guid_from_string(String8 string)
{
OS_Guid guid = {0};
os_try_guid_from_string(string, &guid);
return guid;
}
+39
View File
@@ -0,0 +1,39 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
#if OS_WINDOWS
# include "os_core_win32.c"
#else
# error "undefined OS"
#endif
typedef struct
{
String8Array path_arr;
OS_Handle *handle_arr;
U64 *size_arr;
} OS_DataSizeFromFilePathTask;
typedef struct
{
String8Array data_arr;
OS_Handle *handle_arr;
U64 *size_arr;
U64 *off_arr;
U8 *buffer;
} OS_DataFromFilePathTask;
internal String8Array os_data_from_file_path_parallel(TP_Context *tp, Arena *arena, String8Array path_arr);
internal String8List os_file_search(Arena *arena, String8List dir_list, String8 file_path);
internal B32 os_folder_path_exists(String8 path);
internal OperatingSystem operating_system_from_string(String8 string);
internal B32 os_set_large_pages(B32 toggle);
internal U32 os_get_process_start_time_unix(void);
internal B32 os_try_guid_from_string(String8 string, OS_Guid *guid_out);
internal OS_Guid os_guid_from_string(String8 string);
+168
View File
@@ -0,0 +1,168 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal U32
w32_unix_time_from_file_time(FILETIME file_time)
{
U64 win32_time = ((U64)file_time.dwHighDateTime << 32) | file_time.dwLowDateTime;
U64 unix_time64 = ((win32_time - 0x19DB1DED53E8000ULL) / 10000000);
Assert(unix_time64 <= max_U32);
U32 unix_time32 = (U32)unix_time64;
return unix_time32;
}
internal B32
os_w32_has_path_volume_prefix(String8 path)
{
if (path.size >= 2) {
U8 *ptr = path.str;
U8 *opl = path.str + path.size;
UnicodeDecode a = utf8_decode(ptr, (U64)(opl-ptr));
ptr += a.inc;
UnicodeDecode b = utf8_decode(ptr, (U64)(opl-ptr));
return a.codepoint < max_U8 && char_is_alpha(a.codepoint) && b.codepoint == ':';
}
return 0;
}
internal B32
os_w32_has_device_prefix(String8 path)
{
if (path.size >= 3) {
U8 *ptr = path.str;
U8 *opl = path.str + path.size;
UnicodeDecode a = utf8_decode(ptr, (U64)(opl-ptr));
ptr += a.inc;
UnicodeDecode b = utf8_decode(ptr, (U64)(opl-ptr));
ptr += b.inc;
UnicodeDecode c = utf8_decode(ptr, (U64)(opl-ptr));
return a.codepoint == '\\' && b.codepoint == '\\' && (c.codepoint == '?' || c.codepoint == '.');
}
return 0;
}
internal B32
os_w32_has_unc_prefix(String8 path)
{
if (path.size >= 2) {
U8 *ptr = path.str;
U8 *opl = path.str + path.size;
UnicodeDecode a = utf8_decode(ptr, (U64)(opl-ptr));
ptr += a.inc;
UnicodeDecode b = utf8_decode(ptr, (U64)(opl-ptr));
return a.codepoint == '\\' && b.codepoint == '\\';
}
return 0;
}
internal B32
os_w32_has_root_drive_prefix(String8 path)
{
if (path.size >= 1) {
UnicodeDecode a = utf8_decode(path.str, path.size);
return a.codepoint == '\\';
}
return 0;
}
internal B32
os_w32_is_path_relative_current_directory(String8 path)
{
if (os_w32_has_path_volume_prefix(path)) {
return 0;
}
if (os_w32_has_device_prefix(path)) {
return 0;
}
if (os_w32_has_unc_prefix(path)) {
return 0;
}
if (os_w32_has_root_drive_prefix(path)) {
return 0;
}
return 1;
}
internal String8
os_make_full_path(Arena *arena, String8 path)
{
String8 full_path;
if (os_w32_is_path_relative_current_directory(path)) {
Temp scratch = scratch_begin(&arena, 1);
String8 current_dir = os_get_current_path(scratch.arena);
String8List list = {0};
str8_list_push(scratch.arena, &list, current_dir);
str8_list_push(scratch.arena, &list, path);
String8 temp_full_path = str8_list_join(scratch.arena, &list, &(StringJoin){ .sep = str8_lit_comp("\\") });
String8List split_full_path = str8_split_path(scratch.arena, temp_full_path);
str8_path_list_resolve_dots_in_place(&split_full_path, PathStyle_WindowsAbsolute);
full_path = str8_list_join(arena, &split_full_path, &(StringJoin){ .sep = str8_lit_comp("\\") });
scratch_end(scratch);
} else {
full_path = push_str8_copy(arena, path);
}
return full_path;
}
internal B32
os_folder_path_exists(String8 path)
{
Temp scratch = scratch_begin(0,0);
String8 actual_path = path;
if (os_w32_is_path_relative_current_directory(path)) {
String8 current = os_get_current_path(scratch.arena);
String8List list = {0};
str8_list_push(scratch.arena, &list, current);
str8_list_push(scratch.arena, &list, path);
StringJoin join = { .sep = str8_lit_comp("\\") };
actual_path =str8_list_join(scratch.arena, &list, &join);
}
String16 path16 = str16_from_8(scratch.arena, actual_path);
DWORD attributes = GetFileAttributesW((WCHAR *)path16.str);
B32 exists = (attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY);
scratch_end(scratch);
return exists;
}
internal B32
os_set_large_pages(B32 toggle)
{
B32 is_ok = 0;
HANDLE token;
if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
{
LUID luid;
if(LookupPrivilegeValue(0, SE_LOCK_MEMORY_NAME, &luid))
{
TOKEN_PRIVILEGES priv;
priv.PrivilegeCount = 1;
priv.Privileges[0].Luid = luid;
priv.Privileges[0].Attributes = toggle ? SE_PRIVILEGE_ENABLED : SE_PRIVILEGE_REMOVED;
if (AdjustTokenPrivileges(token, 0, &priv, sizeof(priv), 0, 0) == ERROR_SUCCESS) {
is_ok = 1;
}
}
CloseHandle(token);
}
return is_ok;
}
internal U32
os_get_process_start_time_unix(void)
{
HANDLE handle = GetCurrentProcess();
FILETIME start_time = {0};
FILETIME exit_time;
FILETIME kernel_time;
FILETIME user_time;
if (GetProcessTimes(handle, &start_time, &exit_time, &kernel_time, &user_time)) {
return w32_unix_time_from_file_time(start_time);
}
return 0;
}
+14
View File
@@ -0,0 +1,14 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "core/os_core.c"
#if OS_WINDOWS
//# include "core/win32/os_core_win32.c"
#elif OS_LINUX
//# include "core/linux/os_core_linux.c"
#else
# error no OS layer setup
#endif
+15
View File
@@ -0,0 +1,15 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
#include "core/os_core.h"
#if OS_WINDOWS
//# include "core/win32/os_core_win32.h"
#elif OS_LINUX
//# include "core/linux/os_core_linux.h"
#else
# error no OS layer setup
#endif
+84
View File
@@ -0,0 +1,84 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal String8
make_file_name_with_ext(Arena *arena, String8 file_name, String8 ext)
{
String8 file_name_no_ext = str8_chop_last_dot(file_name);
String8 result = push_str8f(arena, "%S.%S", file_name_no_ext, ext);
return result;
}
internal String8
make_file_path_with_ext(Arena *arena, String8 file_name, String8 ext)
{
Temp scratch = scratch_begin(&arena, 1);
String8 curr = os_get_current_path(scratch.arena);
String8 name = make_file_name_with_ext(scratch.arena, str8_skip_last_slash(file_name), ext);
String8List list = {0};
str8_list_push(scratch.arena, &list, curr);
str8_list_push(scratch.arena, &list, name);
String8 result = str8_path_list_join_by_style(arena, &list, PathStyle_SystemAbsolute);
scratch_end(scratch);
return result;
}
internal String8
path_char_from_style(PathStyle style)
{
String8 result = str8_zero();
switch (style) {
case PathStyle_Null: break;
case PathStyle_Relative: break;
case PathStyle_WindowsAbsolute: result = str8_lit("\\"); break;
case PathStyle_UnixAbsolute: result = str8_lit("/"); break;
}
return result;
}
internal String8
path_convert_slashes(Arena *arena, String8 path, PathStyle path_style)
{
Temp scratch = scratch_begin(&arena, 1);
String8List list = str8_split_path(scratch.arena, path);
StringJoin join = {0};
join.sep = path_char_from_style(path_style);
String8 result = str8_list_join(arena, &list, &join);
scratch_end(scratch);
return result;
}
internal String8
path_canon_from_regular_path(Arena *arena, String8 path)
{
Temp scratch = scratch_begin(&arena, 1);
String8 result;
result = lower_from_str8(scratch.arena, path);
result = path_convert_slashes(arena, result, PathStyle_UnixAbsolute);
scratch_end(scratch);
return result;
}
struct {
String8 string;
PathStyle path_style;
} g_path_style_map[] = {
{ str8_lit_comp("windows"), PathStyle_WindowsAbsolute },
{ str8_lit_comp("unix"), PathStyle_UnixAbsolute },
{ str8_lit_comp("system"), PathStyle_SystemAbsolute },
};
internal PathStyle
path_style_from_string(String8 string)
{
for (U64 i = 0; i < ArrayCount(g_path_style_map); ++i) {
if (str8_match(g_path_style_map[i].string, string, StringMatchFlag_CaseInsensitive)) {
return g_path_style_map[i].path_style;
}
}
return PathStyle_Null;
}
+11
View File
@@ -0,0 +1,11 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
internal String8 make_file_name_with_ext(Arena *arena, String8 file_name, String8 ext);
internal String8 make_file_path_with_ext(Arena *arena, String8 file_name, String8 ext);
internal String8 path_convert_slashes(Arena *arena, String8 path, PathStyle path_style);
internal String8 path_canon_from_regular_path(Arena *arena, String8 path);
internal PathStyle path_style_from_string(String8 string);
File diff suppressed because it is too large Load Diff
+202
View File
@@ -0,0 +1,202 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
#define MSF_PAGE_STATE_FREE 1
#define MSF_PAGE_STATE_ALLOC 0
#define MSF_FPM0 1
#define MSF_FPM1 2
#define MSF_DEFAULT_PAGE_SIZE 4096
#define MSF_DEFAULT_FPM MSF_FPM0
typedef struct MSF_PageNumberArray
{
U64 count;
MSF_PageNumber *v;
} MSF_PageNumberArray;
typedef struct MSF_PageNode
{
struct MSF_PageNode *next;
struct MSF_PageNode *prev;
MSF_PageNumber pn;
} MSF_PageNode;
typedef struct MSF_PageList
{
MSF_PageNode *first;
MSF_PageNode *last;
MSF_UInt count;
} MSF_PageList;
typedef struct MSF_Stream
{
MSF_StreamNumber sn;
MSF_UInt size;
MSF_UInt pos;
MSF_PageNode *pos_page;
MSF_PageList page_list;
} MSF_Stream;
typedef struct MSF_StreamNode
{
struct MSF_StreamNode *next;
struct MSF_StreamNode *prev;
MSF_Stream data;
} MSF_StreamNode;
typedef struct MSF_StreamList
{
MSF_UInt count;
MSF_StreamNode *first;
MSF_StreamNode *last;
} MSF_StreamList;
typedef struct MSF_PageDataNode
{
struct MSF_PageDataNode *next;
struct MSF_PageDataNode *prev;
U8 *data;
} MSF_PageDataNode;
typedef struct MSF_PageDataList
{
MSF_PageDataNode *first;
MSF_PageDataNode *last;
MSF_UInt count;
} MSF_PageDataList;
typedef struct MSF_Context
{
Arena *arena;
MSF_UInt page_size;
MSF_UInt active_fpm;
MSF_UInt fpm_rover;
MSF_PageNumber page_count;
MSF_PageDataList page_data_list;
MSF_PageDataList page_data_pool;
MSF_PageList header_page_list;
MSF_PageList root_page_list;
MSF_PageList st_page_list;
MSF_PageList page_pool;
MSF_StreamList st;
} MSF_Context;
typedef enum MSF_Error
{
MSF_Error_OK,
// if you get this error this means stream table was divided into too many
// pages, and to fix this you need to bump up the page size
MSF_Error_STREAM_TABLE_HAS_TOO_MANY_PAGES,
MSF_OpenError_NOT_ENOUGH_BYTES_TO_READ_HEADER,
MSF_OpenError_INVALID_MAGIC,
MSF_OpenError_PAGE_SIZE_IS_NOT_POW2,
MSF_OpenError_INVALID_PAGE_SIZE,
MSF_OpenError_NOT_ENOUGH_PAGES_TO_INIT,
MSF_OpenError_INVALID_ROOT_STREAM_PAGE_NUMBER,
MSF_OpenError_UNABLE_TO_READ_STREAM_TABLE_PAGE_NUMBERS,
MSF_OpenError_STREAM_COUNT_OVERFLOW,
MSF_OpenError_UNABLE_TO_READ_STREAM_SIZES,
MSF_OpenError_INVALID_STREAM_TABLE,
MSF_OpenError_INVALID_ACTIVE_FPM,
MSF_OpenError_PAGE_COUNT_DOESNT_MATCH_DATA_SIZE,
MSF_BuildError_UNABLE_TO_WRITE_STREAM_TABLE,
MSF_BuildError_UNABLE_TO_WRITE_STREAM_TABLE_PAGE_NUMBER_DIRECTORY,
MSF_BuildError_UNABLE_TO_WRITE_ROOT_DIRECTORY,
MSF_BuildError_UNABLE_TO_WRITE_HEADER,
} MSF_Error;
////////////////////////////////
typedef struct
{
MSF_UInt page_size;
MSF_PageDataList page_data_list;
MSF_PageList page_list;
MSF_UInt stream_pos;
String8 data;
Rng1U64 *range_arr;
} MSF_WriteTask;
////////////////////////////////
internal MSF_Context * msf_alloc(MSF_UInt page_size, MSF_UInt active_fpm);
internal MSF_Error msf_open(String8 data, MSF_Context **msf_out);
internal void msf_release(MSF_Context **msf_ptr);
internal MSF_Error msf_build(MSF_Context *msf);
internal U64 msf_get_save_size(MSF_Context *msf);
internal String8List msf_get_page_data_nodes(Arena *arena, MSF_Context *msf);
internal B32 msf_save(MSF_Context *msf, void *buffer, U64 buffer_size);
internal MSF_Error msf_save_arena(Arena *arena, MSF_Context *msf, String8 *data_out);
internal MSF_StreamNode * msf_find_stream_node(MSF_Context *msf, MSF_StreamNumber sn);
internal MSF_Stream * msf_find_stream(MSF_Context *msf, MSF_StreamNumber sn);
internal B32 msf_grow(MSF_Context *msf, MSF_PageNumber page_count);
internal MSF_PageNumber * msf_alloc_pn_arr(Arena *arena, MSF_Context *msf, MSF_UInt alloc_count);
internal void msf_free_pn_arr(MSF_Context *msf, MSF_PageNumber *pn_arr, MSF_UInt pn_count);
internal MSF_PageList msf_alloc_pages(MSF_Context *msf, MSF_UInt alloc_count);
internal void msf_free_pages(MSF_Context *msf, MSF_PageList *page_list);
internal MSF_StreamNumber msf_stream_alloc_ex(MSF_Context *msf, MSF_UInt size);
internal MSF_StreamNumber msf_stream_alloc(MSF_Context *msf);
internal B32 msf_stream_free(MSF_Context *msf, MSF_StreamNumber sn);
internal B32 msf_stream_resize(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt new_size);
internal B32 msf_stream_resize_ex(MSF_Context *msf, MSF_Stream *stream, MSF_UInt size);
internal void msf_stream_set_size(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt size);
internal MSF_UInt msf_stream_get_size(MSF_Context *msf, MSF_StreamNumber sn);
internal MSF_UInt msf_stream_get_cap(MSF_Context *msf, MSF_StreamNumber);
internal MSF_UInt msf_stream_get_pos(MSF_Context *msf, MSF_StreamNumber sn);
internal void msf_stream_align(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt align);
internal B32 msf_stream_reserve(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt size);
internal B32 msf_stream_seek(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt new_pos);
internal B32 msf_stream_seek_start(MSF_Context *msf, MSF_StreamNumber sn);
internal B32 msf_stream_seek_end(MSF_Context *msf, MSF_StreamNumber sn);
internal MSF_UInt msf_stream_read(MSF_Context *msf, MSF_StreamNumber sn, void *dst, MSF_UInt dst_len);
internal String8 msf_stream_read_block(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, U64 block_size);
internal String8 msf_stream_read_string(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn);
internal S8 msf_stream_read_s8(MSF_Context *msf, MSF_StreamNumber sn);
internal S16 msf_stream_read_s16(MSF_Context *msf, MSF_StreamNumber sn);
internal S32 msf_stream_read_s32(MSF_Context *msf, MSF_StreamNumber sn);
internal S64 msf_stream_read_s64(MSF_Context *msf, MSF_StreamNumber sn);
internal U8 msf_stream_read_u8(MSF_Context *msf, MSF_StreamNumber sn);
internal U16 msf_stream_read_u16(MSF_Context *msf, MSF_StreamNumber sn);
internal U32 msf_stream_read_u32(MSF_Context *msf, MSF_StreamNumber sn);
internal U64 msf_stream_read_u64(MSF_Context *msf, MSF_StreamNumber sn);
#define msf_stream_read_array(msf, sn, ptr, count) msf_stream_read(msf, sn, ptr, sizeof(*ptr) * (count))
#define msf_stream_read_struct(msf, sn, ptr) msf_stream_read_array(msf, sn, ptr, 1)
internal B32 msf_stream_write(MSF_Context *msf, MSF_StreamNumber sn, void *buffer, MSF_UInt buffer_size);
internal B32 msf_stream_write_string(MSF_Context *msf, MSF_StreamNumber sn, String8 string);
internal B32 msf_stream_write_list(MSF_Context *msf, MSF_StreamNumber sn, String8List list);
internal B32 msf_stream_write_uint(MSF_Context *msf, MSF_StreamNumber sn, MSF_UInt value);
internal B32 msf_stream_write_cstr(MSF_Context *msf, MSF_StreamNumber sn, String8 string);
internal B32 msf_stream_write_u8(MSF_Context *msf, MSF_StreamNumber sn, U8 value);
internal B32 msf_stream_write_u16(MSF_Context *msf, MSF_StreamNumber sn, U16 value);
internal B32 msf_stream_write_u32(MSF_Context *msf, MSF_StreamNumber sn, U32 value);
internal B32 msf_stream_write_u64(MSF_Context *msf, MSF_StreamNumber sn, U64 value);
internal B32 msf_stream_write_s8(MSF_Context *msf, MSF_StreamNumber sn, S8 value);
internal B32 msf_stream_write_s16(MSF_Context *msf, MSF_StreamNumber sn, S16 value);
internal B32 msf_stream_write_s32(MSF_Context *msf, MSF_StreamNumber sn, S32 value);
internal B32 msf_stream_write_s64(MSF_Context *msf, MSF_StreamNumber sn, S64 value);
internal B32 msf_stream_write_parallel(TP_Context *tp, MSF_Context *msf, MSF_StreamNumber sn, void *buffer, MSF_UInt buffer_size);
#define msf_stream_write_array(m, s, v, c) msf_stream_write(m, s, (void*)(v), sizeof(*(v)) * (c))
#define msf_stream_write_struct(m, s, v ) msf_stream_write_array(m, s, v, 1)
internal MSF_UInt msf_count_pages(MSF_UInt page_size, U64 data_size);
internal MSF_PageNumber msf_get_page_count_cap(MSF_PageDataList page_data_list, MSF_UInt page_size);
internal MSF_UInt msf_get_fpm_interval_correct(MSF_UInt page_size);
internal MSF_UInt msf_get_fpm_interval_wrong(MSF_UInt page_size);
internal MSF_UInt msf_get_fpm_idx_from_pn(MSF_UInt page_size, MSF_PageNumber pn);
internal MSF_UInt msf_get_fpm_page_bit_state(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageNumber active_fpm, MSF_PageNumber pn);
internal void msf_set_fpm_bit(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageNumber active_fpm, MSF_PageNumber pn, B32 state);
internal B32 msf_write(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageList page_list, MSF_UInt offset, void *buffer, MSF_UInt buffer_size);
internal MSF_UInt msf_read(MSF_PageDataList page_data_list, MSF_UInt page_size, MSF_PageList page_list, MSF_UInt offset, void *buffer, MSF_UInt buffer_size);
internal char * msf_error_to_string(MSF_Error code);
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal U32
pdb_hash_udt(CV_UDTInfo udt_info, String8 data)
{
B32 is_fwdref = !!(udt_info.props & CV_TypeProp_FwdRef);
B32 is_scoped = !!(udt_info.props & CV_TypeProp_Scoped);
B32 has_unique_name = !!(udt_info.props & CV_TypeProp_HasUniqueName);
B32 is_anon = has_unique_name && cv_is_udt_name_anon(udt_info.name);
U32 hash = 0;
// dbi/tpi.cpp:1918
if (!is_fwdref && !is_scoped && !is_anon) {
hash = pdb_hash_v1(udt_info.name);
}
// dbi/tpi.cpp:1937
else if (!is_fwdref && has_unique_name && is_scoped && !is_anon) {
hash = pdb_hash_v1(udt_info.unique_name);
}
// dbi/tpi.cpp 1338
else {
hash = pdb_hash_v1(data);
}
return hash;
}
internal U32
pdb_crc32_from_string(String8 string)
{
return ~update_crc32(~0, string.str, string.size);
}
+7
View File
@@ -0,0 +1,7 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
internal U32 pdb_hash_udt(CV_UDTInfo udt_info, String8 data);
internal U32 pdb_crc32_from_string(String8 string);
File diff suppressed because it is too large Load Diff
+481
View File
@@ -0,0 +1,481 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
////////////////////////////////
#define PDB_NATURAL_ALIGN 4
#define PDB_SYMBOL_ALIGN PDB_NATURAL_ALIGN
////////////////////////////////
// Hash table
#define PDB_HASH_TABLE_PACK_FUNC(name) void name(Arena *arena, String8List *local_data_srl, String8List *key_value_srl, String8 key, String8 value, void *ud)
typedef PDB_HASH_TABLE_PACK_FUNC(PDB_HashTablePackFunc);
#define PDB_HASH_TABLE_UNPACK_FUNC(name) B32 name(void *ud, String8 local_data, String8 key_value_data, U64 *key_value_cursor, String8 *key_out, String8 *value_out)
typedef PDB_HASH_TABLE_UNPACK_FUNC(PDB_HashTableUnpackFunc);
typedef struct PDB_HashTableBucket
{
String8 key;
String8 value;
} PDB_HashTableBucket;
typedef struct PDB_HashTable
{
Arena *arena;
PDB_HashTableBucket *bucket_arr;
U32Array present_bits;
U32Array deleted_bits;
U32 max;
U32 count;
} PDB_HashTable;
typedef enum
{
PDB_HashTableParseError_OK,
PDB_HashTableParseError_OUT_OF_BYTES,
PDB_HashTableParseError_CORRUPTED
} PDB_HashTableParseError;
////////////////////////////////
// String Table
typedef struct PDB_StringTableBucket
{
String8 data;
PDB_StringOffset offset;
PDB_StringIndex istr;
} PDB_StringTableBucket;
typedef struct PDB_StringTable
{
Arena *arena;
U32 version;
U32 size;
U32 bucket_count;
U32 bucket_max;
U32 *ibucket_array;
PDB_StringTableBucket **bucket_array;
} PDB_StringTable;
typedef enum
{
PDB_StringTableOpenError_OK,
PDB_StringTableOpenError_BAD_MAGIC,
PDB_StringTableOpenError_UNKNOWN_VERSION,
PDB_StringTableOpenError_CORRUPTED,
PDB_StringTableOpenError_STRING_OFFSET_OUT_OF_BOUNDS,
PDB_StringTableOpenError_OFFSETS_EXCEED_BUCKET_COUNT
} PDB_StringTableOpenError;
////////////////////////////////
// Type Server
#define PDB_TYPE_HINT_STEP 128
#define PDB_LEAF_ALIGN PDB_NATURAL_ALIGN
typedef enum
{
PDB_OpenTypeServerError_OK,
PDB_OpenTypeServerError_UNKNOWN,
PDB_OpenTypeServerError_INVALID_BUCKET_COUNT,
PDB_OpenTypeServerError_INVALID_TI_RANGE,
PDB_OpenTypeServerError_UNSUPPORTED_VERSION,
} PDB_OpenTypeServerError;
typedef struct PDB_TypeBucket
{
struct PDB_TypeBucket *next;
String8 raw_leaf;
CV_TypeIndex type_index;
} PDB_TypeBucket;
typedef struct PDB_TypeServer
{
Arena *arena;
CV_TypeIndex ti_lo;
String8List leaf_list;
U64 bucket_cap;
PDB_TypeBucket **buckets;
MSF_StreamNumber hash_sn;
PDB_HashTable hash_adj;
} PDB_TypeServer;
typedef struct PDB_TypeHashStreamInfo
{
PDB_OffsetSize hash_vals;
PDB_OffsetSize ti_offs;
PDB_OffsetSize hash_adj;
} PDB_TypeHashStreamInfo;
typedef struct PDB_TypeServerParse
{
Rng1U64 ti_range;
String8 leaf_data;
} PDB_TypeServerParse;
typedef struct
{
CV_DebugT debug_t;
U64 *udt_counts;
U64 *udt_offsets;
Rng1U64 *ranges;
PDB_TypeServer *type_server;
PDB_TypeBucket *udt_buckets;
} PDB_PushLeafTask;
typedef struct
{
PDB_TypeServer *ts;
U32 *map;
} PDB_WriteTypeToBucketMap;
typedef struct
{
U64 align;
CV_TypeIndex ti_lo;
CV_TypeIndex ti_hi;
U64 hint_count;
PDB_TpiOffHint *hint_arr;
String8Node **lf_arr;
Rng1U64 *lf_range_arr;
U64 *lf_cursor_arr;
U8 *lf_buf;
U64 lf_buf_size;
} PDB_WriteTypesTask;
////////////////////////////////
// Info
typedef struct PDB_InfoContext
{
Arena *arena;
COFF_TimeStamp time_stamp;
U32 age;
OS_Guid guid;
PDB_FeatureFlags flags;
PDB_HashTable named_stream_ht;
PDB_HashTable src_header_block_ht;
PDB_StringTable strtab;
} PDB_InfoContext;
////////////////////////////////
// SRC Header Block
typedef enum
{
PDB_SrcError_OK,
PDB_SrcError_DUPLICATE_NAME_STREAM,
PDB_SrcError_DUPLICATE_ENTRY,
PDB_SrcError_UNABLE_TO_WRITE_DATA,
PDB_SrcError_UNSUPPORTED_COMPRESSION,
PDB_SrcError_UNKNOWN
} PDB_SrcError;
////////////////////////////////
// GSI
#define PDB_GSI_V70_SYMBOL_ALIGN 4
#define PDB_GSI_V70_WORD_SIZE 32
#define PDB_GSI_V70_BUCKET_COUNT 4096
#define PDB_GSI_V70_BITMAP_COUNT ((PDB_GSI_V70_BUCKET_COUNT / PDB_GSI_V70_WORD_SIZE) + 1)
#define PDB_GSI_V70_BITMAP_SIZE (PDB_GSI_V70_BITMAP_COUNT * sizeof(U32))
typedef struct PDB_GsiContext
{
Arena *arena;
U64 word_size;
U64 symbol_align;
U64 bucket_count;
U64 symbol_count;
CV_SymbolList *bucket_arr;
} PDB_GsiContext;
typedef struct PDB_GsiSortRecord
{
ISectOff isect_off;
String8 name;
U64 offset;
} PDB_GsiSortRecord;
typedef struct PDB_GsiBuildResult
{
PDB_GsiHeader header;
U64 hash_record_count;
PDB_GsiHashRecord *hash_record_arr;
PDB_GsiSortRecord *sort_record_arr;
U64 bitmap_count;
U32 *bitmap;
U64 compressed_bucket_count;
U32 *compressed_bucket_arr;
U64 total_hash_size;
String8 symbol_data;
} PDB_GsiBuildResult;
typedef struct PDB_GsiSerializeSymbolsTask
{
U64 symbol_align;
CV_SymbolList *bucket_arr;
U64 *bucket_size_arr;
U64 *bucket_off_arr;
U8 *buffer;
PDB_GsiSortRecord **sort_record_arr_arr;
PDB_GsiSortRecord *sort_record_arr;
} PDB_GsiSerializeSymbolsTask;
////////////////////////////////
// PSI
typedef struct PDB_PsiContext
{
Arena *arena;
PDB_GsiContext *gsi;
} PDB_PsiContext;
////////////////////////////////
// DBI
#define PDB_MODULE_ALIGN PDB_NATURAL_ALIGN
typedef struct PDB_DbiModule
{
struct PDB_DbiModule *next;
MSF_StreamNumber sn;
CV_ModIndex imod;
PDB_DbiSectionContrib first_sc;
U64 sym_data_size;
U64 c11_data_size;
U64 c13_data_size;
U64 globrefs_size; // TODO: what is this for?
String8 obj_path;
String8 lib_path;
String8List source_file_list;
} PDB_DbiModule;
typedef struct PDB_DbiModuleList
{
PDB_DbiModule *first;
PDB_DbiModule *last;
U64 count;
} PDB_DbiModuleList;
typedef struct PDB_DbiSectionContribNode
{
struct PDB_DbiSectionContribNode *next;
PDB_DbiSectionContrib data;
} PDB_DbiSectionContribNode;
typedef struct PDB_DbiSectionContribList
{
PDB_DbiSectionContribNode *first;
PDB_DbiSectionContribNode *last;
U64 count;
} PDB_DbiSectionContribList;
typedef struct PDB_DbiSectionNode
{
struct PDB_DbiSectionNode *next;
COFF_SectionHeader data;
} PDB_DbiSectionNode;
typedef struct PDB_DbiSectionList
{
U64 count;
PDB_DbiSectionNode *first;
PDB_DbiSectionNode *last;
} PDB_DbiSectionList;
typedef struct PDB_DbiContext
{
Arena *arena;
U32 age;
COFF_MachineType machine;
MSF_StreamNumber globals_sn;
MSF_StreamNumber publics_sn;
MSF_StreamNumber symbols_sn;
PDB_DbiModuleList module_list;
PDB_DbiSectionContribList sec_contrib_list;
PDB_DbiSectionList section_list;
PDB_StringTable ec_names;
MSF_StreamNumber dbg_streams[PDB_DbiStream_COUNT];
} PDB_DbiContext;
////////////////////////////////
// PDB
typedef struct PDB_Context
{
Arena *arena;
MSF_Context *msf;
PDB_InfoContext *info;
PDB_DbiContext *dbi;
PDB_GsiContext *gsi;
PDB_PsiContext *psi;
PDB_TypeServer *type_servers[CV_TypeIndexSource_COUNT];
} PDB_Context;
////////////////////////////////
typedef struct
{
PDB_GsiContext *gsi;
Rng1U64 *ranges;
CV_SymbolNode **symbols;
U32 *hashes;
} GSI_SymbolHasherTask;
typedef struct
{
CV_StringHashTable string_ht;
PDB_DbiModule **mod_arr;
U16 *imod_arr;
U16 *source_file_name_count_arr;
U32 **source_file_name_offset_arr;
} PDB_DbiBuildFileInfoTask;
////////////////////////////////
// PDB
internal PDB_Context * pdb_alloc(U64 page_size, COFF_MachineType machine, COFF_TimeStamp time_stamp, U32 age, OS_Guid guid);
internal PDB_Context * pdb_open(String8 data);
internal void pdb_release(PDB_Context **pdb_ptr);
internal void pdb_build(TP_Context *tp, TP_Arena *pool_temp, PDB_Context *pdb, CV_StringHashTable string_ht);
internal void pdb_set_machine(PDB_Context *pdb, COFF_MachineType machine);
internal void pdb_set_guid(PDB_Context *pdb, OS_Guid guid);
internal void pdb_set_time_stamp(PDB_Context *pdb, COFF_TimeStamp time_stamp);
internal void pdb_set_age(PDB_Context *pdb, U32 age);
internal COFF_MachineType pdb_get_machine(PDB_Context *pdb);
internal COFF_TimeStamp pdb_get_time_stamp(PDB_Context *pdb);
internal U32 pdb_get_age(PDB_Context *pdb);
internal OS_Guid pdb_get_guid(PDB_Context *pdb);
////////////////////////////////
// Info
internal PDB_InfoContext * pdb_info_alloc(U32 age, COFF_TimeStamp time_stamp, OS_Guid guid);
internal PDB_InfoContext * pdb_info_open(MSF_Context *msf, MSF_StreamNumber sn);
internal void pdb_info_build(PDB_InfoContext *info, MSF_Context *msf, MSF_StreamNumber sn);
internal void pdb_info_release(PDB_InfoContext **info_ptr);
internal MSF_StreamNumber pdb_push_named_stream(PDB_HashTable *named_stream_ht, MSF_Context *msf, String8 name);
internal MSF_StreamNumber pdb_find_named_stream(PDB_HashTable *named_stream_ht, String8 name);
internal PDB_SrcError pdb_add_src(PDB_InfoContext *info, MSF_Context *msf, String8 file_path, String8 file_data, PDB_SrcCompType comp);
////////////////////////////////
// GSI
internal PDB_GsiContext * gsi_alloc(void);
internal PDB_GsiContext * gsi_open(MSF_Context *msf, MSF_StreamNumber sn, String8 symbol_data);
internal void gsi_build(TP_Context *tp, PDB_GsiContext *gsi, MSF_Context *msf, MSF_StreamNumber gsi_sn, MSF_StreamNumber symbols_sn);
internal void gsi_release(PDB_GsiContext **gsi_ptr);
internal void gsi_write_build_result(TP_Context *tp, PDB_GsiBuildResult build, MSF_Context *msf, MSF_StreamNumber sn, MSF_StreamNumber symbols_sn);
internal PDB_GsiBuildResult gsi_build_ex(TP_Context *tp, Arena *arena, PDB_GsiContext *gsi, U64 symbol_data_base, B32 export_symbol_ptr_arr, U64 msf_page_size);
internal U32 gsi_hash(PDB_GsiContext *gsi, String8 input);
internal CV_SymbolNode * gsi_push(PDB_GsiContext *gsi, CV_Symbol *symbol);
internal void gsi_push_many_arr(TP_Context *tp, PDB_GsiContext *gsi, U64 count, CV_SymbolNode **symbol_arr);
internal void gsi_push_many_list(PDB_GsiContext *gsi, U64 count, U32 *hash_arr, CV_SymbolList *list);
internal CV_SymbolNode * gsi_search(PDB_GsiContext *gsi, CV_Symbol *symbol);
////////////////////////////////
// PSI
internal PDB_PsiContext * psi_alloc(void);
internal PDB_PsiContext * psi_open(MSF_Context *msf, MSF_StreamNumber sn, String8 symbol_data);
internal void psi_build(TP_Context *tp, PDB_PsiContext *psi, MSF_Context *msf, MSF_StreamNumber sn, MSF_StreamNumber symbols_sn);
internal void psi_release(PDB_PsiContext **psi_ptr);
internal CV_SymbolNode * psi_push(PDB_PsiContext *psi, CV_Pub32Flags flags, U32 offset, U16 isect, String8 name);
// TODO:
//internal CV_Symbol psi_neareset_symbol(PDB_PsiContext *psi, U16 isect, U32 off);
//internal void psi_push_thunk_map(PDB_PsiContext *psi, U32 *thunk_map, U32 thunk_count, U32 thunk_size, PDB_SO *sect_map, U32 sect_count, ISectOff thunk_table);
////////////////////////////////
// DBI
internal PDB_DbiContext * dbi_alloc(COFF_MachineType machine, U32 age);
internal PDB_DbiContext * dbi_open(MSF_Context *msf, MSF_StreamNumber sn);
internal void dbi_build(TP_Context *tp, PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumber dbi_sn, CV_StringHashTable string_ht);
internal void dbi_release(PDB_DbiContext **dbi_ptr);
internal PDB_DbiModule * dbi_push_module(PDB_DbiContext *dbi, String8 obj_path, String8 lib_path);
internal String8 dbi_module_read_symbol_data(Arena *arena, MSF_Context *msf, PDB_DbiModule *mod);
internal String8 dbi_module_read_c11_data(Arena *arena, MSF_Context *msf, PDB_DbiModule *mod);
internal String8 dbi_module_read_c13_data(Arena *arena, MSF_Context *msf, PDB_DbiModule *mod);
internal void dbi_module_push_section_contrib(PDB_DbiContext *dbi, PDB_DbiModule *mod, ISectOff isect_off, U32 size, U32 data_crc, U32 reloc_crc, COFF_SectionFlags flags);
internal String8List * dbi_open_file_info(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, PDB_DbiHeader *dbi_header);
internal PDB_DbiModuleList dbi_open_module_info(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, PDB_DbiHeader *dbi_header, String8List *file_info);
internal PDB_DbiSectionContribList dbi_open_sec_contrib(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, PDB_DbiHeader *dbi_header);
internal PDB_StringTable dbi_open_ec_names(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, PDB_DbiHeader *dbi_header);
internal void dbi_open_dbg_streams(MSF_StreamNumber *dbg_streams, MSF_Context *msf, MSF_StreamNumber sn, PDB_DbiHeader *dbi_header);
internal PDB_DbiSectionList dbi_open_section_headers(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn);
internal void dbi_build_section_header_stream(PDB_DbiContext *dbi, MSF_Context *msf, MSF_StreamNumber sn);
////////////////////////////////
// Hash Table
internal void pdb_hash_table_alloc(PDB_HashTable *ht, U32 max);
internal void pdb_hash_table_release(PDB_HashTable *ht);
internal PDB_HashTableParseError pdb_hash_table_from_data(PDB_HashTable *ht, String8 data, B32 has_local_data, PDB_HashTableUnpackFunc *unpack_func, void *unpack_ud, U64 *read_bytes_out);
internal String8 pdb_data_from_hash_table(Arena *arena, PDB_HashTable *ht, B32 has_local_data, PDB_HashTablePackFunc *pack_func, void *pack_ud);
internal void pdb_hash_table_set(PDB_HashTable *ht, String8 key, String8 value);
internal B32 pdb_hash_table_get(PDB_HashTable *ht, String8 key, String8 *value_out);
internal void pdb_hash_table_delete(PDB_HashTable *ht, String8 key);
internal B32 pdb_hash_table_try_set(PDB_HashTable *ht, String8 key, String8 value);
internal B32 pdb_hash_table_is_present(PDB_HashTable *ht, U32 k);
internal B32 pdb_hash_table_is_deleted(PDB_HashTable *ht, U32 k);
internal U32 pdb_hash_table_hash(String8 key);
internal void pdb_hash_table_grow(PDB_HashTable *ht, U64 new_capacity);
internal void pdb_hash_table_get_present_keys_and_values(Arena *arena, PDB_HashTable *ht, String8Array *keys_out, String8Array *values_out);
////////////////////////////////
internal PDB_HashTableParseError pdb_hash_adj_hash_table_from_data(PDB_HashTable *ht, String8 data, PDB_StringTable *strtab, U64 *read_bytes_out);
internal PDB_HashTableParseError pdb_src_header_block_ht_from_data(PDB_HashTable *ht, String8 data, PDB_StringTable *strtab, U64 *read_bytes_out);
internal PDB_HashTableParseError pdb_named_stream_ht_from_data(PDB_HashTable *ht, String8 data, U64 *read_bytes_out);
internal String8 pdb_data_from_hash_adj_hash_table(Arena *arena, PDB_HashTable *ht, PDB_StringTable *strtab);
internal String8 pdb_data_from_src_header_block_ht(Arena *arena, PDB_HashTable *ht, PDB_StringTable *strtab);
internal String8 pdb_data_from_named_stream_ht(Arena *arena, PDB_HashTable *ht);
////////////////////////////////
// String Table
internal void pdb_strtab_alloc(PDB_StringTable *strtab, U32 max);
internal PDB_StringTableOpenError pdb_strtab_open(PDB_StringTable *strtab, MSF_Context *msf, MSF_StreamNumber sn);
internal void pdb_strtab_build(PDB_StringTable *strtab, MSF_Context *msf, MSF_StreamNumber sn);
internal void pdb_strtab_release(PDB_StringTable *strtab);
internal PDB_StringIndex pdb_strtab_add(PDB_StringTable *strtab, String8 string);
internal B32 pdb_strtab_search(PDB_StringTable *strtab, String8 string, PDB_StringIndex *index_out);
internal String8 pdb_strtab_string_from_offset(PDB_StringTable *strtab, PDB_StringOffset offset);
internal PDB_StringOffset pdb_strtab_string_to_offset(PDB_StringTable *strtab, PDB_StringIndex stridx);
internal U32 pdb_strtab_get_serialized_size(PDB_StringTable *strtab);
internal B32 pdb_strtab_try_add(PDB_StringTable *strtab, String8 string, PDB_StringIndex *index_out);
internal void pdb_strtab_grow(PDB_StringTable *strtab, U64 new_max);
internal U32 pdb_strtab_hash(PDB_StringTable *strtab, String8 string);
////////////////////////////////
// Type Server
internal PDB_OpenTypeServerError pdb_type_server_parse_from_data_v80(String8 data, PDB_TypeServerParse *parse_out);
internal PDB_OpenTypeServerError pdb_type_server_parse_from_data(String8 data, PDB_TypeServerParse *parse_out);
internal PDB_TypeServer * pdb_type_server_alloc(U64 bucket_count);
internal PDB_TypeServer * pdb_type_server_open_v80(MSF_Context *msf, MSF_StreamNumber sn, PDB_StringTable *strtab);
internal PDB_TypeServer * pdb_type_server_open(MSF_Context *msf, MSF_StreamNumber sn, PDB_StringTable *strtab);
internal void pdb_type_server_build(TP_Context *tp, PDB_TypeServer *ts, PDB_StringTable *strtab, MSF_Context *msf, MSF_StreamNumber sn);
internal void pdb_type_server_release(PDB_TypeServer **serv_ptr);
internal void pdb_type_server_push(PDB_TypeServer *ts, String8 raw_leaf);
internal void pdb_type_server_push_parallel(TP_Context *tp, PDB_TypeServer *ts, CV_DebugT types);
//internal CV_LeafNode * pdb_type_server_leaf_from_string(PDB_TypeServer *ts, String8 string);
internal String8Node * pdb_type_server_reserve(PDB_TypeServer *ts, U64 count);
internal String8Node * pdb_type_server_make_leaf(PDB_TypeServer *ts, CV_LeafKind kind, String8 data);
internal void pdb_type_server_push_bucket(PDB_TypeServer *ts, CV_Leaf *leaf);
internal PDB_TypeHashStreamInfo pdb_type_hash_stream_build(TP_Context *tp, PDB_TypeServer *ts, PDB_StringTable *strtab, MSF_Context *msf, PDB_TpiOffHint *hint_arr, U64 hint_count);
////////////////////////////////
// Enum -> String
internal String8 pdb_string_from_src_error(PDB_SrcError error);
internal String8 pdb_string_from_open_type_server_error(PDB_OpenTypeServerError error);
+89
View File
@@ -0,0 +1,89 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal U64
pdb_read_bit_vector_string(String8 data, U64 offset, U32Array *bits_out)
{
U64 cursor = offset;
U32 word_count = 0;
cursor += str8_deserial_read_struct(data, cursor, &word_count);
U64 word_data_read_size = word_count * sizeof(U32);
String8 word_data = str8(0,0);
cursor += str8_deserial_read_block(data, cursor, word_data_read_size, &word_data);
if (word_data.size == word_data_read_size) {
bits_out->count = word_count;
bits_out->v = (U32*)word_data.str;
} else {
bits_out->count = 0;
bits_out->v = 0;
}
U64 read_size = cursor - offset;
return read_size;
}
internal U64
pdb_read_bit_vector_msf(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, U32Array *bits_out)
{
// peek word count
MSF_UInt pos = msf_stream_get_pos(msf, sn);
U32 word_count = msf_stream_read_u32(msf, sn);
msf_stream_seek(msf, sn, pos);
// read out header + packed words
U64 buffer_size = sizeof(word_count) + word_count * sizeof(U32);
U8 *buffer = push_array(arena, U8, buffer_size);
MSF_UInt read_size = msf_stream_read(msf, sn, buffer, buffer_size);
Assert(read_size == buffer_size);
// parse words
U64 parse_size = pdb_read_bit_vector_string(str8(buffer, buffer_size), 0, bits_out);
return parse_size;
}
internal B32
pdb_write_bit_vector(MSF_Context *msf, MSF_StreamNumber sn, B32 *flag_array, U64 flag_count)
{
B32 is_write_ok = 0;
U32 word_size = sizeof(U32);
U32 bits_per_word = MSF_BITS_PER_CHAR * word_size;
U32 word_count = (flag_count + MSF_BITS_PER_CHAR) / MSF_BITS_PER_CHAR;
is_write_ok = msf_stream_write_struct(msf, sn, &word_count);
if (is_write_ok) {
for (U64 iword = 0, iflag = 0; iword < word_count; ++iword) {
U32 word = 0;
for (U64 iflag_opl = Min(flag_count, iflag + MSF_BITS_PER_CHAR); iflag < iflag_opl; ++iflag) {
if (flag_array[iflag]) {
word |= 1 << (iflag % bits_per_word);
}
}
is_write_ok = msf_stream_write_struct(msf, sn, &word);
if (!is_write_ok) {
break;
}
}
}
return is_write_ok;
}
internal U64
pdb_get_bit_vector_size(U32 bucket_count)
{
U32 word_size = sizeof(U32);
U32 word_count = (bucket_count + MSF_BITS_PER_CHAR) / MSF_BITS_PER_CHAR;
U64 result = 0;
result += sizeof(word_count);
result += word_size * word_count;
return result;
}
+14
View File
@@ -0,0 +1,14 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
internal U32 pdb_hash_v1(String8 data);
internal U32 pdb_hash_udt(CV_UDTInfo udt_info, String8 data);
internal U64 pdb_read_bit_vector_string(String8 data, U64 offset, U32Array *bits_out);
internal U64 pdb_read_bit_vector_msf(Arena *arena, MSF_Context *msf, MSF_StreamNumber sn, U32Array *bits_out);
internal B32 pdb_write_bit_vector(MSF_Context *msf, MSF_StreamNumber sn, B32 *flag_array, U64 flag_count);
internal U64 pdb_get_bit_vector_size(U32 bucket_count);
+15
View File
@@ -0,0 +1,15 @@
internal String8
rdi_string_from_name_map_kind(RDI_NameMapKind kind)
{
switch (kind) {
case RDI_NameMapKind_NULL : return str8_lit("NULL");
case RDI_NameMapKind_GlobalVariables : return str8_lit("GlobalVariables");
case RDI_NameMapKind_ThreadVariables : return str8_lit("ThreadVariables");
case RDI_NameMapKind_Procedures : return str8_lit("Procedures");
case RDI_NameMapKind_Types : return str8_lit("Types");
case RDI_NameMapKind_LinkNameProcedures: return str8_lit("LinkNameProcedures");
case RDI_NameMapKind_NormalSourcePaths : return str8_lit("NormalSourcePaths");
}
return str8_lit("");
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
internal String8 rdi_string_from_name_map_kind(RDI_NameMapKind kind);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+53
View File
@@ -0,0 +1,53 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal RDI_Arch
rdi_arch_from_coff_machine(COFF_MachineType machine)
{
switch (machine) {
case COFF_MachineType_X86: return RDI_Arch_X86;
case COFF_MachineType_X64: return RDI_Arch_X64;
case COFF_MachineType_UNKNOWN:
case COFF_MachineType_AM33:
case COFF_MachineType_ARM:
case COFF_MachineType_ARM64:
case COFF_MachineType_ARMNT:
case COFF_MachineType_EBC:
case COFF_MachineType_IA64:
case COFF_MachineType_M32R:
case COFF_MachineType_MIPS16:
case COFF_MachineType_MIPSFPU:
case COFF_MachineType_MIPSFPU16:
case COFF_MachineType_POWERPC:
case COFF_MachineType_POWERPCFP:
case COFF_MachineType_R4000:
case COFF_MachineType_RISCV32:
case COFF_MachineType_RISCV64:
case COFF_MachineType_SH3:
case COFF_MachineType_SH3DSP:
case COFF_MachineType_SH4:
case COFF_MachineType_SH5:
case COFF_MachineType_THUMB:
case COFF_MachineType_WCEMIPSV2:
NotImplemented;
default:
return RDI_Arch_NULL;
}
}
internal RDI_BinarySectionFlags
rdi_binary_section_flags_from_coff_section_flags(COFF_SectionFlags flags)
{
RDI_BinarySectionFlags result = 0;
if (flags & COFF_SectionFlag_MEM_READ) {
result |= RDI_BinarySectionFlag_Read;
}
if (flags & COFF_SectionFlag_MEM_WRITE) {
result |= RDI_BinarySectionFlag_Write;
}
if (flags & COFF_SectionFlag_MEM_EXECUTE) {
result |= RDI_BinarySectionFlag_Execute;
}
return result;
}
+7
View File
@@ -0,0 +1,7 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
internal RDI_Arch rdi_arch_from_coff_machine(COFF_MachineType machine);
+244
View File
@@ -0,0 +1,244 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal RDI_Arch
rdi_arch_from_cv_arch(CV_Arch arch)
{
switch (arch) {
case CV_Arch_8086: return RDI_Arch_X86;
case CV_Arch_X64: return RDI_Arch_X64;
case CV_Arch_8080:
case CV_Arch_80286:
case CV_Arch_80386:
case CV_Arch_80486:
case CV_Arch_PENTIUM:
case CV_Arch_PENTIUMII:
case CV_Arch_PENTIUMIII:
case CV_Arch_MIPS:
case CV_Arch_MIPS16:
case CV_Arch_MIPS32:
case CV_Arch_MIPS64:
case CV_Arch_MIPSI:
case CV_Arch_MIPSII:
case CV_Arch_MIPSIII:
case CV_Arch_MIPSIV:
case CV_Arch_MIPSV:
case CV_Arch_M68000:
case CV_Arch_M68010:
case CV_Arch_M68020:
case CV_Arch_M68030:
case CV_Arch_M68040:
case CV_Arch_ALPHA:
case CV_Arch_ALPHA_21164:
case CV_Arch_ALPHA_21164A:
case CV_Arch_ALPHA_21264:
case CV_Arch_ALPHA_21364:
case CV_Arch_PPC601:
case CV_Arch_PPC603:
case CV_Arch_PPC604:
case CV_Arch_PPC620:
case CV_Arch_PPCFP:
case CV_Arch_PPCBE:
case CV_Arch_SH3:
case CV_Arch_SH3E:
case CV_Arch_SH3DSP:
case CV_Arch_SH4:
case CV_Arch_SHMEDIA:
case CV_Arch_ARM3:
case CV_Arch_ARM4:
case CV_Arch_ARM4T:
case CV_Arch_ARM5:
case CV_Arch_ARM5T:
case CV_Arch_ARM6:
case CV_Arch_ARM_XMAC:
case CV_Arch_ARM_WMMX:
case CV_Arch_ARM7:
case CV_Arch_OMNI:
case CV_Arch_IA64_1:
case CV_Arch_IA64_2:
case CV_Arch_CEE:
case CV_Arch_AM33:
case CV_Arch_M32R:
case CV_Arch_TRICORE:
case CV_Arch_EBC:
case CV_Arch_THUMB:
case CV_Arch_ARMNT:
case CV_Arch_ARM64:
case CV_Arch_D3D11_SHADER:
NotImplemented;
default:
return RDI_Arch_NULL;
}
}
internal RDI_Language
rdi_language_from_cv_language(CV_Language language)
{
switch (language) {
case CV_Language_C: return RDI_Language_C;
case CV_Language_CXX: return RDI_Language_CPlusPlus;
case CV_Language_MASM: return RDI_Language_Masm;
case CV_Language_LINK: return RDI_Language_NULL;
case CV_Language_CVTRES: return RDI_Language_NULL;
case CV_Language_FORTRAN:
case CV_Language_PASCAL:
case CV_Language_BASIC:
case CV_Language_COBOL:
case CV_Language_CVTPGD:
case CV_Language_CSHARP:
case CV_Language_VB:
case CV_Language_ILASM:
case CV_Language_JAVA:
case CV_Language_JSCRIPT:
case CV_Language_MSIL:
case CV_Language_HLSL:
NotImplemented;
default:
return RDI_Language_NULL;
}
}
internal RDI_TypeModifierFlags
rdi_type_modifier_flags_from_cv_modifier_flags(CV_ModifierFlags flags)
{
RDI_TypeModifierFlags result = 0;
if (flags & CV_ModifierFlag_Const) {
result |= RDI_TypeModifierFlag_Const;
}
if (flags & CV_ModifierFlag_Volatile) {
result |= RDI_TypeModifierFlag_Volatile;
}
return result;
}
internal RDI_TypeModifierFlags
rdi_type_modifier_flags_from_cv_pointer_attribs(CV_PointerAttribs attribs)
{
RDI_TypeModifierFlags result = 0;
if (attribs & CV_PointerAttrib_Const) {
result |= RDI_TypeModifierFlag_Const;
}
if (attribs & CV_PointerAttrib_Volatile) {
result |= RDI_TypeModifierFlag_Volatile;
}
return result;
}
internal RDI_TypeKind
rdi_type_kind_from_pointer(CV_PointerAttribs attribs, CV_PointerMode mode)
{
RDI_TypeKind result = RDI_TypeKind_Ptr;
if (attribs & CV_PointerAttrib_LRef) {
result = RDI_TypeKind_LRef;
} else if (attribs & CV_PointerAttrib_RRef) {
result = RDI_TypeKind_RRef;
}
if (mode == CV_PointerMode_LRef) {
result = RDI_TypeKind_LRef;
} else if (mode == CV_PointerMode_RRef) {
result = RDI_TypeKind_RRef;
}
return result;
}
internal RDI_TypeKind
rdi_type_kind_from_cv_basic_type(CV_BasicType basic_type)
{
switch (basic_type) {
case CV_BasicType_NOTYPE : return RDI_TypeKind_NULL;
case CV_BasicType_ABS : return RDI_TypeKind_NULL;
case CV_BasicType_SEGMENT : return RDI_TypeKind_NULL;
case CV_BasicType_VOID : return RDI_TypeKind_Void;
case CV_BasicType_CURRENCY : return RDI_TypeKind_NULL;
case CV_BasicType_NBASICSTR : return RDI_TypeKind_NULL;
case CV_BasicType_FBASICSTR : return RDI_TypeKind_NULL;
case CV_BasicType_HRESULT : return RDI_TypeKind_Handle;
case CV_BasicType_CHAR : return RDI_TypeKind_Char8;
case CV_BasicType_SHORT : return RDI_TypeKind_S16;
case CV_BasicType_LONG : return RDI_TypeKind_S32;
case CV_BasicType_QUAD : return RDI_TypeKind_S64;
case CV_BasicType_OCT : return RDI_TypeKind_S128;
case CV_BasicType_UCHAR : return RDI_TypeKind_UChar8;
case CV_BasicType_USHORT : return RDI_TypeKind_U16;
case CV_BasicType_ULONG : return RDI_TypeKind_U32;
case CV_BasicType_UQUAD : return RDI_TypeKind_U64;
case CV_BasicType_UOCT : return RDI_TypeKind_U128;
case CV_BasicType_BOOL8 : return RDI_TypeKind_S8;
case CV_BasicType_BOOL16 : return RDI_TypeKind_S16;
case CV_BasicType_BOOL32 : return RDI_TypeKind_S32;
case CV_BasicType_BOOL64 : return RDI_TypeKind_S64;
case CV_BasicType_FLOAT32 : return RDI_TypeKind_F32;
case CV_BasicType_FLOAT64 : return RDI_TypeKind_F64;
case CV_BasicType_FLOAT80 : return RDI_TypeKind_F80;
case CV_BasicType_FLOAT128 : return RDI_TypeKind_F128;
case CV_BasicType_FLOAT48 : return RDI_TypeKind_F48;
case CV_BasicType_FLOAT32PP : return RDI_TypeKind_F32PP;
case CV_BasicType_FLOAT16 : return RDI_TypeKind_F16;
case CV_BasicType_COMPLEX32 : return RDI_TypeKind_ComplexF32;
case CV_BasicType_COMPLEX64 : return RDI_TypeKind_ComplexF64;
case CV_BasicType_COMPLEX80 : return RDI_TypeKind_ComplexF80;
case CV_BasicType_COMPLEX128: return RDI_TypeKind_ComplexF128;
case CV_BasicType_BIT : return RDI_TypeKind_NULL;
case CV_BasicType_PASCHAR : return RDI_TypeKind_NULL;
case CV_BasicType_BOOL32FF : return RDI_TypeKind_NULL;
case CV_BasicType_INT8 : return RDI_TypeKind_S8;
case CV_BasicType_UINT8 : return RDI_TypeKind_U8;
case CV_BasicType_RCHAR : return RDI_TypeKind_Char8;
case CV_BasicType_WCHAR : return RDI_TypeKind_UChar16;
case CV_BasicType_CHAR16 : return RDI_TypeKind_Char16;
case CV_BasicType_CHAR32 : return RDI_TypeKind_Char32;
case CV_BasicType_INT16 : return RDI_TypeKind_S16;
case CV_BasicType_UINT16 : return RDI_TypeKind_U16;
case CV_BasicType_INT32 : return RDI_TypeKind_S32;
case CV_BasicType_UINT32 : return RDI_TypeKind_U32;
case CV_BasicType_INT64 : return RDI_TypeKind_S64;
case CV_BasicType_UINT64 : return RDI_TypeKind_U64;
case CV_BasicType_INT128 : return RDI_TypeKind_S128;
case CV_BasicType_UINT128 : return RDI_TypeKind_U128;
case CV_BasicType_CHAR8 : return RDI_TypeKind_Char8;
case CV_BasicType_PTR : return RDI_TypeKind_Ptr;
}
return RDI_TypeKind_NULL;
}
internal RDI_RegCode
rdi_reg_code_from_cv(CV_Arch arch, CV_Reg reg)
{
RDI_RegCode result = 0;
switch (arch) {
case CV_Arch_8086: {
switch (reg) {
#define X(CVN,C,RDN,BP,BZ) case C: result = RDI_RegCodeX86_##RDN; break;
CV_Reg_X86_XList(X)
#undef X
}
} break;
case CV_Arch_X64: {
switch (reg) {
#define X(CVN,C,RDN,BP,BZ) case C: result = RDI_RegCodeX64_##RDN; break;
CV_Reg_X64_XList(X)
#undef X
}
} break;
default: NotImplemented;
}
return result;
}
internal RDI_ChecksumKind
rdi_checksum_from_cv_c13(CV_C13ChecksumKind kind)
{
switch (kind) {
case CV_C13ChecksumKind_Null: return RDI_Checksum_Null;
case CV_C13ChecksumKind_MD5: return RDI_Checksum_MD5;
case CV_C13ChecksumKind_SHA1: return RDI_Checksum_SHA1;
case CV_C13ChecksumKind_SHA256: return RDI_Checksum_SHA256;
}
InvalidPath;
return RDI_Checksum_Null;
}
+14
View File
@@ -0,0 +1,14 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#pragma once
internal RDI_Arch rdi_arch_from_cv_arch(CV_Arch arch);
internal RDI_Language rdi_language_from_cv_language(CV_Language language);
internal RDI_TypeModifierFlags rdi_type_modifier_flags_from_cv_pointer_attribs(CV_PointerAttribs attribs);
internal RDI_TypeKind rdi_type_kind_from_cv_basic_type(CV_BasicType basic_type);
internal RDI_RegCode rdi_reg_code_from_cv(CV_Arch arch, CV_Reg reg);
internal RDI_ChecksumKind rdi_checksum_from_cv_c13(CV_C13ChecksumKind kind);
+20
View File
@@ -0,0 +1,20 @@
#pragma once
typedef U8 RDI_U8;
typedef U16 RDI_U16;
typedef U32 RDI_U32;
typedef U64 RDI_U64;
typedef S8 RDI_S8;
typedef S16 RDI_S16;
typedef S32 RDI_S32;
typedef S64 RDI_S64;
#define RDI_PROC internal
#define RDIM_MEMSET_OVERRIDE
#define rdim_memset MemorySet
#define RDIM_MEMCPY_OVERRIDE
#define rdim_memcpy MemoryCopy
#define rdim_vsnprintf raddbg_vsnprintf
@@ -0,0 +1,330 @@
This work is released into the public domain with CC0 1.0. Alternatively, it is
licensed under the Apache License 2.0.
-------------------------------------------------------------------------------
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.
-------------------------------------------------------------------------------
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2019 Jack O'Connor and Samuel Neves
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,616 @@
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include "blake3.h"
#include "blake3_impl.h"
const char *blake3_version(void) { return BLAKE3_VERSION_STRING; }
INLINE void chunk_state_init(blake3_chunk_state *self, const uint32_t key[8],
uint8_t flags) {
memcpy(self->cv, key, BLAKE3_KEY_LEN);
self->chunk_counter = 0;
memset(self->buf, 0, BLAKE3_BLOCK_LEN);
self->buf_len = 0;
self->blocks_compressed = 0;
self->flags = flags;
}
INLINE void chunk_state_reset(blake3_chunk_state *self, const uint32_t key[8],
uint64_t chunk_counter) {
memcpy(self->cv, key, BLAKE3_KEY_LEN);
self->chunk_counter = chunk_counter;
self->blocks_compressed = 0;
memset(self->buf, 0, BLAKE3_BLOCK_LEN);
self->buf_len = 0;
}
INLINE size_t chunk_state_len(const blake3_chunk_state *self) {
return (BLAKE3_BLOCK_LEN * (size_t)self->blocks_compressed) +
((size_t)self->buf_len);
}
INLINE size_t chunk_state_fill_buf(blake3_chunk_state *self,
const uint8_t *input, size_t input_len) {
size_t take = BLAKE3_BLOCK_LEN - ((size_t)self->buf_len);
if (take > input_len) {
take = input_len;
}
uint8_t *dest = self->buf + ((size_t)self->buf_len);
memcpy(dest, input, take);
self->buf_len += (uint8_t)take;
return take;
}
INLINE uint8_t chunk_state_maybe_start_flag(const blake3_chunk_state *self) {
if (self->blocks_compressed == 0) {
return CHUNK_START;
} else {
return 0;
}
}
typedef struct {
uint32_t input_cv[8];
uint64_t counter;
uint8_t block[BLAKE3_BLOCK_LEN];
uint8_t block_len;
uint8_t flags;
} output_t;
INLINE output_t make_output(const uint32_t input_cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags) {
output_t ret;
memcpy(ret.input_cv, input_cv, 32);
memcpy(ret.block, block, BLAKE3_BLOCK_LEN);
ret.block_len = block_len;
ret.counter = counter;
ret.flags = flags;
return ret;
}
// Chaining values within a given chunk (specifically the compress_in_place
// interface) are represented as words. This avoids unnecessary bytes<->words
// conversion overhead in the portable implementation. However, the hash_many
// interface handles both user input and parent node blocks, so it accepts
// bytes. For that reason, chaining values in the CV stack are represented as
// bytes.
INLINE void output_chaining_value(const output_t *self, uint8_t cv[32]) {
uint32_t cv_words[8];
memcpy(cv_words, self->input_cv, 32);
blake3_compress_in_place(cv_words, self->block, self->block_len,
self->counter, self->flags);
store_cv_words(cv, cv_words);
}
INLINE void output_root_bytes(const output_t *self, uint64_t seek, uint8_t *out,
size_t out_len) {
uint64_t output_block_counter = seek / 64;
size_t offset_within_block = seek % 64;
uint8_t wide_buf[64];
while (out_len > 0) {
blake3_compress_xof(self->input_cv, self->block, self->block_len,
output_block_counter, self->flags | ROOT, wide_buf);
size_t available_bytes = 64 - offset_within_block;
size_t memcpy_len;
if (out_len > available_bytes) {
memcpy_len = available_bytes;
} else {
memcpy_len = out_len;
}
memcpy(out, wide_buf + offset_within_block, memcpy_len);
out += memcpy_len;
out_len -= memcpy_len;
output_block_counter += 1;
offset_within_block = 0;
}
}
INLINE void chunk_state_update(blake3_chunk_state *self, const uint8_t *input,
size_t input_len) {
if (self->buf_len > 0) {
size_t take = chunk_state_fill_buf(self, input, input_len);
input += take;
input_len -= take;
if (input_len > 0) {
blake3_compress_in_place(
self->cv, self->buf, BLAKE3_BLOCK_LEN, self->chunk_counter,
self->flags | chunk_state_maybe_start_flag(self));
self->blocks_compressed += 1;
self->buf_len = 0;
memset(self->buf, 0, BLAKE3_BLOCK_LEN);
}
}
while (input_len > BLAKE3_BLOCK_LEN) {
blake3_compress_in_place(self->cv, input, BLAKE3_BLOCK_LEN,
self->chunk_counter,
self->flags | chunk_state_maybe_start_flag(self));
self->blocks_compressed += 1;
input += BLAKE3_BLOCK_LEN;
input_len -= BLAKE3_BLOCK_LEN;
}
size_t take = chunk_state_fill_buf(self, input, input_len);
input += take;
input_len -= take;
}
INLINE output_t chunk_state_output(const blake3_chunk_state *self) {
uint8_t block_flags =
self->flags | chunk_state_maybe_start_flag(self) | CHUNK_END;
return make_output(self->cv, self->buf, self->buf_len, self->chunk_counter,
block_flags);
}
INLINE output_t parent_output(const uint8_t block[BLAKE3_BLOCK_LEN],
const uint32_t key[8], uint8_t flags) {
return make_output(key, block, BLAKE3_BLOCK_LEN, 0, flags | PARENT);
}
// Given some input larger than one chunk, return the number of bytes that
// should go in the left subtree. This is the largest power-of-2 number of
// chunks that leaves at least 1 byte for the right subtree.
INLINE size_t left_len(size_t content_len) {
// Subtract 1 to reserve at least one byte for the right side. content_len
// should always be greater than BLAKE3_CHUNK_LEN.
size_t full_chunks = (content_len - 1) / BLAKE3_CHUNK_LEN;
return round_down_to_power_of_2(full_chunks) * BLAKE3_CHUNK_LEN;
}
// Use SIMD parallelism to hash up to MAX_SIMD_DEGREE chunks at the same time
// on a single thread. Write out the chunk chaining values and return the
// number of chunks hashed. These chunks are never the root and never empty;
// those cases use a different codepath.
INLINE size_t compress_chunks_parallel(const uint8_t *input, size_t input_len,
const uint32_t key[8],
uint64_t chunk_counter, uint8_t flags,
uint8_t *out) {
#if defined(BLAKE3_TESTING)
assert(0 < input_len);
assert(input_len <= MAX_SIMD_DEGREE * BLAKE3_CHUNK_LEN);
#endif
const uint8_t *chunks_array[MAX_SIMD_DEGREE];
size_t input_position = 0;
size_t chunks_array_len = 0;
while (input_len - input_position >= BLAKE3_CHUNK_LEN) {
chunks_array[chunks_array_len] = &input[input_position];
input_position += BLAKE3_CHUNK_LEN;
chunks_array_len += 1;
}
blake3_hash_many(chunks_array, chunks_array_len,
BLAKE3_CHUNK_LEN / BLAKE3_BLOCK_LEN, key, chunk_counter,
true, flags, CHUNK_START, CHUNK_END, out);
// Hash the remaining partial chunk, if there is one. Note that the empty
// chunk (meaning the empty message) is a different codepath.
if (input_len > input_position) {
uint64_t counter = chunk_counter + (uint64_t)chunks_array_len;
blake3_chunk_state chunk_state;
chunk_state_init(&chunk_state, key, flags);
chunk_state.chunk_counter = counter;
chunk_state_update(&chunk_state, &input[input_position],
input_len - input_position);
output_t output = chunk_state_output(&chunk_state);
output_chaining_value(&output, &out[chunks_array_len * BLAKE3_OUT_LEN]);
return chunks_array_len + 1;
} else {
return chunks_array_len;
}
}
// Use SIMD parallelism to hash up to MAX_SIMD_DEGREE parents at the same time
// on a single thread. Write out the parent chaining values and return the
// number of parents hashed. (If there's an odd input chaining value left over,
// return it as an additional output.) These parents are never the root and
// never empty; those cases use a different codepath.
INLINE size_t compress_parents_parallel(const uint8_t *child_chaining_values,
size_t num_chaining_values,
const uint32_t key[8], uint8_t flags,
uint8_t *out) {
#if defined(BLAKE3_TESTING)
assert(2 <= num_chaining_values);
assert(num_chaining_values <= 2 * MAX_SIMD_DEGREE_OR_2);
#endif
const uint8_t *parents_array[MAX_SIMD_DEGREE_OR_2];
size_t parents_array_len = 0;
while (num_chaining_values - (2 * parents_array_len) >= 2) {
parents_array[parents_array_len] =
&child_chaining_values[2 * parents_array_len * BLAKE3_OUT_LEN];
parents_array_len += 1;
}
blake3_hash_many(parents_array, parents_array_len, 1, key,
0, // Parents always use counter 0.
false, flags | PARENT,
0, // Parents have no start flags.
0, // Parents have no end flags.
out);
// If there's an odd child left over, it becomes an output.
if (num_chaining_values > 2 * parents_array_len) {
memcpy(&out[parents_array_len * BLAKE3_OUT_LEN],
&child_chaining_values[2 * parents_array_len * BLAKE3_OUT_LEN],
BLAKE3_OUT_LEN);
return parents_array_len + 1;
} else {
return parents_array_len;
}
}
// The wide helper function returns (writes out) an array of chaining values
// and returns the length of that array. The number of chaining values returned
// is the dynamically detected SIMD degree, at most MAX_SIMD_DEGREE. Or fewer,
// if the input is shorter than that many chunks. The reason for maintaining a
// wide array of chaining values going back up the tree, is to allow the
// implementation to hash as many parents in parallel as possible.
//
// As a special case when the SIMD degree is 1, this function will still return
// at least 2 outputs. This guarantees that this function doesn't perform the
// root compression. (If it did, it would use the wrong flags, and also we
// wouldn't be able to implement extendable output.) Note that this function is
// not used when the whole input is only 1 chunk long; that's a different
// codepath.
//
// Why not just have the caller split the input on the first update(), instead
// of implementing this special rule? Because we don't want to limit SIMD or
// multi-threading parallelism for that update().
static size_t blake3_compress_subtree_wide(const uint8_t *input,
size_t input_len,
const uint32_t key[8],
uint64_t chunk_counter,
uint8_t flags, uint8_t *out) {
// Note that the single chunk case does *not* bump the SIMD degree up to 2
// when it is 1. If this implementation adds multi-threading in the future,
// this gives us the option of multi-threading even the 2-chunk case, which
// can help performance on smaller platforms.
if (input_len <= blake3_simd_degree() * BLAKE3_CHUNK_LEN) {
return compress_chunks_parallel(input, input_len, key, chunk_counter, flags,
out);
}
// With more than simd_degree chunks, we need to recurse. Start by dividing
// the input into left and right subtrees. (Note that this is only optimal
// as long as the SIMD degree is a power of 2. If we ever get a SIMD degree
// of 3 or something, we'll need a more complicated strategy.)
size_t left_input_len = left_len(input_len);
size_t right_input_len = input_len - left_input_len;
const uint8_t *right_input = &input[left_input_len];
uint64_t right_chunk_counter =
chunk_counter + (uint64_t)(left_input_len / BLAKE3_CHUNK_LEN);
// Make space for the child outputs. Here we use MAX_SIMD_DEGREE_OR_2 to
// account for the special case of returning 2 outputs when the SIMD degree
// is 1.
uint8_t cv_array[2 * MAX_SIMD_DEGREE_OR_2 * BLAKE3_OUT_LEN];
size_t degree = blake3_simd_degree();
if (left_input_len > BLAKE3_CHUNK_LEN && degree == 1) {
// The special case: We always use a degree of at least two, to make
// sure there are two outputs. Except, as noted above, at the chunk
// level, where we allow degree=1. (Note that the 1-chunk-input case is
// a different codepath.)
degree = 2;
}
uint8_t *right_cvs = &cv_array[degree * BLAKE3_OUT_LEN];
// Recurse! If this implementation adds multi-threading support in the
// future, this is where it will go.
size_t left_n = blake3_compress_subtree_wide(input, left_input_len, key,
chunk_counter, flags, cv_array);
size_t right_n = blake3_compress_subtree_wide(
right_input, right_input_len, key, right_chunk_counter, flags, right_cvs);
// The special case again. If simd_degree=1, then we'll have left_n=1 and
// right_n=1. Rather than compressing them into a single output, return
// them directly, to make sure we always have at least two outputs.
if (left_n == 1) {
memcpy(out, cv_array, 2 * BLAKE3_OUT_LEN);
return 2;
}
// Otherwise, do one layer of parent node compression.
size_t num_chaining_values = left_n + right_n;
return compress_parents_parallel(cv_array, num_chaining_values, key, flags,
out);
}
// Hash a subtree with compress_subtree_wide(), and then condense the resulting
// list of chaining values down to a single parent node. Don't compress that
// last parent node, however. Instead, return its message bytes (the
// concatenated chaining values of its children). This is necessary when the
// first call to update() supplies a complete subtree, because the topmost
// parent node of that subtree could end up being the root. It's also necessary
// for extended output in the general case.
//
// As with compress_subtree_wide(), this function is not used on inputs of 1
// chunk or less. That's a different codepath.
INLINE void compress_subtree_to_parent_node(
const uint8_t *input, size_t input_len, const uint32_t key[8],
uint64_t chunk_counter, uint8_t flags, uint8_t out[2 * BLAKE3_OUT_LEN]) {
#if defined(BLAKE3_TESTING)
assert(input_len > BLAKE3_CHUNK_LEN);
#endif
uint8_t cv_array[MAX_SIMD_DEGREE_OR_2 * BLAKE3_OUT_LEN];
size_t num_cvs = blake3_compress_subtree_wide(input, input_len, key,
chunk_counter, flags, cv_array);
assert(num_cvs <= MAX_SIMD_DEGREE_OR_2);
// If MAX_SIMD_DEGREE is greater than 2 and there's enough input,
// compress_subtree_wide() returns more than 2 chaining values. Condense
// them into 2 by forming parent nodes repeatedly.
uint8_t out_array[MAX_SIMD_DEGREE_OR_2 * BLAKE3_OUT_LEN / 2];
// The second half of this loop condition is always true, and we just
// asserted it above. But GCC can't tell that it's always true, and if NDEBUG
// is set on platforms where MAX_SIMD_DEGREE_OR_2 == 2, GCC emits spurious
// warnings here. GCC 8.5 is particularly sensitive, so if you're changing
// this code, test it against that version.
while (num_cvs > 2 && num_cvs <= MAX_SIMD_DEGREE_OR_2) {
num_cvs =
compress_parents_parallel(cv_array, num_cvs, key, flags, out_array);
memcpy(cv_array, out_array, num_cvs * BLAKE3_OUT_LEN);
}
memcpy(out, cv_array, 2 * BLAKE3_OUT_LEN);
}
INLINE void hasher_init_base(blake3_hasher *self, const uint32_t key[8],
uint8_t flags) {
memcpy(self->key, key, BLAKE3_KEY_LEN);
chunk_state_init(&self->chunk, key, flags);
self->cv_stack_len = 0;
}
void blake3_hasher_init(blake3_hasher *self) { hasher_init_base(self, IV, 0); }
void blake3_hasher_init_keyed(blake3_hasher *self,
const uint8_t key[BLAKE3_KEY_LEN]) {
uint32_t key_words[8];
load_key_words(key, key_words);
hasher_init_base(self, key_words, KEYED_HASH);
}
void blake3_hasher_init_derive_key_raw(blake3_hasher *self, const void *context,
size_t context_len) {
blake3_hasher context_hasher;
hasher_init_base(&context_hasher, IV, DERIVE_KEY_CONTEXT);
blake3_hasher_update(&context_hasher, context, context_len);
uint8_t context_key[BLAKE3_KEY_LEN];
blake3_hasher_finalize(&context_hasher, context_key, BLAKE3_KEY_LEN);
uint32_t context_key_words[8];
load_key_words(context_key, context_key_words);
hasher_init_base(self, context_key_words, DERIVE_KEY_MATERIAL);
}
void blake3_hasher_init_derive_key(blake3_hasher *self, const char *context) {
blake3_hasher_init_derive_key_raw(self, context, strlen(context));
}
// As described in hasher_push_cv() below, we do "lazy merging", delaying
// merges until right before the next CV is about to be added. This is
// different from the reference implementation. Another difference is that we
// aren't always merging 1 chunk at a time. Instead, each CV might represent
// any power-of-two number of chunks, as long as the smaller-above-larger stack
// order is maintained. Instead of the "count the trailing 0-bits" algorithm
// described in the spec, we use a "count the total number of 1-bits" variant
// that doesn't require us to retain the subtree size of the CV on top of the
// stack. The principle is the same: each CV that should remain in the stack is
// represented by a 1-bit in the total number of chunks (or bytes) so far.
INLINE void hasher_merge_cv_stack(blake3_hasher *self, uint64_t total_len) {
size_t post_merge_stack_len = (size_t)popcnt(total_len);
while (self->cv_stack_len > post_merge_stack_len) {
uint8_t *parent_node =
&self->cv_stack[(self->cv_stack_len - 2) * BLAKE3_OUT_LEN];
output_t output = parent_output(parent_node, self->key, self->chunk.flags);
output_chaining_value(&output, parent_node);
self->cv_stack_len -= 1;
}
}
// In reference_impl.rs, we merge the new CV with existing CVs from the stack
// before pushing it. We can do that because we know more input is coming, so
// we know none of the merges are root.
//
// This setting is different. We want to feed as much input as possible to
// compress_subtree_wide(), without setting aside anything for the chunk_state.
// If the user gives us 64 KiB, we want to parallelize over all 64 KiB at once
// as a single subtree, if at all possible.
//
// This leads to two problems:
// 1) This 64 KiB input might be the only call that ever gets made to update.
// In this case, the root node of the 64 KiB subtree would be the root node
// of the whole tree, and it would need to be ROOT finalized. We can't
// compress it until we know.
// 2) This 64 KiB input might complete a larger tree, whose root node is
// similarly going to be the the root of the whole tree. For example, maybe
// we have 196 KiB (that is, 128 + 64) hashed so far. We can't compress the
// node at the root of the 256 KiB subtree until we know how to finalize it.
//
// The second problem is solved with "lazy merging". That is, when we're about
// to add a CV to the stack, we don't merge it with anything first, as the
// reference impl does. Instead we do merges using the *previous* CV that was
// added, which is sitting on top of the stack, and we put the new CV
// (unmerged) on top of the stack afterwards. This guarantees that we never
// merge the root node until finalize().
//
// Solving the first problem requires an additional tool,
// compress_subtree_to_parent_node(). That function always returns the top
// *two* chaining values of the subtree it's compressing. We then do lazy
// merging with each of them separately, so that the second CV will always
// remain unmerged. (That also helps us support extendable output when we're
// hashing an input all-at-once.)
INLINE void hasher_push_cv(blake3_hasher *self, uint8_t new_cv[BLAKE3_OUT_LEN],
uint64_t chunk_counter) {
hasher_merge_cv_stack(self, chunk_counter);
memcpy(&self->cv_stack[self->cv_stack_len * BLAKE3_OUT_LEN], new_cv,
BLAKE3_OUT_LEN);
self->cv_stack_len += 1;
}
void blake3_hasher_update(blake3_hasher *self, const void *input,
size_t input_len) {
// Explicitly checking for zero avoids causing UB by passing a null pointer
// to memcpy. This comes up in practice with things like:
// std::vector<uint8_t> v;
// blake3_hasher_update(&hasher, v.data(), v.size());
if (input_len == 0) {
return;
}
const uint8_t *input_bytes = (const uint8_t *)input;
// If we have some partial chunk bytes in the internal chunk_state, we need
// to finish that chunk first.
if (chunk_state_len(&self->chunk) > 0) {
size_t take = BLAKE3_CHUNK_LEN - chunk_state_len(&self->chunk);
if (take > input_len) {
take = input_len;
}
chunk_state_update(&self->chunk, input_bytes, take);
input_bytes += take;
input_len -= take;
// If we've filled the current chunk and there's more coming, finalize this
// chunk and proceed. In this case we know it's not the root.
if (input_len > 0) {
output_t output = chunk_state_output(&self->chunk);
uint8_t chunk_cv[32];
output_chaining_value(&output, chunk_cv);
hasher_push_cv(self, chunk_cv, self->chunk.chunk_counter);
chunk_state_reset(&self->chunk, self->key, self->chunk.chunk_counter + 1);
} else {
return;
}
}
// Now the chunk_state is clear, and we have more input. If there's more than
// a single chunk (so, definitely not the root chunk), hash the largest whole
// subtree we can, with the full benefits of SIMD (and maybe in the future,
// multi-threading) parallelism. Two restrictions:
// - The subtree has to be a power-of-2 number of chunks. Only subtrees along
// the right edge can be incomplete, and we don't know where the right edge
// is going to be until we get to finalize().
// - The subtree must evenly divide the total number of chunks up until this
// point (if total is not 0). If the current incomplete subtree is only
// waiting for 1 more chunk, we can't hash a subtree of 4 chunks. We have
// to complete the current subtree first.
// Because we might need to break up the input to form powers of 2, or to
// evenly divide what we already have, this part runs in a loop.
while (input_len > BLAKE3_CHUNK_LEN) {
size_t subtree_len = round_down_to_power_of_2(input_len);
uint64_t count_so_far = self->chunk.chunk_counter * BLAKE3_CHUNK_LEN;
// Shrink the subtree_len until it evenly divides the count so far. We know
// that subtree_len itself is a power of 2, so we can use a bitmasking
// trick instead of an actual remainder operation. (Note that if the caller
// consistently passes power-of-2 inputs of the same size, as is hopefully
// typical, this loop condition will always fail, and subtree_len will
// always be the full length of the input.)
//
// An aside: We don't have to shrink subtree_len quite this much. For
// example, if count_so_far is 1, we could pass 2 chunks to
// compress_subtree_to_parent_node. Since we'll get 2 CVs back, we'll still
// get the right answer in the end, and we might get to use 2-way SIMD
// parallelism. The problem with this optimization, is that it gets us
// stuck always hashing 2 chunks. The total number of chunks will remain
// odd, and we'll never graduate to higher degrees of parallelism. See
// https://github.com/BLAKE3-team/BLAKE3/issues/69.
while ((((uint64_t)(subtree_len - 1)) & count_so_far) != 0) {
subtree_len /= 2;
}
// The shrunken subtree_len might now be 1 chunk long. If so, hash that one
// chunk by itself. Otherwise, compress the subtree into a pair of CVs.
uint64_t subtree_chunks = subtree_len / BLAKE3_CHUNK_LEN;
if (subtree_len <= BLAKE3_CHUNK_LEN) {
blake3_chunk_state chunk_state;
chunk_state_init(&chunk_state, self->key, self->chunk.flags);
chunk_state.chunk_counter = self->chunk.chunk_counter;
chunk_state_update(&chunk_state, input_bytes, subtree_len);
output_t output = chunk_state_output(&chunk_state);
uint8_t cv[BLAKE3_OUT_LEN];
output_chaining_value(&output, cv);
hasher_push_cv(self, cv, chunk_state.chunk_counter);
} else {
// This is the high-performance happy path, though getting here depends
// on the caller giving us a long enough input.
uint8_t cv_pair[2 * BLAKE3_OUT_LEN];
compress_subtree_to_parent_node(input_bytes, subtree_len, self->key,
self->chunk.chunk_counter,
self->chunk.flags, cv_pair);
hasher_push_cv(self, cv_pair, self->chunk.chunk_counter);
hasher_push_cv(self, &cv_pair[BLAKE3_OUT_LEN],
self->chunk.chunk_counter + (subtree_chunks / 2));
}
self->chunk.chunk_counter += subtree_chunks;
input_bytes += subtree_len;
input_len -= subtree_len;
}
// If there's any remaining input less than a full chunk, add it to the chunk
// state. In that case, also do a final merge loop to make sure the subtree
// stack doesn't contain any unmerged pairs. The remaining input means we
// know these merges are non-root. This merge loop isn't strictly necessary
// here, because hasher_push_chunk_cv already does its own merge loop, but it
// simplifies blake3_hasher_finalize below.
if (input_len > 0) {
chunk_state_update(&self->chunk, input_bytes, input_len);
hasher_merge_cv_stack(self, self->chunk.chunk_counter);
}
}
void blake3_hasher_finalize(const blake3_hasher *self, uint8_t *out,
size_t out_len) {
blake3_hasher_finalize_seek(self, 0, out, out_len);
}
void blake3_hasher_finalize_seek(const blake3_hasher *self, uint64_t seek,
uint8_t *out, size_t out_len) {
// Explicitly checking for zero avoids causing UB by passing a null pointer
// to memcpy. This comes up in practice with things like:
// std::vector<uint8_t> v;
// blake3_hasher_finalize(&hasher, v.data(), v.size());
if (out_len == 0) {
return;
}
// If the subtree stack is empty, then the current chunk is the root.
if (self->cv_stack_len == 0) {
output_t output = chunk_state_output(&self->chunk);
output_root_bytes(&output, seek, out, out_len);
return;
}
// If there are any bytes in the chunk state, finalize that chunk and do a
// roll-up merge between that chunk hash and every subtree in the stack. In
// this case, the extra merge loop at the end of blake3_hasher_update
// guarantees that none of the subtrees in the stack need to be merged with
// each other first. Otherwise, if there are no bytes in the chunk state,
// then the top of the stack is a chunk hash, and we start the merge from
// that.
output_t output;
size_t cvs_remaining;
if (chunk_state_len(&self->chunk) > 0) {
cvs_remaining = self->cv_stack_len;
output = chunk_state_output(&self->chunk);
} else {
// There are always at least 2 CVs in the stack in this case.
cvs_remaining = self->cv_stack_len - 2;
output = parent_output(&self->cv_stack[cvs_remaining * 32], self->key,
self->chunk.flags);
}
while (cvs_remaining > 0) {
cvs_remaining -= 1;
uint8_t parent_block[BLAKE3_BLOCK_LEN];
memcpy(parent_block, &self->cv_stack[cvs_remaining * 32], 32);
output_chaining_value(&output, &parent_block[32]);
output = parent_output(parent_block, self->key, self->chunk.flags);
}
output_root_bytes(&output, seek, out, out_len);
}
void blake3_hasher_reset(blake3_hasher *self) {
chunk_state_reset(&self->chunk, self->key, 0);
self->cv_stack_len = 0;
}
@@ -0,0 +1,82 @@
#ifndef BLAKE3_H
#define BLAKE3_H
#include <stddef.h>
#include <stdint.h>
#if !defined(BLAKE3_API)
# if defined(_WIN32) || defined(__CYGWIN__)
# if defined(BLAKE3_DLL)
# if defined(BLAKE3_DLL_EXPORTS)
# define BLAKE3_API __declspec(dllexport)
# else
# define BLAKE3_API __declspec(dllimport)
# endif
# define BLAKE3_PRIVATE
# else
# define BLAKE3_API
# define BLAKE3_PRIVATE
# endif
# elif __GNUC__ >= 4
# define BLAKE3_API __attribute__((visibility("default")))
# define BLAKE3_PRIVATE __attribute__((visibility("hidden")))
# else
# define BLAKE3_API
# define BLAKE3_PRIVATE
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define BLAKE3_VERSION_STRING "1.5.0"
#define BLAKE3_KEY_LEN 32
#define BLAKE3_OUT_LEN 32
#define BLAKE3_BLOCK_LEN 64
#define BLAKE3_CHUNK_LEN 1024
#define BLAKE3_MAX_DEPTH 54
// This struct is a private implementation detail. It has to be here because
// it's part of blake3_hasher below.
typedef struct {
uint32_t cv[8];
uint64_t chunk_counter;
uint8_t buf[BLAKE3_BLOCK_LEN];
uint8_t buf_len;
uint8_t blocks_compressed;
uint8_t flags;
} blake3_chunk_state;
typedef struct {
uint32_t key[8];
blake3_chunk_state chunk;
uint8_t cv_stack_len;
// The stack size is MAX_DEPTH + 1 because we do lazy merging. For example,
// with 7 chunks, we have 3 entries in the stack. Adding an 8th chunk
// requires a 4th entry, rather than merging everything down to 1, because we
// don't know whether more input is coming. This is different from how the
// reference implementation does things.
uint8_t cv_stack[(BLAKE3_MAX_DEPTH + 1) * BLAKE3_OUT_LEN];
} blake3_hasher;
BLAKE3_API const char *blake3_version(void);
BLAKE3_API void blake3_hasher_init(blake3_hasher *self);
BLAKE3_API void blake3_hasher_init_keyed(blake3_hasher *self,
const uint8_t key[BLAKE3_KEY_LEN]);
BLAKE3_API void blake3_hasher_init_derive_key(blake3_hasher *self, const char *context);
BLAKE3_API void blake3_hasher_init_derive_key_raw(blake3_hasher *self, const void *context,
size_t context_len);
BLAKE3_API void blake3_hasher_update(blake3_hasher *self, const void *input,
size_t input_len);
BLAKE3_API void blake3_hasher_finalize(const blake3_hasher *self, uint8_t *out,
size_t out_len);
BLAKE3_API void blake3_hasher_finalize_seek(const blake3_hasher *self, uint64_t seek,
uint8_t *out, size_t out_len);
BLAKE3_API void blake3_hasher_reset(blake3_hasher *self);
#ifdef __cplusplus
}
#endif
#endif /* BLAKE3_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,278 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "blake3_impl.h"
#if defined(IS_X86)
#if defined(_MSC_VER)
#include <intrin.h>
#elif defined(__GNUC__)
#include <immintrin.h>
#else
#undef IS_X86 /* Unimplemented! */
#endif
#endif
#define MAYBE_UNUSED(x) (void)((x))
#if defined(IS_X86)
static uint64_t xgetbv(void) {
#if defined(_MSC_VER)
return _xgetbv(0);
#else
uint32_t eax = 0, edx = 0;
__asm__ __volatile__("xgetbv\n" : "=a"(eax), "=d"(edx) : "c"(0));
return ((uint64_t)edx << 32) | eax;
#endif
}
static void cpuid(uint32_t out[4], uint32_t id) {
#if defined(_MSC_VER)
__cpuid((int *)out, id);
#elif defined(__i386__) || defined(_M_IX86)
__asm__ __volatile__("movl %%ebx, %1\n"
"cpuid\n"
"xchgl %1, %%ebx\n"
: "=a"(out[0]), "=r"(out[1]), "=c"(out[2]), "=d"(out[3])
: "a"(id));
#else
__asm__ __volatile__("cpuid\n"
: "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3])
: "a"(id));
#endif
}
static void cpuidex(uint32_t out[4], uint32_t id, uint32_t sid) {
#if defined(_MSC_VER)
__cpuidex((int *)out, id, sid);
#elif defined(__i386__) || defined(_M_IX86)
__asm__ __volatile__("movl %%ebx, %1\n"
"cpuid\n"
"xchgl %1, %%ebx\n"
: "=a"(out[0]), "=r"(out[1]), "=c"(out[2]), "=d"(out[3])
: "a"(id), "c"(sid));
#else
__asm__ __volatile__("cpuid\n"
: "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3])
: "a"(id), "c"(sid));
#endif
}
#endif
enum cpu_feature {
SSE2 = 1 << 0,
SSSE3 = 1 << 1,
SSE41 = 1 << 2,
AVX = 1 << 3,
AVX2 = 1 << 4,
AVX512F = 1 << 5,
AVX512VL = 1 << 6,
/* ... */
UNDEFINED = 1 << 30
};
#if !defined(BLAKE3_TESTING)
static /* Allow the variable to be controlled manually for testing */
#endif
volatile int g_cpu_features = UNDEFINED;
#if !defined(BLAKE3_TESTING)
static
#endif
enum cpu_feature
get_cpu_features(void) {
/* If TSAN detects a data race here, try compiling with -DBLAKE3_ATOMICS=1 */
long features = g_cpu_features;
if (features != UNDEFINED) {
return (enum cpu_feature)features;
} else {
#if defined(IS_X86)
uint32_t regs[4] = {0};
uint32_t *eax = &regs[0], *ebx = &regs[1], *ecx = &regs[2], *edx = &regs[3];
(void)edx;
features = 0;
cpuid(regs, 0);
const int max_id = *eax;
cpuid(regs, 1);
#if defined(__amd64__) || defined(_M_X64)
features |= SSE2;
#else
if (*edx & (1UL << 26))
features |= SSE2;
#endif
if (*ecx & (1UL << 9))
features |= SSSE3;
if (*ecx & (1UL << 19))
features |= SSE41;
if (*ecx & (1UL << 27)) { // OSXSAVE
const uint64_t mask = xgetbv();
if ((mask & 6) == 6) { // SSE and AVX states
if (*ecx & (1UL << 28))
features |= AVX;
if (max_id >= 7) {
cpuidex(regs, 7, 0);
if (*ebx & (1UL << 5))
features |= AVX2;
if ((mask & 224) == 224) { // Opmask, ZMM_Hi256, Hi16_Zmm
if (*ebx & (1UL << 31))
features |= AVX512VL;
if (*ebx & (1UL << 16))
features |= AVX512F;
}
}
}
}
g_cpu_features = features;
return (enum cpu_feature)features;
#else
/* How to detect NEON? */
return 0;
#endif
}
}
void blake3_compress_in_place(uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags) {
#if defined(IS_X86)
const enum cpu_feature features = get_cpu_features();
MAYBE_UNUSED(features);
#if !defined(BLAKE3_NO_AVX512)
if (features & AVX512VL) {
blake3_compress_in_place_avx512(cv, block, block_len, counter, flags);
return;
}
#endif
#if !defined(BLAKE3_NO_SSE41)
if (features & SSE41) {
blake3_compress_in_place_sse41(cv, block, block_len, counter, flags);
return;
}
#endif
#if !defined(BLAKE3_NO_SSE2)
if (features & SSE2) {
blake3_compress_in_place_sse2(cv, block, block_len, counter, flags);
return;
}
#endif
#endif
blake3_compress_in_place_portable(cv, block, block_len, counter, flags);
}
void blake3_compress_xof(const uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter, uint8_t flags,
uint8_t out[64]) {
#if defined(IS_X86)
const enum cpu_feature features = get_cpu_features();
MAYBE_UNUSED(features);
#if !defined(BLAKE3_NO_AVX512)
if (features & AVX512VL) {
blake3_compress_xof_avx512(cv, block, block_len, counter, flags, out);
return;
}
#endif
#if !defined(BLAKE3_NO_SSE41)
if (features & SSE41) {
blake3_compress_xof_sse41(cv, block, block_len, counter, flags, out);
return;
}
#endif
#if !defined(BLAKE3_NO_SSE2)
if (features & SSE2) {
blake3_compress_xof_sse2(cv, block, block_len, counter, flags, out);
return;
}
#endif
#endif
blake3_compress_xof_portable(cv, block, block_len, counter, flags, out);
}
void blake3_hash_many(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8], uint64_t counter,
bool increment_counter, uint8_t flags,
uint8_t flags_start, uint8_t flags_end, uint8_t *out) {
#if defined(IS_X86)
const enum cpu_feature features = get_cpu_features();
MAYBE_UNUSED(features);
#if !defined(BLAKE3_NO_AVX512)
if ((features & (AVX512F|AVX512VL)) == (AVX512F|AVX512VL)) {
blake3_hash_many_avx512(inputs, num_inputs, blocks, key, counter,
increment_counter, flags, flags_start, flags_end,
out);
return;
}
#endif
#if !defined(BLAKE3_NO_AVX2)
if (features & AVX2) {
blake3_hash_many_avx2(inputs, num_inputs, blocks, key, counter,
increment_counter, flags, flags_start, flags_end,
out);
return;
}
#endif
#if !defined(BLAKE3_NO_SSE41)
if (features & SSE41) {
blake3_hash_many_sse41(inputs, num_inputs, blocks, key, counter,
increment_counter, flags, flags_start, flags_end,
out);
return;
}
#endif
#if !defined(BLAKE3_NO_SSE2)
if (features & SSE2) {
blake3_hash_many_sse2(inputs, num_inputs, blocks, key, counter,
increment_counter, flags, flags_start, flags_end,
out);
return;
}
#endif
#endif
#if BLAKE3_USE_NEON == 1
blake3_hash_many_neon(inputs, num_inputs, blocks, key, counter,
increment_counter, flags, flags_start, flags_end, out);
return;
#endif
blake3_hash_many_portable(inputs, num_inputs, blocks, key, counter,
increment_counter, flags, flags_start, flags_end,
out);
}
// The dynamically detected SIMD degree of the current platform.
size_t blake3_simd_degree(void) {
#if defined(IS_X86)
const enum cpu_feature features = get_cpu_features();
MAYBE_UNUSED(features);
#if !defined(BLAKE3_NO_AVX512)
if ((features & (AVX512F|AVX512VL)) == (AVX512F|AVX512VL)) {
return 16;
}
#endif
#if !defined(BLAKE3_NO_AVX2)
if (features & AVX2) {
return 8;
}
#endif
#if !defined(BLAKE3_NO_SSE41)
if (features & SSE41) {
return 4;
}
#endif
#if !defined(BLAKE3_NO_SSE2)
if (features & SSE2) {
return 4;
}
#endif
#endif
#if BLAKE3_USE_NEON == 1
return 4;
#endif
return 1;
}
@@ -0,0 +1,285 @@
#ifndef BLAKE3_IMPL_H
#define BLAKE3_IMPL_H
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "blake3.h"
// internal flags
enum blake3_flags {
CHUNK_START = 1 << 0,
CHUNK_END = 1 << 1,
PARENT = 1 << 2,
ROOT = 1 << 3,
KEYED_HASH = 1 << 4,
DERIVE_KEY_CONTEXT = 1 << 5,
DERIVE_KEY_MATERIAL = 1 << 6,
};
// This C implementation tries to support recent versions of GCC, Clang, and
// MSVC.
#if defined(_MSC_VER)
#define INLINE static __forceinline
#else
#define INLINE static inline __attribute__((always_inline))
#endif
#if defined(__x86_64__) || defined(_M_X64)
#define IS_X86
#define IS_X86_64
#endif
#if defined(__i386__) || defined(_M_IX86)
#define IS_X86
#define IS_X86_32
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#define IS_AARCH64
#endif
#if defined(IS_X86)
#if defined(_MSC_VER)
#include <intrin.h>
#endif
#endif
#if !defined(BLAKE3_USE_NEON)
// If BLAKE3_USE_NEON not manually set, autodetect based on AArch64ness
#if defined(IS_AARCH64)
#if defined(__ARM_BIG_ENDIAN)
#define BLAKE3_USE_NEON 0
#else
#define BLAKE3_USE_NEON 1
#endif
#else
#define BLAKE3_USE_NEON 0
#endif
#endif
#if defined(IS_X86)
#define MAX_SIMD_DEGREE 16
#elif BLAKE3_USE_NEON == 1
#define MAX_SIMD_DEGREE 4
#else
#define MAX_SIMD_DEGREE 1
#endif
// There are some places where we want a static size that's equal to the
// MAX_SIMD_DEGREE, but also at least 2.
#define MAX_SIMD_DEGREE_OR_2 (MAX_SIMD_DEGREE > 2 ? MAX_SIMD_DEGREE : 2)
static const uint32_t IV[8] = {0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL,
0xA54FF53AUL, 0x510E527FUL, 0x9B05688CUL,
0x1F83D9ABUL, 0x5BE0CD19UL};
static const uint8_t MSG_SCHEDULE[7][16] = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8},
{3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1},
{10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6},
{12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4},
{9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7},
{11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13},
};
/* Find index of the highest set bit */
/* x is assumed to be nonzero. */
static unsigned int highest_one(uint64_t x) {
#if defined(__GNUC__) || defined(__clang__)
return 63 ^ (unsigned int)__builtin_clzll(x);
#elif defined(_MSC_VER) && defined(IS_X86_64)
unsigned long index;
_BitScanReverse64(&index, x);
return index;
#elif defined(_MSC_VER) && defined(IS_X86_32)
if(x >> 32) {
unsigned long index;
_BitScanReverse(&index, (unsigned long)(x >> 32));
return 32 + index;
} else {
unsigned long index;
_BitScanReverse(&index, (unsigned long)x);
return index;
}
#else
unsigned int c = 0;
if(x & 0xffffffff00000000ULL) { x >>= 32; c += 32; }
if(x & 0x00000000ffff0000ULL) { x >>= 16; c += 16; }
if(x & 0x000000000000ff00ULL) { x >>= 8; c += 8; }
if(x & 0x00000000000000f0ULL) { x >>= 4; c += 4; }
if(x & 0x000000000000000cULL) { x >>= 2; c += 2; }
if(x & 0x0000000000000002ULL) { c += 1; }
return c;
#endif
}
// Count the number of 1 bits.
INLINE unsigned int popcnt(uint64_t x) {
#if defined(__GNUC__) || defined(__clang__)
return (unsigned int)__builtin_popcountll(x);
#else
unsigned int count = 0;
while (x != 0) {
count += 1;
x &= x - 1;
}
return count;
#endif
}
// Largest power of two less than or equal to x. As a special case, returns 1
// when x is 0.
INLINE uint64_t round_down_to_power_of_2(uint64_t x) {
return 1ULL << highest_one(x | 1);
}
INLINE uint32_t counter_low(uint64_t counter) { return (uint32_t)counter; }
INLINE uint32_t counter_high(uint64_t counter) {
return (uint32_t)(counter >> 32);
}
INLINE uint32_t load32(const void *src) {
const uint8_t *p = (const uint8_t *)src;
return ((uint32_t)(p[0]) << 0) | ((uint32_t)(p[1]) << 8) |
((uint32_t)(p[2]) << 16) | ((uint32_t)(p[3]) << 24);
}
INLINE void load_key_words(const uint8_t key[BLAKE3_KEY_LEN],
uint32_t key_words[8]) {
key_words[0] = load32(&key[0 * 4]);
key_words[1] = load32(&key[1 * 4]);
key_words[2] = load32(&key[2 * 4]);
key_words[3] = load32(&key[3 * 4]);
key_words[4] = load32(&key[4 * 4]);
key_words[5] = load32(&key[5 * 4]);
key_words[6] = load32(&key[6 * 4]);
key_words[7] = load32(&key[7 * 4]);
}
INLINE void store32(void *dst, uint32_t w) {
uint8_t *p = (uint8_t *)dst;
p[0] = (uint8_t)(w >> 0);
p[1] = (uint8_t)(w >> 8);
p[2] = (uint8_t)(w >> 16);
p[3] = (uint8_t)(w >> 24);
}
INLINE void store_cv_words(uint8_t bytes_out[32], uint32_t cv_words[8]) {
store32(&bytes_out[0 * 4], cv_words[0]);
store32(&bytes_out[1 * 4], cv_words[1]);
store32(&bytes_out[2 * 4], cv_words[2]);
store32(&bytes_out[3 * 4], cv_words[3]);
store32(&bytes_out[4 * 4], cv_words[4]);
store32(&bytes_out[5 * 4], cv_words[5]);
store32(&bytes_out[6 * 4], cv_words[6]);
store32(&bytes_out[7 * 4], cv_words[7]);
}
void blake3_compress_in_place(uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags);
void blake3_compress_xof(const uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter, uint8_t flags,
uint8_t out[64]);
void blake3_hash_many(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8], uint64_t counter,
bool increment_counter, uint8_t flags,
uint8_t flags_start, uint8_t flags_end, uint8_t *out);
size_t blake3_simd_degree(void);
// Declarations for implementation-specific functions.
void blake3_compress_in_place_portable(uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags);
void blake3_compress_xof_portable(const uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags, uint8_t out[64]);
void blake3_hash_many_portable(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out);
#if defined(IS_X86)
#if !defined(BLAKE3_NO_SSE2)
void blake3_compress_in_place_sse2(uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags);
void blake3_compress_xof_sse2(const uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags, uint8_t out[64]);
void blake3_hash_many_sse2(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out);
#endif
#if !defined(BLAKE3_NO_SSE41)
void blake3_compress_in_place_sse41(uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags);
void blake3_compress_xof_sse41(const uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags, uint8_t out[64]);
void blake3_hash_many_sse41(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out);
#endif
#if !defined(BLAKE3_NO_AVX2)
void blake3_hash_many_avx2(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out);
#endif
#if !defined(BLAKE3_NO_AVX512)
void blake3_compress_in_place_avx512(uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags);
void blake3_compress_xof_avx512(const uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags, uint8_t out[64]);
void blake3_hash_many_avx512(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out);
#endif
#endif
#if BLAKE3_USE_NEON == 1
void blake3_hash_many_neon(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out);
#endif
#endif /* BLAKE3_IMPL_H */
@@ -0,0 +1,368 @@
#include "blake3_impl.h"
#include <arm_neon.h>
#ifdef __ARM_BIG_ENDIAN
#error "This implementation only supports little-endian ARM."
// It might be that all we need for big-endian support here is to get the loads
// and stores right, but step zero would be finding a way to test it in CI.
#endif
INLINE uint32x4_t loadu_128(const uint8_t src[16]) {
// vld1q_u32 has alignment requirements. Don't use it.
uint32x4_t x;
memcpy(&x, src, 16);
return x;
}
INLINE void storeu_128(uint32x4_t src, uint8_t dest[16]) {
// vst1q_u32 has alignment requirements. Don't use it.
memcpy(dest, &src, 16);
}
INLINE uint32x4_t add_128(uint32x4_t a, uint32x4_t b) {
return vaddq_u32(a, b);
}
INLINE uint32x4_t xor_128(uint32x4_t a, uint32x4_t b) {
return veorq_u32(a, b);
}
INLINE uint32x4_t set1_128(uint32_t x) { return vld1q_dup_u32(&x); }
INLINE uint32x4_t set4(uint32_t a, uint32_t b, uint32_t c, uint32_t d) {
uint32_t array[4] = {a, b, c, d};
return vld1q_u32(array);
}
INLINE uint32x4_t rot16_128(uint32x4_t x) {
// The straightfoward implementation would be two shifts and an or, but that's
// slower on microarchitectures we've tested. See
// https://github.com/BLAKE3-team/BLAKE3/pull/319.
// return vorrq_u32(vshrq_n_u32(x, 16), vshlq_n_u32(x, 32 - 16));
return vreinterpretq_u32_u16(vrev32q_u16(vreinterpretq_u16_u32(x)));
}
INLINE uint32x4_t rot12_128(uint32x4_t x) {
// See comment in rot16_128.
// return vorrq_u32(vshrq_n_u32(x, 12), vshlq_n_u32(x, 32 - 12));
return vsriq_n_u32(vshlq_n_u32(x, 32-12), x, 12);
}
INLINE uint32x4_t rot8_128(uint32x4_t x) {
// See comment in rot16_128.
// return vorrq_u32(vshrq_n_u32(x, 8), vshlq_n_u32(x, 32 - 8));
#if defined(__clang__)
return vreinterpretq_u32_u8(__builtin_shufflevector(vreinterpretq_u8_u32(x), vreinterpretq_u8_u32(x), 1,2,3,0,5,6,7,4,9,10,11,8,13,14,15,12));
#elif __GNUC__ * 10000 + __GNUC_MINOR__ * 100 >=40700
static const uint8x16_t r8 = {1,2,3,0,5,6,7,4,9,10,11,8,13,14,15,12};
return vreinterpretq_u32_u8(__builtin_shuffle(vreinterpretq_u8_u32(x), vreinterpretq_u8_u32(x), r8));
#else
return vsriq_n_u32(vshlq_n_u32(x, 32-8), x, 8);
#endif
}
INLINE uint32x4_t rot7_128(uint32x4_t x) {
// See comment in rot16_128.
// return vorrq_u32(vshrq_n_u32(x, 7), vshlq_n_u32(x, 32 - 7));
return vsriq_n_u32(vshlq_n_u32(x, 32-7), x, 7);
}
// TODO: compress_neon
// TODO: hash2_neon
/*
* ----------------------------------------------------------------------------
* hash4_neon
* ----------------------------------------------------------------------------
*/
INLINE void round_fn4(uint32x4_t v[16], uint32x4_t m[16], size_t r) {
v[0] = add_128(v[0], m[(size_t)MSG_SCHEDULE[r][0]]);
v[1] = add_128(v[1], m[(size_t)MSG_SCHEDULE[r][2]]);
v[2] = add_128(v[2], m[(size_t)MSG_SCHEDULE[r][4]]);
v[3] = add_128(v[3], m[(size_t)MSG_SCHEDULE[r][6]]);
v[0] = add_128(v[0], v[4]);
v[1] = add_128(v[1], v[5]);
v[2] = add_128(v[2], v[6]);
v[3] = add_128(v[3], v[7]);
v[12] = xor_128(v[12], v[0]);
v[13] = xor_128(v[13], v[1]);
v[14] = xor_128(v[14], v[2]);
v[15] = xor_128(v[15], v[3]);
v[12] = rot16_128(v[12]);
v[13] = rot16_128(v[13]);
v[14] = rot16_128(v[14]);
v[15] = rot16_128(v[15]);
v[8] = add_128(v[8], v[12]);
v[9] = add_128(v[9], v[13]);
v[10] = add_128(v[10], v[14]);
v[11] = add_128(v[11], v[15]);
v[4] = xor_128(v[4], v[8]);
v[5] = xor_128(v[5], v[9]);
v[6] = xor_128(v[6], v[10]);
v[7] = xor_128(v[7], v[11]);
v[4] = rot12_128(v[4]);
v[5] = rot12_128(v[5]);
v[6] = rot12_128(v[6]);
v[7] = rot12_128(v[7]);
v[0] = add_128(v[0], m[(size_t)MSG_SCHEDULE[r][1]]);
v[1] = add_128(v[1], m[(size_t)MSG_SCHEDULE[r][3]]);
v[2] = add_128(v[2], m[(size_t)MSG_SCHEDULE[r][5]]);
v[3] = add_128(v[3], m[(size_t)MSG_SCHEDULE[r][7]]);
v[0] = add_128(v[0], v[4]);
v[1] = add_128(v[1], v[5]);
v[2] = add_128(v[2], v[6]);
v[3] = add_128(v[3], v[7]);
v[12] = xor_128(v[12], v[0]);
v[13] = xor_128(v[13], v[1]);
v[14] = xor_128(v[14], v[2]);
v[15] = xor_128(v[15], v[3]);
v[12] = rot8_128(v[12]);
v[13] = rot8_128(v[13]);
v[14] = rot8_128(v[14]);
v[15] = rot8_128(v[15]);
v[8] = add_128(v[8], v[12]);
v[9] = add_128(v[9], v[13]);
v[10] = add_128(v[10], v[14]);
v[11] = add_128(v[11], v[15]);
v[4] = xor_128(v[4], v[8]);
v[5] = xor_128(v[5], v[9]);
v[6] = xor_128(v[6], v[10]);
v[7] = xor_128(v[7], v[11]);
v[4] = rot7_128(v[4]);
v[5] = rot7_128(v[5]);
v[6] = rot7_128(v[6]);
v[7] = rot7_128(v[7]);
v[0] = add_128(v[0], m[(size_t)MSG_SCHEDULE[r][8]]);
v[1] = add_128(v[1], m[(size_t)MSG_SCHEDULE[r][10]]);
v[2] = add_128(v[2], m[(size_t)MSG_SCHEDULE[r][12]]);
v[3] = add_128(v[3], m[(size_t)MSG_SCHEDULE[r][14]]);
v[0] = add_128(v[0], v[5]);
v[1] = add_128(v[1], v[6]);
v[2] = add_128(v[2], v[7]);
v[3] = add_128(v[3], v[4]);
v[15] = xor_128(v[15], v[0]);
v[12] = xor_128(v[12], v[1]);
v[13] = xor_128(v[13], v[2]);
v[14] = xor_128(v[14], v[3]);
v[15] = rot16_128(v[15]);
v[12] = rot16_128(v[12]);
v[13] = rot16_128(v[13]);
v[14] = rot16_128(v[14]);
v[10] = add_128(v[10], v[15]);
v[11] = add_128(v[11], v[12]);
v[8] = add_128(v[8], v[13]);
v[9] = add_128(v[9], v[14]);
v[5] = xor_128(v[5], v[10]);
v[6] = xor_128(v[6], v[11]);
v[7] = xor_128(v[7], v[8]);
v[4] = xor_128(v[4], v[9]);
v[5] = rot12_128(v[5]);
v[6] = rot12_128(v[6]);
v[7] = rot12_128(v[7]);
v[4] = rot12_128(v[4]);
v[0] = add_128(v[0], m[(size_t)MSG_SCHEDULE[r][9]]);
v[1] = add_128(v[1], m[(size_t)MSG_SCHEDULE[r][11]]);
v[2] = add_128(v[2], m[(size_t)MSG_SCHEDULE[r][13]]);
v[3] = add_128(v[3], m[(size_t)MSG_SCHEDULE[r][15]]);
v[0] = add_128(v[0], v[5]);
v[1] = add_128(v[1], v[6]);
v[2] = add_128(v[2], v[7]);
v[3] = add_128(v[3], v[4]);
v[15] = xor_128(v[15], v[0]);
v[12] = xor_128(v[12], v[1]);
v[13] = xor_128(v[13], v[2]);
v[14] = xor_128(v[14], v[3]);
v[15] = rot8_128(v[15]);
v[12] = rot8_128(v[12]);
v[13] = rot8_128(v[13]);
v[14] = rot8_128(v[14]);
v[10] = add_128(v[10], v[15]);
v[11] = add_128(v[11], v[12]);
v[8] = add_128(v[8], v[13]);
v[9] = add_128(v[9], v[14]);
v[5] = xor_128(v[5], v[10]);
v[6] = xor_128(v[6], v[11]);
v[7] = xor_128(v[7], v[8]);
v[4] = xor_128(v[4], v[9]);
v[5] = rot7_128(v[5]);
v[6] = rot7_128(v[6]);
v[7] = rot7_128(v[7]);
v[4] = rot7_128(v[4]);
}
INLINE void transpose_vecs_128(uint32x4_t vecs[4]) {
// Individually transpose the four 2x2 sub-matrices in each corner.
uint32x4x2_t rows01 = vtrnq_u32(vecs[0], vecs[1]);
uint32x4x2_t rows23 = vtrnq_u32(vecs[2], vecs[3]);
// Swap the top-right and bottom-left 2x2s (which just got transposed).
vecs[0] =
vcombine_u32(vget_low_u32(rows01.val[0]), vget_low_u32(rows23.val[0]));
vecs[1] =
vcombine_u32(vget_low_u32(rows01.val[1]), vget_low_u32(rows23.val[1]));
vecs[2] =
vcombine_u32(vget_high_u32(rows01.val[0]), vget_high_u32(rows23.val[0]));
vecs[3] =
vcombine_u32(vget_high_u32(rows01.val[1]), vget_high_u32(rows23.val[1]));
}
INLINE void transpose_msg_vecs4(const uint8_t *const *inputs,
size_t block_offset, uint32x4_t out[16]) {
out[0] = loadu_128(&inputs[0][block_offset + 0 * sizeof(uint32x4_t)]);
out[1] = loadu_128(&inputs[1][block_offset + 0 * sizeof(uint32x4_t)]);
out[2] = loadu_128(&inputs[2][block_offset + 0 * sizeof(uint32x4_t)]);
out[3] = loadu_128(&inputs[3][block_offset + 0 * sizeof(uint32x4_t)]);
out[4] = loadu_128(&inputs[0][block_offset + 1 * sizeof(uint32x4_t)]);
out[5] = loadu_128(&inputs[1][block_offset + 1 * sizeof(uint32x4_t)]);
out[6] = loadu_128(&inputs[2][block_offset + 1 * sizeof(uint32x4_t)]);
out[7] = loadu_128(&inputs[3][block_offset + 1 * sizeof(uint32x4_t)]);
out[8] = loadu_128(&inputs[0][block_offset + 2 * sizeof(uint32x4_t)]);
out[9] = loadu_128(&inputs[1][block_offset + 2 * sizeof(uint32x4_t)]);
out[10] = loadu_128(&inputs[2][block_offset + 2 * sizeof(uint32x4_t)]);
out[11] = loadu_128(&inputs[3][block_offset + 2 * sizeof(uint32x4_t)]);
out[12] = loadu_128(&inputs[0][block_offset + 3 * sizeof(uint32x4_t)]);
out[13] = loadu_128(&inputs[1][block_offset + 3 * sizeof(uint32x4_t)]);
out[14] = loadu_128(&inputs[2][block_offset + 3 * sizeof(uint32x4_t)]);
out[15] = loadu_128(&inputs[3][block_offset + 3 * sizeof(uint32x4_t)]);
transpose_vecs_128(&out[0]);
transpose_vecs_128(&out[4]);
transpose_vecs_128(&out[8]);
transpose_vecs_128(&out[12]);
}
INLINE void load_counters4(uint64_t counter, bool increment_counter,
uint32x4_t *out_low, uint32x4_t *out_high) {
uint64_t mask = (increment_counter ? ~0 : 0);
*out_low = set4(
counter_low(counter + (mask & 0)), counter_low(counter + (mask & 1)),
counter_low(counter + (mask & 2)), counter_low(counter + (mask & 3)));
*out_high = set4(
counter_high(counter + (mask & 0)), counter_high(counter + (mask & 1)),
counter_high(counter + (mask & 2)), counter_high(counter + (mask & 3)));
}
void blake3_hash4_neon(const uint8_t *const *inputs, size_t blocks,
const uint32_t key[8], uint64_t counter,
bool increment_counter, uint8_t flags,
uint8_t flags_start, uint8_t flags_end, uint8_t *out) {
uint32x4_t h_vecs[8] = {
set1_128(key[0]), set1_128(key[1]), set1_128(key[2]), set1_128(key[3]),
set1_128(key[4]), set1_128(key[5]), set1_128(key[6]), set1_128(key[7]),
};
uint32x4_t counter_low_vec, counter_high_vec;
load_counters4(counter, increment_counter, &counter_low_vec,
&counter_high_vec);
uint8_t block_flags = flags | flags_start;
for (size_t block = 0; block < blocks; block++) {
if (block + 1 == blocks) {
block_flags |= flags_end;
}
uint32x4_t block_len_vec = set1_128(BLAKE3_BLOCK_LEN);
uint32x4_t block_flags_vec = set1_128(block_flags);
uint32x4_t msg_vecs[16];
transpose_msg_vecs4(inputs, block * BLAKE3_BLOCK_LEN, msg_vecs);
uint32x4_t v[16] = {
h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3],
h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7],
set1_128(IV[0]), set1_128(IV[1]), set1_128(IV[2]), set1_128(IV[3]),
counter_low_vec, counter_high_vec, block_len_vec, block_flags_vec,
};
round_fn4(v, msg_vecs, 0);
round_fn4(v, msg_vecs, 1);
round_fn4(v, msg_vecs, 2);
round_fn4(v, msg_vecs, 3);
round_fn4(v, msg_vecs, 4);
round_fn4(v, msg_vecs, 5);
round_fn4(v, msg_vecs, 6);
h_vecs[0] = xor_128(v[0], v[8]);
h_vecs[1] = xor_128(v[1], v[9]);
h_vecs[2] = xor_128(v[2], v[10]);
h_vecs[3] = xor_128(v[3], v[11]);
h_vecs[4] = xor_128(v[4], v[12]);
h_vecs[5] = xor_128(v[5], v[13]);
h_vecs[6] = xor_128(v[6], v[14]);
h_vecs[7] = xor_128(v[7], v[15]);
block_flags = flags;
}
transpose_vecs_128(&h_vecs[0]);
transpose_vecs_128(&h_vecs[4]);
// The first four vecs now contain the first half of each output, and the
// second four vecs contain the second half of each output.
storeu_128(h_vecs[0], &out[0 * sizeof(uint32x4_t)]);
storeu_128(h_vecs[4], &out[1 * sizeof(uint32x4_t)]);
storeu_128(h_vecs[1], &out[2 * sizeof(uint32x4_t)]);
storeu_128(h_vecs[5], &out[3 * sizeof(uint32x4_t)]);
storeu_128(h_vecs[2], &out[4 * sizeof(uint32x4_t)]);
storeu_128(h_vecs[6], &out[5 * sizeof(uint32x4_t)]);
storeu_128(h_vecs[3], &out[6 * sizeof(uint32x4_t)]);
storeu_128(h_vecs[7], &out[7 * sizeof(uint32x4_t)]);
}
/*
* ----------------------------------------------------------------------------
* hash_many_neon
* ----------------------------------------------------------------------------
*/
void blake3_compress_in_place_portable(uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags);
INLINE void hash_one_neon(const uint8_t *input, size_t blocks,
const uint32_t key[8], uint64_t counter,
uint8_t flags, uint8_t flags_start, uint8_t flags_end,
uint8_t out[BLAKE3_OUT_LEN]) {
uint32_t cv[8];
memcpy(cv, key, BLAKE3_KEY_LEN);
uint8_t block_flags = flags | flags_start;
while (blocks > 0) {
if (blocks == 1) {
block_flags |= flags_end;
}
// TODO: Implement compress_neon. However note that according to
// https://github.com/BLAKE2/BLAKE2/commit/7965d3e6e1b4193438b8d3a656787587d2579227,
// compress_neon might not be any faster than compress_portable.
blake3_compress_in_place_portable(cv, input, BLAKE3_BLOCK_LEN, counter,
block_flags);
input = &input[BLAKE3_BLOCK_LEN];
blocks -= 1;
block_flags = flags;
}
memcpy(out, cv, BLAKE3_OUT_LEN);
}
void blake3_hash_many_neon(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out) {
while (num_inputs >= 4) {
blake3_hash4_neon(inputs, blocks, key, counter, increment_counter, flags,
flags_start, flags_end, out);
if (increment_counter) {
counter += 4;
}
inputs += 4;
num_inputs -= 4;
out = &out[4 * BLAKE3_OUT_LEN];
}
while (num_inputs > 0) {
hash_one_neon(inputs[0], blocks, key, counter, flags, flags_start,
flags_end, out);
if (increment_counter) {
counter += 1;
}
inputs += 1;
num_inputs -= 1;
out = &out[BLAKE3_OUT_LEN];
}
}
@@ -0,0 +1,160 @@
#include "blake3_impl.h"
#include <string.h>
INLINE uint32_t rotr32(uint32_t w, uint32_t c) {
return (w >> c) | (w << (32 - c));
}
INLINE void g(uint32_t *state, size_t a, size_t b, size_t c, size_t d,
uint32_t x, uint32_t y) {
state[a] = state[a] + state[b] + x;
state[d] = rotr32(state[d] ^ state[a], 16);
state[c] = state[c] + state[d];
state[b] = rotr32(state[b] ^ state[c], 12);
state[a] = state[a] + state[b] + y;
state[d] = rotr32(state[d] ^ state[a], 8);
state[c] = state[c] + state[d];
state[b] = rotr32(state[b] ^ state[c], 7);
}
INLINE void round_fn(uint32_t state[16], const uint32_t *msg, size_t round) {
// Select the message schedule based on the round.
const uint8_t *schedule = MSG_SCHEDULE[round];
// Mix the columns.
g(state, 0, 4, 8, 12, msg[schedule[0]], msg[schedule[1]]);
g(state, 1, 5, 9, 13, msg[schedule[2]], msg[schedule[3]]);
g(state, 2, 6, 10, 14, msg[schedule[4]], msg[schedule[5]]);
g(state, 3, 7, 11, 15, msg[schedule[6]], msg[schedule[7]]);
// Mix the rows.
g(state, 0, 5, 10, 15, msg[schedule[8]], msg[schedule[9]]);
g(state, 1, 6, 11, 12, msg[schedule[10]], msg[schedule[11]]);
g(state, 2, 7, 8, 13, msg[schedule[12]], msg[schedule[13]]);
g(state, 3, 4, 9, 14, msg[schedule[14]], msg[schedule[15]]);
}
INLINE void compress_pre(uint32_t state[16], const uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter, uint8_t flags) {
uint32_t block_words[16];
block_words[0] = load32(block + 4 * 0);
block_words[1] = load32(block + 4 * 1);
block_words[2] = load32(block + 4 * 2);
block_words[3] = load32(block + 4 * 3);
block_words[4] = load32(block + 4 * 4);
block_words[5] = load32(block + 4 * 5);
block_words[6] = load32(block + 4 * 6);
block_words[7] = load32(block + 4 * 7);
block_words[8] = load32(block + 4 * 8);
block_words[9] = load32(block + 4 * 9);
block_words[10] = load32(block + 4 * 10);
block_words[11] = load32(block + 4 * 11);
block_words[12] = load32(block + 4 * 12);
block_words[13] = load32(block + 4 * 13);
block_words[14] = load32(block + 4 * 14);
block_words[15] = load32(block + 4 * 15);
state[0] = cv[0];
state[1] = cv[1];
state[2] = cv[2];
state[3] = cv[3];
state[4] = cv[4];
state[5] = cv[5];
state[6] = cv[6];
state[7] = cv[7];
state[8] = IV[0];
state[9] = IV[1];
state[10] = IV[2];
state[11] = IV[3];
state[12] = counter_low(counter);
state[13] = counter_high(counter);
state[14] = (uint32_t)block_len;
state[15] = (uint32_t)flags;
round_fn(state, &block_words[0], 0);
round_fn(state, &block_words[0], 1);
round_fn(state, &block_words[0], 2);
round_fn(state, &block_words[0], 3);
round_fn(state, &block_words[0], 4);
round_fn(state, &block_words[0], 5);
round_fn(state, &block_words[0], 6);
}
void blake3_compress_in_place_portable(uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags) {
uint32_t state[16];
compress_pre(state, cv, block, block_len, counter, flags);
cv[0] = state[0] ^ state[8];
cv[1] = state[1] ^ state[9];
cv[2] = state[2] ^ state[10];
cv[3] = state[3] ^ state[11];
cv[4] = state[4] ^ state[12];
cv[5] = state[5] ^ state[13];
cv[6] = state[6] ^ state[14];
cv[7] = state[7] ^ state[15];
}
void blake3_compress_xof_portable(const uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t counter,
uint8_t flags, uint8_t out[64]) {
uint32_t state[16];
compress_pre(state, cv, block, block_len, counter, flags);
store32(&out[0 * 4], state[0] ^ state[8]);
store32(&out[1 * 4], state[1] ^ state[9]);
store32(&out[2 * 4], state[2] ^ state[10]);
store32(&out[3 * 4], state[3] ^ state[11]);
store32(&out[4 * 4], state[4] ^ state[12]);
store32(&out[5 * 4], state[5] ^ state[13]);
store32(&out[6 * 4], state[6] ^ state[14]);
store32(&out[7 * 4], state[7] ^ state[15]);
store32(&out[8 * 4], state[8] ^ cv[0]);
store32(&out[9 * 4], state[9] ^ cv[1]);
store32(&out[10 * 4], state[10] ^ cv[2]);
store32(&out[11 * 4], state[11] ^ cv[3]);
store32(&out[12 * 4], state[12] ^ cv[4]);
store32(&out[13 * 4], state[13] ^ cv[5]);
store32(&out[14 * 4], state[14] ^ cv[6]);
store32(&out[15 * 4], state[15] ^ cv[7]);
}
INLINE void hash_one_portable(const uint8_t *input, size_t blocks,
const uint32_t key[8], uint64_t counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t out[BLAKE3_OUT_LEN]) {
uint32_t cv[8];
memcpy(cv, key, BLAKE3_KEY_LEN);
uint8_t block_flags = flags | flags_start;
while (blocks > 0) {
if (blocks == 1) {
block_flags |= flags_end;
}
blake3_compress_in_place_portable(cv, input, BLAKE3_BLOCK_LEN, counter,
block_flags);
input = &input[BLAKE3_BLOCK_LEN];
blocks -= 1;
block_flags = flags;
}
store_cv_words(out, cv);
}
void blake3_hash_many_portable(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out) {
while (num_inputs > 0) {
hash_one_portable(inputs[0], blocks, key, counter, flags, flags_start,
flags_end, out);
if (increment_counter) {
counter += 1;
}
inputs += 1;
num_inputs -= 1;
out = &out[BLAKE3_OUT_LEN];
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More