Print test summary

This commit is contained in:
Eugen Wissner 2022-06-05 23:43:45 +02:00
parent 5490f6ce1c
commit b34e14f36b
97 changed files with 11844 additions and 1575 deletions

6
.gitignore vendored
View File

@ -1,3 +1,5 @@
/.dub/
/dub.selections.json
/build/ /build/
.cache/
CMakeFiles/
CMakeCache.txt
dub.selections.json

31
CMakeLists.txt Normal file
View File

@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.21)
project(Elna)
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_CXX_STANDARD 17)
find_package(Boost COMPONENTS program_options REQUIRED)
find_package(FLEX)
include_directories(${Boost_INCLUDE_DIR})
add_executable(tester tests/tester.cpp include/elna/tester.hpp)
target_include_directories(tester PRIVATE include)
FLEX_TARGET(scanner source/scanner.l ${CMAKE_CURRENT_BINARY_DIR}/scanner.cpp)
add_executable(elna cli/main.cpp
source/lexer.cpp include/elna/source/lexer.hpp
source/parser.cpp include/elna/source/parser.hpp
source/types.cpp include/elna/source/types.hpp
source/symbol_table.cpp include/elna/source/symbol_table.hpp
source/result.cpp include/elna/source/result.hpp
source/semantic.cpp include/elna/source/semantic.hpp
source/optimizer.cpp include/elna/source/optimizer.hpp
backend/riscv.cpp include/elna/backend/riscv.hpp
backend/target.cpp include/elna/backend/target.hpp
cli/cl.cpp include/elna/cli/cl.hpp
${FLEX_scanner_OUTPUTS}
)
target_include_directories(elna PRIVATE include)
target_link_libraries(elna LINK_PUBLIC ${Boost_LIBRARIES})

View File

