Skip to content

Commit 50f1e21

Browse files
committed
Allow dynamically linked main VM for storage example
1 parent ae82f62 commit 50f1e21

File tree

9 files changed

+108
-21
lines changed

9 files changed

+108
-21
lines changed

guest/rust_storage/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ edition = "2021"
77
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
88

99
[dependencies]
10-
png = "0.15.3"
1110
libc = "0.2"
1211

1312
[profile.release]

guest/rust_storage/build.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ objcopy -w --extract-symbol --strip-symbol=!remote* --strip-symbol=* $storage_bi
88
gcc-12 -static -O2 symbol_offset.c -o symbol_offset
99
./symbol_offset storage.syms 0x44000000
1010

11-
RUSTFLAGS="-Ctarget-feature=+crt-static -Zexport-executable-symbols -C link_arg=-Wl,--just-symbols=storage.syms" cargo build --target x86_64-unknown-linux-gnu --release
11+
rm -rf target/
12+
RUSTFLAGS="-Zexport-executable-symbols -C link_arg=-Wl,--undefined=do_calculation,--just-symbols=$PWD/storage.syms" cargo build --release

guest/rust_storage/main

Lines changed: 0 additions & 1 deletion
This file was deleted.

guest/rust_storage/src/main.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
use std::arch::asm;
12
extern "C" { fn remote_function(arg: extern "C" fn(i32) -> i32, value: i32) -> i32; }
23
use std::process::ExitCode;
34

4-
#[no_mangle]
55
extern "C" fn do_calculation(input: i32) -> i32 {
66
return unsafe { remote_function(double_int, input) };
77
}
@@ -10,10 +10,26 @@ extern "C" fn double_int(input: i32) -> i32 {
1010
return input * 2;
1111
}
1212

13+
// Don't exactly know how else to call a named function
14+
// in a dynamic ELF w/interpreter. We don't know the
15+
// base address of the binary, so we can't adjust the
16+
// symbol address.
17+
fn set_callback(_cb: extern "C" fn(i32) -> i32) {
18+
let sysnum = 0x10001; // Set callback syscall
19+
unsafe {
20+
asm!(
21+
"out 0, eax",
22+
in("eax") sysnum,
23+
in("rdi") _cb
24+
);
25+
}
26+
}
27+
1328
fn main() -> ExitCode
1429
{
1530
println!("Hello, world!");
1631
let result = unsafe { remote_function(double_int, 21) };
1732
println!("Result from remote function: {}", result);
33+
set_callback(do_calculation);
1834
return ExitCode::from(result as u8);
1935
}

guest/rust_storage/storage_program/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ edition = "2021"
77
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
88

99
[dependencies]
10-
png = "0.15.3"
1110
libc = "0.2"
1211

1312
[profile.release]

lib/tinykvm/machine.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ struct Machine
180180
void do_mmap_callback(vCPU&, address_t, size_t, int, int, int, address_t);
181181
void set_mmap_callback(mmap_func_t f) { m_mmap_func = std::move(f); }
182182

183-
uint64_t address_of(const char*) const;
183+
uint64_t address_of(std::string_view symbol, const std::vector<uint8_t>&) const;
184+
uint64_t address_of(std::string_view symbol, std::string_view binary = {}) const;
184185
std::string resolve(uint64_t rip, std::string_view binary = {}) const;
185186

186187
bool smp_active() const noexcept;

lib/tinykvm/machine_elf.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,9 @@ void Machine::elf_loader(std::string_view binary, const MachineOptions& options)
164164
stack being above the image base. */
165165
const uint32_t STACK_SIZE = (options.stack_size + PageMask()) & ~PageMask();
166166
this->m_stack_address = this->m_heap_address + STACK_SIZE;
167-
this->m_heap_address = this->m_stack_address;
167+
this->m_brk_address = this->m_stack_address;
168+
this->m_brk_end_address = this->m_stack_address + BRK_MAX;
169+
this->m_heap_address = this->m_brk_end_address;
168170

169171
/* Make sure mmap starts at a sane offset */
170172
this->mmap_cache().current() = this->m_heap_address;
@@ -268,11 +270,18 @@ static const Elf64_Sym* resolve_symbol(std::string_view binary, const char* name
268270
return nullptr;
269271
}
270272

271-
uint64_t Machine::address_of(const char* name) const
273+
uint64_t Machine::address_of(std::string_view name, std::string_view binary) const
272274
{
273-
const auto* sym = resolve_symbol(m_binary, name);
275+
if (binary.empty())
276+
binary = this->m_binary;
277+
const auto* sym = resolve_symbol(binary, name.data());
274278
return (sym) ? this->m_image_base + sym->st_value : 0x0;
275279
}
280+
uint64_t Machine::address_of(std::string_view name, const std::vector<uint8_t>& binary) const
281+
{
282+
return this->address_of(name, std::string_view{
283+
reinterpret_cast<const char*>(binary.data()), binary.size()});
284+
}
276285
std::string Machine::resolve(uint64_t rip, std::string_view binary) const
277286
{
278287
if (binary.empty())

lib/tinykvm/vcpu_run.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,9 @@ long vCPU::run_once()
273273
} else if (UNLIKELY(addr < 0x2000)) {
274274
/* Kernel space page fault */
275275
this->handle_exception(intr);
276+
if (machine().is_remote_connected()) {
277+
machine().remote_disconnect();
278+
}
276279
Machine::machine_exception("Kernel or zero page fault", intr);
277280
} else if (addr >= machine().remote_base_address()) {
278281
if (this->remote_original_tls_base != 0) {
@@ -432,7 +435,7 @@ void vCPU::handle_exception(uint8_t intr)
432435
"*** %s on address 0x%llX\n",
433436
amd64_exception_name(intr), sregs.cr2);
434437
uint64_t code;
435-
machine().unsafe_copy_from_guest(&code, regs.rsp+8, 8);
438+
machine().unsafe_copy_from_guest(&code, regs.rsp+16, 8);
436439
PRINTER(printer, buffer,
437440
"Error code: 0x%lX (%s)\n", code,
438441
(code & 0x02) ? "memory write" : "memory read");
@@ -471,7 +474,7 @@ void vCPU::handle_exception(uint8_t intr)
471474

472475
try {
473476
uint64_t off = (has_code) ? (regs.rsp+8) : (regs.rsp+0);
474-
if (intr == 14) off += 8;
477+
if (intr == 14) off += 16;
475478
uint64_t rip, rfl, cs = 0x0, rsp, ss;
476479
try {
477480
machine().unsafe_copy_from_guest(&rip, off+0, 8);

src/storage.cpp

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#include <tinykvm/machine.hpp>
22
#include <cstring>
33
#include <cstdio>
4+
#include <unistd.h>
45
#include "assert.hpp"
56
#include "load_file.hpp"
67
#include "timing.hpp"
78

89
#include <tinykvm/rsp_client.hpp>
910
#define GUEST_MEMORY 1024UL * 1024 * 1024 /* 1024MB main memory */
1011
#define GUEST_WORK_MEM 256UL * 1024 * 1024 /* 256MB working memory */
12+
static const std::string ld_linux_so = "/lib64/ld-linux-x86-64.so.2";
1113

1214
static double timed_action(std::function<void()> action)
1315
{
@@ -30,9 +32,17 @@ int main(int argc, char** argv)
3032
fprintf(stderr, "%s [guest ELF] [storage ELF]\n", argv[0]);
3133
exit(1);
3234
}
33-
const auto guest_binary = load_file(argv[1]);
34-
const auto storage_binary = load_file(argv[2]);
35-
printf(">>> Guest: %s >>> Storage: %s\n", argv[1], argv[2]);
35+
const std::string guest_binary_path = argv[1];
36+
auto original_guest_binary = load_file(guest_binary_path);
37+
const std::string storage_binary_path = argv[2];
38+
const auto storage_binary = load_file(storage_binary_path);
39+
printf(">>> Guest: %s >>> Storage: %s\n", guest_binary_path.c_str(), storage_binary_path.c_str());
40+
std::string cwd;
41+
{
42+
char buf[PATH_MAX];
43+
if (getcwd(buf, sizeof(buf)) != nullptr)
44+
cwd = buf;
45+
}
3646

3747
tinykvm::Machine::init();
3848

@@ -54,33 +64,83 @@ int main(int argc, char** argv)
5464
}
5565
});
5666

