Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
name: ci.yml

on:
push:
branches: ['master', 'next']
pull_request:
types: [ opened, synchronize, reopened ]
branches: ['master', 'next']

on: [ push, pull_request ]
jobs:
linux-x86:
runs-on: ubuntu-latest
Expand All @@ -27,6 +21,26 @@ jobs:
- name: Test x86
run: cd build && ./test

linux-qemu-riscv:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v3
name: Run commands
with:
arch: riscv64
distro: ubuntu24.04
shell: /bin/sh

install: |
apt-get update
apt-get install -y cmake g++ git python3

run: |
cmake -B build
cmake --build build
cd build && ./test

linux-aarch64:
runs-on: ubuntu-24.04-arm
steps:
Expand Down
26 changes: 16 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.31)
cmake_minimum_required(VERSION 2.28...3.31)
project(ASMIOV C CXX)
include(FetchContent)

Expand All @@ -7,17 +7,21 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Python3 COMPONENTS Interpreter)

option(USE_MOLD "Use mold linker if installed" ON)

# Use the mold linker automatically for GCC if available,
# as it is faster, more modern, and produces better errors.
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
find_program(MOLD_PROGRAM mold)

if (MOLD_PROGRAM)
message(STATUS "The MOLD linker was found: ${MOLD_PROGRAM}")
set(CMAKE_EXE_LINKER_FLAGS "-fuse-ld=mold")
set(CMAKE_SHARED_LINKER_FLAGS "-fuse-ld=mold")
else()
message(STATUS "The MOLD linker was NOT found!")
if (USE_MOLD)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
find_program(MOLD_PROGRAM mold)

if (MOLD_PROGRAM)
message(STATUS "The MOLD linker was found: ${MOLD_PROGRAM}")
set(CMAKE_EXE_LINKER_FLAGS "-fuse-ld=mold")
set(CMAKE_SHARED_LINKER_FLAGS "-fuse-ld=mold")
else()
message(STATUS "The MOLD linker was NOT found!")
endif()
endif()
endif()

Expand All @@ -32,6 +36,7 @@ FetchContent_MakeAvailable(vstl)
set(ASMIO_WRITERS
src/asmio/x86/writer.hpp
src/asmio/aarch64/writer.hpp
src/asmio/riscv/writer.hpp
)

set(ASMIO_BRIDGES "${ASMIO_WRITERS}")
Expand Down Expand Up @@ -68,6 +73,7 @@ add_executable(test
test/x86.cpp
test/aarch64.cpp
test/elf.cpp
test/riscv.cpp
)
target_link_libraries(test PRIVATE asmiov)
target_include_directories(test PRIVATE ${ASMIOV_INCLUDE_DIRS})
Expand Down
4 changes: 4 additions & 0 deletions src/asmio/elf/header.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ namespace asmio {
#if ARCH_X86
NATIVE = X86_64,
#endif

#if ARCH_RISCV64
NATIVE = RISCV,
#endif
};

enum struct ElfClass : uint8_t {
Expand Down
1 change: 1 addition & 0 deletions src/asmio/elf/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "object.hpp"

#include <filesystem>
#include <unistd.h>
#include <asmio/util/platform.hpp>

namespace asmio {
Expand Down
12 changes: 11 additions & 1 deletion src/asmio/external.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@
# define ARCH_AARCH64 false
#endif

#if !(ARCH_X86 || ARCH_AARCH64)
#if defined(__riscv)
# if (__riscv_xlen == 64)
# define ARCH_RISCV64 true
# else
# define ARCH_RISCV64 false
# endif
#else
# define ARCH_RISCV64 false
#endif

#if !(ARCH_X86 || ARCH_AARCH64 || ARCH_RISCV64)
# error "Unsupported target architecture!"
#endif
49 changes: 48 additions & 1 deletion src/asmio/program/linkage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace asmio {
const int64_t distance = buffer->get_offset(src) - buffer->get_offset(dst) + linkage.addend;

if (distance & 0b11) {
throw std::runtime_error {"Can't reference label '" + linkage.label.string() + "' (offset " + util::to_hex(distance) + ") into target " + util::to_hex(dst.offset) + ", offset is not aligned!"};
throw std::runtime_error {"Can't reference label '" + linkage.label.string() + "' (offset " + util::to_hex(distance) + ") into target " + util::to_hex(dst.offset) + ", offset is not 4-aligned!"};
}

const int64_t offset = distance >> 2;
Expand Down Expand Up @@ -66,10 +66,57 @@ namespace asmio {
std::memcpy(buffer->get_pointer(dst), value_ptr, width);
}

static void encode_riscv_b(SegmentedBuffer* buffer, const Linkage& linkage, BufferMarker src, size_t mount) {
BufferMarker dst = linkage.target;
const int64_t dist = buffer->get_offset(src) - buffer->get_offset(dst) + linkage.addend;

if (dist & 1) {
throw std::runtime_error {"Can't reference label '" + linkage.label.string() + "' (offset " + util::to_hex(dist) + ") into target " + util::to_hex(dst.offset) + ", offset is not 2-aligned!"};
}

const int64_t imm12 = dist >> 1;

if (!util::is_signed_encodable(imm12, 12)) {
throw std::runtime_error {"Can't fit label '" + linkage.label.string() + "' (offset " + util::to_hex(dist) + ") into target " + util::to_hex(dst.offset) + ", some data would have been truncated!"};
}

const uint32_t si = (imm12 & 0b1'0'000000'0000) >> 11;
const uint32_t b7 = (imm12 & 0b0'1'000000'0000) >> 10;
const uint32_t hi = (imm12 & 0b0'0'111111'0000) >> 4;
const uint32_t lo = (imm12 & 0b0'0'000000'1111) >> 0;

*reinterpret_cast<uint32_t*>(buffer->get_pointer(dst)) |= (si << 31 | hi << 25 | lo << 8 | b7 << 7);
}

static void encode_riscv_j(SegmentedBuffer* buffer, const Linkage& linkage, BufferMarker src, size_t mount) {
BufferMarker dst = linkage.target;
const int64_t dist = buffer->get_offset(src) - buffer->get_offset(dst) + linkage.addend;

if (dist & 1) {
throw std::runtime_error {"Can't reference label '" + linkage.label.string() + "' (offset " + util::to_hex(dist) + ") into target " + util::to_hex(dst.offset) + ", offset is not 2-aligned!"};
}

const int64_t imm20 = dist >> 1;

if (!util::is_signed_encodable(imm20, 20)) {
throw std::runtime_error {"Can't fit label '" + linkage.label.string() + "' (offset " + util::to_hex(dist) + ") into target " + util::to_hex(dst.offset) + ", some data would have been truncated!"};
}

const uint32_t si = (imm20 & 0b1'00000000'0'0000000000) >> 19;
const uint32_t hi = (imm20 & 0b0'11111111'0'0000000000) >> 11;
const uint32_t b2 = (imm20 & 0b0'00000000'1'0000000000) >> 10;
const uint32_t lo = (imm20 & 0b0'00000000'0'1111111111) >> 0;

*reinterpret_cast<uint32_t*>(buffer->get_pointer(dst)) |= (si << 31 | lo << 21 | b2 << 20 | hi << 12);
}

/*
* class LinkageType
*/

Linkage::Type LinkageType::RISCV_BRANCH {ElfRelocationType::RISCV_BRANCH, encode_riscv_b};
Linkage::Type LinkageType::RISCV_JUMP {ElfRelocationType::RISCV_JAL, encode_riscv_j};

Linkage::Type LinkageType::AARCH64_21_5_LO_HI {ElfRelocationType::AARCH64_ADR_PREL_LO21, encode_21_5_lo_hi};
Linkage::Type LinkageType::AARCH64_14_5_ALIGNED {ElfRelocationType::AARCH64_TSTBR14, encode_shifted_aligned<14, 5>};
Linkage::Type LinkageType::AARCH64_19_5_ALIGNED {ElfRelocationType::AARCH64_CONDBR19, encode_shifted_aligned<19, 5>};
Expand Down
3 changes: 3 additions & 0 deletions src/asmio/program/linkage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ namespace asmio {

struct LinkageType {

static Linkage::Type RISCV_BRANCH;
static Linkage::Type RISCV_JUMP;

static Linkage::Type AARCH64_21_5_LO_HI;
static Linkage::Type AARCH64_14_5_ALIGNED;
static Linkage::Type AARCH64_19_5_ALIGNED;
Expand Down
41 changes: 41 additions & 0 deletions src/asmio/riscv/argument/condition.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include <asmio/external.hpp>

namespace asmio::riscv {

enum struct Condition : uint8_t {
EQ = 0x00,
NE = 0x01,
LT = 0x04, GT = 0x14,
GE = 0x05, LE = 0x15,
LTU = 0x06, GTU = 0x16,
GEU = 0x07, LEU = 0x17,
};

/**
* Extract the condition code used during instruction encoding
*/
constexpr uint8_t get_condition_code(Condition cond) {
return static_cast<uint8_t>(cond) & 0xf;
}

/**
* Check if during instruction encoding this condition requires inverting the arguments
*/
constexpr bool get_condition_swap(Condition cond) {
return static_cast<uint8_t>(cond) & 0x10;
}

inline Condition parse_condition_enum(const std::string_view& view) {
if (view == "eq") return Condition::EQ;
if (view == "ne") return Condition::NE;
if (view == "lt") return Condition::LT;
if (view == "ge") return Condition::GE;
if (view == "ltu") return Condition::LTU;
if (view == "geu") return Condition::GEU;

throw std::runtime_error {"Invalid condition enumeration"};
}

}
25 changes: 25 additions & 0 deletions src/asmio/riscv/argument/order.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include <asmio/external.hpp>

namespace asmio::riscv {

// Copied from the AArch64
// TODO: unify?
enum struct Order : uint8_t {
NONE = 0b00, ///< Ensure no ordering of operations
RELEASE = 0b01, ///< Ensure that the preceding operations finish before this one starts
ACQUIRE = 0b10, ///< Ensure that the following operations wait for this one to finish
ACQUIRE_RELEASE = 0b11, ///< Ensure that both previous and preceding operations are executed in order
};

constexpr Order parse_order_enum(const std::string_view& view) {
if (view == "none") return Order::NONE;
if (view == "rl") return Order::RELEASE;
if (view == "aq") return Order::ACQUIRE;
if (view == "ra" || view == "ar" || view == "aqrl" || view == "rlaq") return Order::ACQUIRE_RELEASE;

throw std::runtime_error {"Invalid order enumeration"};
}

}
91 changes: 91 additions & 0 deletions src/asmio/riscv/argument/registry.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#pragma once

#include <asmio/external.hpp>

namespace asmio::riscv {

struct PACKED Registry {

public:

const uint8_t reg; // registry Risc-V code

public:

constexpr Registry(uint8_t reg) noexcept : reg(reg) {}

};

/// Reference arbitrary register, 'number' MUST be in range [0, 31]
constexpr Registry X(uint8_t number) {
return {static_cast<uint8_t>(number & 0b11111)};
}

// general purpose QWORD registers
constexpr Registry X0 = X(0); // always equal 0
constexpr Registry X1 = X(1);
constexpr Registry X2 = X(2);
constexpr Registry X3 = X(3);
constexpr Registry X4 = X(4);
constexpr Registry X5 = X(5);
constexpr Registry X6 = X(6);
constexpr Registry X7 = X(7);
constexpr Registry X8 = X(8);
constexpr Registry X9 = X(9);
constexpr Registry X10 = X(10);
constexpr Registry X11 = X(11);
constexpr Registry X12 = X(12);
constexpr Registry X13 = X(13);
constexpr Registry X14 = X(14);
constexpr Registry X15 = X(15);
constexpr Registry X16 = X(16);
constexpr Registry X17 = X(17);
constexpr Registry X18 = X(18);
constexpr Registry X19 = X(19);
constexpr Registry X20 = X(20);
constexpr Registry X21 = X(21);
constexpr Registry X22 = X(22);
constexpr Registry X23 = X(23);
constexpr Registry X24 = X(24);
constexpr Registry X25 = X(25);
constexpr Registry X26 = X(26);
constexpr Registry X27 = X(27);
constexpr Registry X28 = X(28);
constexpr Registry X29 = X(29);
constexpr Registry X30 = X(30);
constexpr Registry X31 = X(31);

// register name aliases
constexpr Registry RA = X1;
constexpr Registry SP = X2;
constexpr Registry GP = X3;
constexpr Registry TP = X4;
constexpr Registry T0 = X5;
constexpr Registry T1 = X6;
constexpr Registry T2 = X7;
constexpr Registry S0 = X8;
constexpr Registry S1 = X9;
constexpr Registry A0 = X10;
constexpr Registry A1 = X11;
constexpr Registry A2 = X12;
constexpr Registry A3 = X13;
constexpr Registry A4 = X14;
constexpr Registry A5 = X15;
constexpr Registry A6 = X16;
constexpr Registry A7 = X17;
constexpr Registry S2 = X18;
constexpr Registry S3 = X19;
constexpr Registry S4 = X20;
constexpr Registry S5 = X21;
constexpr Registry S6 = X22;
constexpr Registry S7 = X23;
constexpr Registry S8 = X24;
constexpr Registry S9 = X25;
constexpr Registry S10 = X26;
constexpr Registry S11 = X27;
constexpr Registry T3 = X28;
constexpr Registry T4 = X29;
constexpr Registry T5 = X30;
constexpr Registry T6 = X31;

}
Loading
Loading