@ -1,69 +0,0 @@
require 'pathname'
require 'rake/clean'
require 'open3'
DFLAGS = ['--warn-no-deprecated', '-L/usr/lib64/gcc-12']
BINARY = 'build/bin/elna'
TESTS = FileList['tests/*.elna']
.map { |test| (Pathname.new('build') + test).sub_ext('').to_path }
SOURCES = FileList['source/**/*.d']
directory 'build'
CLEAN.include 'build'
CLEAN.include '.dub'
rule(/build\/tests\/.+/ => ->(file) { test_for_out(file) }) do |t|
Pathname.new(t.name).dirname.mkpath
sh BINARY, t.source
sh 'gcc', '-o', t.name, "#{t.name}.o"
# Open3.pipeline [BINARY, t.source], ['gcc', '-x', 'assembler', '-o', t.name, '-']
end
file BINARY => SOURCES do |t|
sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc-12')
end
file 'build/tests/sample' => BINARY do |t|
sh t.source
sh 'gcc', '-o', t.name, 'build/tests/sample.o'
end
task default: BINARY
desc 'Run all tests and check the results'
task test: TESTS
task test: BINARY do
TESTS.each do |test|
expected = Pathname
.new(test)
.sub_ext('.txt')
.sub(/^build\/tests\//, 'tests/expectations/')
.read
.to_i
puts "Running #{test}"
system test
actual = $?.exitstatus
fail "#{test}: Expected #{expected}, got #{actual}" unless expected == actual
end
# system './build/tests/sample'
# actual = $?.exitstatus
# fail "./build/tests/sample: Expected 3, got #{actual}" unless 3 == actual
end
desc 'Run unittest blocks'
task unittest: SOURCES do |t|
sh('dub', 'test', '--compiler=gdc-12')
end
def test_for_out(out_file)
test_source = Pathname
.new(out_file)
.sub_ext('.elna')
.sub(/^build\//, '')
.to_path
[test_source, BINARY]
end

16
TODO Normal file
View File

@ -0,0 +1,16 @@
# Compiler
- Catch exceptions thrown by the argument parser and print them normally.
- Structs or records.
- Unions.
- Type checking.
- Support immediates greater than 12 bits.
- It seems instructions are correctly encoded only if the compiler is running
on a little endian architecture.
- Assignment to a pointer.
- Static array.
- Syscalls.
- Error message with an empty file wrongly says that a ")" is expected.
- Support any expressions for constants.
- Built-in types are added in the symbol table constructor. It would be better to
add them outside, before the type analysis begins.

455
backend/riscv.cpp Normal file
View File

@ -0,0 +1,455 @@
#include "elna/backend/riscv.hpp"
#include <cassert>
#include <functional>
#include <memory>
namespace elna::riscv
{
instruction::instruction(base_opcode opcode)
{
this->representation = static_cast<std::underlying_type<base_opcode>::type>(opcode);
}
instruction& instruction::i(x_register rd, funct3_t funct3, x_register rs1, std::uint32_t immediate)
{
this->representation |= (static_cast<std::underlying_type<x_register>::type>(rd) << 7)
| (static_cast<std::underlying_type<funct3_t>::type>(funct3) << 12)
| (static_cast<std::underlying_type<x_register>::type>(rs1) << 15)
| (immediate << 20);
return *this;
}
instruction& instruction::s(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2)
{
this->representation |= ((imm & 0x1f) << 7)
| (static_cast<std::underlying_type<funct3_t>::type>(funct3) << 12)
| (static_cast<std::underlying_type<x_register>::type>(rs1) << 15)
| (static_cast<std::underlying_type<x_register>::type>(rs2) << 20)
| ((imm & 0xfe0) << 20);
return *this;
}
instruction& instruction::b(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2)
{
this->representation |= ((imm & 0x800) >> 4) | ((imm & 0x1e) << 7)
| (static_cast<std::underlying_type<funct3_t>::type>(funct3) << 12)
| (static_cast<std::underlying_type<x_register>::type>(rs1) << 15)
| (static_cast<std::underlying_type<x_register>::type>(rs2) << 20)
| ((imm & 0x7e0) << 20) | ((imm & 0x1000) << 19);
return *this;
}
instruction& instruction::r(x_register rd, funct3_t funct3, x_register rs1, x_register rs2, funct7_t funct7)
{
this->representation |= (static_cast<std::underlying_type<x_register>::type>(rd) << 7)
| (static_cast<std::underlying_type<funct3_t>::type>(funct3) << 12)
| (static_cast<std::underlying_type<x_register>::type>(rs1) << 15)
| (static_cast<std::underlying_type<x_register>::type>(rs2) << 20)
| (static_cast<std::underlying_type<funct7_t>::type>(funct7) << 25);
return *this;
}
instruction& instruction::u(x_register rd, std::uint32_t imm)
{
this->representation |= (static_cast<std::underlying_type<x_register>::type>(rd) << 7) | (imm << 12);
return *this;
}
instruction& instruction::j(x_register rd, std::uint32_t imm)
{
this->representation |= (static_cast<std::underlying_type<x_register>::type>(rd) << 7)
| (imm & 0xff000) | ((imm & 0x800) << 9) | ((imm & 0x7fe) << 20) | ((imm & 0x100000) << 11);
return *this;
}
const std::byte *instruction::cbegin() const
{
return reinterpret_cast<const std::byte *>(&this->representation);
}
const std::byte *instruction::cend() const
{
return reinterpret_cast<const std::byte *>(&this->representation) + sizeof(this->representation);
}
static void relocate(std::string_view name, address_t target, std::vector<reference>& references,
std::vector<instruction>& instructions, std::shared_ptr<source::writer<std::byte>> writer)
{
references.push_back(reference());
references.back().name = name;
references.back().offset = writer->size() + instructions.size() * 4;
references.back().target = target;
}
static void prologue(std::vector<instruction>& instructions)
{
instructions.push_back(instruction(base_opcode::opImm));
instructions.push_back(instruction(base_opcode::store));
instructions.push_back(instruction(base_opcode::store));
instructions.push_back(instruction(base_opcode::opImm));
}
static void epilogue(const std::size_t stack_size, std::vector<instruction>& instructions)
{
instructions[0].i(x_register::sp, funct3_t::addi, x_register::sp, -stack_size);
instructions[1].s(0, funct3_t::sw, x_register::sp, x_register::s0);
instructions[2].s(4, funct3_t::sw, x_register::sp, x_register::ra);
instructions[3].i(x_register::s0, funct3_t::addi, x_register::sp, stack_size);
// Epilogue.
instructions.push_back(instruction(base_opcode::load)
.i(x_register::s0, funct3_t::lw, x_register::sp, 0));
instructions.push_back(instruction(base_opcode::load)
.i(x_register::ra, funct3_t::lw, x_register::sp, 4));
instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::sp, funct3_t::addi, x_register::sp, stack_size));
instructions.push_back(instruction(base_opcode::jalr)
.i(x_register::zero, funct3_t::jalr, x_register::ra, 0));
}
static void generate_intrinsics(std::shared_ptr<source::writer<std::byte>> writer,
std::vector<reference>& references)
{
writer->sink("printf");
{
std::vector<instruction> instructions;
auto format_string = writer->sink(reinterpret_cast<const std::byte *>("%c\n\0"), 4);
prologue(instructions);
instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::a1, funct3_t::addi, x_register::zero, 't'));
instructions.push_back(instruction(base_opcode::branch)
.b(8, funct3_t::bne, x_register::zero, x_register::a0));
instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::a1, funct3_t::addi, x_register::zero, 'f'));
relocate(format_string, address_t::high20, references, instructions, writer);
instructions.push_back(instruction(base_opcode::lui).u(x_register::a5, 0));
relocate(format_string, address_t::lower12i, references, instructions, writer);
instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::a0, funct3_t::addi, x_register::a5, 0));
relocate("printf", address_t::text, references, instructions ,writer);
instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0));
instructions.push_back(instruction(base_opcode::jalr)
.i(x_register::ra, funct3_t::jalr, x_register::ra, 0));
epilogue(8, instructions);
writer->sink("writeb", reinterpret_cast<const std::byte *>(instructions.data()),
instructions.size() * sizeof(instruction));
}
{
std::vector<instruction> instructions;
auto format_string = writer->sink(reinterpret_cast<const std::byte *>("%d\n\0"), 4);
prologue(instructions);
instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::a1, funct3_t::addi, x_register::a0, 0));
relocate(format_string, address_t::high20, references, instructions, writer);
instructions.push_back(instruction(base_opcode::lui).u(x_register::a5, 0));
relocate(format_string, address_t::lower12i, references, instructions, writer);
instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::a0, funct3_t::addi, x_register::a5, 0));
relocate("printf", address_t::text, references, instructions, writer);
instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0));
instructions.push_back(instruction(base_opcode::jalr)
.i(x_register::ra, funct3_t::jalr, x_register::ra, 0));
epilogue(8, instructions);
writer->sink("writei", reinterpret_cast<const std::byte *>(instructions.data()),
instructions.size() * sizeof(instruction));
}
}
static void load_in_register(std::shared_ptr<source::operand> operand, const x_register target,
std::shared_ptr<source::procedure_info> procedure_info, std::vector<instruction>& instructions)
{
std::shared_ptr<source::integer_operand> integer_operand{ nullptr };
std::shared_ptr<source::variable_operand> variable_operand{ nullptr };
std::shared_ptr<source::temporary_variable> temporary_variable{ nullptr };
std::shared_ptr<source::variable_info> variable_symbol{ nullptr };
std::shared_ptr<source::parameter_info> parameter_symbol{ nullptr };
if ((integer_operand = std::dynamic_pointer_cast<source::integer_operand>(operand)) != nullptr)
{
instructions.push_back(instruction(base_opcode::opImm)
.i(target, funct3_t::addi, x_register::zero, integer_operand->value()));
}
else if ((variable_operand = std::dynamic_pointer_cast<source::variable_operand>(operand)) != nullptr)
{
const auto& name = procedure_info->scope()->lookup(variable_operand->name());
if ((variable_symbol = std::dynamic_pointer_cast<source::variable_info>(name)) != nullptr)
{
instructions.push_back(instruction(base_opcode::load)
.i(target, funct3_t::lw, x_register::s0, variable_symbol->offset));
}
else if ((parameter_symbol = std::dynamic_pointer_cast<source::parameter_info>(name)) != nullptr)
{
instructions.push_back(instruction(base_opcode::load)
.i(target, funct3_t::lw, x_register::s0, parameter_symbol->offset));
}
}
else if ((temporary_variable = std::dynamic_pointer_cast<source::temporary_variable>(operand)) != nullptr)
{
instructions.push_back(instruction(base_opcode::load));
instructions.back().i(target, funct3_t::lw, x_register::s0,
procedure_info->local_stack_size + 4 * temporary_variable->counter());
}
}
static void store_from_register(std::shared_ptr<source::operand> destination, const x_register target,
std::shared_ptr<source::procedure_info> procedure_info, std::vector<instruction>& instructions)
{
std::shared_ptr<source::variable_operand> variable_operand{ nullptr };
std::shared_ptr<source::temporary_variable> temporary_variable{ nullptr };
std::shared_ptr<source::variable_info> variable_symbol{ nullptr };
if ((variable_operand = std::dynamic_pointer_cast<source::variable_operand>(destination)) != nullptr)
{
variable_symbol = std::dynamic_pointer_cast<source::variable_info>(
procedure_info->scope()->lookup(variable_operand->name()));
instructions.push_back(instruction(base_opcode::store)
.s(variable_symbol->offset, funct3_t::sw, x_register::s0, target));
}
else if ((temporary_variable = std::dynamic_pointer_cast<source::temporary_variable>(destination)) != nullptr)
{
instructions.push_back(instruction(base_opcode::store));
instructions.back().s(procedure_info->local_stack_size + 4 * temporary_variable->counter(),
funct3_t::sw, x_register::s0, target);
}
}
static void perform_binary_operation(const source::binary_operator operation, std::shared_ptr<source::operand> lhs,
std::shared_ptr<source::operand> rhs, std::shared_ptr<source::operand> destination,
std::shared_ptr<source::procedure_info> procedure_info, std::vector<instruction>& instructions)
{
constexpr auto lhs_register = x_register::a0;
std::shared_ptr<source::variable_operand> variable_operand{ nullptr };
std::shared_ptr<source::temporary_variable> temporary_variable{ nullptr };
std::shared_ptr<source::variable_info> variable_symbol{ nullptr };
load_in_register(lhs, x_register::a0, procedure_info, instructions);
load_in_register(rhs, x_register::t0, procedure_info, instructions);
// Calculate the result and assign it to a variable on the stack.
switch (operation)
{
case source::binary_operator::sum:
instructions.push_back(instruction(base_opcode::op)
.r(lhs_register, funct3_t::add, x_register::a0, x_register::t0));
break;
case source::binary_operator::subtraction:
instructions.push_back(instruction(base_opcode::op)
.r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub));
break;
case source::binary_operator::multiplication:
instructions.push_back(instruction(base_opcode::op)
.r(lhs_register, funct3_t::mul, x_register::a0, x_register::t0, funct7_t::muldiv));
break;
case source::binary_operator::division:
instructions.push_back(instruction(base_opcode::op)
.r(lhs_register, funct3_t::div, x_register::a0, x_register::t0, funct7_t::muldiv));
break;
case source::binary_operator::equals:
instructions.push_back(instruction(base_opcode::op)
.r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub));
instructions.push_back(instruction(base_opcode::opImm)
.i(lhs_register, funct3_t::sltiu, lhs_register, 1));
break;
case source::binary_operator::not_equals:
instructions.push_back(instruction(base_opcode::op)
.r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub));
instructions.push_back(instruction(base_opcode::op)
.r(lhs_register, funct3_t::sltu, x_register::zero, lhs_register));
break;
case source::binary_operator::less:
instructions.push_back(instruction(base_opcode::op)
.r(lhs_register, funct3_t::sltu, x_register::a0, x_register::t0));
break;
case source::binary_operator::greater_equal:
instructions.push_back(instruction(base_opcode::op)
.r(lhs_register, funct3_t::sltu, x_register::t0, x_register::a0));
break;
case source::binary_operator::greater:
instructions.push_back(instruction(base_opcode::op)
.r(lhs_register, funct3_t::slt, x_register::a0, x_register::t0));
instructions.push_back(instruction(base_opcode::opImm)
.i(lhs_register, funct3_t::xori, lhs_register, 1));
break;
case source::binary_operator::less_equal:
instructions.push_back(instruction(base_opcode::op)
.r(lhs_register, funct3_t::slt, x_register::t0, x_register::a0));
instructions.push_back(instruction(base_opcode::opImm)
.i(lhs_register, funct3_t::xori, lhs_register, 1));
break;
}
store_from_register(destination, lhs_register, procedure_info, instructions);
}
std::vector<reference> generate(source::intermediate_code_generator generator,
std::shared_ptr<source::symbol_table> table, std::shared_ptr<source::writer<std::byte>> writer)
{
std::vector<reference> references;
generate_intrinsics(writer, references);
for (auto& [identifier, intermediate_code] : generator)
{
std::vector<instruction> instructions;
auto main_symbol = std::dynamic_pointer_cast<source::procedure_info>(table->lookup(identifier));
std::size_t argument_offset{ 0 };
const auto stack_size = static_cast<std::uint32_t>(
intermediate_code.variable_counter() * 4 + 8 + main_symbol->stack_size());
std::unordered_map<std::size_t, std::function<void(std::size_t)>> missing_labels;
std::unordered_map<std::size_t, std::function<void(std::size_t)>>::iterator missing_label =
missing_labels.end();
std::unordered_map<std::size_t, std::size_t> local_labels;
for (auto& quadruple : intermediate_code)
{
switch (quadruple.operation())
{
case source::quadruple_operator::start:
prologue(instructions);
break;
case source::quadruple_operator::stop:
epilogue(stack_size, instructions);
break;
case source::quadruple_operator::add:
perform_binary_operation(source::binary_operator::sum, quadruple.operand1(),
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
break;
case source::quadruple_operator::sub:
perform_binary_operation(source::binary_operator::subtraction, quadruple.operand1(),
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
break;
case source::quadruple_operator::mul:
perform_binary_operation(source::binary_operator::multiplication, quadruple.operand1(),
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
break;
case source::quadruple_operator::div:
perform_binary_operation(source::binary_operator::division, quadruple.operand1(),
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
break;
case source::quadruple_operator::eq:
perform_binary_operation(source::binary_operator::equals, quadruple.operand1(),
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
break;
case source::quadruple_operator::neq:
perform_binary_operation(source::binary_operator::not_equals, quadruple.operand1(),
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
break;
case source::quadruple_operator::lt:
perform_binary_operation(source::binary_operator::less, quadruple.operand1(),
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
break;
case source::quadruple_operator::ge:
perform_binary_operation(source::binary_operator::greater_equal, quadruple.operand1(),
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
break;
case source::quadruple_operator::gt:
perform_binary_operation(source::binary_operator::greater, quadruple.operand1(),
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
break;
case source::quadruple_operator::le:
perform_binary_operation(source::binary_operator::less_equal, quadruple.operand1(),
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
break;
case source::quadruple_operator::load:
{
auto operand_identifier =
std::dynamic_pointer_cast<source::variable_operand>(quadruple.operand1())->name();
auto variable_symbol = std::dynamic_pointer_cast<source::variable_info>(
main_symbol->scope()->lookup(operand_identifier));
load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions);
instructions.push_back(instruction(base_opcode::load)
.i(x_register::a0, funct3_t::lw, x_register::a0, 0));
store_from_register(quadruple.operand3(), x_register::a0, main_symbol, instructions);
}
break;
case source::quadruple_operator::ref:
{
auto operand_identifier =
std::dynamic_pointer_cast<source::variable_operand>(quadruple.operand1())->name();
auto variable_symbol = std::dynamic_pointer_cast<source::variable_info>(
main_symbol->scope()->lookup(operand_identifier));
instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::a0, funct3_t::addi, x_register::s0, variable_symbol->offset));
store_from_register(quadruple.operand3(), x_register::a0, main_symbol, instructions);
}
break;
case source::quadruple_operator::beqz:
load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions);
instructions.push_back(instruction(base_opcode::branch));
missing_labels.emplace(
std::dynamic_pointer_cast<source::label_operand>(quadruple.operand3())->counter(),
[before_branch = instructions.size() - 1, &instructions](std::size_t target) {
instructions[before_branch].b((target - before_branch) * 4,
funct3_t::beq, x_register::zero, x_register::a0);
});
break;
case source::quadruple_operator::j:
{
auto local_label = local_labels.find(
std::dynamic_pointer_cast<source::label_operand>(quadruple.operand3())->counter());
if (local_label != local_labels.end())
{
auto offset = -(instructions.size() - local_label->second) * 4;
instructions.push_back(instruction(base_opcode::auipc).u(x_register::a0, 0));
instructions.push_back(instruction(base_opcode::jalr)
.i(x_register::zero, funct3_t::jalr, x_register::a0, offset));
}
}
break;
case source::quadruple_operator::label:
{
auto label_counter =
std::dynamic_pointer_cast<source::label_operand>(quadruple.operand3())->counter();
missing_label = missing_labels.find(label_counter);
if (missing_label != missing_labels.end())
{
missing_label->second(instructions.size());
}
local_labels[label_counter] = instructions.size();
}
break;
case source::quadruple_operator::assign:
load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions);
store_from_register(quadruple.operand3(), x_register::a0, main_symbol, instructions);
break;
case source::quadruple_operator::param:
load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions);
instructions.push_back(instruction(base_opcode::store)
.s(argument_offset, funct3_t::sw, x_register::sp, x_register::a0));
argument_offset += 4;
break;
case source::quadruple_operator::call:
relocate(std::dynamic_pointer_cast<source::variable_operand>(quadruple.operand1())->name(),
address_t::text, references, instructions, writer);
instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0));
instructions.push_back(instruction(base_opcode::jalr)
.i(x_register::ra, funct3_t::jalr, x_register::ra, 0));
argument_offset = 0;
break;
}
}
writer->sink(identifier,
reinterpret_cast<const std::byte *>(instructions.data()),
instructions.size() * sizeof(instruction));
}
return references;
}
}

233
backend/target.cpp Normal file
View File

@ -0,0 +1,233 @@
#include "elna/backend/target.hpp"
#include "elna/backend/riscv.hpp"
#include <cstring>
namespace elna::riscv
{
elfio_section_writer::iterator::reference elfio_section_writer::iterator::operator*() const noexcept
{
return payload;
}
elfio_section_writer::iterator::pointer elfio_section_writer::iterator::operator->() const noexcept
{
return &payload;
}
elfio_section_writer::iterator& elfio_section_writer::iterator::operator++()
{
this->payload.data += *this->sizes;
this->payload.label = *(++this->labels);
this->payload.size = *(++this->sizes);
return *this;
}
elfio_section_writer::iterator& elfio_section_writer::iterator::operator++(int)
{
auto tmp = *this;
++(*this);
return *this;
}
bool elfio_section_writer::iterator::operator==(const iterator& that) const
{
return this->labels == that.labels;
}
bool elfio_section_writer::iterator::operator!=(const iterator& that) const
{
return !(*this == that);
}
elfio_section_writer::elfio_section_writer(ELFIO::section *section)
: m_section(section)
{
}
void elfio_section_writer::operator()(const std::string& label, const std::byte *data, std::size_t size)
{
labels.push_back(label);
sizes.push_back(size);
m_section->append_data(reinterpret_cast<const char *>(data), size);
}
std::pair<std::string_view, bool> elfio_section_writer::operator()(const std::byte *data, std::size_t size)
{
auto found = std::find_if(begin(), end(),
[data, size](elfio_section_writer::entry entry) {
return size == entry.size && std::memcmp(entry.data, data, size) == 0;
});
if (found == end())
{
(*this)(".CL" + std::to_string(labels.size()), data, size);
return std::pair<std::string_view, bool>(labels.back(), true);
}
return std::pair<std::string_view, bool>(found->label, false);
}
elfio_section_writer::iterator elfio_section_writer::begin() const
{
return elfio_section_writer::iterator(labels.cbegin(), sizes.cbegin(),
reinterpret_cast<const std::byte *>(m_section->get_data()));
}
elfio_section_writer::iterator elfio_section_writer::end() const
{
return elfio_section_writer::iterator(labels.cend(), sizes.cend());
}
ELFIO::section *elfio_section_writer::section() noexcept
{
return m_section;
}
elfio_writer::elfio_writer(ELFIO::section *text, ELFIO::section *read_only,
ELFIO::symbol_section_accessor symbol_accessor, ELFIO::string_section_accessor string_accessor)
: text(text), read_only(elfio_section_writer(read_only)),
symbol_accessor(symbol_accessor), string_accessor(string_accessor)
{
}
std::size_t elfio_writer::sink(const std::string& label, const std::byte *data, std::size_t size)
{
auto offset = text->get_size();
text->append_data(reinterpret_cast<const char *>(data), size);
this->symbol_accessor.add_symbol(this->string_accessor, label.data(), offset, label.size(),
ELFIO::STB_GLOBAL, ELFIO::STT_FUNC, 0, text->get_index());
return text->get_size();
}
std::string_view elfio_writer::sink(const std::byte *data, std::size_t size)
{
auto offset = read_only.section()->get_size();
auto [result, inserted] = read_only(data, size);
if (inserted)
{
this->symbol_accessor.add_symbol(this->string_accessor, result.data(), offset,
result.size(), ELFIO::STB_LOCAL, ELFIO::STT_NOTYPE, 0,
read_only.section()->get_index());
}
return result;
}
void elfio_writer::sink(const std::string& label)
{
this->symbol_accessor.add_symbol(this->string_accessor, "printf", 0x00000000, 0,
ELFIO::STB_GLOBAL, ELFIO::STT_NOTYPE, 0, ELFIO::SHN_UNDEF);
}
std::size_t elfio_writer::size() const
{
return this->text->get_size();
}
std::ptrdiff_t lookup(ELFIO::symbol_section_accessor symbol_accessor, const std::string& label)
{
for (ptrdiff_t j = 0; j < symbol_accessor.get_symbols_num(); ++j)
{
std::string name;
ELFIO::Elf64_Addr value;
ELFIO::Elf_Xword size;
unsigned char bind;
unsigned char type;
ELFIO::Elf_Half section_index;
unsigned char other;
symbol_accessor.get_symbol(j, name, value, size, bind, type, section_index, other);
if (name == label)
{
return j;
}
}
return -1;
}
void riscv32_elf(source::program *ast, source::intermediate_code_generator intermediate_code_generator,
std::shared_ptr<source::symbol_table> table, const std::filesystem::path& out_file)
{
ELFIO::elfio writer;
writer.create(ELFIO::ELFCLASS32, ELFIO::ELFDATA2LSB);
writer.set_os_abi(ELFIO::ELFOSABI_NONE);
writer.set_type(ELFIO::ET_REL);
writer.set_flags(0x4); // EF_RISCV_FLOAT_ABI_DOUBLE
writer.set_machine(ELFIO::EM_RISCV);
// Create code section
ELFIO::section* text_sec = writer.sections.add(".text");
text_sec->set_type(ELFIO::SHT_PROGBITS);
text_sec->set_flags(ELFIO::SHF_ALLOC | ELFIO::SHF_EXECINSTR);
text_sec->set_addr_align(0x1);
// Create string table section
ELFIO::section* str_sec = writer.sections.add(".strtab");
str_sec->set_type(ELFIO::SHT_STRTAB);
// Create string table writer
ELFIO::string_section_accessor stra(str_sec);
// Create symbol table section
ELFIO::section* sym_sec = writer.sections.add(".symtab");
sym_sec->set_type(ELFIO::SHT_SYMTAB);
sym_sec->set_info(2);
sym_sec->set_addr_align(0x4);
sym_sec->set_entry_size(writer.get_default_entry_size(ELFIO::SHT_SYMTAB));
sym_sec->set_link(str_sec->get_index());
// Create relocation table section
ELFIO::section* rel_sec = writer.sections.add(".rel.text");
rel_sec->set_type(ELFIO::SHT_REL);
rel_sec->set_info(text_sec->get_index());
rel_sec->set_addr_align(0x4);
rel_sec->set_entry_size(writer.get_default_entry_size(ELFIO::SHT_REL));
rel_sec->set_link(sym_sec->get_index());
rel_sec->set_flags(ELFIO::SHF_ALLOC);
// Create read only data section
ELFIO::section* ro_sec = writer.sections.add(".rodata");
ro_sec->set_type(ELFIO::SHT_PROGBITS);
ro_sec->set_flags(ELFIO::SHF_ALLOC);
ro_sec->set_addr_align(0x4);
// Create symbol relocation table writers
ELFIO::symbol_section_accessor syma(writer, sym_sec);
ELFIO::relocation_section_accessor rela(writer, rel_sec);
auto _writer = std::make_shared<elfio_writer>(text_sec, ro_sec, syma, stra);
// visitor _visitor{ _writer, table };
// _visitor.visit(ast);
auto references = generate(intermediate_code_generator, table, _writer);
syma.arrange_local_symbols();
for (auto& reference : references)
// for (auto& reference : _visitor.references)
{
ELFIO::Elf_Word relocated_symbol = lookup(syma, reference.name);
switch (reference.target)
{
case address_t::high20:
rela.add_entry(reference.offset, relocated_symbol, 26 /* ELFIO::R_RISCV_HI20 */);
// rela.add_entry(reference.offset, relocated_symbol, 51 /* ELFIO::R_RISCV_RELAX */);
break;
case address_t::lower12i:
rela.add_entry(reference.offset, relocated_symbol, 27 /* ELFIO::R_RISCV_LO12_I */);
// rela.add_entry(reference.offset, relocated_symbol, 51 /* ELFIO::R_RISCV_RELAX */);
break;
case address_t::text:
rela.add_entry(reference.offset, relocated_symbol, 18 /* ELFIO::R_RISCV_CALL */);
// rela.add_entry(reference.offset, relocated_symbol, 51 /* ELFIO::R_RISCV_RELAX */);
break;
}
}
// Create ELF object file
writer.save(out_file);
}
}

50
cli/cl.cpp Normal file
View File

@ -0,0 +1,50 @@
#include "elna/cli/cl.hpp"
#include "elna/backend/target.hpp"
#include "elna/source/semantic.hpp"
#include "elna/source/optimizer.hpp"
#include <iostream>
namespace elna::cli
{
void print_error(const std::unique_ptr<source::error>& compile_error)
{
std::cerr << compile_error->path().string() << ":"
<< compile_error->line() << ':' << compile_error->column()
<< ": " << compile_error->what() << std::endl;
}
int compile(const std::filesystem::path& in_file, const std::filesystem::path& out_file)
{
try
{
source::result<source::lexer> lex_result = source::tokenize(in_file);
if (lex_result.has_errors())
{
print_errors(lex_result.errors().cbegin(), lex_result.errors().cend());
return 1;
}
source::parser parser{ std::move(lex_result.success()) };
auto ast = parser.parse();
if (ast == nullptr)
{
print_errors(parser.errors().cbegin(), parser.errors().cend());
return 2;
}
auto global_scope = std::make_shared<source::symbol_table>();
source::name_analysis_visitor(global_scope).visit(ast.get());
source::type_analysis_visitor().visit(ast.get());
source::allocator_visitor(global_scope).visit(ast.get());
source::intermediate_code_generator intermediate_code_generator{ global_scope };
intermediate_code_generator.visit(ast.get());
riscv::riscv32_elf(ast.get(), intermediate_code_generator, global_scope, out_file);
}
catch (std::ios_base::failure&)
{
return 3;
}
return 0;
}
}

47
cli/main.cpp Normal file
View File

@ -0,0 +1,47 @@
#include "elna/cli/cl.hpp"
#include <filesystem>
#include <iostream>
#include <boost/program_options.hpp>
int main(int argc, char **argv)
{
boost::program_options::options_description visible{ "Allowed options" };
boost::program_options::options_description description;
boost::program_options::positional_options_description positional;
boost::program_options::variables_map variables_map;
visible.add_options()
("help", "Print this help message")
("output,o", boost::program_options::value<std::filesystem::path>(), "Output object file")
;
description.add_options()
("input", boost::program_options::value<std::filesystem::path>()->required())
;
description.add(visible);
positional.add("input", 1);
auto parsed = boost::program_options::command_line_parser(argc, argv)
.options(description).positional(positional)
.run();
boost::program_options::store(parsed, variables_map);
boost::program_options::notify(variables_map);
if (variables_map.count("help"))
{
std::cout << description << std::endl;
return 0;
}
const auto in_file = variables_map["input"].as<std::filesystem::path>();
std::filesystem::path out_file;
if (variables_map.count("output"))
{
out_file = variables_map["output"].as<std::filesystem::path>();
}
else
{
out_file = in_file.filename().replace_extension(".o");
}
return elna::cli::compile(in_file, out_file);
}

View File

@ -1,9 +0,0 @@
{
"dependencies": {
"tanya": "~>0.18.0"
},
"name": "elna",
"targetType": "executable",
"targetPath": "build/bin",
"mainSourceFile": "source/main.d"
}

1347
include/elfio/elf_types.hpp Normal file

File diff suppressed because it is too large Load Diff

1096
include/elfio/elfio.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
/*
Copyright (C) 2001-present by Serge Lamikhov-Center
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ELFIO_ARRAY_HPP
#define ELFIO_ARRAY_HPP
#include <algorithm>
namespace ELFIO {
//------------------------------------------------------------------------------
template <class S, typename T> class array_section_accessor_template
{
public:
//------------------------------------------------------------------------------
explicit array_section_accessor_template( const elfio& elf_file,
S* section )
: elf_file( elf_file ), array_section( section )
{
}
//------------------------------------------------------------------------------
Elf_Xword get_entries_num() const
{
Elf_Xword entry_size = sizeof( T );
return array_section->get_size() / entry_size;
}
//------------------------------------------------------------------------------
bool get_entry( Elf_Xword index, Elf64_Addr& address ) const
{
if ( index >= get_entries_num() ) { // Is index valid
return false;
}
const endianess_convertor& convertor = elf_file.get_convertor();
const T temp = *reinterpret_cast<const T*>( array_section->get_data() +
index * sizeof( T ) );
address = convertor( temp );
return true;
}
//------------------------------------------------------------------------------
void add_entry( Elf64_Addr address )
{
const endianess_convertor& convertor = elf_file.get_convertor();
T temp = convertor( (T)address );
array_section->append_data( reinterpret_cast<char*>( &temp ),
sizeof( temp ) );
}
private:
//------------------------------------------------------------------------------
const elfio& elf_file;
S* array_section;
};
template <typename T = Elf32_Word>
using array_section_accessor = array_section_accessor_template<section, T>;
template <typename T = Elf32_Word>
using const_array_section_accessor =
array_section_accessor_template<const section, T>;
} // namespace ELFIO
#endif // ELFIO_ARRAY_HPP

1346
include/elfio/elfio_dump.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,274 @@
/*
Copyright (C) 2001-present by Serge Lamikhov-Center
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ELFIO_DYNAMIC_HPP
#define ELFIO_DYNAMIC_HPP
#include <algorithm>
namespace ELFIO {
//------------------------------------------------------------------------------
template <class S> class dynamic_section_accessor_template
{
public:
//------------------------------------------------------------------------------
explicit dynamic_section_accessor_template( const elfio& elf_file,
S* section )
: elf_file( elf_file ), dynamic_section( section ), entries_num( 0 )
{
}
//------------------------------------------------------------------------------
Elf_Xword get_entries_num() const
{
size_t needed_entry_size = -1;
if ( elf_file.get_class() == ELFCLASS32 ) {
needed_entry_size = sizeof( Elf32_Dyn );
}
else {
needed_entry_size = sizeof( Elf64_Dyn );
}
if ( ( 0 == entries_num ) &&
( 0 != dynamic_section->get_entry_size() &&
dynamic_section->get_entry_size() >= needed_entry_size ) ) {
entries_num =
dynamic_section->get_size() / dynamic_section->get_entry_size();
Elf_Xword i;
Elf_Xword tag = DT_NULL;
Elf_Xword value = 0;
std::string str;
for ( i = 0; i < entries_num; i++ ) {
get_entry( i, tag, value, str );
if ( tag == DT_NULL )
break;
}
entries_num = std::min<Elf_Xword>( entries_num, i + 1 );
}
return entries_num;
}
//------------------------------------------------------------------------------
bool get_entry( Elf_Xword index,
Elf_Xword& tag,
Elf_Xword& value,
std::string& str ) const
{
if ( index >= get_entries_num() ) { // Is index valid
return false;
}
if ( elf_file.get_class() == ELFCLASS32 ) {
generic_get_entry_dyn<Elf32_Dyn>( index, tag, value );
}
else {
generic_get_entry_dyn<Elf64_Dyn>( index, tag, value );
}
// If the tag has a string table reference - prepare the string
if ( tag == DT_NEEDED || tag == DT_SONAME || tag == DT_RPATH ||
tag == DT_RUNPATH ) {
string_section_accessor strsec(
elf_file.sections[get_string_table_index()] );
const char* result = strsec.get_string( (Elf_Word)value );
if ( nullptr == result ) {
str.clear();
return false;
}
str = result;
}
else {
str.clear();
}
return true;
}
//------------------------------------------------------------------------------
void add_entry( Elf_Xword tag, Elf_Xword value )
{
if ( elf_file.get_class() == ELFCLASS32 ) {
generic_add_entry_dyn<Elf32_Dyn>( tag, value );
}
else {
generic_add_entry_dyn<Elf64_Dyn>( tag, value );
}
}
//------------------------------------------------------------------------------
void add_entry( Elf_Xword tag, const std::string& str )
{
string_section_accessor strsec(
elf_file.sections[get_string_table_index()] );
Elf_Xword value = strsec.add_string( str );
add_entry( tag, value );
}
//------------------------------------------------------------------------------
private:
//------------------------------------------------------------------------------
Elf_Half get_string_table_index() const
{
return (Elf_Half)dynamic_section->get_link();
}
//------------------------------------------------------------------------------
template <class T>
void generic_get_entry_dyn( Elf_Xword index,
Elf_Xword& tag,
Elf_Xword& value ) const
{
const endianess_convertor& convertor = elf_file.get_convertor();
// Check unusual case when dynamic section has no data
if ( dynamic_section->get_data() == nullptr ||
( index + 1 ) * dynamic_section->get_entry_size() >
dynamic_section->get_size() ||
dynamic_section->get_entry_size() < sizeof( T ) ) {
tag = DT_NULL;
value = 0;
return;
}
const T* pEntry = reinterpret_cast<const T*>(
dynamic_section->get_data() +
index * dynamic_section->get_entry_size() );
tag = convertor( pEntry->d_tag );
switch ( tag ) {
case DT_NULL:
case DT_SYMBOLIC:
case DT_TEXTREL:
case DT_BIND_NOW:
value = 0;
break;
case DT_NEEDED:
case DT_PLTRELSZ:
case DT_RELASZ:
case DT_RELAENT:
case DT_STRSZ:
case DT_SYMENT:
case DT_SONAME:
case DT_RPATH:
case DT_RELSZ:
case DT_RELENT:
case DT_PLTREL:
case DT_INIT_ARRAYSZ:
case DT_FINI_ARRAYSZ:
case DT_RUNPATH:
case DT_FLAGS:
case DT_PREINIT_ARRAYSZ:
value = convertor( pEntry->d_un.d_val );
break;
case DT_PLTGOT:
case DT_HASH:
case DT_STRTAB:
case DT_SYMTAB:
case DT_RELA:
case DT_INIT:
case DT_FINI:
case DT_REL:
case DT_DEBUG:
case DT_JMPREL:
case DT_INIT_ARRAY:
case DT_FINI_ARRAY:
case DT_PREINIT_ARRAY:
default:
value = convertor( pEntry->d_un.d_ptr );
break;
}
}
//------------------------------------------------------------------------------
template <class T>
void generic_add_entry_dyn( Elf_Xword tag, Elf_Xword value )
{
const endianess_convertor& convertor = elf_file.get_convertor();
T entry;
switch ( tag ) {
case DT_NULL:
case DT_SYMBOLIC:
case DT_TEXTREL:
case DT_BIND_NOW:
entry.d_un.d_val = convertor( decltype( entry.d_un.d_val )( 0 ) );
break;
case DT_NEEDED:
case DT_PLTRELSZ:
case DT_RELASZ:
case DT_RELAENT:
case DT_STRSZ:
case DT_SYMENT:
case DT_SONAME:
case DT_RPATH:
case DT_RELSZ:
case DT_RELENT:
case DT_PLTREL:
case DT_INIT_ARRAYSZ:
case DT_FINI_ARRAYSZ:
case DT_RUNPATH:
case DT_FLAGS:
case DT_PREINIT_ARRAYSZ:
entry.d_un.d_val =
convertor( decltype( entry.d_un.d_val )( value ) );
break;
case DT_PLTGOT:
case DT_HASH:
case DT_STRTAB:
case DT_SYMTAB:
case DT_RELA:
case DT_INIT:
case DT_FINI:
case DT_REL:
case DT_DEBUG:
case DT_JMPREL:
case DT_INIT_ARRAY:
case DT_FINI_ARRAY:
case DT_PREINIT_ARRAY:
default:
entry.d_un.d_ptr =
convertor( decltype( entry.d_un.d_val )( value ) );
break;
}
entry.d_tag = convertor( decltype( entry.d_tag )( tag ) );
dynamic_section->append_data( reinterpret_cast<char*>( &entry ),
sizeof( entry ) );
}
//------------------------------------------------------------------------------
private:
const elfio& elf_file;
S* dynamic_section;
mutable Elf_Xword entries_num;
};
using dynamic_section_accessor = dynamic_section_accessor_template<section>;
using const_dynamic_section_accessor =
dynamic_section_accessor_template<const section>;
} // namespace ELFIO
#endif // ELFIO_DYNAMIC_HPP

View File

@ -0,0 +1,153 @@
/*
Copyright (C) 2001-present by Serge Lamikhov-Center
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ELF_HEADER_HPP
#define ELF_HEADER_HPP
#include <iostream>
namespace ELFIO {
class elf_header
{
public:
virtual ~elf_header() = default;
virtual bool load( std::istream& stream ) = 0;
virtual bool save( std::ostream& stream ) const = 0;
// ELF header functions
ELFIO_GET_ACCESS_DECL( unsigned char, class );
ELFIO_GET_ACCESS_DECL( unsigned char, elf_version );
ELFIO_GET_ACCESS_DECL( unsigned char, encoding );
ELFIO_GET_ACCESS_DECL( Elf_Half, header_size );
ELFIO_GET_ACCESS_DECL( Elf_Half, section_entry_size );
ELFIO_GET_ACCESS_DECL( Elf_Half, segment_entry_size );
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, version );
ELFIO_GET_SET_ACCESS_DECL( unsigned char, os_abi );
ELFIO_GET_SET_ACCESS_DECL( unsigned char, abi_version );
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, type );
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, machine );
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags );
ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, entry );
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, sections_num );
ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, sections_offset );
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, segments_num );
ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, segments_offset );
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, section_name_str_index );
};
template <class T> struct elf_header_impl_types;
template <> struct elf_header_impl_types<Elf32_Ehdr>
{
using Phdr_type = Elf32_Phdr;
using Shdr_type = Elf32_Shdr;
static const unsigned char file_class = ELFCLASS32;
};
template <> struct elf_header_impl_types<Elf64_Ehdr>
{
using Phdr_type = Elf64_Phdr;
using Shdr_type = Elf64_Shdr;
static const unsigned char file_class = ELFCLASS64;
};
template <class T> class elf_header_impl : public elf_header
{
public:
//------------------------------------------------------------------------------
elf_header_impl( endianess_convertor* convertor,
unsigned char encoding,
const address_translator* translator )
: convertor( convertor ), translator( translator )
{
header.e_ident[EI_MAG0] = ELFMAG0;
header.e_ident[EI_MAG1] = ELFMAG1;
header.e_ident[EI_MAG2] = ELFMAG2;
header.e_ident[EI_MAG3] = ELFMAG3;
header.e_ident[EI_CLASS] = elf_header_impl_types<T>::file_class;
header.e_ident[EI_DATA] = encoding;
header.e_ident[EI_VERSION] = EV_CURRENT;
header.e_version = ( *convertor )( (Elf_Word)EV_CURRENT );
header.e_ehsize = ( sizeof( header ) );
header.e_ehsize = ( *convertor )( header.e_ehsize );
header.e_shstrndx = ( *convertor )( (Elf_Half)1 );
header.e_phentsize =
sizeof( typename elf_header_impl_types<T>::Phdr_type );
header.e_shentsize =
sizeof( typename elf_header_impl_types<T>::Shdr_type );
header.e_phentsize = ( *convertor )( header.e_phentsize );
header.e_shentsize = ( *convertor )( header.e_shentsize );
}
//------------------------------------------------------------------------------
bool load( std::istream& stream ) override
{
stream.seekg( ( *translator )[0] );
stream.read( reinterpret_cast<char*>( &header ), sizeof( header ) );
return ( stream.gcount() == sizeof( header ) );
}
//------------------------------------------------------------------------------
bool save( std::ostream& stream ) const override
{
stream.seekp( ( *translator )[0] );
stream.write( reinterpret_cast<const char*>( &header ),
sizeof( header ) );
return stream.good();
}
//------------------------------------------------------------------------------
// ELF header functions
ELFIO_GET_ACCESS( unsigned char, class, header.e_ident[EI_CLASS] );
ELFIO_GET_ACCESS( unsigned char, elf_version, header.e_ident[EI_VERSION] );
ELFIO_GET_ACCESS( unsigned char, encoding, header.e_ident[EI_DATA] );
ELFIO_GET_ACCESS( Elf_Half, header_size, header.e_ehsize );
ELFIO_GET_ACCESS( Elf_Half, section_entry_size, header.e_shentsize );
ELFIO_GET_ACCESS( Elf_Half, segment_entry_size, header.e_phentsize );
ELFIO_GET_SET_ACCESS( Elf_Word, version, header.e_version );
ELFIO_GET_SET_ACCESS( unsigned char, os_abi, header.e_ident[EI_OSABI] );
ELFIO_GET_SET_ACCESS( unsigned char,
abi_version,
header.e_ident[EI_ABIVERSION] );
ELFIO_GET_SET_ACCESS( Elf_Half, type, header.e_type );
ELFIO_GET_SET_ACCESS( Elf_Half, machine, header.e_machine );
ELFIO_GET_SET_ACCESS( Elf_Word, flags, header.e_flags );
ELFIO_GET_SET_ACCESS( Elf_Half, section_name_str_index, header.e_shstrndx );
ELFIO_GET_SET_ACCESS( Elf64_Addr, entry, header.e_entry );
ELFIO_GET_SET_ACCESS( Elf_Half, sections_num, header.e_shnum );
ELFIO_GET_SET_ACCESS( Elf64_Off, sections_offset, header.e_shoff );
ELFIO_GET_SET_ACCESS( Elf_Half, segments_num, header.e_phnum );
ELFIO_GET_SET_ACCESS( Elf64_Off, segments_offset, header.e_phoff );
private:
T header = {};
endianess_convertor* convertor = nullptr;
const address_translator* translator = nullptr;
};
} // namespace ELFIO
#endif // ELF_HEADER_HPP

View File

@ -0,0 +1,124 @@
/*
Copyright (C) 2001-present by Serge Lamikhov-Center
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ELFIO_MODINFO_HPP
#define ELFIO_MODINFO_HPP
#include <string_view>
#include <vector>
namespace ELFIO {
//------------------------------------------------------------------------------
template <class S> class modinfo_section_accessor_template
{
public:
//------------------------------------------------------------------------------
explicit modinfo_section_accessor_template( S* section )
: modinfo_section( section )
{
process_section();
}
//------------------------------------------------------------------------------
Elf_Word get_attribute_num() const { return (Elf_Word)content.size(); }
//------------------------------------------------------------------------------
bool
get_attribute( Elf_Word no, std::string& field, std::string& value ) const
{
if ( no < content.size() ) {
field = content[no].first;
value = content[no].second;
return true;
}
return false;
}
//------------------------------------------------------------------------------
bool get_attribute( const std::string_view& field_name,
std::string& value ) const
{
for ( const auto [first, second] : content ) {
if ( field_name == first ) {
value = second;
return true;
}
}
return false;
}
//------------------------------------------------------------------------------
Elf_Word add_attribute( const std::string& field, const std::string& value )
{
Elf_Word current_position = 0;
if ( modinfo_section ) {
// Strings are addeded to the end of the current section data
current_position = (Elf_Word)modinfo_section->get_size();
std::string attribute = field + "=" + value;
modinfo_section->append_data( attribute + '\0' );
content.emplace_back( field, value );
}
return current_position;
}
//------------------------------------------------------------------------------
private:
void process_section()
{
const char* pdata = modinfo_section->get_data();
if ( pdata ) {
ELFIO::Elf_Xword i = 0;
while ( i < modinfo_section->get_size() ) {
while ( i < modinfo_section->get_size() && !pdata[i] )
i++;
if ( i < modinfo_section->get_size() ) {
std::string info = pdata + i;
size_t loc = info.find( '=' );
content.emplace_back( info.substr( 0, loc ),
info.substr( loc + 1 ) );
i += info.length();
}
}
}
}
//------------------------------------------------------------------------------
private:
S* modinfo_section;
std::vector<std::pair<std::string, std::string>> content;
};
using modinfo_section_accessor = modinfo_section_accessor_template<section>;
using const_modinfo_section_accessor =
modinfo_section_accessor_template<const section>;
} // namespace ELFIO
#endif // ELFIO_MODINFO_HPP

View File

@ -0,0 +1,184 @@
/*
Copyright (C) 2001-present by Serge Lamikhov-Center
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ELFIO_NOTE_HPP
#define ELFIO_NOTE_HPP
namespace ELFIO {
//------------------------------------------------------------------------------
// There are discrepancies in documentations. SCO documentation
// (http://www.sco.com/developers/gabi/latest/ch5.pheader.html#note_section)
// requires 8 byte entries alignment for 64-bit ELF file,
// but Oracle's definition uses the same structure
// for 32-bit and 64-bit formats.
// (https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-18048.html)
//
// It looks like EM_X86_64 Linux implementation is similar to Oracle's
// definition. Therefore, the same alignment works for both formats
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
template <class S, Elf_Xword ( S::*F_get_size )() const>
class note_section_accessor_template
{
public:
//------------------------------------------------------------------------------
explicit note_section_accessor_template( const elfio& elf_file, S* section )
: elf_file( elf_file ), notes( section )
{
process_section();
}
//------------------------------------------------------------------------------
Elf_Word get_notes_num() const
{
return (Elf_Word)note_start_positions.size();
}
//------------------------------------------------------------------------------
bool get_note( Elf_Word index,
Elf_Word& type,
std::string& name,
char*& desc,
Elf_Word& descSize ) const
{
if ( index >= ( notes->*F_get_size )() ) {
return false;
}
const char* pData = notes->get_data() + note_start_positions[index];
int align = sizeof( Elf_Word );
const endianess_convertor& convertor = elf_file.get_convertor();
type = convertor( *(const Elf_Word*)( pData + 2 * (size_t)align ) );
Elf_Word namesz = convertor( *(const Elf_Word*)( pData ) );
descSize = convertor( *(const Elf_Word*)( pData + sizeof( namesz ) ) );
Elf_Xword max_name_size =
( notes->*F_get_size )() - note_start_positions[index];
if ( namesz < 1 || namesz > max_name_size ||
(Elf_Xword)namesz + descSize > max_name_size ) {
return false;
}
name.assign( pData + 3 * (size_t)align, namesz - 1 );
if ( 0 == descSize ) {
desc = nullptr;
}
else {
desc = const_cast<char*>( pData + 3 * (size_t)align +
( ( namesz + align - 1 ) / align ) *
(size_t)align );
}
return true;
}
//------------------------------------------------------------------------------
void add_note( Elf_Word type,
const std::string& name,
const char* desc,
Elf_Word descSize )
{
const endianess_convertor& convertor = elf_file.get_convertor();
int align = sizeof( Elf_Word );
Elf_Word nameLen = (Elf_Word)name.size() + 1;
Elf_Word nameLenConv = convertor( nameLen );
std::string buffer( reinterpret_cast<char*>( &nameLenConv ), align );
Elf_Word descSizeConv = convertor( descSize );
buffer.append( reinterpret_cast<char*>( &descSizeConv ), align );
type = convertor( type );
buffer.append( reinterpret_cast<char*>( &type ), align );
buffer.append( name );
buffer.append( 1, '\x00' );
const char pad[] = { '\0', '\0', '\0', '\0' };
if ( nameLen % align != 0 ) {
buffer.append( pad, (size_t)align - nameLen % align );
}
if ( desc != nullptr && descSize != 0 ) {
buffer.append( desc, descSize );
if ( descSize % align != 0 ) {
buffer.append( pad, (size_t)align - descSize % align );
}
}
note_start_positions.emplace_back( ( notes->*F_get_size )() );
notes->append_data( buffer );
}
private:
//------------------------------------------------------------------------------
void process_section()
{
const endianess_convertor& convertor = elf_file.get_convertor();
const char* data = notes->get_data();
Elf_Xword size = ( notes->*F_get_size )();
Elf_Xword current = 0;
note_start_positions.clear();
// Is it empty?
if ( nullptr == data || 0 == size ) {
return;
}
Elf_Word align = sizeof( Elf_Word );
while ( current + (Elf_Xword)3 * align <= size ) {
Elf_Word namesz = convertor( *(const Elf_Word*)( data + current ) );
Elf_Word descsz = convertor(
*(const Elf_Word*)( data + current + sizeof( namesz ) ) );
Elf_Word advance =
(Elf_Xword)3 * sizeof( Elf_Word ) +
( ( namesz + align - 1 ) / align ) * (Elf_Xword)align +
( ( descsz + align - 1 ) / align ) * (Elf_Xword)align;
if ( namesz < size && descsz < size && current + advance <= size ) {
note_start_positions.emplace_back( current );
}
else {
break;
}
current += advance;
}
}
//------------------------------------------------------------------------------
private:
const elfio& elf_file;
S* notes;
std::vector<Elf_Xword> note_start_positions;
};
using note_section_accessor =
note_section_accessor_template<section, &section::get_size>;
using const_note_section_accessor =
note_section_accessor_template<const section, &section::get_size>;
using note_segment_accessor =
note_section_accessor_template<segment, &segment::get_file_size>;
using const_note_segment_accessor =
note_section_accessor_template<const segment, &segment::get_file_size>;
} // namespace ELFIO
#endif // ELFIO_NOTE_HPP

View File

@ -0,0 +1,460 @@
/*
Copyright (C) 2001-present by Serge Lamikhov-Center
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ELFIO_RELOCATION_HPP
#define ELFIO_RELOCATION_HPP
namespace ELFIO {
template <typename T> struct get_sym_and_type;
template <> struct get_sym_and_type<Elf32_Rel>
{
static int get_r_sym( Elf_Xword info )
{
return ELF32_R_SYM( (Elf_Word)info );
}
static int get_r_type( Elf_Xword info )
{
return ELF32_R_TYPE( (Elf_Word)info );
}
};
template <> struct get_sym_and_type<Elf32_Rela>
{
static int get_r_sym( Elf_Xword info )
{
return ELF32_R_SYM( (Elf_Word)info );
}
static int get_r_type( Elf_Xword info )
{
return ELF32_R_TYPE( (Elf_Word)info );
}
};
template <> struct get_sym_and_type<Elf64_Rel>
{
static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); }
static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); }
};
template <> struct get_sym_and_type<Elf64_Rela>
{
static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); }
static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); }
};
//------------------------------------------------------------------------------
template <class S> class relocation_section_accessor_template
{
public:
//------------------------------------------------------------------------------
explicit relocation_section_accessor_template( const elfio& elf_file,
S* section )
: elf_file( elf_file ), relocation_section( section )
{
}
//------------------------------------------------------------------------------
Elf_Xword get_entries_num() const
{
Elf_Xword nRet = 0;
if ( 0 != relocation_section->get_entry_size() ) {
nRet = relocation_section->get_size() /
relocation_section->get_entry_size();
}
return nRet;
}
//------------------------------------------------------------------------------
bool get_entry( Elf_Xword index,
Elf64_Addr& offset,
Elf_Word& symbol,
unsigned& type,
Elf_Sxword& addend ) const
{
if ( index >= get_entries_num() ) { // Is index valid
return false;
}
if ( elf_file.get_class() == ELFCLASS32 ) {
if ( SHT_REL == relocation_section->get_type() ) {
generic_get_entry_rel<Elf32_Rel>( index, offset, symbol, type,
addend );
}
else if ( SHT_RELA == relocation_section->get_type() ) {
generic_get_entry_rela<Elf32_Rela>( index, offset, symbol, type,
addend );
}
}
else {
if ( SHT_REL == relocation_section->get_type() ) {
generic_get_entry_rel<Elf64_Rel>( index, offset, symbol, type,
addend );
}
else if ( SHT_RELA == relocation_section->get_type() ) {
generic_get_entry_rela<Elf64_Rela>( index, offset, symbol, type,
addend );
}
}
return true;
}
//------------------------------------------------------------------------------
bool get_entry( Elf_Xword index,
Elf64_Addr& offset,
Elf64_Addr& symbolValue,
std::string& symbolName,
unsigned& type,
Elf_Sxword& addend,
Elf_Sxword& calcValue ) const
{
// Do regular job
Elf_Word symbol = 0;
bool ret = get_entry( index, offset, symbol, type, addend );
// Find the symbol
Elf_Xword size;
unsigned char bind;
unsigned char symbolType;
Elf_Half section;
unsigned char other;
symbol_section_accessor symbols(
elf_file, elf_file.sections[get_symbol_table_index()] );
ret = ret && symbols.get_symbol( symbol, symbolName, symbolValue, size,
bind, symbolType, section, other );
if ( ret ) { // Was it successful?
switch ( type ) {
case R_386_NONE: // none
calcValue = 0;
break;
case R_386_32: // S + A
calcValue = symbolValue + addend;
break;
case R_386_PC32: // S + A - P
calcValue = symbolValue + addend - offset;
break;
case R_386_GOT32: // G + A - P
calcValue = 0;
break;
case R_386_PLT32: // L + A - P
calcValue = 0;
break;
case R_386_COPY: // none
calcValue = 0;
break;
case R_386_GLOB_DAT: // S
case R_386_JMP_SLOT: // S
calcValue = symbolValue;
break;
case R_386_RELATIVE: // B + A
calcValue = addend;
break;
case R_386_GOTOFF: // S + A - GOT
calcValue = 0;
break;
case R_386_GOTPC: // GOT + A - P
calcValue = 0;
break;
default: // Not recognized symbol!
calcValue = 0;
break;
}
}
return ret;
}
//------------------------------------------------------------------------------
bool set_entry( Elf_Xword index,
Elf64_Addr offset,
Elf_Word symbol,
unsigned type,
Elf_Sxword addend )
{
if ( index >= get_entries_num() ) { // Is index valid
return false;
}
if ( elf_file.get_class() == ELFCLASS32 ) {
if ( SHT_REL == relocation_section->get_type() ) {
generic_set_entry_rel<Elf32_Rel>( index, offset, symbol, type,
addend );
}
else if ( SHT_RELA == relocation_section->get_type() ) {
generic_set_entry_rela<Elf32_Rela>( index, offset, symbol, type,
addend );
}
}
else {
if ( SHT_REL == relocation_section->get_type() ) {
generic_set_entry_rel<Elf64_Rel>( index, offset, symbol, type,
addend );
}
else if ( SHT_RELA == relocation_section->get_type() ) {
generic_set_entry_rela<Elf64_Rela>( index, offset, symbol, type,
addend );
}
}
return true;
}
//------------------------------------------------------------------------------
void add_entry( Elf64_Addr offset, Elf_Xword info )
{
if ( elf_file.get_class() == ELFCLASS32 ) {
generic_add_entry<Elf32_Rel>( offset, info );
}
else {
generic_add_entry<Elf64_Rel>( offset, info );
}
}
//------------------------------------------------------------------------------
void add_entry( Elf64_Addr offset, Elf_Word symbol, unsigned type )
{
Elf_Xword info;
if ( elf_file.get_class() == ELFCLASS32 ) {
info = ELF32_R_INFO( (Elf_Xword)symbol, type );
}
else {
info = ELF64_R_INFO( (Elf_Xword)symbol, type );
}
add_entry( offset, info );
}
//------------------------------------------------------------------------------
void add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend )
{
if ( elf_file.get_class() == ELFCLASS32 ) {
generic_add_entry<Elf32_Rela>( offset, info, addend );
}
else {
generic_add_entry<Elf64_Rela>( offset, info, addend );
}
}
//------------------------------------------------------------------------------
void add_entry( Elf64_Addr offset,
Elf_Word symbol,
unsigned type,
Elf_Sxword addend )
{
Elf_Xword info;
if ( elf_file.get_class() == ELFCLASS32 ) {
info = ELF32_R_INFO( (Elf_Xword)symbol, type );
}
else {
info = ELF64_R_INFO( (Elf_Xword)symbol, type );
}
add_entry( offset, info, addend );
}
//------------------------------------------------------------------------------
void add_entry( string_section_accessor str_writer,
const char* str,
symbol_section_accessor sym_writer,
Elf64_Addr value,
Elf_Word size,
unsigned char sym_info,
unsigned char other,
Elf_Half shndx,
Elf64_Addr offset,
unsigned type )
{
Elf_Word str_index = str_writer.add_string( str );
Elf_Word sym_index = sym_writer.add_symbol( str_index, value, size,
sym_info, other, shndx );
add_entry( offset, sym_index, type );
}
//------------------------------------------------------------------------------
void swap_symbols( Elf_Xword first, Elf_Xword second )
{
Elf64_Addr offset = 0;
Elf_Word symbol = 0;
unsigned rtype = 0;
Elf_Sxword addend = 0;
for ( Elf_Word i = 0; i < get_entries_num(); i++ ) {
get_entry( i, offset, symbol, rtype, addend );
if ( symbol == first ) {
set_entry( i, offset, (Elf_Word)second, rtype, addend );
}
if ( symbol == second ) {
set_entry( i, offset, (Elf_Word)first, rtype, addend );
}
}
}
//------------------------------------------------------------------------------
private:
//------------------------------------------------------------------------------
Elf_Half get_symbol_table_index() const
{
return (Elf_Half)relocation_section->get_link();
}
//------------------------------------------------------------------------------
template <class T>
void generic_get_entry_rel( Elf_Xword index,
Elf64_Addr& offset,
Elf_Word& symbol,
unsigned& type,
Elf_Sxword& addend ) const
{
const endianess_convertor& convertor = elf_file.get_convertor();
const T* pEntry = reinterpret_cast<const T*>(
relocation_section->get_data() +
index * relocation_section->get_entry_size() );
offset = convertor( pEntry->r_offset );
Elf_Xword tmp = convertor( pEntry->r_info );
symbol = get_sym_and_type<T>::get_r_sym( tmp );
type = get_sym_and_type<T>::get_r_type( tmp );
addend = 0;
}
//------------------------------------------------------------------------------
template <class T>
void generic_get_entry_rela( Elf_Xword index,
Elf64_Addr& offset,
Elf_Word& symbol,
unsigned& type,
Elf_Sxword& addend ) const
{
const endianess_convertor& convertor = elf_file.get_convertor();
const T* pEntry = reinterpret_cast<const T*>(
relocation_section->get_data() +
index * relocation_section->get_entry_size() );
offset = convertor( pEntry->r_offset );
Elf_Xword tmp = convertor( pEntry->r_info );
symbol = get_sym_and_type<T>::get_r_sym( tmp );
type = get_sym_and_type<T>::get_r_type( tmp );
addend = convertor( pEntry->r_addend );
}
//------------------------------------------------------------------------------
template <class T>
void generic_set_entry_rel( Elf_Xword index,
Elf64_Addr offset,
Elf_Word symbol,
unsigned type,
Elf_Sxword )
{
const endianess_convertor& convertor = elf_file.get_convertor();
T* pEntry = const_cast<T*>( reinterpret_cast<const T*>(
relocation_section->get_data() +
index * relocation_section->get_entry_size() ) );
if ( elf_file.get_class() == ELFCLASS32 ) {
pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type );
}
else {
pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type );
}
pEntry->r_offset = decltype( pEntry->r_offset )( offset );
pEntry->r_offset = convertor( pEntry->r_offset );
pEntry->r_info = convertor( pEntry->r_info );
}
//------------------------------------------------------------------------------
template <class T>
void generic_set_entry_rela( Elf_Xword index,
Elf64_Addr offset,
Elf_Word symbol,
unsigned type,
Elf_Sxword addend )
{
const endianess_convertor& convertor = elf_file.get_convertor();
T* pEntry = const_cast<T*>( reinterpret_cast<const T*>(
relocation_section->get_data() +
index * relocation_section->get_entry_size() ) );
if ( elf_file.get_class() == ELFCLASS32 ) {
pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type );
}
else {
pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type );
}
pEntry->r_offset = decltype( pEntry->r_offset )( offset );
pEntry->r_addend = decltype( pEntry->r_addend )( addend );
pEntry->r_offset = convertor( pEntry->r_offset );
pEntry->r_info = convertor( pEntry->r_info );
pEntry->r_addend = convertor( pEntry->r_addend );
}
//------------------------------------------------------------------------------
template <class T>
void generic_add_entry( Elf64_Addr offset, Elf_Xword info )
{
const endianess_convertor& convertor = elf_file.get_convertor();
T entry;
entry.r_offset = decltype( entry.r_offset )( offset );
entry.r_info = decltype( entry.r_info )( info );
entry.r_offset = convertor( entry.r_offset );
entry.r_info = convertor( entry.r_info );
relocation_section->append_data( reinterpret_cast<char*>( &entry ),
sizeof( entry ) );
}
//------------------------------------------------------------------------------
template <class T>
void
generic_add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend )
{
const endianess_convertor& convertor = elf_file.get_convertor();
T entry;
entry.r_offset = offset;
entry.r_info = info;
entry.r_addend = addend;
entry.r_offset = convertor( entry.r_offset );
entry.r_info = convertor( entry.r_info );
entry.r_addend = convertor( entry.r_addend );
relocation_section->append_data( reinterpret_cast<char*>( &entry ),
sizeof( entry ) );
}
//------------------------------------------------------------------------------
private:
const elfio& elf_file;
S* relocation_section = nullptr;
};
using relocation_section_accessor =
relocation_section_accessor_template<section>;
using const_relocation_section_accessor =
relocation_section_accessor_template<const section>;
} // namespace ELFIO
#endif // ELFIO_RELOCATION_HPP

View File

@ -0,0 +1,367 @@
/*
Copyright (C) 2001-present by Serge Lamikhov-Center
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ELFIO_SECTION_HPP
#define ELFIO_SECTION_HPP
#include <string>
#include <iostream>
#include <new>
#include <limits>
namespace ELFIO {
class section
{
friend class elfio;
public:
virtual ~section() = default;
ELFIO_GET_ACCESS_DECL( Elf_Half, index );
ELFIO_GET_SET_ACCESS_DECL( std::string, name );
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type );
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, flags );
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, info );
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, link );
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, addr_align );
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, entry_size );
ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, address );
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, size );
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, name_string_offset );
ELFIO_GET_ACCESS_DECL( Elf64_Off, offset );
virtual const char* get_data() const = 0;
virtual void set_data( const char* raw_data, Elf_Word size ) = 0;
virtual void set_data( const std::string& data ) = 0;
virtual void append_data( const char* raw_data, Elf_Word size ) = 0;
virtual void append_data( const std::string& data ) = 0;
virtual void
insert_data( Elf_Xword pos, const char* raw_data, Elf_Word size ) = 0;
virtual void insert_data( Elf_Xword pos, const std::string& data ) = 0;
virtual size_t get_stream_size() const = 0;
virtual void set_stream_size( size_t value ) = 0;
protected:
ELFIO_SET_ACCESS_DECL( Elf64_Off, offset );
ELFIO_SET_ACCESS_DECL( Elf_Half, index );
virtual bool load( std::istream& stream,
std::streampos header_offset,
bool is_lazy ) = 0;
virtual void save( std::ostream& stream,
std::streampos header_offset,
std::streampos data_offset ) = 0;
virtual bool is_address_initialized() const = 0;
};
template <class T> class section_impl : public section
{
public:
//------------------------------------------------------------------------------
section_impl( const endianess_convertor* convertor,
const address_translator* translator,
const std::shared_ptr<compression_interface>& compression )
: convertor( convertor ), translator( translator ),
compression( compression )
{
}
//------------------------------------------------------------------------------
// Section info functions
ELFIO_GET_SET_ACCESS( Elf_Word, type, header.sh_type );
ELFIO_GET_SET_ACCESS( Elf_Xword, flags, header.sh_flags );
ELFIO_GET_SET_ACCESS( Elf_Xword, size, header.sh_size );
ELFIO_GET_SET_ACCESS( Elf_Word, link, header.sh_link );
ELFIO_GET_SET_ACCESS( Elf_Word, info, header.sh_info );
ELFIO_GET_SET_ACCESS( Elf_Xword, addr_align, header.sh_addralign );
ELFIO_GET_SET_ACCESS( Elf_Xword, entry_size, header.sh_entsize );
ELFIO_GET_SET_ACCESS( Elf_Word, name_string_offset, header.sh_name );
ELFIO_GET_ACCESS( Elf64_Addr, address, header.sh_addr );
//------------------------------------------------------------------------------
Elf_Half get_index() const override { return index; }
//------------------------------------------------------------------------------
std::string get_name() const override { return name; }
//------------------------------------------------------------------------------
void set_name( const std::string& name_prm ) override
{
this->name = name_prm;
}
//------------------------------------------------------------------------------
void set_address( const Elf64_Addr& value ) override
{
header.sh_addr = decltype( header.sh_addr )( value );
header.sh_addr = ( *convertor )( header.sh_addr );
is_address_set = true;
}
//------------------------------------------------------------------------------
bool is_address_initialized() const override { return is_address_set; }
//------------------------------------------------------------------------------
const char* get_data() const override
{
if ( is_lazy ) {
load_data();
}
return data.get();
}
//------------------------------------------------------------------------------
void set_data( const char* raw_data, Elf_Word size ) override
{
if ( get_type() != SHT_NOBITS ) {
data = std::unique_ptr<char[]>( new ( std::nothrow ) char[size] );
if ( nullptr != data.get() && nullptr != raw_data ) {
data_size = size;
std::copy( raw_data, raw_data + size, data.get() );
}
else {
data_size = 0;
}
}
set_size( data_size );
if ( translator->empty() ) {
set_stream_size( data_size );
}
}
//------------------------------------------------------------------------------
void set_data( const std::string& str_data ) override
{
return set_data( str_data.c_str(), (Elf_Word)str_data.size() );
}
//------------------------------------------------------------------------------
void append_data( const char* raw_data, Elf_Word size ) override
{
insert_data( get_size(), raw_data, size );
}
//------------------------------------------------------------------------------
void append_data( const std::string& str_data ) override
{
return append_data( str_data.c_str(), (Elf_Word)str_data.size() );
}
//------------------------------------------------------------------------------
void
insert_data( Elf_Xword pos, const char* raw_data, Elf_Word size ) override
{
if ( get_type() != SHT_NOBITS ) {
if ( get_size() + size < data_size ) {
char* d = data.get();
std::copy_backward( d + pos, d + get_size(),
d + get_size() + size );
std::copy( raw_data, raw_data + size, d + pos );
}
else {
data_size = 2 * ( data_size + size );
std::unique_ptr<char[]> new_data(
new ( std::nothrow ) char[data_size] );
if ( nullptr != new_data ) {
char* d = data.get();
std::copy( d, d + pos, new_data.get() );
std::copy( raw_data, raw_data + size,
new_data.get() + pos );
std::copy( d + pos, d + get_size(),
new_data.get() + pos + size );
data = std::move( new_data );
}
else {
size = 0;
}
}
set_size( get_size() + size );
if ( translator->empty() ) {
set_stream_size( get_stream_size() + size );
}
}
}
//------------------------------------------------------------------------------
void insert_data( Elf_Xword pos, const std::string& str_data ) override
{
return insert_data( pos, str_data.c_str(), (Elf_Word)str_data.size() );
}
size_t get_stream_size() const override { return stream_size; }
//------------------------------------------------------------------------------
void set_stream_size( size_t value ) override { stream_size = value; }
//------------------------------------------------------------------------------
protected:
//------------------------------------------------------------------------------
ELFIO_GET_SET_ACCESS( Elf64_Off, offset, header.sh_offset );
//------------------------------------------------------------------------------
void set_index( const Elf_Half& value ) override { index = value; }
bool is_compressed() const
{
return ( ( get_flags() & SHF_RPX_DEFLATE ) ||
( get_flags() & SHF_COMPRESSED ) ) &&
compression != nullptr;
}
//------------------------------------------------------------------------------
bool load( std::istream& stream,
std::streampos header_offset,
bool is_lazy_ ) override
{
pstream = &stream;
is_lazy = is_lazy_;
if ( translator->empty() ) {
stream.seekg( 0, std::istream::end );
set_stream_size( size_t( stream.tellg() ) );
}
else {
set_stream_size( std::numeric_limits<size_t>::max() );
}
stream.seekg( ( *translator )[header_offset] );
stream.read( reinterpret_cast<char*>( &header ), sizeof( header ) );
if ( !is_lazy || is_compressed() ) {
bool ret = load_data();
if ( is_compressed() ) {
Elf_Xword size = get_size();
Elf_Xword uncompressed_size = 0;
auto decompressed_data = compression->inflate(
data.get(), convertor, size, uncompressed_size );
if ( decompressed_data != nullptr ) {
set_size( uncompressed_size );
data = std::move( decompressed_data );
}
}
return ret;
}
return true;
}
bool load_data() const
{
is_lazy = false;
Elf_Xword size = get_size();
if ( nullptr == data && SHT_NULL != get_type() &&
SHT_NOBITS != get_type() && size < get_stream_size() ) {
data.reset( new ( std::nothrow ) char[size_t( size ) + 1] );
if ( ( 0 != size ) && ( nullptr != data ) ) {
pstream->seekg(
( *translator )[( *convertor )( header.sh_offset )] );
pstream->read( data.get(), size );
if ( static_cast<Elf_Xword>( pstream->gcount() ) != size ) {
data = nullptr;
return false;
}
// refresh size because it may have changed if we had to decompress data
size = get_size();
data.get()[size] =
0; // Ensure data is ended with 0 to avoid oob read
data_size = decltype( data_size )( size );
}
else {
data_size = 0;
}
}
return true;
}
//------------------------------------------------------------------------------
void save( std::ostream& stream,
std::streampos header_offset,
std::streampos data_offset ) override
{
if ( 0 != get_index() ) {
header.sh_offset = decltype( header.sh_offset )( data_offset );
header.sh_offset = ( *convertor )( header.sh_offset );
}
save_header( stream, header_offset );
if ( get_type() != SHT_NOBITS && get_type() != SHT_NULL &&
get_size() != 0 && data != nullptr ) {
save_data( stream, data_offset );
}
}
//------------------------------------------------------------------------------
private:
//------------------------------------------------------------------------------
void save_header( std::ostream& stream, std::streampos header_offset ) const
{
adjust_stream_size( stream, header_offset );
stream.write( reinterpret_cast<const char*>( &header ),
sizeof( header ) );
}
//------------------------------------------------------------------------------
void save_data( std::ostream& stream, std::streampos data_offset )
{
adjust_stream_size( stream, data_offset );
if ( ( ( get_flags() & SHF_COMPRESSED ) ||
( get_flags() & SHF_RPX_DEFLATE ) ) &&
compression != nullptr ) {
Elf_Xword decompressed_size = get_size();
Elf_Xword compressed_size = 0;
auto compressed_ptr = compression->deflate(
data.get(), convertor, decompressed_size, compressed_size );
stream.write( compressed_ptr.get(), compressed_size );
}
else {
stream.write( get_data(), get_size() );
}
}
//------------------------------------------------------------------------------
private:
mutable std::istream* pstream = nullptr;
T header = {};
Elf_Half index = 0;
std::string name;
mutable std::unique_ptr<char[]> data;
mutable Elf_Word data_size = 0;
const endianess_convertor* convertor = nullptr;
const address_translator* translator = nullptr;
const std::shared_ptr<compression_interface> compression = nullptr;
bool is_address_set = false;
size_t stream_size = 0;
mutable bool is_lazy = false;
};
} // namespace ELFIO
#endif // ELFIO_SECTION_HPP

View File

@ -0,0 +1,254 @@
/*
Copyright (C) 2001-present by Serge Lamikhov-Center
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ELFIO_SEGMENT_HPP
#define ELFIO_SEGMENT_HPP
#include <iostream>
#include <vector>
#include <new>
#include <limits>
namespace ELFIO {
class segment
{
friend class elfio;
public:
virtual ~segment() = default;
ELFIO_GET_ACCESS_DECL( Elf_Half, index );
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type );
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags );
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, align );
ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, virtual_address );
ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, physical_address );
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, file_size );
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, memory_size );
ELFIO_GET_ACCESS_DECL( Elf64_Off, offset );
virtual const char* get_data() const = 0;
virtual Elf_Half add_section( section* psec, Elf_Xword addr_align ) = 0;
virtual Elf_Half add_section_index( Elf_Half index,
Elf_Xword addr_align ) = 0;
virtual Elf_Half get_sections_num() const = 0;
virtual Elf_Half get_section_index_at( Elf_Half num ) const = 0;
virtual bool is_offset_initialized() const = 0;
protected:
ELFIO_SET_ACCESS_DECL( Elf64_Off, offset );
ELFIO_SET_ACCESS_DECL( Elf_Half, index );
virtual const std::vector<Elf_Half>& get_sections() const = 0;
virtual bool load( std::istream& stream,
std::streampos header_offset,
bool is_lazy ) = 0;
virtual void save( std::ostream& stream,
std::streampos header_offset,
std::streampos data_offset ) = 0;
};
//------------------------------------------------------------------------------
template <class T> class segment_impl : public segment
{
public:
//------------------------------------------------------------------------------
segment_impl( const endianess_convertor* convertor,
const address_translator* translator )
: convertor( convertor ), translator( translator )
{
}
//------------------------------------------------------------------------------
// Section info functions
ELFIO_GET_SET_ACCESS( Elf_Word, type, ph.p_type );
ELFIO_GET_SET_ACCESS( Elf_Word, flags, ph.p_flags );
ELFIO_GET_SET_ACCESS( Elf_Xword, align, ph.p_align );
ELFIO_GET_SET_ACCESS( Elf64_Addr, virtual_address, ph.p_vaddr );
ELFIO_GET_SET_ACCESS( Elf64_Addr, physical_address, ph.p_paddr );
ELFIO_GET_SET_ACCESS( Elf_Xword, file_size, ph.p_filesz );
ELFIO_GET_SET_ACCESS( Elf_Xword, memory_size, ph.p_memsz );
ELFIO_GET_ACCESS( Elf64_Off, offset, ph.p_offset );
//------------------------------------------------------------------------------
Elf_Half get_index() const override { return index; }
//------------------------------------------------------------------------------
const char* get_data() const override
{
if ( is_lazy ) {
load_data();
}
return data.get();
}
//------------------------------------------------------------------------------
Elf_Half add_section_index( Elf_Half sec_index,
Elf_Xword addr_align ) override
{
sections.emplace_back( sec_index );
if ( addr_align > get_align() ) {
set_align( addr_align );
}
return (Elf_Half)sections.size();
}
//------------------------------------------------------------------------------
Elf_Half add_section( section* psec, Elf_Xword addr_align ) override
{
return add_section_index( psec->get_index(), addr_align );
}
//------------------------------------------------------------------------------
Elf_Half get_sections_num() const override
{
return (Elf_Half)sections.size();
}
//------------------------------------------------------------------------------
Elf_Half get_section_index_at( Elf_Half num ) const override
{
if ( num < sections.size() ) {
return sections[num];
}
return Elf_Half( -1 );
}
//------------------------------------------------------------------------------
protected:
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void set_offset( const Elf64_Off& value ) override
{
ph.p_offset = decltype( ph.p_offset )( value );
ph.p_offset = ( *convertor )( ph.p_offset );
is_offset_set = true;
}
//------------------------------------------------------------------------------
bool is_offset_initialized() const override { return is_offset_set; }
//------------------------------------------------------------------------------
const std::vector<Elf_Half>& get_sections() const override
{
return sections;
}
//------------------------------------------------------------------------------
void set_index( const Elf_Half& value ) override { index = value; }
//------------------------------------------------------------------------------
bool load( std::istream& stream,
std::streampos header_offset,
bool is_lazy_ ) override
{
pstream = &stream;
is_lazy = is_lazy_;
if ( translator->empty() ) {
stream.seekg( 0, std::istream::end );
set_stream_size( size_t( stream.tellg() ) );
}
else {
set_stream_size( std::numeric_limits<size_t>::max() );
}
stream.seekg( ( *translator )[header_offset] );
stream.read( reinterpret_cast<char*>( &ph ), sizeof( ph ) );
is_offset_set = true;
if ( !is_lazy ) {
return load_data();
}
return true;
}
//------------------------------------------------------------------------------
bool load_data() const
{
is_lazy = false;
if ( PT_NULL == get_type() || 0 == get_file_size() ) {
return true;
}
pstream->seekg( ( *translator )[( *convertor )( ph.p_offset )] );
Elf_Xword size = get_file_size();
if ( size > get_stream_size() ) {
data = nullptr;
}
else {
data.reset( new ( std::nothrow ) char[(size_t)size + 1] );
if ( nullptr != data.get() && pstream->read( data.get(), size ) ) {
data.get()[size] = 0;
}
else {
data = nullptr;
return false;
}
}
return true;
}
//------------------------------------------------------------------------------
void save( std::ostream& stream,
std::streampos header_offset,
std::streampos data_offset ) override
{
ph.p_offset = decltype( ph.p_offset )( data_offset );
ph.p_offset = ( *convertor )( ph.p_offset );
adjust_stream_size( stream, header_offset );
stream.write( reinterpret_cast<const char*>( &ph ), sizeof( ph ) );
}
//------------------------------------------------------------------------------
size_t get_stream_size() const { return stream_size; }
//------------------------------------------------------------------------------
void set_stream_size( size_t value ) { stream_size = value; }
//------------------------------------------------------------------------------
private:
mutable std::istream* pstream = nullptr;
T ph = {};
Elf_Half index = 0;
mutable std::unique_ptr<char[]> data;
std::vector<Elf_Half> sections;
const endianess_convertor* convertor = nullptr;
const address_translator* translator = nullptr;
size_t stream_size = 0;
bool is_offset_set = false;
mutable bool is_lazy = false;
};
} // namespace ELFIO
#endif // ELFIO_SEGMENT_HPP

View File

@ -0,0 +1,97 @@
/*
Copyright (C) 2001-present by Serge Lamikhov-Center
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ELFIO_STRINGS_HPP
#define ELFIO_STRINGS_HPP
#include <cstdlib>
#include <cstring>
#include <string>
namespace ELFIO {
//------------------------------------------------------------------------------
template <class S> class string_section_accessor_template
{
public:
//------------------------------------------------------------------------------
explicit string_section_accessor_template( S* section )
: string_section( section )
{
}
//------------------------------------------------------------------------------
const char* get_string( Elf_Word index ) const
{
if ( string_section ) {
const char* data = string_section->get_data();
if ( index < string_section->get_size() && nullptr != data ) {
size_t string_length =
strnlen( data + index, string_section->get_size() - index );
if ( string_length < ( string_section->get_size() - index ) )
return data + index;
}
}
return nullptr;
}
//------------------------------------------------------------------------------
Elf_Word add_string( const char* str )
{
Elf_Word current_position = 0;
if ( string_section ) {
// Strings are addeded to the end of the current section data
current_position =
static_cast<Elf_Word>( string_section->get_size() );
if ( current_position == 0 ) {
char empty_string = '\0';
string_section->append_data( &empty_string, 1 );
current_position++;
}
string_section->append_data(
str, static_cast<Elf_Word>( std::strlen( str ) + 1 ) );
}
return current_position;
}
//------------------------------------------------------------------------------
Elf_Word add_string( const std::string& str )
{
return add_string( str.c_str() );
}
//------------------------------------------------------------------------------
private:
S* string_section;
};
using string_section_accessor = string_section_accessor_template<section>;
using const_string_section_accessor =
string_section_accessor_template<const section>;
} // namespace ELFIO
#endif // ELFIO_STRINGS_HPP

View File

@ -0,0 +1,562 @@
/*
Copyright (C) 2001-present by Serge Lamikhov-Center
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ELFIO_SYMBOLS_HPP
#define ELFIO_SYMBOLS_HPP
namespace ELFIO {
//------------------------------------------------------------------------------
template <class S> class symbol_section_accessor_template
{
public:
//------------------------------------------------------------------------------
explicit symbol_section_accessor_template( const elfio& elf_file,
S* symbol_section )
: elf_file( elf_file ), symbol_section( symbol_section )
{
find_hash_section();
}
//------------------------------------------------------------------------------
Elf_Xword get_symbols_num() const
{
Elf_Xword nRet = 0;
size_t minimum_symbol_size;
switch ( elf_file.get_class() ) {
case ELFCLASS32:
minimum_symbol_size = sizeof( Elf32_Sym );
break;
case ELFCLASS64:
minimum_symbol_size = sizeof( Elf64_Sym );
break;
default:
return nRet;
}
if ( symbol_section->get_entry_size() >= minimum_symbol_size &&
symbol_section->get_size() <= symbol_section->get_stream_size() ) {
nRet =
symbol_section->get_size() / symbol_section->get_entry_size();
}
return nRet;
}
//------------------------------------------------------------------------------
bool get_symbol( Elf_Xword index,
std::string& name,
Elf64_Addr& value,
Elf_Xword& size,
unsigned char& bind,
unsigned char& type,
Elf_Half& section_index,
unsigned char& other ) const
{
bool ret = false;
if ( elf_file.get_class() == ELFCLASS32 ) {
ret = generic_get_symbol<Elf32_Sym>( index, name, value, size, bind,
type, section_index, other );
}
else {
ret = generic_get_symbol<Elf64_Sym>( index, name, value, size, bind,
type, section_index, other );
}
return ret;
}
//------------------------------------------------------------------------------
bool get_symbol( const std::string& name,
Elf64_Addr& value,
Elf_Xword& size,
unsigned char& bind,
unsigned char& type,
Elf_Half& section_index,
unsigned char& other ) const
{
bool ret = false;
if ( 0 != get_hash_table_index() ) {
if ( hash_section->get_type() == SHT_HASH ) {
ret = hash_lookup( name, value, size, bind, type, section_index,
other );
}
if ( hash_section->get_type() == SHT_GNU_HASH ||
hash_section->get_type() == DT_GNU_HASH ) {
if ( elf_file.get_class() == ELFCLASS32 ) {
ret = gnu_hash_lookup<uint32_t>(
name, value, size, bind, type, section_index, other );
}
else {
ret = gnu_hash_lookup<uint64_t>(
name, value, size, bind, type, section_index, other );
}
}
}
if ( !ret ) {
for ( Elf_Xword i = 0; !ret && i < get_symbols_num(); i++ ) {
std::string symbol_name;
if ( get_symbol( i, symbol_name, value, size, bind, type,
section_index, other ) ) {
if ( symbol_name == name ) {
ret = true;
}
}
}
}
return ret;
}
//------------------------------------------------------------------------------
bool get_symbol( const Elf64_Addr& value,
std::string& name,
Elf_Xword& size,
unsigned char& bind,
unsigned char& type,
Elf_Half& section_index,
unsigned char& other ) const
{
const endianess_convertor& convertor = elf_file.get_convertor();
Elf_Xword idx = 0;
bool match = false;
Elf64_Addr v = 0;
if ( elf_file.get_class() == ELFCLASS32 ) {
match = generic_search_symbols<Elf32_Sym>(
[&]( const Elf32_Sym* sym ) {
return convertor( sym->st_value ) == value;
},
idx );
}
else {
match = generic_search_symbols<Elf64_Sym>(
[&]( const Elf64_Sym* sym ) {
return convertor( sym->st_value ) == value;
},
idx );
}
if ( match ) {
return get_symbol( idx, name, v, size, bind, type, section_index,
other );
}
return false;
}
//------------------------------------------------------------------------------
Elf_Word add_symbol( Elf_Word name,
Elf64_Addr value,
Elf_Xword size,
unsigned char info,
unsigned char other,
Elf_Half shndx )
{
Elf_Word nRet;
if ( symbol_section->get_size() == 0 ) {
if ( elf_file.get_class() == ELFCLASS32 ) {
nRet = generic_add_symbol<Elf32_Sym>( 0, 0, 0, 0, 0, 0 );
}
else {
nRet = generic_add_symbol<Elf64_Sym>( 0, 0, 0, 0, 0, 0 );
}
}
if ( elf_file.get_class() == ELFCLASS32 ) {
nRet = generic_add_symbol<Elf32_Sym>( name, value, size, info,
other, shndx );
}
else {
nRet = generic_add_symbol<Elf64_Sym>( name, value, size, info,
other, shndx );
}
return nRet;
}
//------------------------------------------------------------------------------
Elf_Word add_symbol( Elf_Word name,
Elf64_Addr value,
Elf_Xword size,
unsigned char bind,
unsigned char type,
unsigned char other,
Elf_Half shndx )
{
return add_symbol( name, value, size, ELF_ST_INFO( bind, type ), other,
shndx );
}
//------------------------------------------------------------------------------
Elf_Word add_symbol( string_section_accessor& pStrWriter,
const char* str,
Elf64_Addr value,
Elf_Xword size,
unsigned char info,
unsigned char other,
Elf_Half shndx )
{
Elf_Word index = pStrWriter.add_string( str );
return add_symbol( index, value, size, info, other, shndx );
}
//------------------------------------------------------------------------------
Elf_Word add_symbol( string_section_accessor& pStrWriter,
const char* str,
Elf64_Addr value,
Elf_Xword size,
unsigned char bind,
unsigned char type,
unsigned char other,
Elf_Half shndx )
{
return add_symbol( pStrWriter, str, value, size,
ELF_ST_INFO( bind, type ), other, shndx );
}
//------------------------------------------------------------------------------
Elf_Xword arrange_local_symbols(
std::function<void( Elf_Xword first, Elf_Xword second )> func =
nullptr )
{
Elf_Xword nRet = 0;
if ( elf_file.get_class() == ELFCLASS32 ) {
nRet = generic_arrange_local_symbols<Elf32_Sym>( func );
}
else {
nRet = generic_arrange_local_symbols<Elf64_Sym>( func );
}
return nRet;
}
//------------------------------------------------------------------------------
private:
//------------------------------------------------------------------------------
void find_hash_section()
{
Elf_Half nSecNo = elf_file.sections.size();
for ( Elf_Half i = 0; i < nSecNo; ++i ) {
const section* sec = elf_file.sections[i];
if ( sec->get_link() == symbol_section->get_index() &&
( sec->get_type() == SHT_HASH ||
sec->get_type() == SHT_GNU_HASH ||
sec->get_type() == DT_GNU_HASH ) ) {
hash_section = sec;
hash_section_index = i;
break;
}
}
}
//------------------------------------------------------------------------------
Elf_Half get_string_table_index() const
{
return (Elf_Half)symbol_section->get_link();
}
//------------------------------------------------------------------------------
Elf_Half get_hash_table_index() const { return hash_section_index; }
//------------------------------------------------------------------------------
bool hash_lookup( const std::string& name,
Elf64_Addr& value,
Elf_Xword& size,
unsigned char& bind,
unsigned char& type,
Elf_Half& section_index,
unsigned char& other ) const
{
bool ret = false;
const endianess_convertor& convertor = elf_file.get_convertor();
Elf_Word nbucket = *(const Elf_Word*)hash_section->get_data();
nbucket = convertor( nbucket );
Elf_Word nchain =
*(const Elf_Word*)( hash_section->get_data() + sizeof( Elf_Word ) );
nchain = convertor( nchain );
Elf_Word val = elf_hash( (const unsigned char*)name.c_str() );
Elf_Word y =
*(const Elf_Word*)( hash_section->get_data() +
( 2 + val % nbucket ) * sizeof( Elf_Word ) );
y = convertor( y );
std::string str;
get_symbol( y, str, value, size, bind, type, section_index, other );
while ( str != name && STN_UNDEF != y && y < nchain ) {
y = *(const Elf_Word*)( hash_section->get_data() +
( 2 + nbucket + y ) * sizeof( Elf_Word ) );
y = convertor( y );
get_symbol( y, str, value, size, bind, type, section_index, other );
}
if ( str == name ) {
ret = true;
}
return ret;
}
//------------------------------------------------------------------------------
template <class T>
bool gnu_hash_lookup( const std::string& name,
Elf64_Addr& value,
Elf_Xword& size,
unsigned char& bind,
unsigned char& type,
Elf_Half& section_index,
unsigned char& other ) const
{
bool ret = false;
const endianess_convertor& convertor = elf_file.get_convertor();
uint32_t nbuckets = *( (uint32_t*)hash_section->get_data() + 0 );
uint32_t symoffset = *( (uint32_t*)hash_section->get_data() + 1 );
uint32_t bloom_size = *( (uint32_t*)hash_section->get_data() + 2 );
uint32_t bloom_shift = *( (uint32_t*)hash_section->get_data() + 3 );
nbuckets = convertor( nbuckets );
symoffset = convertor( symoffset );
bloom_size = convertor( bloom_size );
bloom_shift = convertor( bloom_shift );
T* bloom_filter =
(T*)( hash_section->get_data() + 4 * sizeof( uint32_t ) );
uint32_t hash = elf_gnu_hash( (const unsigned char*)name.c_str() );
uint32_t bloom_index = ( hash / ( 8 * sizeof( T ) ) ) % bloom_size;
T bloom_bits =
( (T)1 << ( hash % ( 8 * sizeof( T ) ) ) ) |
( (T)1 << ( ( hash >> bloom_shift ) % ( 8 * sizeof( T ) ) ) );
if ( ( convertor( bloom_filter[bloom_index] ) & bloom_bits ) !=
bloom_bits )
return ret;
uint32_t bucket = hash % nbuckets;
auto* buckets =
(uint32_t*)( hash_section->get_data() + 4 * sizeof( uint32_t ) +
bloom_size * sizeof( T ) );
auto* chains =
(uint32_t*)( hash_section->get_data() + 4 * sizeof( uint32_t ) +
bloom_size * sizeof( T ) +
nbuckets * sizeof( uint32_t ) );
if ( convertor( buckets[bucket] ) >= symoffset ) {
uint32_t chain_index = convertor( buckets[bucket] ) - symoffset;
uint32_t chain_hash = convertor( chains[chain_index] );
std::string symname;
while ( true ) {
if ( ( chain_hash >> 1 ) == ( hash >> 1 ) &&
get_symbol( chain_index + symoffset, symname, value, size,
bind, type, section_index, other ) &&
name == symname ) {
ret = true;
break;
}
if ( chain_hash & 1 )
break;
chain_hash = convertor( chains[++chain_index] );
}
}
return ret;
}
//------------------------------------------------------------------------------
template <class T> const T* generic_get_symbol_ptr( Elf_Xword index ) const
{
if ( 0 != symbol_section->get_data() && index < get_symbols_num() ) {
const T* pSym = reinterpret_cast<const T*>(
symbol_section->get_data() +
index * symbol_section->get_entry_size() );
return pSym;
}
return nullptr;
}
//------------------------------------------------------------------------------
template <class T>
bool generic_search_symbols( std::function<bool( const T* )> match,
Elf_Xword& idx ) const
{
for ( Elf_Xword i = 0; i < get_symbols_num(); i++ ) {
const T* symPtr = generic_get_symbol_ptr<T>( i );
if ( symPtr == nullptr )
return false;
if ( match( symPtr ) ) {
idx = i;
return true;
}
}
return false;
}
//------------------------------------------------------------------------------
template <class T>
bool generic_get_symbol( Elf_Xword index,
std::string& name,
Elf64_Addr& value,
Elf_Xword& size,
unsigned char& bind,
unsigned char& type,
Elf_Half& section_index,
unsigned char& other ) const
{
bool ret = false;
if ( nullptr != symbol_section->get_data() &&
index < get_symbols_num() ) {
const T* pSym = reinterpret_cast<const T*>(
symbol_section->get_data() +
index * symbol_section->get_entry_size() );
const endianess_convertor& convertor = elf_file.get_convertor();
section* string_section =
elf_file.sections[get_string_table_index()];
string_section_accessor str_reader( string_section );
const char* pStr =
str_reader.get_string( convertor( pSym->st_name ) );
if ( nullptr != pStr ) {
name = pStr;
}
value = convertor( pSym->st_value );
size = convertor( pSym->st_size );
bind = ELF_ST_BIND( pSym->st_info );
type = ELF_ST_TYPE( pSym->st_info );
section_index = convertor( pSym->st_shndx );
other = pSym->st_other;
ret = true;
}
return ret;
}
//------------------------------------------------------------------------------
template <class T>
Elf_Word generic_add_symbol( Elf_Word name,
Elf64_Addr value,
Elf_Xword size,
unsigned char info,
unsigned char other,
Elf_Half shndx )
{
const endianess_convertor& convertor = elf_file.get_convertor();
T entry;
entry.st_name = convertor( name );
entry.st_value = decltype( entry.st_value )( value );
entry.st_value = convertor( entry.st_value );
entry.st_size = decltype( entry.st_size )( size );
entry.st_size = convertor( entry.st_size );
entry.st_info = convertor( info );
entry.st_other = convertor( other );
entry.st_shndx = convertor( shndx );
symbol_section->append_data( reinterpret_cast<char*>( &entry ),
sizeof( entry ) );
Elf_Word nRet =
Elf_Word( symbol_section->get_size() / sizeof( entry ) - 1 );
return nRet;
}
//------------------------------------------------------------------------------
template <class T>
Elf_Xword generic_arrange_local_symbols(
std::function<void( Elf_Xword first, Elf_Xword second )> func )
{
const endianess_convertor& convertor = elf_file.get_convertor();
Elf_Word first_not_local =
1; // Skip the first entry. It is always NOTYPE
Elf_Xword current = 0;
Elf_Xword count = get_symbols_num();
while ( true ) {
T* p1 = nullptr;
T* p2 = nullptr;
while ( first_not_local < count ) {
p1 = const_cast<T*>(
generic_get_symbol_ptr<T>( first_not_local ) );
if ( ELF_ST_BIND( convertor( p1->st_info ) ) != STB_LOCAL )
break;
++first_not_local;
}
current = first_not_local + 1;
while ( current < count ) {
p2 = const_cast<T*>( generic_get_symbol_ptr<T>( current ) );
if ( ELF_ST_BIND( convertor( p2->st_info ) ) == STB_LOCAL )
break;
++current;
}
if ( first_not_local < count && current < count ) {
if ( func )
func( first_not_local, current );
std::swap( *p1, *p2 );
}
else {
// Update 'info' field of the section
symbol_section->set_info( first_not_local );
break;
}
}
return first_not_local;
}
//------------------------------------------------------------------------------
private:
const elfio& elf_file;
S* symbol_section;
Elf_Half hash_section_index{ 0 };
const section* hash_section{ nullptr };
};
using symbol_section_accessor = symbol_section_accessor_template<section>;
using const_symbol_section_accessor =
symbol_section_accessor_template<const section>;
} // namespace ELFIO
#endif // ELFIO_SYMBOLS_HPP

View File

@ -0,0 +1,302 @@
/*
Copyright (C) 2001-present by Serge Lamikhov-Center
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ELFIO_UTILS_HPP
#define ELFIO_UTILS_HPP
#include <cstdint>
#include <ostream>
#define ELFIO_GET_ACCESS_DECL( TYPE, NAME ) virtual TYPE get_##NAME() const = 0
#define ELFIO_SET_ACCESS_DECL( TYPE, NAME ) \
virtual void set_##NAME( const TYPE& value ) = 0
#define ELFIO_GET_SET_ACCESS_DECL( TYPE, NAME ) \
virtual TYPE get_##NAME() const = 0; \
virtual void set_##NAME( const TYPE& value ) = 0
#define ELFIO_GET_ACCESS( TYPE, NAME, FIELD ) \
TYPE get_##NAME() const override { return ( *convertor )( FIELD ); }
#define ELFIO_SET_ACCESS( TYPE, NAME, FIELD ) \
void set_##NAME( const TYPE& value ) override \
{ \
FIELD = decltype( FIELD )( value ); \
FIELD = ( *convertor )( FIELD ); \
}
#define ELFIO_GET_SET_ACCESS( TYPE, NAME, FIELD ) \
TYPE get_##NAME() const override { return ( *convertor )( FIELD ); } \
void set_##NAME( const TYPE& value ) override \
{ \
FIELD = decltype( FIELD )( value ); \
FIELD = ( *convertor )( FIELD ); \
}
namespace ELFIO {
//------------------------------------------------------------------------------
class endianess_convertor
{
public:
//------------------------------------------------------------------------------
void setup( unsigned char elf_file_encoding )
{
need_conversion = ( elf_file_encoding != get_host_encoding() );
}
//------------------------------------------------------------------------------
uint64_t operator()( uint64_t value ) const
{
if ( !need_conversion ) {
return value;
}
value = ( ( value & 0x00000000000000FFuLL ) << 56 ) |
( ( value & 0x000000000000FF00uLL ) << 40 ) |
( ( value & 0x0000000000FF0000uLL ) << 24 ) |
( ( value & 0x00000000FF000000uLL ) << 8 ) |
( ( value & 0x000000FF00000000uLL ) >> 8 ) |
( ( value & 0x0000FF0000000000uLL ) >> 24 ) |
( ( value & 0x00FF000000000000uLL ) >> 40 ) |
( ( value & 0xFF00000000000000uLL ) >> 56 );
return value;
}
//------------------------------------------------------------------------------
int64_t operator()( int64_t value ) const
{
if ( !need_conversion ) {
return value;
}
return (int64_t)( *this )( (uint64_t)value );
}
//------------------------------------------------------------------------------
uint32_t operator()( uint32_t value ) const
{
if ( !need_conversion ) {
return value;
}
value =
( ( value & 0x000000FF ) << 24 ) | ( ( value & 0x0000FF00 ) << 8 ) |
( ( value & 0x00FF0000 ) >> 8 ) | ( ( value & 0xFF000000 ) >> 24 );
return value;
}
//------------------------------------------------------------------------------
int32_t operator()( int32_t value ) const
{
if ( !need_conversion ) {
return value;
}
return (int32_t)( *this )( (uint32_t)value );
}
//------------------------------------------------------------------------------
uint16_t operator()( uint16_t value ) const
{
if ( !need_conversion ) {
return value;
}
value =
(uint16_t)( ( value & 0x00FF ) << 8 ) | ( ( value & 0xFF00 ) >> 8 );
return value;
}
//------------------------------------------------------------------------------
int16_t operator()( int16_t value ) const
{
if ( !need_conversion ) {
return value;
}
return (int16_t)( *this )( (uint16_t)value );
}
//------------------------------------------------------------------------------
int8_t operator()( int8_t value ) const { return value; }
//------------------------------------------------------------------------------
uint8_t operator()( uint8_t value ) const { return value; }
//------------------------------------------------------------------------------
private:
//------------------------------------------------------------------------------
unsigned char get_host_encoding() const
{
static const int tmp = 1;
if ( 1 == *reinterpret_cast<const char*>( &tmp ) ) {
return ELFDATA2LSB;
}
else {
return ELFDATA2MSB;
}
}
//------------------------------------------------------------------------------
bool need_conversion = false;
};
//------------------------------------------------------------------------------
struct address_translation
{
address_translation( uint64_t start, uint64_t size, uint64_t mapped_to )
: start( start ), size( size ), mapped_to( mapped_to ){};
std::streampos start;
std::streampos size;
std::streampos mapped_to;
};
//------------------------------------------------------------------------------
class address_translator
{
public:
//------------------------------------------------------------------------------
void set_address_translation( std::vector<address_translation>& addr_trans )
{
addr_translations = addr_trans;
std::sort( addr_translations.begin(), addr_translations.end(),
[]( const address_translation& a,
const address_translation& b ) -> bool {
return a.start < b.start;
} );
}
//------------------------------------------------------------------------------
std::streampos operator[]( std::streampos value ) const
{
if ( addr_translations.empty() ) {
return value;
}
for ( auto& t : addr_translations ) {
if ( ( t.start <= value ) && ( ( value - t.start ) < t.size ) ) {
return value - t.start + t.mapped_to;
}
}
return value;
}
bool empty() const { return addr_translations.empty(); }
private:
std::vector<address_translation> addr_translations;
};
//------------------------------------------------------------------------------
inline uint32_t elf_hash( const unsigned char* name )
{
uint32_t h = 0;
uint32_t g = 0;
while ( *name != '\0' ) {
h = ( h << 4 ) + *name++;
g = h & 0xf0000000;
if ( g != 0 )
h ^= g >> 24;
h &= ~g;
}
return h;
}
//------------------------------------------------------------------------------
inline uint32_t elf_gnu_hash( const unsigned char* s )
{
uint32_t h = 0x1505;
for ( unsigned char c = *s; c != '\0'; c = *++s )
h = ( h << 5 ) + h + c;
return h;
}
//------------------------------------------------------------------------------
inline std::string to_hex_string( uint64_t value )
{
std::string str;
while ( value ) {
if ( auto digit = value & 0xF; digit < 0xA ) {
str = char( '0' + digit ) + str;
}
else {
str = char( 'A' + digit - 0xA ) + str;
}
value >>= 4;
}
return "0x" + str;
}
//------------------------------------------------------------------------------
inline void adjust_stream_size( std::ostream& stream, std::streamsize offset )
{
stream.seekp( 0, std::ios_base::end );
if ( stream.tellp() < offset ) {
std::streamsize size = offset - stream.tellp();
stream.write( std::string( size_t( size ), '\0' ).c_str(), size );
}
stream.seekp( offset );
}
/**
* Consumers should write an implementation of this class and pass an instance of it to the ELFIO::elfio constructor.
*/
class compression_interface
{
public:
virtual ~compression_interface() = default;
/**
* decompresses a compressed section
*
* @param data the buffer of compressed data
* @param endianness_convertor pointer to an endianness_convertor instance, used to convert numbers to/from the target endianness.
* @param compressed_size the size of the data buffer, in bytes
* @param decompressed_size a reference to a variable where the decompressed buffer size will be stored.
* @returns a smart pointer to the decompressed data.
*/
virtual std::unique_ptr<char[]>
inflate( const char* data,
const endianess_convertor* convertor,
Elf_Xword compressed_size,
Elf_Xword& uncompressed_size ) const = 0;
/**
* compresses a section
*
* @param data the buffer of uncompressed data
* @param endianness_convertor pointer to an endianness_convertor instance, used to convert numbers to/from the target endianness.
* @param decompressed_size the size of the data buffer, in bytes
* @param compressed_size a reference to a variable where the compressed buffer size will be stored.
* @returns a smart pointer to the compressed data.
*/
virtual std::unique_ptr<char[]>
deflate( const char* data,
const endianess_convertor* convertor,
Elf_Xword decompressed_size,
Elf_Xword& compressed_size ) const = 0;
};
} // namespace ELFIO
#endif // ELFIO_UTILS_HPP

