removes code dupication by unifying elf file read
This commit is contained in:
parent
76ea0db25d
commit
a8f56b6e27
@ -35,6 +35,7 @@
|
|||||||
#ifndef _RISCV_HART_COMMON
|
#ifndef _RISCV_HART_COMMON
|
||||||
#define _RISCV_HART_COMMON
|
#define _RISCV_HART_COMMON
|
||||||
|
|
||||||
|
#include "iss/vm_types.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <elfio/elfio.hpp>
|
#include <elfio/elfio.hpp>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
@ -314,29 +315,33 @@ struct riscv_hart_common {
|
|||||||
riscv_hart_common(){};
|
riscv_hart_common(){};
|
||||||
~riscv_hart_common(){};
|
~riscv_hart_common(){};
|
||||||
std::unordered_map<std::string, uint64_t> symbol_table;
|
std::unordered_map<std::string, uint64_t> symbol_table;
|
||||||
|
uint64_t entry_address{0};
|
||||||
|
uint64_t tohost = tohost_dflt;
|
||||||
|
uint64_t fromhost = fromhost_dflt;
|
||||||
|
|
||||||
std::unordered_map<std::string, uint64_t> get_sym_table(std::string name) {
|
bool read_elf_file(std::string name, uint8_t expected_elf_class, std::function<iss::status(uint64_t, uint64_t, const uint8_t* const)> cb) {
|
||||||
if(!symbol_table.empty())
|
|
||||||
return symbol_table;
|
|
||||||
FILE* fp = fopen(name.c_str(), "r");
|
|
||||||
if(fp) {
|
|
||||||
std::array<char, 5> buf;
|
|
||||||
auto n = fread(buf.data(), 1, 4, fp);
|
|
||||||
fclose(fp);
|
|
||||||
if(n != 4)
|
|
||||||
throw std::runtime_error("input file has insufficient size");
|
|
||||||
buf[4] = 0;
|
|
||||||
if(strcmp(buf.data() + 1, "ELF") == 0) {
|
|
||||||
// Create elfio reader
|
// Create elfio reader
|
||||||
ELFIO::elfio reader;
|
ELFIO::elfio reader;
|
||||||
// Load ELF data
|
// Load ELF data
|
||||||
if(!reader.load(name))
|
if(reader.load(name)) {
|
||||||
throw std::runtime_error("could not process elf file");
|
|
||||||
// check elf properties
|
// check elf properties
|
||||||
|
if(reader.get_class() != expected_elf_class)
|
||||||
|
return false;
|
||||||
if(reader.get_type() != ET_EXEC)
|
if(reader.get_type() != ET_EXEC)
|
||||||
throw std::runtime_error("wrong elf type in file");
|
return false;
|
||||||
if(reader.get_machine() != EM_RISCV)
|
if(reader.get_machine() != EM_RISCV)
|
||||||
throw std::runtime_error("wrong elf machine in file");
|
return false;
|
||||||
|
entry_address = reader.get_entry();
|
||||||
|
for(const auto pseg : reader.segments) {
|
||||||
|
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
|
||||||
|
const auto seg_data = pseg->get_data();
|
||||||
|
const auto type = pseg->get_type();
|
||||||
|
if(type == 1 && fsize > 0) {
|
||||||
|
auto res = cb(pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data));
|
||||||
|
if(res != iss::Ok)
|
||||||
|
CPPLOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address();
|
||||||
|
}
|
||||||
|
}
|
||||||
const auto sym_sec = reader.sections[".symtab"];
|
const auto sym_sec = reader.sections[".symtab"];
|
||||||
if(SHT_SYMTAB == sym_sec->get_type() || SHT_DYNSYM == sym_sec->get_type()) {
|
if(SHT_SYMTAB == sym_sec->get_type() || SHT_DYNSYM == sym_sec->get_type()) {
|
||||||
ELFIO::symbol_section_accessor symbols(reader, sym_sec);
|
ELFIO::symbol_section_accessor symbols(reader, sym_sec);
|
||||||
@ -350,19 +355,26 @@ struct riscv_hart_common {
|
|||||||
unsigned char other = 0;
|
unsigned char other = 0;
|
||||||
for(auto i = 0U; i < sym_no; ++i) {
|
for(auto i = 0U; i < sym_no; ++i) {
|
||||||
symbols.get_symbol(i, name, value, size, bind, type, section, other);
|
symbols.get_symbol(i, name, value, size, bind, type, section, other);
|
||||||
if(name != "") {
|
if(type==0 && name != "") {
|
||||||
this->symbol_table[name] = value;
|
this->symbol_table[name] = value;
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
CPPLOG(DEBUG) << "Found Symbol " << name;
|
CPPLOG(DEBUG) << "Found Symbol " << name;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
tohost = symbol_table.at("tohost");
|
||||||
|
try {
|
||||||
|
fromhost = symbol_table.at("fromhost");
|
||||||
|
} catch(std::out_of_range& e) {
|
||||||
|
fromhost = tohost + 0x40;
|
||||||
}
|
}
|
||||||
return symbol_table;
|
} catch(std::out_of_range& e) {
|
||||||
}
|
}
|
||||||
throw std::runtime_error(fmt::format("memory load file {} is not a valid elf file", name));
|
}
|
||||||
} else
|
return true;
|
||||||
throw std::runtime_error(fmt::format("memory load file not found, check if {} is a valid file", name));
|
}
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include "iss/vm_if.h"
|
#include "iss/vm_if.h"
|
||||||
#include "iss/vm_types.h"
|
#include "iss/vm_types.h"
|
||||||
#include "riscv_hart_common.h"
|
#include "riscv_hart_common.h"
|
||||||
|
#include <elfio/elf_types.hpp>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#ifndef FMT_HEADER_ONLY
|
#ifndef FMT_HEADER_ONLY
|
||||||
#define FMT_HEADER_ONLY
|
#define FMT_HEADER_ONLY
|
||||||
@ -321,7 +322,7 @@ protected:
|
|||||||
|
|
||||||
unsigned get_reg_size(unsigned num) override { return traits<BASE>::reg_bit_widths[num]; }
|
unsigned get_reg_size(unsigned num) override { return traits<BASE>::reg_bit_widths[num]; }
|
||||||
|
|
||||||
std::unordered_map<std::string, uint64_t> get_symbol_table(std::string name) override { return arch.get_sym_table(name); }
|
std::unordered_map<std::string, uint64_t> const& get_symbol_table(std::string name) override { return arch.symbol_table; }
|
||||||
|
|
||||||
riscv_hart_m_p<BASE, FEAT, LOGCAT>& arch;
|
riscv_hart_m_p<BASE, FEAT, LOGCAT>& arch;
|
||||||
};
|
};
|
||||||
@ -343,8 +344,6 @@ protected:
|
|||||||
int64_t instret_offset{0};
|
int64_t instret_offset{0};
|
||||||
uint64_t minstret_csr{0};
|
uint64_t minstret_csr{0};
|
||||||
reg_t fault_data;
|
reg_t fault_data;
|
||||||
uint64_t tohost = tohost_dflt;
|
|
||||||
uint64_t fromhost = fromhost_dflt;
|
|
||||||
bool tohost_lower_written = false;
|
bool tohost_lower_written = false;
|
||||||
riscv_instrumentation_if instr_if;
|
riscv_instrumentation_if instr_if;
|
||||||
|
|
||||||
@ -573,57 +572,15 @@ riscv_hart_m_p<BASE, FEAT, LOGCAT>::riscv_hart_m_p(feature_config cfg)
|
|||||||
|
|
||||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
std::pair<uint64_t, bool> riscv_hart_m_p<BASE, FEAT, LOGCAT>::load_file(std::string name, int type) {
|
std::pair<uint64_t, bool> riscv_hart_m_p<BASE, FEAT, LOGCAT>::load_file(std::string name, int type) {
|
||||||
get_sym_table(name);
|
if(read_elf_file(name,sizeof(reg_t)==4?ELFCLASS32:ELFCLASS64,
|
||||||
try {
|
[this](uint64_t addr, uint64_t size, const uint8_t* const data) -> iss::status {
|
||||||
tohost = symbol_table.at("tohost");
|
return this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, traits<BASE>::MEM,
|
||||||
fromhost = symbol_table.at("fromhost");
|
addr, size, data);
|
||||||
} catch(std::out_of_range& e) {
|
|
||||||
|
})) {
|
||||||
|
return std::make_pair(entry_address, true);
|
||||||
}
|
}
|
||||||
FILE* fp = fopen(name.c_str(), "r");
|
return std::make_pair(entry_address, false);
|
||||||
if(fp) {
|
|
||||||
std::array<char, 5> buf;
|
|
||||||
auto n = fread(buf.data(), 1, 4, fp);
|
|
||||||
fclose(fp);
|
|
||||||
if(n != 4)
|
|
||||||
throw std::runtime_error("input file has insufficient size");
|
|
||||||
buf[4] = 0;
|
|
||||||
if(strcmp(buf.data() + 1, "ELF") == 0) {
|
|
||||||
// Create elfio reader
|
|
||||||
ELFIO::elfio reader;
|
|
||||||
// Load ELF data
|
|
||||||
if(!reader.load(name))
|
|
||||||
throw std::runtime_error("could not process elf file");
|
|
||||||
// check elf properties
|
|
||||||
if(reader.get_class() != ELFCLASS32)
|
|
||||||
if(sizeof(reg_t) == 4)
|
|
||||||
throw std::runtime_error("wrong elf class in file");
|
|
||||||
if(reader.get_type() != ET_EXEC)
|
|
||||||
throw std::runtime_error("wrong elf type in file");
|
|
||||||
if(reader.get_machine() != EM_RISCV)
|
|
||||||
throw std::runtime_error("wrong elf machine in file");
|
|
||||||
auto entry = reader.get_entry();
|
|
||||||
for(const auto& pseg : reader.segments) {
|
|
||||||
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
|
|
||||||
const auto seg_data = pseg->get_data();
|
|
||||||
const auto type = pseg->get_type();
|
|
||||||
if(type == 1 && fsize > 0) {
|
|
||||||
auto res = this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, traits<BASE>::MEM,
|
|
||||||
pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data));
|
|
||||||
if(res != iss::Ok)
|
|
||||||
CPPLOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(const auto& sec : reader.sections) {
|
|
||||||
if(sec->get_name() == ".tohost") {
|
|
||||||
tohost = sec->get_address();
|
|
||||||
fromhost = tohost + 0x40;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::make_pair(entry, true);
|
|
||||||
}
|
|
||||||
throw std::runtime_error(fmt::format("memory load file {} is not a valid elf file", name));
|
|
||||||
}
|
|
||||||
throw std::runtime_error(fmt::format("memory load file not found, check if {} is a valid file", name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
|
@ -371,7 +371,7 @@ protected:
|
|||||||
|
|
||||||
unsigned get_reg_size(unsigned num) override { return traits<BASE>::reg_bit_widths[num]; }
|
unsigned get_reg_size(unsigned num) override { return traits<BASE>::reg_bit_widths[num]; }
|
||||||
|
|
||||||
std::unordered_map<std::string, uint64_t> get_symbol_table(std::string name) override { return arch.get_sym_table(name); }
|
std::unordered_map<std::string, uint64_t> const& get_symbol_table(std::string name) override { return arch.symbol_table; }
|
||||||
|
|
||||||
riscv_hart_msu_vp<BASE>& arch;
|
riscv_hart_msu_vp<BASE>& arch;
|
||||||
};
|
};
|
||||||
@ -393,8 +393,6 @@ protected:
|
|||||||
uint64_t minstret_csr{0};
|
uint64_t minstret_csr{0};
|
||||||
reg_t fault_data;
|
reg_t fault_data;
|
||||||
std::array<vm_info, 2> vm;
|
std::array<vm_info, 2> vm;
|
||||||
uint64_t tohost = tohost_dflt;
|
|
||||||
uint64_t fromhost = fromhost_dflt;
|
|
||||||
bool tohost_lower_written = false;
|
bool tohost_lower_written = false;
|
||||||
riscv_instrumentation_if instr_if;
|
riscv_instrumentation_if instr_if;
|
||||||
|
|
||||||
@ -557,71 +555,15 @@ riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE> std::pair<uint64_t, bool> riscv_hart_msu_vp<BASE>::load_file(std::string name, int type) {
|
template <typename BASE> std::pair<uint64_t, bool> riscv_hart_msu_vp<BASE>::load_file(std::string name, int type) {
|
||||||
FILE* fp = fopen(name.c_str(), "r");
|
if(read_elf_file(name,sizeof(reg_t)==4?ELFCLASS32:ELFCLASS64,
|
||||||
if(fp) {
|
[this](uint64_t addr, uint64_t size, const uint8_t* const data) -> iss::status {
|
||||||
std::array<char, 5> buf;
|
return this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, traits<BASE>::MEM,
|
||||||
auto n = fread(buf.data(), 1, 4, fp);
|
addr, size, data);
|
||||||
fclose(fp);
|
|
||||||
if(n != 4)
|
})) {
|
||||||
throw std::runtime_error("input file has insufficient size");
|
return std::make_pair(entry_address, true);
|
||||||
buf[4] = 0;
|
|
||||||
if(strcmp(buf.data() + 1, "ELF") == 0) {
|
|
||||||
// Create elfio reader
|
|
||||||
ELFIO::elfio reader;
|
|
||||||
// Load ELF data
|
|
||||||
if(!reader.load(name))
|
|
||||||
throw std::runtime_error("could not process elf file");
|
|
||||||
// check elf properties
|
|
||||||
if(reader.get_class() != ELFCLASS32)
|
|
||||||
if(sizeof(reg_t) == 4)
|
|
||||||
throw std::runtime_error("wrong elf class in file");
|
|
||||||
if(reader.get_type() != ET_EXEC)
|
|
||||||
throw std::runtime_error("wrong elf type in file");
|
|
||||||
if(reader.get_machine() != EM_RISCV)
|
|
||||||
throw std::runtime_error("wrong elf machine in file");
|
|
||||||
auto entry = reader.get_entry();
|
|
||||||
for(const auto pseg : reader.segments) {
|
|
||||||
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
|
|
||||||
const auto seg_data = pseg->get_data();
|
|
||||||
const auto type = pseg->get_type();
|
|
||||||
if(type == 1 && fsize > 0) {
|
|
||||||
auto res = this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, traits<BASE>::MEM,
|
|
||||||
pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data));
|
|
||||||
if(res != iss::Ok)
|
|
||||||
CPPLOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address();
|
|
||||||
}
|
}
|
||||||
}
|
return std::make_pair(entry_address, false);
|
||||||
for(const auto sec : reader.sections) {
|
|
||||||
if(sec->get_name() == ".symtab") {
|
|
||||||
if(SHT_SYMTAB == sec->get_type() || SHT_DYNSYM == sec->get_type()) {
|
|
||||||
ELFIO::symbol_section_accessor symbols(reader, sec);
|
|
||||||
auto sym_no = symbols.get_symbols_num();
|
|
||||||
std::string name;
|
|
||||||
ELFIO::Elf64_Addr value = 0;
|
|
||||||
ELFIO::Elf_Xword size = 0;
|
|
||||||
unsigned char bind = 0;
|
|
||||||
unsigned char type = 0;
|
|
||||||
ELFIO::Elf_Half section = 0;
|
|
||||||
unsigned char other = 0;
|
|
||||||
for(auto i = 0U; i < sym_no; ++i) {
|
|
||||||
symbols.get_symbol(i, name, value, size, bind, type, section, other);
|
|
||||||
if(name == "tohost") {
|
|
||||||
tohost = value;
|
|
||||||
} else if(name == "fromhost") {
|
|
||||||
fromhost = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if(sec->get_name() == ".tohost") {
|
|
||||||
tohost = sec->get_address();
|
|
||||||
fromhost = tohost + 0x40;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::make_pair(entry, true);
|
|
||||||
}
|
|
||||||
throw std::runtime_error(fmt::format("memory load file {} is not a valid elf file", name));
|
|
||||||
}
|
|
||||||
throw std::runtime_error(fmt::format("memory load file not found, check if {} is a valid file", name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE>
|
template <typename BASE>
|
||||||
|
@ -348,7 +348,7 @@ protected:
|
|||||||
|
|
||||||
unsigned get_reg_size(unsigned num) override { return traits<BASE>::reg_bit_widths[num]; }
|
unsigned get_reg_size(unsigned num) override { return traits<BASE>::reg_bit_widths[num]; }
|
||||||
|
|
||||||
std::unordered_map<std::string, uint64_t> get_symbol_table(std::string name) override { return arch.get_sym_table(name); }
|
std::unordered_map<std::string, uint64_t> const& get_symbol_table(std::string name) override { return arch.symbol_table; }
|
||||||
|
|
||||||
riscv_hart_mu_p<BASE, FEAT, LOGCAT>& arch;
|
riscv_hart_mu_p<BASE, FEAT, LOGCAT>& arch;
|
||||||
};
|
};
|
||||||
@ -370,8 +370,6 @@ protected:
|
|||||||
int64_t instret_offset{0};
|
int64_t instret_offset{0};
|
||||||
uint64_t minstret_csr{0};
|
uint64_t minstret_csr{0};
|
||||||
reg_t fault_data;
|
reg_t fault_data;
|
||||||
uint64_t tohost = tohost_dflt;
|
|
||||||
uint64_t fromhost = fromhost_dflt;
|
|
||||||
bool tohost_lower_written = false;
|
bool tohost_lower_written = false;
|
||||||
riscv_instrumentation_if instr_if;
|
riscv_instrumentation_if instr_if;
|
||||||
|
|
||||||
@ -651,71 +649,15 @@ riscv_hart_mu_p<BASE, FEAT, LOGCAT>::riscv_hart_mu_p(feature_config cfg)
|
|||||||
|
|
||||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
std::pair<uint64_t, bool> riscv_hart_mu_p<BASE, FEAT, LOGCAT>::load_file(std::string name, int type) {
|
std::pair<uint64_t, bool> riscv_hart_mu_p<BASE, FEAT, LOGCAT>::load_file(std::string name, int type) {
|
||||||
FILE* fp = fopen(name.c_str(), "r");
|
if(read_elf_file(name,sizeof(reg_t)==4?ELFCLASS32:ELFCLASS64,
|
||||||
if(fp) {
|
[this](uint64_t addr, uint64_t size, const uint8_t* const data) -> iss::status {
|
||||||
std::array<char, 5> buf;
|
return this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, traits<BASE>::MEM,
|
||||||
auto n = fread(buf.data(), 1, 4, fp);
|
addr, size, data);
|
||||||
fclose(fp);
|
|
||||||
if(n != 4)
|
})) {
|
||||||
throw std::runtime_error("input file has insufficient size");
|
return std::make_pair(entry_address, true);
|
||||||
buf[4] = 0;
|
|
||||||
if(strcmp(buf.data() + 1, "ELF") == 0) {
|
|
||||||
// Create elfio reader
|
|
||||||
ELFIO::elfio reader;
|
|
||||||
// Load ELF data
|
|
||||||
if(!reader.load(name))
|
|
||||||
throw std::runtime_error("could not process elf file");
|
|
||||||
// check elf properties
|
|
||||||
if(reader.get_class() != ELFCLASS32)
|
|
||||||
if(sizeof(reg_t) == 4)
|
|
||||||
throw std::runtime_error("wrong elf class in file");
|
|
||||||
if(reader.get_type() != ET_EXEC)
|
|
||||||
throw std::runtime_error("wrong elf type in file");
|
|
||||||
if(reader.get_machine() != EM_RISCV)
|
|
||||||
throw std::runtime_error("wrong elf machine in file");
|
|
||||||
auto entry = reader.get_entry();
|
|
||||||
for(const auto& pseg : reader.segments) {
|
|
||||||
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
|
|
||||||
const auto seg_data = pseg->get_data();
|
|
||||||
const auto type = pseg->get_type();
|
|
||||||
if(type == 1 && fsize > 0) {
|
|
||||||
auto res = this->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_WRITE, traits<BASE>::MEM,
|
|
||||||
pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data));
|
|
||||||
if(res != iss::Ok)
|
|
||||||
CPPLOG(ERR) << "problem writing " << fsize << "bytes to 0x" << std::hex << pseg->get_physical_address();
|
|
||||||
}
|
}
|
||||||
}
|
return std::make_pair(entry_address, false);
|
||||||
for(const auto& sec : reader.sections) {
|
|
||||||
if(sec->get_name() == ".symtab") {
|
|
||||||
if(SHT_SYMTAB == sec->get_type() || SHT_DYNSYM == sec->get_type()) {
|
|
||||||
ELFIO::symbol_section_accessor symbols(reader, sec);
|
|
||||||
auto sym_no = symbols.get_symbols_num();
|
|
||||||
std::string name;
|
|
||||||
ELFIO::Elf64_Addr value = 0;
|
|
||||||
ELFIO::Elf_Xword size = 0;
|
|
||||||
unsigned char bind = 0;
|
|
||||||
unsigned char type = 0;
|
|
||||||
ELFIO::Elf_Half section = 0;
|
|
||||||
unsigned char other = 0;
|
|
||||||
for(auto i = 0U; i < sym_no; ++i) {
|
|
||||||
symbols.get_symbol(i, name, value, size, bind, type, section, other);
|
|
||||||
if(name == "tohost") {
|
|
||||||
tohost = value;
|
|
||||||
} else if(name == "fromhost") {
|
|
||||||
fromhost = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if(sec->get_name() == ".tohost") {
|
|
||||||
tohost = sec->get_address();
|
|
||||||
fromhost = tohost + 0x40;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::make_pair(entry, true);
|
|
||||||
}
|
|
||||||
throw std::runtime_error(fmt::format("memory load file {} is not a valid elf file", name));
|
|
||||||
}
|
|
||||||
throw std::runtime_error(fmt::format("memory load file not found, check if {} is a valid file", name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename BASE, features_e FEAT, typename LOGCAT>
|
template <typename BASE, features_e FEAT, typename LOGCAT>
|
||||||
|
Loading…
Reference in New Issue
Block a user