diff options
author | Mitya Selivanov <automainint@guattari.tech> | 2024-07-30 02:28:59 +0200 |
---|---|---|
committer | Mitya Selivanov <automainint@guattari.tech> | 2024-07-30 02:28:59 +0200 |
commit | 5fc5a3ae127a907bf2057d934aa579ea3e76f8d8 (patch) | |
tree | d8b94fe64e4e57f5fe2c87e690a39d399efd57fd | |
parent | 941491cf484bd6f11dd743aae76e8dea8057cc60 (diff) | |
download | bxgen-5fc5a3ae127a907bf2057d934aa579ea3e76f8d8.zip |
Codegen: call (incomplete)
-rwxr-xr-x | bxgen.c | 258 |
1 files changed, 174 insertions, 84 deletions
@@ -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; |