View File

@ -0,0 +1 @@
#define ELFIO_VERSION "3.12"

View File

@ -0,0 +1,179 @@
/*
Copyright (C) 2001-present by Serge Lamikhov-Center
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ELFIO_VERSYM_HPP
#define ELFIO_VERSYM_HPP
namespace ELFIO {
//------------------------------------------------------------------------------
template <class S> class versym_section_accessor_template
{
public:
//------------------------------------------------------------------------------
explicit versym_section_accessor_template( S* section )
: versym_section( section )
{
if ( section != nullptr ) {
entries_num = decltype( entries_num )( section->get_size() /
sizeof( Elf_Half ) );
}
}
//------------------------------------------------------------------------------
Elf_Word get_entries_num() const
{
if ( versym_section ) {
return entries_num;
}
return 0;
}
//------------------------------------------------------------------------------
bool get_entry( Elf_Word no, Elf_Half& value ) const
{
if ( versym_section && ( no < get_entries_num() ) ) {
value = ( (Elf_Half*)versym_section->get_data() )[no];
return true;
}
return false;
}
//------------------------------------------------------------------------------
bool modify_entry( Elf_Word no, Elf_Half value )
{
if ( versym_section && ( no < get_entries_num() ) ) {
( (Elf_Half*)versym_section->get_data() )[no] = value;
return true;
}
return false;
}
//------------------------------------------------------------------------------
bool add_entry( Elf_Half value )
{
if ( !versym_section ) {
return false;
}
versym_section->append_data( (const char*)&value, sizeof( Elf_Half ) );
++entries_num;
return true;
}
//------------------------------------------------------------------------------
private:
S* versym_section = nullptr;
Elf_Word entries_num = 0;
};
using versym_section_accessor = versym_section_accessor_template<section>;
using const_versym_section_accessor =
versym_section_accessor_template<const section>;
//------------------------------------------------------------------------------
template <class S> class versym_r_section_accessor_template
{
public:
//------------------------------------------------------------------------------
versym_r_section_accessor_template( const elfio& elf_file,
S* versym_r_section )
: elf_file( elf_file ), versym_r_section( versym_r_section ),
entries_num( 0 )
{
// Find .dynamic section
const section* dynamic_section = elf_file.sections[".dynamic"];
if ( dynamic_section == nullptr ) {
return;
}
const_dynamic_section_accessor dynamic_section_acc( elf_file,
dynamic_section );
Elf_Xword dyn_sec_num = dynamic_section_acc.get_entries_num();
for ( Elf_Xword i = 0; i < dyn_sec_num; ++i ) {
Elf_Xword tag;
Elf_Xword value;
std::string str;
if ( dynamic_section_acc.get_entry( i, tag, value, str ) &&
tag == DT_VERNEEDNUM ) {
entries_num = (Elf_Word)value;
break;
}
}
}
//------------------------------------------------------------------------------
Elf_Word get_entries_num() const { return entries_num; }
//------------------------------------------------------------------------------
bool get_entry( Elf_Word no,
Elf_Half& version,
std::string& file_name,
Elf_Word& hash,
Elf_Half& flags,
Elf_Half& other,
std::string& dep_name ) const
{
if ( versym_r_section == nullptr || ( no >= get_entries_num() ) ) {
return false;
}
const_string_section_accessor string_section_acc(
elf_file.sections[versym_r_section->get_link()] );
Elfxx_Verneed* verneed = (Elfxx_Verneed*)versym_r_section->get_data();
Elfxx_Vernaux* veraux =
(Elfxx_Vernaux*)( (char*)verneed + verneed->vn_aux );
for ( Elf_Word i = 0; i < no; ++i ) {
verneed = (Elfxx_Verneed*)( (char*)verneed + verneed->vn_next );
veraux = (Elfxx_Vernaux*)( (char*)verneed + verneed->vn_aux );
}
version = verneed->vn_version;
file_name = string_section_acc.get_string( verneed->vn_file );
hash = veraux->vna_hash;
flags = veraux->vna_flags;
other = veraux->vna_other;
dep_name = string_section_acc.get_string( veraux->vna_name );
return true;
}
//------------------------------------------------------------------------------
private:
const elfio& elf_file;
S* versym_r_section = nullptr;
Elf_Word entries_num = 0;
};
using versym_r_section_accessor = versym_r_section_accessor_template<section>;
using const_versym_r_section_accessor =
versym_r_section_accessor_template<const section>;
} // namespace ELFIO
#endif // ELFIO_VERSYM_HPP

View File

@ -0,0 +1,163 @@
#pragma once
#include <cstdint>
#include "elna/source/optimizer.hpp"
namespace elna::riscv
{
enum class address_t
{
text,
high20,
lower12i
};
struct reference
{
std::string name;
std::size_t offset;
address_t target;
};
enum class x_register : std::uint8_t
{
zero = 0,
ra = 1,
sp = 2,
gp = 3,
tp = 4,
t0 = 5,
t1 = 6,
t2 = 7,
s0 = 8,
s1 = 9,
a0 = 10,
a1 = 11,
a2 = 12,
a3 = 13,
a4 = 14,
a5 = 15,
a6 = 16,
a7 = 17,
s2 = 18,
s3 = 19,
s4 = 20,
s5 = 21,
s6 = 22,
s7 = 23,
s8 = 24,
s9 = 25,
s10 = 26,
s11 = 27,
t3 = 28,
t4 = 29,
t5 = 30,
t6 = 31,
};
enum class funct3_t : std::uint8_t
{
addi = 0b000,
slti = 0b001,
sltiu = 0b011,
andi = 0b111,
ori = 0b110,
xori = 0b100,
slli = 0b000,
srli = 0b101,
srai = 0b101,
add = 0b000,
slt = 0b010,
sltu = 0b011,
_and = 0b111,
_or = 0b110,
_xor = 0b100,
sll = 0b001,
srl = 0b101,
sub = 0b000,
sra = 0b101,
beq = 0b000,
bne = 0b001,
blt = 0b100,
bltu = 0b110,
bge = 0b101,
bgeu = 0b111,
fence = 0b000,
fenceI = 0b001,
csrrw = 0b001,
csrrs = 0b010,
csrrc = 0b011,
csrrwi = 0b101,
csrrsi = 0b110,
csrrci = 0b111,
priv = 0b000,
sb = 0b000,
sh = 0b001,
sw = 0b010,
lb = 0b000,
lh = 0b001,
lw = 0b010,
lbu = 0b100,
lhu = 0b101,
jalr = 0b000,
mul = 0b000,
mulh = 0b001,
mulhsu = 0b010,
mulhu = 0b011,
div = 0b100,
divu = 0b101,
rem = 0b110,
remu = 0b111
};
enum class funct12_t : std::uint8_t
{
ecall = 0b000000000000,
ebreak = 0b000000000001,
};
enum class funct7_t : std::uint8_t
{
none = 0,
sub = 0b0100000,
muldiv = 0b0000001
};
enum class base_opcode : std::uint8_t
{
opImm = 0b0010011,
lui = 0b0110111,
auipc = 0b0010111,
op = 0b0110011,
jal = 0b1101111,
jalr = 0b1100111,
branch = 0b1100011,
load = 0b0000011,
store = 0b0100011,
miscMem = 0b0001111,
system = 0b1110011,
};
struct instruction
{
instruction() = default; // NOP = addi x0, x0, 0.
instruction(base_opcode opcode);
instruction& i(x_register rd, funct3_t funct3, x_register rs1, std::uint32_t immediate);
instruction& s(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2);
instruction& b(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2);
instruction& r(x_register rd, funct3_t funct3, x_register rs1, x_register rs2,
funct7_t funct7 = funct7_t::none);
instruction& u(x_register rd, std::uint32_t imm);
instruction& j(x_register rd, std::uint32_t imm);
const std::byte *cbegin() const;
const std::byte *cend() const;
private:
std::uint32_t representation{ 0 };
};
std::vector<reference> generate(source::intermediate_code_generator generator,
std::shared_ptr<source::symbol_table> table, std::shared_ptr<source::writer<std::byte>> writer);
}

View File

@ -0,0 +1,107 @@
#pragma once
#include "elna/source/parser.hpp"
#include "elna/source/optimizer.hpp"
#include <filesystem>
#include <elfio/elfio.hpp>
namespace elna::riscv
{
/**
* Writer to a single label.
*/
struct elfio_section_writer
{
struct entry
{
std::string_view label;
const std::byte *data{ nullptr };
std::size_t size{ 0 };
};
/**
* An iterator over label and string pairs.
*/
struct iterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = ptrdiff_t;
using value_type = entry;
using pointer = const value_type *;
using reference = const value_type&;
reference operator*() const noexcept;
pointer operator->() const noexcept;
iterator& operator++();
iterator& operator++(int);
bool operator==(const iterator& that) const;
bool operator!=(const iterator& that) const;
private:
std::vector<std::string>::const_iterator labels;
std::vector<std::size_t>::const_iterator sizes;
value_type payload;
iterator(std::vector<std::string>::const_iterator labels, std::vector<std::size_t>::const_iterator sizes,
const std::byte *data)
: labels(labels), sizes(sizes)
{
if (data != nullptr)
{
payload = { *this->labels, data, *this->sizes};
}
}
iterator(std::vector<std::string>::const_iterator labels, std::vector<std::size_t>::const_iterator sizes)
: labels(labels), sizes(sizes), payload{}
{
}
friend elfio_section_writer;
};
explicit elfio_section_writer(ELFIO::section *section);
void operator()(const std::string& label, const std::byte *data, std::size_t size);
std::pair<std::string_view, bool> operator()(const std::byte *data, std::size_t size);
iterator begin() const;
iterator end() const;
ELFIO::section *section() noexcept;
private:
std::vector<std::string> labels;
std::vector<std::size_t> sizes;
ELFIO::section *m_section;
};
class elfio_writer final : public source::writer<std::byte>
{
ELFIO::section *text;
elfio_section_writer read_only;
ELFIO::symbol_section_accessor symbol_accessor;
ELFIO::string_section_accessor string_accessor;
public:
elfio_writer(ELFIO::section *text, ELFIO::section *read_only, ELFIO::symbol_section_accessor symbol_accessor,
ELFIO::string_section_accessor string_accessor);
std::size_t sink(const std::string& label, const std::byte *data, std::size_t size) override;
std::string_view sink(const std::byte *data, std::size_t size) override;
void sink(const std::string& label) override;
std::size_t size() const override;
};
/**
* Searches the content by label and returns its index or -1 when the
* label does not exist.
*
* \param symbol_accessor Object accessor.
* \param needle Label name.
* \return Data index.
*/
std::ptrdiff_t lookup(ELFIO::symbol_section_accessor symbol_accessor, const std::string& label);
void riscv32_elf(source::program *ast, source::intermediate_code_generator intermediate_code_generator,
std::shared_ptr<source::symbol_table> table, const std::filesystem::path& out_file);
}

