diff --git a/Dockerfile b/Dockerfile index 5e92e3c..1a310f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bookworm-slim AS builder +FROM ubuntu:22.04 AS builder WORKDIR /wanco diff --git a/a.c b/a.c new file mode 100644 index 0000000..5e50a05 --- /dev/null +++ b/a.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int *safepoint = NULL; + +// シグナルハンドラ +void signal_segv_handler(int signo, siginfo_t *info, void *context) { + void *buffer[1000]; + int nptrs; + ucontext_t *ucontext; + ucontext = (ucontext_t *)context; + + nptrs = backtrace(buffer, sizeof(buffer) / sizeof(void *)); + + // バックトレースを標準出力に表示 + backtrace_symbols_fd(buffer, nptrs, fileno(stdout)); + exit(0); +} + +void signal_usr1_handler(int signo) { mprotect(safepoint, 4096, PROT_NONE); } + +// スレッドの処理 +void cause_segfault() { + while (1) { + int hoge = *safepoint; + printf("hoge: %d\n", hoge); + sleep(1); + } +} + +int main() { + // mmap + safepoint = mmap(NULL, 4096, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + *safepoint = 42; + + struct sigaction sa; + + sa.sa_sigaction = signal_segv_handler; + sa.sa_flags = SA_SIGINFO; + sigaction(SIGSEGV, &sa, NULL); + + signal(SIGUSR1, signal_usr1_handler); + + cause_segfault(); + + return 0; +} diff --git a/b.c b/b.c new file mode 100644 index 0000000..6f8b270 --- /dev/null +++ b/b.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include + +int main() { + int efd = eventfd(0, 0); // eventfd作成 + if (efd == -1) { + perror("eventfd"); + return 1; + } + + struct pollfd pfd = { .fd = efd, .events = POLLIN }; + + printf("Waiting for event...\n"); + + if (fork() == 0) { // 子プロセスで通知 + sleep(1); + uint64_t u = 1; + write(efd, &u, sizeof(u)); // カウンタを増やす + return 0; + } + + poll(&pfd, 1, -1); // イベント待機 + uint64_t value; + read(efd, &value, sizeof(value)); // カウンタをリセット + printf("Event received!\n"); + + close(efd); + return 0; +} diff --git a/lib-rt/aot.h b/lib-rt/aot.h index 2e50b23..d581b71 100644 --- a/lib-rt/aot.h +++ b/lib-rt/aot.h @@ -19,7 +19,7 @@ extern "C" Checkpoint chkpt; } // namespace wanco -extern "C" struct ExecEnv { +extern "C" struct __attribute__((packed)) ExecEnv { int8_t *memory_base; int32_t memory_size; wanco::MigrationState migration_state; @@ -30,6 +30,8 @@ extern "C" struct ExecEnv { // defined in wasm AOT module extern "C" const int32_t INIT_MEMORY_SIZE; extern "C" void aot_main(ExecEnv *); +extern "C" void store_globals(ExecEnv *); +extern "C" void store_table(ExecEnv *); // defined in wrt.c extern "C" ExecEnv exec_env; diff --git a/lib-rt/api.cc b/lib-rt/api.cc index 5b0a63e..2d4b6e1 100644 --- a/lib-rt/api.cc +++ b/lib-rt/api.cc @@ -1,17 +1,11 @@ #include "aot.h" -#include "arch/arch.h" #include "chkpt/chkpt.h" -#include "elf/elf.h" -#include "osr/wasm_stacktrace.h" -#include "stackmap/stackmap.h" -#include "stacktrace/stacktrace.h" #include "wanco.h" #include #include #include #include #include -#include #include #include #include @@ -24,10 +18,6 @@ int32_t extend_memory(ExecEnv *exec_env, int32_t inc_pages); } // namespace wanco -// defined by AOT module -extern "C" void store_globals(ExecEnv *); -extern "C" void store_table(ExecEnv *); - extern "C" int32_t memory_grow(ExecEnv *exec_env, int32_t inc_pages) { return wanco::extend_memory(exec_env, inc_pages); } @@ -51,68 +41,6 @@ extern "C" void sleep_msec(ExecEnv *exec_env, int32_t ms) { ** checkpoint related functions */ -extern "C" void start_checkpoint(ExecEnv *exec_env) { - WANCO_SAVE_REGISTERS(); - wanco::CallerSavedRegisters regs{}; - WANCO_RESTORE_REGISTERS(regs); - - // override migration state - exec_env->migration_state = wanco::MigrationState::STATE_CHECKPOINT_CONTINUE; - - // auto regs = wanco::stackmap::CallerSavedRegisters{}; - Info() << "Checkpoint started" << std::endl; - - wanco::ElfFile elf_file{"/proc/self/exe"}; - auto stackmap_section = elf_file.get_section_data(".llvm_stackmaps"); - if (!stackmap_section.has_value()) { - Fatal() << "Failed to get stackmap section" << std::endl; - exit(1); - } - - wanco::stackmap::Stackmap stackmap = - wanco::stackmap::parse_stackmap(stackmap_section.value()); - - const auto native_trace = wanco::get_stack_trace(); - - const auto wasm_trace = wanco::asr_exit(regs, native_trace, stackmap); - - Debug() << "Wasm trace:" << std::endl; - for (const auto &frame : wasm_trace) { - Debug() << frame.to_string() << std::endl; - } - - // store the call stack - for (const auto &frame : wasm_trace) { - wanco::chkpt.frames.push_front(wanco::Frame{ - .fn_index = frame.loc.get_func(), - .pc = frame.loc.get_insn(), - .locals = frame.locals, - .stack = frame.stack, - }); - } - - // store the globals, table, and memory - store_globals(exec_env); - store_table(exec_env); - wanco::chkpt.memory_size = exec_env->memory_size; - - // write snapshot - { - std::ofstream ofs("checkpoint.pb"); - encode_checkpoint_proto(ofs, wanco::chkpt, exec_env->memory_base); - Info() << "Snapshot has been saved to checkpoint.pb" << '\n'; - - auto time = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - time = time - wanco::CHKPT_START_TIME; - // TODO(tamaron): remove this (research purpose) - std::ofstream chktime("chkpt-time.txt"); - chktime << time << '\n'; - } - exit(0); -} - extern "C" void push_frame(ExecEnv *exec_env) { ASSERT(exec_env->migration_state == wanco::MigrationState::STATE_CHECKPOINT_CONTINUE && @@ -288,11 +216,11 @@ void dump_checkpoint(wanco::Checkpoint &chkpt) { */ static void check_restore_finished(ExecEnv *exec_env, bool cond) { - DEBUG_LOG << "Rest frame size: " << std::dec << wanco::chkpt.frames.size() - << std::endl; + DEBUG_LOG << "Rest frame size to restore: " << std::dec + << wanco::chkpt.frames.size() << std::endl; if (cond) { exec_env->migration_state = wanco::MigrationState::STATE_NONE; - DEBUG_LOG << " Restore completed" << std::endl; + DEBUG_LOG << "Restore completed" << std::endl; ASSERT(wanco::chkpt.restore_stack.empty() && "Stack not empty"); ASSERT(wanco::chkpt.frames.empty() && "Frames not empty"); // equivalent to date +%s.%N diff --git a/lib-rt/arch/x86_64.h b/lib-rt/arch/x86_64.h index 162eefa..9f642e5 100644 --- a/lib-rt/arch/x86_64.h +++ b/lib-rt/arch/x86_64.h @@ -2,27 +2,10 @@ #pragma once #include "wanco.h" #include +#include +#include #include - -#define WANCO_SAVE_REGISTERS() \ - asm volatile(".intel_syntax noprefix \n\t" \ - "push rbx \n\t" \ - "push r12 \n\t" \ - "push r13 \n\t" \ - "push r14 \n\t" \ - "push r15 \n\t" \ - ".att_syntax \n\t"); - -#define WANCO_RESTORE_REGISTERS(regs) \ - asm volatile(".intel_syntax noprefix \n\t" \ - "pop %0 \n\t" \ - "pop %1 \n\t" \ - "pop %2 \n\t" \ - "pop %3 \n\t" \ - "pop %4 \n\t" \ - ".att_syntax \n\t" \ - : "=r"((regs).r15), "=r"((regs).r14), "=r"((regs).r13), \ - "=r"((regs).r12), "=r"((regs).rbx)); +#include namespace wanco { @@ -218,6 +201,49 @@ struct CallerSavedRegisters { exit(1); } } + + static CallerSavedRegisters from_unw_cursor(unw_cursor_t *cursor) { + CallerSavedRegisters regs{}; + unw_get_reg(cursor, UNW_X86_64_RBX, ®s.rbx); + unw_get_reg(cursor, UNW_X86_64_R12, ®s.r12); + unw_get_reg(cursor, UNW_X86_64_R13, ®s.r13); + unw_get_reg(cursor, UNW_X86_64_R14, ®s.r14); + unw_get_reg(cursor, UNW_X86_64_R15, ®s.r15); + return regs; + } }; +inline unw_context_t convert_ucontext(ucontext_t *uc) { + unw_context_t unw_ctx; + unw_getcontext(&unw_ctx); + unw_ctx.uc_mcontext.gregs[REG_RAX] = uc->uc_mcontext.gregs[REG_RAX]; + unw_ctx.uc_mcontext.gregs[REG_RDX] = uc->uc_mcontext.gregs[REG_RDX]; + unw_ctx.uc_mcontext.gregs[REG_RCX] = uc->uc_mcontext.gregs[REG_RCX]; + unw_ctx.uc_mcontext.gregs[REG_RBX] = uc->uc_mcontext.gregs[REG_RBX]; + unw_ctx.uc_mcontext.gregs[REG_RSI] = uc->uc_mcontext.gregs[REG_RSI]; + unw_ctx.uc_mcontext.gregs[REG_RDI] = uc->uc_mcontext.gregs[REG_RDI]; + unw_ctx.uc_mcontext.gregs[REG_RBP] = uc->uc_mcontext.gregs[REG_RBP]; + unw_ctx.uc_mcontext.gregs[REG_RSP] = uc->uc_mcontext.gregs[REG_RSP]; + unw_ctx.uc_mcontext.gregs[REG_R8] = uc->uc_mcontext.gregs[REG_R8]; + unw_ctx.uc_mcontext.gregs[REG_R9] = uc->uc_mcontext.gregs[REG_R9]; + unw_ctx.uc_mcontext.gregs[REG_R10] = uc->uc_mcontext.gregs[REG_R10]; + unw_ctx.uc_mcontext.gregs[REG_R11] = uc->uc_mcontext.gregs[REG_R11]; + unw_ctx.uc_mcontext.gregs[REG_R12] = uc->uc_mcontext.gregs[REG_R12]; + unw_ctx.uc_mcontext.gregs[REG_R13] = uc->uc_mcontext.gregs[REG_R13]; + unw_ctx.uc_mcontext.gregs[REG_R14] = uc->uc_mcontext.gregs[REG_R14]; + unw_ctx.uc_mcontext.gregs[REG_R15] = uc->uc_mcontext.gregs[REG_R15]; + unw_ctx.uc_mcontext.gregs[REG_RIP] = uc->uc_mcontext.gregs[REG_RIP]; + unw_ctx.uc_mcontext.gregs[REG_RSP] = uc->uc_mcontext.gregs[REG_RSP]; + unw_ctx.uc_mcontext.fpregs = &uc->__fpregs_mem; + + unw_ctx.uc_stack.ss_sp = uc->uc_stack.ss_sp; + unw_ctx.uc_stack.ss_size = uc->uc_stack.ss_size; + unw_ctx.uc_stack.ss_flags = uc->uc_stack.ss_flags; + + unw_ctx.uc_link = uc->uc_link; + unw_ctx.uc_sigmask = uc->uc_sigmask; + + return unw_ctx; +} + } // namespace wanco diff --git a/lib-rt/chkpt/chkpt.h b/lib-rt/chkpt/chkpt.h index a918868..55c73d4 100644 --- a/lib-rt/chkpt/chkpt.h +++ b/lib-rt/chkpt/chkpt.h @@ -80,6 +80,7 @@ class Checkpoint { } void prepare_restore() { + Info() << "Loading value stack" << '\n'; restore_stack.clear(); for (auto &frame : frames) { for (auto &value : frame.stack) { @@ -92,6 +93,7 @@ class Checkpoint { std::pair decode_checkpoint_proto(std::ifstream &f); -void encode_checkpoint_proto(std::ofstream &ofs, Checkpoint &chkpt, int8_t *memory_base); +void encode_checkpoint_proto(std::ofstream &ofs, Checkpoint &chkpt, + int8_t *memory_base); } // namespace wanco diff --git a/lib-rt/osr/asr_exit.cc b/lib-rt/osr/asr_exit.cc index 64afebb..ac6cbf8 100644 --- a/lib-rt/osr/asr_exit.cc +++ b/lib-rt/osr/asr_exit.cc @@ -24,6 +24,8 @@ using stackmap_table = // func => stackmap_record[] static stackmap_table populate_stackmap(const stackmap::Stackmap &stackmap) { stackmap_table map; + Info() << "Populating stackmaps: " << std::dec + << stackmap.stkmap_records.size() << " records" << '\n'; for (auto &record : stackmap.stkmap_records) { auto id = record->patchpoint_id; @@ -38,6 +40,8 @@ static stackmap_table populate_stackmap(const stackmap::Stackmap &stackmap) { auto &ent = it->second; ent.push_back(record); } + Debug() << "Found stackmap record for func_" << std::dec << loc.get_func() + << ", pc_offset=" << std::dec << record->instruction_offset << '\n'; } // sort all entries by the first element @@ -72,6 +76,7 @@ lookup_stackmap(stackmap_table &map, int32_t func_index, int32_t pc_offset) { Debug() << "Instead, found a record for pc_offset=" << std::dec << record->instruction_offset << '\n'; } + Warn() << "Assuming the record is empty" << '\n'; return std::nullopt; } diff --git a/lib-rt/stackmap/stackmap.cc b/lib-rt/stackmap/stackmap.cc index a344174..cf78e17 100644 --- a/lib-rt/stackmap/stackmap.cc +++ b/lib-rt/stackmap/stackmap.cc @@ -110,6 +110,8 @@ auto parse_stackmap(const std::span data) -> Stackmap { uint32_t const num_functions = parse_u32(ptr); uint32_t const num_constants = parse_u32(ptr); uint32_t const num_records = parse_u32(ptr); + Info() << "Expects " << num_functions << " functions, " << num_constants + << " constants, and " << num_records << " records" << '\n'; std::vector stksize_records; stksize_records.reserve(num_functions); @@ -130,6 +132,11 @@ auto parse_stackmap(const std::span data) -> Stackmap { std::make_shared(parse_stk_map_record(ptr))); } + ASSERT(num_functions == stksize_records.size() && + "Invalid number of records"); + ASSERT(num_constants == constants.size() && "Invalid number of constants"); + ASSERT(num_records == stkmap_records.size() && "Invalid number of records"); + return Stackmap{.header = header, .num_functions = num_functions, .num_constants = num_constants, diff --git a/lib-rt/stacktrace/stacktrace.cc b/lib-rt/stacktrace/stacktrace.cc index d778f08..8f5c5a4 100644 --- a/lib-rt/stacktrace/stacktrace.cc +++ b/lib-rt/stacktrace/stacktrace.cc @@ -1,7 +1,10 @@ #include "stacktrace.h" +#include "arch/arch.h" +#include "arch/x86_64.h" #include "wanco.h" #include #include +#include #include #include @@ -12,27 +15,32 @@ namespace wanco { -auto get_stack_trace() -> std::deque { +unw_context_t uc; + +int save_context(ucontext_t *c) { + uc = convert_ucontext(c); + return 0; +} + +// pre-condition: wanco_save_context() must be called before this function +auto get_stack_trace() + -> std::pair, CallerSavedRegisters> { std::deque trace; // initialize libunwind - unw_context_t context; - if (unw_getcontext(&context) != 0) { - Fatal() << "Failed to get context" << '\n'; - exit(EXIT_FAILURE); - } unw_cursor_t cursor; - if (unw_init_local(&cursor, &context) != 0) { + if (unw_init_local(&cursor, &uc) != 0) { Fatal() << "Failed to initialize cursor" << '\n'; exit(EXIT_FAILURE); } + CallerSavedRegisters regs = CallerSavedRegisters::from_unw_cursor(&cursor); do { unw_word_t offset = 0; char fname[64]; fname[0] = '\0'; (void)unw_get_proc_name(&cursor, fname, sizeof(fname), &offset); - std::string const function_name = {fname}; + std::string const function_name = {fname, strlen(fname)}; // Get pc. unw_word_t pc = 0; @@ -54,7 +62,7 @@ auto get_stack_trace() -> std::deque { .bp = reinterpret_cast(bp), }); } while (unw_step(&cursor) > 0); - return trace; + return {trace, regs}; } } // namespace wanco diff --git a/lib-rt/stacktrace/stacktrace.h b/lib-rt/stacktrace/stacktrace.h index 072d2e9..05a848e 100644 --- a/lib-rt/stacktrace/stacktrace.h +++ b/lib-rt/stacktrace/stacktrace.h @@ -1,22 +1,26 @@ #pragma once +#include "arch/arch.h" #include "wanco.h" #include +#include namespace wanco { struct NativeStackFrame { - // Function name. - std::string function_name; - // Address offset from the beginning of the function. - uint64_t pc_offset; - // program counter - uint64_t pc; - // stack pointer - uint8_t* sp; - // base pointer - uint8_t* bp; + // Function name. + std::string function_name; + // Address offset from the beginning of the function. + uint64_t pc_offset; + // program counter + uint64_t pc; + // stack pointer + uint8_t *sp; + // base pointer + uint8_t *bp; }; -std::deque get_stack_trace(); +int save_context(ucontext_t *); -} +std::pair, CallerSavedRegisters> get_stack_trace(); + +} // namespace wanco diff --git a/lib-rt/wanco.h b/lib-rt/wanco.h index 93cc44c..b9949dd 100644 --- a/lib-rt/wanco.h +++ b/lib-rt/wanco.h @@ -13,7 +13,7 @@ namespace wanco { constexpr bool USE_LZ4 = false; -constexpr bool DEBUG_ENABLED = false; +constexpr bool DEBUG_ENABLED = true; extern uint64_t CHKPT_START_TIME; extern uint64_t RESTORE_START_TIME; diff --git a/lib-rt/wrt.cc b/lib-rt/wrt.cc index 505d6e5..9b0344d 100644 --- a/lib-rt/wrt.cc +++ b/lib-rt/wrt.cc @@ -1,12 +1,19 @@ #include "aot.h" #include "chkpt/chkpt.h" +#include "elf/elf.h" +#include "osr/wasm_stacktrace.h" +#include "stackmap/stackmap.h" +#include "stacktrace/stacktrace.h" #include "wanco.h" #include #include #include #include +#include +#include #include #include +#include #include #include @@ -21,11 +28,18 @@ uint64_t RESTORE_START_TIME = 0; // global instance of checkpoint Checkpoint chkpt; +int efd = 0; + // linear memory: 4GiB -static constexpr uint64_t LINEAR_MEMORY_BEGIN = 0x100000000000; -static constexpr uint64_t MAX_LINEAR_MEMORY_SIZE = 0x400000; // 4GiB -// guard page: 2GiB -static constexpr uint64_t GUARD_PAGE_SIZE = 0x200000; +static constexpr uint64_t GUARD_PAGE_BEGIN = 0xA0000000; +static constexpr uint64_t GUARD_PAGE_END = 0xA0010000; +static constexpr uint64_t LINEAR_MEMORY_BEGIN = 0xA0010000; +// static constexpr uint64_t MAX_LINEAR_MEMORY_SIZE = 0x400000; +static constexpr uint64_t GUARD_PAGE2_BEGIN = 0xA0050000; +static constexpr uint64_t GUARD_PAGE2_END = 0xA00600000; + +static constexpr uint64_t POLLING_PAGE_BEGIN = 0xA0060000; +static constexpr uint64_t POLLING_PAGE_END = 0xA0061000; static std::string_view USAGE = R"(WebAssembly AOT executable USAGE: [options] -- [arguments] @@ -37,29 +51,57 @@ USAGE: [options] -- [arguments] )"; // forward decl -//static void dump_exec_env(ExecEnv &exec_env); -//static void dump_checkpoint(Checkpoint &chkpt); +static void start_checkpoint(); -extern "C" auto memory_grow(ExecEnv *exec_env, int32_t inc_pages) -> int32_t; +static void signal_segv_handler(int signum, siginfo_t *info, void *context) { + int err = save_context((ucontext_t *)context); + ASSERT(err == 0 && "Failed to save context"); + exec_env.migration_state = MigrationState::STATE_CHECKPOINT_START; + uint64_t val = 1; + write(efd, &val, sizeof(val)); + while (1) { + sleep(1000); + } +} -// signal handler for debugging -static void signal_segv_handler(int signum) { - void *array[10]; - size_t size = 0; - ASSERT(signum == SIGSEGV && "Unexpected signal"); +static void signal_chkpt_handler(int signum) { + uint64_t val = 1; + write(efd, &val, sizeof(val)); +} - // get void*'s for all entries on the stack - size = backtrace(array, 10); +void *supervisor_thread(void *arg) { + ASSERT(efd != 0 && efd != -1 && "efd not initialized"); + struct pollfd pfd = {.fd = efd, .events = POLLIN}; + for (int i = 0; i < 2; i++) { + if (poll(&pfd, 1, -1) == -1) { // イベント待機 + perror("poll"); + close(efd); + exit(1); + } - // print out all the frames to stderr - fprintf(stderr, "Error: segmentation fault\n"); - backtrace_symbols_fd(array, size, STDERR_FILENO); - exit(1); -} + if (pfd.revents & POLLIN) { + uint64_t value; + // reset the counter + read(efd, &value, sizeof(value)); + Info() << "Event received! Value: " << std::dec << value << '\n'; + + if (i == 0) { + // mprotect the polling page + int err = mprotect((void *)POLLING_PAGE_BEGIN, + POLLING_PAGE_END - POLLING_PAGE_BEGIN, PROT_NONE); + ASSERT(err == 0 && "Failed to mprotect polling page"); + // This is necessary to flush the TLB. + // FIXME: I have no idea why it works if the following line is commented + // out. + + // asm volatile("invlpg (%0)" ::"r"(exec_env.polling_page) : "memory"); + } else { + start_checkpoint(); + } + } + } -static void signal_chkpt_handler(int signum) { - ASSERT(signum == SIGCHKPT && "Unexpected signal"); - exec_env.migration_state = MigrationState::STATE_CHECKPOINT_START; + return NULL; } struct Config { @@ -77,17 +119,17 @@ auto allocate_memory(int32_t num_pages) -> int8_t * { // Add guard pages Info() << "Allocating guard pages" << '\n'; - if (mmap((void *)(LINEAR_MEMORY_BEGIN - GUARD_PAGE_SIZE), - (GUARD_PAGE_SIZE * 2) + MAX_LINEAR_MEMORY_SIZE, PROT_NONE, - MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0) == nullptr) { + if (mmap((void *)GUARD_PAGE_BEGIN, GUARD_PAGE_END - GUARD_PAGE_BEGIN, + PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0) == nullptr) { + Fatal() << "Failed to allocate guard pages" << '\n'; + } + + if (mmap((void *)GUARD_PAGE2_BEGIN, GUARD_PAGE2_END - GUARD_PAGE2_BEGIN, + PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0) == nullptr) { Fatal() << "Failed to allocate guard pages" << '\n'; } // Allocate linear memory - if (munmap((void *)LINEAR_MEMORY_BEGIN, num_bytes) < 0) { - Fatal() << "Failed to unmap part of guard pages" << '\n'; - exit(1); - }; auto *res = static_cast(mmap((void *)LINEAR_MEMORY_BEGIN, num_bytes, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); @@ -96,13 +138,14 @@ auto allocate_memory(int32_t num_pages) -> int8_t * { << " bytes to linear memory" << '\n'; exit(1); } - Info() << "Allocating linear memory: " << num_pages - << " pages, starting at 0x" << std::hex << (uint64_t)res << '\n'; -// Zero out memory + // Zero out memory #ifdef __FreeBSD__ std::memset(res, 0, num_bytes); #endif + Info() << "Allocating linear memory: " << num_pages + << " pages, starting at 0x" << std::hex << (uint64_t)res << '\n'; + return res; } @@ -165,73 +208,54 @@ static auto parse_from_args(int argc, char **argv) -> Config { return config; } -static auto wanco_main(int argc, char **argv) -> int { - signal(SIGSEGV, signal_segv_handler); +static void start_checkpoint() { + Info() << "Checkpoint started" << std::endl; + exec_env.migration_state = MigrationState::STATE_CHECKPOINT_CONTINUE; - // Parse CLI arguments - Config const config = parse_from_args(argc, argv); + ElfFile elf_file{"/proc/self/exe"}; + auto stackmap_section = elf_file.get_section_data(".llvm_stackmaps"); + if (!stackmap_section.has_value()) { + Fatal() << "Failed to get stackmap section" << std::endl; + exit(1); + } - if (config.restore_file.empty()) { - // Allocate memory - int const memory_size = INIT_MEMORY_SIZE; - int8_t *memory = allocate_memory(memory_size); - // Initialize exec_env - exec_env = ExecEnv{ - .memory_base = memory, - .memory_size = memory_size, - .migration_state = MigrationState::STATE_NONE, - .argc = argc, - .argv = reinterpret_cast(argv), - }; - } else { - RESTORE_START_TIME = - std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); + stackmap::Stackmap stackmap = + stackmap::parse_stackmap(stackmap_section.value()); + Info() << "Parsed stackmap:" << std::dec << stackmap.stkmap_records.size() + << " records" << std::endl; - // Restore from checkpoint - std::ifstream ifs(config.restore_file); - if (!ifs.is_open()) { - Fatal() << "Failed to open checkpoint file: " << config.restore_file - << '\n'; - return 1; - } + const auto [native_trace, regs] = get_stack_trace(); + for (const auto &frame : native_trace) { + Debug() << frame.function_name << " + " << std::dec << frame.pc_offset + << std::endl; + } - int8_t *memory = nullptr; - if (!config.restore_file.ends_with(".pb")) { - Warn() << "The file does not have a .pb extension. " - "Attempting to parse as proto." - << '\n'; - } - auto p = decode_checkpoint_proto(ifs); - chkpt = p.first; - memory = p.second; - chkpt.prepare_restore(); - Info() << "Checkpoint has been loaded" << '\n'; - Info() << "- call stack: " << chkpt.frames.size() << " frames" << '\n'; - Info() << "- value stack: " << chkpt.restore_stack.size() << " values" - << '\n'; + const auto wasm_trace = asr_exit(regs, native_trace, stackmap); - // Initialize exec_env - exec_env = ExecEnv{ - .memory_base = memory, - .memory_size = chkpt.memory_size, - .migration_state = MigrationState::STATE_RESTORE, - .argc = argc, - .argv = reinterpret_cast(argv), - }; + Debug() << "Wasm trace:" << std::endl; + for (const auto &frame : wasm_trace) { + Debug() << frame.to_string() << std::endl; } - // Register signal handler - signal(SIGCHKPT, signal_chkpt_handler); - aot_main(&exec_env); + // store the call stack + for (const auto &frame : wasm_trace) { + wanco::chkpt.frames.push_front(wanco::Frame{ + .fn_index = frame.loc.get_func(), + .pc = frame.loc.get_insn(), + .locals = frame.locals, + .stack = frame.stack, + }); + } - if (exec_env.migration_state == MigrationState::STATE_CHECKPOINT_CONTINUE) { - chkpt.memory_size = exec_env.memory_size; + // store the globals, table, and memory + store_globals(&exec_env); + store_table(&exec_env); + wanco::chkpt.memory_size = exec_env.memory_size; - // write snapshot + // write snapshot + { std::ofstream ofs("checkpoint.pb"); - encode_checkpoint_proto(ofs, chkpt, exec_env.memory_base); + encode_checkpoint_proto(ofs, wanco::chkpt, exec_env.memory_base); Info() << "Snapshot has been saved to checkpoint.pb" << '\n'; auto time = std::chrono::duration_cast( @@ -242,6 +266,135 @@ static auto wanco_main(int argc, char **argv) -> int { std::ofstream chktime("chkpt-time.txt"); chktime << time << '\n'; } + exit(0); +} + +static void allocate_polling_page() { + void *polling_page = + mmap((void *)POLLING_PAGE_BEGIN, POLLING_PAGE_END - POLLING_PAGE_BEGIN, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (polling_page == MAP_FAILED) { + Fatal() << "Failed to mmap a polling page" << '\n'; + exit(1); + } +} + +static void setup_signal_handlers() { + signal(SIGCHKPT, signal_chkpt_handler); + + struct sigaction sa; + sa.sa_sigaction = signal_segv_handler; + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + sigaction(SIGSEGV, &sa, NULL); +} + +static void setup_supervisor_thread() { + efd = eventfd(0, 0); + if (efd == -1) { + perror("eventfd"); + exit(1); + } + + // spawn a thread which checks if the main thread is suspended and performs + // checkpoint + pthread_t tid; + int err = pthread_create(&tid, NULL, supervisor_thread, NULL); + if (err != 0) { + Fatal() << "Failed to create supervisor thread" << '\n'; + exit(1); + } +} + +static ExecEnv init_env(int argc, char **argv) { + // Allocate memory + int const memory_size = INIT_MEMORY_SIZE; + int8_t *memory = allocate_memory(memory_size); + // Initialize exec_env + ExecEnv exec_env = ExecEnv{ + .memory_base = memory, + .memory_size = memory_size, + .migration_state = MigrationState::STATE_NONE, + .argc = argc, + .argv = reinterpret_cast(argv), + }; + return exec_env; +} + +static ExecEnv restore_exec_env(const std::string &restore_file, int argc, + char **argv) { + // Restore from checkpoint + std::ifstream ifs{restore_file}; + if (!ifs.is_open()) { + Fatal() << "Failed to open checkpoint file: " << restore_file << '\n'; + exit(1); + } + + int8_t *memory = nullptr; + if (!restore_file.ends_with(".pb")) { + Warn() << "The file does not have a .pb extension. " + "Attempting to parse as proto." + << '\n'; + } + auto p = decode_checkpoint_proto(ifs); + chkpt = p.first; + memory = p.second; + chkpt.prepare_restore(); + Info() << "Checkpoint has been loaded" << '\n'; + Info() << "- call stack: " << chkpt.frames.size() << " frames" << '\n'; + Info() << "- value stack: " << chkpt.restore_stack.size() << " values" + << '\n'; + + // Initialize exec_env + ExecEnv exec_env = ExecEnv{ + .memory_base = memory, + .memory_size = chkpt.memory_size, + .migration_state = MigrationState::STATE_RESTORE, + .argc = argc, + .argv = reinterpret_cast(argv), + }; + return exec_env; +} + +static void finalize_legacy_checkpoint() { + chkpt.memory_size = exec_env.memory_size; + + // write snapshot + std::ofstream ofs("checkpoint.pb"); + encode_checkpoint_proto(ofs, chkpt, exec_env.memory_base); + Info() << "Snapshot has been saved to checkpoint.pb" << '\n'; + + auto time = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + time = time - wanco::CHKPT_START_TIME; + // TODO(tamaron): remove this (research purpose) + std::ofstream chktime("chkpt-time.txt"); + chktime << time << '\n'; + chktime.close(); +} + +static auto wanco_main(int argc, char **argv) -> int { + setup_signal_handlers(); + setup_supervisor_thread(); + allocate_polling_page(); + Config const config = parse_from_args(argc, argv); + + if (config.restore_file.empty()) { + exec_env = init_env(argc, argv); + } else { + RESTORE_START_TIME = + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + exec_env = restore_exec_env(config.restore_file, argc, argv); + } + + aot_main(&exec_env); + + if (exec_env.migration_state == MigrationState::STATE_CHECKPOINT_CONTINUE) { + finalize_legacy_checkpoint(); + } // cleanup munmap(exec_env.memory_base, exec_env.memory_size * PAGE_SIZE); diff --git a/lib-wasi/src/lib.rs b/lib-wasi/src/lib.rs index e9df284..89a5141 100644 --- a/lib-wasi/src/lib.rs +++ b/lib-wasi/src/lib.rs @@ -14,16 +14,18 @@ use std::sync::{Mutex, OnceLock}; static CTX: OnceLock> = OnceLock::new(); static RUNTIME: OnceLock = OnceLock::new(); -#[repr(C)] +#[repr(C, packed)] pub(crate) struct ExecEnv { memory: *mut u8, memory_size: i32, migration_state: i32, argc: i32, argv: *mut *mut c_char, + safepoint: *const i8, } pub(crate) fn memory<'a>(exec_env: &'a ExecEnv) -> wiggle::GuestMemory<'a> { + // FIXME: incorrect memory size let memory_size = 4 * 1024 * 1024; let slice = unsafe { slice::from_raw_parts_mut(exec_env.memory, memory_size) }; let cell_slice: &[UnsafeCell] = unsafe { &*(slice as *mut [u8] as *mut [UnsafeCell]) }; diff --git a/memo.md b/memo.md new file mode 100644 index 0000000..4499334 --- /dev/null +++ b/memo.md @@ -0,0 +1,11 @@ + +bb1: + call func2() + stackmap(...) + br ret + +ret: + ret + +この場合optimized outされる +- func2はtail callだから? \ No newline at end of file diff --git a/wanco/src/compile/cr/checkpoint.rs b/wanco/src/compile/cr/checkpoint.rs index 219e396..b31a9a3 100644 --- a/wanco/src/compile/cr/checkpoint.rs +++ b/wanco/src/compile/cr/checkpoint.rs @@ -416,11 +416,13 @@ pub(crate) fn generate_stackmap<'a>( ctx: &mut Context<'a, '_>, locals: &[(PointerValue<'a>, BasicTypeEnum<'a>)], ) -> Result<()> { + // We use llvm.experimental.patchpoint instead of llvm.experimental.stackmap so that we can prevent it + // from being optimized away by LLVM. let func_idx = ctx.current_function_idx.unwrap(); let insn_offset = ctx.current_op.unwrap(); let stackmap_id = stackmap::stackmap_id(func_idx, insn_offset); - // Prepare arguments for llvm.experimental.stackmap + // Prepare arguments for llvm.experimental.patchpoint let mut args = vec![ // stackmap id ctx.inkwell_types @@ -429,6 +431,19 @@ pub(crate) fn generate_stackmap<'a>( .into(), // numShadowBytes is 0. ctx.inkwell_types.i32_type.const_zero().into(), + /* + // target function is nullptr. + ctx.inkwell_types.ptr_type.const_null().into(), + // numArgs equals to 1 + 2 * numLocals + 2 * numStack. + ctx.inkwell_types + .i32_type + .const_int( + 1 + 2 * locals.len() as u64 + + 2 * ctx.stack_frames.last().unwrap().stack.len() as u64, + false, + ) + .into(), + */ ]; // the number of locals follows. diff --git a/wanco/src/compile/cr/mod.rs b/wanco/src/compile/cr/mod.rs index e59b727..b6deaee 100644 --- a/wanco/src/compile/cr/mod.rs +++ b/wanco/src/compile/cr/mod.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use checkpoint::gen_checkpoint_start; +use checkpoint::generate_stackmap; use inkwell::{ types::BasicTypeEnum, values::{BasicValue, BasicValueEnum, PointerValue}, @@ -12,13 +12,15 @@ pub(crate) mod checkpoint; pub(crate) mod restore; //pub(crate) const MIGRATION_STATE_NONE: i32 = 0; -pub(crate) const MIGRATION_STATE_CHECKPOINT_START: i32 = 1; +//pub(crate) const MIGRATION_STATE_CHECKPOINT_START: i32 = 1; pub(crate) const MIGRATION_STATE_CHECKPOINT_CONTINUE: i32 = 2; pub(crate) const MIGRATION_STATE_RESTORE: i32 = 3; pub(crate) const MAX_LOCALS_STORE: usize = 10000; pub(crate) const MAX_STACK_STORE: usize = 10000; +const POLLING_PAGE_BEGIN: i64 = 0xA0060000; + fn gen_migration_state<'a>( ctx: &mut Context<'a, '_>, exec_env_ptr: &PointerValue<'a>, @@ -110,14 +112,29 @@ fn gen_set_migration_state<'a>( Ok(()) } -// almost equiavalent call both to gen_checkpoint and gen_restore, but emit more efficient code -// by wrapping them in a single conditional branch -// TODO: 最後のブロックと、ローカル変数、スタックを返す pub(crate) fn gen_migration_point<'a>( ctx: &mut Context<'a, '_>, exec_env_ptr: &PointerValue<'a>, locals: &[(PointerValue<'a>, BasicTypeEnum<'a>)], ) -> Result<()> { + // TODO: change these to a single load instruction + + let safepoint_ptr = ctx + .inkwell_types + .i64_type + .const_int(POLLING_PAGE_BEGIN as u64, false) + .const_to_pointer(ctx.inkwell_types.ptr_type); + + let safepoint = + ctx.builder + .build_load(ctx.inkwell_types.i32_type, safepoint_ptr, "safepoint")?; + let load_insn = safepoint.as_instruction_value().unwrap(); + load_insn.set_volatile(true).expect("fail to set_volatile"); + + generate_stackmap(ctx, locals)?; + + // TODO: restore + /* let chkpt_bb = ctx.ictx.append_basic_block( ctx.current_fn.unwrap(), &format!("chkpt_op_{}.start", ctx.current_op.unwrap()), @@ -140,14 +157,17 @@ pub(crate) fn gen_migration_point<'a>( // start unwinding gen_checkpoint_start(ctx, exec_env_ptr, locals).expect("fail to gen_checkpoint"); + */ // restore (create new bb) if !ctx.config.no_restore { + let current_bb = ctx.builder.get_insert_block().unwrap(); + let phi_bb = ctx.ictx.append_basic_block( ctx.current_fn.unwrap(), &format!("restore_op_{}.end", ctx.current_op.unwrap()), ); - ctx.builder.position_at_end(chkpt_else_bb); + ctx.builder.position_at_end(current_bb); ctx.builder.build_unconditional_branch(phi_bb).unwrap(); gen_restore_point( @@ -160,8 +180,6 @@ pub(crate) fn gen_migration_point<'a>( ); ctx.builder.position_at_end(phi_bb); - } else { - ctx.builder.position_at_end(chkpt_else_bb); } Ok(()) } diff --git a/wanco/src/compile/synthesize.rs b/wanco/src/compile/synthesize.rs index b2ddeba..91b1b65 100644 --- a/wanco/src/compile/synthesize.rs +++ b/wanco/src/compile/synthesize.rs @@ -27,7 +27,7 @@ pub fn initialize(ctx: &mut Context<'_, '_>) -> anyhow::Result<()> { ctx.inkwell_types.i32_type.into(), ctx.inkwell_types.ptr_type.into(), ], - false, + true, ); ctx.exec_env_type = Some(exec_env_type); ctx.exec_env_fields = exec_env_fields; diff --git a/wanco/src/driver.rs b/wanco/src/driver.rs index 7b792f6..2764090 100644 --- a/wanco/src/driver.rs +++ b/wanco/src/driver.rs @@ -195,6 +195,7 @@ impl<'a> AotWasmModule<'a> { .arg("-o") .arg(&exe_path) .arg("-no-pie") + .arg("-rdynamic") .arg(format!("-{}", args.optimization)); // link protobuf to the exe @@ -285,9 +286,7 @@ pub fn compile_and_link(wasm: &[u8], args: &Args) -> Result<()> { } // Write and Link object files - log::info!("Linking the object files"); let exe_path = aot_module.link_with_runtime(args)?; - log::info!("Linked to {}", exe_path.display()); Ok(()) } diff --git a/wanco/src/inkwell.rs b/wanco/src/inkwell.rs index 58e1718..d7c74f4 100644 --- a/wanco/src/inkwell.rs +++ b/wanco/src/inkwell.rs @@ -43,6 +43,7 @@ pub struct InkwellIntrinsics<'ctx> { pub expect_i32: FunctionValue<'ctx>, pub experimental_stackmap: FunctionValue<'ctx>, + pub experimental_patchpoint: FunctionValue<'ctx>, } pub fn init_inkwell<'a>( @@ -113,6 +114,20 @@ pub fn init_inkwell<'a>( None, ); + let experimental_patchpoint = module.add_function( + "llvm.experimental.patchpoint.void", + void_type.fn_type( + &[ + i64_type_meta, + i32_type_meta, + ptr_type.into(), + i32_type_meta, + ], + true, + ), + None, + ); + ( InkwellTypes { void_type, @@ -153,6 +168,7 @@ pub fn init_inkwell<'a>( expect_i32, experimental_stackmap, + experimental_patchpoint, }, ) } diff --git a/wasm.s b/wasm.s new file mode 100644 index 0000000..98700f0 --- /dev/null +++ b/wasm.s @@ -0,0 +1,430 @@ + .text + .file "wanco_aot" + .globl aot_main # -- Begin function aot_main + .p2align 4, 0x90 + .type aot_main,@function +aot_main: # @aot_main + .cfi_startproc +# %bb.0: # %entry + pushq %rax + .cfi_def_cfa_offset 16 + movl 12(%rdi), %eax + callq func_3@PLT + popq %rax + .cfi_def_cfa_offset 8 + retq +.Lfunc_end0: + .size aot_main, .Lfunc_end0-aot_main + .cfi_endproc + # -- End function + .globl func_2 # -- Begin function func_2 + .p2align 4, 0x90 + .type func_2,@function +func_2: # @func_2 + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + pushq %r14 + pushq %rbx + subq $16, %rsp + .cfi_offset %rbx, -32 + .cfi_offset %r14, -24 + movq %rdi, %rbx + movl %esi, -20(%rbp) + movl %edx, -24(%rbp) + movl 12(%rdi), %eax + cmpl $3, %eax + jne .LBB1_4 +# %bb.1: # %restore.dispatch + movq %rbx, %rdi + callq get_pc_from_frame@PLT + movq %rbx, %rdi + cmpl $2, %eax + je .LBB1_10 +# %bb.2: # %restore.dispatch + cmpl $5, %eax + jne .LBB1_9 +# %bb.3: # %restore_op_5.start + callq pop_front_local_i32@PLT + movl %eax, %r14d + movq %rbx, %rdi + callq pop_front_local_i32@PLT + movl %r14d, -20(%rbp) + movl %eax, -24(%rbp) + movq %rbx, %rdi + callq pop_front_frame@PLT + xorl %esi, %esi + jmp .LBB1_7 +.LBB1_4: # %main + movq 28(%rbx), %rax + movl (%rax), %eax +.Ltmp0: + jmp .LBB1_5 + nopw 8(%rax,%rax) +.LBB1_10: # %restore_op_2.start + callq pop_front_local_i32@PLT + movl %eax, %r14d + movq %rbx, %rdi + callq pop_front_local_i32@PLT + movl %r14d, -20(%rbp) + movl %eax, -24(%rbp) + movq %rbx, %rdi + callq pop_front_frame@PLT + xorl %esi, %esi + jmp .LBB1_6 +.LBB1_9: # %restore_op_7.start + callq pop_front_local_i32@PLT + movl %eax, %r14d + movq %rbx, %rdi + callq pop_front_local_i32@PLT + movl %r14d, -20(%rbp) + movl %eax, -24(%rbp) + movq %rbx, %rdi + callq pop_front_frame@PLT + xorl %esi, %esi + jmp .LBB1_8 +.LBB1_5: # %loop.body + movq 28(%rbx), %rax + movl (%rax), %eax +.Ltmp1: + movl -20(%rbp), %esi + nopl 8(%rax,%rax) +.LBB1_6: # %non_leaf_op_2_restore.end + movq %rbx, %rdi + callq print_i32@PLT +.Ltmp2: + movq (%rbx), %rax + movl 5(%rax), %esi + xchgw %ax, %ax +.LBB1_7: # %non_leaf_op_5_restore.end + movq %rbx, %rdi + callq print_i32@PLT +.Ltmp3: + movl $1000, %esi # imm = 0x3E8 + nopl (%rax) +.LBB1_8: # %non_leaf_op_7_restore.end + movq %rbx, %rdi + callq sleep_msec@PLT +.Ltmp4: + movl -24(%rbp), %eax + addl %eax, -20(%rbp) + jmp .LBB1_5 +.Lfunc_end1: + .size func_2, .Lfunc_end1-func_2 + .cfi_endproc + # -- End function + .globl func_3 # -- Begin function func_3 + .p2align 4, 0x90 + .type func_3,@function +func_3: # @func_3 + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + pushq %rbx + pushq %rax + .cfi_offset %rbx, -24 + movq %rdi, %rbx + movl 12(%rdi), %eax + cmpl $3, %eax + jne .LBB2_2 +# %bb.1: # %restore.dispatch + movq %rbx, %rdi + callq get_pc_from_frame@PLT + movq %rbx, %rdi + callq pop_front_frame@PLT + xorl %edx, %edx + jmp .LBB2_3 +.LBB2_2: # %main + movq 28(%rbx), %rax + movl (%rax), %eax +.Ltmp5: + movq (%rbx), %rax + movl $10, 5(%rax) + movl $1, %edx +.LBB2_3: # %non_leaf_op_5_restore.end + movq %rbx, %rdi + xorl %esi, %esi + callq func_2@PLT +.Ltmp6: + addq $8, %rsp + popq %rbx + popq %rbp + .cfi_def_cfa %rsp, 8 + retq + nop +.Lfunc_end2: + .size func_3, .Lfunc_end2-func_3 + .cfi_endproc + # -- End function + .globl store_globals # -- Begin function store_globals + .p2align 4, 0x90 + .type store_globals,@function +store_globals: # @store_globals + .cfi_startproc +# %bb.0: # %entry + retq +.Lfunc_end3: + .size store_globals, .Lfunc_end3-store_globals + .cfi_endproc + # -- End function + .globl store_table # -- Begin function store_table + .p2align 4, 0x90 + .type store_table,@function +store_table: # @store_table + .cfi_startproc +# %bb.0: # %entry + retq +.Lfunc_end4: + .size store_table, .Lfunc_end4-store_table + .cfi_endproc + # -- End function + .type INIT_MEMORY_SIZE,@object # @INIT_MEMORY_SIZE + .section .rodata,"a",@progbits + .globl INIT_MEMORY_SIZE + .p2align 2, 0x0 +INIT_MEMORY_SIZE: + .long 1 # 0x1 + .size INIT_MEMORY_SIZE, 4 + + .section .llvm_stackmaps,"a",@progbits +__LLVM_StackMaps: + .byte 3 + .byte 0 + .short 0 + .long 2 + .long 0 + .long 7 + .quad func_2 + .quad 40 + .quad 5 + .quad func_3 + .quad 24 + .quad 2 + .quad 12884901887 + .long .Ltmp0-func_2 + .short 0 + .short 5 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 2 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 0 + .byte 2 + .byte 0 + .short 8 + .short 6 + .short 0 + .long -20 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 0 + .byte 2 + .byte 0 + .short 8 + .short 6 + .short 0 + .long -24 + .p2align 3, 0x0 + .short 0 + .short 0 + .p2align 3, 0x0 + .quad 8589934592 + .long .Ltmp1-func_2 + .short 0 + .short 5 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 2 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 0 + .byte 2 + .byte 0 + .short 8 + .short 6 + .short 0 + .long -20 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 0 + .byte 2 + .byte 0 + .short 8 + .short 6 + .short 0 + .long -24 + .p2align 3, 0x0 + .short 0 + .short 0 + .p2align 3, 0x0 + .quad 8589934594 + .long .Ltmp2-func_2 + .short 0 + .short 5 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 2 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 0 + .byte 2 + .byte 0 + .short 8 + .short 6 + .short 0 + .long -20 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 0 + .byte 2 + .byte 0 + .short 8 + .short 6 + .short 0 + .long -24 + .p2align 3, 0x0 + .short 0 + .short 0 + .p2align 3, 0x0 + .quad 8589934597 + .long .Ltmp3-func_2 + .short 0 + .short 5 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 2 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 0 + .byte 2 + .byte 0 + .short 8 + .short 6 + .short 0 + .long -20 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 0 + .byte 2 + .byte 0 + .short 8 + .short 6 + .short 0 + .long -24 + .p2align 3, 0x0 + .short 0 + .short 0 + .p2align 3, 0x0 + .quad 8589934599 + .long .Ltmp4-func_2 + .short 0 + .short 5 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 2 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 0 + .byte 2 + .byte 0 + .short 8 + .short 6 + .short 0 + .long -20 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 0 + .byte 2 + .byte 0 + .short 8 + .short 6 + .short 0 + .long -24 + .p2align 3, 0x0 + .short 0 + .short 0 + .p2align 3, 0x0 + .quad 17179869183 + .long .Ltmp5-func_3 + .short 0 + .short 1 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 0 + .p2align 3, 0x0 + .short 0 + .short 0 + .p2align 3, 0x0 + .quad 12884901893 + .long .Ltmp6-func_3 + .short 0 + .short 1 + .byte 4 + .byte 0 + .short 8 + .short 0 + .short 0 + .long 0 + .p2align 3, 0x0 + .short 0 + .short 0 + .p2align 3, 0x0 + + .section ".note.GNU-stack","",@progbits