Skip to content

Extract common bytecode operations into WasmBase. #161

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ cc_library(
"src/*.h",
"src/*.cc",
"src/common/*.h",
"src/common/*.cc",
"src/null/*.cc",
"src/third_party/*.h",
"src/third_party/*.cc",
"src/null/*.cc",
]),
deps = [
":include",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include "include/proxy-wasm/wasm_vm.h"

namespace proxy_wasm {
namespace common {

// Utilitiy functions which directly operate on Wasm bytecodes.
class BytecodeUtil {
Expand Down Expand Up @@ -73,5 +72,4 @@ class BytecodeUtil {
static bool parseVarint(const char *&begin, const char *end, uint32_t &ret);
};

} // namespace common
} // namespace proxy_wasm
4 changes: 2 additions & 2 deletions include/proxy-wasm/null_vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ struct NullVm : public WasmVm {
std::string_view runtime() override { return "null"; }
Cloneable cloneable() override { return Cloneable::InstantiatedModule; };
std::unique_ptr<WasmVm> clone() override;
bool load(const std::string &code, bool allow_precompiled) override;
AbiVersion getAbiVersion() override;
bool load(std::string_view bytecode, std::string_view precompiled,
std::unordered_map<uint32_t, std::string> function_names) override;
bool link(std::string_view debug_name) override;
uint64_t getMemorySize() override;
std::optional<std::string_view> getMemory(uint64_t pointer, uint64_t size) override;
Expand Down
25 changes: 15 additions & 10 deletions include/proxy-wasm/wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
WasmBase(const std::shared_ptr<WasmHandleBase> &other, WasmVmFactory factory);
virtual ~WasmBase();

bool initialize(const std::string &code, bool allow_precompiled = false);
bool load(const std::string &code, bool allow_precompiled = false);
bool initialize();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WasmBase::initialize(module, ...) is split into WasmBase::load(module, ...) and WasmBase::initialize(), and only the latter is called in clones (even in case of non-cloneable runtimes, since the base WasmBase is supposed to do all necessary pre-processing of the bytecode).

void startVm(ContextBase *root_context);
bool configure(ContextBase *root_context, std::shared_ptr<PluginBase> plugin);
// Returns the root ContextBase or nullptr if onStart returns false.
Expand All @@ -79,9 +80,11 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
bool isFailed() { return failed_ != FailState::Ok; }
FailState fail_state() { return failed_; }

const std::string &code() const { return code_; }
const std::string &vm_configuration() const;
bool allow_precompiled() const { return allow_precompiled_; }

const std::string &moduleBytecode() const { return module_bytecode_; }
const std::string &modulePrecompiled() const { return module_precompiled_; }
const std::unordered_map<uint32_t, std::string> functionNames() const { return function_names_; }

void timerReady(uint32_t root_context_id);
void queueReady(uint32_t root_context_id, uint32_t token);
Expand Down Expand Up @@ -135,7 +138,7 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
virtual void error(std::string_view message) { std::cerr << message << "\n"; }
virtual void unimplemented() { error("unimplemented proxy-wasm API"); }

AbiVersion abiVersion() { return abi_version_; }
AbiVersion abiVersion() const { return abi_version_; }

const std::unordered_map<std::string, std::string> &envs() { return envs_; }

Expand Down Expand Up @@ -182,7 +185,7 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
std::string vm_id_; // User-provided vm_id.
std::string vm_key_; // vm_id + hash of code.
std::unique_ptr<WasmVm> wasm_vm_;
Cloneable started_from_{Cloneable::NotCloneable};
std::optional<Cloneable> started_from_;

uint32_t next_context_id_ = 1; // 0 is reserved for the VM context.
std::shared_ptr<ContextBase> vm_context_; // Context unrelated to any specific root or stream
Expand Down Expand Up @@ -260,15 +263,17 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
std::shared_ptr<WasmHandleBase> base_wasm_handle_;

// Used by the base_wasm to enable non-clonable thread local Wasm(s) to be constructed.
std::string code_;
std::string vm_configuration_;
bool allow_precompiled_ = false;
bool stop_iteration_ = false;
FailState failed_ = FailState::Ok; // Wasm VM fatal error.
std::string module_bytecode_;
std::string module_precompiled_;
std::unordered_map<uint32_t, std::string> function_names_;

// ABI version.
AbiVersion abi_version_ = AbiVersion::Unknown;

std::string vm_configuration_;
bool stop_iteration_ = false;
FailState failed_ = FailState::Ok; // Wasm VM fatal error.

// Plugin Stats/Metrics
uint32_t next_counter_metric_id_ = static_cast<uint32_t>(MetricType::Counter);
uint32_t next_gauge_metric_id_ = static_cast<uint32_t>(MetricType::Gauge);
Expand Down
18 changes: 6 additions & 12 deletions include/proxy-wasm/wasm_vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>

#include "include/proxy-wasm/word.h"

Expand Down Expand Up @@ -174,14 +175,13 @@ class WasmVm {
* Load the WASM code from a file. Return true on success. Once the module is loaded it can be
* queried, e.g. to see which version of emscripten support is required. After loading, the
* appropriate ABI callbacks can be registered and then the module can be link()ed (see below).
* @param code the WASM binary code (or registered NullVm plugin name).
* @param allow_precompiled if true, allows supporting VMs (e.g. WAVM) to load the binary
* machine code from a user-defined section of the WASM file. Because that code is not verified by
* the proxy process it is up to the user to ensure that the code is both safe and is built for
* the linked in version of WAVM.
* @param bytecode the Wasm bytecode or registered NullVm plugin name.
* @param precompiled (optional) the precompiled Wasm module.
* @param function_names (optional) an index-to-name mapping for the functions.
* @return whether or not the load was successful.
*/
virtual bool load(const std::string &code, bool allow_precompiled) = 0;
virtual bool load(std::string_view bytecode, std::string_view precompiled,
const std::unordered_map<uint32_t, std::string> function_names) = 0;

/**
* Link the WASM code to the host-provided functions, e.g. the ABI. Prior to linking, the module
Expand All @@ -192,12 +192,6 @@ class WasmVm {
*/
virtual bool link(std::string_view debug_name) = 0;

/**
* Get ABI version of the module.
* @return the ABI version.
*/
virtual AbiVersion getAbiVersion() = 0;

/**
* Get size of the currently allocated memory in the VM.
* @return the size of memory in bytes.
Expand Down
5 changes: 2 additions & 3 deletions src/common/bytecode_util.cc → src/bytecode_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#include "src/common/bytecode_util.h"
#include "include/proxy-wasm/bytecode_util.h"

#include <cstring>

namespace proxy_wasm {
namespace common {

bool BytecodeUtil::checkWasmHeader(std::string_view bytecode) {
// Wasm file header is 8 bytes (magic number + version).
Expand Down Expand Up @@ -240,5 +240,4 @@ bool BytecodeUtil::parseVarint(const char *&pos, const char *end, uint32_t &ret)
return ret != static_cast<uint32_t>(-1);
}

} // namespace common
} // namespace proxy_wasm
11 changes: 5 additions & 6 deletions src/null/null_vm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,23 @@ std::unique_ptr<WasmVm> NullVm::clone() {
auto cloned_null_vm = std::make_unique<NullVm>(*this);
if (integration())
cloned_null_vm->integration().reset(integration()->clone());
cloned_null_vm->load(plugin_name_, false /* unused */);
cloned_null_vm->load(plugin_name_, {} /* unused */, {} /* unused */);
return cloned_null_vm;
}

// "Load" the plugin by obtaining a pointer to it from the factory.
bool NullVm::load(const std::string &name, bool /* allow_precompiled */) {
bool NullVm::load(std::string_view name, std::string_view,
const std::unordered_map<uint32_t, std::string>) {
if (!null_vm_plugin_factories_)
return false;
auto factory = (*null_vm_plugin_factories_)[name];
auto factory = (*null_vm_plugin_factories_)[std::string(name)];
if (!factory)
return false;
plugin_name_ = name;
plugin_ = factory();
plugin_->wasm_vm_ = this;
return true;
}

AbiVersion NullVm::getAbiVersion() { return AbiVersion::ProxyWasm_0_2_1; }
} // namespace proxy_wasm

bool NullVm::link(std::string_view /* name */) { return true; }

Expand Down
68 changes: 17 additions & 51 deletions src/v8/v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
#include <utility>
#include <vector>

#include "src/common/bytecode_util.h"

#include "v8.h"
#include "v8-version.h"
#include "wasm-api/wasm.hh"
Expand Down Expand Up @@ -64,8 +62,8 @@ class V8 : public WasmVm {
// WasmVm
std::string_view runtime() override { return "v8"; }

bool load(const std::string &code, bool allow_precompiled) override;
AbiVersion getAbiVersion() override;
bool load(std::string_view bytecode, std::string_view precompiled,
const std::unordered_map<uint32_t, std::string> function_names) override;
std::string_view getPrecompiledSectionName() override;
bool link(std::string_view debug_name) override;

Expand Down Expand Up @@ -122,8 +120,6 @@ class V8 : public WasmVm {

std::unordered_map<std::string, FuncDataPtr> host_functions_;
std::unordered_map<std::string, wasm::own<wasm::Func>> module_functions_;

AbiVersion abi_version_;
std::unordered_map<uint32_t, std::string> function_names_index_;
};

Expand Down Expand Up @@ -251,58 +247,31 @@ template <typename T, typename U> constexpr T convertValTypesToArgsTuple(const U

// V8 implementation.

bool V8::load(const std::string &code, bool allow_precompiled) {
bool V8::load(std::string_view bytecode, std::string_view precompiled,
const std::unordered_map<uint32_t, std::string> function_names) {
store_ = wasm::Store::make(engine());

// Get ABI version from bytecode.
if (!common::BytecodeUtil::getAbiVersion(code, abi_version_)) {
fail(FailState::UnableToInitializeCode, "Failed to parse corrupted Wasm module");
return false;
}
if (!precompiled.empty()) {
auto vec = wasm::vec<byte_t>::make_uninitialized(precompiled.size());
::memcpy(vec.get(), precompiled.data(), precompiled.size());
module_ = wasm::Module::deserialize(store_.get(), vec);

if (allow_precompiled) {
const auto section_name = getPrecompiledSectionName();
if (!section_name.empty()) {
std::string_view precompiled = {};
if (!common::BytecodeUtil::getCustomSection(code, section_name, precompiled)) {
fail(FailState::UnableToInitializeCode, "Failed to parse corrupted Wasm module");
return false;
}
if (!precompiled.empty()) {
auto vec = wasm::vec<byte_t>::make_uninitialized(precompiled.size());
::memcpy(vec.get(), precompiled.data(), precompiled.size());

module_ = wasm::Module::deserialize(store_.get(), vec);
if (!module_) {
// Precompiled module that cannot be loaded is considered a hard error,
// so don't fallback to compiling the bytecode.
return false;
}
}
}
} else {
auto vec = wasm::vec<byte_t>::make_uninitialized(bytecode.size());
::memcpy(vec.get(), bytecode.data(), bytecode.size());
module_ = wasm::Module::make(store_.get(), vec);
}

if (!module_) {
std::string stripped;
if (!common::BytecodeUtil::getStrippedSource(code, stripped)) {
fail(FailState::UnableToInitializeCode, "Failed to parse corrupted Wasm module");
return false;
};
wasm::vec<byte_t> stripped_vec = wasm::vec<byte_t>::make(stripped.size(), stripped.data());
module_ = wasm::Module::make(store_.get(), stripped_vec);
return false;
}

if (module_) {
shared_module_ = module_->share();
assert((shared_module_ != nullptr));
}
shared_module_ = module_->share();
assert(shared_module_ != nullptr);

if (!common::BytecodeUtil::getFunctionNameIndex(code, function_names_index_)) {
fail(FailState::UnableToInitializeCode, "Failed to parse corrupted Wasm module");
return false;
};
function_names_index_ = function_names;

return module_ != nullptr;
return true;
}

std::unique_ptr<WasmVm> V8::clone() {
Expand All @@ -314,7 +283,6 @@ std::unique_ptr<WasmVm> V8::clone() {

clone->module_ = wasm::Module::obtain(clone->store_.get(), shared_module_.get());
clone->function_names_index_ = function_names_index_;
clone->abi_version_ = abi_version_;

return clone;
}
Expand All @@ -335,8 +303,6 @@ std::string_view V8::getPrecompiledSectionName() {
return name;
}

AbiVersion V8::getAbiVersion() { return abi_version_; }

bool V8::link(std::string_view debug_name) {
assert(module_ != nullptr);

Expand Down
32 changes: 9 additions & 23 deletions src/wamr/wamr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@
#include <utility>
#include <vector>

#include "include/proxy-wasm/bytecode_util.h"

#include "src/wamr/types.h"
#include "wasm_c_api.h"
#include "src/common/bytecode_util.h"

namespace proxy_wasm {
namespace wamr {
Expand Down Expand Up @@ -62,9 +63,8 @@ class Wamr : public WasmVm {
Cloneable cloneable() override { return Cloneable::NotCloneable; }
std::unique_ptr<WasmVm> clone() override { return nullptr; }

AbiVersion getAbiVersion() override;

bool load(const std::string &code, bool allow_precompiled = false) override;
bool load(std::string_view bytecode, std::string_view precompiled,
const std::unordered_map<uint32_t, std::string> function_names) override;
bool link(std::string_view debug_name) override;
uint64_t getMemorySize() override;
std::optional<std::string_view> getMemory(uint64_t pointer, uint64_t size) override;
Expand Down Expand Up @@ -113,27 +113,15 @@ class Wamr : public WasmVm {

std::unordered_map<std::string, HostFuncDataPtr> host_functions_;
std::unordered_map<std::string, WasmFuncPtr> module_functions_;
AbiVersion abi_version_;
};

bool Wamr::load(const std::string &code, bool allow_precompiled) {
bool Wamr::load(std::string_view bytecode, std::string_view,
const std::unordered_map<uint32_t, std::string>) {
store_ = wasm_store_new(engine());

// Get ABI version from bytecode.
if (!common::BytecodeUtil::getAbiVersion(code, abi_version_)) {
fail(FailState::UnableToInitializeCode, "Failed to parse corrupted Wasm module");
return false;
}

std::string stripped;
if (!common::BytecodeUtil::getStrippedSource(code, stripped)) {
fail(FailState::UnableToInitializeCode, "Failed to parse corrupted Wasm module");
return false;
};

WasmByteVec stripped_vec;
wasm_byte_vec_new(stripped_vec.get(), stripped.size(), stripped.data());
module_ = wasm_module_new(store_.get(), stripped_vec.get());
WasmByteVec vec;
wasm_byte_vec_new(vec.get(), bytecode.size(), bytecode.data());
module_ = wasm_module_new(store_.get(), vec.get());

return module_ != nullptr;
}
Expand Down Expand Up @@ -622,8 +610,6 @@ void Wamr::getModuleFunctionImpl(std::string_view function_name,
};
};

AbiVersion Wamr::getAbiVersion() { return abi_version_; }

} // namespace wamr

std::unique_ptr<WasmVm> createWamrVm() { return std::make_unique<wamr::Wamr>(); }
Expand Down
Loading