37
include/elna/cli/cl.hpp Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <algorithm>
#include <filesystem>
#include "elna/source/result.hpp"
namespace elna::cli
{
/**
* Formats and prints the given error.
*
* \param compile_error The error to print.
*/
void print_error(const std::unique_ptr<source::error>& compile_error);
/**
* Prints the given errors to the standard output.
*
* \param begin Pointer to the first error.
* \param end Pointer pass the last error.
*/
template<typename I>
void print_errors(I begin, I end)
{
std::for_each(begin, end, &print_error);
}
/**
* Compiles \a in_file and writes the generated code into \a out_file.
*
* \param in_file Input file.
* \param out_file Output file.
*
* \return Exit status.
*/
int compile(const std::filesystem::path& in_file, const std::filesystem::path& out_file);
}

View File

@ -0,0 +1,212 @@
#pragma once
#include <cstdint>
#include <filesystem>
#include <optional>
#include <string>
#include <vector>
#include "elna/source/result.hpp"
namespace elna::source
{
/**
* Union type representing a single token.
*/
struct token
{
/**
* Token type.
*/
enum class type : std::uint16_t
{
dot,
number,
boolean,
term_operator,
let,
identifier,
equals,
var,
semicolon,
left_paren,
right_paren,
comma,
factor_operator,
eof,
begin,
end,
assignment,
colon,
when,
then,
loop,
_do,
procedure,
comparison_operator,
hat,
at
};
/**
* Type of the token value.
*/
union value
{
value();
value(std::int32_t value);
value(const std::string& value);
~value();
std::nullptr_t nil;
std::int32_t number;
std::string identifier;
};
token(type of, elna::source::position position);
token(type of, std::int32_t value, const elna::source::position position);
token(type of, const std::string& value, const elna::source::position position);
token(type of, value&& value, const elna::source::position position);
token(const token& that);
token(token&& that);
~token();
token& operator=(const token& that);
token& operator=(token&& that);
type of() const noexcept;
const std::string& identifier() const;
std::int32_t number() const;
const elna::source::position& position() const noexcept;
std::string to_string() const;
private:
type m_type;
value m_value{};
elna::source::position m_position;
bool has_identifier() const noexcept;
bool is_numeric() const noexcept;
};
class unexpected_character final : public error
{
std::string character;
public:
/**
* \param character Unexpected character.
* \param path Source file name.
* \param position Unexpected token position.
*/
unexpected_character(const std::string& character, const std::filesystem::path& path,
const source::position position);
std::string what() const override;
};
class unexpected_token final : public error
{
token m_token;
public:
/**
* \param token Unexpected token.
* \param path Source file name.
*/
unexpected_token(const token& token, const std::filesystem::path& path);
std::string what() const override;
};
struct lexer
{
lexer(std::vector<token>&& tokens, const position last_position, const std::filesystem::path& path);
lexer(const lexer&) = delete;
lexer(lexer&&) = default;
lexer& operator=(const lexer&) = delete;
lexer& operator=(lexer&&) = default;
lexer& operator++();
const token& operator*() const;
const token *operator->() const;
/**
* This function never fails and returns \ref token::type::eof at the
* end of the token stream.
*
* \return Current token.
*/
const token& current() const noexcept;
/**
* \param token_type Token type.
* \return Whether the current token is \a token_type.
*/
bool current(const token::type token_type) const noexcept;
/**
* Adds an \ref unexpected_token error to the error list.
*
* \param expected The token was expected.
*/
void add_error(const token& expected);
/**
* Expects the current token to be \a token_type. In this case returns
* this token and advances to the next token in the stream.
*
* \param token_type Expected token type.
* \return Current token.
*/
std::optional<std::reference_wrapper<const token>> advance(const token::type token_type);
/**
* Returns that follows the current token. If the current token is
* \ref token::type::eof, returns it.
*
* \return The token that follows the current one.
*/
const token& look_ahead() const;
/**
* Tells whether the token following the current one is \a token_type.
*
* \param token_type Token type.
* \return Whether the next token is \a token_type.
*/
bool look_ahead(const token::type token_type) const;
/**
* Skips one token if it is of type \a token_type. Adds an
* \ref unexpected_token error otherwise.
*
* \param token_type The token type was expected.
* \return Whether the current token was \a token_type.
*/
bool skip(const token::type token_type);
/**
* Gets produced errors.
*
* \return Produced error list.
*/
const std::list<std::unique_ptr<error>>& errors() const noexcept;
private:
std::vector<token> tokens;
std::vector<token>::const_iterator iterator;
std::list<std::unique_ptr<error>> m_errors;
std::filesystem::path source_file;
token eof;
};
/**
* Splits the source text into tokens.
*
* \param path Source file location.
* \return Tokens or error.
*/
elna::source::result<lexer> tokenize(const std::filesystem::path& path);
}

