diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/binary-reader-objdump.c | 11 | ||||
-rw-r--r-- | src/binary-reader.c | 21 | ||||
-rw-r--r-- | src/binary-reader.h | 10 | ||||
-rw-r--r-- | src/binary-writer.c | 153 | ||||
-rw-r--r-- | src/binary-writer.h | 6 | ||||
-rw-r--r-- | src/common.c | 6 | ||||
-rw-r--r-- | src/common.h | 19 | ||||
-rw-r--r-- | src/stream.c | 1 | ||||
-rw-r--r-- | src/tools/wasm-link.c | 906 | ||||
-rw-r--r-- | src/tools/wast2wasm.c | 12 |
10 files changed, 1117 insertions, 28 deletions
diff --git a/src/binary-reader-objdump.c b/src/binary-reader-objdump.c index 4c2b4018..e00cb3d8 100644 --- a/src/binary-reader-objdump.c +++ b/src/binary-reader-objdump.c @@ -566,6 +566,15 @@ static WasmResult on_local_name(uint32_t func_index, return WASM_OK; } +WasmResult on_reloc(WasmRelocType type, + uint32_t section, + uint32_t offset, + void* user_data) { + print_details(user_data, " - %-20s section=%d offset=%#x\n", + wasm_get_reloc_type_name(type), section, offset); + return WASM_OK; +} + static void on_error(WasmBinaryReaderContext* ctx, const char* message) { WasmDefaultErrorHandlerInfo info; info.header = "error reading binary"; @@ -657,6 +666,8 @@ static WasmBinaryReader s_binary_reader = { .on_function_name = on_function_name, .on_local_name = on_local_name, + .on_reloc = on_reloc, + .on_init_expr_i32_const_expr = on_init_expr_i32_const_expr, .on_init_expr_i64_const_expr = on_init_expr_i64_const_expr, .on_init_expr_f32_const_expr = on_init_expr_f32_const_expr, diff --git a/src/binary-reader.c b/src/binary-reader.c index 9223935a..ae63ef30 100644 --- a/src/binary-reader.c +++ b/src/binary-reader.c @@ -629,6 +629,9 @@ LOGGING_BEGIN(names_section) LOGGING_UINT32(on_function_names_count) LOGGING_UINT32_UINT32(on_local_names_count, "index", "count") LOGGING_END(names_section) +LOGGING_BEGIN(reloc_section) +LOGGING_UINT32(on_reloc_count) +LOGGING_END(reloc_section) LOGGING_UINT32_UINT32(on_init_expr_get_global_expr, "index", "global_index") static void sprint_limits(char* dst, size_t size, const WasmLimits* limits) { @@ -1092,6 +1095,10 @@ static WasmBinaryReader s_logging_binary_reader = { .on_local_name = logging_on_local_name, .end_names_section = logging_end_names_section, + .begin_reloc_section = logging_begin_reloc_section, + .on_reloc_count = logging_on_reloc_count, + .end_reloc_section = logging_end_reloc_section, + .on_init_expr_f32_const_expr = logging_on_init_expr_f32_const_expr, .on_init_expr_f64_const_expr = logging_on_init_expr_f64_const_expr, .on_init_expr_get_global_expr = logging_on_init_expr_get_global_expr, @@ -1669,6 +1676,20 @@ static void read_custom_section(Context* ctx, uint32_t section_size) { } } CALLBACK0(end_names_section); + } else if (strncmp(section_name.start, WASM_BINARY_SECTION_RELOC, + strlen(WASM_BINARY_SECTION_RELOC)) == 0) { + CALLBACK_SECTION(begin_reloc_section, section_size); + uint32_t i, num_relocs, section; + in_u32_leb128(ctx, §ion, "section"); + in_u32_leb128(ctx, &num_relocs, "relocation count count"); + CALLBACK(on_reloc_count, num_relocs); + for (i = 0; i < num_relocs; ++i) { + uint32_t reloc_type, offset; + in_u32_leb128(ctx, &reloc_type, "relocation type"); + in_u32_leb128(ctx, &offset, "offset"); + CALLBACK(on_reloc, reloc_type, section, offset); + } + CALLBACK0(end_reloc_section); } else { /* This is an unknown custom section, skip it. */ ctx->offset = ctx->read_end; diff --git a/src/binary-reader.h b/src/binary-reader.h index 93ffe1cc..297b0e55 100644 --- a/src/binary-reader.h +++ b/src/binary-reader.h @@ -287,6 +287,16 @@ typedef struct WasmBinaryReader { void* user_data); WasmResult (*end_names_section)(WasmBinaryReaderContext* ctx); + /* names section */ + WasmResult (*begin_reloc_section)(WasmBinaryReaderContext* ctx, + uint32_t size); + WasmResult (*on_reloc_count)(uint32_t count, void* user_data); + WasmResult (*on_reloc)(WasmRelocType type, + uint32_t section_index, + uint32_t offset, + void* user_data); + WasmResult (*end_reloc_section)(WasmBinaryReaderContext* ctx); + /* init_expr - used by elem, data and global sections; these functions are * only called between calls to begin_*_init_expr and end_*_init_expr */ WasmResult (*on_init_expr_f32_const_expr)(uint32_t index, diff --git a/src/binary-writer.c b/src/binary-writer.c index b7cae6d1..a1ce8658 100644 --- a/src/binary-writer.c +++ b/src/binary-writer.c @@ -33,19 +33,47 @@ #define MAX_U32_LEB128_BYTES 5 #define MAX_U64_LEB128_BYTES 10 +/* TODO(binji): better leb size guess. Some sections we know will only be 1 + byte, but others we can be fairly certain will be larger. */ +static const size_t LEB_SECTION_SIZE_GUESS = 1; + #define ALLOC_FAILURE \ fprintf(stderr, "%s:%d: allocation failed\n", __FILE__, __LINE__) +typedef struct Reloc { + WasmRelocType type; + size_t offset; +} Reloc; +WASM_DEFINE_VECTOR(reloc, Reloc); + +typedef struct RelocSection { + const char* name; + uint32_t section_index; + RelocVector relocations; +} RelocSection; +WASM_DEFINE_VECTOR(reloc_section, RelocSection); + typedef struct Context { WasmAllocator* allocator; WasmStream stream; WasmStream* log_stream; const WasmWriteBinaryOptions* options; + RelocSectionVector reloc_sections; + RelocSection* current_reloc_section; + size_t last_section_offset; size_t last_section_leb_size_guess; + size_t last_section_index; + WasmBinarySection last_section_type; + size_t last_section_payload_offset; } Context; +void wasm_destroy_reloc_section(WasmAllocator* allocator, + RelocSection* reloc_section) { + wasm_destroy_reloc_vector(allocator, &reloc_section->relocations); +} + static uint8_t log2_u32(uint32_t x) { uint8_t result = 0; while (x > 1) { @@ -77,6 +105,13 @@ static void write_header(Context* ctx, const char* name, int index) { } \ } while (1) +uint32_t wasm_u32_leb128_length(uint32_t value) { + uint8_t data[MAX_U32_LEB128_BYTES] WASM_UNUSED; + uint32_t i = 0; + LEB128_LOOP_UNTIL(value == 0); + return i; +} + /* returns the length of the leb128 */ uint32_t wasm_write_u32_leb128_at(WasmStream* stream, uint32_t offset, @@ -245,9 +280,12 @@ static void begin_known_section(Context* ctx, wasm_get_section_name(section_code), section_code); write_header(ctx, desc, PRINT_HEADER_NO_INDEX); wasm_write_u8(&ctx->stream, section_code, "section code"); + ctx->last_section_index++; + ctx->last_section_type = section_code; ctx->last_section_leb_size_guess = leb_size_guess; ctx->last_section_offset = write_u32_leb128_space(ctx, leb_size_guess, "section size (guess)"); + ctx->last_section_payload_offset = ctx->stream.offset; } static void begin_custom_section(Context* ctx, @@ -259,9 +297,12 @@ static void begin_custom_section(Context* ctx, write_header(ctx, desc, PRINT_HEADER_NO_INDEX); wasm_write_u8(&ctx->stream, WASM_BINARY_SECTION_CUSTOM, "custom section code"); + ctx->last_section_index++; + ctx->last_section_type = WASM_BINARY_SECTION_CUSTOM; ctx->last_section_leb_size_guess = leb_size_guess; ctx->last_section_offset = write_u32_leb128_space(ctx, leb_size_guess, "section size (guess)"); + ctx->last_section_payload_offset = ctx->stream.offset; wasm_write_str(&ctx->stream, name, strlen(name), WASM_PRINT_CHARS, "custom section name"); } @@ -284,6 +325,36 @@ static void write_expr_list(Context* ctx, const WasmFunc* func, const WasmExpr* first_expr); +static void add_reloc(Context* ctx, WasmRelocType reloc_type) { + // Add a new reloc section if needed + if (!ctx->current_reloc_section || + ctx->current_reloc_section->section_index != ctx->last_section_index) { + ctx->current_reloc_section = + wasm_append_reloc_section(ctx->allocator, &ctx->reloc_sections); + ctx->current_reloc_section->name = + wasm_get_section_name(ctx->last_section_type); + ctx->current_reloc_section->section_index = ctx->last_section_index; + } + + // Add a new relocation to the curent reloc section + Reloc* r = wasm_append_reloc(ctx->allocator, + &ctx->current_reloc_section->relocations); + r->type = reloc_type; + r->offset = ctx->stream.offset - ctx->last_section_payload_offset; +} + +static void write_u32_leb128_with_reloc(Context* ctx, + uint32_t value, + const char* desc, + WasmRelocType reloc_type) { + if (ctx->options->relocatable) { + add_reloc(ctx, reloc_type); + wasm_write_fixed_u32_leb128(&ctx->stream, value, desc); + } else { + wasm_write_u32_leb128(&ctx->stream, value, desc); + } +} + static void write_expr(Context* ctx, const WasmModule* module, const WasmFunc* func, @@ -328,7 +399,8 @@ static void write_expr(Context* ctx, assert(ctx->options->is_invalid || (index >= 0 && (size_t)index < module->funcs.size)); wasm_write_opcode(&ctx->stream, WASM_OPCODE_CALL); - wasm_write_u32_leb128(&ctx->stream, index, "function index"); + write_u32_leb128_with_reloc(ctx, index, "function index", + WASM_RELOC_FUNC_INDEX); break; } case WASM_EXPR_TYPE_CALL_INDIRECT: { @@ -382,7 +454,8 @@ static void write_expr(Context* ctx, case WASM_EXPR_TYPE_GET_GLOBAL: { int index = wasm_get_global_index_by_var(module, &expr->get_global.var); wasm_write_opcode(&ctx->stream, WASM_OPCODE_GET_GLOBAL); - wasm_write_u32_leb128(&ctx->stream, index, "global index"); + write_u32_leb128_with_reloc(ctx, index, "global index", + WASM_RELOC_GLOBAL_INDEX); break; } case WASM_EXPR_TYPE_GET_LOCAL: { @@ -432,7 +505,8 @@ static void write_expr(Context* ctx, case WASM_EXPR_TYPE_SET_GLOBAL: { int index = wasm_get_global_index_by_var(module, &expr->get_global.var); wasm_write_opcode(&ctx->stream, WASM_OPCODE_SET_GLOBAL); - wasm_write_u32_leb128(&ctx->stream, index, "global index"); + write_u32_leb128_with_reloc(ctx, index, "global index", + WASM_RELOC_GLOBAL_INDEX); break; } case WASM_EXPR_TYPE_SET_LOCAL: { @@ -557,17 +631,32 @@ static void write_global_header(Context* ctx, const WasmGlobal* global) { wasm_write_u8(&ctx->stream, global->mutable_, "global mutability"); } -static WasmResult write_module(Context* ctx, const WasmModule* module) { - /* TODO(binji): better leb size guess. Some sections we know will only be 1 - byte, but others we can be fairly certain will be larger. */ - const size_t leb_size_guess = 1; +static void write_reloc_section(Context* ctx, RelocSection* reloc_section) { + char section_name[128]; + sprintf(section_name, "%s.%s", WASM_BINARY_SECTION_RELOC, + reloc_section->name); + begin_custom_section(ctx, section_name, LEB_SECTION_SIZE_GUESS); + wasm_write_u32_leb128(&ctx->stream, reloc_section->section_index, + "reloc section"); + RelocVector* relocs = &reloc_section->relocations; + wasm_write_u32_leb128(&ctx->stream, relocs->size, "num relocs"); + + size_t i; + for (i = 0; i < relocs->size; i++) { + wasm_write_u32_leb128(&ctx->stream, relocs->data[i].type, "reloc type"); + wasm_write_u32_leb128(&ctx->stream, relocs->data[i].offset, "reloc offset"); + } + + end_section(ctx); +} +static WasmResult write_module(Context* ctx, const WasmModule* module) { size_t i; wasm_write_u32(&ctx->stream, WASM_BINARY_MAGIC, "WASM_BINARY_MAGIC"); wasm_write_u32(&ctx->stream, WASM_BINARY_VERSION, "WASM_BINARY_VERSION"); if (module->func_types.size) { - begin_known_section(ctx, WASM_BINARY_SECTION_TYPE, leb_size_guess); + begin_known_section(ctx, WASM_BINARY_SECTION_TYPE, LEB_SECTION_SIZE_GUESS); wasm_write_u32_leb128(&ctx->stream, module->func_types.size, "num types"); for (i = 0; i < module->func_types.size; ++i) { const WasmFuncType* func_type = module->func_types.data[i]; @@ -590,7 +679,8 @@ static WasmResult write_module(Context* ctx, const WasmModule* module) { } if (module->imports.size) { - begin_known_section(ctx, WASM_BINARY_SECTION_IMPORT, leb_size_guess); + begin_known_section(ctx, WASM_BINARY_SECTION_IMPORT, + LEB_SECTION_SIZE_GUESS); wasm_write_u32_leb128(&ctx->stream, module->imports.size, "num imports"); for (i = 0; i < module->imports.size; ++i) { @@ -605,9 +695,9 @@ static WasmResult write_module(Context* ctx, const WasmModule* module) { wasm_write_u8(&ctx->stream, import->kind, "import kind"); switch (import->kind) { case WASM_EXTERNAL_KIND_FUNC: - wasm_write_u32_leb128(&ctx->stream, wasm_get_func_type_index_by_decl( - module, &import->func.decl), - "import signature index"); + write_u32_leb128_with_reloc( + ctx, wasm_get_func_type_index_by_decl(module, &import->func.decl), + "import signature index", WASM_RELOC_TYPE_INDEX); break; case WASM_EXTERNAL_KIND_TABLE: write_table(ctx, &import->table); @@ -629,7 +719,8 @@ static WasmResult write_module(Context* ctx, const WasmModule* module) { assert(module->funcs.size >= module->num_func_imports); uint32_t num_funcs = module->funcs.size - module->num_func_imports; if (num_funcs) { - begin_known_section(ctx, WASM_BINARY_SECTION_FUNCTION, leb_size_guess); + begin_known_section(ctx, WASM_BINARY_SECTION_FUNCTION, + LEB_SECTION_SIZE_GUESS); wasm_write_u32_leb128(&ctx->stream, num_funcs, "num functions"); for (i = 0; i < num_funcs; ++i) { @@ -647,7 +738,7 @@ static WasmResult write_module(Context* ctx, const WasmModule* module) { assert(module->tables.size >= module->num_table_imports); uint32_t num_tables = module->tables.size - module->num_table_imports; if (num_tables) { - begin_known_section(ctx, WASM_BINARY_SECTION_TABLE, leb_size_guess); + begin_known_section(ctx, WASM_BINARY_SECTION_TABLE, LEB_SECTION_SIZE_GUESS); wasm_write_u32_leb128(&ctx->stream, num_tables, "num tables"); for (i = 0; i < num_tables; ++i) { const WasmTable* table = @@ -661,7 +752,8 @@ static WasmResult write_module(Context* ctx, const WasmModule* module) { assert(module->memories.size >= module->num_memory_imports); uint32_t num_memories = module->memories.size - module->num_memory_imports; if (num_memories) { - begin_known_section(ctx, WASM_BINARY_SECTION_MEMORY, leb_size_guess); + begin_known_section(ctx, WASM_BINARY_SECTION_MEMORY, + LEB_SECTION_SIZE_GUESS); wasm_write_u32_leb128(&ctx->stream, num_memories, "num memories"); for (i = 0; i < num_memories; ++i) { const WasmMemory* memory = @@ -675,7 +767,8 @@ static WasmResult write_module(Context* ctx, const WasmModule* module) { assert(module->globals.size >= module->num_global_imports); uint32_t num_globals = module->globals.size - module->num_global_imports; if (num_globals) { - begin_known_section(ctx, WASM_BINARY_SECTION_GLOBAL, leb_size_guess); + begin_known_section(ctx, WASM_BINARY_SECTION_GLOBAL, + LEB_SECTION_SIZE_GUESS); wasm_write_u32_leb128(&ctx->stream, num_globals, "num globals"); for (i = 0; i < num_globals; ++i) { @@ -688,7 +781,8 @@ static WasmResult write_module(Context* ctx, const WasmModule* module) { } if (module->exports.size) { - begin_known_section(ctx, WASM_BINARY_SECTION_EXPORT, leb_size_guess); + begin_known_section(ctx, WASM_BINARY_SECTION_EXPORT, + LEB_SECTION_SIZE_GUESS); wasm_write_u32_leb128(&ctx->stream, module->exports.size, "num exports"); for (i = 0; i < module->exports.size; ++i) { @@ -701,7 +795,8 @@ static WasmResult write_module(Context* ctx, const WasmModule* module) { int index = wasm_get_func_index_by_var(module, &export->var); assert(ctx->options->is_invalid || (index >= 0 && (size_t)index < module->funcs.size)); - wasm_write_u32_leb128(&ctx->stream, index, "export func index"); + write_u32_leb128_with_reloc(ctx, index, "export func index", + WASM_RELOC_FUNC_INDEX); break; } case WASM_EXTERNAL_KIND_TABLE: { @@ -736,14 +831,15 @@ static WasmResult write_module(Context* ctx, const WasmModule* module) { if (module->start) { int start_func_index = wasm_get_func_index_by_var(module, module->start); if (start_func_index != -1) { - begin_known_section(ctx, WASM_BINARY_SECTION_START, leb_size_guess); + begin_known_section(ctx, WASM_BINARY_SECTION_START, + LEB_SECTION_SIZE_GUESS); wasm_write_u32_leb128(&ctx->stream, start_func_index, "start func index"); end_section(ctx); } } if (module->elem_segments.size) { - begin_known_section(ctx, WASM_BINARY_SECTION_ELEM, leb_size_guess); + begin_known_section(ctx, WASM_BINARY_SECTION_ELEM, LEB_SECTION_SIZE_GUESS); wasm_write_u32_leb128(&ctx->stream, module->elem_segments.size, "num elem segments"); for (i = 0; i < module->elem_segments.size; ++i) { @@ -760,14 +856,15 @@ static WasmResult write_module(Context* ctx, const WasmModule* module) { int index = wasm_get_func_index_by_var(module, &segment->vars.data[j]); assert(ctx->options->is_invalid || (index >= 0 && (size_t)index < module->funcs.size)); - wasm_write_u32_leb128(&ctx->stream, index, "function index"); + write_u32_leb128_with_reloc(ctx, index, "function index", + WASM_RELOC_FUNC_INDEX); } } end_section(ctx); } if (num_funcs) { - begin_known_section(ctx, WASM_BINARY_SECTION_CODE, leb_size_guess); + begin_known_section(ctx, WASM_BINARY_SECTION_CODE, LEB_SECTION_SIZE_GUESS); wasm_write_u32_leb128(&ctx->stream, num_funcs, "num functions"); for (i = 0; i < num_funcs; ++i) { @@ -786,7 +883,7 @@ static WasmResult write_module(Context* ctx, const WasmModule* module) { } if (module->data_segments.size) { - begin_known_section(ctx, WASM_BINARY_SECTION_DATA, leb_size_guess); + begin_known_section(ctx, WASM_BINARY_SECTION_DATA, LEB_SECTION_SIZE_GUESS); wasm_write_u32_leb128(&ctx->stream, module->data_segments.size, "num data segments"); for (i = 0; i < module->data_segments.size; ++i) { @@ -809,7 +906,7 @@ static WasmResult write_module(Context* ctx, const WasmModule* module) { WASM_ZERO_MEMORY(index_to_name); char desc[100]; - begin_custom_section(ctx, WASM_BINARY_SECTION_NAME, leb_size_guess); + begin_custom_section(ctx, WASM_BINARY_SECTION_NAME, LEB_SECTION_SIZE_GUESS); wasm_write_u32_leb128(&ctx->stream, module->funcs.size, "num functions"); for (i = 0; i < module->funcs.size; ++i) { const WasmFunc* func = module->funcs.data[i]; @@ -851,6 +948,14 @@ static WasmResult write_module(Context* ctx, const WasmModule* module) { wasm_destroy_string_slice_vector(ctx->allocator, &index_to_name); } + if (ctx->options->relocatable) { + for (i = 0; i < ctx->reloc_sections.size; i++) { + write_reloc_section(ctx, &ctx->reloc_sections.data[i]); + } + WASM_DESTROY_VECTOR_AND_ELEMENTS(ctx->allocator, ctx->reloc_sections, + reloc_section); + } + return ctx->stream.result; } diff --git a/src/binary-writer.h b/src/binary-writer.h index b00bc9ef..4d0decc6 100644 --- a/src/binary-writer.h +++ b/src/binary-writer.h @@ -27,11 +27,12 @@ struct WasmStream; enum WasmPrintChars; #define WASM_WRITE_BINARY_OPTIONS_DEFAULT \ - { NULL, WASM_TRUE, WASM_FALSE, WASM_FALSE } + { NULL, WASM_TRUE, WASM_FALSE, WASM_FALSE, WASM_FALSE } typedef struct WasmWriteBinaryOptions { struct WasmStream* log_stream; WasmBool canonicalize_lebs; + WasmBool relocatable; WasmBool write_debug_names; WasmBool is_invalid; } WasmWriteBinaryOptions; @@ -42,6 +43,9 @@ WasmResult wasm_write_binary_module(struct WasmAllocator*, const struct WasmModule*, const WasmWriteBinaryOptions*); +/* returns the length of the leb128 */ +uint32_t wasm_u32_leb128_length(uint32_t value); + void wasm_write_u32_leb128(struct WasmStream* stream, uint32_t value, const char* desc); diff --git a/src/common.c b/src/common.c index 0bd14686..df93af39 100644 --- a/src/common.c +++ b/src/common.c @@ -38,6 +38,12 @@ const char* g_wasm_kind_name[] = {"func", "table", "memory", "global"}; WASM_STATIC_ASSERT(WASM_ARRAY_SIZE(g_wasm_kind_name) == WASM_NUM_EXTERNAL_KINDS); +const char* g_wasm_reloc_type_name[] = { + "RELOC_FUNC_INDEX", "RELOC_FUNC_INDEX_SLEB", "RELOC_TABLE_INDEX", + "RELOC_GLOBAL_INDEX", "RELOC_GLOBAL_TYPE_INDEX", "RELOC_DATA"}; +WASM_STATIC_ASSERT(WASM_ARRAY_SIZE(g_wasm_reloc_type_name) == + WASM_NUM_RELOC_TYPES); + WasmBool wasm_is_naturally_aligned(WasmOpcode opcode, uint32_t alignment) { uint32_t opcode_align = wasm_get_opcode_memory_size(opcode); return alignment == WASM_USE_NATURAL_ALIGNMENT || alignment == opcode_align; diff --git a/src/common.h b/src/common.h index 230b16a1..27e3aef8 100644 --- a/src/common.h +++ b/src/common.h @@ -153,6 +153,16 @@ typedef enum WasmType { WASM_TYPE_ANY = 0, /* Not actually specified, but useful for type-checking */ } WasmType; +typedef enum WasmRelocType { + WASM_RELOC_FUNC_INDEX = 0, + WASM_RELOC_FUNC_INDEX_SLEB = 1, + WASM_RELOC_TABLE_INDEX = 2, + WASM_RELOC_GLOBAL_INDEX = 3, + WASM_RELOC_TYPE_INDEX = 4, + WASM_RELOC_DATA = 5, + WASM_NUM_RELOC_TYPES, +} WasmRelocType; + /* matches binary format, do not change */ typedef enum WasmExternalKind { WASM_EXTERNAL_KIND_FUNC = 0, @@ -456,6 +466,15 @@ static WASM_INLINE const char* wasm_get_kind_name(WasmExternalKind kind) { return g_wasm_kind_name[kind]; } +/* reloc */ + +extern const char* g_wasm_reloc_type_name[]; + +static WASM_INLINE const char* wasm_get_reloc_type_name(WasmRelocType reloc) { + assert(reloc < WASM_NUM_RELOC_TYPES); + return g_wasm_reloc_type_name[reloc]; +} + /* type */ static WASM_INLINE const char* wasm_get_type_name(WasmType type) { diff --git a/src/stream.c b/src/stream.c index e4288662..bf56b0bb 100644 --- a/src/stream.c +++ b/src/stream.c @@ -75,6 +75,7 @@ void wasm_write_data(WasmStream* stream, desc); stream->offset += size; } + void wasm_move_data(WasmStream* stream, size_t dst_offset, size_t src_offset, diff --git a/src/tools/wasm-link.c b/src/tools/wasm-link.c new file mode 100644 index 00000000..244be1eb --- /dev/null +++ b/src/tools/wasm-link.c @@ -0,0 +1,906 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * 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. + */ + +#include "allocator.h" +#include "binary-reader.h" +#include "binary-writer.h" +#include "option-parser.h" +#include "stream.h" +#include "vector.h" +#include "writer.h" + +#define PROGRAM_NAME "wasm-link" +#define NOPE WASM_OPTION_NO_ARGUMENT +#define YEP WASM_OPTION_HAS_ARGUMENT +#define RELOC_SIZE 5 +#define FIRST_KNOWN_SECTION WASM_BINARY_SECTION_TYPE + +enum { FLAG_VERBOSE, FLAG_OUTPUT, FLAG_HELP, NUM_FLAGS }; + +static const char s_description[] = + " link one or more wasm binary modules into a single binary module." + "\n" + " $ wasm-link m1.wasm m2.wasm -o out.wasm\n"; + +static WasmOption s_options[] = { + {FLAG_VERBOSE, 'v', "verbose", NULL, NOPE, + "use multiple times for more info"}, + {FLAG_OUTPUT, 'o', "output", "FILE", YEP, "output wasm binary file"}, + {FLAG_HELP, 'h', "help", NULL, NOPE, "print this help message"}, +}; +WASM_STATIC_ASSERT(NUM_FLAGS == WASM_ARRAY_SIZE(s_options)); + +typedef const char* String; +WASM_DEFINE_VECTOR(string, String); + +static WasmBool s_verbose; +static const char* s_outfile = "a.wasm"; +static StringVector s_infiles; +static WasmAllocator* s_allocator; +static WasmFileWriter s_log_stream_writer; +static WasmStream s_log_stream; + +static void on_option(struct WasmOptionParser* parser, + struct WasmOption* option, + const char* argument) { + switch (option->id) { + case FLAG_VERBOSE: + s_verbose++; + wasm_init_file_writer_existing(&s_log_stream_writer, stdout); + wasm_init_stream(&s_log_stream, &s_log_stream_writer.base, NULL); + break; + + case FLAG_OUTPUT: + s_outfile = argument; + break; + + case FLAG_HELP: + wasm_print_help(parser, PROGRAM_NAME); + exit(0); + break; + } +} + +static void on_argument(struct WasmOptionParser* parser, const char* argument) { + wasm_append_string_value(s_allocator, &s_infiles, &argument); +} + +static void on_option_error(struct WasmOptionParser* parser, + const char* message) { + WASM_FATAL("%s\n", message); +} + +static void parse_options(int argc, char** argv) { + WasmOptionParser parser; + WASM_ZERO_MEMORY(parser); + parser.description = s_description; + parser.options = s_options; + parser.num_options = WASM_ARRAY_SIZE(s_options); + parser.on_option = on_option; + parser.on_argument = on_argument; + parser.on_error = on_option_error; + wasm_parse_options(&parser, argc, argv); + + if (!s_infiles.size) { + wasm_print_help(&parser, PROGRAM_NAME); + WASM_FATAL("No inputs files specified.\n"); + } +} + +typedef struct DataSegment { + uint32_t memory_index; + uint32_t offset; + const uint8_t* data; + size_t size; +} DataSegment; +WASM_DEFINE_VECTOR(data_segment, DataSegment); + +typedef struct Reloc { + WasmRelocType type; + size_t offset; +} Reloc; +WASM_DEFINE_VECTOR(reloc, Reloc); + +struct InputBinary; + +typedef struct SectionDataCustom { + WasmBool linked; + /* Reference to string data stored in the containing InputBinary */ + WasmStringSlice name; +} SectionDataCustom; + +typedef struct Section { + struct InputBinary* binary; /* The binary to which this section belongs */ + RelocVector relocations; /* The relocations for this section */ + + WasmBinarySection section_code; + size_t size; + size_t offset; + + size_t payload_size; + size_t payload_offset; + + /* For known sections, the count of the number of elements in the section */ + uint32_t count; + + union { + /* CUSTOM section data */ + SectionDataCustom data_custom; + /* DATA section data */ + DataSegmentVector data_segments; + /* MEMORY section data */ + WasmLimits memory_limits; + }; + + /* The offset at which this section appears within the combined output + * section. */ + size_t output_payload_offset; +} Section; +WASM_DEFINE_VECTOR(section, Section); + +typedef Section* SectionPtr; +WASM_DEFINE_VECTOR(section_ptr, SectionPtr); + +typedef struct InputBinary { + uint8_t* data; + size_t size; + SectionVector sections; + Section* current_section; + + uint32_t type_index_offset; + uint32_t function_index_offset; + uint32_t imported_function_index_offset; + uint32_t global_index_offset; + uint32_t imported_global_index_offset; + uint32_t indirect_function_index_offset; + uint32_t memory_page_count; + uint32_t memory_page_offset; + + uint32_t num_func_imports; + uint32_t num_global_imports; + uint32_t table_elem_count; +} InputBinary; +WASM_DEFINE_VECTOR(binary, InputBinary); + +typedef struct Context { + WasmStream stream; + InputBinaryVector inputs; + uint32_t total_function_imports; + uint32_t total_global_imports; + ssize_t current_section_payload_offset; +} Context; + +void wasm_destroy_section(WasmAllocator* allocator, Section* section) { + wasm_destroy_reloc_vector(allocator, §ion->relocations); + switch (section->section_code) { + case WASM_BINARY_SECTION_DATA: + wasm_destroy_data_segment_vector(allocator, §ion->data_segments); + break; + default: + break; + } +} + +void wasm_destroy_binary(WasmAllocator* allocator, InputBinary* binary) { + WASM_DESTROY_VECTOR_AND_ELEMENTS(allocator, binary->sections, section); + wasm_free(allocator, binary->data); +} + +static WasmResult on_reloc(WasmRelocType type, + uint32_t section_index, + uint32_t offset, + void* user_data) { + InputBinary* binary = user_data; + if (section_index >= binary->sections.size) { + WASM_FATAL("invalid section index: %d\n", section_index); + } + Section* sec = &binary->sections.data[section_index - 1]; + if (offset + RELOC_SIZE > sec->size) { + WASM_FATAL("invalid relocation offset: %#x\n", offset); + } + + Reloc* reloc = wasm_append_reloc(s_allocator, &sec->relocations); + reloc->type = type; + reloc->offset = offset; + + return WASM_OK; +} + +static WasmResult on_import_func(uint32_t index, + uint32_t sig_index, + void* user_data) { + InputBinary* binary = user_data; + binary->num_func_imports++; + return WASM_OK; +} + +static WasmResult on_import_global(uint32_t index, + WasmType type, + WasmBool mutable, + void* user_data) { + InputBinary* binary = user_data; + binary->num_global_imports++; + return WASM_OK; +} + +static WasmResult begin_section(WasmBinaryReaderContext* ctx, + WasmBinarySection section_code, + uint32_t size) { + InputBinary* binary = ctx->user_data; + Section* sec = wasm_append_section(s_allocator, &binary->sections); + binary->current_section = sec; + sec->section_code = section_code; + sec->size = size; + sec->offset = ctx->offset; + sec->binary = binary; + + if (sec->section_code != WASM_BINARY_SECTION_CUSTOM && + sec->section_code != WASM_BINARY_SECTION_START) { + size_t bytes_read = wasm_read_u32_leb128( + &binary->data[sec->offset], &binary->data[binary->size], &sec->count); + if (bytes_read == 0) + WASM_FATAL("error reading section element count\n"); + sec->payload_offset = sec->offset + bytes_read; + sec->payload_size = sec->size - bytes_read; + } + return WASM_OK; +} + +static WasmResult begin_custom_section(WasmBinaryReaderContext* ctx, + uint32_t size, + WasmStringSlice section_name) { + InputBinary* binary = ctx->user_data; + Section* sec = binary->current_section; + sec->data_custom.name = section_name; + + /* Modify section size and offset to not include the name itself. */ + size_t delta = ctx->offset - sec->offset; + sec->offset = sec->offset + delta; + sec->size = sec->size - delta; + sec->payload_offset = sec->offset; + sec->payload_size = sec->size; + + /* Special handling for certain CUSTOM sections */ + if (wasm_string_slice_eq_cstr(§ion_name, "name")) { + size_t bytes_read = wasm_read_u32_leb128( + &binary->data[sec->offset], &binary->data[binary->size], &sec->count); + sec->payload_offset += bytes_read; + sec->payload_size -= bytes_read; + + /* We don't currently support merging name sections unless they contain + * a name for every function. */ + size_t i; + for (i = 0; i < binary->sections.size; i++) { + if (binary->sections.data[i].section_code == + WASM_BINARY_SECTION_FUNCTION) { + if (binary->sections.data[i].count != sec->count) { + WASM_FATAL( + "name section count (%d) does not match function count (%d)\n", + sec->count, binary->sections.data[i].count); + } + } + } + } + + return WASM_OK; +} + +static WasmResult on_table(uint32_t index, + WasmType elem_type, + const WasmLimits* elem_limits, + void* user_data) { + if (elem_limits->has_max && (elem_limits->max != elem_limits->initial)) + WASM_FATAL("Tables with max != initial not supported by wasm-link\n"); + + InputBinary* binary = user_data; + binary->table_elem_count = elem_limits->initial; + return WASM_OK; +} + +static WasmResult on_elem_segment_function_index_count( + WasmBinaryReaderContext* ctx, + uint32_t index, + uint32_t count) { + InputBinary* binary = ctx->user_data; + Section* sec = binary->current_section; + + /* Modify the payload to include only the actual function indexes */ + size_t delta = ctx->offset - sec->payload_offset; + sec->payload_offset += delta; + sec->payload_size -= delta; + return WASM_OK; +} + +static WasmResult on_memory(uint32_t index, + const WasmLimits* page_limits, + void* user_data) { + InputBinary* binary = user_data; + Section* sec = binary->current_section; + sec->memory_limits = *page_limits; + binary->memory_page_count = page_limits->initial; + return WASM_OK; +} + +static WasmResult begin_data_segment(uint32_t index, + uint32_t memory_index, + void* user_data) { + InputBinary* binary = user_data; + Section* sec = binary->current_section; + DataSegment* segment = + wasm_append_data_segment(s_allocator, &sec->data_segments); + segment->memory_index = memory_index; + return WASM_OK; +} + +static WasmResult on_init_expr_i32_const_expr(uint32_t index, + uint32_t value, + void* user_data) { + InputBinary* binary = user_data; + Section* sec = binary->current_section; + if (sec->section_code != WASM_BINARY_SECTION_DATA) + return WASM_OK; + DataSegment* segment = &sec->data_segments.data[sec->data_segments.size - 1]; + segment->offset = value; + return WASM_OK; +} + +static WasmResult on_data_segment_data(uint32_t index, + const void* src_data, + uint32_t size, + void* user_data) { + InputBinary* binary = user_data; + Section* sec = binary->current_section; + DataSegment* segment = &sec->data_segments.data[sec->data_segments.size - 1]; + segment->data = src_data; + segment->size = size; + return WASM_OK; +} + +static WasmResult read_binary(uint8_t* data, + size_t size, + InputBinary* input_info) { + input_info->data = data; + input_info->size = size; + + WasmBinaryReader reader; + WASM_ZERO_MEMORY(reader); + + static WasmBinaryReader s_binary_reader = { + .begin_section = begin_section, + .begin_custom_section = begin_custom_section, + + .on_reloc = on_reloc, + + .on_import_func = on_import_func, + .on_import_global = on_import_global, + + .on_table = on_table, + + .on_memory = on_memory, + + .begin_data_segment = begin_data_segment, + .on_init_expr_i32_const_expr = on_init_expr_i32_const_expr, + .on_data_segment_data = on_data_segment_data, + + .on_elem_segment_function_index_count = + on_elem_segment_function_index_count, + }; + + reader = s_binary_reader; + reader.user_data = input_info; + + WasmReadBinaryOptions read_options = WASM_READ_BINARY_OPTIONS_DEFAULT; + return wasm_read_binary(s_allocator, data, size, &reader, 1, &read_options); +} + +static void apply_relocation(Section* section, Reloc* r) { + InputBinary* binary = section->binary; + uint8_t* section_data = &binary->data[section->offset]; + size_t section_size = section->size; + + uint32_t cur_value = 0; + wasm_read_u32_leb128(section_data + r->offset, section_data + section_size, + &cur_value); + + uint32_t offset = 0; + switch (r->type) { + case WASM_RELOC_FUNC_INDEX: + if (cur_value >= binary->num_func_imports) { + offset = binary->function_index_offset; + } else { + offset = binary->imported_function_index_offset; + } + break; + case WASM_RELOC_GLOBAL_INDEX: + if (cur_value >= binary->num_global_imports) { + offset = binary->global_index_offset; + } else { + offset = binary->imported_global_index_offset; + } + break; + case WASM_RELOC_TYPE_INDEX: + offset = binary->type_index_offset; + break; + default: + WASM_FATAL("unhandled relocation type: %d\n", r->type); + break; + } + + cur_value += offset; + wasm_write_fixed_u32_leb128_raw(section_data + r->offset, + section_data + section_size, cur_value); +} + +static void apply_relocations(Section* section) { + if (!section->relocations.size) + return; + + /* Perform relocations in-place */ + size_t i; + for (i = 0; i < section->relocations.size; i++) { + Reloc* reloc = §ion->relocations.data[i]; + apply_relocation(section, reloc); + } +} + +static void write_section_payload(Context* ctx, Section* sec) { + assert(ctx->current_section_payload_offset != -1); + + sec->output_payload_offset = + ctx->stream.offset - ctx->current_section_payload_offset; + + uint8_t* payload = &sec->binary->data[sec->payload_offset]; + wasm_write_data(&ctx->stream, payload, sec->payload_size, "section content"); +} + +#define WRITE_UNKNOWN_SIZE \ + uint32_t fixup_offset = stream->offset; \ + wasm_write_fixed_u32_leb128(stream, 0, "unknown size"); \ + ctx->current_section_payload_offset = ctx->stream.offset; \ + uint32_t start = stream->offset; + +#define FIXUP_SIZE \ + wasm_write_fixed_u32_leb128_at(stream, fixup_offset, stream->offset - start, \ + "fixup size"); + +static void write_combined_custom_section(Context* ctx, + const SectionPtrVector* sections, + const WasmStringSlice* name) { + /* Write this section, along with all the following sections with the + * same name. */ + size_t i; + size_t total_size = 0; + size_t total_count = 0; + + /* Reloc sections are handled seperatedly. */ + if (wasm_string_slice_startswith(name, "reloc")) + return; + + if (!wasm_string_slice_eq_cstr(name, "name")) { + WASM_FATAL("Don't know how to link custom section: " PRIstringslice "\n", + WASM_PRINTF_STRING_SLICE_ARG(*name)); + } + + /* First pass to calculate total size and count */ + for (i = 0; i < sections->size; i++) { + Section* sec = sections->data[i]; + if (!wasm_string_slices_are_equal(name, &sec->data_custom.name)) + continue; + total_size += sec->payload_size; + total_count += sec->count; + } + + WasmStream* stream = &ctx->stream; + wasm_write_u8(stream, WASM_BINARY_SECTION_CUSTOM, "section code"); + WRITE_UNKNOWN_SIZE; + wasm_write_str(stream, name->start, name->length, WASM_PRINT_CHARS, + "custom section name"); + wasm_write_u32_leb128(stream, total_count, "element count"); + + for (i = 0; i < sections->size; i++) { + Section* sec = sections->data[i]; + if (!wasm_string_slices_are_equal(name, &sec->data_custom.name)) + continue; + apply_relocations(sec); + write_section_payload(ctx, sec); + sec->data_custom.linked = WASM_TRUE; + } + + FIXUP_SIZE; +} + +static void write_combined_table_section(Context* ctx, + const SectionPtrVector* sections) { + /* Total section size includes the element count leb128 which is + * always 1 in the current spec */ + uint32_t table_count = 1; + uint32_t flags = WASM_BINARY_LIMITS_HAS_MAX_FLAG; + uint32_t elem_count = 0; + + size_t i; + for (i = 0; i < sections->size; i++) { + Section* sec = sections->data[i]; + elem_count += sec->binary->table_elem_count; + } + + WasmStream* stream = &ctx->stream; + WRITE_UNKNOWN_SIZE; + wasm_write_u32_leb128(stream, table_count, "table count"); + wasm_write_type(stream, WASM_TYPE_ANYFUNC); + wasm_write_u32_leb128(stream, flags, "table elem flags"); + wasm_write_u32_leb128(stream, elem_count, "table initial length"); + wasm_write_u32_leb128(stream, elem_count, "table max length"); + FIXUP_SIZE; +} + +static void write_combined_elem_section(Context* ctx, + const SectionPtrVector* sections) { + WasmStream* stream = &ctx->stream; + WRITE_UNKNOWN_SIZE; + + size_t i; + uint32_t total_elem_count = 0; + for (i = 0; i < sections->size; i++) { + Section* sec = sections->data[i]; + total_elem_count += sec->binary->table_elem_count; + } + + wasm_write_u32_leb128(stream, 1, "segment count"); + wasm_write_u32_leb128(stream, 0, "table index"); + wasm_write_opcode(&ctx->stream, WASM_OPCODE_I32_CONST); + wasm_write_i32_leb128(&ctx->stream, 0, "elem init literal"); + wasm_write_opcode(&ctx->stream, WASM_OPCODE_END); + wasm_write_u32_leb128(stream, total_elem_count, "num elements"); + + ctx->current_section_payload_offset = stream->offset; + + for (i = 0; i < sections->size; i++) { + Section* sec = sections->data[i]; + apply_relocations(sec); + write_section_payload(ctx, sec); + } + + FIXUP_SIZE; +} + +static void write_combined_memory_section(Context* ctx, + const SectionPtrVector* sections) { + WasmStream* stream = &ctx->stream; + WRITE_UNKNOWN_SIZE; + + wasm_write_u32_leb128(stream, 1, "memory count"); + + WasmLimits limits; + WASM_ZERO_MEMORY(limits); + limits.has_max = WASM_TRUE; + size_t i; + for (i = 0; i < sections->size; i++) { + SectionPtr sec = sections->data[i]; + limits.initial += sec->memory_limits.initial; + } + limits.max = limits.initial; + wasm_write_limits(stream, &limits); + + FIXUP_SIZE; +} + +static void write_combined_function_section(Context* ctx, + const SectionPtrVector* sections, + uint32_t total_count) { + WasmStream* stream = &ctx->stream; + WRITE_UNKNOWN_SIZE; + + wasm_write_u32_leb128(stream, total_count, "function count"); + + size_t i; + for (i = 0; i < sections->size; i++) { + SectionPtr sec = sections->data[i]; + uint32_t count = sec->count; + uint32_t input_offset = 0; + uint32_t sig_index = 0; + const uint8_t* start = &sec->binary->data[sec->payload_offset]; + const uint8_t* end = + &sec->binary->data[sec->payload_offset + sec->payload_size]; + while (count--) { + input_offset += + wasm_read_u32_leb128(start + input_offset, end, &sig_index); + sig_index += sec->binary->type_index_offset; + wasm_write_u32_leb128(stream, sig_index, "sig"); + } + } + + FIXUP_SIZE; +} + +static void write_data_segment(WasmStream* stream, + DataSegment* segment, + uint32_t offset) { + assert(segment->memory_index == 0); + wasm_write_u32_leb128(stream, segment->memory_index, "memory index"); + wasm_write_opcode(stream, WASM_OPCODE_I32_CONST); + wasm_write_u32_leb128(stream, segment->offset + offset, "offset"); + wasm_write_opcode(stream, WASM_OPCODE_END); + wasm_write_u32_leb128(stream, segment->size, "segment size"); + wasm_write_data(stream, segment->data, segment->size, "segment data"); +} + +static void write_combined_data_section(Context* ctx, + SectionPtrVector* sections, + uint32_t total_count) { + WasmStream* stream = &ctx->stream; + WRITE_UNKNOWN_SIZE; + + wasm_write_u32_leb128(stream, total_count, "data segment count"); + size_t i, j; + for (i = 0; i < sections->size; i++) { + Section* sec = sections->data[i]; + for (j = 0; j < sec->data_segments.size; j++) { + DataSegment* segment = &sec->data_segments.data[j]; + write_data_segment(stream, segment, + sec->binary->memory_page_offset * WASM_PAGE_SIZE); + } + } + + FIXUP_SIZE; +} + +static void write_combined_reloc_section(Context* ctx, + WasmBinarySection section_code, + SectionPtrVector* sections, + uint32_t section_index) { + size_t i, j; + uint32_t total_relocs = 0; + + /* First pass to know total reloc count */ + for (i = 0; i < sections->size; i++) { + Section* sec = sections->data[i]; + total_relocs += sec->relocations.size; + } + + if (!total_relocs) + return; + + char section_name[128]; + wasm_snprintf(section_name, sizeof(section_name), "%s.%s", + WASM_BINARY_SECTION_RELOC, wasm_get_section_name(section_code)); + + WasmStream* stream = &ctx->stream; + wasm_write_u8(stream, WASM_BINARY_SECTION_CUSTOM, "section code"); + WRITE_UNKNOWN_SIZE; + wasm_write_str(stream, section_name, strlen(section_name), WASM_PRINT_CHARS, + "reloc section name"); + wasm_write_u32_leb128(&ctx->stream, section_index, "reloc section"); + wasm_write_u32_leb128(&ctx->stream, total_relocs, "num relocs"); + + for (i = 0; i < sections->size; i++) { + Section* sec = sections->data[i]; + RelocVector* relocs = &sec->relocations; + for (j = 0; j < relocs->size; j++) { + wasm_write_u32_leb128(&ctx->stream, relocs->data[j].type, "reloc type"); + uint32_t new_offset = relocs->data[j].offset + sec->output_payload_offset; + wasm_write_u32_leb128(&ctx->stream, new_offset, "reloc offset"); + } + } + + FIXUP_SIZE; +} + +static WasmBool write_combined_section(Context* ctx, + WasmBinarySection section_code, + SectionPtrVector* sections) { + if (!sections->size) + return WASM_FALSE; + + if (section_code == WASM_BINARY_SECTION_START && sections->size > 1) { + WASM_FATAL("Don't know how to combine sections of type: %s\n", + wasm_get_section_name(section_code)); + } + + size_t i; + uint32_t total_count = 0; + uint32_t total_size = 0; + + /* Sum section size and element count */ + for (i = 0; i < sections->size; i++) { + Section* sec = sections->data[i]; + total_size += sec->payload_size; + total_count += sec->count; + } + + wasm_write_u8(&ctx->stream, section_code, "section code"); + ctx->current_section_payload_offset = -1; + + switch (section_code) { + case WASM_BINARY_SECTION_FUNCTION: + write_combined_function_section(ctx, sections, total_count); + break; + case WASM_BINARY_SECTION_TABLE: + write_combined_table_section(ctx, sections); + break; + case WASM_BINARY_SECTION_ELEM: + write_combined_elem_section(ctx, sections); + break; + case WASM_BINARY_SECTION_MEMORY: + write_combined_memory_section(ctx, sections); + break; + case WASM_BINARY_SECTION_DATA: + write_combined_data_section(ctx, sections, total_count); + break; + default: { + /* Total section size includes the element count leb128. */ + total_size += wasm_u32_leb128_length(total_count); + + /* Write section to stream */ + WasmStream* stream = &ctx->stream; + wasm_write_u32_leb128(stream, total_size, "section size"); + wasm_write_u32_leb128(stream, total_count, "element count"); + ctx->current_section_payload_offset = ctx->stream.offset; + for (i = 0; i < sections->size; i++) { + Section* sec = sections->data[i]; + apply_relocations(sec); + write_section_payload(ctx, sec); + } + } + } + + return WASM_TRUE; +} + +static void write_binary(Context* ctx) { + /* Find all the sections of each type */ + SectionPtrVector sections[WASM_NUM_BINARY_SECTIONS]; + WASM_ZERO_MEMORY(sections); + uint32_t section_indices[WASM_NUM_BINARY_SECTIONS]; + WASM_ZERO_MEMORY(section_indices); + uint32_t section_index = 1; + + size_t i; + size_t j; + uint32_t memory_page_offset = 0; + for (j = 0; j < ctx->inputs.size; j++) { + InputBinary* binary = &ctx->inputs.data[j]; + /* The imported_function_index_offset is the sum of all the function + * imports from objects that precede this one. i.e. the current running + * total */ + binary->imported_function_index_offset = ctx->total_function_imports; + binary->imported_global_index_offset = ctx->total_global_imports; + binary->memory_page_offset = memory_page_offset; + memory_page_offset += binary->memory_page_count; + ctx->total_function_imports += binary->num_func_imports; + ctx->total_global_imports += binary->num_global_imports; + for (i = 0; i < binary->sections.size; i++) { + Section* s = &binary->sections.data[i]; + SectionPtrVector* sec_list = §ions[s->section_code]; + wasm_append_section_ptr_value(s_allocator, sec_list, &s); + } + } + + /* Calculate offsets needed for relocation */ + uint32_t total_count = 0; + for (i = 0; i < sections[WASM_BINARY_SECTION_TYPE].size; i++) { + Section* sec = sections[WASM_BINARY_SECTION_TYPE].data[i]; + sec->binary->type_index_offset = total_count; + total_count += sec->count; + } + + total_count = 0; + for (i = 0; i < sections[WASM_BINARY_SECTION_GLOBAL].size; i++) { + Section* sec = sections[WASM_BINARY_SECTION_GLOBAL].data[i]; + sec->binary->global_index_offset = ctx->total_global_imports - + sec->binary->num_global_imports + + total_count; + total_count += sec->count; + } + + total_count = 0; + for (i = 0; i < sections[WASM_BINARY_SECTION_FUNCTION].size; i++) { + Section* sec = sections[WASM_BINARY_SECTION_FUNCTION].data[i]; + sec->binary->function_index_offset = ctx->total_function_imports - + sec->binary->num_func_imports + + total_count; + total_count += sec->count; + } + + /* Write the final binary */ + wasm_write_u32(&ctx->stream, WASM_BINARY_MAGIC, "WASM_BINARY_MAGIC"); + wasm_write_u32(&ctx->stream, WASM_BINARY_VERSION, "WASM_BINARY_VERSION"); + + /* Write known sections first */ + for (i = FIRST_KNOWN_SECTION; i < WASM_NUM_BINARY_SECTIONS; i++) { + if (write_combined_section(ctx, i, §ions[i])) + section_indices[i] = section_index++; + } + + /* Write custom sections */ + SectionPtrVector* custom_sections = §ions[WASM_BINARY_SECTION_CUSTOM]; + for (i = 0; i < custom_sections->size; i++) { + Section* section = custom_sections->data[i]; + if (section->data_custom.linked) + continue; + write_combined_custom_section(ctx, custom_sections, + §ion->data_custom.name); + } + + /* Generate a new set of reloction sections */ + for (i = FIRST_KNOWN_SECTION; i < WASM_NUM_BINARY_SECTIONS; i++) { + write_combined_reloc_section(ctx, i, §ions[i], section_indices[i]); + } + + for (i = 0; i < WASM_NUM_BINARY_SECTIONS; i++) { + wasm_destroy_section_ptr_vector(s_allocator, §ions[i]); + } +} + +static WasmResult perform_link(Context* ctx) { + WasmMemoryWriter writer; + WASM_ZERO_MEMORY(writer); + if (WASM_FAILED(wasm_init_mem_writer(s_allocator, &writer))) + WASM_FATAL("unable to open memory writer for writing\n"); + + WasmStream* log_stream = NULL; + if (s_verbose) + log_stream = &s_log_stream; + + if (s_verbose) + wasm_writef(&s_log_stream, "writing file: %s\n", s_outfile); + + wasm_init_stream(&ctx->stream, &writer.base, log_stream); + write_binary(ctx); + + if (WASM_FAILED(wasm_write_output_buffer_to_file(&writer.buf, s_outfile))) + WASM_FATAL("error writing linked output to file\n"); + + wasm_close_mem_writer(&writer); + return WASM_OK; +} + +int main(int argc, char** argv) { + s_allocator = &g_wasm_libc_allocator; + wasm_init_stdio(); + parse_options(argc, argv); + + Context context; + WASM_ZERO_MEMORY(context); + + WasmResult result = WASM_OK; + size_t i; + for (i = 0; i < s_infiles.size; i++) { + const char* input_filename = s_infiles.data[i]; + if (s_verbose) + wasm_writef(&s_log_stream, "reading file: %s\n", input_filename); + void* data; + size_t size; + result = wasm_read_file(s_allocator, input_filename, &data, &size); + if (WASM_FAILED(result)) + return result; + InputBinary* b = wasm_append_binary(s_allocator, &context.inputs); + result = read_binary(data, size, b); + if (WASM_FAILED(result)) + WASM_FATAL("error parsing file: %s\n", input_filename); + } + + result = perform_link(&context); + if (WASM_FAILED(result)) + return result; + + /* Cleanup */ + WASM_DESTROY_VECTOR_AND_ELEMENTS(s_allocator, context.inputs, binary); + wasm_destroy_string_vector(s_allocator, &s_infiles); + + wasm_print_allocator_stats(s_allocator); + wasm_destroy_allocator(s_allocator); + return result; +} diff --git a/src/tools/wast2wasm.c b/src/tools/wast2wasm.c index 14b7fcfb..6a2e60bf 100644 --- a/src/tools/wast2wasm.c +++ b/src/tools/wast2wasm.c @@ -61,6 +61,7 @@ enum { FLAG_HELP, FLAG_DUMP_MODULE, FLAG_OUTPUT, + FLAG_RELOCATABLE, FLAG_SPEC, FLAG_USE_LIBC_ALLOCATOR, FLAG_NO_CANONICALIZE_LEB128S, @@ -95,8 +96,9 @@ static WasmOption s_options[] = { {FLAG_HELP, 'h', "help", NULL, NOPE, "print this help message"}, {FLAG_DUMP_MODULE, 'd', "dump-module", NULL, NOPE, "print a hexdump of the module to stdout"}, - {FLAG_OUTPUT, 'o', "output", "FILE", YEP, - "output file for the generated binary format"}, + {FLAG_OUTPUT, 'o', "output", "FILE", YEP, "output wasm binary file"}, + {FLAG_RELOCATABLE, 'r', NULL, NULL, NOPE, + "create a relocatable wasm binary (suitable for linking with wasm-link)"}, {FLAG_SPEC, 0, "spec", NULL, NOPE, "parse a file with multiple modules and assertions, like the spec " "tests"}, @@ -136,6 +138,10 @@ static void on_option(struct WasmOptionParser* parser, s_outfile = argument; break; + case FLAG_RELOCATABLE: + s_write_binary_options.relocatable = WASM_TRUE; + break; + case FLAG_SPEC: s_spec = WASM_TRUE; break; @@ -231,7 +237,7 @@ int main(int argc, char** argv) { WasmAstLexer* lexer = wasm_new_ast_file_lexer(allocator, s_infile); if (!lexer) - WASM_FATAL("unable to read %s\n", s_infile); + WASM_FATAL("unable to read file: %s\n", s_infile); WasmScript script; WasmResult result = wasm_parse_ast(lexer, &script, &s_error_handler); |