summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
})