View File

@ -0,0 +1,106 @@
#pragma once
#include <unordered_map>
#include "elna/source/parser.hpp"
#include "elna/source/symbol_table.hpp"
namespace elna::source
{
enum class quadruple_operator
{
start,
stop,
add,
sub,
mul,
div,
eq,
neq,
lt,
ge,
gt,
le,
load,
ref,
beqz,
j,
label,
assign,
param,
call
};
/**
* Single instruction representation.
*/
struct quadruple
{
quadruple(const quadruple_operator operation, std::shared_ptr<operand> operand1 = nullptr,
std::shared_ptr<operand> operand2 = nullptr, std::shared_ptr<operand> operand3 = nullptr);
quadruple_operator operation() const noexcept;
std::shared_ptr<operand> operand1();
std::shared_ptr<operand> operand2();
std::shared_ptr<operand> operand3();
private:
quadruple_operator m_operation;
std::shared_ptr<operand> m_operand1;
std::shared_ptr<operand> m_operand2;
std::shared_ptr<operand> m_operand3;
};
class intermediate_code final
{
std::vector<quadruple> instructions;
std::uint32_t m_variable_counter;
std::uint32_t m_label_counter;
public:
intermediate_code();
void emplace_back(const quadruple_operator operation, std::shared_ptr<operand> operand1 = nullptr,
std::shared_ptr<operand> operand2 = nullptr, std::shared_ptr<operand> operand3 = nullptr);
void clear();
std::vector<quadruple>::iterator begin();
std::vector<quadruple>::iterator end();
std::int32_t variable_counter() const noexcept;
std::int32_t increment_variable() noexcept;
std::int32_t label_counter() const noexcept;
std::int32_t increment_label() noexcept;
};
class intermediate_code_generator final : public empty_visitor
{
std::unordered_map<std::string, intermediate_code> code;
intermediate_code current;
std::shared_ptr<symbol_table> table;
quadruple_operator convert(const binary_operator operation) const;
quadruple_operator convert(const unary_operator operation) const;
public:
intermediate_code_generator(std::shared_ptr<symbol_table> table);
std::unordered_map<std::string, intermediate_code>::iterator begin();
std::unordered_map<std::string, intermediate_code>::iterator end();
void visit(declaration *declaration) override;
void visit(constant_definition *definition) override;
void visit(procedure_definition *definition) override;
void visit(call_statement *statement) override;
void visit(assign_statement *statement) override;
void visit(if_statement *statement) override;
void visit(while_statement *statement) override;
void visit(block *block) override;
void visit(program *program) override;
void visit(type_expression *variable) override;
void visit(variable_expression *variable) override;
void visit(binary_expression *expression) override;
void visit(unary_expression *expression) override;
void visit(integer_literal *number) override;
void visit(boolean_literal *number) override;
};
}

View File

@ -0,0 +1,520 @@
#pragma once
#include <boost/core/noncopyable.hpp>
#include <memory>
#include <elna/source/lexer.hpp>
namespace elna::source
{
enum class binary_operator
{
sum,
subtraction,
multiplication,
division,
equals,
not_equals,
less,
greater,
less_equal,
greater_equal
};
enum class unary_operator
{
reference,
dereference
};
class declaration;
class constant_definition;
class procedure_definition;
class call_statement;
class compound_statement;
class assign_statement;
class if_statement;
class while_statement;
class block;
class program;
class binary_expression;
class unary_expression;
class type_expression;
class variable_expression;
class integer_literal;
class boolean_literal;
/**
* Interface for AST visitors.
*/
struct parser_visitor
{
virtual void visit(declaration *) = 0;
virtual void visit(constant_definition *) = 0;
virtual void visit(procedure_definition *) = 0;
virtual void visit(call_statement *) = 0;
virtual void visit(compound_statement *) = 0;
virtual void visit(assign_statement *) = 0;
virtual void visit(if_statement *) = 0;
virtual void visit(while_statement *) = 0;
virtual void visit(block *) = 0;
virtual void visit(program *) = 0;
virtual void visit(binary_expression *) = 0;
virtual void visit(unary_expression *) = 0;
virtual void visit(type_expression *) = 0;
virtual void visit(variable_expression *) = 0;
virtual void visit(integer_literal *) = 0;
virtual void visit(boolean_literal *) = 0;
};
/**
* A visitor which visits all nodes but does nothing.
*/
struct empty_visitor : parser_visitor
{
virtual void visit(declaration *declaration) override;
virtual void visit(constant_definition *definition) override;
virtual void visit(procedure_definition *definition) override;
virtual void visit(call_statement *statement) override;
virtual void visit(compound_statement *statement) override;
virtual void visit(assign_statement *statement) override;
virtual void visit(if_statement *) override;
virtual void visit(while_statement *) override;
virtual void visit(block *block) override;
virtual void visit(program *program) override;
virtual void visit(binary_expression *expression) override;
virtual void visit(unary_expression *expression) override;
virtual void visit(type_expression *variable) override;
virtual void visit(variable_expression *variable) override;
virtual void visit(integer_literal *number) override;
virtual void visit(boolean_literal *boolean) override;
};
/**
* Operand representing a subexpression in the 3 address code.
*/
struct operand
{
public:
virtual ~operand() noexcept = 0;
};
struct integer_operand final : public operand
{
std::int32_t m_value;
public:
explicit integer_operand(const std::int32_t value);
std::int32_t value() const noexcept;
};
class variable_operand final : public operand
{
std::string m_name;
public:
explicit variable_operand(const std::string& name);
const std::string& name() const noexcept;
};
struct temporary_variable final : public operand
{
std::size_t m_counter;
public:
explicit temporary_variable(const std::size_t counter);
std::size_t counter() const noexcept;
};
struct label_operand final : public operand
{
std::size_t m_counter;
public:
explicit label_operand(const std::size_t counter);
std::size_t counter() const noexcept;
};
/**
* AST node.
*/
class node
{
const struct position source_position;
protected:
/**
* \param position Source code position.
*/
explicit node(const position position);
public:
virtual void accept(parser_visitor *) = 0;
/**
* \return Node position in the source code.
*/
const struct position& position() const noexcept;
};
class statement : public node
{
protected:
/**
* \param position Source code position.
*/
explicit statement(const struct position position);
};
class expression : public node
{
public:
std::shared_ptr<operand> place;
protected:
/**
* \param position Source code position.
*/
explicit expression(const struct position position);
};
/**
* Symbol definition.
*/
class definition : public node
{
std::string m_identifier;
protected:
/**
* Constructs a definition identified by some name.
*
* \param position Source code position.
* \param identifier Definition name.
*/
definition(const struct position position, const std::string& identifier);
public:
/**
* \return Definition name.
*/
std::string& identifier() noexcept;
};
class type_expression : public node
{
std::string m_base;
bool m_pointer{ false };
public:
/**
* \param position Source code position.
* \param name Type name.
* \param is_pointer Whether it is a pointer type.
*/
type_expression(const struct position position, const std::string& name, const bool is_pointer = false);
virtual void accept(parser_visitor *visitor) override;
/**
* \return Name of the base type.
*/
const std::string& base() const noexcept;
/**
* \return Whether the type is a pointer.
*/
bool is_pointer() const noexcept;
};
/**
* Variable declaration.
*/
class declaration : public definition
{
std::unique_ptr<type_expression> m_type;
public:
/**
* Constructs a declaration with a name and a type.
*
* \param position Source code position.
* \param identifier Definition name.
* \param type Declared type.
*/
declaration(const struct position position, const std::string& identifier,
std::unique_ptr<type_expression>&& type);
virtual void accept(parser_visitor *visitor) override;
type_expression& type() noexcept;
};
/**
* Constant definition.
*/
class constant_definition : public definition
{
std::unique_ptr<integer_literal> m_body;
public:
/**
* \param position Source code position.
* \param identifier Constant name.
* \param body Constant value.
*/
constant_definition(const struct position position, const std::string& identifier,
std::unique_ptr<integer_literal>&& body);
virtual void accept(parser_visitor *visitor) override;
integer_literal& body();
};
/**
* Procedure definition.
*/
class procedure_definition : public definition
{
std::unique_ptr<block> m_body;
std::vector<std::unique_ptr<declaration>> m_parameters;
public:
/**
* \param position Source code position.
* \param identifier Procedure name.
* \param body Procedure body.
*/
procedure_definition(const struct position position, const std::string& identifier,
std::unique_ptr<block>&& body);
virtual void accept(parser_visitor *visitor) override;
block& body();
std::vector<std::unique_ptr<declaration>>& parameters() noexcept;
};
/**
* Call statement.
*/
class call_statement : public statement
{
std::string m_name;
std::vector<std::unique_ptr<expression>> m_arguments;
public:
/**
* \param position Source code position.
* \param name Callable's name.
*/
call_statement(const struct position position, const std::string& name);
virtual void accept(parser_visitor *visitor) override;
std::string& name() noexcept;
std::vector<std::unique_ptr<expression>>& arguments() noexcept;
};
class compound_statement : public statement
{
std::vector<std::unique_ptr<statement>> m_statements;
public:
explicit compound_statement(const struct position position);
virtual void accept(parser_visitor *visitor) override;
std::vector<std::unique_ptr<statement>>& statements();
};
class assign_statement : public statement
{
std::string m_lvalue;
std::unique_ptr<expression> m_rvalue;
public:
/**
* \param position Source code position.
* \param lvalue Left-hand side.
* \param rvalue Assigned expression.
*/
assign_statement(const struct position position, const std::string& lvalue,
std::unique_ptr<expression>&& rvalue);
virtual void accept(parser_visitor *visitor) override;
std::string& lvalue() noexcept;
expression& rvalue();
};
/**
* If-statement.
*/
class if_statement : public statement
{
std::unique_ptr<expression> m_prerequisite;
std::unique_ptr<statement> m_body;
public:
/**
* \param position Source code position.
* \param prerequisite Condition.
* \param body Statement executed if the condition is met.
*/
if_statement(const struct position position, std::unique_ptr<expression>&& prerequisite,
std::unique_ptr<statement>&& body);
virtual void accept(parser_visitor *visitor) override;
expression& prerequisite();
statement& body();
};
/**
* While-statement.
*/
class while_statement : public statement
{
std::unique_ptr<expression> m_prerequisite;
std::unique_ptr<statement> m_body;
public:
/**
* \param position Source code position.
* \param prerequisite Condition.
* \param body Statement executed while the condition is met.
*/
while_statement(const struct position position, std::unique_ptr<expression>&& prerequisite,
std::unique_ptr<statement>&& body);
virtual void accept(parser_visitor *visitor) override;
expression& prerequisite();
statement& body();
};
class block : public node
{
std::unique_ptr<statement> m_body;
std::vector<std::unique_ptr<definition>> m_definitions;
std::vector<std::unique_ptr<declaration>> m_declarations;
public:
block(const struct position position, std::vector<std::unique_ptr<definition>>&& definitions,
std::vector<std::unique_ptr<declaration>>&& declarations,
std::unique_ptr<statement>&& body);
virtual void accept(parser_visitor *visitor) override;
statement& body();
std::vector<std::unique_ptr<definition>>& definitions() noexcept;
std::vector<std::unique_ptr<declaration>>& declarations() noexcept;
};
class program : public block
{
public:
program(const struct position position, std::vector<std::unique_ptr<definition>>&& definitions,
std::vector<std::unique_ptr<declaration>>&& declarations,
std::unique_ptr<statement>&& body);
virtual void accept(parser_visitor *visitor) override;
};
class integer_literal : public expression
{
std::int32_t m_number;
public:
integer_literal(const struct position position, const std::int32_t value);
virtual void accept(parser_visitor *visitor) override;
std::int32_t number() const noexcept;
};
class boolean_literal : public expression
{
bool m_boolean;
public:
boolean_literal(const struct position position, const bool value);
virtual void accept(parser_visitor *visitor) override;
bool boolean() const noexcept;
};
class variable_expression : public expression
{
std::string m_name;
public:
variable_expression(const struct position position, const std::string& name);
virtual void accept(parser_visitor *visitor) override;
const std::string& name() const noexcept;
};
class binary_expression : public expression
{
std::unique_ptr<expression> m_lhs;
std::unique_ptr<expression> m_rhs;
binary_operator m_operator;
public:
binary_expression(const struct position position, std::unique_ptr<expression>&& lhs,
std::unique_ptr<expression>&& rhs, const unsigned char operation);
virtual void accept(parser_visitor *visitor) override;
expression& lhs();
expression& rhs();
binary_operator operation() const noexcept;
};
class unary_expression : public expression
{
std::unique_ptr<expression> m_operand;
unary_operator m_operator;
public:
unary_expression(const struct position position, std::unique_ptr<expression>&& operand,
const unsigned char operation);
virtual void accept(parser_visitor *visitor) override;
expression& operand();
unary_operator operation() const noexcept;
};
class parser : boost::noncopyable
{
std::unique_ptr<expression> parse_unary_expression();
std::unique_ptr<expression> parse_factor();
std::unique_ptr<expression> parse_term();
std::unique_ptr<expression> parse_expression();
std::unique_ptr<expression> parse_condition();
std::unique_ptr<constant_definition> parse_constant_definition();
std::unique_ptr<procedure_definition> parse_procedure_definition();
std::unique_ptr<type_expression> parse_type_expression();
std::unique_ptr<declaration> parse_declaration();
std::unique_ptr<statement> parse_statement();
std::unique_ptr<call_statement> parse_call_statement();
std::unique_ptr<compound_statement> parse_compound_statement();
std::unique_ptr<assign_statement> parse_assign_statement();
std::unique_ptr<if_statement> parse_if_statement();
std::unique_ptr<while_statement> parse_while_statement();
std::vector<std::unique_ptr<constant_definition>> parse_constant_definitions();
std::vector<std::unique_ptr<procedure_definition>> parse_procedure_definitions();
std::vector<std::unique_ptr<declaration>> parse_declarations();
std::unique_ptr<block> parse_block();
lexer iterator;
public:
parser(lexer&& tokens);
/**
* Parses a source text.
*
* \return Parsed program or nothing if an error occurred.
*/
std::unique_ptr<program> parse();
/**
* Gets produced errors.
*
* \return Produced error list.
*/
const std::list<std::unique_ptr<error>>& errors() const noexcept;
};
}

