Skip to content

Commit d36f2bf

Browse files
committed
Project: switch to libelfin
1 parent e5661a7 commit d36f2bf

File tree

6 files changed

+112
-172
lines changed

6 files changed

+112
-172
lines changed

CMakeLists.txt

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -226,30 +226,11 @@ if ("${WASM}")
226226
else ()
227227
# Not available for WASM
228228
enable_testing()
229-
230-
if (NOT "${FORCE_ELFLIB_STATIC}")
231-
find_package(LibElf)
232-
if ("${LibElf_FOUND}")
233-
include(CheckSymbolExists)
234-
check_symbol_exists(EM_RISCV "gelf.h" LIBELF_HAS_RISCV)
235-
if ("${LIBELF_HAS_RISCV}")
236-
# Turn non-cmake library into a cmake target
237-
add_library(libelf INTERFACE)
238-
target_link_libraries(libelf INTERFACE ${LIBELF_LIBRARY})
239-
target_include_directories(libelf INTERFACE ${LIBELF_INCLUDE_DIR})
240-
message(STATUS "Using system libelf")
241-
else ()
242-
message(STATUS "System libelf does not support RISC-V")
243-
set(LibElf_FOUND FALSE) # Force fallback
244-
endif ()
245-
endif ()
246-
endif ()
247229
endif ()
248230

249-
if ("${WASM}" OR "${FORCE_ELFLIB_STATIC}" OR NOT "${LibElf_FOUND}")
250-
message(STATUS "Using local libelf fallback.")
251-
add_subdirectory("external/libelf")
252-
endif ()
231+
set(CMAKE_POLICY_VERSION_MINIMUM 3.5)
232+
set(CMAKE_PROJECT_libelfin_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibElfinSettings.cmake")
233+
add_subdirectory("external/libelfin")
253234

254235
# Detect Qt used qt version
255236
# Based on article https://www.steinzone.de/wordpress/how-to-support-both-qt5-and-qt6-using-cmake/

cmake/FindPythonInterp.cmake

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Compatibility wrapper for CMake > 3.12 where FindPythonInterp is deprecated/removed
2+
# This allows external projects using the old module to work with newer CMake and Python3
3+
4+
find_package(Python3 COMPONENTS Interpreter QUIET)
5+
6+
if(Python3_Interpreter_FOUND)
7+
set(PYTHONINTERP_FOUND TRUE)
8+
set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE})
9+
set(PYTHON_VERSION_STRING ${Python3_VERSION})
10+
set(PYTHON_VERSION_MAJOR ${Python3_VERSION_MAJOR})
11+
set(PYTHON_VERSION_MINOR ${Python3_VERSION_MINOR})
12+
set(PYTHON_VERSION_PATCH ${Python3_VERSION_PATCH})
13+
14+
if(NOT PythonInterp_FIND_QUIETLY)
15+
message(STATUS "Found PythonInterp (via Python3): ${PYTHON_EXECUTABLE} (found version \"${PYTHON_VERSION_STRING}\")")
16+
endif()
17+
else()
18+
if(PythonInterp_FIND_REQUIRED)
19+
message(FATAL_ERROR "Could NOT find PythonInterp (missing: PYTHON_EXECUTABLE)")
20+
endif()
21+
endif()

cmake/LibElfinSettings.cmake

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
if(POLICY CMP0148)
2+
cmake_policy(SET CMP0148 OLD)
3+
endif()
4+
5+
# Libelfin requires Threads for dwarf++ library
6+
find_package(Threads REQUIRED)
7+

src/machine/CMakeLists.txt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ add_library(machine STATIC
7777
${machine_HEADERS})
7878
target_link_libraries(machine
7979
PRIVATE ${QtLib}::Core
80-
PUBLIC libelf)
80+
PUBLIC elf++ dwarf++)
81+
target_include_directories(machine
82+
PUBLIC "${PROJECT_SOURCE_DIR}/external/libelfin")
8183

8284
if(NOT ${WASM})
8385
# Machine tests (not available on WASM)
@@ -181,7 +183,9 @@ if(NOT ${WASM})
181183
symboltable.h
182184
)
183185
target_link_libraries(program_loader_test
184-
PRIVATE ${QtLib}::Core ${QtLib}::Test libelf)
186+
PRIVATE ${QtLib}::Core ${QtLib}::Test elf++ dwarf++)
187+
target_include_directories(program_loader_test
188+
PRIVATE "${PROJECT_SOURCE_DIR}/external/libelfin")
185189
add_test(NAME program_loader COMMAND program_loader_test)
186190

187191

@@ -217,7 +221,9 @@ if(NOT ${WASM})
217221
machineconfig.cpp
218222
)
219223
target_link_libraries(core_test
220-
PRIVATE ${QtLib}::Core ${QtLib}::Test libelf)
224+
PRIVATE ${QtLib}::Core ${QtLib}::Test elf++ dwarf++)
225+
target_include_directories(core_test
226+
PRIVATE "${PROJECT_SOURCE_DIR}/external/libelfin")
221227
add_test(NAME core COMMAND core_test)
222228

223229
add_custom_target(machine_unit_tests

src/machine/programloader.cpp

Lines changed: 69 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -4,150 +4,104 @@
44
#include "common/logging.h"
55
#include "simulator_exception.h"
66

7-
#include <cerrno>
8-
#include <cstring>
97
#include <exception>
8+
#include <stdexcept>
9+
#include <sys/types.h>
1010

1111
LOG_CATEGORY("machine.ProgramLoader");
1212

13-
// TODO - document
14-
#ifndef O_BINARY
15-
#define O_BINARY 0
16-
#endif
17-
1813
using namespace machine;
1914

20-
ProgramLoader::ProgramLoader(const QString &file) : elf_file(file) {
21-
const GElf_Ehdr *elf_ehdr;
22-
// Initialize elf library
23-
if (elf_version(EV_CURRENT) == EV_NONE) {
24-
throw SIMULATOR_EXCEPTION(Input, "Elf library initialization failed", elf_errmsg(-1));
25-
}
26-
// Open source file - option QIODevice::ExistingOnly cannot be used on Qt
27-
// <5.11
28-
if (!elf_file.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) {
29-
throw SIMULATOR_EXCEPTION(
30-
Input,
31-
QString("Can't open input elf file for reading (") + QString(file) + QString(")"),
32-
std::strerror(errno));
33-
}
34-
// Initialize elf
35-
if (!(this->elf = elf_begin(elf_file.handle(), ELF_C_READ, nullptr))) {
36-
throw SIMULATOR_EXCEPTION(Input, "Elf read begin failed", elf_errmsg(-1));
15+
constexpr int EM_RISCV = 243;
16+
17+
class MemLoader : public elf::loader {
18+
public:
19+
MemLoader(const QString &fname) : file(fname) {
20+
if (!file.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) {
21+
throw SIMULATOR_EXCEPTION(
22+
Input, QString("Can't open input elf file for reading (") + fname + QString(")"),
23+
file.errorString());
24+
}
25+
size = file.size();
26+
mapped = file.map(0, size);
27+
if (mapped == nullptr) {
28+
throw SIMULATOR_EXCEPTION(
29+
Input, QString("Can't mmap input elf file (") + fname + QString(")"),
30+
file.errorString());
31+
}
3732
}
38-
// Check elf kind
39-
if (elf_kind(this->elf) != ELF_K_ELF) {
40-
throw SIMULATOR_EXCEPTION(
41-
Input, "Invalid input file elf format, plain elf file expected", "");
33+
34+
const void *load(off_t offset, size_t len) override {
35+
if ((size_t)offset + len > (size_t)size) {
36+
throw SANITY_EXCEPTION("ELF loader requested offset exceeds file size");
37+
}
38+
return mapped + offset;
4239
}
4340

44-
elf_ehdr = gelf_getehdr(this->elf, &this->hdr);
45-
if (!elf_ehdr) {
46-
throw SIMULATOR_EXCEPTION(Input, "Getting elf file header failed", elf_errmsg(-1));
41+
private:
42+
QFile file;
43+
unsigned char *mapped;
44+
std::int64_t size;
45+
};
46+
47+
ProgramLoader::ProgramLoader(const QString &file) {
48+
try {
49+
elf_file = elf::elf(std::make_shared<MemLoader>(file));
50+
} catch (const std::exception &e) {
51+
throw SIMULATOR_EXCEPTION(Input, "Elf library initialization failed", e.what());
4752
}
4853

49-
executable_entry = Address(elf_ehdr->e_entry);
50-
// Check elf file format, executable expected, nothing else.
51-
if (this->hdr.e_type != ET_EXEC) {
54+
const auto &hdr = elf_file.get_hdr();
55+
executable_entry = Address(hdr.entry);
56+
57+
if (hdr.type != elf::et::exec) {
5258
throw SIMULATOR_EXCEPTION(Input, "Invalid input file type", "");
5359
}
54-
// Check elf file architecture, of course only mips is supported.
55-
// Note: This also checks that this is big endian as EM_MIPS is suppose to
56-
// be: MIPS R3000 big-endian
57-
if (this->hdr.e_machine != EM_RISCV) {
60+
61+
if (hdr.machine != EM_RISCV) {
5862
throw SIMULATOR_EXCEPTION(Input, "Invalid input file architecture", "");
5963
}
60-
// Check elf file class, only 32bit architecture is supported.
61-
int elf_class;
62-
if ((elf_class = gelf_getclass(this->elf)) == ELFCLASSNONE) {
63-
throw SIMULATOR_EXCEPTION(Input, "Getting elf class failed", elf_errmsg(-1));
64-
}
65-
// Get number of program sections in elf file
66-
if (elf_getphdrnum(this->elf, &this->n_secs)) {
67-
throw SIMULATOR_EXCEPTION(Input, "Elf program sections count query failed", elf_errmsg(-1));
68-
}
6964

70-
if (elf_class == ELFCLASS32) {
65+
if (hdr.ei_class == elf::elfclass::_32) {
7166
LOG("Loaded executable: 32bit");
7267
architecture_type = ARCH32;
73-
// Get program sections headers
74-
if (!(sections_headers.arch32 = elf32_getphdr(elf))) {
75-
throw SIMULATOR_EXCEPTION(Input, "Elf program sections get failed", elf_errmsg(-1));
76-
}
77-
// We want only LOAD sections so we create load_sections_indexes of those sections
78-
for (unsigned i = 0; i < n_secs; i++) {
79-
if (sections_headers.arch32[i].p_type != PT_LOAD) { continue; }
80-
indexes_of_load_sections.push_back(i);
81-
}
82-
} else if (elf_class == ELFCLASS64) {
68+
} else if (hdr.ei_class == elf::elfclass::_64) {
8369
LOG("Loaded executable: 64bit");
8470
architecture_type = ARCH64;
8571
WARN("64bit simulation is not fully supported.");
86-
// Get program sections headers
87-
if (!(sections_headers.arch64 = elf64_getphdr(elf))) {
88-
throw SIMULATOR_EXCEPTION(Input, "Elf program sections get failed", elf_errmsg(-1));
89-
}
90-
// We want only LOAD sections so we create load_sections_indexes of those sections
91-
for (unsigned i = 0; i < this->n_secs; i++) {
92-
if (sections_headers.arch64[i].p_type != PT_LOAD) { continue; }
93-
this->indexes_of_load_sections.push_back(i);
94-
}
95-
9672
} else {
97-
WARN("Unsupported elf class: %d", elf_class);
9873
throw SIMULATOR_EXCEPTION(
9974
Input,
10075
"Unsupported architecture type."
10176
"This simulator only supports 32bit and 64bit CPUs.",
10277
"");
10378
}
79+
80+
for (const auto &seg : elf_file.segments()) {
81+
if (seg.get_hdr().type == elf::pt::load) { load_segments.push_back(seg); }
82+
}
10483
}
10584

10685
ProgramLoader::ProgramLoader(const char *file) : ProgramLoader(QString::fromLocal8Bit(file)) {}
10786

108-
ProgramLoader::~ProgramLoader() {
109-
// Close elf
110-
elf_end(this->elf);
111-
// Close file
112-
elf_file.close();
113-
}
87+
ProgramLoader::~ProgramLoader() {}
11488

11589
void ProgramLoader::to_memory(Memory *mem) {
116-
// Load program to memory (just dump it byte by byte)
117-
if (architecture_type == ARCH32) {
118-
for (size_t phdrs_i : this->indexes_of_load_sections) {
119-
uint32_t base_address = this->sections_headers.arch32[phdrs_i].p_vaddr;
120-
char *f = elf_rawfile(this->elf, nullptr);
121-
for (unsigned y = 0; y < this->sections_headers.arch32[phdrs_i].p_filesz; y++) {
122-
const auto buffer = (uint8_t)f[this->sections_headers.arch32[phdrs_i].p_offset + y];
123-
memory_write_u8(mem, base_address + y, buffer);
124-
}
125-
}
126-
} else if (architecture_type == ARCH64) {
127-
for (size_t phdrs_i : this->indexes_of_load_sections) {
128-
uint32_t base_address = this->sections_headers.arch64[phdrs_i].p_vaddr;
129-
char *f = elf_rawfile(this->elf, nullptr);
130-
for (unsigned y = 0; y < this->sections_headers.arch64[phdrs_i].p_filesz; y++) {
131-
const auto buffer = (uint8_t)f[this->sections_headers.arch64[phdrs_i].p_offset + y];
132-
memory_write_u8(mem, base_address + y, buffer);
133-
}
90+
for (const auto &seg : load_segments) {
91+
uint64_t base_address = seg.get_hdr().vaddr;
92+
const char *data = (const char *)seg.data();
93+
size_t filesz = seg.get_hdr().filesz;
94+
for (size_t i = 0; i < filesz; i++) {
95+
memory_write_u8(mem, base_address + i, (uint8_t)data[i]);
13496
}
13597
}
13698
}
13799

138100
Address ProgramLoader::end() {
139-
uint32_t last = 0;
140-
// Go trough all sections and found out last one
141-
if (architecture_type == ARCH32) {
142-
for (size_t i : this->indexes_of_load_sections) {
143-
Elf32_Phdr *phdr = &(this->sections_headers.arch32[i]);
144-
if ((phdr->p_vaddr + phdr->p_filesz) > last) { last = phdr->p_vaddr + phdr->p_filesz; }
145-
}
146-
} else if (architecture_type == ARCH64) {
147-
for (size_t i : this->indexes_of_load_sections) {
148-
Elf64_Phdr *phdr = &(this->sections_headers.arch64[i]);
149-
if ((phdr->p_vaddr + phdr->p_filesz) > last) { last = phdr->p_vaddr + phdr->p_filesz; }
150-
}
101+
uint64_t last = 0;
102+
for (const auto &seg : load_segments) {
103+
uint64_t end_addr = seg.get_hdr().vaddr + seg.get_hdr().filesz;
104+
if (end_addr > last) { last = end_addr; }
151105
}
152106
return Address(last + 0x10); // We add offset so we are sure that also
153107
// pipeline is empty TODO propagate address
@@ -160,42 +114,21 @@ Address ProgramLoader::get_executable_entry() const {
160114

161115
SymbolTable *ProgramLoader::get_symbol_table() {
162116
auto *p_st = new SymbolTable();
163-
Elf_Scn *scn = nullptr;
164-
GElf_Shdr shdr;
165-
Elf_Data *data;
166-
int count, ii;
167-
168-
elf_version(EV_CURRENT);
169-
170-
while (true) {
171-
if ((scn = elf_nextscn(this->elf, scn)) == nullptr) { return p_st; }
172-
gelf_getshdr(scn, &shdr);
173-
if (shdr.sh_type == SHT_SYMTAB) {
174-
/* found a symbol table, go print it. */
175-
break;
117+
for (const auto &sec : elf_file.sections()) {
118+
if (sec.get_hdr().type == elf::sht::symtab) {
119+
for (const auto &sym : sec.as_symtab()) {
120+
const auto &d = sym.get_data();
121+
p_st->add_symbol(sym.get_name().c_str(), d.value, d.size, d.info, d.other);
122+
}
176123
}
177124
}
178-
179-
data = elf_getdata(scn, nullptr);
180-
count = shdr.sh_size / shdr.sh_entsize;
181-
182-
/* retrieve the symbol names */
183-
for (ii = 0; ii < count; ++ii) {
184-
GElf_Sym sym;
185-
gelf_getsym(data, ii, &sym);
186-
p_st->add_symbol(
187-
elf_strptr(elf, shdr.sh_link, sym.st_name), sym.st_value, sym.st_size, sym.st_info,
188-
sym.st_other);
189-
}
190-
191125
return p_st;
192126
}
127+
193128
Endian ProgramLoader::get_endian() const {
194-
// Reading elf endian_id_byte according to the ELF specs.
195-
unsigned char endian_id_byte = this->hdr.e_ident[EI_DATA];
196-
if (endian_id_byte == ELFDATA2LSB) {
129+
if (elf_file.get_hdr().ei_data == elf::elfdata::lsb) {
197130
return LITTLE;
198-
} else if (endian_id_byte == ELFDATA2MSB) {
131+
} else if (elf_file.get_hdr().ei_data == elf::elfdata::msb) {
199132
return BIG;
200133
} else {
201134
throw SIMULATOR_EXCEPTION(
@@ -206,6 +139,7 @@ Endian ProgramLoader::get_endian() const {
206139
"");
207140
}
208141
}
142+
209143
ArchitectureType ProgramLoader::get_architecture_type() const {
210144
return architecture_type;
211145
}

src/machine/programloader.h

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
#include <QFile>
99
#include <cstdint>
10-
#include <gelf.h>
10+
#include <elf/elf++.hh>
1111
#include <qstring.h>
1212
#include <qvector.h>
1313

@@ -37,19 +37,10 @@ class ProgramLoader {
3737
ArchitectureType get_architecture_type() const;
3838

3939
private:
40-
QFile elf_file;
41-
Elf *elf;
42-
GElf_Ehdr hdr {}; // elf file header
43-
size_t n_secs {}; // number of sections in elf program header
40+
elf::elf elf_file;
4441
ArchitectureType architecture_type;
45-
46-
private:
47-
union {
48-
Elf32_Phdr *arch32;
49-
Elf64_Phdr *arch64;
50-
} sections_headers {};
51-
QVector<size_t> indexes_of_load_sections; // external index to sections_headers index
5242
Address executable_entry;
43+
std::vector<elf::segment> load_segments;
5344
};
5445

5546
} // namespace machine

0 commit comments

Comments
 (0)