67+
std::vector<std::string> guest_args;
68+
std::vector<uint8_t> guest_binary;
69+
const tinykvm::DynamicElf guest_dyn_elf = tinykvm::is_dynamic_elf(
70+
std::string_view{(const char*)original_guest_binary.data(), original_guest_binary.size()});
71+
if (guest_dyn_elf.is_dynamic) {
72+
// Add ld-linux.so.2 as first argument
73+
guest_binary = load_file(ld_linux_so);
74+
guest_args.push_back(ld_linux_so);
75+
} else {
76+
guest_binary = original_guest_binary;
77+
}
78+
guest_args.push_back(guest_binary_path);
79+
guest_args.push_back("Hello Main World!");
80+
5781
/* Setup */
5882
const tinykvm::MachineOptions options {
5983
.max_mem = GUEST_MEMORY,
6084
.max_cow_mem = GUEST_WORK_MEM,
61-
.verbose_loader = false
85+
.dylink_address_hint = 0x400000, // 4MB
86+
.verbose_loader = false,
87+
.executable_heap = guest_dyn_elf.is_dynamic,
88+
.mmap_backed_files = true,
6289
};
6390
tinykvm::Machine master_vm {guest_binary, options};
6491
master_vm.setup_linux(
65-
{"main", "Hello Main World!"},
92+
guest_args,
6693
{"LC_TYPE=C", "LC_ALL=C", "USER=root"});
6794
//master_vm.print_pagetables();
95+
master_vm.fds().set_open_readable_callback(
96+
[] (std::string&) -> bool {
97+
return true;
98+
});
99+
master_vm.set_verbose_system_calls(getenv("VERBOSE") != nullptr);
100+
101+
std::vector<std::string> storage_args;
102+
storage_args.push_back(storage_binary_path);
103+
storage_args.push_back("Hello Storage World!");
68104

69105
/* Create storage VM */
70106
const tinykvm::MachineOptions storage_options {
71107
.max_mem = 256ULL << 20, // MB
72108
.dylink_address_hint = 0x44000000, // 1GB + 64MB
73109
.vmem_base_address = 1ULL << 30, // 1GB
74110
.verbose_loader = false,
111+
.mmap_backed_files = true,
75112
};
76113
tinykvm::Machine storage_vm{storage_binary, storage_options};
114+
storage_vm.set_verbose_system_calls(getenv("VERBOSE") != nullptr);
115+
storage_vm.set_verbose_mmap_syscalls(getenv("VERBOSE") != nullptr);
116+
storage_vm.set_verbose_thread_syscalls(getenv("VERBOSE") != nullptr);
117+
storage_vm.fds().set_open_readable_callback(
118+
[] (std::string&) -> bool {
119+
return true;
120+
});
121+
storage_vm.fds().set_current_working_directory(cwd.c_str());
77122
storage_vm.setup_linux(
78-
{"storage", "Hello Storage World!"},
123+
storage_args,
79124
{"LC_TYPE=C", "LC_ALL=C", "USER=root"});
80125
storage_vm.run(5.0f);
81126

82127
master_vm.remote_connect(storage_vm, false);
83128

129+
static uint64_t callback_address = 0x0;
130+
master_vm.install_unhandled_syscall_handler(
131+
[] (tinykvm::vCPU& cpu, unsigned sysnum) {
132+
auto& regs = cpu.registers();
133+
switch (sysnum) {
134+
case 0x10001: // Set callback address
135+
callback_address = regs.rdi;
136+
return;
137+
default:
138+
printf("Unhandled master VM syscall: %u\n", sysnum);
139+
regs.rax = -ENOSYS;
140+
cpu.set_registers(regs);
141+
}
142+
});
143+
84144
auto tdiff = timed_action([&] {
85145
try {
86146
master_vm.run();
@@ -107,15 +167,15 @@ int main(int argc, char** argv)
107167
assert(vm.is_remote_connected());
108168

109169
/* Call 'do_calculation' with 21 as argument */
110-
const auto call_addr = vm.address_of("do_calculation");
111-
if (call_addr == 0) {
170+
if (callback_address == 0) {
112171
fprintf(stderr, "Error: no do_calculation() in guest\n");
113172
exit(1);
114173
}
174+
printf("Calling do_calculation() @ 0x%lX\n", callback_address);
115175
for (int i = 0; i < 100; i++)
116-
vm.timed_vmcall(call_addr, 5.0f, 21);
176+
vm.timed_vmcall(callback_address, 5.0f, 21);
117177
auto fork_tdiff = timed_action([&] {
118-
vm.timed_vmcall(call_addr, 5.0f, 21);
178+
vm.timed_vmcall(callback_address, 5.0f, 21);
119179
});
120180
printf("Fork call time: %.2fus Return value: %ld\n", fork_tdiff*1e6, vm.return_value());
121181
}

0 commit comments

Comments
 (0)