summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/binary-reader-objdump.c11
-rw-r--r--src/binary-reader.c21
-rw-r--r--src/binary-reader.h10
-rw-r--r--src/binary-writer.c153
-rw-r--r--src/binary-writer.h6
-rw-r--r--src/common.c6
-rw-r--r--src/common.h19
-rw-r--r--src/stream.c1
-rw-r--r--src/tools/wasm-link.c906
-rw-r--r--src/tools/wast2wasm.c12
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, &section, "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, &section->relocations);
+ switch (section->section_code) {
+ case WASM_BINARY_SECTION_DATA:
+ wasm_destroy_data_segment_vector(allocator, &section->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(&section_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 = &section->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 = &sections[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, &sections[i]))
+ section_indices[i] = section_index++;
+ }
+
+ /* Write custom sections */
+ SectionPtrVector* custom_sections = &sections[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,
+ &section->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, &sections[i], section_indices[i]);
+ }
+
+ for (i = 0; i < WASM_NUM_BINARY_SECTIONS; i++) {
+ wasm_destroy_section_ptr_vector(s_allocator, &sections[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);