AsmGrader 0.0.0
Loading...
Searching...
No Matches
program.hpp
Go to the documentation of this file.
1#pragma once
2
14
15#include <fmt/format.h>
16
17#include <concepts>
18#include <csignal>
19#include <cstddef>
20#include <cstdint>
21#include <filesystem>
22#include <memory>
23#include <optional>
24#include <string>
25#include <string_view>
26#include <vector>
27
28namespace asmgrader {
29
31{
32public:
33 explicit Program(std::filesystem::path path, std::vector<std::string> args = {});
34
35 Program(Program&& other) = default;
36 Program& operator=(Program&& rhs) = default;
37
38 ~Program() = default;
39
41
43 const TracedSubprocess& get_subproc() const;
45 const SymbolTable& get_symtab() const;
46
47 const std::filesystem::path& get_path() const;
48 const std::vector<std::string>& get_args() const;
49
52 template <typename Func, typename... Args>
53 Result<typename FunctionTraits<Func>::Ret> call_function(std::string_view name, Args&&... args);
54
55 template <typename Func, typename... Args>
56 Result<typename FunctionTraits<Func>::Ret> call_function(std::uintptr_t addr, Args&&... args);
57
58 // TODO: Proper allocation (and deallocation!!)
59 std::uintptr_t alloc_mem(std::size_t amt);
60
61 static Expected<void, std::string> check_is_elf(const std::filesystem::path& path);
62
63private:
64 std::filesystem::path path_;
65 std::vector<std::string> args_;
66
67 std::unique_ptr<TracedSubprocess> subproc_;
68 std::unique_ptr<SymbolTable> symtab_;
69
70 std::size_t alloced_mem_{};
71};
72
73template <typename Func, typename... Args>
75 using Ret = typename FunctionTraits<Func>::Ret;
76
77 Tracer& tracer = subproc_->get_tracer();
78 TRY(tracer.setup_function_call(std::forward<Args>(args)...));
79
80 if (auto res = subproc_->get_tracer().get_memory_io().read_bytes(subproc_->get_tracer().get_mmapped_addr(), 32);
81 res) {
82 LOG_TRACE("Memory (32 bytes) at start of mmaped addr (0x{:x}): {::x}",
83 subproc_->get_tracer().get_mmapped_addr(), *res);
84 }
85
86#if defined(ASMGRADER_AARCH64)
87 [[maybe_unused]] std::uintptr_t instr_pointer = TRY(tracer.get_registers()).pc;
88#elif defined(ASMGRADER_X86_64)
89 [[maybe_unused]] std::uintptr_t instr_pointer = TRY(tracer.get_registers()).rip;
90#endif
91 LOG_TRACE("Jumping to: {:#X} from {:#X}", addr, instr_pointer);
92 TRY(tracer.jump_to(addr));
93
94 auto run_res = tracer.run();
95
96 if (!run_res) {
97 return run_res.error();
98 }
99
100 using enum RunResult::Kind;
101
102 // If the subprocess is no longer alive, restart it
103 // TODO: Should instead catch exit condition before it has an effect
104 if (run_res->get_kind() == Exited || run_res->get_kind() == Killed) {
105 TRY(subproc_->restart());
106 }
107
108 // We expect that the function returns to our written instruction, which is essentially a breakpoint
109 // that raises SIGTRAP
110 if (run_res->get_kind() != SignalCaught || run_res->get_code() != SIGTRAP) {
111 LOG_DEBUG("Unexpected return from function: kind={}, code={}", fmt::underlying(run_res->get_kind()),
112 run_res->get_code());
113
114 std::uintptr_t instr_addr =
115#ifdef __aarch64__
116 subproc_->get_tracer().get_registers()->pc;
117#else
118 subproc_->get_tracer().get_registers()->rip;
119#endif
120 if (auto res = subproc_->get_tracer().get_memory_io().read_bytes(instr_addr, 16); res) {
121 LOG_TRACE("Memory (16 bytes) at point of instruction ptr (0x{:x}): {::x}", instr_addr, *res);
122 }
123
124 return ErrorKind::UnexpectedReturn;
125 }
126
127 if constexpr (std::same_as<Ret, void>) {
128 return {};
129 } else {
130 std::optional<Ret> return_val = TRY(tracer.process_function_ret<Ret>());
131
132 if (!return_val) {
133 return ErrorKind::UnknownError;
134 }
135
136 return *return_val;
137 }
138}
139
140template <typename Func, typename... Args>
142 auto symbol = symtab_->find(name);
143 if (!symbol) {
144 return ErrorKind::UnresolvedSymbol;
145 }
146
147 LOG_TRACE("Resolved symbol {:?} at {:#X}", symbol->name, symbol->address);
148
149 return call_function<Func>(symbol->address, std::forward<Args>(args)...);
150}
151
152} // namespace asmgrader
std::variant wrapper for a partial implementation of C++23's expected type
Definition expected.hpp:34
A trivially-movable, but non-copyable type.
Definition class_traits.hpp:14
Definition program.hpp:31
const std::vector< std::string > & get_args() const
Definition program.cpp:104
Program(std::filesystem::path path, std::vector< std::string > args={})
Definition program.cpp:33
Program(Program &&other)=default
Result< typename FunctionTraits< Func >::Ret > call_function(std::string_view name, Args &&... args)
Returns the result of the function call, or nullopt if the symbol name was not found or some other er...
Definition program.hpp:141
SymbolTable & get_symtab()
Definition program.cpp:75
static Expected< void, std::string > check_is_elf(const std::filesystem::path &path)
Definition program.cpp:53
Program & operator=(Program &&rhs)=default
std::uintptr_t alloc_mem(std::size_t amt)
Definition program.cpp:87
Result< RunResult > run()
Definition program.cpp:83
TracedSubprocess & get_subproc()
Definition program.cpp:67
const std::filesystem::path & get_path() const
Definition program.cpp:100
Kind
Definition run_result.hpp:8
A basic symbol table, for simple interaction with a number of symbols loaded from an ELF file.
Definition symbol_table.hpp:14
A subprocess managed by a tracer.
Definition traced_subprocess.hpp:14
A lightweight wrapper of ptrace(2)
Definition tracer.hpp:49
Result< void > jump_to(std::uintptr_t address)
Set the child process's instruction pointer to address
Definition tracer.cpp:187
Result< Ret > process_function_ret()
AFTER a function has been called, inspects register values (and memory if necessary) to construct the...
Definition tracer.hpp:264
Result< RunResult > run()
Run the child process. Collect syscall info each time one is executed.
Definition tracer.cpp:297
Result< user_regs_struct > get_registers() const
Get the general purpose registers of the stopped tracee IMPORTANT: this is (obviously) architecture-d...
Definition tracer.cpp:199
Result< void > setup_function_call(Args &&... args)
Definition tracer.hpp:160
#define TRY(val)
If the supplied argument is an error (unexpected) type, then propegate it up the call stack....
Definition error_types.hpp:46
#define LOG_TRACE(...)
Definition logging.hpp:43
#define LOG_DEBUG(...)
Definition logging.hpp:44
Definition asm_buffer.hpp:19
Definition functional_traits.hpp:14