feat: initial support
This commit is contained in:
parent
dd2feddfd6
commit
5ddf0b48be
13 changed files with 449 additions and 42 deletions
|
@ -1,2 +1,3 @@
|
|||
add_executable(test main.cpp)
|
||||
|
||||
add_executable(diffu cli.cpp difftest.cpp loader.cpp main.cpp)
|
||||
target_link_libraries(diffu PRIVATE gdbstub)
|
||||
set_target_properties(diffu PROPERTIES ENABLE_EXPORTS 1)
|
||||
|
|
25
src/cli.cpp
Normal file
25
src/cli.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "config.hpp"
|
||||
#include <CLI/App.hpp>
|
||||
#include <CLI/Validators.hpp>
|
||||
|
||||
int Config::cli_parse(int argc, char **argv) {
|
||||
CLI::App app;
|
||||
app.add_option("-m,--memory", memory_file, "Content of memory")
|
||||
->required()
|
||||
->check(CLI::ExistingFile);
|
||||
|
||||
app.add_option("--ref", refs, "Reference dynamic library")
|
||||
->required()
|
||||
->check(CLI::ExistingFile);
|
||||
|
||||
app.add_option("--dut", dut, "Design under test")
|
||||
->required()
|
||||
->check(CLI::ExistingFile);
|
||||
|
||||
app.set_config("-c,--config")
|
||||
->transform(CLI::FileOnDefaultPath("./difftest.toml"));
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
return 0;
|
||||
}
|
98
src/difftest.cpp
Normal file
98
src/difftest.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
#include "api.hpp"
|
||||
#include <difftest.hpp>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
Difftest::Difftest(Target &&dut, std::vector<Target> &&refs) {
|
||||
this->dut = std::move(dut);
|
||||
this->refs = std::move(refs);
|
||||
|
||||
for (const auto &ref : refs) {
|
||||
if (dut.arch.reg_byte != ref.arch.reg_byte ||
|
||||
dut.arch.reg_num != ref.arch.reg_num) {
|
||||
throw std::runtime_error("Ref and dut must have the same architecture");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Difftest::setup(const std::filesystem::path &memory_file) {
|
||||
std::ifstream is = std::ifstream(memory_file, std::ios::binary);
|
||||
|
||||
// Seek to the end to determine the file size
|
||||
is.seekg(0, std::ios::end);
|
||||
std::streampos memsize = is.tellg();
|
||||
is.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<char> membuf(memsize);
|
||||
is.read(membuf.data(), memsize);
|
||||
is.close();
|
||||
|
||||
// Initialize memory
|
||||
// TODO: reset vector should not be hardcoded
|
||||
// for(auto target : *this) {
|
||||
for (auto it = this->begin(); it != this->end(); ++it) {
|
||||
auto &target = *it;
|
||||
printf("init addr: %p\n", target.ops.init);
|
||||
target.ops.init(target.args.data());
|
||||
target.ops.write_mem(target.args.data(), 0x80000000UL, membuf.size(),
|
||||
membuf.data());
|
||||
target.ops.write_reg(target.args.data(), 32, 0x80000000UL);
|
||||
}
|
||||
}
|
||||
|
||||
bool Difftest::check_all() {
|
||||
for (auto &ref : refs) {
|
||||
check(dut, ref);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
gdb_action_t Difftest::stepi() {
|
||||
bool breakflag = false;
|
||||
Target *pbreak;
|
||||
for (auto it = this->begin(); it != this->end(); ++it) {
|
||||
auto &target = *it;
|
||||
target.ops.stepi(target.args.data(), &target.last_res);
|
||||
if (target.is_on_breakpoint()) {
|
||||
breakflag = true;
|
||||
pbreak = ⌖
|
||||
}
|
||||
}
|
||||
|
||||
if (breakflag) {
|
||||
gdb_action_t ret = {.reason = gdb_action_t::ACT_BREAKPOINT};
|
||||
pbreak->ops.read_reg(pbreak->args.data(), 32, &ret.data);
|
||||
return ret;
|
||||
}
|
||||
return {gdb_action_t::ACT_NONE, 0};
|
||||
}
|
||||
|
||||
gdb_action_t Difftest::cont() {
|
||||
bool breakflag = false;
|
||||
Target *pbreak;
|
||||
check_all();
|
||||
std::cerr << "setup finished." << std::endl;
|
||||
while (true) {
|
||||
// for(auto &target : *this) {
|
||||
for (auto it = this->begin(); it != this->end(); ++it) {
|
||||
auto &target = *it;
|
||||
target.ops.stepi(target.args.data(), &target.last_res);
|
||||
|
||||
if (target.is_on_breakpoint()) {
|
||||
breakflag = true;
|
||||
pbreak = ⌖
|
||||
}
|
||||
}
|
||||
|
||||
check_all();
|
||||
|
||||
if (breakflag) {
|
||||
gdb_action_t ret = {.reason = gdb_action_t::ACT_BREAKPOINT};
|
||||
pbreak->ops.read_reg(pbreak->args.data(), 32, &ret.data);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return {gdb_action_t::ACT_NONE, 0};
|
||||
}
|
76
src/loader.cpp
Normal file
76
src/loader.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include "api.hpp"
|
||||
#include <cstdint>
|
||||
#include <dlfcn.h>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
Target::Target(const std::string &name, const std::string &func_prefix,
|
||||
const std::filesystem::path &path) {
|
||||
|
||||
std::cout << path.c_str() << std::endl;
|
||||
meta = {.name = name,
|
||||
.libpath = path,
|
||||
.dlhandle = dlopen(path.c_str(), RTLD_LAZY)};
|
||||
|
||||
if (!meta.dlhandle) {
|
||||
throw std::runtime_error(dlerror());
|
||||
}
|
||||
|
||||
#define LOAD_SYMBOL(ops, handle, prefix, name) \
|
||||
do { \
|
||||
ops.name = reinterpret_cast<decltype(TargetOps::name)>( \
|
||||
dlsym(handle, (prefix + #name).c_str())); \
|
||||
if (!ops.name) \
|
||||
goto load_error; \
|
||||
} while (0);
|
||||
|
||||
LOAD_SYMBOL(ops, meta.dlhandle, func_prefix, cont);
|
||||
LOAD_SYMBOL(ops, meta.dlhandle, func_prefix, stepi);
|
||||
LOAD_SYMBOL(ops, meta.dlhandle, func_prefix, read_reg);
|
||||
LOAD_SYMBOL(ops, meta.dlhandle, func_prefix, write_reg);
|
||||
LOAD_SYMBOL(ops, meta.dlhandle, func_prefix, read_mem);
|
||||
LOAD_SYMBOL(ops, meta.dlhandle, func_prefix, write_mem);
|
||||
LOAD_SYMBOL(ops, meta.dlhandle, func_prefix, set_bp);
|
||||
LOAD_SYMBOL(ops, meta.dlhandle, func_prefix, del_bp);
|
||||
LOAD_SYMBOL(ops, meta.dlhandle, func_prefix, on_interrupt);
|
||||
LOAD_SYMBOL(ops, meta.dlhandle, func_prefix, init);
|
||||
|
||||
#undef LOAD_SYMBOL
|
||||
|
||||
size_t *argsize_sym;
|
||||
argsize_sym = reinterpret_cast<size_t *>(dlsym(meta.dlhandle, "argsize"));
|
||||
if (!argsize_sym)
|
||||
goto load_error;
|
||||
|
||||
argsize = *argsize_sym;
|
||||
args = std::vector<uint8_t>(argsize);
|
||||
|
||||
arch_info_t *arch_sym;
|
||||
arch_sym =
|
||||
reinterpret_cast<arch_info_t *>(dlsym(meta.dlhandle, "isa_arch_info"));
|
||||
if (!arch_sym)
|
||||
goto load_error;
|
||||
return;
|
||||
|
||||
load_error:
|
||||
std::string err = std::string(dlerror());
|
||||
dlclose(meta.dlhandle);
|
||||
throw std::runtime_error(err);
|
||||
}
|
||||
|
||||
Target::~Target() {
|
||||
std::cout << "Destruct target " << meta.name << std::endl;
|
||||
dlclose(meta.dlhandle);
|
||||
}
|
||||
|
||||
bool Target::is_on_breakpoint() const { return is_on_breakpoint(last_res); }
|
||||
|
||||
bool Target::is_on_breakpoint(const gdb_action_t &res) const {
|
||||
if (res.reason == gdb_action_t::ACT_BREAKPOINT ||
|
||||
res.reason == gdb_action_t::ACT_RWATCH ||
|
||||
res.reason == gdb_action_t::ACT_WATCH ||
|
||||
res.reason == gdb_action_t::ACT_WWATCH) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
24
src/main.cpp
24
src/main.cpp
|
@ -1,5 +1,23 @@
|
|||
#include <difftest.hpp>
|
||||
#include "api.hpp"
|
||||
#include "config.hpp"
|
||||
#include "difftest.hpp"
|
||||
|
||||
using reg_t = uint32_t;
|
||||
int main(int argc, char **argv) {
|
||||
Config config;
|
||||
int ret = 0;
|
||||
ret = config.cli_parse(argc, argv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
int main() { return 0; }
|
||||
std::vector<Target> refs;
|
||||
Target dut = Target{"dut", "nemu_", config.dut};
|
||||
for (const auto &ref_libpath : config.refs) {
|
||||
refs.emplace_back(ref_libpath.string(), "nemu_", ref_libpath);
|
||||
}
|
||||
|
||||
Difftest difftest{std::move(dut), std::move(refs)};
|
||||
difftest.setup(config.memory_file);
|
||||
difftest.cont();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue