Skip to content

Commit

Permalink
Allow disabling exceptions for outer projects
Browse files Browse the repository at this point in the history
Projects that include headers from libriscv can now be built without exceptions. libriscv will still need to be built with exceptions internally.
  • Loading branch information
fwsGonzo committed Jun 29, 2024
1 parent f024df4 commit 5a98000
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 33 deletions.
9 changes: 9 additions & 0 deletions examples/noexcept/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.14)
project(noexcept LANGUAGES CXX)

add_subdirectory(../../lib riscv)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -fno-exceptions -fno-rtti -fno-unwind-tables -fno-asynchronous-unwind-tables")

add_executable(example example.cpp)
target_link_libraries(example riscv)
14 changes: 14 additions & 0 deletions examples/noexcept/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Adding _libriscv_ to your project

This example project shows how you can build libriscv without C++ exceptions.

For now, only the outer project is without exceptions. This is on-going work.

## fib.rv64.elf

The example program calculates fibonacci numbers. Run the program with the number as argument:

```
./.build/example fib.rv64.elf 64
```
Will calculate the 64th fibonacci number.
11 changes: 11 additions & 0 deletions examples/noexcept/build_and_run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
set -e

mkdir -p .build
pushd .build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j4
popd

# fib(64) == 10610209857723
./.build/example fib.rv64.elf 64
42 changes: 42 additions & 0 deletions examples/noexcept/example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include <fstream>
#include <iostream>
#include <libriscv/machine.hpp>
using namespace riscv;

int main(int argc, char** argv)
{
if (argc < 2) {
std::cout << argv[0] << ": [program file] [arguments ...]" << std::endl;
return -1;
}

// Read the RISC-V program into a std::vector:
std::ifstream stream(argv[1], std::ios::in | std::ios::binary);
if (!stream) {
std::cout << argv[1] << ": File not found?" << std::endl;
return -1;
}
const std::vector<uint8_t> binary(
(std::istreambuf_iterator<char>(stream)),
std::istreambuf_iterator<char>()
);

// Take program arguments and make a new string vector, from 1..N
std::vector<std::string> arguments { argv[1] };
for (size_t i = 2; i < argc; i++)
arguments.push_back(argv[i]);

// Create a new 64-bit RISC-V machine
Machine<RISCV64> machine{binary, {.memory_max = 256UL << 20}};

// Use string vector as arguments to the RISC-V program
machine.setup_linux(
arguments,
{"LC_TYPE=C", "LC_ALL=C", "USER=root"});
machine.setup_linux_syscalls();

// Run the program, but timeout after 128bn instructions
machine.simulate(128'000'000'000ull);

std::cout << "Program exited with status: " << machine.return_value<long>() << std::endl;
}
1 change: 1 addition & 0 deletions examples/noexcept/fib.rv64.elf
28 changes: 22 additions & 6 deletions lib/libriscv/machine_inline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,29 +253,45 @@ void Machine<W>::realign_stack() noexcept
template <int W> inline
const MultiThreading<W>& Machine<W>::threads() const
{
if (UNLIKELY(m_mt == nullptr))
throw MachineException(FEATURE_DISABLED, "Threads are not initialized");
return *m_mt;
if (LIKELY(m_mt != nullptr))
return *m_mt;
#if __has_feature(__cpp_exceptions)
throw MachineException(FEATURE_DISABLED, "Threads are not initialized");
#else
std::abort();
#endif
}
template <int W> inline
MultiThreading<W>& Machine<W>::threads()
{
if (UNLIKELY(m_mt == nullptr))
throw MachineException(FEATURE_DISABLED, "Threads are not initialized");
return *m_mt;
if (LIKELY(m_mt != nullptr))
return *m_mt;
#if __has_feature(__cpp_exceptions)
throw MachineException(FEATURE_DISABLED, "Threads are not initialized");
#else
std::abort();
#endif
}

template <int W> inline
const FileDescriptors& Machine<W>::fds() const
{
if (m_fds != nullptr) return *m_fds;
#if __has_feature(__cpp_exceptions)
throw MachineException(ILLEGAL_OPERATION, "No access to files or sockets", 0);
#else
std::abort();
#endif
}
template <int W> inline
FileDescriptors& Machine<W>::fds()
{
if (m_fds != nullptr) return *m_fds;
#if __has_feature(__cpp_exceptions)
throw MachineException(ILLEGAL_OPERATION, "No access to files or sockets", 0);
#else
std::abort();
#endif
}

template <int W> inline
Expand Down
2 changes: 1 addition & 1 deletion lib/libriscv/memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ namespace riscv
// Add the correct offset to address for dynamically loaded programs
address = this->elf_base_address(address);

const auto* symtab = elf_sym_index(sym_hdr, 0);
const auto* symtab = elf_offset<typename Elf::Sym>(sym_hdr->sh_offset);
const size_t symtab_ents = sym_hdr->sh_size / sizeof(typename Elf::Sym);
const char* strtab = elf_offset<char>(str_hdr->sh_offset);

Expand Down
12 changes: 6 additions & 6 deletions lib/libriscv/memory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,12 @@ namespace riscv
template <typename T> T* elf_offset(size_t ofs) const {
if (ofs + sizeof(T) >= ofs && ofs + sizeof(T) < m_binary.size())
return (T*) &m_binary[ofs];
#if __has_feature(__cpp_exceptions)
throw MachineException(INVALID_PROGRAM, "Invalid ELF offset", ofs);
#else
std::abort();
#endif

}
const auto* elf_header() const {
return elf_offset<const typename Elf::Header> (0);
Expand All @@ -230,12 +235,7 @@ namespace riscv
void dynamic_linking(const typename Elf::Header&);
void relocate_section(const char* section_name, const char* symtab);
const typename Elf::Sym* resolve_symbol(std::string_view name) const;
const auto* elf_sym_index(const typename Elf::SectionHeader* shdr, uint32_t symidx) const {
if (symidx >= shdr->sh_size / sizeof(typename Elf::Sym))
throw MachineException(INVALID_PROGRAM, "ELF Symtab section index overflow");
auto* symtab = elf_offset<typename Elf::Sym>(shdr->sh_offset);
return &symtab[symidx];
}
const typename Elf::Sym* elf_sym_index(const typename Elf::SectionHeader* shdr, uint32_t symidx) const;
// ELF loader
void binary_loader(const MachineOptions<W>&);
void binary_load_ph(const MachineOptions<W>&, const typename Elf::ProgramHeader*, address_t vaddr);
Expand Down
13 changes: 13 additions & 0 deletions lib/libriscv/memory_elf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ namespace riscv
}
}