View File

@ -0,0 +1,160 @@
#pragma once
#include <cstddef>
#include <filesystem>
#include <variant>
#include <list>
#include <memory>
#include <string>
#include <type_traits>
namespace elna::source
{
/**
* Position in the source text.
*/
struct position
{
/// Line.
std::size_t line = 1;
/// Column.
std::size_t column = 1;
};
/**
* A compilation error consists of an error message and position.
*/
class error
{
position m_position;
std::filesystem::path m_path;
protected:
/**
* Constructs an error.
*
* \param path Source file name.
* \param position Error position in the source text.
*/
error(const std::filesystem::path& path, const position position);
public:
/// Error text.
virtual std::string what() const = 0;
/// Error line in the source text.
std::size_t line() const noexcept;
/// Error column in the source text.
std::size_t column() const noexcept;
/// Source file name.
const std::filesystem::path& path() const noexcept;
};
template<typename T>
struct result
{
using E = std::list<std::unique_ptr<source::error>>;
template<typename... Args, typename = std::enable_if<std::is_constructible_v<T, Args...>>>
explicit result(std::in_place_t, Args&&... arguments)
: payload(std::in_place_type<T>, std::forward<Args>(arguments)...)
{
}
explicit result(T&& result)
: payload(std::move(result))
{
}
template<typename U, typename = std::enable_if<std::is_base_of_v<error, U>>>
explicit result(U&& first)
: payload(E())
{
errors().emplace_back(std::make_unique<U>(first));
}
explicit result(E&& errors)
: payload(std::move(errors))
{
}
bool has_errors() const noexcept
{
return std::holds_alternative<E>(payload);
}
bool is_success() const noexcept
{
return std::holds_alternative<T>(payload);
}
T& success()
{
return std::get<T>(payload);
}
E& errors()
{
return std::get<E>(payload);
}
private:
std::variant<T, E> payload;
};
class name_collision final : public error
{
position previous;
std::string name;
public:
/**
* \param name Symbol name.
* \param path Source file name.
* \param current Current symbol position.
* \param previous Position of the previously defined symbol.
*/
name_collision(const std::string& name, const std::filesystem::path& path,
const position current, const position previous);
std::string what() const override;
};
template<typename T>
struct writer
{
/**
* Writes data to the table and saves it under the given label.
*
* \param label Symbol name.
* \param data Data to be written.
* \param size Data size.
*
* \return New size of the table.
*/
virtual std::size_t sink(const std::string& label, const T *data, std::size_t size) = 0;
/**
* Writes data and returns a label under that the data can be accessed.
*
* \param data Data to be written.
* \param size Data size.
*
* \return Label for the symbol.
*/
virtual std::string_view sink(const T *data, std::size_t size) = 0;
/**
* Creates an external symbol.
*/
virtual void sink(const std::string& label) = 0;
/**
* \return Actual size of the text section.
*/
virtual std::size_t size() const = 0;
};
}

View File

@ -0,0 +1,48 @@
#pragma once
#include "elna/source/parser.hpp"
#include "elna/source/symbol_table.hpp"
namespace elna::source
{
class name_analysis_visitor final : public empty_visitor
{
std::shared_ptr<symbol_table> table = std::make_shared<symbol_table>();
std::shared_ptr<const type> convert_declaration_type(const type_expression& ast_type) const;
public:
name_analysis_visitor(std::shared_ptr<symbol_table> table);
void visit(constant_definition *definition) override;
void visit(declaration *declaration) override;
void visit(program *program) override;
void visit(procedure_definition *procedure) override;
};
/**
* Visitor which allocates registers and stack space for variables and
* parameters.
*/
class allocator_visitor final : public empty_visitor
{
std::ptrdiff_t local_offset;
std::ptrdiff_t argument_offset;
std::shared_ptr<symbol_table> table;
public:
allocator_visitor(std::shared_ptr<symbol_table> table);
void visit(declaration *declaration) override;
void visit(program *program) override;
void visit(procedure_definition *procedure) override;
void visit(call_statement *statement) override;
};
/**
* This visitor performs the type checking.
*/
class type_analysis_visitor final : public empty_visitor
{
};
}

View File

@ -0,0 +1,151 @@
#pragma once
#include <cstdint>
#include <unordered_map>
#include <string>
#include <memory>
#include <vector>
namespace elna::source
{
class symbol_table;
/**
* Generic language entity information.
*/
class info
{
public:
virtual ~info() = 0;
protected:
info();
};
/**
* Type information.
*/
class type_info final : public info
{
std::shared_ptr<class type> m_type;
public:
explicit type_info(const class type& type);
~type_info() override;
std::shared_ptr<const class type> type() const noexcept;
};
/**
* Constant information.
*/
class constant_info final : public info
{
std::int32_t m_value;
public:
constant_info(const std::int32_t value);
~constant_info() override;
std::int32_t value() const noexcept;
};
/**
* Variable information.
*/
class variable_info final : public info
{
std::shared_ptr<const class type> m_type;
public:
std::ptrdiff_t offset{ 0 };
explicit variable_info(std::shared_ptr<const class type> type);
~variable_info() override;
std::shared_ptr<const class type> type() noexcept;
};
/**
* Procedure parameter information.
*/
class parameter_info final : public info
{
std::shared_ptr<const class type> m_type;
public:
std::ptrdiff_t offset{ 0 };
explicit parameter_info(std::shared_ptr<const class type> type);
~parameter_info() override;
std::shared_ptr<const class type> type() const noexcept;
};
/**
* Intrinsic and external procedure information.
*/
class intrinsic_info : public info
{
public:
std::vector<parameter_info> parameter_infos;
~intrinsic_info() override;
std::size_t parameter_stack_size() const noexcept;
};
/**
* Procedure information.
*/
class procedure_info final : public intrinsic_info
{
std::shared_ptr<symbol_table> local_table;
public:
std::size_t local_stack_size{ 0 };
std::size_t argument_stack_size{ 0 };
explicit procedure_info(std::shared_ptr<symbol_table> outer_scope);
~procedure_info() override;
std::shared_ptr<symbol_table> scope();
std::size_t stack_size() const noexcept;
};
/**
* Symbol table.
*/
class symbol_table
{
std::unordered_map<std::string, std::shared_ptr<info>> entries;
std::shared_ptr<symbol_table> outer_scope;
public:
/**
* Constructs a new symbol with an optional outer scope.
*
* \param scope Outer scope.
*/
explicit symbol_table(std::shared_ptr<symbol_table> scope = nullptr);
/**
* Looks for symbol in the table by name. Returns nullptr if the symbol
* can not be found.
*
* \param name Symbol name.
* \return Symbol from the table if found.
*/
std::shared_ptr<info> lookup(const std::string& name);
/**
* Registers new symbol.
*
* \param name Symbol name.
* \param entry Symbol information.
*/
void enter(const std::string& name, std::shared_ptr<info> entry);
/**
* Returns the outer scope or nullptr if the this is the global scope.
*
* \return Outer scope.
*/
std::shared_ptr<symbol_table> scope();
};
}

View File

@ -0,0 +1,41 @@
#pragma once
#include <memory>
#include <string>
namespace elna::source
{
/**
* Type representation.
*/
struct type
{
const std::size_t byte_size;
protected:
explicit type(const std::size_t byte_size);
};
/**
* Built-in type representation.
*/
struct primitive_type : public type
{
const std::string type_name;
primitive_type(const std::string& type_name, const std::size_t byte_size);
};
/**
* Typed pointer.
*/
struct pointer_type : public type
{
std::shared_ptr<const type> base_type;
pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size);
};
inline const primitive_type boolean_type{ "Boolean", 1 };
inline const primitive_type int_type{ "Int", 4 };
}

55
include/elna/tester.hpp Normal file
View File

@ -0,0 +1,55 @@
#pragma once
#include <cstdint>
#include <string>
#include <filesystem>
#define BOOST_PROCESS_USE_STD_FS
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/process/v2.hpp>
namespace elna
{
enum class test_status
{
successful,
compile_failed,
build_failed,
expectation_failed,
expectation_not_found
};
struct test_result final
{
test_status status{ test_status::successful };
int code{ 0 };
std::string output;
};
class test_results final
{
std::uint32_t m_total{ 0 };
std::uint32_t m_passed{ 0 };
public:
test_results() = default;
std::uint32_t total() const noexcept;
std::uint32_t passed() const noexcept;
std::uint32_t failed() const noexcept;
int exit_code() const noexcept;
void add_exit_code(const test_status result) noexcept;
};
boost::process::v2::process_stdio get_output_streams(const std::uint8_t stream_number,
boost::asio::readable_pipe& read_pipe);
test_result run_for_output(boost::asio::io_context& context, const std::uint8_t stream_number,
const std::filesystem::path& binary, std::initializer_list<boost::string_view> arguments);
std::string find_object(const std::vector<std::filesystem::path>& environment, const std::string& object);
test_result build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry);
test_result run_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry);
void print_result(const std::filesystem::directory_entry& test_entry, const test_result& result);
}

View File

@ -1,9 +0,0 @@
/**
* File I/O that can be moved into more generic library when and if finished.
*/
module elna.extended;
struct File
{
@disable this(this);
}

View File

