summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2024-07-30 02:28:59 +0200
committerMitya Selivanov <automainint@guattari.tech>2024-07-30 02:28:59 +0200
commit5fc5a3ae127a907bf2057d934aa579ea3e76f8d8 (patch)
treed8b94fe64e4e57f5fe2c87e690a39d399efd57fd
parent941491cf484bd6f11dd743aae76e8dea8057cc60 (diff)
downloadbxgen-5fc5a3ae127a907bf2057d934aa579ea3e76f8d8.zip
Codegen: call (incomplete)
-rwxr-xr-xbxgen.c258
1 files changed, 174 insertions, 84 deletions
diff --git a/bxgen.c b/bxgen.c
index 1442daf..824cdfb 100755
--- a/bxgen.c
+++ b/bxgen.c
@@ -10,7 +10,7 @@
#/
#/ Qualities
#/
-#/ - Single source file (for now)
+#/ - Single source file
#/ - Simple and flexible API
#/ - No external dependencies
#/ - No configuration required
@@ -31,7 +31,7 @@
#/ - Linking libraries
#/ - GNU ld script
#/ - Unused dependencies elimination
-#/ - Use 0 for UNDEFINED
+#/ - Use 0 for UNDEFINED. Make the zero value useful
#/ - String table for names and arrays
#/ - Proper prefixes for identifiers
#/ - Effective entity allocation
@@ -195,6 +195,7 @@ enum {
DATA_C8 = 0,
DATA_I64,
+ DATA_REFERENCE,
CTRL_CALL,
CTRL_RET,
@@ -263,7 +264,8 @@ enum {
// Relocations
//
- REL_CALL = 0,
+ REL_ADD_RODATA_ADDRESS = 0,
+ REL_PROC_ADDRESS_RELATIVE,
};
// TODO
@@ -281,12 +283,28 @@ typedef struct {
} Strint_Table;
typedef struct {
- i16 num;
- u16 type;
i64 node;
} Var;
typedef struct {
+ i64 address;
+ i64 num_bytes;
+ union {
+ // TODO use string table
+ i8 as_i8 [MAX_LITERAL_SIZE];
+ i16 as_i16[MAX_LITERAL_SIZE / 2];
+ i32 as_i32[MAX_LITERAL_SIZE / 4];
+ i64 as_i64[MAX_LITERAL_SIZE / 8];
+ u8 as_u8 [MAX_LITERAL_SIZE];
+ u16 as_u16[MAX_LITERAL_SIZE / 2];
+ u32 as_u32[MAX_LITERAL_SIZE / 4];
+ u64 as_u64[MAX_LITERAL_SIZE / 8];
+ f32 as_f32[MAX_LITERAL_SIZE / sizeof(f32)];
+ f64 as_f64[MAX_LITERAL_SIZE / sizeof(f64)];
+ };
+} Literal;
+
+typedef struct {
i16 num_vals;
Var vals[MAX_NUM_ARGS];
} Ret;
@@ -311,20 +329,10 @@ typedef struct {
u16 op;
i64 index_in_proc;
union {
- u8 lit_bytes[MAX_LITERAL_SIZE]; // byte array literal
- // TODO use string table
- i8 lit_i8;
- i16 lit_i16;
- i32 lit_i32;
- i64 lit_i64;
- u8 lit_u8;
- u16 lit_u16;
- u32 lit_u32;
- u64 lit_u64;
- f32 lit_f32;
- f64 lit_f64;
- Ret ret;
- Call call;
+ Literal lit;
+ Var ref;
+ Ret ret;
+ Call call;
};
} Node;
@@ -376,10 +384,11 @@ typedef struct {
typedef struct {
u16 type;
- i64 name_size;
- i64 name[MAX_NAME_SIZE];
- i64 address;
+ i64 offset;
i64 size;
+ i64 value;
+ i64 name_size;
+ c8 *name;
} Rel_Entry;
typedef struct {
@@ -749,17 +758,28 @@ void node_destroy(Pool *pool, i64 node) {
pool_remove(pool, node, ENTITY_NODE);
}
+i64 node_data_reference(Pool *pool, i64 node) {
+ return node_init(pool, (Node) {
+ .op = DATA_REFERENCE,
+ .ref = node,
+ });
+}
+
i64 node_data_array_c8(Pool *pool, i64 size, c8 *data) {
BX_CHECK(size < MAX_LITERAL_SIZE, "Too big", UNDEFINED);
- Node node_entry = { .op = DATA_C8, };
- bx_mem_cpy(node_entry.lit_bytes, data, size);
+ Node node_entry = {
+ .op = DATA_C8,
+ .lit.num_bytes = size
+ };
+ bx_mem_cpy(node_entry.lit.as_u8, data, size);
return node_init(pool, node_entry);
}
i64 node_data_i64(Pool *pool, i64 value) {
return node_init(pool, (Node) {
- .op = DATA_I64,
- .lit_i64 = value,
+ .op = DATA_I64,
+ .lit.num_bytes = sizeof value,
+ .lit.as_i64 = value,
});
}
@@ -769,7 +789,7 @@ i64 node_ctrl_call(Pool *pool, u16 convention, i64 target_proc, i64 num_args, Va
Call call = {
.convention = convention,
.target_proc = target_proc,
- .num_args = num_args,
+ .num_args = num_args,
};
if (num_args > 0)
@@ -786,8 +806,9 @@ i64 node_ctrl_call_by_name(Pool *pool, u16 convention, i64 name_size, c8 *name,
Call call = {
.convention = convention,
+ .target_proc = UNDEFINED,
.target_name_size = name_size,
- .num_args = num_args,
+ .num_args = num_args,
};
if (name_size > 0)
@@ -1688,37 +1709,75 @@ void x86_64_emit_node(
switch (n->op) {
case DATA_C8:
case DATA_I64:
+ case DATA_REFERENCE:
// Do nothing
break;
case CTRL_CALL: {
- BX_LOG(ERROR, "CTRL_CALL: not implemented", 0);
+ BX_CHECK(n->call.convention == CONV_CDECL, "Not implemented", 0);
+ BX_CHECK(n->call.target_proc == UNDEFINED, "Not implemented", 0);
+ BX_CHECK(n->call.target_name_size > 0, "No proc name", 0);
+ BX_CHECK(n->call.num_args == 1, "Not implemented", 0);
- if (0) {
- BX_CHECK(n->call.convention == CONV_CDECL, "Not implemented", 0);
- BX_CHECK(n->call.target_proc == UNDEFINED, "Not implemented", 0);
- BX_CHECK(n->call.target_name_size > 0, "No proc name", 0);
+ i64 n_arg = n->call.args[0].node;
+ BX_CHECK(n_arg != UNDEFINED, "Internal", 0);
+ BX_CHECK(pool->entities[n_arg].is_enabled, "Internal", 0);
+ BX_CHECK(pool->entities[n_arg].type == ENTITY_NODE, "Internal", 0);
- BX_CHECK(n->call.num_args == 1, "Not implemented", 0);
- BX_CHECK(n->call.args[0].num == 1, "Not implemented", 0);
- BX_CHECK(n->call.args[0].type == TYPE_PTR, "Not implemented", 0);
+ Node *ref = &pool->entities[n_arg].node;
+ BX_CHECK(ref->op == DATA_REFERENCE, "Not implemented", 0);
- i64 n_arg = n->call.args[0].node;
- BX_CHECK(n_arg != UNDEFINED, "Internal", 0);
- BX_CHECK(pool->entities[n_arg].is_enabled, "Internal", 0);
- BX_CHECK(pool->entities[n_arg].type == ENTITY_NODE, "Internal", 0);
+ Node *data = &pool->entities[ref->ref.node].node;
+ BX_CHECK(data->op == DATA_C8, "Not implemented", 0);
- Node *arg = &pool->entities[n_arg].node;
- BX_CHECK(arg->op == DATA_C8, "Not implemented", 0);
+ BX_CHECK(codegen->offset_rodata + data->lit.num_bytes <= codegen->max_rodata_size, "Out of memory", 0);
+ BX_CHECK(codegen->num_rels + 2 <= codegen->max_num_rels, "Out of memory", 0);
- // TODO
- }
+ // Write data
+ //
+
+ i64 arg_offset = codegen->offset_rodata;
+ codegen->offset_rodata += data->lit.num_bytes;
+ bx_mem_cpy(codegen->buffer_rodata + arg_offset, data->lit.as_u8, data->lit.num_bytes);
+
+ // Write code and relocations
+ //
+
+ write_u8(LE, 0x90, begin, end); // nop
+ write_u8(LE, 0xbf, begin + 1, end); // mov edi
+
+ codegen->rels[codegen->num_rels++] = (Rel_Entry) {
+ .type = REL_ADD_RODATA_ADDRESS,
+ .offset = codegen->offset_code + 2,
+ .size = 4,
+ .value = arg_offset,
+ };
+
+ write_u8(LE, 0x31, begin + 6, end); // xor eax
+ write_u8(LE, 0xc0, begin + 7, end); // eax
+ write_u8(LE, 0xe8, begin + 8, end); // call
+
+ codegen->rels[codegen->num_rels++] = (Rel_Entry) {
+ .type = REL_PROC_ADDRESS_RELATIVE,
+ .offset = codegen->offset_code + 9,
+ .size = 4,
+ .name_size = n->call.target_name_size,
+ .name = n->call.target_name,
+ };
+
+ write_u8(LE, 0x90, begin + 13, end); // nop
+
+ write_u8(LE, 0xeb, begin, end); // jmp
+ write_u8(LE, 0x0c, begin + 1, end); // 12
+
+ codegen->offset_code += 14;
+
+ BX_CHECK(codegen->offset_code <= codegen->max_code_size, "Out of memory", 0);
} break;
case CTRL_RET: {
if ((context & EMIT_ENTRY_PROC) != 0) {
BX_CHECK(n->ret.num_vals == 1, "Not implemented", 0);
- BX_CHECK(n->ret.vals[0].num == 1, "Not implemented", 0);
write_u8 (LE, 0xb8, begin, end); // mov eax
write_u32(LE, 60, begin + 1, end); // 60
@@ -1737,10 +1796,11 @@ void x86_64_emit_node(
BX_CHECK(pool->entities[n_val].type == ENTITY_NODE, "Internal", 0);
Node *val = &pool->entities[n_val].node;
- BX_CHECK(val->op == DATA_I64, "Not implemented", 0);
+ BX_CHECK(val->op == DATA_I64, "Not implemented", 0);
+ BX_CHECK(val->lit.num_bytes == 8, "Not implemented", 0);
- write_u8 (LE, 0xbf, begin + 5, end); // mov edi
- write_u32(LE, val->lit_u32, begin + 6, end); // <- literal
+ write_u8 (LE, 0xbf, begin + 5, end); // mov edi
+ write_u32(LE, *val->lit.as_u32, begin + 6, end); // <- literal
}
write_u8 (LE, 0x0f, begin + 10, end); // syscall
@@ -2288,8 +2348,6 @@ i64 unit_write_in_memory(
i64 rotext_address = base_address + program_offset;
i64 entry = rotext_address + codegen->entry_point;
- Symbol_Entry sym_printf = {0};
-
i64 rotext_size = codegen->offset_code;
i64 rwzval_size = 0;
i64 rwdata_size = 0;
@@ -2480,7 +2538,7 @@ i64 unit_write_in_memory(
// ==============================================================
//
- // TODO Apply relocations
+ // Apply relocations
for (i64 elf_index = 0, sec_index_global = 0; elf_index < linker->num_obj_files; ++elf_index) {
Buffer_Context buf = elf_buffer_context(pool, linker, linker->num_obj_files, elf_index);
@@ -2537,6 +2595,8 @@ i64 unit_write_in_memory(
u8 *dst = buf.begin + elf_section(buf, dst_index).data.offset + relx.offset;
+ // TODO Implement GOT and PLT.
+
// Represents the addend used to compute the value of the relocatable field.
i64 A = relx.addent;
@@ -2568,7 +2628,7 @@ i64 unit_write_in_memory(
write_i##BITS(LE, (i##BITS) x_, dst, buf.end); \
} while (0)
- #define TODO_ BX_FAIL("Not implemented",0)
+ #define TODO_ BX_FAIL("Not implemented", 0)
case R_X86_64_NONE: /* Do nothing */ break;
case R_X86_64_64: ADD_(64, S + A); break;
@@ -2622,13 +2682,49 @@ i64 unit_write_in_memory(
// ==============================================================
//
- // TODO Search symbols
+ // Apply our relocations
- for (i64 i = 0; i < num_symbols; ++i)
- if (linker->symbols[i].name_size == 6 && bx_mem_eq(linker->symbols[i].name, "printf", 6)) {
- sym_printf = linker->symbols[i];
- break;
+ for (i64 rel_index = 0; rel_index < codegen->num_rels; ++rel_index) {
+ Rel_Entry rel = codegen->rels[rel_index];
+
+ u8 *begin = codegen->buffer_code + rel.offset;
+ u8 *end = codegen->buffer_code + codegen->offset_code;
+
+ switch (rel.type) {
+ case REL_ADD_RODATA_ADDRESS: {
+ BX_CHECK(rel.size == 4, "Not implemented", 0);
+
+ i64 value = rel.value + rodata_address;
+ BX_CHECK(value == (i64) (i32) value, "Integer overflow", 0);
+ write_i32(LE, (i32) value, begin, end);
+ } break;
+
+ case REL_PROC_ADDRESS_RELATIVE: {
+ BX_CHECK(rel.size == 4, "Not implemented", 0);
+ BX_CHECK(rel.name_size > 0 && rel.name != NULL, "No proc name", 0);
+
+ b8 found = 0;
+ for (i64 i = 0; i < num_symbols; ++i)
+ if (linker->symbols[i].name_size == rel.name_size &&
+ bx_mem_eq(linker->symbols[i].name, rel.name, 6)) {
+ i64 rel_address = rotext_address + rel.offset;
+ i64 sym_address = linker->symbols[i].address;
+ i64 value = sym_address - rel_address;
+
+ BX_CHECK(value == (i64) (i32) value, "Integer overflow", 0);
+
+ write_i32(LE, (i32) value, begin, end);
+
+ BX_LOG(VERBOSE, "Found %.*s: %08llx (+%llx)", rel.name_size, rel.name, sym_address, value);
+ found = 1;
+ break;
+ }
+
+ if (!found)
+ BX_LOG(ERROR, "Undefined symbol: %.*s", rel.name_size, rel.name);
+ } break;
}
+ }
// ==============================================================
//
@@ -2643,15 +2739,10 @@ i64 unit_write_in_memory(
BX_LOG(VERBOSE, "Total %lld symbols", num_symbols);
BX_LOG(VERBOSE, "Total size");
- BX_LOG(VERBOSE, ".rotext - %lld bytes", rotext_size);
- BX_LOG(VERBOSE, ".rwzval - %lld bytes", rwzval_size);
- BX_LOG(VERBOSE, ".rwdata - %lld bytes", rwdata_size);
- BX_LOG(VERBOSE, ".rodata - %lld bytes", rodata_size);
-
- if (sym_printf.name_size != 0)
- BX_LOG(VERBOSE, "Found printf: %08llx", sym_printf.address);
- else
- BX_LOG(ERROR, "Undefined symbol: printf");
+ BX_LOG(VERBOSE, ".rotext - %7lld bytes", rotext_size);
+ BX_LOG(VERBOSE, ".rwzval - %7lld bytes", rwzval_size);
+ BX_LOG(VERBOSE, ".rwdata - %7lld bytes", rwdata_size);
+ BX_LOG(VERBOSE, ".rodata - %7lld bytes", rodata_size);
BX_LOG(VERBOSE, "Writing ELF x86_64 executable");
@@ -2842,7 +2933,7 @@ void unit_write(
i64 delta_size = bx_align(size, X86_64_ALIGNMENT);
BX_CHECK(obj_files_size + delta_size < linker->max_dependencies_size, "Out of memory",);
- BX_CHECK(linker->num_obj_files + 1 < linker->max_num_obj_files, "Out of memory",);
+ BX_CHECK(linker->num_obj_files + 1 < linker->max_num_obj_files, "Out of memory",);
bx_mem_cpy(linker->dependencies_buffer + obj_files_size, f_data, size);
@@ -3169,9 +3260,11 @@ Linker_Context g_linker = {
i64 n_str(i64 proc, c8 *value) {
i64 len = bx_str_len(value, value + MAX_LITERAL_SIZE);
- i64 n = node_data_array_c8(&g_pool, len, value);
- p_add(proc, n);
- return n;
+ i64 n_data = node_data_array_c8(&g_pool, len, value);
+ i64 n_ref = node_data_reference(&g_pool, n_data);
+ p_add(proc, n_data);
+ p_add(proc, n_ref);
+ return n_ref;
}
i64 n_i64(i64 proc, i64 value) {
@@ -3314,10 +3407,7 @@ int main(int argc, char **argv) {
"printf", // proc name
1, // number of arguments
(Var[]) {{
- // the first argument
- .num = 1,
- .type = TYPE_PTR,
- .node = n_str(mainproc, "hello sailor"),
+ .node = n_str(mainproc, "hello sailor"), // the first argument
}}
);
@@ -3326,10 +3416,7 @@ int main(int argc, char **argv) {
mainproc,
1, // number of returned values
(Var[]) {{
- // the return value
- .num = 1, // number of elements
- .type = TYPE_I32, // type
- .node = n_i64(mainproc, 42), // source to get the value from
+ .node = n_i64(mainproc, 42), // the return value
}}
);
@@ -3346,13 +3433,16 @@ int main(int argc, char **argv) {
// ============================================================
- BX_CHECK(HOST == HOST_Linux, "Host system is not compatible", -1);
- BX_CHECK(HO == LE, "Host data ordering is not compatible", -1);
-
- // Run the created executable file.
- i32 ret = system("./test_foo");
+ if (HOST != HOST_Linux) {
+ BX_LOG(INFO, "Skip running the executable. Host system is not compatible.");
+ } else if (HO != LE) {
+ BX_LOG(INFO, "Skip running the executable. Host data ordering is not compatible.");
+ } else {
+ // Run the created executable file.
+ i32 ret = system("./test_foo");
- BX_CHECK(WEXITSTATUS(ret) == 42, "Failure", -1);
+ BX_CHECK(WEXITSTATUS(ret) == 42, "Failure", -1);
+ }
BX_LOG(INFO, "Bye!");
return 0;