Skip to content
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

Implement directory mapping and WASI file functions #208

Merged
merged 1 commit into from
Feb 16, 2024
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
29 changes: 29 additions & 0 deletions src/runtime/SpecTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class SpecTestFunctionTypes {
I32_RI32,
I32I32_RI32,
I32I32I32I32_RI32,
I32I64I32I32_RI32,
I32I32I32I32I32I64I64I32I32_RI32,
RI32,
I64R,
F32R,
Expand Down Expand Up @@ -88,6 +90,33 @@ class SpecTestFunctionTypes {
result->push_back(Value::Type::I32);
m_vector[index++] = new FunctionType(param, result);
}
{
// I32I64I32I32_RI32
param = new ValueTypeVector();
result = new ValueTypeVector();
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I64);
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I32);
result->push_back(Value::Type::I32);
m_vector[index++] = new FunctionType(param, result);
}
{
// I32I32I32I32I32I64I64I32I32_RI32
param = new ValueTypeVector();
result = new ValueTypeVector();
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I64);
param->push_back(Value::Type::I64);
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I32);
result->push_back(Value::Type::I32);
m_vector[index++] = new FunctionType(param, result);
}
{
// RI32
param = new ValueTypeVector();
Expand Down
47 changes: 43 additions & 4 deletions src/shell/Shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "wabt/wast-lexer.h"
#include "wabt/wast-parser.h"
#include "wabt/binary-writer.h"
#include "string-view-lite/string_view.h"

struct spectestseps : std::numpunct<char> {
char do_thousands_sep() const { return '_'; }
Expand All @@ -39,6 +40,7 @@ struct ArgParser {

static bool useJIT = false;
static int jitVerbose = 0;
static bool shareHostEnv = false;

using namespace Walrus;

Expand Down Expand Up @@ -999,11 +1001,12 @@ static void runExports(Store* store, const std::string& filename, const std::vec
&data);
}

static void parseArguments(int argc, char* argv[], ArgParser& argParser)
static void parseArguments(int argc, char* argv[], ArgParser& argParser, std::vector<uvwasi_preopen_s>& preopenDirs)
{
const std::vector<nonstd::string_view> args(argv + 1, argv + argc);

for (auto it = args.begin(); it != args.end(); ++it) {
auto it = args.begin();
while (it != args.end()) {
if (*it == "--run-export") {
if (*it == args.back()) {
fprintf(stderr, "error: --run-export requires an argument\n");
Expand All @@ -1018,6 +1021,40 @@ static void parseArguments(int argc, char* argv[], ArgParser& argParser)
jitVerbose = 1;
} else if (*it == "--jit-verbose-color") {
jitVerbose = 2;
} else if (*it == "--mapdirs") {
it++;
while (it != args.end() && nonstd::to_string(*it).find("--") == std::string::npos) {
if (it + 1 == args.end()) {
break;
}

uvwasi_preopen_s preopen;
preopen.real_path = reinterpret_cast<char*>(calloc(1, it->length() + 1));
it->nonstd::sv_lite::string_view::copy(const_cast<char*>(preopen.real_path), it->length());

it++;

preopen.mapped_path = reinterpret_cast<char*>(calloc(1, it->length() + 1));
it->nonstd::sv_lite::string_view::copy(const_cast<char*>(preopen.mapped_path), it->length());
Comment on lines +1032 to +1038
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will these strings allocated by calloc be correctly freed at the end of Walrus?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, I forgot about those. After running valgrind I fixed these and also added initialization to wasi envrionment variables, so valgrind did not return any errors.

preopenDirs.push_back(preopen);
it++;
}
it--;
} else if (*it == "--env") {
shareHostEnv = true;
} else if (*it == "--help" || *it == "-h") {
fprintf(stdout, "Run a WebAssembly wasm, wat or wast file.\n\n");
fprintf(stdout, "Usage: walrus [OPTIONS] <INPUT>\n\n");
fprintf(stdout, "OPTIONS:\n");
fprintf(stdout, "\t-h, --help\n\t\tShow this message then exit.\n\n");
fprintf(stdout, "\t--jit\n\t\tEnable just-in-time interpretation.\n\n");
fprintf(stdout, "\t--jit-verbose\n\t\tEnable verbose output for just-in-time interpretation.\n\n");
fprintf(stdout, "\t--jit-verbose-color\n\t\tEnable colored verbose output for just-in-time interpretation.\n\n");
fprintf(stdout, "\t--mapdirs <HOST_DIR> <VIRTUAL_DIR>\n\t\tMap real directories to virtual ones for WASI functions to use.\n\t\tExample: ./walrus test.wasm --mapdirs this/real/directory/ this/virtual/directory\n\n");
fprintf(stdout, "\t--env\n\t\tShare host environment to walrus WASI.\n\n");


exit(0);
} else {
auto arg = nonstd::to_string(*it);
if (endsWith(arg, "wat") || endsWith(arg, "wast") || endsWith(arg, "wasm")) {
Expand All @@ -1027,6 +1064,7 @@ static void parseArguments(int argc, char* argv[], ArgParser& argParser)
exit(1);
}
}
++it;
}

if (argParser.fileNames.empty()) {
Expand Down Expand Up @@ -1055,12 +1093,13 @@ int main(int argc, char* argv[])

Engine* engine = new Engine();
Store* store = new Store(engine);
WASI* wasi = new WASI();

std::vector<uvwasi_preopen_s> preopenDirs;
SpecTestFunctionTypes functionTypes;
ArgParser argParser;

parseArguments(argc, argv, argParser);
parseArguments(argc, argv, argParser, preopenDirs);
WASI* wasi = new WASI(preopenDirs, shareHostEnv);

for (const auto& filePath : argParser.fileNames) {
FILE* fp = fopen(filePath.data(), "r");
Expand Down
41 changes: 41 additions & 0 deletions src/wasi/Environ.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2023-present Samsung Electronics Co., Ltd
*
* 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.
*/

namespace Walrus {

void WASI::environ_sizes_get(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t count = argv[0].asI32();
uint32_t buf = argv[1].asI32();

uvwasi_size_t* uvCount = reinterpret_cast<uvwasi_size_t*>(instance->memory(0)->buffer() + count);
uvwasi_size_t* uvBufSize = reinterpret_cast<uvwasi_size_t*>(instance->memory(0)->buffer() + buf);

result[0] = Value(static_cast<uint16_t>(uvwasi_environ_sizes_get(WASI::m_uvwasi, uvCount, uvBufSize)));
}

void WASI::environ_get(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t env = argv[0].asI32();
uint32_t environBuf = argv[1].asI32();

char** uvEnviron = reinterpret_cast<char**>(instance->memory(0)->buffer() + env);
char* uvEnvironBuf = reinterpret_cast<char*>(instance->memory(0)->buffer() + environBuf);

result[0] = Value(static_cast<uint16_t>(uvwasi_environ_get(WASI::m_uvwasi, uvEnviron, uvEnvironBuf)));
}

} // namespace Walrus
47 changes: 43 additions & 4 deletions src/wasi/Fd.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,52 @@ void WASI::fd_write(ExecutionState& state, Value* argv, Value* result, Instance*
std::vector<uvwasi_ciovec_t> iovs(iovcnt);
for (uint32_t i = 0; i < iovcnt; i++) {
iovs[i].buf = instance->memory(0)->buffer() + *reinterpret_cast<uint32_t*>(instance->memory(0)->buffer() + iovptr + i * 8);
iovs[i].buf_len = *(instance->memory(0)->buffer() + iovptr + 4 + i * 8);
iovs[i].buf_len = *reinterpret_cast<uint32_t*>(instance->memory(0)->buffer() + iovptr + 4 + i * 8);
}

uvwasi_size_t out_addr = *(instance->memory(0)->buffer() + out);
uvwasi_size_t* out_addr = (uvwasi_size_t*)(instance->memory(0)->buffer() + out);

result[0] = Value(static_cast<int16_t>(uvwasi_fd_write(WASI::m_uvwasi, fd, iovs.data(), iovs.size(), &out_addr)));
*(instance->memory(0)->buffer() + out) = out_addr;
result[0] = Value(static_cast<int16_t>(uvwasi_fd_write(WASI::m_uvwasi, fd, iovs.data(), iovs.size(), out_addr)));
*(instance->memory(0)->buffer() + out) = *out_addr;
}

void WASI::fd_read(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t fd = argv[0].asI32();
uint32_t iovptr = argv[1].asI32();
uint32_t iovcnt = argv[2].asI32();
uint32_t out = argv[3].asI32();

std::vector<uvwasi_iovec_t> iovs(iovcnt);
for (uint32_t i = 0; i < iovcnt; i++) {
iovs[i].buf = instance->memory(0)->buffer() + *reinterpret_cast<uint32_t*>(instance->memory(0)->buffer() + iovptr + i * 8);
iovs[i].buf_len = *reinterpret_cast<uint32_t*>(instance->memory(0)->buffer() + iovptr + 4 + i * 8);
}

uvwasi_size_t* out_addr = (uvwasi_size_t*)(instance->memory(0)->buffer() + out);

result[0] = Value(static_cast<int16_t>(uvwasi_fd_read(WASI::m_uvwasi, fd, iovs.data(), iovs.size(), out_addr)));
*(instance->memory(0)->buffer() + out) = *out_addr;
}

void WASI::fd_close(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t fd = argv[0].asI32();

result[0] = Value(static_cast<int16_t>(uvwasi_fd_close(WASI::m_uvwasi, fd)));
}

void WASI::fd_seek(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t fd = argv[0].asI32();
uint64_t fileDelta = argv[1].asI32();
uint32_t whence = argv[2].asI32();
uint64_t newOffset = argv[3].asI32();

uvwasi_filesize_t out_addr = *(instance->memory(0)->buffer() + newOffset);

result[0] = Value(static_cast<int16_t>(uvwasi_fd_seek(WASI::m_uvwasi, fd, fileDelta, whence, &out_addr)));
*(instance->memory(0)->buffer() + newOffset) = out_addr;
}

} // namespace Walrus
48 changes: 48 additions & 0 deletions src/wasi/Path.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2023-present Samsung Electronics Co., Ltd
*
* 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.
*/

namespace Walrus {

void WASI::path_open(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t fd = argv[0].asI32();
uint32_t dirflags = argv[1].asI32();
uint32_t path_offset = argv[2].asI32();
uint32_t len = argv[3].asI32();
uint32_t oflags = argv[4].asI32();
uint64_t rights = argv[5].asI64();
uint64_t right_inheriting = argv[6].asI64();
uint32_t fdflags = argv[7].asI32();
uint32_t ret_fd_offset = argv[8].asI32();

uvwasi_fd_t* ret_fd = reinterpret_cast<uvwasi_fd_t*>(instance->memory(0)->buffer() + ret_fd_offset);

const char* path = reinterpret_cast<char*>(instance->memory(0)->buffer() + path_offset);

result[0] = Value(static_cast<uint16_t>(
uvwasi_path_open(WASI::m_uvwasi,
fd,
dirflags,
path,
len,
oflags,
rights,
right_inheriting,
fdflags,
ret_fd)));
}

} // namespace Walrus
78 changes: 75 additions & 3 deletions src/wasi/Wasi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#include "wasi/Wasi.h"
#include "wasi/Fd.h"
#include "wasi/Path.h"
#include "wasi/Environ.h"

// https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md

Expand Down Expand Up @@ -93,11 +95,10 @@ void WASI::fillWasiFuncTable()
#undef WASI_FUNC_TABLE
}

WASI::WASI()
WASI::WASI(std::vector<uvwasi_preopen_s>& preopenDirs, bool shareHostEnv)
{
fillWasiFuncTable();

uvwasi_t uvwasi;
WASI::m_uvwasi = reinterpret_cast<uvwasi_t*>(malloc(sizeof(uvwasi_t)));

uvwasi_options_t init_options;
Expand All @@ -108,12 +109,83 @@ WASI::WASI()
init_options.argc = 0;
init_options.argv = nullptr;
init_options.envp = nullptr;
init_options.preopenc = 0;
if (shareHostEnv) {
std::vector<std::string> variables = {
// Common Unix environment variables.
"PATH",
"LIB",
"PWD",
"SHELL",
Comment on lines +113 to +118
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder about how did you define these common env variables
Is there any reference?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"TERM",
"HOME",
"LOGNAME",
"HOSTNAME",
"UID",
"TEMP",
"EDITOR"
"LANG",
"LC_TIME",
// Common Windows environment variables.
"APPDATA",
"USERPROFILE",
"USERDOMAIN",
"windir",
"ProgramFiles",
"OS"
"LOCALAPPDATA",
"SystemRoot",
"SystemDrive",
};

char** envp = (char**)malloc(sizeof(char**) * variables.size());
Copy link
Collaborator

Choose a reason for hiding this comment

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

What about simply allocating a vector of std::string here?
This kind of manual memory alloc is so complicated to handle and hard for correct freeing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After spending some time thinking how I would change it to be easily used with uvwasi, I could not think of a way. If you have an idea, it would be greatly appriciated.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see. I'll revise this later


size_t j = 0;
for (size_t i = 0; i < variables.size(); i++) {
if (getenv(variables[i].c_str()) == nullptr) {
continue;
}

size_t length = strlen(variables[i].c_str());
length += strlen(getenv(variables[i].c_str()));
length += 2;
envp[j] = (char*)malloc(length);
strcpy(envp[j], variables[i].c_str());
envp[j][variables[i].size()] = '=';
strcpy(envp[j] + variables[i].size() + 1, getenv(variables[i].c_str()));
envp[j][length - 1] = '\0';
j++;
}

while (j < variables.size()) {
envp[j] = nullptr;
j++;
}

init_options.envp = const_cast<const char**>(envp);
}
init_options.preopenc = preopenDirs.size();
init_options.preopens = preopenDirs.data();
init_options.preopen_socketc = 0;
init_options.allocator = nullptr;

uvwasi_errno_t err = uvwasi_init(WASI::m_uvwasi, &init_options);
assert(err == UVWASI_ESUCCESS);

if (init_options.envp != nullptr) {
for (size_t i = 0; init_options.envp[i] != nullptr; i++) {
free((void*)init_options.envp[i]);
}
free(init_options.envp);
}

for (auto& elem : preopenDirs) {
if (elem.mapped_path != nullptr) {
free((void*)elem.mapped_path);
}
if (elem.real_path != nullptr) {
free((void*)elem.real_path);
}
}
}

} // namespace Walrus
Loading
Loading