@ -1,660 +0,0 @@
module elna.generator;
import core.stdc.stdio;
import core.stdc.stdlib;
import core.stdc.string;
import elna.ir;
import tanya.container.array;
import tanya.container.string;
import tanya.memory.mmappool;
import tanya.format;
/// Unsigned program address.
alias Elf64_Addr = void*;
/// Unsigned file offset.
alias Elf64_Off = ulong;
/// Unsigned medium integer.
alias Elf64_Half = ushort;
/// Unsigned integer.
alias Elf64_Word = uint;
/// Signed integer.
alias Elf64_Sword = int;
/// Unsigned long integer.
alias Elf64_Xword = ulong;
/// Signed long integer.
alias Elf64_Sxword = long;
enum size_t EI_INDENT = 16;
/**
* File header.
*/
struct Elf64_Ehdr
{
/// ELF identification.
ubyte[EI_INDENT] e_ident;
/// Object file type.
Elf64_Half e_type;
/// Machine type.
Elf64_Half e_machine;
/// Object file version
Elf64_Word e_version;
/// Entry point address.
Elf64_Addr e_entry;
/// Program header offset.
Elf64_Off e_phoff;
/// Section header offset.
Elf64_Off e_shoff;
/// Processor-specific flags.
Elf64_Word e_flags;
/// ELF header size.
Elf64_Half e_ehsize;
/// Size of program header entry.
Elf64_Half e_phentsize;
/// Number of program header entries.
Elf64_Half e_phnum;
/// Size of section header entry.
Elf64_Half e_shentsize;
/// Number of section header entries.
Elf64_Half e_shnum;
/// Section name string table index.
Elf64_Half e_shstrndx;
}
/**
* Section header.
*/
struct Elf64_Shdr
{
/// Section name.
Elf64_Word sh_name;
/// Section type.
Elf64_Word sh_type;
/// Section attributes.
Elf64_Xword sh_flags;
/// Virtual address in memory.
Elf64_Addr sh_addr;
/// Offset in file.
Elf64_Off sh_offset;
/// Size of section.
Elf64_Xword sh_size;
/// Link to other section.
Elf64_Word sh_link;
/// Miscellaneous information.
Elf64_Word sh_info;
/// Address alignment boundary.
Elf64_Xword sh_addralign;
/// Size of entries, if section has table.
Elf64_Xword sh_entsize;
}
struct Elf64_Sym
{
/// Symbol name.
Elf64_Word st_name;
/// Type and Binding attributes.
ubyte st_info;
/// Reserved.
ubyte st_other;
/// Section table index.
Elf64_Half st_shndx;
/// Symbol value.
Elf64_Addr st_value;
/// Size of object (e.g., common).
Elf64_Xword st_size;
}
/// Section Types, sh_type.
enum : Elf64_Word
{
/// Marks an unused section header.
SHT_NULL = 0,
/// Contains information defined by the program.
SHT_PROGBITS = 1,
/// Contains a linker symbol table.
SHT_SYMTAB = 2,
/// Contains a string table.
SHT_STRTAB = 3,
/// Contains “Rela” type relocation entries.
SHT_RELA = 4,
/// Contains a symbol hash table
SHT_HASH = 5,
/// Contains dynamic linking tables
SHT_DYNAMIC = 6,
/// Contains note information
SHT_NOTE = 7,
/// Contains uninitialized space; does not occupy any space in the file.
SHT_NOBITS = 8,
/// Contains "Rel" type relocation entries.
SHT_REL = 9,
/// Reserved.
SHT_SHLIB = 10,
/// Contains a dynamic loader symbol table.
SHT_DYNSYM = 11,
/// Environment-specific use.
SHT_LOOS = 0x60000000,
SHT_HIOS = 0x6FFFFFFF,
/// Processor-specific use.
SHT_LOPROC = 0x70000000,
SHT_HIPROC = 0x7FFFFFFF,
}
/**
* Section Attributes, sh_flags.
*/
enum : Elf64_Xword
{
/// Section contains writable data.
SHF_WRITE = 0x1,
/// Section is allocated in memory image of program.
SHF_ALLOC = 0x2,
/// Section contains executable instructions.
SHF_EXECINSTR = 0x4,
/// Environment-specific use.
SHF_MASKOS = 0x0F000000,
/// Processor-specific use.
SHF_MASKPROC = 0xF0000000,
}
enum : Elf64_Word
{
/// Not visible outside the object file.
STB_LOCAL = 0,
/// Global symbol, visible to all object files.
STB_GLOBAL = 1,
/// Global scope, but with lower precedence than global symbols.
STB_WEAK = 2,
/// Environment-specific use.
STB_LOOS = 10,
STB_HIOS = 12,
/// Processor-specific use.
STB_LOPROC = 13,
STB_HIPROC = 15,
}
enum : Elf64_Word
{
/// No type specified (e.g., an absolute symbol).
STT_NOTYPE = 0,
/// Data object.
STT_OBJECT = 1,
/// Function entry point.
STT_FUNC = 2,
/// Symbol is associated with a section.
STT_SECTION = 3,
/// Source file associated with the object file.
STT_FILE = 4,
/// Environment-specific use.
STT_LOOS = 10,
STT_HIOS = 12,
/// Processor-specific use.
STT_LOPROC = 13,
STT_HIPROC = 15,
}
Elf64_Ehdr makeFileHeader(Elf64_Off sectionHeaderOffset,
Elf64_Half sectionHeaderCount,
Elf64_Half stringIndex) @nogc
{
Elf64_Ehdr header;
// Magic number.
header.e_ident[0] = '\x7f';
header.e_ident[1] = 'E';
header.e_ident[2] = 'L';
header.e_ident[3] = 'F';
// File class.
header.e_ident[4] = EI_CLASS.ELFCLASS64;
// Data encoding.
header.e_ident[5] = EI_DATA.ELFDATA2LSB;
// Version.
header.e_ident[6] = EV_CURRENT;
// OS/ABI identification.
header.e_ident[7] = EI_OSABI.ELFOSABI_SYSV;
// ABI version.
header.e_ident[8] = 0;
// Size of e_ident[].
header.e_ident[15] = 0;
header.e_type = ET_REL;
header.e_machine = 0x3e; // EM_X86_64: AMD x86-64 architecture
header.e_version = EV_CURRENT;
header.e_entry = null;
header.e_phoff = 0;
header.e_shoff = sectionHeaderOffset;
header.e_flags = 0;
header.e_ehsize = Elf64_Ehdr.sizeof;
header.e_phentsize = 0;
header.e_phnum = 0;
header.e_shentsize = Elf64_Shdr.sizeof;
header.e_shnum = sectionHeaderCount;
header.e_shstrndx = stringIndex;
return header;
}
enum char[33] sectionStringTable = "\0.symtab\0.strtab\0.shstrtab\0.text\0";
Elf64_Shdr makeTextHeader(Elf64_Off offset, Elf64_Xword size) @nogc
{
Elf64_Shdr table;
table.sh_name = 0x1b;
table.sh_type = SHT_PROGBITS;
table.sh_flags = SHF_EXECINSTR | SHF_ALLOC;
table.sh_addr = null;
table.sh_offset = offset;
table.sh_size = size;
table.sh_link = SHN_UNDEF;
table.sh_info = 0;
table.sh_addralign = 1;
table.sh_entsize = 0;
return table;
}
Elf64_Shdr makeDataHeader(Elf64_Off offset, Elf64_Xword size) @nogc
{
Elf64_Shdr table;
table.sh_name = 0x21;
table.sh_type = SHT_PROGBITS;
table.sh_flags = SHF_WRITE | SHF_ALLOC;
table.sh_addr = null;
table.sh_offset = offset;
table.sh_size = size;
table.sh_link = SHN_UNDEF;
table.sh_info = 0;
table.sh_addralign = 1;
table.sh_entsize = 0;
return table;
}
Elf64_Shdr makeSymtableHeader(Elf64_Off offset, Elf64_Xword size, Elf64_Word entriesCount) @nogc
{
Elf64_Shdr table;
table.sh_name = 0x01;
table.sh_type = SHT_SYMTAB;
table.sh_flags = 0;
table.sh_addr = null;
table.sh_offset = offset;
table.sh_size = size;
table.sh_link = 0x03; // String table used by entries in this section.
table.sh_info = entriesCount;
table.sh_addralign = 8;
table.sh_entsize = Elf64_Sym.sizeof;
return table;
}
Elf64_Shdr makeStringHeader(Elf64_Word stringIndex, Elf64_Off offset, Elf64_Xword size) @nogc
{
Elf64_Shdr table;
table.sh_name = stringIndex;
table.sh_type = SHT_STRTAB;
table.sh_flags = 0;
table.sh_addr = null;
table.sh_offset = offset;
table.sh_size = size;
table.sh_link = SHN_UNDEF;
table.sh_info = 0;
table.sh_addralign = 1;
table.sh_entsize = 0;
return table;
}
Elf64_Shdr makeInitialHeader() @nogc
{
Elf64_Shdr table;
table.sh_name = 0;
table.sh_type = SHT_NULL;
table.sh_flags = 0;
table.sh_addr = null;
table.sh_offset = 0;
table.sh_size = 0;
table.sh_link = SHN_UNDEF;
table.sh_info = 0;
table.sh_addralign = 0;
table.sh_entsize = 0;
return table;
}
Elf64_Sym makeInitialSymTable() @nogc
{
Elf64_Sym table;
table.st_name = 0;
table.st_info = 0;
table.st_other = 0;
table.st_shndx = 0;
table.st_value = null;
table.st_size = 0;
return table;
}
Elf64_Sym makeMainSymTable(Elf64_Half textIndex) @nogc
{
Elf64_Sym table;
table.st_name = 0x01;
table.st_info = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC);
table.st_other = 0;
table.st_shndx = textIndex;
table.st_value = null;
table.st_size = 0;
return table;
}
ubyte ELF32_ST_BIND(ubyte i) @nogc nothrow pure @safe
{
return i >> 4;
}
ubyte ELF32_ST_TYPE(ubyte i) @nogc nothrow pure @safe
{
return i & 0xf;
}
ubyte ELF32_ST_INFO(ubyte b, ubyte t) @nogc nothrow pure @safe
{
return cast(ubyte) ((b << 4) + (t & 0xf));
}
/// Special Section Indices.
enum : Elf64_Half
{
/// Used to mark an undefined or meaningless section reference.
SHN_UNDEF = 0,
/// Processor-specific use.
SHN_LOPROC = 0xFF00,
SHN_HIPROC = 0xFF1F,
/// Environment-specific use.
SHN_LOOS = 0xFF20,
SHN_HIOS = 0xFF3F,
/// Indicates that the corresponding reference is an absolute value.
SHN_ABS = 0xFFF1,
/**
* Indicates a symbol that has been declared as a common block (Fortran
* COMMON or C tentative declaration).
*/
SHN_COMMON = 0xFFF2,
}
/**
* Object File Classes, e_ident[EI_CLASS].
*/
enum EI_CLASS : ubyte
{
/// 32-bit objects.
ELFCLASS32 = 1,
/// 64-bit objects.
ELFCLASS64 = 2,
}
enum ubyte EV_CURRENT = 1;
/**
* Data Encodings, e_ident[EI_DATA].
*/
enum EI_DATA : ubyte
{
/// Object file data structures are little-endian.
ELFDATA2LSB = 1,
/// Object file data structures are big-endian.
ELFDATA2MSB = 2,
}
/**
* Operating System and ABI Identifiers, e_ident[EI_OSABI].
*/
enum EI_OSABI : ubyte
{
/// System V ABI.
ELFOSABI_SYSV = 0,
/// HP-UX operating system.
ELFOSABI_HPUX = 1,
/// Standalone (embedded) application.
ELFOSABI_STANDALONE = 255,
}
enum : Elf64_Half
{
ET_NONE = 0, /// No file type.
ET_REL = 1, /// Relocatable object file.
ET_EXEC = 2, /// Executable file.
ET_DYN = 3, /// Shared object file.
ET_CORE = 4, /// Core file.
ET_LOOS = 0xFE00, /// Environment-specific use.
ET_HIOS = 0xFEFF,
ET_LOPROC = 0xFF00, /// Processor-specific use.
ET_HIPROC = 0xFFFF,
}
private size_t pad(size_t value) @nogc
{
return (value / 8 + 1) * 8;
}
struct Symbol
{
String name;
Array!ubyte instructions;
}
/*
.text
.globl main
.type main, @function
main:
movl $3, %eax
ret
*/
immutable ubyte[] instructions = [
// Opcode of pushq is “0x50 + r”, where “r” is the register opcode.
// Register opcode of %rbq is 5.
0x50 + 5, // push% %rbp
0x48, 0x89, 0xe5, // movq %rsp, %rbp
0xb8, 0x03, 0x00, 0x00, 0x00, // movl $3, %eax
// Epilogue.
0x48, 0x89, 0xec, // movq %rbp, %rsp
0x58 + 5, // popq %rbp
0xc3, // ret
];
void writeObject(Definition ast, String outputFilename) @nogc
{
auto handle = fopen(outputFilename.toStringz, "wb");
if (handle is null)
{
perror("writing sample");
return;
}
scope (exit)
{
fclose(handle);
}
size_t currentOffset = Elf64_Ehdr.sizeof;
Array!Elf64_Shdr sectionHeaders;
Array!Elf64_Sym symbolEntries;
// Prologue
Array!ubyte asmTemplate = Array!ubyte(cast(ubyte[]) [
// Opcode of pushq is “0x50 + r”, where “r” is the register opcode.
// Register opcode of %rbq is 5.
0x50 + 5, // pushq %rbp
0x48, 0x89, 0xe5, // movq %rsp, %rbp
]);
int i = 1;
foreach (statement; ast.statements[])
{
if ((cast(Number) statement.subroutine.lhs) !is null)
{
// Opcode of mov is “0xb8 + r”, where “r” is the register opcode.
// Register opcode of %eax is 0.
asmTemplate.insertBack(cast(ubyte) 0xb8); // movl $x, %eax; where $x is a number.
asmTemplate.insertBack((cast(ubyte*) &(cast(Number) statement.subroutine.lhs).value)[0 .. int.sizeof]);
}
else if ((cast(Variable) statement.subroutine.lhs) !is null)
{
// movl -x(%rbp), %ebx; where x is a number.
asmTemplate.insertBack(cast(ubyte[]) [0x8b, 0x45]);
const disposition = (cast(Variable) statement.subroutine.lhs).counter * (-4);
asmTemplate.insertBack((cast(ubyte*) &disposition)[0 .. 1]);
}
if ((cast(Number) statement.subroutine.rhs) !is null)
{
// Opcode of mov is “0xb8 + r”, where “r” is the register opcode.
// Register opcode of %ebx is 3.
asmTemplate.insertBack(cast(ubyte) 0xbb); // movl $x, %ebx; where $x is a number.
asmTemplate.insertBack((cast(ubyte*) &(cast(Number) statement.subroutine.rhs).value)[0 .. int.sizeof]);
}
else if ((cast(Variable) statement.subroutine.rhs) !is null)
{
// movl -x(%rbp), %ebx; where x is a number.
asmTemplate.insertBack(cast(ubyte[]) [0x8b, 0x5d]);
const disposition = (cast(Variable) statement.subroutine.rhs).counter * (-4);
asmTemplate.insertBack((cast(ubyte*) &disposition)[0 .. 1]);
}
// Calculate the result and assign it to a variable on the stack.
asmTemplate.insertBack(cast(ubyte[]) [0x01, 0xd8]); // add %ebx, %eax
asmTemplate.insertBack(cast(ubyte[]) [0x89, 0x45]); // movl %eax, -x(%rbp); where x is a number.
const disposition = i * (-4);
asmTemplate.insertBack((cast(ubyte*) &disposition)[0 .. 1]);
++i;
}
// Epilogue.
asmTemplate.insertBack(cast(ubyte[]) [
0x48, 0x89, 0xec, // movq %rbp, %rsp
0x58 + 5, // popq %rbp
0xc3, // ret
]);
Symbol[1] symbols = [Symbol(String("main"), asmTemplate)];
sectionHeaders.insertBack(makeInitialHeader());
symbolEntries.insertBack(makeInitialSymTable());
String stringTable = String("\0");
foreach (symbol; symbols[])
{
stringTable.insertBack(symbol.name[]);
stringTable.insertBack('\0');
sectionHeaders.insertBack(makeTextHeader(currentOffset, symbol.instructions.length));
currentOffset = pad(currentOffset + symbol.instructions.length);
symbolEntries.insertBack(makeMainSymTable(cast(Elf64_Half) (sectionHeaders.length - 1)));
}
const symbolTableSize = (symbols.length + 1) * Elf64_Sym.sizeof;
sectionHeaders.insertBack(makeSymtableHeader(currentOffset, symbolTableSize, symbols.length));
currentOffset += symbolTableSize;
sectionHeaders.insertBack(makeStringHeader(0x09, currentOffset, stringTable.length));
currentOffset += stringTable.length;
sectionHeaders.insertBack(makeStringHeader(0x11, currentOffset, sectionStringTable.length));
currentOffset = pad(currentOffset + sectionStringTable.length);
auto fileHeader = makeFileHeader(currentOffset, 5, 4);
version (none)
{
printf("%.2x\n", cast(uint) currentOffset);
}
ubyte[8] padding = 0;
size_t codeLength = stringTable.length + sectionStringTable.length;
fwrite(&fileHeader, 8, Elf64_Ehdr.sizeof / 8, handle);
foreach (symbol; symbols[])
{
immutable size_t instructionsLength = pad(symbol.instructions.length);
fwrite(symbol.instructions.get.ptr, 1, symbol.instructions.length, handle);
fwrite(padding.ptr, 1, instructionsLength - symbol.instructions.length, handle);
codeLength += instructionsLength;
}
fwrite(symbolEntries.get.ptr, Elf64_Sym.sizeof, symbolEntries.length, handle);
fwrite(stringTable.get.ptr, 1, stringTable.length, handle);
fwrite(sectionStringTable.ptr, 1, sectionStringTable.length, handle);
fwrite(padding.ptr, pad(codeLength) - codeLength, 1, handle);
fwrite(sectionHeaders.get.ptr, Elf64_Shdr.sizeof, sectionHeaders.length, handle);
}
String generate(Definition ast) @nogc
{
// Prologue
String asmTemplate = ".text
.globl main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
";
/* Allocate space on the stack for local variables.
asmTemplate.insertBack(" sub $");
asmTemplate.insertBack(format!"{}"(ast.statements.length)[]);
asmTemplate.insertBack(", $esp\n"); */
int i = 1;
foreach (statement; ast.statements[])
{
if ((cast(Number) statement.subroutine.lhs) !is null)
{
asmTemplate.insertBack(" movl $");
asmTemplate.insertBack(format!"{}"((cast(Number) statement.subroutine.lhs).value)[]);
asmTemplate.insertBack(", %eax\n");
}
else if ((cast(Variable) statement.subroutine.lhs) !is null)
{
asmTemplate.insertBack(" movl -");
asmTemplate.insertBack(format!"{}"((cast(Variable) statement.subroutine.lhs).counter * 4)[]);
asmTemplate.insertBack("(%rbp), %eax\n");
}
if ((cast(Number) statement.subroutine.rhs) !is null)
{
asmTemplate.insertBack(" movl $");
asmTemplate.insertBack(format!"{}"((cast(Number) statement.subroutine.rhs).value)[]);
asmTemplate.insertBack(", %ebx\n");
}
else if ((cast(Variable) statement.subroutine.rhs) !is null)
{
asmTemplate.insertBack(" movl -");
asmTemplate.insertBack(format!"{}"((cast(Variable) statement.subroutine.rhs).counter * 4)[]);
asmTemplate.insertBack("(%rbp), %ebx\n");
}
// Calculate the result and assign it to a variable on the stack.
asmTemplate.insertBack(" add %ebx, %eax\n");
asmTemplate.insertBack(" movl %eax, -");
asmTemplate.insertBack(format!"{}"(i * 4)[]);
asmTemplate.insertBack("(%rbp)\n");
++i;
}
// Epilogue.
asmTemplate.insertBack(" movq %rbp, %rsp
popq %rbp
ret
");
return asmTemplate;
}

View File

@ -1,144 +0,0 @@
module elna.ir;
import parser = elna.parser;
import tanya.container.array;
import tanya.container.hashtable;
import tanya.container.string;
import tanya.memory.allocator;
import tanya.memory.mmappool;
/**
* Definition.
*/
class Definition
{
char[] identifier;
Array!Statement statements;
Array!VariableDeclaration variableDeclarations;
}
class Statement
{
Subroutine subroutine;
}
abstract class Expression
{
}
class Number : Expression
{
int value;
}
class Variable : Expression
{
size_t counter;
}
class VariableDeclaration
{
String identifier;
}
class Subroutine
{
Expression lhs, rhs;
}
private Number transformNumber(parser.Number number) @nogc
{
return MmapPool.instance.make!Number(number.value);
}
private Variable transformSubroutine(parser.Subroutine subroutine,
ref Array!Statement statements,
ref HashTable!(String, int) constants) @nogc
{
auto target = MmapPool.instance.make!Subroutine;
target.lhs = transformExpression(subroutine.lhs, statements, constants);
target.rhs = transformExpression(subroutine.rhs, statements, constants);
auto newStatement = MmapPool.instance.make!Statement;
newStatement.subroutine = target;
statements.insertBack(newStatement);
auto newVariable = MmapPool.instance.make!Variable;
newVariable.counter = statements.length;
return newVariable;
}
private Expression transformExpression(parser.Expression expression,
ref Array!Statement statements,
ref HashTable!(String, int) constants) @nogc
{
if ((cast(parser.Number) expression) !is null)
{
auto numberExpression = MmapPool.instance.make!Number;
numberExpression.value = (cast(parser.Number) expression).value;
return numberExpression;
}
if ((cast(parser.Variable) expression) !is null)
{
auto numberExpression = MmapPool.instance.make!Number;
numberExpression.value = constants[(cast(parser.Variable) expression).identifier];
return numberExpression;
}
else if ((cast(parser.Subroutine) expression) !is null)
{
return transformSubroutine(cast(parser.Subroutine) expression, statements, constants);
}
return null;
}
Expression transformStatement(parser.Statement statement,
ref Array!Statement statements,
ref HashTable!(String, int) constants) @nogc
{
if ((cast(parser.BangStatement) statement) !is null)
{
return transformExpression((cast(parser.BangStatement) statement).expression, statements, constants);
}
return null;
}
HashTable!(String, int) transformConstants(ref Array!(parser.Definition) definitions) @nogc
{
typeof(return) constants;
foreach (definition; definitions[])
{
constants[definition.identifier] = definition.number.value;
}
return constants;
}
Array!VariableDeclaration transformVariableDeclarations(ref Array!(parser.VariableDeclaration) variableDeclarations)
@nogc
{
typeof(return) variables;
foreach (ref variableDeclaration; variableDeclarations)
{
auto newDeclaration = MmapPool.instance.make!VariableDeclaration;
newDeclaration.identifier = variableDeclaration.identifier;
variables.insertBack(newDeclaration);
}
return variables;
}
Definition transform(parser.Block block) @nogc
{
auto target = MmapPool.instance.make!Definition;
auto constants = transformConstants(block.definitions);
transformStatement(block.statement, target.statements, constants);
target.variableDeclarations = transformVariableDeclarations(block.variableDeclarations);
return target;
}

View File

@ -1,252 +0,0 @@
module elna.lexer;
import core.stdc.stdlib;
import core.stdc.ctype;
import core.stdc.string;
import elna.result;
import std.range;
import tanya.container.array;
import tanya.container.string;
import tanya.memory.mmappool;
struct Token
{
enum Type
{
number,
subroutine, // Operator.
let,
identifier,
equals,
var,
semicolon,
leftParen,
rightParen,
bang,
dot,
comma,
}
union Value
{
int number;
String identifier;
}
private Type type;
private Value value_;
private Position position_;
@disable this();
this(Type type, Position position) @nogc nothrow pure @safe
{
this.type = type;
this.position_ = position;
}
this(Type type, int value, Position position) @nogc nothrow pure @trusted
in (type == Type.number)
{
this(type, position);
this.value_.number = value;
}
this()(Type type, auto ref String value, Position position)
@nogc nothrow pure @trusted
in (type == Type.identifier)
{
this(type, position);
this.value_.identifier = value;
}
/**
* Params:
* type = Expected type.
*
* Returns: Whether this token is of the expected type.
*/
bool ofType(Type type) const @nogc nothrow pure @safe
{
return this.type == type;
}
@property auto value(Type type)() @nogc nothrow pure @trusted
in (ofType(type))
{
static if (type == Type.number)
{
return this.value_.number;
}
else static if (type == Type.identifier)
{
return this.value_.identifier;
}
else
{
static assert(false, "This type doesn't have a value");
}
}
/**
* Returns: The token position in the source text.
*/
@property const(Position) position() const @nogc nothrow pure @safe
{
return this.position_;
}
}
/**
* Range over the source text that keeps track of the current position.
*/
struct Source
{
char[] buffer;
Position position;
this(char[] buffer) @nogc nothrow pure @safe
{
this.buffer = buffer;
}
@disable this();
bool empty() @nogc nothrow pure @safe
{
return this.length == 0;
}
char front() @nogc nothrow pure @safe
in (!empty)
{
return this.buffer[0];
}
void popFront() @nogc nothrow pure @safe
in (!empty)
{
this.buffer = buffer[1 .. $];
++this.position.column;
}
void breakLine() @nogc nothrow pure @safe
in (!empty)
{
this.buffer = buffer[1 .. $];
++this.position.line;
this.position.column = 1;
}
@property size_t length() const @nogc nothrow pure @safe
{
return this.buffer.length;
}
char opIndex(size_t index) @nogc nothrow pure @safe
in (index < length)
{
return this.buffer[index];
}
char[] opSlice(size_t i, size_t j) @nogc nothrow pure @safe
in
{
assert(i <= j);
assert(j <= length);
}
do
{
return this.buffer[i .. j];
}
}
Array!Token lex(char[] buffer) @nogc
{
Array!Token tokens;
auto source = Source(buffer);
while (!source.empty)
{
if (source.front == ' ')
{
source.popFront;
}
else if (source.front >= '0' && source.front <= '9') // Multi-digit.
{
tokens.insertBack(Token(Token.Type.number, source.front - '0', source.position));
source.popFront;
}
else if (source.front == '=')
{
tokens.insertBack(Token(Token.Type.equals, source.position));
source.popFront;
}
else if (source.front == '(')
{
tokens.insertBack(Token(Token.Type.leftParen, source.position));
source.popFront;
}
else if (source.front == ')')
{
tokens.insertBack(Token(Token.Type.rightParen, source.position));
source.popFront;
}
else if (source.front == ';')
{
tokens.insertBack(Token(Token.Type.semicolon, source.position));
source.popFront;
}
else if (source.front == ',')
{
tokens.insertBack(Token(Token.Type.comma, source.position));
source.popFront;
}
else if (source.front == '!')
{
tokens.insertBack(Token(Token.Type.bang, source.position));
source.popFront;
}
else if (source.front == '.')
{
tokens.insertBack(Token(Token.Type.dot, source.position));
source.popFront;
}
else if (isalpha(source.front))
{
size_t i = 1;
while (i < source.length && isalpha(source[i]))
{
++i;
}
if (source[0 .. i] == "const")
{
tokens.insertBack(Token(Token.Type.let, source.position));
}
else if (source[0 .. i] == "var")
{
tokens.insertBack(Token(Token.Type.var, source.position));
}
else
{
auto identifier = String(source[0 .. i]);
tokens.insertBack(Token(Token.Type.identifier, identifier, source.position));
}
source.popFrontN(i);
}
else if (source.front == '+') // Multi-character, random special characters.
{
tokens.insertBack(Token(Token.Type.subroutine, source.position));
source.popFront;
}
else if (source.front == '\n')
{
source.breakLine;
}
else
{
return typeof(tokens)(); // Error.
}
}
return tokens;
}

View File

@ -1,269 +0,0 @@
module elna.parser;
import elna.lexer;
import elna.result;
import tanya.container.array;
import tanya.container.string;
import tanya.memory.allocator;
import tanya.memory.mmappool;
/**
* Constant definition.
*/
class Definition
{
Number number;
String identifier;
}
/**
* Variable declaration.
*/
class VariableDeclaration
{
String identifier;
}
abstract class Statement
{
}
class BangStatement : Statement
{
Expression expression;
}
class Block
{
Array!Definition definitions;
Array!VariableDeclaration variableDeclarations;
Statement statement;
}
abstract class Expression
{
}
class Number : Expression
{
int value;
}
class Variable : Expression
{
String identifier;
}
class Subroutine : Expression
{
Expression lhs, rhs;
}
private Result!Expression parseExpression(ref Array!(Token).Range tokens) @nogc
in (!tokens.empty, "Expected expression, got end of stream")
{
if (tokens.front.ofType(Token.Type.number))
{
auto number = MmapPool.instance.make!Number;
number.value = tokens.front.value!(Token.Type.number);
tokens.popFront;
return Result!Expression(number);
}
else if (tokens.front.ofType(Token.Type.identifier))
{
auto variable = MmapPool.instance.make!Variable;
variable.identifier = tokens.front.value!(Token.Type.identifier);
tokens.popFront;
return Result!Expression(variable);
}
else if (tokens.front.ofType(Token.Type.subroutine))
{
auto subroutine = MmapPool.instance.make!Subroutine;
tokens.popFront;
auto expression = parseExpression(tokens);
if (expression.valid)
{
subroutine.lhs = expression.result;
}
else
{
return Result!Expression("Expected left-hand side to be an expression", tokens.front.position);
}
expression = parseExpression(tokens);
if (expression.valid)
{
subroutine.rhs = expression.result;
}
else
{
return Result!Expression("Expected left-hand side to be an expression", tokens.front.position);
}
return Result!Expression(subroutine);
}
else if (tokens.front.ofType(Token.Type.leftParen))
{
tokens.popFront;
auto expression = parseExpression(tokens);
tokens.popFront;
return expression;
}
return Result!Expression("Expected an expression", tokens.front.position);
}
private Result!Definition parseDefinition(ref Array!Token.Range tokens) @nogc
in (!tokens.empty, "Expected definition, got end of stream")
{
auto definition = MmapPool.instance.make!Definition;
definition.identifier = tokens.front.value!(Token.Type.identifier); // Copy.
tokens.popFront();
tokens.popFront(); // Skip the equals sign.
if (tokens.front.ofType(Token.Type.number))
{
auto number = MmapPool.instance.make!Number;
number.value = tokens.front.value!(Token.Type.number);
definition.number = number;
tokens.popFront;
return Result!Definition(definition);
}
return Result!Definition("Expected a number", tokens.front.position);
}
private Result!Statement parseStatement(ref Array!Token.Range tokens) @nogc
in (!tokens.empty, "Expected block, got end of stream")
{
if (tokens.front.ofType(Token.Type.bang))
{
tokens.popFront;
auto statement = MmapPool.instance.make!BangStatement;
auto expression = parseExpression(tokens);
if (expression.valid)
{
statement.expression = expression.result;
}
else
{
return Result!Statement(expression.error.get);
}
return Result!Statement(statement);
}
return Result!Statement("Expected ! statement", tokens.front.position);
}
private Result!(Array!Definition) parseDefinitions(ref Array!Token.Range tokens) @nogc
in (!tokens.empty, "Expected definition, got end of stream")
{
tokens.popFront; // Skip const.
Array!Definition definitions;
while (!tokens.empty)
{
auto definition = parseDefinition(tokens);
if (!definition.valid)
{
return typeof(return)(definition.error.get);
}
definitions.insertBack(definition.result);
if (tokens.front.ofType(Token.Type.semicolon))
{
break;
}
if (tokens.front.ofType(Token.Type.comma))
{
tokens.popFront;
}
}
return typeof(return)(definitions);
}
private Result!(Array!VariableDeclaration) parseVariableDeclarations(ref Array!Token.Range tokens) @nogc
in (!tokens.empty, "Expected variable declarations, got end of stream")
{
tokens.popFront; // Skip var.
Array!VariableDeclaration variableDeclarations;
while (!tokens.empty)
{
auto currentToken = tokens.front;
if (currentToken.ofType(Token.Type.identifier))
{
auto variableDeclaration = MmapPool.instance.make!VariableDeclaration;
variableDeclaration.identifier = currentToken.value!(Token.Type.identifier);
variableDeclarations.insertBack(variableDeclaration);
tokens.popFront;
}
else
{
return typeof(return)("Expected variable name", tokens.front.position);
}
if (tokens.empty)
{
return typeof(return)("Expected \";\" or \",\" name", currentToken.position);
}
if (tokens.front.ofType(Token.Type.semicolon))
{
break;
}
if (tokens.front.ofType(Token.Type.comma))
{
tokens.popFront;
}
}
return typeof(return)(variableDeclarations);
}
private Result!Block parseBlock(ref Array!Token.Range tokens) @nogc
in (!tokens.empty, "Expected block, got end of stream")
{
auto block = MmapPool.instance.make!Block;
if (tokens.front.ofType(Token.Type.let))
{
auto constDefinitions = parseDefinitions(tokens);
if (constDefinitions.valid)
{
block.definitions = constDefinitions.result;
}
else
{
return Result!Block(constDefinitions.error.get);
}
tokens.popFront;
}
if (tokens.front.ofType(Token.Type.var))
{
auto variableDeclarations = parseVariableDeclarations(tokens);
if (variableDeclarations.valid)
{
block.variableDeclarations = variableDeclarations.result;
}
else
{
return Result!Block(variableDeclarations.error.get);
}
tokens.popFront;
}
auto statement = parseStatement(tokens);
if (statement.valid)
{
block.statement = statement.result;
}
else
{
return Result!Block(statement.error.get);
}
return Result!Block(block);
}
Result!Block parse(ref Array!Token tokenStream) @nogc
{
auto tokens = tokenStream[];
return parseBlock(tokens);
}

View File

@ -1,84 +0,0 @@
module elna.result;
import std.typecons;
/**
* Position in the source text.
*/
struct Position
{
/// Line.
size_t line = 1;
/// Column.
size_t column = 1;
}
struct CompileError
{
private string message_;
private Position position_;
@disable this();
/**
* Params:
* message = Error text.
* position = Error position in the source text.
*/
this(string message, Position position) @nogc nothrow pure @safe
{
this.message_ = message;
this.position_ = position;
}
/// Error text.
@property string message() const @nogc nothrow pure @safe
{
return this.message_;
}
/// Error line in the source text.
@property size_t line() const @nogc nothrow pure @safe
{
return this.position_.line;
}
/// Error column in the source text.
@property size_t column() const @nogc nothrow pure @safe
{
return this.position_.column;
}
}
struct Result(T)
{
Nullable!CompileError error;
T result;
this(T result)
{
this.result = result;
this.error = typeof(this.error).init;
}
this(string message, Position position)
{
this.result = T.init;
this.error = CompileError(message, position);
}
this(CompileError compileError)
{
this.result = null;
this.error = compileError;
}
@disable this();
@property bool valid() const
{
return error.isNull;
}
}

325
source/lexer.cpp Normal file
View File

@ -0,0 +1,325 @@
#include "elna/source/lexer.hpp"
#include <cassert>
#include <variant>
namespace elna::source
{
using source_position = elna::source::position;
using source_error = elna::source::error;
token::value::value()
: nil(nullptr)
{
}
token::value::value(std::int32_t value)
: number(value)
{
}
token::value::value(const std::string& value)
: identifier(value)
{
}
token::value::~value()
{
}
token::token(const type of, const std::string& value, const source_position position)
: m_type(of), m_value(value), m_position(position)
{
}
token::token(const type of, std::int32_t number, const source_position position)
: m_type(of), m_value(number), m_position(position)
{
}
token::token(type of, value&& value, const elna::source::position position)
: m_type(of), m_position(position)
{
if (has_identifier())
{
new((void *) &m_value.identifier) std::string(std::move(value.identifier));
}
else if (is_numeric())
{
m_value.number = value.number;
}
else
{
m_value.nil = nullptr;
}
}
token::token(const type of, source_position position)
: m_type(of), m_position(position)
{
}
token::token(const token& that)
{
*this = that;
}
token::token(token&& that)
{
*this = std::move(that);
}
token::~token()
{
if (has_identifier())
{
m_value.identifier.~basic_string();
}
}
token& token::operator=(const token& that)
{
if (has_identifier())
{
m_value.identifier.~basic_string();
}
m_type = that.of();
m_position = that.position();
if (that.has_identifier())
{
new((void *) &m_value.identifier) std::string(that.identifier());
}
else if (that.is_numeric())
{
m_value.number = that.number();
}
else
{
m_value.nil = nullptr;
}
return *this;
}
token& token::operator=(token&& that)
{
if (has_identifier())
{
m_value.identifier.~basic_string();
}
m_type = that.of();
m_position = that.position();
if (that.has_identifier())
{
new((void *) &m_value.identifier) std::string(std::move(that.identifier()));
}
else if (that.is_numeric())
{
m_value.number = that.number();
}
else
{
m_value.nil = nullptr;
}
return *this;
}
token::type token::of() const noexcept
{
return m_type;
}
const std::string& token::identifier() const
{
if (!has_identifier())
{
throw std::bad_variant_access();
}
return m_value.identifier;
}
std::int32_t token::number() const
{
if (!is_numeric())
{
throw std::bad_variant_access();
}
return m_value.number;
}
const source_position& token::position() const noexcept
{
return m_position;
}
bool token::has_identifier() const noexcept
{
return of() == type::identifier
|| of() == type::term_operator
|| of() == type::factor_operator
|| of() == type::comparison_operator;
}
bool token::is_numeric() const noexcept
{
return of() == type::number
|| of() == type::boolean;
}
std::string token::to_string() const
{
switch (this->m_type)
{
case type::number:
return "«number»";
case type::boolean:
return "«boolean»";
case type::term_operator:
return "«term_operator»";
case type::let:
return "«const»";
case type::identifier:
return "«identifier»";
case type::equals:
return "«=»";
case type::var:
return "«var»";
case type::semicolon:
return "«;»";
case type::left_paren:
return "«(»";
case type::right_paren:
return "«)»";
case type::dot:
return "«)»";
case type::comma:
return "«,»";
case type::factor_operator:
return "«*»";
case type::eof:
return "«EOF»";
case type::begin:
return "«begin»";
case type::end:
return "«end»";
case type::assignment:
return "«:=»";
case type::colon:
return "«:»";
case type::when:
return "«if»";
case type::then:
return "«then»";
case type::loop:
return "«while»";
case type::_do:
return "«do»";
case type::procedure:
return "«proc»";
case type::comparison_operator:
return "«comparison_operator»";
};
assert(false);
}
unexpected_character::unexpected_character(const std::string& character, const std::filesystem::path& path,
const source::position position)
: error(path, position), character(character)
{
}
std::string unexpected_character::what() const
{
std::string ss{ "Unexpected character '" };
ss.insert(ss.cend(), character.cbegin(), character.cend());
ss.push_back('\'');
return ss;
}
unexpected_token::unexpected_token(const token& token, const std::filesystem::path& path)
: error(path, token.position()), m_token(token)
{
}
std::string unexpected_token::what() const
{
return "Unexpected token " + m_token.to_string();
}
lexer::lexer(std::vector<token>&& tokens, const position last_position, const std::filesystem::path& path)
: tokens(std::move(tokens)), iterator(this->tokens.cbegin()), eof(token(token::type::eof, last_position)),
source_file(path)
{
}
lexer& lexer::operator++()
{
++iterator;
return *this;
}
const token& lexer::operator*() const
{
return *iterator;
}
const token *lexer::operator->() const
{
return iterator.base();
}
const token& lexer::current() const noexcept
{
if (iterator == tokens.cend())
{
return this->eof;
}
return *iterator;
}
bool lexer::current(const token::type token_type) const noexcept
{
return current().of() == token_type;
}
void lexer::add_error(const token& expected)
{
m_errors.push_back(std::make_unique<unexpected_token>(expected, this->source_file));
}
std::optional<std::reference_wrapper<const token>> lexer::advance(const token::type token_type)
{
if (iterator != tokens.cend() && iterator->of() == token_type)
{
return std::make_optional<>(std::cref(*iterator++));
}
add_error(current());
return std::optional<std::reference_wrapper<const token>>();
}
const token& lexer::look_ahead() const
{
auto tmp = iterator;
++tmp;
if (iterator == tokens.cend() || tmp == tokens.cend())
{
return eof;
}
return *tmp;
}
bool lexer::look_ahead(const token::type token_type) const
{
return look_ahead().of() == token_type;
}
bool lexer::skip(const token::type token_type)
{
return advance(token_type).has_value();
}
const std::list<std::unique_ptr<error>>& lexer::errors() const noexcept
{
return m_errors;
}
}

View File

@ -1,72 +0,0 @@
import core.stdc.stdio;
import core.stdc.string;
import core.stdc.stdlib;
import elna.lexer;
import elna.parser;
import elna.generator;
import elna.ir;
import tanya.container.string;
import tanya.memory.allocator;
import tanya.memory.mmappool;
private char[] readSource(size_t N)(string source, out char[N] buffer) @nogc
{
memcpy(buffer.ptr, source.ptr, source.length + 1);
buffer[source.length] = '\0';
auto handle = fopen(buffer.ptr, "r");
if (handle is null)
{
perror(buffer.ptr);
return null;
}
fseek(handle, 0, SEEK_END);
size_t fsize = ftell(handle);
rewind(handle);
fread(buffer.ptr, fsize, 1, handle);
fclose(handle);
buffer[fsize] = '\0';
return buffer[0 .. fsize];
}
int main(string[] args)
{
char[255] buffer;
defaultAllocator = MmapPool.instance;
if (args.length < 2)
{
return 4;
}
auto sourceText = readSource(args[1], buffer);
if (sourceText is null)
{
return 3;
}
auto tokens = lex(sourceText);
if (tokens.length == 0)
{
printf("Lexical analysis failed.\n");
return 1;
}
auto ast = parse(tokens);
if (!ast.valid)
{
auto compileError = ast.error.get;
printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.message.ptr);
return 2;
}
auto ir = transform(ast.result);
String outputFilename = String("build/");
outputFilename.insertBack(args[1][0 .. $ - 4]);
outputFilename.insertBack("o");
writeObject(ir, outputFilename);
auto code = generate(ir);
printf("%s", code.toStringz());
return 0;
}

267
source/optimizer.cpp Normal file
View File