template <int W>
const typename Elf<W>::Sym* Memory<W>::elf_sym_index(const typename Elf::SectionHeader* shdr, uint32_t symidx) const
{
if (symidx >= shdr->sh_size / sizeof(typename Elf::Sym))
#ifdef __EXCEPTIONS
throw MachineException(INVALID_PROGRAM, "ELF Symtab section index overflow");
#else
std::abort();
#endif
auto* symtab = this->elf_offset<typename Elf::Sym>(shdr->sh_offset);
return &symtab[symidx];
}

template <int W>
const typename Elf<W>::SectionHeader* Memory<W>::section_by_name(const std::string& name) const
{
Expand Down
4 changes: 2 additions & 2 deletions lib/libriscv/memory_helpers_paging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ size_t Memory<W>::gather_buffers_from_range(
len -= size;
}
if (UNLIKELY(len != 0)) {
throw MachineException(OUT_OF_MEMORY, "Out of buffers", index);
machine().cpu.trigger_exception(OUT_OF_MEMORY, index);
}
return index;
}
Expand Down Expand Up @@ -356,7 +356,7 @@ size_t Memory<W>::gather_writable_buffers_from_range(
len -= size;
}
if (UNLIKELY(len != 0)) {
throw MachineException(OUT_OF_MEMORY, "Out of buffers", index);
machine().cpu.trigger_exception(OUT_OF_MEMORY, index);
}
return index;
}
19 changes: 16 additions & 3 deletions lib/libriscv/page.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ struct alignas(32) PageData {
{
if constexpr (memory_alignment_check) {
if (offset % sizeof(T))
#if __has_feature(__cpp_exceptions)
throw MachineException(INVALID_ALIGNMENT, "Misaligned read", offset);
#else
std::abort();
#endif
}
return *(T*) &buffer8[offset];
}
Expand All @@ -58,7 +62,11 @@ struct alignas(32) PageData {
{
if constexpr (memory_alignment_check) {
if (offset % sizeof(T))
#if __has_feature(__cpp_exceptions)
throw MachineException(INVALID_ALIGNMENT, "Misaligned write", offset);
#else
std::abort();
#endif
}
*(T*) &buffer8[offset] = value;
}
Expand Down Expand Up @@ -164,7 +172,7 @@ struct Page

bool has_trap() const noexcept { return m_trap != nullptr; }
// NOTE: Setting a trap makes the page uncacheable
void set_trap(mmio_cb_t newtrap) const;
bool set_trap(mmio_cb_t newtrap) const;
void trap(uint32_t offset, int mode, int64_t value) const;
static int trap_mode(int mode) noexcept { return mode & 0xF000; }
static int trap_size(int mode) noexcept { return mode & 0x0FFF; }
Expand All @@ -176,7 +184,11 @@ inline Page::Page(const PageAttributes& a, PageData* data)
: attr(a)
{
if (UNLIKELY(data == nullptr))
#if __has_feature(__cpp_exceptions)
throw MachineException(ILLEGAL_OPERATION, "Tried to create a page with no page data");
#else
std::abort();
#endif
attr.non_owning = true;
m_page.reset(data);
}
Expand All @@ -193,16 +205,17 @@ inline void Page::trap(uint32_t offset, int mode, int64_t value) const
{
this->m_trap((Page&) *this, offset, mode, value);
}
inline void Page::set_trap(mmio_cb_t newtrap) const {
inline bool Page::set_trap(mmio_cb_t newtrap) const {
if constexpr (memory_traps_enabled) {
// Setting a trap makes the page uncacheable and vice versa
// This is done so that reads and writes always trigger the
// slow-path that allows trapping.
this->attr.cacheable = (newtrap == nullptr);
this->m_trap = newtrap;
return true;
} else {
(void) newtrap;
throw MachineException(FEATURE_DISABLED, "Memory traps have not been enabled");
return false;
}
}

Expand Down
25 changes: 13 additions & 12 deletions lib/libriscv/rva.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,25 @@ namespace riscv
using address_t = address_type<W>;
static constexpr size_t MAX_RESV = 48;

void load_reserve(int size, address_t addr) RISCV_INTERNAL
bool load_reserve(int size, address_t addr) RISCV_INTERNAL
{
check_alignment(size, addr);
if (LIKELY(m_reservations.size() < MAX_RESV))
if (!check_alignment(size, addr))
return false;

if (LIKELY(m_reservations.size() < MAX_RESV)) {
m_reservations.insert(addr);
else
throw MachineException(DEADLOCK_REACHED,
"Not enough room for memory reservations", addr);
return true;
}
return false;
}

// Volume I: RISC-V Unprivileged ISA V20190608 p.49:
// An SC can only pair with the most recent LR in program order.
bool store_conditional(int size, address_t addr) RISCV_INTERNAL
{
check_alignment(size, addr);
if (!check_alignment(size, addr))
return false;

bool result = (m_reservations.count(addr) != 0);
// Regardless of success or failure, executing an SC.W
// instruction invalidates any reservation held by this hart.
Expand All @@ -34,12 +38,9 @@ namespace riscv
}

private:
inline void check_alignment(int size, address_t addr) RISCV_INTERNAL
inline bool check_alignment(int size, address_t addr) RISCV_INTERNAL
{
if (UNLIKELY(addr & (size-1))) {
throw MachineException(INVALID_ALIGNMENT,
"Load-Reserved address is misaligned", addr);
}
return (addr & (size-1)) == 0;
}

std::set<address_t> m_reservations;
Expand Down
9 changes: 6 additions & 3 deletions lib/libriscv/rva_instr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,21 +305,24 @@ namespace riscv
// switch on atomic type
if (instr.Atype.funct3 == AMOSIZE_W)
{
cpu.atomics().load_reserve(4, addr);
if (!cpu.atomics().load_reserve(4, addr))
cpu.trigger_exception(DEADLOCK_REACHED);
value = (int32_t)cpu.machine().memory.template read<uint32_t> (addr);
}
else if (instr.Atype.funct3 == AMOSIZE_D)
{
if constexpr (RVISGE64BIT(cpu)) {
cpu.atomics().load_reserve(8, addr);
if (!cpu.atomics().load_reserve(8, addr))
cpu.trigger_exception(DEADLOCK_REACHED);
value = (int64_t)cpu.machine().memory.template read<uint64_t> (addr);
} else
cpu.trigger_exception(ILLEGAL_OPCODE);
}
else if (instr.Atype.funct3 == AMOSIZE_Q)
{
if constexpr (RVIS128BIT(cpu)) {
cpu.atomics().load_reserve(16, addr);
if (!cpu.atomics().load_reserve(16, addr))
cpu.trigger_exception(DEADLOCK_REACHED);
value = cpu.machine().memory.template read<RVREGTYPE(cpu)> (addr);
} else
cpu.trigger_exception(ILLEGAL_OPCODE);
Expand Down

0 comments on commit 5a98000

Please sign in to comment.