summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Clegg <sbc@chromium.org>2017-01-11 06:21:22 -0800
committerGitHub <noreply@github.com>2017-01-11 06:21:22 -0800
commitd3c6c973d9c5fe13ad1066d5de47470a41b54943 (patch)
treeccb73b371844c45a5f6b93efcec10c2cd797ecc4
parent6f4df52f245c2aee9d9d071065d38e200bc2bac5 (diff)
downloadwabt-d3c6c973d9c5fe13ad1066d5de47470a41b54943.tar.gz
wabt-d3c6c973d9c5fe13ad1066d5de47470a41b54943.tar.bz2
wabt-d3c6c973d9c5fe13ad1066d5de47470a41b54943.zip
Add support for linkable modules (#228)
This change adds support for writing wasm modules such that they are compatible with the proposed linking spec: https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md Basically this means that wast2wasm will generate and extra 'reloc' sections and will pad any LEBs in the code section that might require relocation. This mode is activated by passing the -r flag to wast2wasm. Also, start work on wasm-link which should be able to link such modules together.
-rw-r--r--CMakeLists.txt5
-rw-r--r--Makefile2
-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
-rw-r--r--test/find_exe.py6
-rw-r--r--test/help/sexpr-wasm.txt3
-rw-r--r--test/link/data.txt34
-rw-r--r--test/link/export.txt55
-rw-r--r--test/link/function_calls.txt65
-rw-r--r--test/link/function_calls_incremental.txt86
-rw-r--r--test/link/globals.txt73
-rw-r--r--test/link/incremental.txt113
-rw-r--r--test/link/names.txt74
-rw-r--r--test/link/table.txt58
-rwxr-xr-xtest/run-tests.py9
-rwxr-xr-xtest/run-wasm-link.py111
-rwxr-xr-xtest/run-wasmdump.py2
25 files changed, 1810 insertions, 31 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 93b82aaa..61e5d0d6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -253,6 +253,11 @@ if (NOT EMSCRIPTEN)
add_dependencies(everything wasmdump)
target_link_libraries(wasmdump libwasm)
+ # wasm-link
+ add_executable(wasm-link src/tools/wasm-link.c)
+ add_dependencies(everything wasm-link)
+ target_link_libraries(wasm-link libwasm)
+
# wasm-interp
add_executable(wasm-interp src/tools/wasm-interp.c)
add_dependencies(everything wasm-interp)
diff --git a/Makefile b/Makefile
index 1278d735..9b67450d 100644
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,7 @@ BUILD_TYPES := DEBUG RELEASE
SANITIZERS := ASAN MSAN LSAN UBSAN
CONFIGS := NORMAL $(SANITIZERS) NO_RE2C_BISON NO_TESTS
EXECUTABLES := wast2wasm wasm2wast wasm-interp wasmopcodecnt hexfloat_test \
- wasmdump wast-desugar
+ wasmdump wast-desugar wasm-link
# directory names
GCC_DIR := gcc/
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);
diff --git a/test/find_exe.py b/test/find_exe.py
index 53d23ba9..3ee34c8f 100644
--- a/test/find_exe.py
+++ b/test/find_exe.py
@@ -25,7 +25,7 @@ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
REPO_ROOT_DIR = os.path.dirname(SCRIPT_DIR)
EXECUTABLES = [
'wast2wasm', 'wasm2wast', 'wasmdump', 'wasm-interp', 'wasmopcodecnt',
- 'wast-desugar'
+ 'wast-desugar', 'wasm-link'
]
@@ -70,6 +70,10 @@ def GetWasmdumpExecutable(override=None):
return FindExecutable('wasmdump', override)
+def GetWasmlinkExecutable(override=None):
+ return FindExecutable('wasm-link', override)
+
+
def GetWasmInterpExecutable(override=None):
return FindExecutable('wasm-interp', override)
diff --git a/test/help/sexpr-wasm.txt b/test/help/sexpr-wasm.txt
index 91bdd61d..eea625a2 100644
--- a/test/help/sexpr-wasm.txt
+++ b/test/help/sexpr-wasm.txt
@@ -25,7 +25,8 @@ options:
-v, --verbose use multiple times for more info
-h, --help print this help message
-d, --dump-module print a hexdump of the module to stdout
- -o, --output=FILE output file for the generated binary format
+ -o, --output=FILE output wasm binary file
+ -r, create a relocatable wasm binary (suitable for linking with wasm-link)
--spec parse a file with multiple modules and assertions, like the spec tests
--use-libc-allocator use malloc, free, etc. instead of stack allocator
--no-canonicalize-leb128s Write all LEB128 sizes as 5-bytes instead of their minimal size
diff --git a/test/link/data.txt b/test/link/data.txt
new file mode 100644
index 00000000..f9580f71
--- /dev/null
+++ b/test/link/data.txt
@@ -0,0 +1,34 @@
+;;; TOOL: run-wasm-link
+;;; FLAGS:
+(module
+ (memory 1 1)
+ (data (i32.const 0) "foo")
+ (data (i32.const 10) "bar")
+)
+(module
+ (memory 1 1)
+ (data (i32.const 0) "hello")
+ (data (i32.const 100) "world")
+)
+(;; STDOUT ;;;
+linked.wasm: file format wasm 0x00000d
+
+Sections:
+ MEMORY start=0x0000000e end=0x00000012 (size=0x00000004) count: 1
+ DATA start=0x00000018 end=0x00000041 (size=0x00000029) count: 4
+
+Section Details:
+MEMORY:
+ - memory[0] pages: initial=2 max=2
+DATA:
+ - memory[0] - init i32=0
+ - 0000000: 666f 6f foo
+ - memory[0] - init i32=10
+ - 0000000: 6261 72 bar
+ - memory[0] - init i32=65536
+ - 0000000: 6865 6c6c 6f hello
+ - memory[0] - init i32=65636
+ - 0000000: 776f 726c 64 world
+
+Code Disassembly:
+;;; STDOUT ;;)
diff --git a/test/link/export.txt b/test/link/export.txt
new file mode 100644
index 00000000..c4f80006
--- /dev/null
+++ b/test/link/export.txt
@@ -0,0 +1,55 @@
+;;; TOOL: run-wasm-link
+;;; FLAGS:
+(module
+ (export "foo" (func 0))
+ (func (param i32)
+ i32.const 1
+ call 0)
+)
+(module
+ (export "bar" (func 0))
+ (func (param i32)
+ i32.const 2
+ call 0)
+)
+(;; STDOUT ;;;
+linked.wasm: file format wasm 0x00000d
+
+Sections:
+ TYPE start=0x0000000a end=0x00000013 (size=0x00000009) count: 2
+ FUNCTION start=0x00000019 end=0x0000001c (size=0x00000003) count: 2
+ EXPORT start=0x0000001e end=0x00000033 (size=0x00000015) count: 2
+ CODE start=0x00000035 end=0x0000004c (size=0x00000017) count: 2
+ CUSTOM start=0x00000052 end=0x00000065 (size=0x00000013) "reloc.EXPORT"
+ CUSTOM start=0x0000006b end=0x0000007c (size=0x00000011) "reloc.CODE"
+
+Section Details:
+TYPE:
+ - [0] (i32) -> nil
+ - [1] (i32) -> nil
+FUNCTION:
+ - func[0] sig=0
+ - func[1] sig=1
+EXPORT:
+ - func[0] foo
+ - func[1] bar
+CODE:
+ - func 0
+ - func 1
+CUSTOM:
+ - name: "reloc.EXPORT"
+ - RELOC_FUNC_INDEX section=3 offset=0x6
+ - RELOC_FUNC_INDEX section=3 offset=0x10
+CUSTOM:
+ - name: "reloc.CODE"
+ - RELOC_FUNC_INDEX section=4 offset=0x6
+ - RELOC_FUNC_INDEX section=4 offset=0x11
+
+Code Disassembly:
+func 0
+ 000038: 41 01 | i32.const 0x1
+ 00003a: 10 80 80 80 80 00 | call 0
+func 1
+ 000043: 41 02 | i32.const 0x2
+ 000045: 10 81 80 80 80 00 | call 0x1
+;;; STDOUT ;;)
diff --git a/test/link/function_calls.txt b/test/link/function_calls.txt
new file mode 100644
index 00000000..379e83db
--- /dev/null
+++ b/test/link/function_calls.txt
@@ -0,0 +1,65 @@
+;;; TOOL: run-wasm-link
+;;; FLAGS:
+(module
+ (import "foo" "bar" (func (param i32) (result i32)))
+ (func (param i32)
+ get_local 0
+ call 0
+ call 1)
+)
+(module
+ (import "does" "nothing" (func (param f64)))
+ (func (param i64)
+ f64.const 1
+ call 0
+ i64.const 10
+ call 1)
+)
+(;; STDOUT ;;;
+linked.wasm: file format wasm 0x00000d
+
+Sections:
+ TYPE start=0x0000000a end=0x0000001c (size=0x00000012) count: 4
+ IMPORT start=0x0000001e end=0x00000040 (size=0x00000022) count: 2
+ FUNCTION start=0x00000046 end=0x00000049 (size=0x00000003) count: 2
+ CODE start=0x0000004b end=0x00000077 (size=0x0000002c) count: 2
+ CUSTOM start=0x0000007d end=0x00000090 (size=0x00000013) "reloc.IMPORT"
+ CUSTOM start=0x00000096 end=0x000000ab (size=0x00000015) "reloc.CODE"
+
+Section Details:
+TYPE:
+ - [0] (i32) -> i32
+ - [1] (i32) -> nil
+ - [2] (f64) -> nil
+ - [3] (i64) -> nil
+IMPORT:
+ - func[0] sig=0 <- foo.bar
+ - func[1] sig=2 <- does.nothing
+FUNCTION:
+ - func[2] sig=1
+ - func[3] sig=3
+CODE:
+ - func 0
+ - func 1
+CUSTOM:
+ - name: "reloc.IMPORT"
+ - RELOC_GLOBAL_TYPE_INDEX section=2 offset=0xa
+ - RELOC_GLOBAL_TYPE_INDEX section=2 offset=0x1d
+CUSTOM:
+ - name: "reloc.CODE"
+ - RELOC_FUNC_INDEX section=4 offset=0x6
+ - RELOC_FUNC_INDEX section=4 offset=0xc
+ - RELOC_FUNC_INDEX section=4 offset=0x1e
+ - RELOC_FUNC_INDEX section=4 offset=0x26
+
+Code Disassembly:
+func 0
+ 00004e: 20 00 | get_local 0
+ 000050: 10 80 80 80 80 00 | call 0
+ 000056: 10 82 80 80 80 00 | call 0x2
+func 1
+ 00005f: 44 00 00 00 00 00 00 f0 3f | f64.const 0x1p+0
+ 000068: 10 81 80 80 80 00 | call 0x1
+ 00006e: 42 0a | i64.const 10
+ 000070: 10 83 80 80 80 00 | call 0x3
+;;; STDOUT ;;)
diff --git a/test/link/function_calls_incremental.txt b/test/link/function_calls_incremental.txt
new file mode 100644
index 00000000..434c1184
--- /dev/null
+++ b/test/link/function_calls_incremental.txt
@@ -0,0 +1,86 @@
+;;; TOOL: run-wasm-link
+;;; FLAGS: --incremental
+(module
+ (import "foo" "bar" (func (param i32) (result i32)))
+ (func (param i32)
+ get_local 0
+ call 0
+ call 1)
+)
+(module
+ (import "does" "nothing" (func (param f64)))
+ (func (param i64)
+ f64.const 1
+ call 0
+ i64.const 10
+ call 1)
+)
+(module
+ (import "hello" "world" (func (param f32)))
+ (func (param i32)
+ f32.const 1
+ call 0
+ i32.const 10
+ call 1)
+)
+(;; STDOUT ;;;
+linked.wasm: file format wasm 0x00000d
+
+Sections:
+ TYPE start=0x0000000a end=0x00000024 (size=0x0000001a) count: 6
+ IMPORT start=0x00000026 end=0x0000005a (size=0x00000034) count: 3
+ FUNCTION start=0x00000060 end=0x00000064 (size=0x00000004) count: 3
+ CODE start=0x00000066 end=0x000000a8 (size=0x00000042) count: 3
+ CUSTOM start=0x000000ae end=0x000000c3 (size=0x00000015) "reloc.IMPORT"
+ CUSTOM start=0x000000c9 end=0x000000e2 (size=0x00000019) "reloc.CODE"
+
+Section Details:
+TYPE:
+ - [0] (i32) -> i32
+ - [1] (i32) -> nil
+ - [2] (f64) -> nil
+ - [3] (i64) -> nil
+ - [4] (f32) -> nil
+ - [5] (i32) -> nil
+IMPORT:
+ - func[0] sig=0 <- foo.bar
+ - func[1] sig=2 <- does.nothing
+ - func[2] sig=4 <- hello.world
+FUNCTION:
+ - func[3] sig=1
+ - func[4] sig=3
+ - func[5] sig=5
+CODE:
+ - func 0
+ - func 1
+ - func 2
+CUSTOM:
+ - name: "reloc.IMPORT"
+ - RELOC_GLOBAL_TYPE_INDEX section=2 offset=0xa
+ - RELOC_GLOBAL_TYPE_INDEX section=2 offset=0x1d
+ - RELOC_GLOBAL_TYPE_INDEX section=2 offset=0x2f
+CUSTOM:
+ - name: "reloc.CODE"
+ - RELOC_FUNC_INDEX section=4 offset=0x6
+ - RELOC_FUNC_INDEX section=4 offset=0xc
+ - RELOC_FUNC_INDEX section=4 offset=0x1e
+ - RELOC_FUNC_INDEX section=4 offset=0x26
+ - RELOC_FUNC_INDEX section=4 offset=0x34
+ - RELOC_FUNC_INDEX section=4 offset=0x3c
+
+Code Disassembly:
+func 0
+ 000069: 20 00 | get_local 0
+ 00006b: 10 80 80 80 80 00 | call 0
+ 000071: 10 83 80 80 80 00 | call 0x3
+func 1
+ 00007a: 44 00 00 00 00 00 00 f0 3f | f64.const 0x1p+0
+ 000083: 10 81 80 80 80 00 | call 0x1
+ 000089: 42 0a | i64.const 10
+ 00008b: 10 84 80 80 80 00 | call 0x4
+func 2
+ 000094: 43 00 00 80 3f | f32.const 0x1p+0
+ 000099: 10 82 80 80 80 00 | call 0x2
+ 00009f: 41 0a | i32.const 0xa
+ 0000a1: 10 85 80 80 80 00 | call 0x5
+;;; STDOUT ;;)
diff --git a/test/link/globals.txt b/test/link/globals.txt
new file mode 100644
index 00000000..b5da3feb
--- /dev/null
+++ b/test/link/globals.txt
@@ -0,0 +1,73 @@
+;;; TOOL: run-wasm-link
+;;; FLAGS:
+(module
+ (import "foo" "bar" (global i32))
+ (global i32 (i32.const 1))
+ (global i32 (i32.const 2))
+ (func (param i32)
+ get_global 2
+ get_global 1
+ i32.add
+ get_global 0
+ i32.add
+ call 0)
+)
+(module
+ (import "a" "b" (global i64))
+ (global i64 (i64.const 2))
+ (func (param i64) (param i64)
+ get_global 0
+ get_global 1
+ call 0)
+)
+(;; STDOUT ;;;
+linked.wasm: file format wasm 0x00000d
+
+Sections:
+ TYPE start=0x0000000a end=0x00000014 (size=0x0000000a) count: 2
+ IMPORT start=0x00000016 end=0x00000029 (size=0x00000013) count: 2
+ FUNCTION start=0x0000002f end=0x00000032 (size=0x00000003) count: 2
+ GLOBAL start=0x00000034 end=0x00000044 (size=0x00000010) count: 3
+ CODE start=0x00000046 end=0x00000079 (size=0x00000033) count: 2
+ CUSTOM start=0x0000007f end=0x0000009a (size=0x0000001b) "reloc.CODE"
+
+Section Details:
+TYPE:
+ - [0] (i32) -> nil
+ - [1] (i64, i64) -> nil
+IMPORT:
+ - global[0] i32 mutable=0 <- foo.bar
+ - global[1] i64 mutable=0 <- a.b
+FUNCTION:
+ - func[0] sig=0
+ - func[1] sig=1
+GLOBAL:
+ - global[2] i32 mutable=0 - init i32=1
+ - global[3] i32 mutable=0 - init i32=2
+ - global[4] i64 mutable=0 - init i64=2
+CODE:
+ - func 0
+ - func 1
+CUSTOM:
+ - name: "reloc.CODE"
+ - RELOC_GLOBAL_INDEX section=5 offset=0x4
+ - RELOC_GLOBAL_INDEX section=5 offset=0xa
+ - RELOC_GLOBAL_INDEX section=5 offset=0x11
+ - RELOC_FUNC_INDEX section=5 offset=0x18
+ - RELOC_GLOBAL_INDEX section=5 offset=0x21
+ - RELOC_GLOBAL_INDEX section=5 offset=0x27
+ - RELOC_FUNC_INDEX section=5 offset=0x2d
+
+Code Disassembly:
+func 0
+ 000049: 23 83 80 80 80 00 | get_global 0x3
+ 00004f: 23 82 80 80 80 00 | get_global 0x2
+ 000055: 6a | i32.add
+ 000056: 23 80 80 80 80 00 | get_global 0
+ 00005c: 6a | i32.add
+ 00005d: 10 80 80 80 80 00 | call 0
+func 1
+ 000066: 23 81 80 80 80 00 | get_global 0x1
+ 00006c: 23 84 80 80 80 00 | get_global 0x4
+ 000072: 10 81 80 80 80 00 | call 0x1
+;;; STDOUT ;;)
diff --git a/test/link/incremental.txt b/test/link/incremental.txt
new file mode 100644
index 00000000..309f6c77
--- /dev/null
+++ b/test/link/incremental.txt
@@ -0,0 +1,113 @@
+;;; TOOL: run-wasm-link
+;;; FLAGS: --incremental
+(module
+ (import "foo" "bar" (func (param i32) (result i32)))
+ (func $func1 (param i32)
+ get_local 0
+ call 0
+ call 1)
+ (table anyfunc (elem $func1 $func1 $func1))
+)
+(module
+ (import "does" "nothing" (func (param f64)))
+ (func $func2 (param i64)
+ f64.const 1
+ call 0
+ i64.const 10
+ call 1)
+ (table anyfunc (elem $func2 $func2))
+)
+(module
+ (import "hello" "world" (func (param f32)))
+ (func $func3 (param i32)
+ f32.const 1
+ call 0
+ i32.const 10
+ call 1)
+ (table anyfunc (elem $func3 $func3))
+)
+(;; STDOUT ;;;
+linked.wasm: file format wasm 0x00000d
+
+Sections:
+ TYPE start=0x0000000a end=0x00000024 (size=0x0000001a) count: 6
+ IMPORT start=0x00000026 end=0x0000005a (size=0x00000034) count: 3
+ FUNCTION start=0x00000060 end=0x00000064 (size=0x00000004) count: 3
+ TABLE start=0x0000006a end=0x0000006f (size=0x00000005) count: 1
+ ELEM start=0x00000075 end=0x0000009e (size=0x00000029) count: 1
+ CODE start=0x000000a0 end=0x000000e2 (size=0x00000042) count: 3
+ CUSTOM start=0x000000e8 end=0x000000fd (size=0x00000015) "reloc.IMPORT"
+ CUSTOM start=0x00000103 end=0x0000011e (size=0x0000001b) "reloc.ELEM"
+ CUSTOM start=0x00000124 end=0x0000013d (size=0x00000019) "reloc.CODE"
+
+Section Details:
+TYPE:
+ - [0] (i32) -> i32
+ - [1] (i32) -> nil
+ - [2] (f64) -> nil
+ - [3] (i64) -> nil
+ - [4] (f32) -> nil
+ - [5] (i32) -> nil
+IMPORT:
+ - func[0] sig=0 <- foo.bar
+ - func[1] sig=2 <- does.nothing
+ - func[2] sig=4 <- hello.world
+FUNCTION:
+ - func[3] sig=1
+ - func[4] sig=3
+ - func[5] sig=5
+TABLE:
+ - table[0] type=anyfunc initial=7 max=7
+ELEM:
+ - segment[0] table=0
+ - init i32=0
+ - func[3]
+ - func[3]
+ - func[3]
+ - func[4]
+ - func[4]
+ - func[5]
+ - func[5]
+CODE:
+ - func 0
+ - func 1
+ - func 2
+CUSTOM:
+ - name: "reloc.IMPORT"
+ - RELOC_GLOBAL_TYPE_INDEX section=2 offset=0xa
+ - RELOC_GLOBAL_TYPE_INDEX section=2 offset=0x1d
+ - RELOC_GLOBAL_TYPE_INDEX section=2 offset=0x2f
+CUSTOM:
+ - name: "reloc.ELEM"
+ - RELOC_FUNC_INDEX section=5 offset=0x6
+ - RELOC_FUNC_INDEX section=5 offset=0xb
+ - RELOC_FUNC_INDEX section=5 offset=0x10
+ - RELOC_FUNC_INDEX section=5 offset=0x15
+ - RELOC_FUNC_INDEX section=5 offset=0x1a
+ - RELOC_FUNC_INDEX section=5 offset=0x1f
+ - RELOC_FUNC_INDEX section=5 offset=0x24
+CUSTOM:
+ - name: "reloc.CODE"
+ - RELOC_FUNC_INDEX section=6 offset=0x6
+ - RELOC_FUNC_INDEX section=6 offset=0xc
+ - RELOC_FUNC_INDEX section=6 offset=0x1e
+ - RELOC_FUNC_INDEX section=6 offset=0x26
+ - RELOC_FUNC_INDEX section=6 offset=0x34
+ - RELOC_FUNC_INDEX section=6 offset=0x3c
+
+Code Disassembly:
+func 0
+ 0000a3: 20 00 | get_local 0
+ 0000a5: 10 80 80 80 80 00 | call 0
+ 0000ab: 10 83 80 80 80 00 | call 0x3
+func 1
+ 0000b4: 44 00 00 00 00 00 00 f0 3f | f64.const 0x1p+0
+ 0000bd: 10 81 80 80 80 00 | call 0x1
+ 0000c3: 42 0a | i64.const 10
+ 0000c5: 10 84 80 80 80 00 | call 0x4
+func 2
+ 0000ce: 43 00 00 80 3f | f32.const 0x1p+0
+ 0000d3: 10 82 80 80 80 00 | call 0x2
+ 0000d9: 41 0a | i32.const 0xa
+ 0000db: 10 85 80 80 80 00 | call 0x5
+;;; STDOUT ;;)
diff --git a/test/link/names.txt b/test/link/names.txt
new file mode 100644
index 00000000..527d2502
--- /dev/null
+++ b/test/link/names.txt
@@ -0,0 +1,74 @@
+;;; TOOL: run-wasm-link
+;;; FLAGS: --debug-names
+(module
+ (export "foo" (func 0))
+ (func $name1 (param $param1 i32)
+ i32.const 1
+ call 0)
+ (func $name2 (param $param2 i64)
+ i64.const 1
+ call 1)
+)
+(module
+ (export "bar" (func 0))
+ (func $name3 (param $param3 i32)
+ i32.const 2
+ call 0)
+)
+(;; STDOUT ;;;
+linked.wasm: file format wasm 0x00000d
+
+Sections:
+ TYPE start=0x0000000a end=0x00000017 (size=0x0000000d) count: 3
+ FUNCTION start=0x0000001d end=0x00000021 (size=0x00000004) count: 3
+ EXPORT start=0x00000023 end=0x00000038 (size=0x00000015) count: 2
+ CODE start=0x0000003a end=0x0000005c (size=0x00000022) count: 3
+ CUSTOM start=0x00000062 end=0x00000098 (size=0x00000036) "name"
+ CUSTOM start=0x0000009e end=0x000000b1 (size=0x00000013) "reloc.EXPORT"
+ CUSTOM start=0x000000b7 end=0x000000ca (size=0x00000013) "reloc.CODE"
+
+Section Details:
+TYPE:
+ - [0] (i32) -> nil
+ - [1] (i64) -> nil
+ - [2] (i32) -> nil
+FUNCTION:
+ - func[0] sig=0
+ - func[1] sig=1
+ - func[2] sig=2
+EXPORT:
+ - func[0] foo
+ - func[2] bar
+CODE:
+ - func 0
+ - func 1
+ - func 2
+CUSTOM:
+ - name: "name"
+ - func[0] $name1
+ - local[0] $param1
+ - func[1] $name2
+ - local[0] $param2
+ - func[2] $name3
+ - local[0] $param3
+CUSTOM:
+ - name: "reloc.EXPORT"
+ - RELOC_FUNC_INDEX section=3 offset=0x6
+ - RELOC_FUNC_INDEX section=3 offset=0x10
+CUSTOM:
+ - name: "reloc.CODE"
+ - RELOC_FUNC_INDEX section=4 offset=0x6
+ - RELOC_FUNC_INDEX section=4 offset=0x11
+ - RELOC_FUNC_INDEX section=4 offset=0x1c
+
+Code Disassembly:
+func 0
+ 00003d: 41 01 | i32.const 0x1
+ 00003f: 10 80 80 80 80 00 | call 0
+func 1
+ 000048: 42 01 | i64.const 1
+ 00004a: 10 81 80 80 80 00 | call 0x1
+func 2
+ 000053: 41 02 | i32.const 0x2
+ 000055: 10 82 80 80 80 00 | call 0x2
+;;; STDOUT ;;)
diff --git a/test/link/table.txt b/test/link/table.txt
new file mode 100644
index 00000000..2ba4f80b
--- /dev/null
+++ b/test/link/table.txt
@@ -0,0 +1,58 @@
+;;; TOOL: run-wasm-link
+;;; FLAGS:
+(module
+ (func $func1 (param i32))
+ (table anyfunc (elem $func1 $func1))
+)
+(module
+ (func $func2 (param i64))
+ (func $func3 (param f64))
+ (table anyfunc (elem $func3 $func2 $func2))
+)
+(;; STDOUT ;;;
+linked.wasm: file format wasm 0x00000d
+
+Sections:
+ TYPE start=0x0000000a end=0x00000017 (size=0x0000000d) count: 3
+ FUNCTION start=0x0000001d end=0x00000021 (size=0x00000004) count: 3
+ TABLE start=0x00000027 end=0x0000002c (size=0x00000005) count: 1
+ ELEM start=0x00000032 end=0x00000051 (size=0x0000001f) count: 1
+ CODE start=0x00000053 end=0x0000005d (size=0x0000000a) count: 3
+ CUSTOM start=0x00000063 end=0x0000007a (size=0x00000017) "reloc.ELEM"
+
+Section Details:
+TYPE:
+ - [0] (i32) -> nil
+ - [1] (i64) -> nil
+ - [2] (f64) -> nil
+FUNCTION:
+ - func[0] sig=0
+ - func[1] sig=1
+ - func[2] sig=2
+TABLE:
+ - table[0] type=anyfunc initial=5 max=5
+ELEM:
+ - segment[0] table=0
+ - init i32=0
+ - func[0]
+ - func[0]
+ - func[2]
+ - func[1]
+ - func[1]
+CODE:
+ - func 0
+ - func 1
+ - func 2
+CUSTOM:
+ - name: "reloc.ELEM"
+ - RELOC_FUNC_INDEX section=4 offset=0x6
+ - RELOC_FUNC_INDEX section=4 offset=0xb
+ - RELOC_FUNC_INDEX section=4 offset=0x10
+ - RELOC_FUNC_INDEX section=4 offset=0x15
+ - RELOC_FUNC_INDEX section=4 offset=0x1a
+
+Code Disassembly:
+func 0
+func 1
+func 2
+;;; STDOUT ;;)
diff --git a/test/run-tests.py b/test/run-tests.py
index 5ad7afd6..13d15857 100755
--- a/test/run-tests.py
+++ b/test/run-tests.py
@@ -64,6 +64,15 @@ TOOLS = {
]),
'VERBOSE-FLAGS': ['-v']
},
+ 'run-wasm-link': {
+ 'EXE': 'test/run-wasm-link.py',
+ 'FLAGS': ' '.join([
+ '--wast2wasm=%(wast2wasm)s',
+ '--wasm-link=%(wasm-link)s',
+ '--wasmdump=%(wasmdump)s',
+ ]),
+ 'VERBOSE-FLAGS': ['-v']
+ },
'run-roundtrip': {
'EXE': 'test/run-roundtrip.py',
'FLAGS': ' '.join([
diff --git a/test/run-wasm-link.py b/test/run-wasm-link.py
new file mode 100755
index 00000000..135ecacd
--- /dev/null
+++ b/test/run-wasm-link.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+import argparse
+import os
+import sys
+import tempfile
+
+import find_exe
+import utils
+
+
+def main(args):
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-v', '--verbose', help='print more diagnotic messages.',
+ action='store_true')
+ parser.add_argument('-o', '--out-dir', metavar='PATH',
+ help='output directory for files.')
+ parser.add_argument('--wast2wasm', metavar='PATH',
+ help='set the wast2wasm executable to use.')
+ parser.add_argument('--wasm-link', metavar='PATH',
+ help='set the wasm-link executable to use.')
+ parser.add_argument('--wasmdump', metavar='PATH',
+ help='set the wasmdump executable to use.')
+ parser.add_argument('--no-error-cmdline',
+ help='don\'t display the subprocess\'s commandline when' +
+ ' an error occurs', dest='error_cmdline',
+ action='store_false')
+ parser.add_argument('-p', '--print-cmd', help='print the commands that are run.',
+ action='store_true')
+ parser.add_argument('--incremental', help='incremenatly link one object at' +
+ ' a time to produce the final linked binary.',
+ action='store_true')
+ parser.add_argument('--debug-names', action='store_true')
+ parser.add_argument('--use-libc-allocator', action='store_true')
+ parser.add_argument('file', help='test file.')
+ options = parser.parse_args(args)
+
+ wast2wasm = utils.Executable(
+ find_exe.GetWast2WasmExecutable(options.wast2wasm),
+ error_cmdline=options.error_cmdline)
+ wast2wasm.AppendOptionalArgs({
+ '--debug-names': options.debug_names,
+ '--use-libc-allocator': options.use_libc_allocator,
+ '-v': options.verbose,
+ })
+
+ wasm_link = utils.Executable(
+ find_exe.GetWasmlinkExecutable(options.wasm_link),
+ error_cmdline=options.error_cmdline)
+ wasm_link.AppendOptionalArgs({
+ '-v': options.verbose,
+ })
+
+ wasmdump = utils.Executable(
+ find_exe.GetWasmdumpExecutable(options.wasmdump),
+ error_cmdline=options.error_cmdline)
+ wasmdump.AppendOptionalArgs({
+ })
+
+ wast2wasm.verbose = options.print_cmd
+ wasm_link.verbose = options.print_cmd
+ wasmdump.verbose = options.print_cmd
+
+ filename = options.file
+
+ with utils.TempDirectory(options.out_dir, 'wasm-link-') as out_dir:
+ basename = os.path.basename(filename)
+ basename_noext = os.path.splitext(basename)[0]
+ out_file = os.path.join(out_dir, basename_noext + '.json')
+ wast2wasm.RunWithArgs('--spec', '-r', '-o', out_file, filename)
+
+ wasm_files = utils.GetModuleFilenamesFromSpecJSON(out_file)
+ wasm_files = [utils.ChangeDir(f, out_dir) for f in wasm_files]
+
+ output = os.path.join(out_dir, 'linked.wasm')
+ if options.incremental:
+ partialy_linked = output + '.partial'
+ for i, f in enumerate(wasm_files):
+ if i == 0:
+ wasm_link.RunWithArgs('-o', output, f)
+ else:
+ os.rename(output, partialy_linked)
+ wasm_link.RunWithArgs('-o', output, partialy_linked, f)
+ #wasmdump.RunWithArgs('-d', '-h', output)
+ wasmdump.RunWithArgs('-d', '-v', '-h', output)
+ else:
+ wasm_link.RunWithArgs('-o', output, *wasm_files)
+ wasmdump.RunWithArgs('-d', '-h', '-v', output)
+
+
+if __name__ == '__main__':
+ try:
+ sys.exit(main(sys.argv[1:]))
+ except utils.Error as e:
+ sys.stderr.write(str(e) + '\n')
+ sys.exit(1)
diff --git a/test/run-wasmdump.py b/test/run-wasmdump.py
index e33230e4..9b14b0df 100755
--- a/test/run-wasmdump.py
+++ b/test/run-wasmdump.py
@@ -42,6 +42,7 @@ def main(args):
action='store_true')
parser.add_argument('--headers', action='store_true')
parser.add_argument('--no-check', action='store_true')
+ parser.add_argument('-c', '--compile-only', action='store_true')
parser.add_argument('--dump-verbose', action='store_true')
parser.add_argument('--spec', action='store_true')
parser.add_argument('--no-canonicalize-leb128s', action='store_true')
@@ -59,6 +60,7 @@ def main(args):
'--no-canonicalize-leb128s': options.no_canonicalize_leb128s,
'--spec': options.spec,
'-v': options.verbose,
+ '-c': options.compile_only,
'--use-libc-allocator': options.use_libc_allocator
})