@ -0,0 +1,267 @@
#include "elna/source/optimizer.hpp"
#include <cassert>
namespace elna::source
{
quadruple::quadruple(const quadruple_operator operation, std::shared_ptr<operand> operand1,
std::shared_ptr<operand> operand2, std::shared_ptr<operand> operand3)
: m_operation(operation), m_operand1(operand1), m_operand2(operand2), m_operand3(operand3)
{
}
quadruple_operator quadruple::operation() const noexcept
{
return m_operation;
}
std::shared_ptr<operand> quadruple::operand1()
{
return m_operand1;
}
std::shared_ptr<operand> quadruple::operand2()
{
return m_operand2;
}
std::shared_ptr<operand> quadruple::operand3()
{
return m_operand3;
}
intermediate_code::intermediate_code()
{
clear();
}
void intermediate_code::emplace_back(const quadruple_operator operation, std::shared_ptr<operand> operand1,
std::shared_ptr<operand> operand2, std::shared_ptr<operand> operand3)
{
this->instructions.emplace_back(operation, operand1, operand2, operand3);
}
void intermediate_code::clear()
{
this->instructions.clear();
std::uint32_t variable_counter = 1;
std::uint32_t label_counter = 0;
}
std::vector<quadruple>::iterator intermediate_code::begin()
{
return this->instructions.begin();
}
std::vector<quadruple>::iterator intermediate_code::end()
{
return this->instructions.end();
}
std::int32_t intermediate_code::variable_counter() const noexcept
{
return m_variable_counter;
}
std::int32_t intermediate_code::increment_variable() noexcept
{
return m_variable_counter++;
}
std::int32_t intermediate_code::label_counter() const noexcept
{
return m_label_counter;
}
std::int32_t intermediate_code::increment_label() noexcept
{
return m_label_counter++;
}
intermediate_code_generator::intermediate_code_generator(std::shared_ptr<symbol_table> table)
: table(table)
{
}
quadruple_operator intermediate_code_generator::convert(const binary_operator operation) const
{
switch (operation)
{
case binary_operator::sum:
return quadruple_operator::add;
case binary_operator::subtraction:
return quadruple_operator::sub;
case binary_operator::multiplication:
return quadruple_operator::mul;
case binary_operator::division:
return quadruple_operator::div;
case binary_operator::equals:
return quadruple_operator::eq;
case source::binary_operator::not_equals:
return quadruple_operator::neq;
case source::binary_operator::less:
return quadruple_operator::lt;
case source::binary_operator::greater_equal:
return quadruple_operator::ge;
case source::binary_operator::greater:
return quadruple_operator::gt;
case source::binary_operator::less_equal:
return quadruple_operator::le;
default:
assert(false);
}
}
quadruple_operator intermediate_code_generator::convert(const unary_operator operation) const
{
switch (operation)
{
case unary_operator::reference:
return quadruple_operator::ref;
case unary_operator::dereference:
return quadruple_operator::load;
default:
assert(false);
}
}
void intermediate_code_generator::visit(source::declaration *declaration)
{
}
void intermediate_code_generator::visit(source::constant_definition *definition)
{
}
void intermediate_code_generator::visit(procedure_definition *definition)
{
this->current.emplace_back(quadruple_operator::start);
definition->body().accept(this);
this->current.emplace_back(quadruple_operator::stop);
code[definition->identifier()] = std::move(this->current);
this->current.clear();
}
std::unordered_map<std::string, intermediate_code>::iterator intermediate_code_generator::begin()
{
return code.begin();
}
std::unordered_map<std::string, intermediate_code>::iterator intermediate_code_generator::end()
{
return code.end();
}
void intermediate_code_generator::visit(block *block)
{
block->body().accept(this);
}
void intermediate_code_generator::visit(program *program)
{
for (auto& definition : program->definitions())
{
definition->accept(this);
}
this->current.emplace_back(quadruple_operator::start);
program->body().accept(this);
this->current.emplace_back(quadruple_operator::stop);
code["main"] = std::move(this->current);
}
void intermediate_code_generator::visit(call_statement *statement)
{
for (auto& argument : statement->arguments())
{
argument->accept(this);
this->current.emplace_back(quadruple_operator::param, argument->place, nullptr, nullptr);
}
this->current.emplace_back(quadruple_operator::call,
std::make_shared<variable_operand>(statement->name()),
std::make_shared<integer_operand>(statement->arguments().size()));
}
void intermediate_code_generator::visit(assign_statement *statement)
{
statement->rvalue().accept(this);
this->current.emplace_back(quadruple_operator::assign, statement->rvalue().place, nullptr,
std::make_shared<variable_operand>(statement->lvalue()));
}
void intermediate_code_generator::visit(if_statement *statement)
{
statement->prerequisite().accept(this);
auto end_label = std::make_shared<label_operand>(this->current.increment_label());
this->current.emplace_back(quadruple_operator::beqz, statement->prerequisite().place, nullptr, end_label);
statement->body().accept(this);
this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, end_label);
}
void intermediate_code_generator::visit(while_statement *statement)
{
auto condition_label = std::make_shared<label_operand>(this->current.increment_label());
this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, condition_label);
statement->prerequisite().accept(this);
auto end_label = std::make_shared<label_operand>(this->current.increment_label());
this->current.emplace_back(quadruple_operator::beqz, statement->prerequisite().place, nullptr, end_label);
statement->body().accept(this);
this->current.emplace_back(quadruple_operator::j, nullptr, nullptr, condition_label);
this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, end_label);
}
void intermediate_code_generator::visit(type_expression *type)
{
}
void intermediate_code_generator::visit(variable_expression *variable)
{
auto symbol = table->lookup(variable->name());
if (auto constant_symbol = std::dynamic_pointer_cast<source::constant_info>(symbol))
{
variable->place = std::make_shared<integer_operand>(constant_symbol->value());
}
else
{
variable->place = std::make_shared<variable_operand>(variable->name());
}
}
void intermediate_code_generator::visit(binary_expression *expression)
{
expression->lhs().accept(this);
auto left = expression->lhs().place;
expression->rhs().accept(this);
auto right = expression->rhs().place;
auto operation = convert(expression->operation());
auto new_place = std::make_shared<temporary_variable>(this->current.increment_variable());
this->current.emplace_back(operation, left, right, new_place);
expression->place = new_place;
}
void intermediate_code_generator::visit(unary_expression *expression)
{
expression->operand().accept(this);
auto operation = convert(expression->operation());
auto new_place = std::make_shared<temporary_variable>(this->current.increment_variable());
this->current.emplace_back(operation, expression->operand().place, nullptr, new_place);
expression->place = new_place;
}
void intermediate_code_generator::visit(integer_literal *number)
{
number->place = std::make_shared<integer_operand>(number->number());
}
void intermediate_code_generator::visit(boolean_literal *number)
{
number->place = std::make_shared<integer_operand>(number->boolean());
}
}

1063
source/parser.cpp Normal file

File diff suppressed because it is too large Load Diff

36
source/result.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "elna/source/result.hpp"
#include "elna/source/types.hpp"
namespace elna::source
{
error::error(const std::filesystem::path& path, const position position)
: m_position(position), m_path(path)
{
}
std::size_t error::line() const noexcept
{
return this->m_position.line;
}
std::size_t error::column() const noexcept
{
return this->m_position.column;
}
const std::filesystem::path& error::path() const noexcept
{
return this->m_path;
}
name_collision::name_collision(const std::string& name, const std::filesystem::path& path,
const position current, const position previous)
: error(path, current), name(name), previous(previous)
{
}
std::string name_collision::what() const
{
return "Name '" + name + "' was already defined";
}
}

204
source/scanner.l Normal file
View File

@ -0,0 +1,204 @@
%{
#define YY_NO_UNISTD_H
#define YY_USER_ACTION token_position = elna::source::position{ line_no, column_no }; column_no += yyleng;
#include <fstream>
#include "elna/source/lexer.hpp"
elna::source::token::value yylval{};
elna::source::position token_position{};
static std::size_t column_no = 1;
static std::size_t line_no = 1;
%}
%option noyywrap
%option never-interactive
%%
\-\-.* {
/* Skip the comment */
}
[\ \t\r] {
/* Skip the whitespaces */
}
\n {
++line_no;
column_no = 1;
}
if {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::when);
}
then {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::then);
}
while {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::loop);
}
do {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::_do);
}
proc {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::procedure);
}
begin {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::begin);
}
end {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::end);
}
const {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::let);
}
var {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::var);
}
True {
yylval.number = 1;
return static_cast<int>(elna::source::token::type::boolean);
}
False {
yylval.number = 0;
return static_cast<int>(elna::source::token::type::boolean);
}
[A-Za-z_][A-Za-z0-9_]* {
new((void *) &yylval.identifier) std::string(yytext);
return static_cast<int>(elna::source::token::type::identifier);
}
[0-9]+ {
yylval.number = strtol(yytext, NULL, 10);
return static_cast<int>(elna::source::token::type::number);
}
\( {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::left_paren);
}
\) {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::right_paren);
}
\>= {
new((void *) &yylval.identifier) std::string(1, 'g');
return static_cast<int>(elna::source::token::type::comparison_operator);
}
\<= {
new((void *) &yylval.identifier) std::string(1, 'l');
return static_cast<int>(elna::source::token::type::comparison_operator);
}
(>|<) {
new((void *) &yylval.identifier) std::string(yytext);
return static_cast<int>(elna::source::token::type::comparison_operator);
}
\/= {
new((void *) &yylval.identifier) std::string(1, 'n');
return static_cast<int>(elna::source::token::type::comparison_operator);
}
= {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::equals);
}
; {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::semicolon);
}
\. {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::dot);
}
, {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::comma);
}
(\+|\-) {
new((void *) &yylval.identifier) std::string(yytext);
return static_cast<int>(elna::source::token::type::term_operator);
}
(\*|\/) {
new((void *) &yylval.identifier) std::string(yytext);
return static_cast<int>(elna::source::token::type::factor_operator);
}
:= {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::assignment);
}
: {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::colon);
}
\^ {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::hat);
}
@ {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::at);
}
. {
return -1;
}
%%
namespace elna::source
{
result<lexer> tokenize(const std::filesystem::path& path)
{
int yytoken;
std::vector<token> tokens;
yyin = fopen(path.c_str(), "rb");
if (yyin == nullptr)
{
throw std::ios_base::failure("File does not exist");
}
do
{
yytoken = yylex();
if (yytoken < 0)
{
return result<lexer>(unexpected_character{ std::string{ yytext[0] }, path, token_position });
}
tokens.emplace_back(static_cast<token::type>(yytoken), std::move(yylval), token_position);
}
while (yytoken != 0);
return result<lexer>(std::in_place, std::move(tokens), position{ line_no, column_no }, path);
}
}

118
source/semantic.cpp Normal file
View File

@ -0,0 +1,118 @@
#include "elna/source/semantic.hpp"
#include "elna/source/types.hpp"
#include <cstdlib>
namespace elna::source
{
name_analysis_visitor::name_analysis_visitor(std::shared_ptr<symbol_table> table)
: table(table)
{
}
void name_analysis_visitor::visit(constant_definition *definition)
{
this->table->enter(definition->identifier(),
std::make_shared<constant_info>(constant_info(definition->body().number())));
}
std::shared_ptr<const type> name_analysis_visitor::convert_declaration_type(const type_expression& ast_type) const
{
auto variable_type = std::dynamic_pointer_cast<type_info>(table->lookup(ast_type.base()))
->type();
std::shared_ptr<type> declaration_type;
if (ast_type.is_pointer())
{
return std::make_shared<pointer_type>(variable_type, 4);
}
else
{
return variable_type;
}
}
void name_analysis_visitor::visit(declaration *declaration)
{
std::shared_ptr<const type> declaration_type = convert_declaration_type(declaration->type());
this->table->enter(declaration->identifier(),
std::make_shared<variable_info>(declaration_type));
}
void name_analysis_visitor::visit(program *program)
{
this->table->enter("main", std::make_shared<procedure_info>(this->table));
empty_visitor::visit(program);
}
void name_analysis_visitor::visit(procedure_definition *procedure)
{
auto info = std::make_shared<procedure_info>(this->table);
this->table->enter(procedure->identifier(), info);
this->table = info->scope();
for (auto& parameter : procedure->parameters())
{
auto declaration_type = convert_declaration_type(parameter->type());
this->table->enter(parameter->identifier(),
std::make_shared<parameter_info>(declaration_type));
}
procedure->body().accept(this);
this->table = info->scope()->scope();
}
allocator_visitor::allocator_visitor(std::shared_ptr<symbol_table> table)
: table(table)
{
}
void allocator_visitor::visit(declaration *declaration)
{
auto declaration_info = this->table->lookup(declaration->identifier());
if (auto variable = std::dynamic_pointer_cast<variable_info>(declaration_info))
{
this->local_offset -= sizeof(std::int32_t);
variable->offset = this->local_offset;
}
else if (auto parameter = std::dynamic_pointer_cast<parameter_info>(declaration_info))
{
parameter->offset = this->argument_offset;
this->argument_offset += sizeof(std::int32_t);
}
}
void allocator_visitor::visit(program *program)
{
this->local_offset = 0;
this->argument_offset = 0;
empty_visitor::visit(program);
std::dynamic_pointer_cast<procedure_info>(table->lookup("main"))->local_stack_size =
std::abs(this->local_offset);
}
void allocator_visitor::visit(procedure_definition *procedure)
{
this->local_offset = 0;
this->argument_offset = 0;
auto info = std::dynamic_pointer_cast<procedure_info>(this->table->lookup(procedure->identifier()));
this->table = info->scope();
empty_visitor::visit(procedure);
this->table = info->scope()->scope();
info->local_stack_size = std::abs(this->local_offset);
info->argument_stack_size = this->argument_offset;
}
void allocator_visitor::visit(call_statement *statement)
{
auto call_info = std::dynamic_pointer_cast<intrinsic_info>(this->table->lookup(statement->name()));
this->argument_offset = std::max(static_cast<std::size_t>(this->argument_offset),
call_info->parameter_stack_size());
}
}

142
source/symbol_table.cpp Normal file
View File

@ -0,0 +1,142 @@
#include "elna/source/types.hpp"
#include "elna/source/symbol_table.hpp"
namespace elna::source
{
symbol_table::symbol_table(std::shared_ptr<symbol_table> scope)
: outer_scope(scope)
{
if (scope == nullptr)
{
auto boolean_info = std::make_shared<type_info>(boolean_type);
auto int_info = std::make_shared<type_info>(int_type);
enter("Boolean", boolean_info);
enter("Int", int_info);
auto writei = std::make_shared<intrinsic_info>();
writei->parameter_infos.emplace_back(int_info->type());
enter("writei", writei);
auto writeb = std::make_shared<intrinsic_info>();
writeb->parameter_infos.emplace_back(boolean_info->type());
enter("writeb", writeb);
}
}
std::shared_ptr<info> symbol_table::lookup(const std::string& name)
{
auto entry = entries.find(name);
if (entry != entries.cend())
{
return entry->second;
}
if (this->outer_scope != nullptr)
{
return this->outer_scope->lookup(name);
}
return nullptr;
}
void symbol_table::enter(const std::string& name, std::shared_ptr<info> entry)
{
entries.insert_or_assign(name, entry);
}
std::shared_ptr<symbol_table> symbol_table::scope()
{
return this->outer_scope;
}
info::~info()
{
}
info::info()
{
}
type_info::type_info(const class type& type)
: info(), m_type(std::make_shared<class type>(type))
{
}
type_info::~type_info()
{
}
std::shared_ptr<const class type> type_info::type() const noexcept
{
return m_type;
}
constant_info::constant_info(const std::int32_t value)
: m_value(value)
{
}
constant_info::~constant_info()
{
}
std::int32_t constant_info::value() const noexcept
{
return m_value;
}
variable_info::variable_info(std::shared_ptr<const class type> type)
: m_type(type)
{
}
variable_info::~variable_info()
{
}
std::shared_ptr<const class type> variable_info::type() noexcept
{
return m_type;
}
parameter_info::parameter_info(std::shared_ptr<const class type> type)
: m_type(type)
{
}
parameter_info::~parameter_info()
{
}
std::shared_ptr<const class type> parameter_info::type() const noexcept
{
return m_type;
}
intrinsic_info::~intrinsic_info()
{
}
std::size_t intrinsic_info::parameter_stack_size() const noexcept
{
return this->parameter_infos.size() * sizeof(std::int32_t);
}
procedure_info::procedure_info(std::shared_ptr<symbol_table> outer_scope)
: local_table(std::make_shared<symbol_table>(outer_scope))
{
}
procedure_info::~procedure_info()
{
}
std::shared_ptr<symbol_table> procedure_info::scope()
{
return local_table;
}
std::size_t procedure_info::stack_size() const noexcept
{
return local_stack_size + argument_stack_size;
}
}

19
source/types.cpp Normal file
View File

@ -0,0 +1,19 @@
#include <elna/source/types.hpp>
namespace elna::source
{
type::type(const std::size_t byte_size)
: byte_size(byte_size)
{
}
primitive_type::primitive_type(const std::string& type_name, const std::size_t byte_size)
: type(byte_size), type_name(type_name)
{
}
pointer_type::pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size)
: type(byte_size), base_type(base_type)
{
}
}

2
tests/7_member_sum.eln Normal file
View File

@ -0,0 +1,2 @@
writei(3 + 4 + 5 + 1 + 2 + 4 + 3)
.

View File

@ -1,3 +1,3 @@
const a = 1, b = 2; const a = 1, b = 2;
! + a b writei(a + b)
. .

View File

@ -0,0 +1,5 @@
var x: Int;
begin
x := 5;
writei(x)
end.

1
tests/divide.eln Normal file
View File

@ -0,0 +1 @@
writei(6 / 3)

View File

@ -0,0 +1 @@
22

View File

@ -0,0 +1 @@
5

View File

@ -0,0 +1 @@
2

View File

@ -0,0 +1,2 @@
8
9

View File

@ -0,0 +1 @@
8

View File

@ -0,0 +1,2 @@
5
7

View File

@ -0,0 +1 @@
10

View File

@ -0,0 +1 @@
24

View File

@ -0,0 +1 @@
4

View File

@ -0,0 +1 @@
5

View File

@ -0,0 +1,2 @@
t
f

View File

@ -0,0 +1,12 @@
t
f
t
f
f
t
t
f
t
f
f
t

View File

@ -0,0 +1,5 @@
1
2
3
4
5

View File

@ -0,0 +1 @@
3

View File

@ -0,0 +1,2 @@
t
5

View File

@ -0,0 +1 @@
5

View File

@ -0,0 +1 @@
5

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
tests/missing_semicolon.eln:3:3: Unexpected token «identifier»

View File

@ -0,0 +1 @@
tests/single_word_error.eln:1:1: Unexpected token «identifier»

11
tests/if_condition.eln Normal file
View File

@ -0,0 +1,11 @@
begin
if True then writei(8);
if False then
begin
writei(5);
writei(5);
writei(5)
end;
if 1 < 2 then writei(9);
if 1 > 2 then writei(10)
end.

View File

@ -0,0 +1,2 @@
writei((3 + 4) + 1)
.

View File

@ -0,0 +1,4 @@
begin
writei(1)
writei(2)
end.

View File

@ -0,0 +1,4 @@
begin
writei(5);
writei(7)
end.

2
tests/multiply.eln Normal file
View File

@ -0,0 +1,2 @@
writei(5 * 2)
.

2
tests/multiply_3.eln Normal file
View File

@ -0,0 +1,2 @@
writei(3 * 4 * 2)
.

2
tests/plus_minus.eln Normal file
View File

@ -0,0 +1,2 @@
writei(2 + 3 - 1)
.

View File

@ -0,0 +1,6 @@
var a: Int, b: ^Int;
begin
a := 5;
b := @a;
writei(b^)
end.

4
tests/print_boolean.eln Normal file
View File

@ -0,0 +1,4 @@
begin
writeb(True);
writeb(False)
end.

14
tests/print_equals.eln Normal file
View File

@ -0,0 +1,14 @@
begin
writeb(5 = 5);
writeb(5 = 4);
writeb(5 /= 4);
writeb(5 /= 5);
writeb(5 < 4);
writeb(4 < 5);
writeb(5 >= 4);
writeb(4 >= 5);
writeb(5 > 4);
writeb(4 > 5);
writeb(5 <= 4);
writeb(4 <= 5)
end.

10
tests/print_in_loop.eln Normal file
View File

@ -0,0 +1,10 @@
var i: Int;
begin
i := 1;
while i < 6 do
begin
writei(i);
i := i + 1
end;
end.

2
tests/print_number.eln Normal file
View File

@ -0,0 +1,2 @@
writei(3)
.

View File

@ -0,0 +1,9 @@
proc g(a: Boolean, b: Int)
begin
writeb(a);
writei(b)
end;
begin
g(True, 5)
end.

View File

@ -0,0 +1,5 @@
proc f() writei(5);
begin
f()
end.

View File

@ -0,0 +1,5 @@
proc f(a: Int) writei(a);
begin
f(5)
end.

View File

@ -0,0 +1 @@
asdf

2
tests/subtraction.eln Normal file
View File

@ -0,0 +1,2 @@
writei(5 - 4)
.

2
tests/sum.eln Normal file
View File

@ -0,0 +1,2 @@
writei(1 + 7)
.

View File

@ -1,2 +0,0 @@
! + 1 7
.

2
tests/sums.eln Normal file
View File

@ -0,0 +1,2 @@
writei(1 + (3 + 4))
.

View File

@ -1,2 +0,0 @@
! + 1 (+ 3 4)
.

263
tests/tester.cpp Executable file
View File

@ -0,0 +1,263 @@
#include "elna/tester.hpp"
#include <iostream>
#include <algorithm>
#include <boost/process/env.hpp>
namespace elna
{
std::uint32_t test_results::total() const noexcept
{
return m_total;
}
std::uint32_t test_results::passed() const noexcept
{
return m_passed;
}
std::uint32_t test_results::failed() const noexcept
{
return m_total - m_passed;
}
int test_results::exit_code() const noexcept
{
return m_total == m_passed ? EXIT_SUCCESS : EXIT_FAILURE;
}
void test_results::add_exit_code(const test_status status) noexcept
{
++m_total;
if (status == test_status::successful)
{
++m_passed;
}
}
static std::filesystem::path in_build_directory()
{
return "build/riscv";
}
static std::string in_build_directory(const std::filesystem::path& path)
{
return in_build_directory() / path;
}
boost::process::v2::process_stdio get_output_streams(const std::uint8_t stream_number,
boost::asio::readable_pipe& read_pipe)
{
if (stream_number == 1)
{
return boost::process::v2::process_stdio{ nullptr, read_pipe };
}
if (stream_number == 2)
{
return boost::process::v2::process_stdio{ nullptr, {}, read_pipe };
}
return boost::process::v2::process_stdio{ nullptr, read_pipe, read_pipe };
}
test_result run_for_output(boost::asio::io_context& context, const std::uint8_t stream_number,
const std::filesystem::path& binary, std::initializer_list<boost::string_view> arguments)
{
boost::asio::readable_pipe read_pipe{ context };
test_result result{};
std::string output;
boost::asio::dynamic_string_buffer buffer = boost::asio::dynamic_buffer(output);
boost::system::error_code ec;
boost::process::v2::process_stdio process_stdio;
boost::process::v2::process elna_child(context,
binary, arguments,
get_output_streams(stream_number, read_pipe));
do
{
std::size_t transferred = read_pipe.read_some(buffer.prepare(512), ec);
if (transferred == 0)
{
break;
}
buffer.commit(transferred);
result.output.append(boost::asio::buffer_cast<const char *>(buffer.data()), buffer.size());
buffer.consume(transferred);
}
while (ec == boost::system::errc::success);
result.code = elna_child.wait();
return result;
}
std::string find_object(const std::vector<std::filesystem::path>& environment, const std::string& object)
{
auto variable = std::find(environment.cbegin(), environment.cend(), object);
for (const auto& variable : environment)
{
auto full_path = variable / object;
if (std::filesystem::exists(full_path))
{
return full_path.native();
}
}
return object;
}
test_result build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry)
{
const std::filesystem::path test_filename = test_entry.path().filename();
std::filesystem::path test_binary = in_build_directory(test_filename);
std::filesystem::path test_object = test_binary;
test_binary.replace_extension();
test_object.replace_extension(".o");
std::filesystem::remove(test_binary);
std::filesystem::remove(test_object);
test_result result = run_for_output(context, 2, "./build/bin/elna",
{ "-o", test_object.string(), test_entry.path().string() });
if (result.code != 0)
{
result.status = test_status::compile_failed;
return result;
}
std::vector<std::filesystem::path> environment;
std::vector<std::string> linker_arguments = { "-o", test_binary.string() };
for (const auto segment : boost::this_process::environment()["LIBRARY_PATH"].to_vector())
{
linker_arguments.push_back("-L" + segment);
environment.push_back(std::filesystem::path(segment));
}
linker_arguments.push_back(find_object(environment, "crt0.o"));
linker_arguments.push_back(find_object(environment, "crtbegin.o"));
linker_arguments.push_back(test_object.string());
linker_arguments.insert(linker_arguments.cend(),
{ "--start-group", "-lgcc", "-lc", "-lgloss", "--end-group" });
linker_arguments.push_back(find_object(environment, "crtend.o"));
boost::process::v2::execute(boost::process::v2::process(context,
boost::process::search_path("ld"),
linker_arguments
));
return test_result{};
}
static test_result check_expectation(const std::filesystem::directory_entry& test_entry, const test_result& run)
{
std::filesystem::path test_filename = test_entry.path().filename();
test_filename.replace_extension(".txt");
const std::filesystem::path expectation_path =
test_entry.path().parent_path() / "expectations" / test_filename;
const std::filesystem::path failures_path =
test_entry.path().parent_path() / "failures" / test_filename;
std::string expected_result_path;
if (std::filesystem::exists(expectation_path))
{
expected_result_path = expectation_path.string();
}
else if (std::filesystem::exists(failures_path))
{
expected_result_path = failures_path.string();
}
else
{
return test_result{ test_status::expectation_not_found, run.code, run.output };
}
boost::process::opstream pipe_stream;
boost::process::child diff(
boost::process::search_path("diff"), "-Nur", "--color",
expected_result_path, "-",
boost::process::std_in < pipe_stream
);
pipe_stream << run.output;
pipe_stream.flush();
pipe_stream.pipe().close();
diff.wait();
if (diff.exit_code() == 0)
{
return test_result{ test_status::successful, run.code, run.output };
}
return test_result{ test_status::expectation_failed, run.code, run.output };
}
test_result run_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry)
{
const std::filesystem::path test_filename = test_entry.path().filename();
std::filesystem::path test_binary = in_build_directory(test_filename);
test_binary.replace_extension();
return run_for_output(context, 1,
boost::process::search_path("qemu-riscv32"),
{ test_binary.string() });
}
static test_results run_in_path(const std::filesystem::path test_directory)
{
test_results results;
boost::asio::io_context context;
for (const auto& test_entry : std::filesystem::directory_iterator(test_directory))
{
if (test_entry.path().extension() != ".eln" || !test_entry.is_regular_file())
{
continue;
}
test_result result;
std::cout << "Running " << test_entry << std::endl;
try
{
result = build_test(context, test_entry);
if (result.status == test_status::successful)
{
result = run_test(context, test_entry);
}
result = check_expectation(test_entry, result);
}
catch (const boost::process::process_error& exception)
{
result.status = test_status::build_failed;
result.output = exception.what();
std::cout << result.output << std::endl;
}
print_result(test_entry, result);
results.add_exit_code(result.status);
}
return results;
}
void print_result(const std::filesystem::directory_entry& test_entry, const test_result& result)
{
if (result.status != test_status::successful)
{
std::cout << test_entry << " failed." << std::endl;
}
}
};
int main()
{
std::filesystem::create_directory(elna::in_build_directory());
std::cout << "Run all tests and check the results" << std::endl;
std::filesystem::path test_directory{ "tests" };
const auto results = elna::run_in_path(test_directory);
std::cout << std::endl;
std::cout << results.total() << " tests run, "
<< results.passed() << " passed, "
<< results.failed() << " failed." << std::endl;
return results.exit_code();
}