| /* |
| * 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 "wasm-link.h" |
| |
| #include "binary-reader.h" |
| #include "binding-hash.h" |
| #include "binary-writer.h" |
| #include "option-parser.h" |
| #include "stream.h" |
| #include "writer.h" |
| #include "binary-reader-linker.h" |
| |
| #include <memory> |
| #include <vector> |
| |
| #define PROGRAM_NAME "wasm-link" |
| #define NOPE HasArgument::No |
| #define YEP HasArgument::Yes |
| #define FIRST_KNOWN_SECTION static_cast<size_t>(BinarySection::Type) |
| |
| using namespace wabt; |
| using namespace wabt::link; |
| |
| enum { FLAG_DEBUG, FLAG_OUTPUT, FLAG_RELOCATABLE, 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 Option s_options[] = { |
| {FLAG_DEBUG, '\0', "debug", nullptr, NOPE, |
| "log extra information when reading and writing wasm files"}, |
| {FLAG_OUTPUT, 'o', "output", "FILE", YEP, "output wasm binary file"}, |
| {FLAG_RELOCATABLE, 'r', "relocatable", nullptr, NOPE, |
| "output a relocatable object file"}, |
| {FLAG_HELP, 'h', "help", nullptr, NOPE, "print this help message"}, |
| }; |
| WABT_STATIC_ASSERT(NUM_FLAGS == WABT_ARRAY_SIZE(s_options)); |
| |
| static bool s_debug; |
| static bool s_relocatable; |
| static const char* s_outfile = "a.wasm"; |
| static std::vector<std::string> s_infiles; |
| static std::unique_ptr<FileStream> s_log_stream; |
| |
| struct Context { |
| WABT_DISALLOW_COPY_AND_ASSIGN(Context); |
| Context() {} |
| |
| MemoryStream stream; |
| std::vector<std::unique_ptr<LinkerInputBinary>> inputs; |
| ssize_t current_section_payload_offset = 0; |
| }; |
| |
| static void on_option(struct OptionParser* parser, |
| struct Option* option, |
| const char* argument) { |
| switch (option->id) { |
| case FLAG_DEBUG: |
| s_debug = true; |
| s_log_stream = FileStream::CreateStdout(); |
| break; |
| |
| case FLAG_OUTPUT: |
| s_outfile = argument; |
| break; |
| |
| case FLAG_RELOCATABLE: |
| s_relocatable = true; |
| break; |
| |
| case FLAG_HELP: |
| print_help(parser, PROGRAM_NAME); |
| exit(0); |
| break; |
| } |
| } |
| |
| static void on_argument(struct OptionParser* parser, const char* argument) { |
| s_infiles.emplace_back(argument); |
| } |
| |
| static void on_option_error(struct OptionParser* parser, const char* message) { |
| WABT_FATAL("%s\n", message); |
| } |
| |
| static void parse_options(int argc, char** argv) { |
| OptionParser parser; |
| WABT_ZERO_MEMORY(parser); |
| parser.description = s_description; |
| parser.options = s_options; |
| parser.num_options = WABT_ARRAY_SIZE(s_options); |
| parser.on_option = on_option; |
| parser.on_argument = on_argument; |
| parser.on_error = on_option_error; |
| parse_options(&parser, argc, argv); |
| |
| if (!s_infiles.size()) { |
| print_help(&parser, PROGRAM_NAME); |
| WABT_FATAL("No inputs files specified.\n"); |
| } |
| } |
| |
| Section::Section() |
| : binary(nullptr), |
| section_code(BinarySection::Invalid), |
| size(0), |
| offset(0), |
| payload_size(0), |
| payload_offset(0), |
| count(0), |
| output_payload_offset(0) { |
| WABT_ZERO_MEMORY(data); |
| } |
| |
| Section::~Section() { |
| if (section_code == BinarySection::Data) { |
| delete data.data_segments; |
| } |
| } |
| |
| LinkerInputBinary::LinkerInputBinary(const char* filename, |
| uint8_t* data, |
| size_t size) |
| : filename(filename), |
| data(data), |
| size(size), |
| active_function_imports(0), |
| active_global_imports(0), |
| type_index_offset(0), |
| function_index_offset(0), |
| imported_function_index_offset(0), |
| table_index_offset(0), |
| memory_page_count(0), |
| memory_page_offset(0), |
| table_elem_count(0) {} |
| |
| LinkerInputBinary::~LinkerInputBinary() { |
| delete[] data; |
| } |
| |
| static uint32_t relocate_func_index(LinkerInputBinary* binary, |
| uint32_t function_index) { |
| uint32_t offset; |
| if (function_index >= binary->function_imports.size()) { |
| /* locally declared function call */ |
| offset = binary->function_index_offset; |
| if (s_debug) |
| s_log_stream->Writef("func reloc %d + %d\n", function_index, offset); |
| } else { |
| /* imported function call */ |
| FunctionImport* import = &binary->function_imports[function_index]; |
| if (!import->active) { |
| function_index = import->foreign_index; |
| offset = import->foreign_binary->function_index_offset; |
| if (s_debug) |
| s_log_stream->Writef("reloc for disabled import. new index = %d + %d\n", |
| function_index, offset); |
| } else { |
| uint32_t new_index = import->relocated_function_index; |
| if (s_debug) |
| s_log_stream->Writef( |
| "reloc for active import. old index = %d, new index = %d\n", |
| function_index, new_index); |
| return new_index; |
| } |
| } |
| return function_index + offset; |
| } |
| |
| static uint32_t relocate_global_index(LinkerInputBinary* binary, |
| uint32_t global_index) { |
| uint32_t offset; |
| if (global_index >= binary->global_imports.size()) { |
| offset = binary->global_index_offset; |
| } else { |
| offset = binary->imported_global_index_offset; |
| } |
| return global_index + offset; |
| } |
| |
| static void apply_relocation(Section* section, Reloc* r) { |
| LinkerInputBinary* binary = section->binary; |
| uint8_t* section_data = &binary->data[section->offset]; |
| size_t section_size = section->size; |
| |
| uint32_t cur_value = 0, new_value = 0; |
| read_u32_leb128(section_data + r->offset, section_data + section_size, |
| &cur_value); |
| |
| uint32_t offset = 0; |
| switch (r->type) { |
| case RelocType::FuncIndexLEB: |
| new_value = relocate_func_index(binary, cur_value); |
| break; |
| case RelocType::TableIndexSLEB: |
| printf("%s: table index reloc: %d offset=%d\n", binary->filename, |
| cur_value, binary->table_index_offset); |
| offset = binary->table_index_offset; |
| new_value = cur_value + offset; |
| break; |
| case RelocType::GlobalIndexLEB: |
| new_value = relocate_global_index(binary, cur_value); |
| break; |
| default: |
| WABT_FATAL("unhandled relocation type: %s\n", |
| get_reloc_type_name(r->type)); |
| break; |
| } |
| |
| write_fixed_u32_leb128_raw(section_data + r->offset, |
| section_data + section_size, new_value); |
| } |
| |
| static void apply_relocations(Section* section) { |
| if (!section->relocations.size()) |
| return; |
| |
| if (s_debug) { |
| s_log_stream->Writef("apply_relocations: %s\n", |
| get_section_name(section->section_code)); |
| } |
| |
| /* Perform relocations in-place */ |
| for (Reloc& reloc: section->relocations) { |
| 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]; |
| ctx->stream.WriteData(payload, sec->payload_size, "section content"); |
| } |
| |
| static void write_c_str(Stream* stream, const char* str, const char* desc) { |
| write_str(stream, str, strlen(str), desc, PrintChars::Yes); |
| } |
| |
| static void write_slice(Stream* stream, StringSlice str, const char* desc) { |
| write_str(stream, str.start, str.length, desc, PrintChars::Yes); |
| } |
| |
| static void write_string(Stream* stream, |
| const std::string& str, |
| const char* desc) { |
| write_str(stream, str.data(), str.length(), desc, PrintChars::Yes); |
| } |
| |
| #define WRITE_UNKNOWN_SIZE(STREAM) \ |
| { \ |
| uint32_t fixup_offset = (STREAM)->offset(); \ |
| write_fixed_u32_leb128(STREAM, 0, "unknown size"); \ |
| ctx->current_section_payload_offset = (STREAM)->offset(); \ |
| uint32_t start = (STREAM)->offset(); |
| |
| #define FIXUP_SIZE(STREAM) \ |
| write_fixed_u32_leb128_at(STREAM, fixup_offset, (STREAM)->offset() - start, \ |
| "fixup size"); \ |
| } |
| |
| static void write_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 = WABT_BINARY_LIMITS_HAS_MAX_FLAG; |
| uint32_t elem_count = 0; |
| |
| for (Section* section: sections) { |
| elem_count += section->binary->table_elem_count; |
| } |
| |
| Stream* stream = &ctx->stream; |
| WRITE_UNKNOWN_SIZE(stream); |
| write_u32_leb128(stream, table_count, "table count"); |
| write_type(stream, Type::Anyfunc); |
| write_u32_leb128(stream, flags, "table elem flags"); |
| write_u32_leb128(stream, elem_count, "table initial length"); |
| write_u32_leb128(stream, elem_count, "table max length"); |
| FIXUP_SIZE(stream); |
| } |
| |
| static void write_export_section(Context* ctx) { |
| uint32_t total_exports = 0; |
| for (const std::unique_ptr<LinkerInputBinary>& binary: ctx->inputs) { |
| total_exports += binary->exports.size(); |
| } |
| |
| Stream* stream = &ctx->stream; |
| WRITE_UNKNOWN_SIZE(stream); |
| write_u32_leb128(stream, total_exports, "export count"); |
| |
| for (const std::unique_ptr<LinkerInputBinary>& binary : ctx->inputs) { |
| for (const Export& export_ : binary->exports) { |
| write_slice(stream, export_.name, "export name"); |
| stream->WriteU8Enum(export_.kind, "export kind"); |
| uint32_t index = export_.index; |
| switch (export_.kind) { |
| case ExternalKind::Func: |
| index = relocate_func_index(binary.get(), index); |
| break; |
| default: |
| WABT_FATAL("unsupport export type: %d\n", |
| static_cast<int>(export_.kind)); |
| break; |
| } |
| write_u32_leb128(stream, index, "export index"); |
| } |
| } |
| FIXUP_SIZE(stream); |
| } |
| |
| static void write_elem_section(Context* ctx, |
| const SectionPtrVector& sections) { |
| Stream* stream = &ctx->stream; |
| WRITE_UNKNOWN_SIZE(stream); |
| |
| uint32_t total_elem_count = 0; |
| for (Section* section : sections) { |
| total_elem_count += section->binary->table_elem_count; |
| } |
| |
| write_u32_leb128(stream, 1, "segment count"); |
| write_u32_leb128(stream, 0, "table index"); |
| write_opcode(&ctx->stream, Opcode::I32Const); |
| write_i32_leb128(&ctx->stream, 0, "elem init literal"); |
| write_opcode(&ctx->stream, Opcode::End); |
| write_u32_leb128(stream, total_elem_count, "num elements"); |
| |
| ctx->current_section_payload_offset = stream->offset(); |
| |
| for (Section* section : sections) { |
| apply_relocations(section); |
| write_section_payload(ctx, section); |
| } |
| |
| FIXUP_SIZE(stream); |
| } |
| |
| static void write_memory_section(Context* ctx, |
| const SectionPtrVector& sections) { |
| Stream* stream = &ctx->stream; |
| WRITE_UNKNOWN_SIZE(stream); |
| |
| write_u32_leb128(stream, 1, "memory count"); |
| |
| Limits limits; |
| WABT_ZERO_MEMORY(limits); |
| limits.has_max = true; |
| for (size_t i = 0; i < sections.size(); i++) { |
| Section* sec = sections[i]; |
| limits.initial += sec->data.memory_limits.initial; |
| } |
| limits.max = limits.initial; |
| write_limits(stream, &limits); |
| |
| FIXUP_SIZE(stream); |
| } |
| |
| static void write_function_import(Context* ctx, |
| FunctionImport* import, |
| uint32_t offset) { |
| write_c_str(&ctx->stream, WABT_LINK_MODULE_NAME, "import module name"); |
| write_slice(&ctx->stream, import->name, "import field name"); |
| ctx->stream.WriteU8Enum(ExternalKind::Func, "import kind"); |
| write_u32_leb128(&ctx->stream, import->sig_index + offset, |
| "import signature index"); |
| } |
| |
| static void write_global_import(Context* ctx, GlobalImport* import) { |
| write_c_str(&ctx->stream, WABT_LINK_MODULE_NAME, "import module name"); |
| write_slice(&ctx->stream, import->name, "import field name"); |
| ctx->stream.WriteU8Enum(ExternalKind::Global, "import kind"); |
| write_type(&ctx->stream, import->type); |
| ctx->stream.WriteU8(import->mutable_, "global mutability"); |
| } |
| |
| static void write_import_section(Context* ctx) { |
| uint32_t num_imports = 0; |
| for (size_t i = 0; i < ctx->inputs.size(); i++) { |
| LinkerInputBinary* binary = ctx->inputs[i].get(); |
| std::vector<FunctionImport>& imports = binary->function_imports; |
| for (size_t j = 0; j < imports.size(); j++) { |
| FunctionImport* import = &imports[j]; |
| if (import->active) |
| num_imports++; |
| } |
| num_imports += binary->global_imports.size(); |
| } |
| |
| WRITE_UNKNOWN_SIZE(&ctx->stream); |
| write_u32_leb128(&ctx->stream, num_imports, "num imports"); |
| |
| for (size_t i = 0; i < ctx->inputs.size(); i++) { |
| LinkerInputBinary* binary = ctx->inputs[i].get(); |
| std::vector<FunctionImport>& imports = binary->function_imports; |
| for (size_t j = 0; j < imports.size(); j++) { |
| FunctionImport* import = &imports[j]; |
| if (import->active) |
| write_function_import(ctx, import, binary->type_index_offset); |
| } |
| |
| std::vector<GlobalImport>& globals = binary->global_imports; |
| for (size_t j = 0; j < globals.size(); j++) { |
| write_global_import(ctx, &globals[j]); |
| } |
| } |
| |
| FIXUP_SIZE(&ctx->stream); |
| } |
| |
| static void write_function_section(Context* ctx, |
| const SectionPtrVector& sections, |
| uint32_t total_count) { |
| Stream* stream = &ctx->stream; |
| WRITE_UNKNOWN_SIZE(stream); |
| |
| write_u32_leb128(stream, total_count, "function count"); |
| |
| for (size_t i = 0; i < sections.size(); i++) { |
| Section* sec = sections[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 += read_u32_leb128(start + input_offset, end, &sig_index); |
| sig_index += sec->binary->type_index_offset; |
| write_u32_leb128(stream, sig_index, "sig"); |
| } |
| } |
| |
| FIXUP_SIZE(stream); |
| } |
| |
| static void write_data_segment(Stream* stream, |
| const DataSegment& segment, |
| uint32_t offset) { |
| assert(segment.memory_index == 0); |
| write_u32_leb128(stream, segment.memory_index, "memory index"); |
| write_opcode(stream, Opcode::I32Const); |
| write_u32_leb128(stream, segment.offset + offset, "offset"); |
| write_opcode(stream, Opcode::End); |
| write_u32_leb128(stream, segment.size, "segment size"); |
| stream->WriteData(segment.data, segment.size, "segment data"); |
| } |
| |
| static void write_data_section(Context* ctx, |
| const SectionPtrVector& sections, |
| uint32_t total_count) { |
| Stream* stream = &ctx->stream; |
| WRITE_UNKNOWN_SIZE(stream); |
| |
| write_u32_leb128(stream, total_count, "data segment count"); |
| for (size_t i = 0; i < sections.size(); i++) { |
| Section* sec = sections[i]; |
| for (size_t j = 0; j < sec->data.data_segments->size(); j++) { |
| const DataSegment& segment = (*sec->data.data_segments)[j]; |
| write_data_segment(stream, segment, |
| sec->binary->memory_page_offset * WABT_PAGE_SIZE); |
| } |
| } |
| |
| FIXUP_SIZE(stream); |
| } |
| |
| static void write_names_section(Context* ctx) { |
| uint32_t total_count = 0; |
| for (const std::unique_ptr<LinkerInputBinary>& binary: ctx->inputs) { |
| for (size_t i = 0; i < binary->debug_names.size(); i++) { |
| if (binary->debug_names[i].empty()) |
| continue; |
| if (i < binary->function_imports.size()) { |
| if (!binary->function_imports[i].active) |
| continue; |
| } |
| total_count++; |
| } |
| } |
| |
| if (!total_count) |
| return; |
| |
| Stream* stream = &ctx->stream; |
| stream->WriteU8Enum(BinarySection::Custom, "section code"); |
| WRITE_UNKNOWN_SIZE(stream); |
| write_c_str(stream, "name", "custom section name"); |
| |
| stream->WriteU8Enum(NameSectionSubsection::Function, "subsection code"); |
| WRITE_UNKNOWN_SIZE(stream); |
| write_u32_leb128(stream, total_count, "element count"); |
| for (const std::unique_ptr<LinkerInputBinary>& binary: ctx->inputs) { |
| for (size_t i = 0; i < binary->debug_names.size(); i++) { |
| if (i < binary->function_imports.size()) { |
| if (!binary->function_imports[i].active) { |
| continue; |
| } |
| } |
| |
| if (binary->debug_names[i].empty()) |
| continue; |
| write_u32_leb128(stream, relocate_func_index(&*binary, i), |
| "function index"); |
| write_string(stream, binary->debug_names[i], "function name"); |
| } |
| } |
| |
| FIXUP_SIZE(stream); |
| |
| FIXUP_SIZE(stream); |
| } |
| |
| static void write_reloc_section(Context* ctx, |
| BinarySection section_code, |
| const SectionPtrVector& sections) { |
| uint32_t total_relocs = 0; |
| |
| /* First pass to know total reloc count */ |
| for (Section* sec: sections) |
| total_relocs += sec->relocations.size(); |
| |
| if (!total_relocs) |
| return; |
| |
| char section_name[128]; |
| snprintf(section_name, sizeof(section_name), "%s.%s", |
| WABT_BINARY_SECTION_RELOC, get_section_name(section_code)); |
| |
| Stream* stream = &ctx->stream; |
| stream->WriteU8Enum(BinarySection::Custom, "section code"); |
| WRITE_UNKNOWN_SIZE(stream); |
| write_c_str(stream, section_name, "reloc section name"); |
| write_u32_leb128_enum(&ctx->stream, section_code, "reloc section"); |
| write_u32_leb128(&ctx->stream, total_relocs, "num relocs"); |
| |
| for (Section* sec: sections) { |
| for (const Reloc& reloc: sec->relocations) { |
| write_u32_leb128_enum(&ctx->stream, reloc.type, "reloc type"); |
| uint32_t new_offset = reloc.offset + sec->output_payload_offset; |
| write_u32_leb128(&ctx->stream, new_offset, "reloc offset"); |
| uint32_t relocated_index; |
| switch (reloc.type) { |
| case RelocType::FuncIndexLEB: |
| relocated_index = relocate_func_index(sec->binary, reloc.index); |
| break; |
| case RelocType::GlobalIndexLEB: |
| relocated_index = relocate_global_index(sec->binary, reloc.index); |
| break; |
| // TODO(sbc): Handle other relocation types. |
| default: |
| WABT_FATAL("Unhandled reloc type: %s\n", get_reloc_type_name(reloc.type)); |
| break; |
| } |
| write_u32_leb128(&ctx->stream, relocated_index, "reloc index"); |
| } |
| } |
| |
| FIXUP_SIZE(stream); |
| } |
| |
| static bool write_combined_section(Context* ctx, |
| BinarySection section_code, |
| const SectionPtrVector& sections) { |
| if (!sections.size()) |
| return false; |
| |
| if (section_code == BinarySection::Start && sections.size() > 1) { |
| WABT_FATAL("Don't know how to combine sections of type: %s\n", |
| get_section_name(section_code)); |
| } |
| |
| uint32_t total_count = 0; |
| uint32_t total_size = 0; |
| |
| /* Sum section size and element count */ |
| for (Section* sec: sections) { |
| total_size += sec->payload_size; |
| total_count += sec->count; |
| } |
| |
| ctx->stream.WriteU8Enum(section_code, "section code"); |
| ctx->current_section_payload_offset = -1; |
| |
| switch (section_code) { |
| case BinarySection::Import: |
| write_import_section(ctx); |
| break; |
| case BinarySection::Function: |
| write_function_section(ctx, sections, total_count); |
| break; |
| case BinarySection::Table: |
| write_table_section(ctx, sections); |
| break; |
| case BinarySection::Export: |
| write_export_section(ctx); |
| break; |
| case BinarySection::Elem: |
| write_elem_section(ctx, sections); |
| break; |
| case BinarySection::Memory: |
| write_memory_section(ctx, sections); |
| break; |
| case BinarySection::Data: |
| write_data_section(ctx, sections, total_count); |
| break; |
| default: { |
| /* Total section size includes the element count leb128. */ |
| total_size += u32_leb128_length(total_count); |
| |
| /* Write section to stream */ |
| Stream* stream = &ctx->stream; |
| write_u32_leb128(stream, total_size, "section size"); |
| write_u32_leb128(stream, total_count, "element count"); |
| ctx->current_section_payload_offset = ctx->stream.offset(); |
| for (Section* sec: sections) { |
| apply_relocations(sec); |
| write_section_payload(ctx, sec); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| struct ExportInfo { |
| ExportInfo(Export* export_, LinkerInputBinary* binary) |
| : export_(export_), binary(binary) {} |
| |
| Export* export_; |
| LinkerInputBinary* binary; |
| }; |
| |
| static void resolve_symbols(Context* ctx) { |
| /* Create hashmap of all exported symbols from all inputs */ |
| BindingHash export_map; |
| std::vector<ExportInfo> export_list; |
| |
| for (size_t i = 0; i < ctx->inputs.size(); i++) { |
| LinkerInputBinary* binary = ctx->inputs[i].get(); |
| for (size_t j = 0; j < binary->exports.size(); j++) { |
| Export* export_ = &binary->exports[j]; |
| export_list.emplace_back(export_, binary); |
| |
| /* TODO(sbc): Handle duplicate names */ |
| export_map.emplace(string_slice_to_string(export_->name), |
| Binding(export_list.size() - 1)); |
| } |
| } |
| |
| /* |
| * Iterate through all imported functions resolving them against exported |
| * ones. |
| */ |
| for (size_t i = 0; i < ctx->inputs.size(); i++) { |
| LinkerInputBinary* binary = ctx->inputs[i].get(); |
| for (size_t j = 0; j < binary->function_imports.size(); j++) { |
| FunctionImport* import = &binary->function_imports[j]; |
| int export_index = export_map.find_index(import->name); |
| if (export_index == -1) { |
| if (!s_relocatable) |
| WABT_FATAL("undefined symbol: " PRIstringslice "\n", |
| WABT_PRINTF_STRING_SLICE_ARG(import->name)); |
| continue; |
| } |
| |
| /* We found the symbol exported by another module */ |
| ExportInfo* export_info = &export_list[export_index]; |
| |
| /* TODO(sbc): verify the foriegn function has the correct signature */ |
| import->active = false; |
| import->foreign_binary = export_info->binary; |
| import->foreign_index = export_info->export_->index; |
| binary->active_function_imports--; |
| } |
| } |
| } |
| |
| static void calculate_reloc_offsets(Context* ctx) { |
| uint32_t memory_page_offset = 0; |
| uint32_t type_count = 0; |
| uint32_t global_count = 0; |
| uint32_t function_count = 0; |
| uint32_t table_elem_count = 0; |
| uint32_t total_function_imports = 0; |
| uint32_t total_global_imports = 0; |
| for (size_t i = 0; i < ctx->inputs.size(); i++) { |
| LinkerInputBinary* binary = ctx->inputs[i].get(); |
| /* 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 = total_function_imports; |
| binary->imported_global_index_offset = total_global_imports; |
| binary->memory_page_offset = memory_page_offset; |
| |
| size_t delta = 0; |
| for (size_t i = 0; i < binary->function_imports.size(); i++) { |
| if (!binary->function_imports[i].active) { |
| delta++; |
| } else { |
| binary->function_imports[i].relocated_function_index = |
| total_function_imports + i - delta; |
| } |
| } |
| |
| memory_page_offset += binary->memory_page_count; |
| total_function_imports += binary->active_function_imports; |
| total_global_imports += binary->global_imports.size(); |
| } |
| |
| for (size_t i = 0; i < ctx->inputs.size(); i++) { |
| LinkerInputBinary* binary = ctx->inputs[i].get(); |
| binary->table_index_offset = table_elem_count; |
| table_elem_count += binary->table_elem_count; |
| for (size_t j = 0; j < binary->sections.size(); j++) { |
| Section* sec = binary->sections[j].get(); |
| switch (sec->section_code) { |
| case BinarySection::Type: |
| binary->type_index_offset = type_count; |
| type_count += sec->count; |
| break; |
| case BinarySection::Global: |
| binary->global_index_offset = total_global_imports - |
| sec->binary->global_imports.size() + |
| global_count; |
| global_count += sec->count; |
| break; |
| case BinarySection::Function: |
| binary->function_index_offset = total_function_imports - |
| sec->binary->function_imports.size() + |
| function_count; |
| function_count += sec->count; |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| } |
| |
| static void write_binary(Context* ctx) { |
| /* Find all the sections of each type */ |
| SectionPtrVector sections[kBinarySectionCount]; |
| |
| for (size_t j = 0; j < ctx->inputs.size(); j++) { |
| LinkerInputBinary* binary = ctx->inputs[j].get(); |
| for (size_t i = 0; i < binary->sections.size(); i++) { |
| Section* s = binary->sections[i].get(); |
| SectionPtrVector& sec_list = sections[static_cast<int>(s->section_code)]; |
| sec_list.push_back(s); |
| } |
| } |
| |
| /* Write the final binary */ |
| ctx->stream.WriteU32(WABT_BINARY_MAGIC, "WABT_BINARY_MAGIC"); |
| ctx->stream.WriteU32(WABT_BINARY_VERSION, "WABT_BINARY_VERSION"); |
| |
| /* Write known sections first */ |
| for (size_t i = FIRST_KNOWN_SECTION; i < kBinarySectionCount; i++) { |
| write_combined_section(ctx, static_cast<BinarySection>(i), sections[i]); |
| } |
| |
| write_names_section(ctx); |
| |
| /* Generate a new set of reloction sections */ |
| for (size_t i = FIRST_KNOWN_SECTION; i < kBinarySectionCount; i++) { |
| write_reloc_section(ctx, static_cast<BinarySection>(i), sections[i]); |
| } |
| } |
| |
| static void dump_reloc_offsets(Context* ctx) { |
| if (s_debug) { |
| for (uint32_t i = 0; i < ctx->inputs.size(); i++) { |
| LinkerInputBinary* binary = ctx->inputs[i].get(); |
| s_log_stream->Writef("Relocation info for: %s\n", binary->filename); |
| s_log_stream->Writef(" - type index offset : %d\n", |
| binary->type_index_offset); |
| s_log_stream->Writef(" - mem page offset : %d\n", |
| binary->memory_page_offset); |
| s_log_stream->Writef(" - function index offset : %d\n", |
| binary->function_index_offset); |
| s_log_stream->Writef(" - global index offset : %d\n", |
| binary->global_index_offset); |
| s_log_stream->Writef(" - imported function offset: %d\n", |
| binary->imported_function_index_offset); |
| s_log_stream->Writef(" - imported global offset : %d\n", |
| binary->imported_global_index_offset); |
| } |
| } |
| } |
| |
| static Result perform_link(Context* ctx) { |
| if (s_debug) { |
| ctx->stream.set_log_stream(s_log_stream.get()); |
| s_log_stream->Writef("writing file: %s\n", s_outfile); |
| } |
| |
| calculate_reloc_offsets(ctx); |
| resolve_symbols(ctx); |
| calculate_reloc_offsets(ctx); |
| dump_reloc_offsets(ctx); |
| write_binary(ctx); |
| |
| if (WABT_FAILED(ctx->stream.WriteToFile(s_outfile))) { |
| WABT_FATAL("error writing linked output to file\n"); |
| } |
| |
| return Result::Ok; |
| } |
| |
| int main(int argc, char** argv) { |
| init_stdio(); |
| |
| Context context; |
| |
| parse_options(argc, argv); |
| |
| Result result = Result::Ok; |
| for (size_t i = 0; i < s_infiles.size(); i++) { |
| const std::string& input_filename = s_infiles[i]; |
| if (s_debug) |
| s_log_stream->Writef("reading file: %s\n", input_filename.c_str()); |
| char* data; |
| size_t size; |
| result = read_file(input_filename.c_str(), &data, &size); |
| if (WABT_FAILED(result)) |
| return result != Result::Ok; |
| LinkerInputBinary* b = new LinkerInputBinary( |
| input_filename.c_str(), reinterpret_cast<uint8_t*>(data), size); |
| context.inputs.emplace_back(b); |
| LinkOptions options = { NULL }; |
| if (s_debug) |
| options.log_stream = s_log_stream.get(); |
| result = read_binary_linker(b, &options); |
| if (WABT_FAILED(result)) |
| WABT_FATAL("error parsing file: %s\n", input_filename.c_str()); |
| } |
| |
| result = perform_link(&context); |
| return result != Result::Ok; |
| } |