Print test summary
This commit is contained in:
parent
5490f6ce1c
commit
b34e14f36b
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,3 +1,5 @@
|
|||||||
/.dub/
|
|
||||||
/dub.selections.json
|
|
||||||
/build/
|
/build/
|
||||||
|
.cache/
|
||||||
|
CMakeFiles/
|
||||||
|
CMakeCache.txt
|
||||||
|
dub.selections.json
|
||||||
|
31
CMakeLists.txt
Normal file
31
CMakeLists.txt
Normal 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})
|
69
Rakefile
69
Rakefile
@ -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
16
TODO
Normal 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
455
backend/riscv.cpp
Normal 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
233
backend/target.cpp
Normal 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
50
cli/cl.cpp
Normal 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
47
cli/main.cpp
Normal 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);
|
||||||
|
}
|
9
dub.json
9
dub.json
@ -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
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
1096
include/elfio/elfio.hpp
Normal file
File diff suppressed because it is too large
Load Diff
88
include/elfio/elfio_array.hpp
Normal file
88
include/elfio/elfio_array.hpp
Normal 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
1346
include/elfio/elfio_dump.hpp
Normal file
File diff suppressed because it is too large
Load Diff
274
include/elfio/elfio_dynamic.hpp
Normal file
274
include/elfio/elfio_dynamic.hpp
Normal 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
|
153
include/elfio/elfio_header.hpp
Normal file
153
include/elfio/elfio_header.hpp
Normal 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
|
124
include/elfio/elfio_modinfo.hpp
Normal file
124
include/elfio/elfio_modinfo.hpp
Normal 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
|
184
include/elfio/elfio_note.hpp
Normal file
184
include/elfio/elfio_note.hpp
Normal 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, §ion::get_size>;
|
||||||
|
using const_note_section_accessor =
|
||||||
|
note_section_accessor_template<const section, §ion::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
|
460
include/elfio/elfio_relocation.hpp
Normal file
460
include/elfio/elfio_relocation.hpp
Normal 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
|
367
include/elfio/elfio_section.hpp
Normal file
367
include/elfio/elfio_section.hpp
Normal 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
|
254
include/elfio/elfio_segment.hpp
Normal file
254
include/elfio/elfio_segment.hpp
Normal 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
|
97
include/elfio/elfio_strings.hpp
Normal file
97
include/elfio/elfio_strings.hpp
Normal 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
|
562
include/elfio/elfio_symbols.hpp
Normal file
562
include/elfio/elfio_symbols.hpp
Normal 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
|
302
include/elfio/elfio_utils.hpp
Normal file
302
include/elfio/elfio_utils.hpp
Normal 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
|
1
include/elfio/elfio_version.hpp
Normal file
1
include/elfio/elfio_version.hpp
Normal file
@ -0,0 +1 @@
|
|||||||
|
#define ELFIO_VERSION "3.12"
|
179
include/elfio/elfio_versym.hpp
Normal file
179
include/elfio/elfio_versym.hpp
Normal 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
|
163
include/elna/backend/riscv.hpp
Normal file
163
include/elna/backend/riscv.hpp
Normal 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);
|
||||||
|
}
|
107
include/elna/backend/target.hpp
Normal file
107
include/elna/backend/target.hpp
Normal 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
37
include/elna/cli/cl.hpp
Normal 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);
|
||||||
|
}
|
212
include/elna/source/lexer.hpp
Normal file
212
include/elna/source/lexer.hpp
Normal 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);
|
||||||
|
}
|
106
include/elna/source/optimizer.hpp
Normal file
106
include/elna/source/optimizer.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
520
include/elna/source/parser.hpp
Normal file
520
include/elna/source/parser.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
160
include/elna/source/result.hpp
Normal file
160
include/elna/source/result.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
48
include/elna/source/semantic.hpp
Normal file
48
include/elna/source/semantic.hpp
Normal 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
|
||||||
|
{
|
||||||
|
};
|
||||||
|
}
|
151
include/elna/source/symbol_table.hpp
Normal file
151
include/elna/source/symbol_table.hpp
Normal 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();
|
||||||
|
};
|
||||||
|
}
|
41
include/elna/source/types.hpp
Normal file
41
include/elna/source/types.hpp
Normal 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
55
include/elna/tester.hpp
Normal 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);
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
144
source/elna/ir.d
144
source/elna/ir.d
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
@ -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
325
source/lexer.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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
267
source/optimizer.cpp
Normal 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
1063
source/parser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
36
source/result.cpp
Normal file
36
source/result.cpp
Normal 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
204
source/scanner.l
Normal 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
118
source/semantic.cpp
Normal 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
142
source/symbol_table.cpp
Normal 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
19
source/types.cpp
Normal 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
2
tests/7_member_sum.eln
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
writei(3 + 4 + 5 + 1 + 2 + 4 + 3)
|
||||||
|
.
|
@ -1,3 +1,3 @@
|
|||||||
const a = 1, b = 2;
|
const a = 1, b = 2;
|
||||||
! + a b
|
writei(a + b)
|
||||||
.
|
.
|
5
tests/declare_variable.eln
Normal file
5
tests/declare_variable.eln
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var x: Int;
|
||||||
|
begin
|
||||||
|
x := 5;
|
||||||
|
writei(x)
|
||||||
|
end.
|
1
tests/divide.eln
Normal file
1
tests/divide.eln
Normal file
@ -0,0 +1 @@
|
|||||||
|
writei(6 / 3)
|
1
tests/expectations/7_member_sum.txt
Normal file
1
tests/expectations/7_member_sum.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
22
|
1
tests/expectations/declare_variable.txt
Normal file
1
tests/expectations/declare_variable.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
5
|
1
tests/expectations/divide.txt
Normal file
1
tests/expectations/divide.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
2
|
2
tests/expectations/if_condition.txt
Normal file
2
tests/expectations/if_condition.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
8
|
||||||
|
9
|
1
tests/expectations/left_nested_sum.txt
Normal file
1
tests/expectations/left_nested_sum.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
8
|
2
tests/expectations/multiline_output.txt
Normal file
2
tests/expectations/multiline_output.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
5
|
||||||
|
7
|
1
tests/expectations/multiply.txt
Normal file
1
tests/expectations/multiply.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
10
|
1
tests/expectations/multiply_3.txt
Normal file
1
tests/expectations/multiply_3.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
24
|
1
tests/expectations/plus_minus.txt
Normal file
1
tests/expectations/plus_minus.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
4
|
1
tests/expectations/pointer_in_expression.txt
Normal file
1
tests/expectations/pointer_in_expression.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
5
|
2
tests/expectations/print_boolean.txt
Normal file
2
tests/expectations/print_boolean.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
t
|
||||||
|
f
|
12
tests/expectations/print_equals.txt
Normal file
12
tests/expectations/print_equals.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
t
|
||||||
|
f
|
||||||
|
t
|
||||||
|
f
|
||||||
|
f
|
||||||
|
t
|
||||||
|
t
|
||||||
|
f
|
||||||
|
t
|
||||||
|
f
|
||||||
|
f
|
||||||
|
t
|
5
tests/expectations/print_in_loop.txt
Normal file
5
tests/expectations/print_in_loop.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
4
|
||||||
|
5
|
1
tests/expectations/print_number.txt
Normal file
1
tests/expectations/print_number.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
3
|
2
tests/expectations/procedure_2_statements.txt
Normal file
2
tests/expectations/procedure_2_statements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
t
|
||||||
|
5
|
1
tests/expectations/procedure_definition.txt
Normal file
1
tests/expectations/procedure_definition.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
5
|
1
tests/expectations/procedure_print_argument.txt
Normal file
1
tests/expectations/procedure_print_argument.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
5
|
1
tests/expectations/subtraction.txt
Normal file
1
tests/expectations/subtraction.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
1
|
1
tests/failures/missing_semicolon.txt
Normal file
1
tests/failures/missing_semicolon.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
tests/missing_semicolon.eln:3:3: Unexpected token «identifier»
|
1
tests/failures/single_word_error.txt
Normal file
1
tests/failures/single_word_error.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
tests/single_word_error.eln:1:1: Unexpected token «identifier»
|
11
tests/if_condition.eln
Normal file
11
tests/if_condition.eln
Normal 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.
|
2
tests/left_nested_sum.eln
Normal file
2
tests/left_nested_sum.eln
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
writei((3 + 4) + 1)
|
||||||
|
.
|
4
tests/missing_semicolon.eln
Normal file
4
tests/missing_semicolon.eln
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
begin
|
||||||
|
writei(1)
|
||||||
|
writei(2)
|
||||||
|
end.
|
4
tests/multiline_output.eln
Normal file
4
tests/multiline_output.eln
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
begin
|
||||||
|
writei(5);
|
||||||
|
writei(7)
|
||||||
|
end.
|
2
tests/multiply.eln
Normal file
2
tests/multiply.eln
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
writei(5 * 2)
|
||||||
|
.
|
2
tests/multiply_3.eln
Normal file
2
tests/multiply_3.eln
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
writei(3 * 4 * 2)
|
||||||
|
.
|
2
tests/plus_minus.eln
Normal file
2
tests/plus_minus.eln
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
writei(2 + 3 - 1)
|
||||||
|
.
|
6
tests/pointer_in_expression.eln
Normal file
6
tests/pointer_in_expression.eln
Normal 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
4
tests/print_boolean.eln
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
begin
|
||||||
|
writeb(True);
|
||||||
|
writeb(False)
|
||||||
|
end.
|
14
tests/print_equals.eln
Normal file
14
tests/print_equals.eln
Normal 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
10
tests/print_in_loop.eln
Normal 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
2
tests/print_number.eln
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
writei(3)
|
||||||
|
.
|
9
tests/procedure_2_statements.eln
Normal file
9
tests/procedure_2_statements.eln
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
proc g(a: Boolean, b: Int)
|
||||||
|
begin
|
||||||
|
writeb(a);
|
||||||
|
writei(b)
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
g(True, 5)
|
||||||
|
end.
|
5
tests/procedure_definition.eln
Normal file
5
tests/procedure_definition.eln
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
proc f() writei(5);
|
||||||
|
|
||||||
|
begin
|
||||||
|
f()
|
||||||
|
end.
|
5
tests/procedure_print_argument.eln
Normal file
5
tests/procedure_print_argument.eln
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
proc f(a: Int) writei(a);
|
||||||
|
|
||||||
|
begin
|
||||||
|
f(5)
|
||||||
|
end.
|
1
tests/single_word_error.eln
Normal file
1
tests/single_word_error.eln
Normal file
@ -0,0 +1 @@
|
|||||||
|
asdf
|
2
tests/subtraction.eln
Normal file
2
tests/subtraction.eln
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
writei(5 - 4)
|
||||||
|
.
|
2
tests/sum.eln
Normal file
2
tests/sum.eln
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
writei(1 + 7)
|
||||||
|
.
|
@ -1,2 +0,0 @@
|
|||||||
! + 1 7
|
|
||||||
.
|
|
2
tests/sums.eln
Normal file
2
tests/sums.eln
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
writei(1 + (3 + 4))
|
||||||
|
.
|
@ -1,2 +0,0 @@
|
|||||||
! + 1 (+ 3 4)
|
|
||||||
.
|
|
263
tests/tester.cpp
Executable file
263
tests/tester.cpp
Executable 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();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user