From b34e14f36b56e0c65dead2d5145a46e3f867f517 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 5 Jun 2022 23:43:45 +0200 Subject: [PATCH] Print test summary --- .gitignore | 6 +- CMakeLists.txt | 31 + Rakefile | 69 - TODO | 16 + backend/riscv.cpp | 455 ++++++ backend/target.cpp | 233 +++ cli/cl.cpp | 50 + cli/main.cpp | 47 + dub.json | 9 - include/elfio/elf_types.hpp | 1347 +++++++++++++++++ include/elfio/elfio.hpp | 1096 ++++++++++++++ include/elfio/elfio_array.hpp | 88 ++ include/elfio/elfio_dump.hpp | 1346 ++++++++++++++++ include/elfio/elfio_dynamic.hpp | 274 ++++ include/elfio/elfio_header.hpp | 153 ++ include/elfio/elfio_modinfo.hpp | 124 ++ include/elfio/elfio_note.hpp | 184 +++ include/elfio/elfio_relocation.hpp | 460 ++++++ include/elfio/elfio_section.hpp | 367 +++++ include/elfio/elfio_segment.hpp | 254 ++++ include/elfio/elfio_strings.hpp | 97 ++ include/elfio/elfio_symbols.hpp | 562 +++++++ include/elfio/elfio_utils.hpp | 302 ++++ include/elfio/elfio_version.hpp | 1 + include/elfio/elfio_versym.hpp | 179 +++ include/elna/backend/riscv.hpp | 163 ++ include/elna/backend/target.hpp | 107 ++ include/elna/cli/cl.hpp | 37 + include/elna/source/lexer.hpp | 212 +++ include/elna/source/optimizer.hpp | 106 ++ include/elna/source/parser.hpp | 520 +++++++ include/elna/source/result.hpp | 160 ++ include/elna/source/semantic.hpp | 48 + include/elna/source/symbol_table.hpp | 151 ++ include/elna/source/types.hpp | 41 + include/elna/tester.hpp | 55 + source/elna/extended.d | 9 - source/elna/generator.d | 660 -------- source/elna/ir.d | 144 -- source/elna/lexer.d | 252 --- source/elna/parser.d | 269 ---- source/elna/result.d | 84 - source/lexer.cpp | 325 ++++ source/main.d | 72 - source/optimizer.cpp | 267 ++++ source/parser.cpp | 1063 +++++++++++++ source/result.cpp | 36 + source/scanner.l | 204 +++ source/semantic.cpp | 118 ++ source/symbol_table.cpp | 142 ++ source/types.cpp | 19 + tests/7_member_sum.eln | 2 + tests/{const_list.elna => const_list.eln} | 2 +- tests/declare_variable.eln | 5 + tests/divide.eln | 1 + tests/expectations/7_member_sum.txt | 1 + tests/expectations/declare_variable.txt | 1 + tests/expectations/divide.txt | 1 + tests/expectations/if_condition.txt | 2 + tests/expectations/left_nested_sum.txt | 1 + tests/expectations/multiline_output.txt | 2 + tests/expectations/multiply.txt | 1 + tests/expectations/multiply_3.txt | 1 + tests/expectations/plus_minus.txt | 1 + tests/expectations/pointer_in_expression.txt | 1 + tests/expectations/print_boolean.txt | 2 + tests/expectations/print_equals.txt | 12 + tests/expectations/print_in_loop.txt | 5 + tests/expectations/print_number.txt | 1 + tests/expectations/procedure_2_statements.txt | 2 + tests/expectations/procedure_definition.txt | 1 + .../expectations/procedure_print_argument.txt | 1 + tests/expectations/subtraction.txt | 1 + tests/failures/missing_semicolon.txt | 1 + tests/failures/single_word_error.txt | 1 + tests/if_condition.eln | 11 + tests/left_nested_sum.eln | 2 + tests/missing_semicolon.eln | 4 + tests/multiline_output.eln | 4 + tests/multiply.eln | 2 + tests/multiply_3.eln | 2 + tests/plus_minus.eln | 2 + tests/pointer_in_expression.eln | 6 + tests/print_boolean.eln | 4 + tests/print_equals.eln | 14 + tests/print_in_loop.eln | 10 + tests/print_number.eln | 2 + tests/procedure_2_statements.eln | 9 + tests/procedure_definition.eln | 5 + tests/procedure_print_argument.eln | 5 + tests/single_word_error.eln | 1 + tests/subtraction.eln | 2 + tests/sum.eln | 2 + tests/sum.elna | 2 - tests/sums.eln | 2 + tests/sums.elna | 2 - tests/tester.cpp | 263 ++++ 97 files changed, 11844 insertions(+), 1575 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 Rakefile create mode 100644 TODO create mode 100644 backend/riscv.cpp create mode 100644 backend/target.cpp create mode 100644 cli/cl.cpp create mode 100644 cli/main.cpp delete mode 100644 dub.json create mode 100644 include/elfio/elf_types.hpp create mode 100644 include/elfio/elfio.hpp create mode 100644 include/elfio/elfio_array.hpp create mode 100644 include/elfio/elfio_dump.hpp create mode 100644 include/elfio/elfio_dynamic.hpp create mode 100644 include/elfio/elfio_header.hpp create mode 100644 include/elfio/elfio_modinfo.hpp create mode 100644 include/elfio/elfio_note.hpp create mode 100644 include/elfio/elfio_relocation.hpp create mode 100644 include/elfio/elfio_section.hpp create mode 100644 include/elfio/elfio_segment.hpp create mode 100644 include/elfio/elfio_strings.hpp create mode 100644 include/elfio/elfio_symbols.hpp create mode 100644 include/elfio/elfio_utils.hpp create mode 100644 include/elfio/elfio_version.hpp create mode 100644 include/elfio/elfio_versym.hpp create mode 100644 include/elna/backend/riscv.hpp create mode 100644 include/elna/backend/target.hpp create mode 100644 include/elna/cli/cl.hpp create mode 100644 include/elna/source/lexer.hpp create mode 100644 include/elna/source/optimizer.hpp create mode 100644 include/elna/source/parser.hpp create mode 100644 include/elna/source/result.hpp create mode 100644 include/elna/source/semantic.hpp create mode 100644 include/elna/source/symbol_table.hpp create mode 100644 include/elna/source/types.hpp create mode 100644 include/elna/tester.hpp delete mode 100644 source/elna/extended.d delete mode 100644 source/elna/generator.d delete mode 100644 source/elna/ir.d delete mode 100644 source/elna/lexer.d delete mode 100644 source/elna/parser.d delete mode 100644 source/elna/result.d create mode 100644 source/lexer.cpp delete mode 100644 source/main.d create mode 100644 source/optimizer.cpp create mode 100644 source/parser.cpp create mode 100644 source/result.cpp create mode 100644 source/scanner.l create mode 100644 source/semantic.cpp create mode 100644 source/symbol_table.cpp create mode 100644 source/types.cpp create mode 100644 tests/7_member_sum.eln rename tests/{const_list.elna => const_list.eln} (61%) create mode 100644 tests/declare_variable.eln create mode 100644 tests/divide.eln create mode 100644 tests/expectations/7_member_sum.txt create mode 100644 tests/expectations/declare_variable.txt create mode 100644 tests/expectations/divide.txt create mode 100644 tests/expectations/if_condition.txt create mode 100644 tests/expectations/left_nested_sum.txt create mode 100644 tests/expectations/multiline_output.txt create mode 100644 tests/expectations/multiply.txt create mode 100644 tests/expectations/multiply_3.txt create mode 100644 tests/expectations/plus_minus.txt create mode 100644 tests/expectations/pointer_in_expression.txt create mode 100644 tests/expectations/print_boolean.txt create mode 100644 tests/expectations/print_equals.txt create mode 100644 tests/expectations/print_in_loop.txt create mode 100644 tests/expectations/print_number.txt create mode 100644 tests/expectations/procedure_2_statements.txt create mode 100644 tests/expectations/procedure_definition.txt create mode 100644 tests/expectations/procedure_print_argument.txt create mode 100644 tests/expectations/subtraction.txt create mode 100644 tests/failures/missing_semicolon.txt create mode 100644 tests/failures/single_word_error.txt create mode 100644 tests/if_condition.eln create mode 100644 tests/left_nested_sum.eln create mode 100644 tests/missing_semicolon.eln create mode 100644 tests/multiline_output.eln create mode 100644 tests/multiply.eln create mode 100644 tests/multiply_3.eln create mode 100644 tests/plus_minus.eln create mode 100644 tests/pointer_in_expression.eln create mode 100644 tests/print_boolean.eln create mode 100644 tests/print_equals.eln create mode 100644 tests/print_in_loop.eln create mode 100644 tests/print_number.eln create mode 100644 tests/procedure_2_statements.eln create mode 100644 tests/procedure_definition.eln create mode 100644 tests/procedure_print_argument.eln create mode 100644 tests/single_word_error.eln create mode 100644 tests/subtraction.eln create mode 100644 tests/sum.eln delete mode 100644 tests/sum.elna create mode 100644 tests/sums.eln delete mode 100644 tests/sums.elna create mode 100755 tests/tester.cpp diff --git a/.gitignore b/.gitignore index d0d201a..021b743 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ -/.dub/ -/dub.selections.json /build/ +.cache/ +CMakeFiles/ +CMakeCache.txt +dub.selections.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9461ef6 --- /dev/null +++ b/CMakeLists.txt @@ -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}) diff --git a/Rakefile b/Rakefile deleted file mode 100644 index acafe3b..0000000 --- a/Rakefile +++ /dev/null @@ -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 diff --git a/TODO b/TODO new file mode 100644 index 0000000..593f42e --- /dev/null +++ b/TODO @@ -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. diff --git a/backend/riscv.cpp b/backend/riscv.cpp new file mode 100644 index 0000000..e6584cb --- /dev/null +++ b/backend/riscv.cpp @@ -0,0 +1,455 @@ +#include "elna/backend/riscv.hpp" +#include +#include +#include + +namespace elna::riscv +{ + instruction::instruction(base_opcode opcode) + { + this->representation = static_cast::type>(opcode); + } + + instruction& instruction::i(x_register rd, funct3_t funct3, x_register rs1, std::uint32_t immediate) + { + this->representation |= (static_cast::type>(rd) << 7) + | (static_cast::type>(funct3) << 12) + | (static_cast::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::type>(funct3) << 12) + | (static_cast::type>(rs1) << 15) + | (static_cast::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::type>(funct3) << 12) + | (static_cast::type>(rs1) << 15) + | (static_cast::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::type>(rd) << 7) + | (static_cast::type>(funct3) << 12) + | (static_cast::type>(rs1) << 15) + | (static_cast::type>(rs2) << 20) + | (static_cast::type>(funct7) << 25); + + return *this; + } + + instruction& instruction::u(x_register rd, std::uint32_t imm) + { + this->representation |= (static_cast::type>(rd) << 7) | (imm << 12); + + return *this; + } + + instruction& instruction::j(x_register rd, std::uint32_t imm) + { + this->representation |= (static_cast::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(&this->representation); + } + + const std::byte *instruction::cend() const + { + return reinterpret_cast(&this->representation) + sizeof(this->representation); + } + + static void relocate(std::string_view name, address_t target, std::vector& references, + std::vector& instructions, std::shared_ptr> 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& 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& 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> writer, + std::vector& references) + { + writer->sink("printf"); + { + std::vector instructions; + auto format_string = writer->sink(reinterpret_cast("%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(instructions.data()), + instructions.size() * sizeof(instruction)); + } + { + std::vector instructions; + auto format_string = writer->sink(reinterpret_cast("%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(instructions.data()), + instructions.size() * sizeof(instruction)); + } + } + + static void load_in_register(std::shared_ptr operand, const x_register target, + std::shared_ptr procedure_info, std::vector& instructions) + { + std::shared_ptr integer_operand{ nullptr }; + std::shared_ptr variable_operand{ nullptr }; + std::shared_ptr temporary_variable{ nullptr }; + std::shared_ptr variable_symbol{ nullptr }; + std::shared_ptr parameter_symbol{ nullptr }; + + if ((integer_operand = std::dynamic_pointer_cast(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(operand)) != nullptr) + { + const auto& name = procedure_info->scope()->lookup(variable_operand->name()); + if ((variable_symbol = std::dynamic_pointer_cast(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(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(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 destination, const x_register target, + std::shared_ptr procedure_info, std::vector& instructions) + { + std::shared_ptr variable_operand{ nullptr }; + std::shared_ptr temporary_variable{ nullptr }; + std::shared_ptr variable_symbol{ nullptr }; + + if ((variable_operand = std::dynamic_pointer_cast(destination)) != nullptr) + { + variable_symbol = std::dynamic_pointer_cast( + 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(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 lhs, + std::shared_ptr rhs, std::shared_ptr destination, + std::shared_ptr procedure_info, std::vector& instructions) + { + constexpr auto lhs_register = x_register::a0; + std::shared_ptr variable_operand{ nullptr }; + std::shared_ptr temporary_variable{ nullptr }; + std::shared_ptr 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 generate(source::intermediate_code_generator generator, + std::shared_ptr table, std::shared_ptr> writer) + { + std::vector references; + + generate_intrinsics(writer, references); + + for (auto& [identifier, intermediate_code] : generator) + { + std::vector instructions; + auto main_symbol = std::dynamic_pointer_cast(table->lookup(identifier)); + std::size_t argument_offset{ 0 }; + const auto stack_size = static_cast( + intermediate_code.variable_counter() * 4 + 8 + main_symbol->stack_size()); + std::unordered_map> missing_labels; + std::unordered_map>::iterator missing_label = + missing_labels.end(); + std::unordered_map 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(quadruple.operand1())->name(); + auto variable_symbol = std::dynamic_pointer_cast( + 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(quadruple.operand1())->name(); + auto variable_symbol = std::dynamic_pointer_cast( + 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(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(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(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(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(instructions.data()), + instructions.size() * sizeof(instruction)); + } + return references; + } +} diff --git a/backend/target.cpp b/backend/target.cpp new file mode 100644 index 0000000..5ab5d04 --- /dev/null +++ b/backend/target.cpp @@ -0,0 +1,233 @@ +#include "elna/backend/target.hpp" +#include "elna/backend/riscv.hpp" +#include + +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(data), size); + } + + std::pair 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(labels.back(), true); + } + return std::pair(found->label, false); + } + + elfio_section_writer::iterator elfio_section_writer::begin() const + { + return elfio_section_writer::iterator(labels.cbegin(), sizes.cbegin(), + reinterpret_cast(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(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 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(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); + } +} diff --git a/cli/cl.cpp b/cli/cl.cpp new file mode 100644 index 0000000..97b4d4b --- /dev/null +++ b/cli/cl.cpp @@ -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 + +namespace elna::cli +{ + void print_error(const std::unique_ptr& 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 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::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; + } +} diff --git a/cli/main.cpp b/cli/main.cpp new file mode 100644 index 0000000..409abbd --- /dev/null +++ b/cli/main.cpp @@ -0,0 +1,47 @@ +#include "elna/cli/cl.hpp" + +#include +#include +#include + +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(), "Output object file") + ; + description.add_options() + ("input", boost::program_options::value()->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 out_file; + + if (variables_map.count("output")) + { + out_file = variables_map["output"].as(); + } + else + { + out_file = in_file.filename().replace_extension(".o"); + } + return elna::cli::compile(in_file, out_file); +} diff --git a/dub.json b/dub.json deleted file mode 100644 index 8567d3a..0000000 --- a/dub.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "dependencies": { - "tanya": "~>0.18.0" - }, - "name": "elna", - "targetType": "executable", - "targetPath": "build/bin", - "mainSourceFile": "source/main.d" -} diff --git a/include/elfio/elf_types.hpp b/include/elfio/elf_types.hpp new file mode 100644 index 0000000..c4f6b47 --- /dev/null +++ b/include/elfio/elf_types.hpp @@ -0,0 +1,1347 @@ +/* +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 ELFTYPES_H +#define ELFTYPES_H + +#ifdef __cplusplus +namespace ELFIO { +#endif + +using Elf_Half = uint16_t; +using Elf_Word = uint32_t; +using Elf_Sword = int32_t; +using Elf_Xword = uint64_t; +using Elf_Sxword = int64_t; + +using Elf32_Addr = uint32_t; +using Elf32_Off = uint32_t; +using Elf64_Addr = uint64_t; +using Elf64_Off = uint64_t; + +using Elf32_Half = Elf_Half; +using Elf64_Half = Elf_Half; +using Elf32_Word = Elf_Word; +using Elf64_Word = Elf_Word; +using Elf32_Sword = Elf_Sword; +using Elf64_Sword = Elf_Sword; + +/////////////////////// +// ELF Header Constants + +// File type +constexpr Elf_Half ET_NONE = 0; +constexpr Elf_Half ET_REL = 1; +constexpr Elf_Half ET_EXEC = 2; +constexpr Elf_Half ET_DYN = 3; +constexpr Elf_Half ET_CORE = 4; +constexpr Elf_Half ET_LOOS = 0xFE00; +constexpr Elf_Half ET_HIOS = 0xFEFF; +constexpr Elf_Half ET_LOPROC = 0xFF00; +constexpr Elf_Half ET_HIPROC = 0xFFFF; + +// clang-format off +// Machine number +constexpr Elf_Half EM_NONE = 0 ; // No machine +constexpr Elf_Half EM_M32 = 1 ; // AT&T WE 32100 +constexpr Elf_Half EM_SPARC = 2 ; // SUN SPARC +constexpr Elf_Half EM_386 = 3 ; // Intel 80386 +constexpr Elf_Half EM_68K = 4 ; // Motorola m68k family +constexpr Elf_Half EM_88K = 5 ; // Motorola m88k family +constexpr Elf_Half EM_486 = 6 ; // Intel 80486// Reserved for future use +constexpr Elf_Half EM_860 = 7 ; // Intel 80860 +constexpr Elf_Half EM_MIPS = 8 ; // MIPS R3000 (officially, big-endian only) +constexpr Elf_Half EM_S370 = 9 ; // IBM System/370 +constexpr Elf_Half EM_MIPS_RS3_LE = 10; // MIPS R3000 little-endian (Deprecated) +constexpr Elf_Half EM_res011 = 11; // Reserved +constexpr Elf_Half EM_res012 = 12; // Reserved +constexpr Elf_Half EM_res013 = 13; // Reserved +constexpr Elf_Half EM_res014 = 14; // Reserved +constexpr Elf_Half EM_PARISC = 15; // HPPA +constexpr Elf_Half EM_res016 = 16; // Reserved +constexpr Elf_Half EM_VPP550 = 17; // Fujitsu VPP500 +constexpr Elf_Half EM_SPARC32PLUS = 18; // Sun's "v8plus" +constexpr Elf_Half EM_960 = 19; // Intel 80960 +constexpr Elf_Half EM_PPC = 20; // PowerPC +constexpr Elf_Half EM_PPC64 = 21; // 64-bit PowerPC +constexpr Elf_Half EM_S390 = 22; // IBM S/390 +constexpr Elf_Half EM_SPU = 23; // Sony/Toshiba/IBM SPU +constexpr Elf_Half EM_res024 = 24; // Reserved +constexpr Elf_Half EM_res025 = 25; // Reserved +constexpr Elf_Half EM_res026 = 26; // Reserved +constexpr Elf_Half EM_res027 = 27; // Reserved +constexpr Elf_Half EM_res028 = 28; // Reserved +constexpr Elf_Half EM_res029 = 29; // Reserved +constexpr Elf_Half EM_res030 = 30; // Reserved +constexpr Elf_Half EM_res031 = 31; // Reserved +constexpr Elf_Half EM_res032 = 32; // Reserved +constexpr Elf_Half EM_res033 = 33; // Reserved +constexpr Elf_Half EM_res034 = 34; // Reserved +constexpr Elf_Half EM_res035 = 35; // Reserved +constexpr Elf_Half EM_V800 = 36; // NEC V800 series +constexpr Elf_Half EM_FR20 = 37; // Fujitsu FR20 +constexpr Elf_Half EM_RH32 = 38; // TRW RH32 +constexpr Elf_Half EM_MCORE = 39; // Motorola M*Core // May also be taken by Fujitsu MMA +constexpr Elf_Half EM_RCE = 39; // Old name for MCore +constexpr Elf_Half EM_ARM = 40; // ARM +constexpr Elf_Half EM_OLD_ALPHA = 41; // Digital Alpha +constexpr Elf_Half EM_SH = 42; // Renesas (formerly Hitachi) / SuperH SH +constexpr Elf_Half EM_SPARCV9 = 43; // SPARC v9 64-bit +constexpr Elf_Half EM_TRICORE = 44; // Siemens Tricore embedded processor +constexpr Elf_Half EM_ARC = 45; // ARC Cores +constexpr Elf_Half EM_H8_300 = 46; // Renesas (formerly Hitachi) H8/300 +constexpr Elf_Half EM_H8_300H = 47; // Renesas (formerly Hitachi) H8/300H +constexpr Elf_Half EM_H8S = 48; // Renesas (formerly Hitachi) H8S +constexpr Elf_Half EM_H8_500 = 49; // Renesas (formerly Hitachi) H8/500 +constexpr Elf_Half EM_IA_64 = 50; // Intel IA-64 Processor +constexpr Elf_Half EM_MIPS_X = 51; // Stanford MIPS-X +constexpr Elf_Half EM_COLDFIRE = 52; // Motorola Coldfire +constexpr Elf_Half EM_68HC12 = 53; // Motorola M68HC12 +constexpr Elf_Half EM_MMA = 54; // Fujitsu Multimedia Accelerator +constexpr Elf_Half EM_PCP = 55; // Siemens PCP +constexpr Elf_Half EM_NCPU = 56; // Sony nCPU embedded RISC processor +constexpr Elf_Half EM_NDR1 = 57; // Denso NDR1 microprocesspr +constexpr Elf_Half EM_STARCORE = 58; // Motorola Star*Core processor +constexpr Elf_Half EM_ME16 = 59; // Toyota ME16 processor +constexpr Elf_Half EM_ST100 = 60; // STMicroelectronics ST100 processor +constexpr Elf_Half EM_TINYJ = 61; // Advanced Logic Corp. TinyJ embedded processor +constexpr Elf_Half EM_X86_64 = 62; // Advanced Micro Devices X86-64 processor +constexpr Elf_Half EM_PDSP = 63; // Sony DSP Processor +constexpr Elf_Half EM_PDP10 = 64; // Digital Equipment Corp. PDP-10 +constexpr Elf_Half EM_PDP11 = 65; // Digital Equipment Corp. PDP-11 +constexpr Elf_Half EM_FX66 = 66; // Siemens FX66 microcontroller +constexpr Elf_Half EM_ST9PLUS = 67; // STMicroelectronics ST9+ 8/16 bit microcontroller +constexpr Elf_Half EM_ST7 = 68 ; // STMicroelectronics ST7 8-bit microcontroller +constexpr Elf_Half EM_68HC16 = 69 ; // Motorola MC68HC16 Microcontroller +constexpr Elf_Half EM_68HC11 = 70 ; // Motorola MC68HC11 Microcontroller +constexpr Elf_Half EM_68HC08 = 71 ; // Motorola MC68HC08 Microcontroller +constexpr Elf_Half EM_68HC05 = 72 ; // Motorola MC68HC05 Microcontroller +constexpr Elf_Half EM_SVX = 73 ; // Silicon Graphics SVx +constexpr Elf_Half EM_ST19 = 74 ; // STMicroelectronics ST19 8-bit cpu +constexpr Elf_Half EM_VAX = 75 ; // Digital VAX +constexpr Elf_Half EM_CRIS = 76 ; // Axis Communications 32-bit embedded processor +constexpr Elf_Half EM_JAVELIN = 77 ; // Infineon Technologies 32-bit embedded cpu +constexpr Elf_Half EM_FIREPATH = 78 ; // Element 14 64-bit DSP processor +constexpr Elf_Half EM_ZSP = 79 ; // LSI Logic's 16-bit DSP processor +constexpr Elf_Half EM_MMIX = 80 ; // Donald Knuth's educational 64-bit processor +constexpr Elf_Half EM_HUANY = 81 ; // Harvard's machine-independent format +constexpr Elf_Half EM_PRISM = 82 ; // SiTera Prism +constexpr Elf_Half EM_AVR = 83 ; // Atmel AVR 8-bit microcontroller +constexpr Elf_Half EM_FR30 = 84 ; // Fujitsu FR30 +constexpr Elf_Half EM_D10V = 85 ; // Mitsubishi D10V +constexpr Elf_Half EM_D30V = 86 ; // Mitsubishi D30V +constexpr Elf_Half EM_V850 = 87 ; // NEC v850 +constexpr Elf_Half EM_M32R = 88 ; // Renesas M32R (formerly Mitsubishi M32R) +constexpr Elf_Half EM_MN10300 = 89 ; // Matsushita MN10300 +constexpr Elf_Half EM_MN10200 = 90 ; // Matsushita MN10200 +constexpr Elf_Half EM_PJ = 91 ; // picoJava +constexpr Elf_Half EM_OPENRISC = 92 ; // OpenRISC 32-bit embedded processor +constexpr Elf_Half EM_ARC_A5 = 93 ; // ARC Cores Tangent-A5 +constexpr Elf_Half EM_XTENSA = 94 ; // Tensilica Xtensa Architecture +constexpr Elf_Half EM_VIDEOCORE = 95 ; // Alphamosaic VideoCore processor +constexpr Elf_Half EM_TMM_GPP = 96 ; // Thompson Multimedia General Purpose Processor +constexpr Elf_Half EM_NS32K = 97 ; // National Semiconductor 32000 series +constexpr Elf_Half EM_TPC = 98 ; // Tenor Network TPC processor +constexpr Elf_Half EM_SNP1K = 99 ; // Trebia SNP 1000 processor +constexpr Elf_Half EM_ST200 = 100; // STMicroelectronics ST200 microcontroller +constexpr Elf_Half EM_IP2K = 101; // Ubicom IP2022 micro controller +constexpr Elf_Half EM_MAX = 102; // MAX Processor +constexpr Elf_Half EM_CR = 103; // National Semiconductor CompactRISC +constexpr Elf_Half EM_F2MC16 = 104; // Fujitsu F2MC16 +constexpr Elf_Half EM_MSP430 = 105; // TI msp430 micro controller +constexpr Elf_Half EM_BLACKFIN = 106; // ADI Blackfin +constexpr Elf_Half EM_SE_C33 = 107; // S1C33 Family of Seiko Epson processors +constexpr Elf_Half EM_SEP = 108; // Sharp embedded microprocessor +constexpr Elf_Half EM_ARCA = 109; // Arca RISC Microprocessor +constexpr Elf_Half EM_UNICORE = 110; // Microprocessor series from PKU-Unity Ltd. +constexpr Elf_Half EM_EXCESS = 111; // eXcess: 16/32/64-bit configurable embedded CPU +constexpr Elf_Half EM_DXP = 112; // Icera Semiconductor Inc. Deep Execution Processor +constexpr Elf_Half EM_ALTERA_NIOS2 = 113; // Altera Nios II soft-core processor +constexpr Elf_Half EM_CRX = 114; // National Semiconductor CRX +constexpr Elf_Half EM_XGATE = 115; // Motorola XGATE embedded processor +constexpr Elf_Half EM_C166 = 116; // Infineon C16x/XC16x processor +constexpr Elf_Half EM_M16C = 117; // Renesas M16C series microprocessors +constexpr Elf_Half EM_DSPIC30F = 118; // Microchip Technology dsPIC30F DSignal Controller +constexpr Elf_Half EM_CE = 119; // Freescale Communication Engine RISC core +constexpr Elf_Half EM_M32C = 120; // Renesas M32C series microprocessors +constexpr Elf_Half EM_res121 = 121; // Reserved +constexpr Elf_Half EM_res122 = 122; // Reserved +constexpr Elf_Half EM_res123 = 123; // Reserved +constexpr Elf_Half EM_res124 = 124; // Reserved +constexpr Elf_Half EM_res125 = 125; // Reserved +constexpr Elf_Half EM_res126 = 126; // Reserved +constexpr Elf_Half EM_res127 = 127; // Reserved +constexpr Elf_Half EM_res128 = 128; // Reserved +constexpr Elf_Half EM_res129 = 129; // Reserved +constexpr Elf_Half EM_res130 = 130; // Reserved +constexpr Elf_Half EM_TSK3000 = 131; // Altium TSK3000 core +constexpr Elf_Half EM_RS08 = 132; // Freescale RS08 embedded processor +constexpr Elf_Half EM_res133 = 133; // Reserved +constexpr Elf_Half EM_ECOG2 = 134; // Cyan Technology eCOG2 microprocessor +constexpr Elf_Half EM_SCORE = 135; // Sunplus Score +constexpr Elf_Half EM_SCORE7 = 135; // Sunplus S+core7 RISC processor +constexpr Elf_Half EM_DSP24 = 136; // New Japan Radio (NJR) 24-bit DSP Processor +constexpr Elf_Half EM_VIDEOCORE3 = 137; // Broadcom VideoCore III processor +constexpr Elf_Half EM_LATTICEMICO32 = 138; // RISC processor for Lattice FPGA architecture +constexpr Elf_Half EM_SE_C17 = 139; // Seiko Epson C17 family +constexpr Elf_Half EM_TI_C6000 = 140; // Texas Instruments TMS320C6000 DSP family +constexpr Elf_Half EM_TI_C2000 = 141; // Texas Instruments TMS320C2000 DSP family +constexpr Elf_Half EM_TI_C5500 = 142; // Texas Instruments TMS320C55x DSP family +constexpr Elf_Half EM_res143 = 143; // Reserved +constexpr Elf_Half EM_res144 = 144; // Reserved +constexpr Elf_Half EM_res145 = 145; // Reserved +constexpr Elf_Half EM_res146 = 146; // Reserved +constexpr Elf_Half EM_res147 = 147; // Reserved +constexpr Elf_Half EM_res148 = 148; // Reserved +constexpr Elf_Half EM_res149 = 149; // Reserved +constexpr Elf_Half EM_res150 = 150; // Reserved +constexpr Elf_Half EM_res151 = 151; // Reserved +constexpr Elf_Half EM_res152 = 152; // Reserved +constexpr Elf_Half EM_res153 = 153; // Reserved +constexpr Elf_Half EM_res154 = 154; // Reserved +constexpr Elf_Half EM_res155 = 155; // Reserved +constexpr Elf_Half EM_res156 = 156; // Reserved +constexpr Elf_Half EM_res157 = 157; // Reserved +constexpr Elf_Half EM_res158 = 158; // Reserved +constexpr Elf_Half EM_res159 = 159; // Reserved +constexpr Elf_Half EM_MMDSP_PLUS = 160; // STMicroelectronics 64bit VLIW Data Signal Processor +constexpr Elf_Half EM_CYPRESS_M8C = 161; // Cypress M8C microprocessor +constexpr Elf_Half EM_R32C = 162; // Renesas R32C series microprocessors +constexpr Elf_Half EM_TRIMEDIA = 163; // NXP Semiconductors TriMedia architecture family +constexpr Elf_Half EM_QDSP6 = 164; // QUALCOMM DSP6 Processor +constexpr Elf_Half EM_8051 = 165; // Intel 8051 and variants +constexpr Elf_Half EM_STXP7X = 166; // STMicroelectronics STxP7x family +constexpr Elf_Half EM_NDS32 = 167; // Andes Technology embedded RISC processor family +constexpr Elf_Half EM_ECOG1 = 168; // Cyan Technology eCOG1X family +constexpr Elf_Half EM_ECOG1X = 168; // Cyan Technology eCOG1X family +constexpr Elf_Half EM_MAXQ30 = 169; // Dallas Semiconductor MAXQ30 Core Micro-controllers +constexpr Elf_Half EM_XIMO16 = 170; // New Japan Radio (NJR) 16-bit DSP Processor +constexpr Elf_Half EM_MANIK = 171; // M2000 Reconfigurable RISC Microprocessor +constexpr Elf_Half EM_CRAYNV2 = 172; // Cray Inc. NV2 vector architecture +constexpr Elf_Half EM_RX = 173; // Renesas RX family +constexpr Elf_Half EM_METAG = 174; // Imagination Technologies META processor architecture +constexpr Elf_Half EM_MCST_ELBRUS = 175; // MCST Elbrus general purpose hardware architecture +constexpr Elf_Half EM_ECOG16 = 176; // Cyan Technology eCOG16 family +constexpr Elf_Half EM_CR16 = 177; // National Semiconductor CompactRISC 16-bit processor +constexpr Elf_Half EM_ETPU = 178; // Freescale Extended Time Processing Unit +constexpr Elf_Half EM_SLE9X = 179; // Infineon Technologies SLE9X core +constexpr Elf_Half EM_L1OM = 180; // Intel L1OM +constexpr Elf_Half EM_INTEL181 = 181; // Reserved by Intel +constexpr Elf_Half EM_INTEL182 = 182; // Reserved by Intel +constexpr Elf_Half EM_AARCH64 = 183; // ARM AArch64 +constexpr Elf_Half EM_res184 = 184; // Reserved by ARM +constexpr Elf_Half EM_AVR32 = 185; // Atmel Corporation 32-bit microprocessor family +constexpr Elf_Half EM_STM8 = 186; // STMicroeletronics STM8 8-bit microcontroller +constexpr Elf_Half EM_TILE64 = 187; // Tilera TILE64 multicore architecture family +constexpr Elf_Half EM_TILEPRO = 188; // Tilera TILEPro multicore architecture family +constexpr Elf_Half EM_MICROBLAZE = 189; // Xilinx MicroBlaze 32-bit RISC soft processor core +constexpr Elf_Half EM_CUDA = 190; // NVIDIA CUDA architecture +constexpr Elf_Half EM_TILEGX = 191; // Tilera TILE-Gx multicore architecture family +constexpr Elf_Half EM_CLOUDSHIELD = 192; // CloudShield architecture family +constexpr Elf_Half EM_COREA_1ST = 193; // KIPO-KAIST Core-A 1st generation processor family +constexpr Elf_Half EM_COREA_2ND = 194; // KIPO-KAIST Core-A 2nd generation processor family +constexpr Elf_Half EM_ARC_COMPACT2 = 195; // Synopsys ARCompact V2 +constexpr Elf_Half EM_OPEN8 = 196; // Open8 8-bit RISC soft processor core +constexpr Elf_Half EM_RL78 = 197; // Renesas RL78 family +constexpr Elf_Half EM_VIDEOCORE5 = 198; // Broadcom VideoCore V processor +constexpr Elf_Half EM_78KOR = 199; // Renesas 78KOR family +constexpr Elf_Half EM_56800EX = 200; // Freescale 56800EX Digital Signal Controller (DSC) +constexpr Elf_Half EM_BA1 = 201; // Beyond BA1 CPU architecture +constexpr Elf_Half EM_BA2 = 202; // Beyond BA2 CPU architecture +constexpr Elf_Half EM_XCORE = 203; // XMOS xCORE processor family +constexpr Elf_Half EM_MCHP_PIC = 204; // Microchip 8-bit PIC(r) family +constexpr Elf_Half EM_INTEL205 = 205; // Reserved by Intel +constexpr Elf_Half EM_INTEL206 = 206; // Reserved by Intel +constexpr Elf_Half EM_INTEL207 = 207; // Reserved by Intel +constexpr Elf_Half EM_INTEL208 = 208; // Reserved by Intel +constexpr Elf_Half EM_INTEL209 = 209; // Reserved by Intel +constexpr Elf_Half EM_KM32 = 210; // KM211 KM32 32-bit processor +constexpr Elf_Half EM_KMX32 = 211; // KM211 KMX32 32-bit processor +constexpr Elf_Half EM_KMX16 = 212; // KM211 KMX16 16-bit processor +constexpr Elf_Half EM_KMX8 = 213; // KM211 KMX8 8-bit processor +constexpr Elf_Half EM_KVARC = 214; // KM211 KVARC processor +constexpr Elf_Half EM_CDP = 215; // Paneve CDP architecture family +constexpr Elf_Half EM_COGE = 216; // Cognitive Smart Memory Processor +constexpr Elf_Half EM_COOL = 217; // iCelero CoolEngine +constexpr Elf_Half EM_NORC = 218; // Nanoradio Optimized RISC +constexpr Elf_Half EM_CSR_KALIMBA = 219; // CSR Kalimba architecture family +constexpr Elf_Half EM_Z80 = 220; // Zilog Z80 +constexpr Elf_Half EM_VISIUM = 221; // Controls and Data Services VISIUMcore processor +constexpr Elf_Half EM_FT32 = 222; // FTDI Chip FT32 high performance 32-bit RISC architecture +constexpr Elf_Half EM_MOXIE = 223; // Moxie processor family +constexpr Elf_Half EM_AMDGPU = 224; // AMD GPU architecture +constexpr Elf_Half EM_RISCV = 243; // RISC-V +constexpr Elf_Half EM_LANAI = 244; // Lanai processor +constexpr Elf_Half EM_CEVA = 245; // CEVA Processor Architecture Family +constexpr Elf_Half EM_CEVA_X2 = 246; // CEVA X2 Processor Family +constexpr Elf_Half EM_BPF = 247; // Linux BPF – in-kernel virtual machine +constexpr Elf_Half EM_GRAPHCORE_IPU = 248; // Graphcore Intelligent Processing Unit +constexpr Elf_Half EM_IMG1 = 249; // Imagination Technologies +constexpr Elf_Half EM_NFP = 250; // Netronome Flow Processor (P) +constexpr Elf_Half EM_CSKY = 252; // C-SKY processor family +constexpr Elf_Half EM_ARC_COMPACT3_64 = 253; // Synopsys ARCv2.3 64-bit +constexpr Elf_Half EM_MCS6502 = 254; // MOS Technology MCS 6502 processor +constexpr Elf_Half EM_ARC_COMPACT3 = 255; // Synopsys ARCv2.3 32-bit +constexpr Elf_Half EM_KVX = 256; // Kalray VLIW core of the MPPA processor family +constexpr Elf_Half EM_65816 = 257; // WDC 65816/65C816 +constexpr Elf_Half EM_LOONGARCH = 258; // Loongson Loongarch +constexpr Elf_Half EM_KF32 = 259; // ChipON KungFu32 + +constexpr Elf_Half EM_MT = 0x2530; // Morpho Techologies MT processor +constexpr Elf_Half EM_ALPHA = 0x9026; // Alpha +constexpr Elf_Half EM_WEBASSEMBLY = 0x4157; // Web Assembly +constexpr Elf_Half EM_DLX = 0x5aa5; // OpenDLX +constexpr Elf_Half EM_XSTORMY16 = 0xad45; // Sanyo XStormy16 CPU core +constexpr Elf_Half EM_IQ2000 = 0xFEBA; // Vitesse IQ2000 +constexpr Elf_Half EM_M32C_OLD = 0xFEB; +constexpr Elf_Half EM_NIOS32 = 0xFEBB; // Altera Nios +constexpr Elf_Half EM_CYGNUS_MEP = 0xF00D; // Toshiba MeP Media Engine +constexpr Elf_Half EM_ADAPTEVA_EPIPHANY = 0x1223; // Adapteva EPIPHANY +constexpr Elf_Half EM_CYGNUS_FRV = 0x5441; // Fujitsu FR-V +constexpr Elf_Half EM_S12Z = 0x4DEF; // Freescale S12Z +// clang-format on + +// File version +constexpr unsigned char EV_NONE = 0; +constexpr unsigned char EV_CURRENT = 1; + +// Identification index +constexpr unsigned char EI_MAG0 = 0; +constexpr unsigned char EI_MAG1 = 1; +constexpr unsigned char EI_MAG2 = 2; +constexpr unsigned char EI_MAG3 = 3; +constexpr unsigned char EI_CLASS = 4; +constexpr unsigned char EI_DATA = 5; +constexpr unsigned char EI_VERSION = 6; +constexpr unsigned char EI_OSABI = 7; +constexpr unsigned char EI_ABIVERSION = 8; +constexpr unsigned char EI_PAD = 9; +constexpr unsigned char EI_NIDENT = 16; + +// Magic number +constexpr unsigned char ELFMAG0 = 0x7F; +constexpr unsigned char ELFMAG1 = 'E'; +constexpr unsigned char ELFMAG2 = 'L'; +constexpr unsigned char ELFMAG3 = 'F'; + +// File class +constexpr unsigned char ELFCLASSNONE = 0; +constexpr unsigned char ELFCLASS32 = 1; +constexpr unsigned char ELFCLASS64 = 2; + +// Encoding +constexpr unsigned char ELFDATANONE = 0; +constexpr unsigned char ELFDATA2LSB = 1; +constexpr unsigned char ELFDATA2MSB = 2; + +// clang-format off +// OS extensions +constexpr unsigned char ELFOSABI_NONE = 0; // No extensions or unspecified +constexpr unsigned char ELFOSABI_HPUX = 1; // Hewlett-Packard HP-UX +constexpr unsigned char ELFOSABI_NETBSD = 2; // NetBSD +constexpr unsigned char ELFOSABI_LINUX = 3; // Linux +constexpr unsigned char ELFOSABI_HURD = 4; // GNU Hurd +constexpr unsigned char ELFOSABI_SOLARIS = 6; // Sun Solaris +constexpr unsigned char ELFOSABI_AIX = 7; // AIX +constexpr unsigned char ELFOSABI_IRIX = 8; // IRIX +constexpr unsigned char ELFOSABI_FREEBSD = 9; // FreeBSD +constexpr unsigned char ELFOSABI_TRU64 = 10; // Compaq TRU64 UNIX +constexpr unsigned char ELFOSABI_MODESTO = 11; // Novell Modesto +constexpr unsigned char ELFOSABI_OPENBSD = 12; // Open BSD +constexpr unsigned char ELFOSABI_OPENVMS = 13; // Open VMS +constexpr unsigned char ELFOSABI_NSK = 14; // Hewlett-Packard Non-Stop Kernel +constexpr unsigned char ELFOSABI_AROS = 15; // Amiga Research OS +constexpr unsigned char ELFOSABI_FENIXOS = 16; // The FenixOS highly scalable multi-core OS +constexpr unsigned char ELFOSABI_NUXI = 17; // Nuxi CloudABI +constexpr unsigned char ELFOSABI_OPENVOS = 18; // Stratus Technologies OpenVOS +constexpr unsigned char ELFOSABI_ARM = 97; // ARM +constexpr unsigned char ELFOSABI_STANDALONE = 255; // Standalone (embedded) application + +// 64-255 Architecture-specific value range +// AMDGPU OS for HSA compatible compute kernels +constexpr unsigned char ELFOSABI_AMDGPU_HSA = 64; +// AMDGPU OS for AMD PAL compatible graphics +// shaders and compute kernels +constexpr unsigned char ELFOSABI_AMDGPU_PAL = 65; +// AMDGPU OS for Mesa3D compatible graphics +// shaders and compute kernels +constexpr unsigned char ELFOSABI_AMDGPU_MESA3D = 66; +// clang-format on + +constexpr unsigned char ELFABIVERSION_AMDGPU_HSA_V2 = 0; +constexpr unsigned char ELFABIVERSION_AMDGPU_HSA_V3 = 1; +constexpr unsigned char ELFABIVERSION_AMDGPU_HSA_V4 = 2; + +// AMDGPU specific e_flags +constexpr Elf_Word EF_AMDGPU_MACH = 0x0ff; // AMDGPU processor selection mask. +// Indicates if the XNACK target feature is +// enabled for all code contained in the ELF. +constexpr Elf_Word EF_AMDGPU_XNACK = 0x100; + +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_V2 = 0x01; +constexpr Elf_Word EF_AMDGPU_FEATURE_TRAP_HANDLER_V2 = 0x02; +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_V3 = 0x100; +constexpr Elf_Word EF_AMDGPU_FEATURE_SRAMECC_V3 = 0x200; +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_V4 = 0x300; +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_UNSUPPORTED_V4 = 0x000; +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_ANY_V4 = 0x100; +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_OFF_V4 = 0x200; +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_ON_V4 = 0x300; +constexpr Elf_Word EF_AMDGPU_FEATURE_SRAMECC_V4 = 0xc00; +constexpr Elf_Word EF_AMDGPU_FEATURE_SRAMECC_UNSUPPORTED_V4 = 0x000; +constexpr Elf_Word EF_AMDGPU_FEATURE_SRAMECC_ANY_V4 = 0x400; +constexpr Elf_Word EF_AMDGPU_FEATURE_SRAMECC_OFF_V4 = 0x800; +constexpr Elf_Word EF_AMDGPU_FEATURE_SRAMECC_ON_V4 = 0xc00; + +// AMDGPU processors +constexpr Elf_Word EF_AMDGPU_MACH_NONE = 0x000; // Unspecified processor. +constexpr Elf_Word EF_AMDGPU_MACH_R600_R600 = 0x001; +constexpr Elf_Word EF_AMDGPU_MACH_R600_R630 = 0x002; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RS880 = 0x003; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RV670 = 0x004; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RV710 = 0x005; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RV730 = 0x006; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RV770 = 0x007; +constexpr Elf_Word EF_AMDGPU_MACH_R600_CEDAR = 0x008; +constexpr Elf_Word EF_AMDGPU_MACH_R600_CYPRESS = 0x009; +constexpr Elf_Word EF_AMDGPU_MACH_R600_JUNIPER = 0x00a; +constexpr Elf_Word EF_AMDGPU_MACH_R600_REDWOOD = 0x00b; +constexpr Elf_Word EF_AMDGPU_MACH_R600_SUMO = 0x00c; +constexpr Elf_Word EF_AMDGPU_MACH_R600_BARTS = 0x00d; +constexpr Elf_Word EF_AMDGPU_MACH_R600_CAICOS = 0x00e; +constexpr Elf_Word EF_AMDGPU_MACH_R600_CAYMAN = 0x00f; +constexpr Elf_Word EF_AMDGPU_MACH_R600_TURKS = 0x010; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RESERVED_FIRST = 0x011; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RESERVED_LAST = 0x01f; +constexpr Elf_Word EF_AMDGPU_MACH_R600_FIRST = EF_AMDGPU_MACH_R600_R600; +constexpr Elf_Word EF_AMDGPU_MACH_R600_LAST = EF_AMDGPU_MACH_R600_TURKS; + +// AMDGCN-based processors. +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX600 = 0x020; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX601 = 0x021; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX700 = 0x022; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX701 = 0x023; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX702 = 0x024; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX703 = 0x025; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX704 = 0x026; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_RESERVED_0X27 = 0x027; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX801 = 0x028; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX802 = 0x029; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX803 = 0x02a; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX810 = 0x02b; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX900 = 0x02c; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX902 = 0x02d; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX904 = 0x02e; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX906 = 0x02f; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX908 = 0x030; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX909 = 0x031; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX90C = 0x032; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1010 = 0x033; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1011 = 0x034; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1012 = 0x035; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1030 = 0x036; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1031 = 0x037; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1032 = 0x038; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1033 = 0x039; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX602 = 0x03a; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX705 = 0x03b; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX805 = 0x03c; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_RESERVED_0X3D = 0x03d; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1034 = 0x03e; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX90A = 0x03f; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_RESERVED_0X40 = 0x040; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_RESERVED_0X41 = 0x041; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1013 = 0x042; +// First/last AMDGCN-based processors. +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_FIRST = EF_AMDGPU_MACH_AMDGCN_GFX600; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_LAST = EF_AMDGPU_MACH_AMDGCN_GFX1013; + +///////////////////// +// Sections constants + +// Section indexes +constexpr Elf_Word SHN_UNDEF = 0; +constexpr Elf_Word SHN_LORESERVE = 0xFF00; +constexpr Elf_Word SHN_LOPROC = 0xFF00; +constexpr Elf_Word SHN_HIPROC = 0xFF1F; +constexpr Elf_Word SHN_LOOS = 0xFF20; +constexpr Elf_Word SHN_HIOS = 0xFF3F; +constexpr Elf_Word SHN_ABS = 0xFFF1; +constexpr Elf_Word SHN_COMMON = 0xFFF2; +constexpr Elf_Word SHN_XINDEX = 0xFFFF; +constexpr Elf_Word SHN_HIRESERVE = 0xFFFF; + +// Section types +constexpr Elf_Word SHT_NULL = 0; +constexpr Elf_Word SHT_PROGBITS = 1; +constexpr Elf_Word SHT_SYMTAB = 2; +constexpr Elf_Word SHT_STRTAB = 3; +constexpr Elf_Word SHT_RELA = 4; +constexpr Elf_Word SHT_HASH = 5; +constexpr Elf_Word SHT_DYNAMIC = 6; +constexpr Elf_Word SHT_NOTE = 7; +constexpr Elf_Word SHT_NOBITS = 8; +constexpr Elf_Word SHT_REL = 9; +constexpr Elf_Word SHT_SHLIB = 10; +constexpr Elf_Word SHT_DYNSYM = 11; +constexpr Elf_Word SHT_INIT_ARRAY = 14; +constexpr Elf_Word SHT_FINI_ARRAY = 15; +constexpr Elf_Word SHT_PREINIT_ARRAY = 16; +constexpr Elf_Word SHT_GROUP = 17; +constexpr Elf_Word SHT_SYMTAB_SHNDX = 18; +constexpr Elf_Word SHT_GNU_ATTRIBUTES = 0x6ffffff5; +constexpr Elf_Word SHT_GNU_HASH = 0x6ffffff6; +constexpr Elf_Word SHT_GNU_LIBLIST = 0x6ffffff7; +constexpr Elf_Word SHT_CHECKSUM = 0x6ffffff8; +constexpr Elf_Word SHT_LOSUNW = 0x6ffffffa; +constexpr Elf_Word SHT_SUNW_move = 0x6ffffffa; +constexpr Elf_Word SHT_SUNW_COMDAT = 0x6ffffffb; +constexpr Elf_Word SHT_SUNW_syminfo = 0x6ffffffc; +constexpr Elf_Word SHT_GNU_verdef = 0x6ffffffd; +constexpr Elf_Word SHT_GNU_verneed = 0x6ffffffe; +constexpr Elf_Word SHT_GNU_versym = 0x6fffffff; +constexpr Elf_Word SHT_LOOS = 0x60000000; +constexpr Elf_Word SHT_HIOS = 0x6fffffff; +constexpr Elf_Word SHT_LOPROC = 0x70000000; +constexpr Elf_Word SHT_ARM_EXIDX = 0x70000001; +constexpr Elf_Word SHT_ARM_PREEMPTMAP = 0x70000002; +constexpr Elf_Word SHT_ARM_ATTRIBUTES = 0x70000003; +constexpr Elf_Word SHT_ARM_DEBUGOVERLAY = 0x70000004; +constexpr Elf_Word SHT_ARM_OVERLAYSECTION = 0x70000005; +constexpr Elf_Word SHT_HIPROC = 0x7FFFFFFF; +constexpr Elf_Word SHT_LOUSER = 0x80000000; +// Used by Nintendo Wii U +constexpr Elf_Word SHT_RPL_EXPORTS = 0x80000001; +constexpr Elf_Word SHT_RPL_IMPORTS = 0x80000002; +constexpr Elf_Word SHT_RPL_CRCS = 0x80000003; +constexpr Elf_Word SHT_RPL_FILEINFO = 0x80000004; +constexpr Elf_Word SHT_HIUSER = 0xFFFFFFFF; + +// Section attribute flags +constexpr Elf_Xword SHF_WRITE = 0x1; +constexpr Elf_Xword SHF_ALLOC = 0x2; +constexpr Elf_Xword SHF_EXECINSTR = 0x4; +constexpr Elf_Xword SHF_MERGE = 0x10; +constexpr Elf_Xword SHF_STRINGS = 0x20; +constexpr Elf_Xword SHF_INFO_LINK = 0x40; +constexpr Elf_Xword SHF_LINK_ORDER = 0x80; +constexpr Elf_Xword SHF_OS_NONCONFORMING = 0x100; +constexpr Elf_Xword SHF_GROUP = 0x200; +constexpr Elf_Xword SHF_TLS = 0x400; +constexpr Elf_Xword SHF_COMPRESSED = 0x800; +constexpr Elf_Xword SHF_GNU_RETAIN = 0x200000; +constexpr Elf_Xword SHF_GNU_MBIND = 0x01000000; +// flag used in Nintendo RPX/RPL to indicate section data is compressed +constexpr Elf_Xword SHF_RPX_DEFLATE = 0x08000000; +constexpr Elf_Xword SHF_MASKOS = 0x0FF00000; +constexpr Elf_Xword SHF_MIPS_GPREL = 0x10000000; +constexpr Elf_Xword SHF_ORDERED = 0x40000000; +constexpr Elf_Xword SHF_EXCLUDE = 0x80000000; +constexpr Elf_Xword SHF_MASKPROC = 0xF0000000; + +// Section group flags +constexpr Elf_Word GRP_COMDAT = 0x1; +constexpr Elf_Word GRP_MASKOS = 0x0ff00000; +constexpr Elf_Word GRP_MASKPROC = 0xf0000000; + +// Symbol binding +constexpr unsigned char STB_LOCAL = 0; +constexpr unsigned char STB_GLOBAL = 1; +constexpr unsigned char STB_WEAK = 2; +constexpr unsigned char STB_LOOS = 10; +constexpr unsigned char STB_HIOS = 12; +constexpr unsigned char STB_MULTIDEF = 13; +constexpr unsigned char STB_LOPROC = 13; +constexpr unsigned char STB_HIPROC = 15; + +// Values of note segment descriptor types for core files +constexpr Elf_Word NT_PRSTATUS = 1; // Contains copy of prstatus struct +constexpr Elf_Word NT_FPREGSET = 2; // Contains copy of fpregset struct +constexpr Elf_Word NT_PRPSINFO = 3; // Contains copy of prpsinfo struct +constexpr Elf_Word NT_TASKSTRUCT = 4; // Contains copy of task struct +constexpr Elf_Word NT_AUXV = 6; // Contains copy of Elfxx_auxv_t +constexpr Elf_Word NT_SIGINFO = 0x53494749; // Fields of siginfo_t. +constexpr Elf_Word NT_FILE = 0x46494c45; // Description of mapped files. + +// Note segments for core files on dir-style procfs systems. +constexpr Elf_Word NT_PSTATUS = 10; // Has a struct pstatus +constexpr Elf_Word NT_FPREGS = 12; // Has a struct fpregset +constexpr Elf_Word NT_PSINFO = 13; // Has a struct psinfo +constexpr Elf_Word NT_LWPSTATUS = 16; // Has a struct lwpstatus_t +constexpr Elf_Word NT_LWPSINFO = 17; // Has a struct lwpsinfo_t +constexpr Elf_Word NT_WIN32PSTATUS = 18; // Has a struct win32_pstatus + +// clang-format off + +// Note name must be "LINUX" +constexpr Elf_Word NT_PRXFPREG = 0x46e62b7f; // Contains a user_xfpregs_struct +constexpr Elf_Word NT_PPC_VMX = 0x100; // PowerPC Altivec/VMX registers +constexpr Elf_Word NT_PPC_VSX = 0x102; // PowerPC VSX registers +constexpr Elf_Word NT_PPC_TAR = 0x103; // PowerPC Target Address Register +constexpr Elf_Word NT_PPC_PPR = 0x104; // PowerPC Program Priority Register +constexpr Elf_Word NT_PPC_DSCR = 0x105; // PowerPC Data Stream Control Register +constexpr Elf_Word NT_PPC_EBB = 0x106; // PowerPC Event Based Branch Registers +constexpr Elf_Word NT_PPC_PMU = 0x107; // PowerPC Performance Monitor Registers +constexpr Elf_Word NT_PPC_TM_CGPR = 0x108; // PowerPC TM checkpointed GPR Registers +constexpr Elf_Word NT_PPC_TM_CFPR = 0x109; // PowerPC TM checkpointed FPR Registers +constexpr Elf_Word NT_PPC_TM_CVMX = 0x10a; // PowerPC TM checkpointed VMX Registers +constexpr Elf_Word NT_PPC_TM_CVSX = 0x10b; // PowerPC TM checkpointed VSX Registers +constexpr Elf_Word NT_PPC_TM_SPR = 0x10c; // PowerPC TM Special Purpose Registers +constexpr Elf_Word NT_PPC_TM_CTAR = 0x10d; // PowerPC TM checkpointed TAR +constexpr Elf_Word NT_PPC_TM_CPPR = 0x10e; // PowerPC TM checkpointed PPR +constexpr Elf_Word NT_PPC_TM_CDSCR = 0x10f; // PowerPC TM checkpointed Data SCR +constexpr Elf_Word NT_386_TLS = 0x200; // x86 TLS information +constexpr Elf_Word NT_386_IOPERM = 0x201; // x86 io permissions +constexpr Elf_Word NT_X86_XSTATE = 0x202; // x86 XSAVE extended state +constexpr Elf_Word NT_X86_CET = 0x203; // x86 CET state. +constexpr Elf_Word NT_S390_HIGH_GPRS = 0x300; // S/390 upper halves of GPRs +constexpr Elf_Word NT_S390_TIMER = 0x301; // S390 timer +constexpr Elf_Word NT_S390_TODCMP = 0x302; // S390 TOD clock comparator +constexpr Elf_Word NT_S390_TODPREG = 0x303; // S390 TOD programmable register +constexpr Elf_Word NT_S390_CTRS = 0x304; // S390 control registers +constexpr Elf_Word NT_S390_PREFIX = 0x305; // S390 prefix register +constexpr Elf_Word NT_S390_LAST_BREAK = 0x306; // S390 breaking event address +constexpr Elf_Word NT_S390_SYSTEM_CALL = 0x307; // S390 system call restart data +constexpr Elf_Word NT_S390_TDB = 0x308; // S390 transaction diagnostic block +constexpr Elf_Word NT_S390_VXRS_LOW = 0x309; // S390 vector registers 0-15 upper half +constexpr Elf_Word NT_S390_VXRS_HIGH = 0x30a; // S390 vector registers 16-31 +constexpr Elf_Word NT_S390_GS_CB = 0x30b; // s390 guarded storage registers +constexpr Elf_Word NT_S390_GS_BC = 0x30c; // s390 guarded storage broadcast control block +constexpr Elf_Word NT_ARM_VFP = 0x400; // ARM VFP registers +constexpr Elf_Word NT_ARM_TLS = 0x401; // AArch TLS registers +constexpr Elf_Word NT_ARM_HW_BREAK = 0x402; // AArch hardware breakpoint registers +constexpr Elf_Word NT_ARM_HW_WATCH = 0x403; // AArch hardware watchpoint registers +constexpr Elf_Word NT_ARM_SVE = 0x405; // AArch SVE registers. +constexpr Elf_Word NT_ARM_PAC_MASK = 0x406; // AArch pointer authentication code masks +constexpr Elf_Word NT_ARM_PACA_KEYS = 0x407; // ARM pointer authentication address keys +constexpr Elf_Word NT_ARM_PACG_KEYS = 0x408; // ARM pointer authentication generic keys +constexpr Elf_Word NT_ARM_TAGGED_ADDR_CTRL = 0x409; // AArch64 tagged address control (prctl()) +constexpr Elf_Word NT_ARM_PAC_ENABLED_KEYS = 0x40a; // AArch64 pointer authentication enabled keys (prctl()) +constexpr Elf_Word NT_ARC_V2 = 0x600; // ARC HS accumulator/extra registers. +constexpr Elf_Word NT_LARCH_CPUCFG = 0xa00; // LoongArch CPU config registers +constexpr Elf_Word NT_LARCH_CSR = 0xa01; // LoongArch Control State Registers +constexpr Elf_Word NT_LARCH_LSX = 0xa02; // LoongArch SIMD eXtension registers +constexpr Elf_Word NT_LARCH_LASX = 0xa03; // LoongArch Advanced SIMD eXtension registers +constexpr Elf_Word NT_RISCV_CSR = 0x900; // RISC-V Control and Status Registers + +// Note name must be "CORE" +constexpr Elf_Word NT_LARCH_LBT = 0xa04; // LoongArch Binary Translation registers + +/* The range 0xff000000 to 0xffffffff is set aside for notes that don't + originate from any particular operating system. */ +constexpr Elf_Word NT_GDB_TDESC = 0xff000000; // Contains copy of GDB's target description XML. +constexpr Elf_Word NT_MEMTAG = 0xff000001; // Contains a copy of the memory tags. +/* ARM-specific NT_MEMTAG types. */ +constexpr Elf_Word NT_MEMTAG_TYPE_AARCH_MTE = 0x400; // MTE memory tags for AArch64. + +constexpr Elf_Word NT_STAPSDT = 3; // Note segment for SystemTap probes. + +// Note name is "FreeBSD" +constexpr Elf_Word NT_FREEBSD_THRMISC = 7; // Thread miscellaneous info. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_PROC = 8; // Procstat proc data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_FILES = 9; // Procstat files data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_VMMAP = 10; // Procstat vmmap data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_GROUPS = 11; // Procstat groups data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_UMASK = 12; // Procstat umask data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_RLIMIT = 13; // Procstat rlimit data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_OSREL = 14; // Procstat osreldate data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_PSSTRINGS = 15; // Procstat ps_strings data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_AUXV = 16; // Procstat auxv data. +constexpr Elf_Word NT_FREEBSD_PTLWPINFO = 17; // Thread ptrace miscellaneous info. + +// Note name must start with "NetBSD-CORE" +constexpr Elf_Word NT_NETBSDCORE_PROCINFO = 1; // Has a struct procinfo +constexpr Elf_Word NT_NETBSDCORE_AUXV = 2; // Has auxv data +constexpr Elf_Word NT_NETBSDCORE_LWPSTATUS = 24; // Has LWPSTATUS data +constexpr Elf_Word NT_NETBSDCORE_FIRSTMACH = 32; // start of machdep note types + +// Note name is "OpenBSD" +constexpr Elf_Word NT_OPENBSD_PROCINFO = 10; +constexpr Elf_Word NT_OPENBSD_AUXV = 11; +constexpr Elf_Word NT_OPENBSD_REGS = 20; +constexpr Elf_Word NT_OPENBSD_FPREGS = 21; +constexpr Elf_Word NT_OPENBSD_XFPREGS = 22; +constexpr Elf_Word NT_OPENBSD_WCOOKIE = 23; + +// Note name must start with "SPU" +constexpr Elf_Word NT_SPU = 1; + +// Values of note segment descriptor types for object files +constexpr Elf_Word NT_VERSION = 1; // Contains a version string. +constexpr Elf_Word NT_ARCH = 2; // Contains an architecture string. +constexpr Elf_Word NT_GO_BUILDID = 4; // Contains GO buildid data. + +// Values for notes in non-core files using name "GNU" +constexpr Elf_Word NT_GNU_ABI_TAG = 1; +constexpr Elf_Word NT_GNU_HWCAP = 2; // Used by ld.so and kernel vDSO. +constexpr Elf_Word NT_GNU_BUILD_ID = 3; // Generated by ld --build-id. +constexpr Elf_Word NT_GNU_GOLD_VERSION = 4; // Generated by gold. +constexpr Elf_Word NT_GNU_PROPERTY_TYPE_0 = 5; // Generated by gcc. +// clang-format on + +constexpr Elf_Word NT_GNU_BUILD_ATTRIBUTE_OPEN = 0x100; +constexpr Elf_Word NT_GNU_BUILD_ATTRIBUTE_FUNC = 0x101; + +// Symbol types +constexpr Elf_Word STT_NOTYPE = 0; +constexpr Elf_Word STT_OBJECT = 1; +constexpr Elf_Word STT_FUNC = 2; +constexpr Elf_Word STT_SECTION = 3; +constexpr Elf_Word STT_FILE = 4; +constexpr Elf_Word STT_COMMON = 5; +constexpr Elf_Word STT_TLS = 6; +constexpr Elf_Word STT_LOOS = 10; +constexpr Elf_Word STT_AMDGPU_HSA_KERNEL = 10; +constexpr Elf_Word STT_HIOS = 12; +constexpr Elf_Word STT_LOPROC = 13; +constexpr Elf_Word STT_HIPROC = 15; + +// Symbol visibility +constexpr unsigned char STV_DEFAULT = 0; +constexpr unsigned char STV_INTERNAL = 1; +constexpr unsigned char STV_HIDDEN = 2; +constexpr unsigned char STV_PROTECTED = 3; + +// Undefined name +constexpr Elf_Word STN_UNDEF = 0; + +// Relocation types +// X86 +constexpr unsigned R_386_NONE = 0; +constexpr unsigned R_X86_64_NONE = 0; +constexpr unsigned R_AMDGPU_NONE = 0; +constexpr unsigned R_386_32 = 1; +constexpr unsigned R_X86_64_64 = 1; +constexpr unsigned R_AMDGPU_ABS32_LO = 1; +constexpr unsigned R_386_PC32 = 2; +constexpr unsigned R_X86_64_PC32 = 2; +constexpr unsigned R_AMDGPU_ABS32_HI = 2; +constexpr unsigned R_386_GOT32 = 3; +constexpr unsigned R_X86_64_GOT32 = 3; +constexpr unsigned R_AMDGPU_ABS64 = 3; +constexpr unsigned R_386_PLT32 = 4; +constexpr unsigned R_X86_64_PLT32 = 4; +constexpr unsigned R_AMDGPU_REL32 = 4; +constexpr unsigned R_386_COPY = 5; +constexpr unsigned R_X86_64_COPY = 5; +constexpr unsigned R_AMDGPU_REL64 = 5; +constexpr unsigned R_386_GLOB_DAT = 6; +constexpr unsigned R_X86_64_GLOB_DAT = 6; +constexpr unsigned R_AMDGPU_ABS32 = 6; +constexpr unsigned R_386_JMP_SLOT = 7; +constexpr unsigned R_X86_64_JUMP_SLOT = 7; +constexpr unsigned R_AMDGPU_GOTPCREL = 7; +constexpr unsigned R_386_RELATIVE = 8; +constexpr unsigned R_X86_64_RELATIVE = 8; +constexpr unsigned R_AMDGPU_GOTPCREL32_LO = 8; +constexpr unsigned R_386_GOTOFF = 9; +constexpr unsigned R_X86_64_GOTPCREL = 9; +constexpr unsigned R_AMDGPU_GOTPCREL32_HI = 9; +constexpr unsigned R_386_GOTPC = 10; +constexpr unsigned R_X86_64_32 = 10; +constexpr unsigned R_AMDGPU_REL32_LO = 10; +constexpr unsigned R_386_32PLT = 11; +constexpr unsigned R_X86_64_32S = 11; +constexpr unsigned R_AMDGPU_REL32_HI = 11; +constexpr unsigned R_X86_64_16 = 12; +constexpr unsigned R_X86_64_PC16 = 13; +constexpr unsigned R_AMDGPU_RELATIVE64 = 13; +constexpr unsigned R_386_TLS_TPOFF = 14; +constexpr unsigned R_X86_64_8 = 14; +constexpr unsigned R_386_TLS_IE = 15; +constexpr unsigned R_X86_64_PC8 = 15; +constexpr unsigned R_386_TLS_GOTIE = 16; +constexpr unsigned R_X86_64_DTPMOD64 = 16; +constexpr unsigned R_386_TLS_LE = 17; +constexpr unsigned R_X86_64_DTPOFF64 = 17; +constexpr unsigned R_386_TLS_GD = 18; +constexpr unsigned R_X86_64_TPOFF64 = 18; +constexpr unsigned R_386_TLS_LDM = 19; +constexpr unsigned R_X86_64_TLSGD = 19; +constexpr unsigned R_386_16 = 20; +constexpr unsigned R_X86_64_TLSLD = 20; +constexpr unsigned R_386_PC16 = 21; +constexpr unsigned R_X86_64_DTPOFF32 = 21; +constexpr unsigned R_386_8 = 22; +constexpr unsigned R_X86_64_GOTTPOFF = 22; +constexpr unsigned R_386_PC8 = 23; +constexpr unsigned R_X86_64_TPOFF32 = 23; +constexpr unsigned R_386_TLS_GD_32 = 24; +constexpr unsigned R_X86_64_PC64 = 24; +constexpr unsigned R_386_TLS_GD_PUSH = 25; +constexpr unsigned R_X86_64_GOTOFF64 = 25; +constexpr unsigned R_386_TLS_GD_CALL = 26; +constexpr unsigned R_X86_64_GOTPC32 = 26; +constexpr unsigned R_386_TLS_GD_POP = 27; +constexpr unsigned R_X86_64_GOT64 = 27; +constexpr unsigned R_386_TLS_LDM_32 = 28; +constexpr unsigned R_X86_64_GOTPCREL64 = 28; +constexpr unsigned R_386_TLS_LDM_PUSH = 29; +constexpr unsigned R_X86_64_GOTPC64 = 29; +constexpr unsigned R_386_TLS_LDM_CALL = 30; +constexpr unsigned R_X86_64_GOTPLT64 = 30; +constexpr unsigned R_386_TLS_LDM_POP = 31; +constexpr unsigned R_X86_64_PLTOFF64 = 31; +constexpr unsigned R_386_TLS_LDO_32 = 32; +constexpr unsigned R_386_TLS_IE_32 = 33; +constexpr unsigned R_386_TLS_LE_32 = 34; +constexpr unsigned R_X86_64_GOTPC32_TLSDESC = 34; +constexpr unsigned R_386_TLS_DTPMOD32 = 35; +constexpr unsigned R_X86_64_TLSDESC_CALL = 35; +constexpr unsigned R_386_TLS_DTPOFF32 = 36; +constexpr unsigned R_X86_64_TLSDESC = 36; +constexpr unsigned R_386_TLS_TPOFF32 = 37; +constexpr unsigned R_X86_64_IRELATIVE = 37; +constexpr unsigned R_386_SIZE32 = 38; +constexpr unsigned R_386_TLS_GOTDESC = 39; +constexpr unsigned R_386_TLS_DESC_CALL = 40; +constexpr unsigned R_386_TLS_DESC = 41; +constexpr unsigned R_386_IRELATIVE = 42; +constexpr unsigned R_386_GOT32X = 43; +constexpr unsigned R_X86_64_GNU_VTINHERIT = 250; +constexpr unsigned R_X86_64_GNU_VTENTRY = 251; +// AArch64 +constexpr unsigned R_AARCH64_NONE = 0; +constexpr unsigned R_AARCH64_P32_ABS32 = 1; +constexpr unsigned R_AARCH64_P32_COPY = 180; +constexpr unsigned R_AARCH64_P32_GLOB_DAT = 181; +constexpr unsigned R_AARCH64_P32_JUMP_SLOT = 182; +constexpr unsigned R_AARCH64_P32_RELATIVE = 183; +constexpr unsigned R_AARCH64_P32_TLS_DTPMOD = 184; +constexpr unsigned R_AARCH64_P32_TLS_DTPREL = 185; +constexpr unsigned R_AARCH64_P32_TLS_TPREL = 186; +constexpr unsigned R_AARCH64_P32_TLSDESC = 187; +constexpr unsigned R_AARCH64_P32_IRELATIVE = 188; +constexpr unsigned R_AARCH64_ABS64 = 257; +constexpr unsigned R_AARCH64_ABS32 = 258; +constexpr unsigned R_AARCH64_ABS16 = 259; +constexpr unsigned R_AARCH64_PREL64 = 260; +constexpr unsigned R_AARCH64_PREL32 = 261; +constexpr unsigned R_AARCH64_PREL16 = 262; +constexpr unsigned R_AARCH64_MOVW_UABS_G0 = 263; +constexpr unsigned R_AARCH64_MOVW_UABS_G0_NC = 264; +constexpr unsigned R_AARCH64_MOVW_UABS_G1 = 265; +constexpr unsigned R_AARCH64_MOVW_UABS_G1_NC = 266; +constexpr unsigned R_AARCH64_MOVW_UABS_G2 = 267; +constexpr unsigned R_AARCH64_MOVW_UABS_G2_NC = 268; +constexpr unsigned R_AARCH64_MOVW_UABS_G3 = 269; +constexpr unsigned R_AARCH64_MOVW_SABS_G0 = 270; +constexpr unsigned R_AARCH64_MOVW_SABS_G1 = 271; +constexpr unsigned R_AARCH64_MOVW_SABS_G2 = 272; +constexpr unsigned R_AARCH64_LD_PREL_LO19 = 273; +constexpr unsigned R_AARCH64_ADR_PREL_LO21 = 274; +constexpr unsigned R_AARCH64_ADR_PREL_PG_HI21 = 275; +constexpr unsigned R_AARCH64_ADR_PREL_PG_HI21_NC = 276; +constexpr unsigned R_AARCH64_ADD_ABS_LO12_NC = 277; +constexpr unsigned R_AARCH64_LDST8_ABS_LO12_NC = 278; +constexpr unsigned R_AARCH64_TSTBR14 = 279; +constexpr unsigned R_AARCH64_CONDBR19 = 280; +constexpr unsigned R_AARCH64_JUMP26 = 282; +constexpr unsigned R_AARCH64_CALL26 = 283; +constexpr unsigned R_AARCH64_LDST16_ABS_LO12_NC = 284; +constexpr unsigned R_AARCH64_LDST32_ABS_LO12_NC = 285; +constexpr unsigned R_AARCH64_LDST64_ABS_LO12_NC = 286; +constexpr unsigned R_AARCH64_MOVW_PREL_G0 = 287; +constexpr unsigned R_AARCH64_MOVW_PREL_G0_NC = 288; +constexpr unsigned R_AARCH64_MOVW_PREL_G1 = 289; +constexpr unsigned R_AARCH64_MOVW_PREL_G1_NC = 290; +constexpr unsigned R_AARCH64_MOVW_PREL_G2 = 291; +constexpr unsigned R_AARCH64_MOVW_PREL_G2_NC = 292; +constexpr unsigned R_AARCH64_MOVW_PREL_G3 = 293; +constexpr unsigned R_AARCH64_LDST128_ABS_LO12_NC = 299; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G0 = 300; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G0_NC = 301; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G1 = 302; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G1_NC = 303; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G2 = 304; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G2_NC = 305; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G3 = 306; +constexpr unsigned R_AARCH64_GOTREL64 = 307; +constexpr unsigned R_AARCH64_GOTREL32 = 308; +constexpr unsigned R_AARCH64_GOT_LD_PREL19 = 309; +constexpr unsigned R_AARCH64_LD64_GOTOFF_LO15 = 310; +constexpr unsigned R_AARCH64_ADR_GOT_PAGE = 311; +constexpr unsigned R_AARCH64_LD64_GOT_LO12_NC = 312; +constexpr unsigned R_AARCH64_LD64_GOTPAGE_LO15 = 313; +constexpr unsigned R_AARCH64_TLSGD_ADR_PREL21 = 512; +constexpr unsigned R_AARCH64_TLSGD_ADR_PAGE21 = 513; +constexpr unsigned R_AARCH64_TLSGD_ADD_LO12_NC = 514; +constexpr unsigned R_AARCH64_TLSGD_MOVW_G1 = 515; +constexpr unsigned R_AARCH64_TLSGD_MOVW_G0_NC = 516; +constexpr unsigned R_AARCH64_TLSLD_ADR_PREL21 = 517; +constexpr unsigned R_AARCH64_TLSLD_ADR_PAGE21 = 518; +constexpr unsigned R_AARCH64_TLSLD_ADD_LO12_NC = 519; +constexpr unsigned R_AARCH64_TLSLD_MOVW_G1 = 520; +constexpr unsigned R_AARCH64_TLSLD_MOVW_G0_NC = 521; +constexpr unsigned R_AARCH64_TLSLD_LD_PREL19 = 522; +constexpr unsigned R_AARCH64_TLSLD_MOVW_DTPREL_G2 = 523; +constexpr unsigned R_AARCH64_TLSLD_MOVW_DTPREL_G1 = 524; +constexpr unsigned R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC = 525; +constexpr unsigned R_AARCH64_TLSLD_MOVW_DTPREL_G0 = 526; +constexpr unsigned R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC = 527; +constexpr unsigned R_AARCH64_TLSLD_ADD_DTPREL_HI12 = 528; +constexpr unsigned R_AARCH64_TLSLD_ADD_DTPREL_LO12 = 529; +constexpr unsigned R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC = 530; +constexpr unsigned R_AARCH64_TLSLD_LDST8_DTPREL_LO12 = 531; +constexpr unsigned R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC = 532; +constexpr unsigned R_AARCH64_TLSLD_LDST16_DTPREL_LO12 = 533; +constexpr unsigned R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC = 534; +constexpr unsigned R_AARCH64_TLSLD_LDST32_DTPREL_LO12 = 535; +constexpr unsigned R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC = 536; +constexpr unsigned R_AARCH64_TLSLD_LDST64_DTPREL_LO12 = 537; +constexpr unsigned R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC = 538; +constexpr unsigned R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 = 539; +constexpr unsigned R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC = 540; +constexpr unsigned R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 = 541; +constexpr unsigned R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC = 542; +constexpr unsigned R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 = 543; +constexpr unsigned R_AARCH64_TLSLE_MOVW_TPREL_G2 = 544; +constexpr unsigned R_AARCH64_TLSLE_MOVW_TPREL_G1 = 545; +constexpr unsigned R_AARCH64_TLSLE_MOVW_TPREL_G1_NC = 546; +constexpr unsigned R_AARCH64_TLSLE_MOVW_TPREL_G0 = 547; +constexpr unsigned R_AARCH64_TLSLE_MOVW_TPREL_G0_NC = 548; +constexpr unsigned R_AARCH64_TLSLE_ADD_TPREL_HI12 = 549; +constexpr unsigned R_AARCH64_TLSLE_ADD_TPREL_LO12 = 550; +constexpr unsigned R_AARCH64_TLSLE_ADD_TPREL_LO12_NC = 551; +constexpr unsigned R_AARCH64_TLSLE_LDST8_TPREL_LO12 = 552; +constexpr unsigned R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC = 553; +constexpr unsigned R_AARCH64_TLSLE_LDST16_TPREL_LO12 = 554; +constexpr unsigned R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC = 555; +constexpr unsigned R_AARCH64_TLSLE_LDST32_TPREL_LO12 = 556; +constexpr unsigned R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC = 557; +constexpr unsigned R_AARCH64_TLSLE_LDST64_TPREL_LO12 = 558; +constexpr unsigned R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC = 559; +constexpr unsigned R_AARCH64_TLSDESC_LD_PREL19 = 560; +constexpr unsigned R_AARCH64_TLSDESC_ADR_PREL21 = 561; +constexpr unsigned R_AARCH64_TLSDESC_ADR_PAGE21 = 562; +constexpr unsigned R_AARCH64_TLSDESC_LD64_LO12 = 563; +constexpr unsigned R_AARCH64_TLSDESC_ADD_LO12 = 564; +constexpr unsigned R_AARCH64_TLSDESC_OFF_G1 = 565; +constexpr unsigned R_AARCH64_TLSDESC_OFF_G0_NC = 566; +constexpr unsigned R_AARCH64_TLSDESC_LDR = 567; +constexpr unsigned R_AARCH64_TLSDESC_ADD = 568; +constexpr unsigned R_AARCH64_TLSDESC_CALL = 569; +constexpr unsigned R_AARCH64_TLSLE_LDST128_TPREL_LO12 = 570; +constexpr unsigned R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC = 571; +constexpr unsigned R_AARCH64_TLSLD_LDST128_DTPREL_LO12 = 572; +constexpr unsigned R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC = 573; +constexpr unsigned R_AARCH64_COPY = 1024; +constexpr unsigned R_AARCH64_GLOB_DAT = 1025; +constexpr unsigned R_AARCH64_JUMP_SLOT = 1026; +constexpr unsigned R_AARCH64_RELATIVE = 1027; +constexpr unsigned R_AARCH64_TLS_DTPMOD = 1028; +constexpr unsigned R_AARCH64_TLS_DTPMOD64 = 1028; +constexpr unsigned R_AARCH64_TLS_DTPREL = 1029; +constexpr unsigned R_AARCH64_TLS_DTPREL64 = 1029; +constexpr unsigned R_AARCH64_TLS_TPREL = 1030; +constexpr unsigned R_AARCH64_TLS_TPREL64 = 1030; +constexpr unsigned R_AARCH64_TLSDESC = 1031; + +// Segment types +constexpr Elf_Word PT_NULL = 0; +constexpr Elf_Word PT_LOAD = 1; +constexpr Elf_Word PT_DYNAMIC = 2; +constexpr Elf_Word PT_INTERP = 3; +constexpr Elf_Word PT_NOTE = 4; +constexpr Elf_Word PT_SHLIB = 5; +constexpr Elf_Word PT_PHDR = 6; +constexpr Elf_Word PT_TLS = 7; +constexpr Elf_Word PT_LOOS = 0X60000000; +constexpr Elf_Word PT_GNU_EH_FRAME = 0X6474E550; // Frame unwind information +constexpr Elf_Word PT_GNU_STACK = 0X6474E551; // Stack flags +constexpr Elf_Word PT_GNU_RELRO = 0X6474E552; // Read only after relocs +constexpr Elf_Word PT_GNU_PROPERTY = 0X6474E553; // GNU property +constexpr Elf_Word PT_GNU_MBIND_LO = 0X6474E555; // Mbind segments start +constexpr Elf_Word PT_GNU_MBIND_HI = 0X6474F554; // Mbind segments finish +constexpr Elf_Word PT_PAX_FLAGS = 0X65041580; +constexpr Elf_Word PT_OPENBSD_RANDOMIZE = 0X65A3DBE6; +constexpr Elf_Word PT_OPENBSD_WXNEEDED = 0X65A3DBE7; +constexpr Elf_Word PT_OPENBSD_BOOTDATA = 0X65A41BE6; +constexpr Elf_Word PT_SUNWBSS = 0X6FFFFFFA; +constexpr Elf_Word PT_SUNWSTACK = 0X6FFFFFFB; +constexpr Elf_Word PT_HIOS = 0X6FFFFFFF; +constexpr Elf_Word PT_LOPROC = 0X70000000; +constexpr Elf_Word PT_HIPROC = 0X7FFFFFFF; + +// Segment flags +constexpr Elf_Word PF_X = 1; // Execute +constexpr Elf_Word PF_W = 2; // Write +constexpr Elf_Word PF_R = 4; // Read +constexpr Elf_Word PF_MASKOS = 0x0ff00000; // Unspecified +constexpr Elf_Word PF_MASKPROC = 0xf0000000; // Unspecified + +// Dynamic Array Tags +constexpr Elf_Word DT_NULL = 0; +constexpr Elf_Word DT_NEEDED = 1; +constexpr Elf_Word DT_PLTRELSZ = 2; +constexpr Elf_Word DT_PLTGOT = 3; +constexpr Elf_Word DT_HASH = 4; +constexpr Elf_Word DT_STRTAB = 5; +constexpr Elf_Word DT_SYMTAB = 6; +constexpr Elf_Word DT_RELA = 7; +constexpr Elf_Word DT_RELASZ = 8; +constexpr Elf_Word DT_RELAENT = 9; +constexpr Elf_Word DT_STRSZ = 10; +constexpr Elf_Word DT_SYMENT = 11; +constexpr Elf_Word DT_INIT = 12; +constexpr Elf_Word DT_FINI = 13; +constexpr Elf_Word DT_SONAME = 14; +constexpr Elf_Word DT_RPATH = 15; +constexpr Elf_Word DT_SYMBOLIC = 16; +constexpr Elf_Word DT_REL = 17; +constexpr Elf_Word DT_RELSZ = 18; +constexpr Elf_Word DT_RELENT = 19; +constexpr Elf_Word DT_PLTREL = 20; +constexpr Elf_Word DT_DEBUG = 21; +constexpr Elf_Word DT_TEXTREL = 22; +constexpr Elf_Word DT_JMPREL = 23; +constexpr Elf_Word DT_BIND_NOW = 24; +constexpr Elf_Word DT_INIT_ARRAY = 25; +constexpr Elf_Word DT_FINI_ARRAY = 26; +constexpr Elf_Word DT_INIT_ARRAYSZ = 27; +constexpr Elf_Word DT_FINI_ARRAYSZ = 28; +constexpr Elf_Word DT_RUNPATH = 29; +constexpr Elf_Word DT_FLAGS = 30; +constexpr Elf_Word DT_ENCODING = 32; +constexpr Elf_Word DT_PREINIT_ARRAY = 32; +constexpr Elf_Word DT_PREINIT_ARRAYSZ = 33; +constexpr Elf_Word DT_MAXPOSTAGS = 34; +constexpr Elf_Word DT_LOOS = 0x6000000D; +constexpr Elf_Word DT_HIOS = 0x6ffff000; +constexpr Elf_Word DT_GNU_HASH = 0x6ffffef5; +constexpr Elf_Word DT_TLSDESC_PLT = 0x6ffffef6; +constexpr Elf_Word DT_TLSDESC_GOT = 0x6ffffef7; +constexpr Elf_Word DT_GNU_CONFLICT = 0x6ffffef8; +constexpr Elf_Word DT_GNU_LIBLIST = 0x6ffffef9; +constexpr Elf_Word DT_CONFIG = 0x6ffffefa; +constexpr Elf_Word DT_DEPAUDIT = 0x6ffffefb; +constexpr Elf_Word DT_AUDIT = 0x6ffffefc; +constexpr Elf_Word DT_PLTPAD = 0x6ffffefd; +constexpr Elf_Word DT_MOVETAB = 0x6ffffefe; +constexpr Elf_Word DT_SYMINFO = 0x6ffffeff; +constexpr Elf_Word DT_ADDRRNGHI = 0x6ffffeff; +constexpr Elf_Word DT_VERSYM = 0x6ffffff0; +constexpr Elf_Word DT_RELACOUNT = 0x6ffffff9; +constexpr Elf_Word DT_RELCOUNT = 0x6ffffffa; +constexpr Elf_Word DT_FLAGS_1 = 0x6ffffffb; +constexpr Elf_Word DT_VERDEF = 0x6ffffffc; +constexpr Elf_Word DT_VERDEFNUM = 0x6ffffffd; +constexpr Elf_Word DT_VERNEED = 0x6ffffffe; +constexpr Elf_Word DT_VERNEEDNUM = 0x6fffffff; +constexpr Elf_Word DT_LOPROC = 0x70000000; +constexpr Elf_Word DT_HIPROC = 0x7FFFFFFF; + +// DT_FLAGS values +constexpr Elf_Word DF_ORIGIN = 0x1; +constexpr Elf_Word DF_SYMBOLIC = 0x2; +constexpr Elf_Word DF_TEXTREL = 0x4; +constexpr Elf_Word DF_BIND_NOW = 0x8; +constexpr Elf_Word DF_STATIC_TLS = 0x10; + +// Legal values for d_tag (dynamic entry type). +constexpr Elf_Word AT_NULL = 0; // End of vector +constexpr Elf_Word AT_IGNORE = 1; // Entry should be ignored +constexpr Elf_Word AT_EXECFD = 2; // File descriptor of program +constexpr Elf_Word AT_PHDR = 3; // Program headers for program +constexpr Elf_Word AT_PHENT = 4; // Size of program header entry +constexpr Elf_Word AT_PHNUM = 5; // Number of program headers +constexpr Elf_Word AT_PAGESZ = 6; // System page size +constexpr Elf_Word AT_BASE = 7; // Base address of interpreter +constexpr Elf_Word AT_FLAGS = 8; // Flags +constexpr Elf_Word AT_ENTRY = 9; // Entry point of program +constexpr Elf_Word AT_NOTELF = 10; // Program is not ELF +constexpr Elf_Word AT_UID = 11; // Real uid +constexpr Elf_Word AT_EUID = 12; // Effective uid +constexpr Elf_Word AT_GID = 13; // Real gid +constexpr Elf_Word AT_EGID = 14; // Effective gid +constexpr Elf_Word AT_CLKTCK = 17; // Frequency of times() +constexpr Elf_Word AT_PLATFORM = 15; // String identifying platform. +constexpr Elf_Word AT_HWCAP = 16; // Hints about processor capabilities. +constexpr Elf_Word AT_FPUCW = 18; // Used FPU control word. +constexpr Elf_Word AT_DCACHEBSIZE = 19; // Data cache block size. +constexpr Elf_Word AT_ICACHEBSIZE = 20; // Instruction cache block size. +constexpr Elf_Word AT_UCACHEBSIZE = 21; // Unified cache block size. +constexpr Elf_Word AT_IGNOREPPC = 22; // Entry should be ignored. +constexpr Elf_Word AT_SECURE = 23; // Boolean, was exec setuid-like? +constexpr Elf_Word AT_BASE_PLATFORM = 24; // String identifying real platforms. +constexpr Elf_Word AT_RANDOM = 25; // Address of 16 random bytes. +constexpr Elf_Word AT_HWCAP2 = 26; // More hints about processor capabilities. +constexpr Elf_Word AT_EXECFN = 31; // Filename of executable. +constexpr Elf_Word AT_SYSINFO = 32; // EP to the system call in the vDSO. +constexpr Elf_Word AT_SYSINFO_EHDR = 33; // Start of the ELF header of the vDSO. +constexpr Elf_Word AT_L1I_CACHESHAPE = 34; +constexpr Elf_Word AT_L1D_CACHESHAPE = 35; +constexpr Elf_Word AT_L2_CACHESHAPE = 36; +constexpr Elf_Word AT_L3_CACHESHAPE = 37; +constexpr Elf_Word AT_L1I_CACHESIZE = 40; +constexpr Elf_Word AT_L1I_CACHEGEOMETRY = 41; +constexpr Elf_Word AT_L1D_CACHESIZE = 42; +constexpr Elf_Word AT_L1D_CACHEGEOMETRY = 43; +constexpr Elf_Word AT_L2_CACHESIZE = 44; +constexpr Elf_Word AT_L2_CACHEGEOMETRY = 45; +constexpr Elf_Word AT_L3_CACHESIZE = 46; + +// ELF file header +struct Elf32_Ehdr +{ + unsigned char e_ident[EI_NIDENT]; + Elf_Half e_type; + Elf_Half e_machine; + Elf_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf_Word e_flags; + Elf_Half e_ehsize; + Elf_Half e_phentsize; + Elf_Half e_phnum; + Elf_Half e_shentsize; + Elf_Half e_shnum; + Elf_Half e_shstrndx; +}; + +struct Elf64_Ehdr +{ + unsigned char e_ident[EI_NIDENT]; + Elf_Half e_type; + Elf_Half e_machine; + Elf_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf_Word e_flags; + Elf_Half e_ehsize; + Elf_Half e_phentsize; + Elf_Half e_phnum; + Elf_Half e_shentsize; + Elf_Half e_shnum; + Elf_Half e_shstrndx; +}; + +// Section header +struct Elf32_Shdr +{ + Elf_Word sh_name; + Elf_Word sh_type; + Elf_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf_Word sh_size; + Elf_Word sh_link; + Elf_Word sh_info; + Elf_Word sh_addralign; + Elf_Word sh_entsize; +}; + +struct Elf64_Shdr +{ + Elf_Word sh_name; + Elf_Word sh_type; + Elf_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf_Xword sh_size; + Elf_Word sh_link; + Elf_Word sh_info; + Elf_Xword sh_addralign; + Elf_Xword sh_entsize; +}; + +// Segment header +struct Elf32_Phdr +{ + Elf_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf_Word p_filesz; + Elf_Word p_memsz; + Elf_Word p_flags; + Elf_Word p_align; +}; + +struct Elf64_Phdr +{ + Elf_Word p_type; + Elf_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf_Xword p_filesz; + Elf_Xword p_memsz; + Elf_Xword p_align; +}; + +// Symbol table entry +struct Elf32_Sym +{ + Elf_Word st_name; + Elf32_Addr st_value; + Elf_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf_Half st_shndx; +}; + +struct Elf64_Sym +{ + Elf_Word st_name; + unsigned char st_info; + unsigned char st_other; + Elf_Half st_shndx; + Elf64_Addr st_value; + Elf_Xword st_size; +}; + +#define ELF_ST_BIND( i ) ( ( i ) >> 4 ) +#define ELF_ST_TYPE( i ) ( (i)&0xf ) +#define ELF_ST_INFO( b, t ) ( ( ( b ) << 4 ) + ( (t)&0xf ) ) + +#define ELF_ST_VISIBILITY( o ) ( (o)&0x3 ) + +// Relocation entries +struct Elf32_Rel +{ + Elf32_Addr r_offset; + Elf_Word r_info; +}; + +struct Elf32_Rela +{ + Elf32_Addr r_offset; + Elf_Word r_info; + Elf_Sword r_addend; +}; + +struct Elf64_Rel +{ + Elf64_Addr r_offset; + Elf_Xword r_info; +}; + +struct Elf64_Rela +{ + Elf64_Addr r_offset; + Elf_Xword r_info; + Elf_Sxword r_addend; +}; + +#define ELF32_R_SYM( i ) ( ( i ) >> 8 ) +#define ELF32_R_TYPE( i ) ( (unsigned char)( i ) ) +#define ELF32_R_INFO( s, t ) ( ( ( s ) << 8 ) + (unsigned char)( t ) ) + +#define ELF64_R_SYM( i ) ( ( i ) >> 32 ) +#define ELF64_R_TYPE( i ) ( (i)&0xffffffffL ) +#define ELF64_R_INFO( s, t ) \ + ( ( ( (int64_t)( s ) ) << 32 ) + ( (t)&0xffffffffL ) ) + +// Dynamic structure +struct Elf32_Dyn +{ + Elf_Sword d_tag; + union { + Elf_Word d_val; + Elf32_Addr d_ptr; + } d_un; +}; + +struct Elf64_Dyn +{ + Elf_Sxword d_tag; + union { + Elf_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +}; + +struct Elfxx_Verneed +{ + Elf_Half vn_version; + Elf_Half vn_cnt; + Elf_Word vn_file; + Elf_Word vn_aux; + Elf_Word vn_next; +}; + +struct Elfxx_Vernaux +{ + Elf_Word vna_hash; + Elf_Half vna_flags; + Elf_Half vna_other; + Elf_Word vna_name; + Elf_Word vna_next; +}; + +// ELF auxiliary vectors, they are usually run-time information +// being passed to program when the kernel is loading it. +// This is now required, +// because in order to initialize the stack cookie +// to protect against buffer overflows, +// most of libc ask us to have a valid pointer for the AT_RANDOM entry. +// glibc for example crashes if you don't. +// https://sourceware.org/git/?p=glibc.git;a=blob;f=csu/libc-start.c;h=543560f36c33b07a1fbe1b7e4578374fe8007b1f;hb=HEAD#l308 +// This is also useful to be able to reconstruct at run-time +// the ELF headers, if ELF headers were erased after loading. +// Although this library is targeted to be parsing files only, +// I assume auxiliary vectors could be also used to get +// more information about the ELF binary at run-time in future. +// The main purpose is also for ELF injectors. +struct Elf32_auxv +{ + uint32_t a_type; // Entry type + + union { + uint32_t a_val; // Integer value, usually a pointer + } a_un; +}; + +struct Elf64_auxv +{ + uint64_t a_type; // Entry type + + union { + uint64_t a_val; // Integer value, usually a pointer + } a_un; +}; + +struct Elf32_Chdr +{ + Elf32_Word ch_type; // The compression algorithm used + Elf32_Word ch_size; //The size, in bytes, of the uncompressed section data + Elf32_Word + ch_addralign; // The address alignment of the uncompressed section data +}; + +struct Elf64_Chdr +{ + Elf64_Word ch_type; //The compression algorithm used + Elf64_Word ch_reserved; // Reserved + Elf_Xword ch_size; //The size, in bytes, of the uncompressed section data + Elf_Xword + ch_addralign; //The address alignment of the uncompressed section data +}; + +#ifdef __cplusplus +} // namespace ELFIO +#endif + +#endif // ELFTYPES_H diff --git a/include/elfio/elfio.hpp b/include/elfio/elfio.hpp new file mode 100644 index 0000000..fc5995f --- /dev/null +++ b/include/elfio/elfio.hpp @@ -0,0 +1,1096 @@ +/* +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_HPP +#define ELFIO_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define ELFIO_HEADER_ACCESS_GET( TYPE, FNAME ) \ + TYPE get_##FNAME() const { return header ? ( header->get_##FNAME() ) : 0; } + +#define ELFIO_HEADER_ACCESS_GET_SET( TYPE, FNAME ) \ + TYPE get_##FNAME() const \ + { \ + return header ? ( header->get_##FNAME() ) : 0; \ + } \ + void set_##FNAME( TYPE val ) \ + { \ + if ( header ) { \ + header->set_##FNAME( val ); \ + } \ + } + +namespace ELFIO { + +//------------------------------------------------------------------------------ +class elfio +{ + public: + //------------------------------------------------------------------------------ + elfio() noexcept : sections( this ), segments( this ) + { + create( ELFCLASS32, ELFDATA2LSB ); + } + + explicit elfio( compression_interface* compression ) noexcept + : sections( this ), segments( this ), + compression( std::shared_ptr( compression ) ) + { + elfio(); + } + + elfio( elfio&& other ) noexcept + : sections( this ), segments( this ), + current_file_pos( other.current_file_pos ) + { + header = std::move( other.header ); + sections_ = std::move( other.sections_ ); + segments_ = std::move( other.segments_ ); + convertor = std::move( other.convertor ); + addr_translator = std::move( other.addr_translator ); + compression = std::move( other.compression ); + + other.header = nullptr; + other.sections_.clear(); + other.segments_.clear(); + other.compression = nullptr; + } + + elfio& operator=( elfio&& other ) noexcept + { + if ( this != &other ) { + header = std::move( other.header ); + sections_ = std::move( other.sections_ ); + segments_ = std::move( other.segments_ ); + convertor = std::move( other.convertor ); + addr_translator = std::move( other.addr_translator ); + current_file_pos = other.current_file_pos; + compression = std::move( other.compression ); + + other.current_file_pos = 0; + other.header = nullptr; + other.compression = nullptr; + other.sections_.clear(); + other.segments_.clear(); + } + return *this; + } + + //------------------------------------------------------------------------------ + // clang-format off + elfio( const elfio& ) = delete; + elfio& operator=( const elfio& ) = delete; + ~elfio() = default; + // clang-format on + + //------------------------------------------------------------------------------ + void create( unsigned char file_class, unsigned char encoding ) + { + sections_.clear(); + segments_.clear(); + convertor.setup( encoding ); + header = create_header( file_class, encoding ); + create_mandatory_sections(); + } + + void set_address_translation( std::vector& addr_trans ) + { + addr_translator.set_address_translation( addr_trans ); + } + + //------------------------------------------------------------------------------ + bool load( const std::string& file_name, bool is_lazy = false ) + { + pstream = std::make_unique(); + pstream->open( file_name.c_str(), std::ios::in | std::ios::binary ); + if ( pstream == nullptr || !*pstream ) { + return false; + } + + bool ret = load( *pstream, is_lazy ); + + if ( !is_lazy ) { + pstream.reset(); + } + + return ret; + } + + //------------------------------------------------------------------------------ + bool load( std::istream& stream, bool is_lazy = false ) + { + sections_.clear(); + segments_.clear(); + + std::array e_ident = { 0 }; + // Read ELF file signature + stream.seekg( addr_translator[0] ); + stream.read( e_ident.data(), sizeof( e_ident ) ); + + // Is it ELF file? + if ( stream.gcount() != sizeof( e_ident ) || + e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || + e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3 ) { + return false; + } + + if ( ( e_ident[EI_CLASS] != ELFCLASS64 ) && + ( e_ident[EI_CLASS] != ELFCLASS32 ) ) { + return false; + } + + if ( ( e_ident[EI_DATA] != ELFDATA2LSB ) && + ( e_ident[EI_DATA] != ELFDATA2MSB ) ) { + return false; + } + + convertor.setup( e_ident[EI_DATA] ); + header = create_header( e_ident[EI_CLASS], e_ident[EI_DATA] ); + if ( nullptr == header ) { + return false; + } + if ( !header->load( stream ) ) { + return false; + } + + load_sections( stream, is_lazy ); + bool is_still_good = load_segments( stream, is_lazy ); + return is_still_good; + } + + //------------------------------------------------------------------------------ + bool save( const std::string& file_name ) + { + std::ofstream stream; + stream.open( file_name.c_str(), std::ios::out | std::ios::binary ); + if ( !stream ) { + return false; + } + + return save( stream ); + } + + //------------------------------------------------------------------------------ + bool save( std::ostream& stream ) + { + if ( !stream || header == nullptr ) { + return false; + } + + // Define layout specific header fields + // The position of the segment table is fixed after the header. + // The position of the section table is variable and needs to be fixed + // before saving. + header->set_segments_num( segments.size() ); + header->set_segments_offset( + segments.size() > 0 ? header->get_header_size() : 0 ); + header->set_sections_num( sections.size() ); + header->set_sections_offset( 0 ); + + // Layout the first section right after the segment table + current_file_pos = + header->get_header_size() + + header->get_segment_entry_size() * + static_cast( header->get_segments_num() ); + + calc_segment_alignment(); + + bool is_still_good = layout_segments_and_their_sections(); + is_still_good = is_still_good && layout_sections_without_segments(); + is_still_good = is_still_good && layout_section_table(); + + is_still_good = is_still_good && save_header( stream ); + is_still_good = is_still_good && save_sections( stream ); + is_still_good = is_still_good && save_segments( stream ); + + return is_still_good; + } + + //------------------------------------------------------------------------------ + // ELF header access functions + ELFIO_HEADER_ACCESS_GET( unsigned char, class ); + ELFIO_HEADER_ACCESS_GET( unsigned char, elf_version ); + ELFIO_HEADER_ACCESS_GET( unsigned char, encoding ); + ELFIO_HEADER_ACCESS_GET( Elf_Word, version ); + ELFIO_HEADER_ACCESS_GET( Elf_Half, header_size ); + ELFIO_HEADER_ACCESS_GET( Elf_Half, section_entry_size ); + ELFIO_HEADER_ACCESS_GET( Elf_Half, segment_entry_size ); + + ELFIO_HEADER_ACCESS_GET_SET( unsigned char, os_abi ); + ELFIO_HEADER_ACCESS_GET_SET( unsigned char, abi_version ); + ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, type ); + ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, machine ); + ELFIO_HEADER_ACCESS_GET_SET( Elf_Word, flags ); + ELFIO_HEADER_ACCESS_GET_SET( Elf64_Addr, entry ); + ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, sections_offset ); + ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, segments_offset ); + ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, section_name_str_index ); + + //------------------------------------------------------------------------------ + const endianess_convertor& get_convertor() const { return convertor; } + + //------------------------------------------------------------------------------ + Elf_Xword get_default_entry_size( Elf_Word section_type ) const + { + switch ( section_type ) { + case SHT_RELA: + if ( header->get_class() == ELFCLASS64 ) { + return sizeof( Elf64_Rela ); + } + else { + return sizeof( Elf32_Rela ); + } + case SHT_REL: + if ( header->get_class() == ELFCLASS64 ) { + return sizeof( Elf64_Rel ); + } + else { + return sizeof( Elf32_Rel ); + } + case SHT_SYMTAB: + if ( header->get_class() == ELFCLASS64 ) { + return sizeof( Elf64_Sym ); + } + else { + return sizeof( Elf32_Sym ); + } + case SHT_DYNAMIC: + if ( header->get_class() == ELFCLASS64 ) { + return sizeof( Elf64_Dyn ); + } + else { + return sizeof( Elf32_Dyn ); + } + default: + return 0; + } + } + + //------------------------------------------------------------------------------ + //! returns an empty string if no problems are detected, + //! or a string containing an error message if problems are found, + //! with one error per line. + std::string validate() const + { + // clang-format off + + std::string errors; + // Check for overlapping sections in the file + // This is explicitly forbidden by ELF specification + for ( int i = 0; i < sections.size(); ++i) { + for ( int j = i+1; j < sections.size(); ++j ) { + const section* a = sections[i]; + const section* b = sections[j]; + if ( ( ( a->get_type() & SHT_NOBITS) == 0 ) + && ( ( b->get_type() & SHT_NOBITS) == 0 ) + && ( a->get_size() > 0 ) + && ( b->get_size() > 0 ) + && ( a->get_offset() > 0 ) + && ( b->get_offset() > 0 ) + && ( is_offset_in_section( a->get_offset(), b ) + || is_offset_in_section( a->get_offset()+a->get_size()-1, b ) + || is_offset_in_section( b->get_offset(), a ) + || is_offset_in_section( b->get_offset()+b->get_size()-1, a ) ) ) { + errors += "Sections " + a->get_name() + " and " + b->get_name() + " overlap in file\n"; + } + } + } + // clang-format on + + // Check for conflicting section / program header tables, where + // the same offset has different vaddresses in section table and + // program header table. + // This doesn't seem to be explicitly forbidden by ELF specification, + // but: + // - it doesn't make any sense + // - ELFIO relies on this being consistent when writing ELF files, + // since offsets are re-calculated from vaddress + for ( int h = 0; h < segments.size(); ++h ) { + const segment* seg = segments[h]; + const section* sec = + find_prog_section_for_offset( seg->get_offset() ); + if ( seg->get_type() == PT_LOAD && seg->get_file_size() > 0 && + sec != nullptr ) { + Elf64_Addr sec_addr = + get_virtual_addr( seg->get_offset(), sec ); + if ( sec_addr != seg->get_virtual_address() ) { + errors += "Virtual address of segment " + + std::to_string( h ) + " (" + + to_hex_string( seg->get_virtual_address() ) + + ")" + " conflicts with address of section " + + sec->get_name() + " (" + + to_hex_string( sec_addr ) + ")" + " at offset " + + to_hex_string( seg->get_offset() ) + "\n"; + } + } + } + + // more checks to be added here... + + return errors; + } + + private: + //------------------------------------------------------------------------------ + static bool is_offset_in_section( Elf64_Off offset, const section* sec ) + { + return ( offset >= sec->get_offset() ) && + ( offset < ( sec->get_offset() + sec->get_size() ) ); + } + + //------------------------------------------------------------------------------ + static Elf64_Addr get_virtual_addr( Elf64_Off offset, const section* sec ) + { + return sec->get_address() + offset - sec->get_offset(); + } + + //------------------------------------------------------------------------------ + const section* find_prog_section_for_offset( Elf64_Off offset ) const + { + for ( const auto& sec : sections ) { + if ( sec->get_type() == SHT_PROGBITS && + is_offset_in_section( offset, sec.get() ) ) { + return sec.get(); + } + } + return nullptr; + } + + //------------------------------------------------------------------------------ + std::unique_ptr create_header( unsigned char file_class, + unsigned char encoding ) + { + std::unique_ptr new_header; + + if ( file_class == ELFCLASS64 ) { + new_header = std::unique_ptr( + new ( std::nothrow ) elf_header_impl( + &convertor, encoding, &addr_translator ) ); + } + else if ( file_class == ELFCLASS32 ) { + new_header = std::unique_ptr( + new ( std::nothrow ) elf_header_impl( + &convertor, encoding, &addr_translator ) ); + } + else { + return nullptr; + } + + return new_header; + } + + //------------------------------------------------------------------------------ + section* create_section() + { + if ( auto file_class = get_class(); file_class == ELFCLASS64 ) { + sections_.emplace_back( + new ( std::nothrow ) section_impl( + &convertor, &addr_translator, compression ) ); + } + else if ( file_class == ELFCLASS32 ) { + sections_.emplace_back( + new ( std::nothrow ) section_impl( + &convertor, &addr_translator, compression ) ); + } + else { + sections_.pop_back(); + return nullptr; + } + + section* new_section = sections_.back().get(); + new_section->set_index( static_cast( sections_.size() - 1 ) ); + + return new_section; + } + + //------------------------------------------------------------------------------ + segment* create_segment() + { + if ( auto file_class = header->get_class(); file_class == ELFCLASS64 ) { + segments_.emplace_back( + new ( std::nothrow ) + segment_impl( &convertor, &addr_translator ) ); + } + else if ( file_class == ELFCLASS32 ) { + segments_.emplace_back( + new ( std::nothrow ) + segment_impl( &convertor, &addr_translator ) ); + } + else { + segments_.pop_back(); + return nullptr; + } + + segment* new_segment = segments_.back().get(); + new_segment->set_index( static_cast( segments_.size() - 1 ) ); + + return new_segment; + } + + //------------------------------------------------------------------------------ + void create_mandatory_sections() + { + // Create null section without calling to 'add_section' as no string + // section containing section names exists yet + section* sec0 = create_section(); + sec0->set_index( 0 ); + sec0->set_name( "" ); + sec0->set_name_string_offset( 0 ); + + set_section_name_str_index( 1 ); + section* shstrtab = sections.add( ".shstrtab" ); + shstrtab->set_type( SHT_STRTAB ); + shstrtab->set_addr_align( 1 ); + } + + //------------------------------------------------------------------------------ + bool load_sections( std::istream& stream, bool is_lazy ) + { + unsigned char file_class = header->get_class(); + Elf_Half entry_size = header->get_section_entry_size(); + Elf_Half num = header->get_sections_num(); + Elf64_Off offset = header->get_sections_offset(); + + if ( ( num != 0 && file_class == ELFCLASS64 && + entry_size < sizeof( Elf64_Shdr ) ) || + ( num != 0 && file_class == ELFCLASS32 && + entry_size < sizeof( Elf32_Shdr ) ) ) { + return false; + } + + for ( Elf_Half i = 0; i < num; ++i ) { + section* sec = create_section(); + sec->load( stream, + static_cast( offset ) + + static_cast( i ) * entry_size, + is_lazy ); + // To mark that the section is not permitted to reassign address + // during layout calculation + sec->set_address( sec->get_address() ); + } + + if ( Elf_Half shstrndx = get_section_name_str_index(); + SHN_UNDEF != shstrndx ) { + string_section_accessor str_reader( sections[shstrndx] ); + for ( Elf_Half i = 0; i < num; ++i ) { + Elf_Word section_offset = sections[i]->get_name_string_offset(); + const char* p = str_reader.get_string( section_offset ); + if ( p != nullptr ) { + sections[i]->set_name( p ); + } + } + } + + return true; + } + + //------------------------------------------------------------------------------ + //! Checks whether the addresses of the section entirely fall within the given segment. + //! It doesn't matter if the addresses are memory addresses, or file offsets, + //! they just need to be in the same address space + static bool is_sect_in_seg( Elf64_Off sect_begin, + Elf_Xword sect_size, + Elf64_Off seg_begin, + Elf64_Off seg_end ) + { + return ( seg_begin <= sect_begin ) && + ( sect_begin + sect_size <= seg_end ) && + ( sect_begin < + seg_end ); // this is important criteria when sect_size == 0 + // Example: seg_begin=10, seg_end=12 (-> covering the bytes 10 and 11) + // sect_begin=12, sect_size=0 -> shall return false! + } + + //------------------------------------------------------------------------------ + bool load_segments( std::istream& stream, bool is_lazy ) + { + unsigned char file_class = header->get_class(); + Elf_Half entry_size = header->get_segment_entry_size(); + Elf_Half num = header->get_segments_num(); + Elf64_Off offset = header->get_segments_offset(); + + if ( ( num != 0 && file_class == ELFCLASS64 && + entry_size < sizeof( Elf64_Phdr ) ) || + ( num != 0 && file_class == ELFCLASS32 && + entry_size < sizeof( Elf32_Phdr ) ) ) { + return false; + } + + for ( Elf_Half i = 0; i < num; ++i ) { + if ( file_class == ELFCLASS64 ) { + segments_.emplace_back( + new ( std::nothrow ) segment_impl( + &convertor, &addr_translator ) ); + } + else if ( file_class == ELFCLASS32 ) { + segments_.emplace_back( + new ( std::nothrow ) segment_impl( + &convertor, &addr_translator ) ); + } + else { + segments_.pop_back(); + return false; + } + + segment* seg = segments_.back().get(); + + if ( !seg->load( stream, + static_cast( offset ) + + static_cast( i ) * entry_size, + is_lazy ) || + stream.fail() ) { + segments_.pop_back(); + return false; + } + + seg->set_index( i ); + + // Add sections to the segments (similar to readelfs algorithm) + Elf64_Off segBaseOffset = seg->get_offset(); + Elf64_Off segEndOffset = segBaseOffset + seg->get_file_size(); + Elf64_Off segVBaseAddr = seg->get_virtual_address(); + Elf64_Off segVEndAddr = segVBaseAddr + seg->get_memory_size(); + for ( const auto& psec : sections ) { + // SHF_ALLOC sections are matched based on the virtual address + // otherwise the file offset is matched + if ( ( ( psec->get_flags() & SHF_ALLOC ) == SHF_ALLOC ) + ? is_sect_in_seg( psec->get_address(), + psec->get_size(), segVBaseAddr, + segVEndAddr ) + : is_sect_in_seg( psec->get_offset(), psec->get_size(), + segBaseOffset, segEndOffset ) ) { + // Alignment of segment shall not be updated, to preserve original value + // It will be re-calculated on saving. + seg->add_section_index( psec->get_index(), 0 ); + } + } + } + + return true; + } + + //------------------------------------------------------------------------------ + bool save_header( std::ostream& stream ) const + { + return header->save( stream ); + } + + //------------------------------------------------------------------------------ + bool save_sections( std::ostream& stream ) const + { + for ( const auto& sec : sections_ ) { + std::streampos headerPosition = + static_cast( header->get_sections_offset() ) + + static_cast( + header->get_section_entry_size() ) * + sec->get_index(); + + sec->save( stream, headerPosition, sec->get_offset() ); + } + return true; + } + + //------------------------------------------------------------------------------ + bool save_segments( std::ostream& stream ) const + { + for ( const auto& seg : segments_ ) { + std::streampos headerPosition = + static_cast( header->get_segments_offset() ) + + static_cast( + header->get_segment_entry_size() ) * + seg->get_index(); + + seg->save( stream, headerPosition, seg->get_offset() ); + } + return true; + } + + //------------------------------------------------------------------------------ + bool is_section_without_segment( unsigned int section_index ) const + { + bool found = false; + + for ( unsigned int j = 0; !found && ( j < segments.size() ); ++j ) { + for ( Elf_Half k = 0; + !found && ( k < segments[j]->get_sections_num() ); ++k ) { + found = segments[j]->get_section_index_at( k ) == section_index; + } + } + + return !found; + } + + //------------------------------------------------------------------------------ + static bool is_subsequence_of( const segment* seg1, const segment* seg2 ) + { + // Return 'true' if sections of seg1 are a subset of sections in seg2 + const std::vector& sections1 = seg1->get_sections(); + const std::vector& sections2 = seg2->get_sections(); + + bool found = false; + if ( sections1.size() < sections2.size() ) { + found = std::includes( sections2.begin(), sections2.end(), + sections1.begin(), sections1.end() ); + } + + return found; + } + + //------------------------------------------------------------------------------ + std::vector get_ordered_segments() const + { + std::vector res; + std::deque worklist; + + res.reserve( segments.size() ); + for ( const auto& seg : segments ) { + worklist.emplace_back( seg.get() ); + } + + // Bring the segments which start at address 0 to the front + size_t nextSlot = 0; + for ( size_t i = 0; i < worklist.size(); ++i ) { + if ( i != nextSlot && worklist[i]->is_offset_initialized() && + worklist[i]->get_offset() == 0 ) { + if ( worklist[nextSlot]->get_offset() == 0 ) { + ++nextSlot; + } + std::swap( worklist[i], worklist[nextSlot] ); + ++nextSlot; + } + } + + while ( !worklist.empty() ) { + segment* seg = worklist.front(); + worklist.pop_front(); + + size_t i = 0; + for ( ; i < worklist.size(); ++i ) { + if ( is_subsequence_of( seg, worklist[i] ) ) { + break; + } + } + + if ( i < worklist.size() ) { + worklist.emplace_back( seg ); + } + else { + res.emplace_back( seg ); + } + } + + return res; + } + + //------------------------------------------------------------------------------ + bool layout_sections_without_segments() + { + for ( unsigned int i = 0; i < sections_.size(); ++i ) { + if ( is_section_without_segment( i ) ) { + const auto& sec = sections_[i]; + + if ( Elf_Xword section_align = sec->get_addr_align(); + section_align > 1 && + current_file_pos % section_align != 0 ) { + current_file_pos += + section_align - current_file_pos % section_align; + } + + if ( 0 != sec->get_index() ) { + sec->set_offset( current_file_pos ); + } + + if ( SHT_NOBITS != sec->get_type() && + SHT_NULL != sec->get_type() ) { + current_file_pos += sec->get_size(); + } + } + } + + return true; + } + + //------------------------------------------------------------------------------ + void calc_segment_alignment() const + { + for ( const auto& seg : segments_ ) { + for ( Elf_Half i = 0; i < seg->get_sections_num(); ++i ) { + const auto& sect = sections_[seg->get_section_index_at( i )]; + if ( sect->get_addr_align() > seg->get_align() ) { + seg->set_align( sect->get_addr_align() ); + } + } + } + } + + //------------------------------------------------------------------------------ + bool layout_segments_and_their_sections() + { + std::vector worklist; + std::vector section_generated( sections.size(), false ); + + // Get segments in a order in where segments which contain a + // sub sequence of other segments are located at the end + worklist = get_ordered_segments(); + + for ( auto* seg : worklist ) { + Elf_Xword segment_memory = 0; + Elf_Xword segment_filesize = 0; + Elf_Xword seg_start_pos = current_file_pos; + // Special case: PHDR segment + // This segment contains the program headers but no sections + if ( seg->get_type() == PT_PHDR && seg->get_sections_num() == 0 ) { + seg_start_pos = header->get_segments_offset(); + segment_memory = segment_filesize = + header->get_segment_entry_size() * + static_cast( header->get_segments_num() ); + } + // Special case: + else if ( seg->is_offset_initialized() && seg->get_offset() == 0 ) { + seg_start_pos = 0; + if ( seg->get_sections_num() > 0 ) { + segment_memory = segment_filesize = current_file_pos; + } + } + // New segments with not generated sections + // have to be aligned + else if ( seg->get_sections_num() > 0 && + !section_generated[seg->get_section_index_at( 0 )] ) { + Elf_Xword align = seg->get_align() > 0 ? seg->get_align() : 1; + Elf64_Off cur_page_alignment = current_file_pos % align; + Elf64_Off req_page_alignment = + seg->get_virtual_address() % align; + Elf64_Off error = req_page_alignment - cur_page_alignment; + + current_file_pos += ( seg->get_align() + error ) % align; + seg_start_pos = current_file_pos; + } + else if ( seg->get_sections_num() > 0 ) { + seg_start_pos = + sections[seg->get_section_index_at( 0 )]->get_offset(); + } + + // Write segment's data + if ( !write_segment_data( seg, section_generated, segment_memory, + segment_filesize, seg_start_pos ) ) { + return false; + } + + seg->set_file_size( segment_filesize ); + + // If we already have a memory size from loading an elf file (value > 0), + // it must not shrink! + // Memory size may be bigger than file size and it is the loader's job to do something + // with the surplus bytes in memory, like initializing them with a defined value. + if ( seg->get_memory_size() < segment_memory ) { + seg->set_memory_size( segment_memory ); + } + + seg->set_offset( seg_start_pos ); + } + + return true; + } + + //------------------------------------------------------------------------------ + bool layout_section_table() + { + // Simply place the section table at the end for now + Elf64_Off alignmentError = current_file_pos % 4; + current_file_pos += ( 4 - alignmentError ) % 4; + header->set_sections_offset( current_file_pos ); + return true; + } + + //------------------------------------------------------------------------------ + bool write_segment_data( const segment* seg, + std::vector& section_generated, + Elf_Xword& segment_memory, + Elf_Xword& segment_filesize, + const Elf_Xword& seg_start_pos ) + { + for ( Elf_Half j = 0; j < seg->get_sections_num(); ++j ) { + Elf_Half index = seg->get_section_index_at( j ); + + section* sec = sections[index]; + + // The NULL section is always generated + if ( SHT_NULL == sec->get_type() ) { + section_generated[index] = true; + continue; + } + + Elf_Xword section_align = 0; + // Fix up the alignment + if ( !section_generated[index] && sec->is_address_initialized() && + SHT_NOBITS != sec->get_type() && SHT_NULL != sec->get_type() && + 0 != sec->get_size() ) { + // Align the sections based on the virtual addresses + // when possible (this is what matters for execution) + Elf64_Off req_offset = + sec->get_address() - seg->get_virtual_address(); + Elf64_Off cur_offset = current_file_pos - seg_start_pos; + if ( req_offset < cur_offset ) { + // something has gone awfully wrong, abort! + // section_align would turn out negative, seeking backwards and overwriting previous data + return false; + } + section_align = req_offset - cur_offset; + } + else if ( !section_generated[index] && + !sec->is_address_initialized() ) { + // If no address has been specified then only the section + // alignment constraint has to be matched + Elf_Xword align = sec->get_addr_align(); + if ( align == 0 ) { + align = 1; + } + Elf64_Off error = current_file_pos % align; + section_align = ( align - error ) % align; + } + else if ( section_generated[index] ) { + // Alignment for already generated sections + section_align = + sec->get_offset() - seg_start_pos - segment_filesize; + } + + // Determine the segment file and memory sizes + // Special case .tbss section (NOBITS) in non TLS segment + if ( ( ( sec->get_flags() & SHF_ALLOC ) == SHF_ALLOC ) && + !( ( ( sec->get_flags() & SHF_TLS ) == SHF_TLS ) && + ( seg->get_type() != PT_TLS ) && + ( SHT_NOBITS == sec->get_type() ) ) ) { + segment_memory += sec->get_size() + section_align; + } + + if ( SHT_NOBITS != sec->get_type() ) { + segment_filesize += sec->get_size() + section_align; + } + + // Nothing to be done when generating nested segments + if ( section_generated[index] ) { + continue; + } + + current_file_pos += section_align; + + // Set the section addresses when missing + if ( !sec->is_address_initialized() ) { + sec->set_address( seg->get_virtual_address() + + current_file_pos - seg_start_pos ); + } + + if ( 0 != sec->get_index() ) { + sec->set_offset( current_file_pos ); + } + + if ( SHT_NOBITS != sec->get_type() ) { + current_file_pos += sec->get_size(); + } + + section_generated[index] = true; + } + + return true; + } + + //------------------------------------------------------------------------------ + public: + friend class Sections; + class Sections + { + public: + //------------------------------------------------------------------------------ + explicit Sections( elfio* parent ) : parent( parent ) {} + + //------------------------------------------------------------------------------ + Elf_Half size() const + { + return static_cast( parent->sections_.size() ); + } + + //------------------------------------------------------------------------------ + section* operator[]( unsigned int index ) const + { + section* sec = nullptr; + + if ( index < parent->sections_.size() ) { + sec = parent->sections_[index].get(); + } + + return sec; + } + + //------------------------------------------------------------------------------ + section* operator[]( const std::string_view& name ) const + { + section* sec = nullptr; + + for ( const auto& it : parent->sections_ ) { + if ( it->get_name() == name ) { + sec = it.get(); + break; + } + } + + return sec; + } + + //------------------------------------------------------------------------------ + section* add( const std::string& name ) const + { + section* new_section = parent->create_section(); + new_section->set_name( name ); + + Elf_Half str_index = parent->get_section_name_str_index(); + section* string_table( parent->sections_[str_index].get() ); + string_section_accessor str_writer( string_table ); + Elf_Word pos = str_writer.add_string( name ); + new_section->set_name_string_offset( pos ); + + return new_section; + } + + //------------------------------------------------------------------------------ + std::vector>::iterator begin() + { + return parent->sections_.begin(); + } + + //------------------------------------------------------------------------------ + std::vector>::iterator end() + { + return parent->sections_.end(); + } + + //------------------------------------------------------------------------------ + std::vector>::const_iterator begin() const + { + return parent->sections_.cbegin(); + } + + //------------------------------------------------------------------------------ + std::vector>::const_iterator end() const + { + return parent->sections_.cend(); + } + + //------------------------------------------------------------------------------ + private: + elfio* parent; + }; + Sections sections; + + //------------------------------------------------------------------------------ + friend class Segments; + class Segments + { + public: + //------------------------------------------------------------------------------ + explicit Segments( elfio* parent ) : parent( parent ) {} + + //------------------------------------------------------------------------------ + Elf_Half size() const + { + return static_cast( parent->segments_.size() ); + } + + //------------------------------------------------------------------------------ + segment* operator[]( unsigned int index ) const + { + return parent->segments_[index].get(); + } + + //------------------------------------------------------------------------------ + segment* add() { return parent->create_segment(); } + + //------------------------------------------------------------------------------ + std::vector>::iterator begin() + { + return parent->segments_.begin(); + } + + //------------------------------------------------------------------------------ + std::vector>::iterator end() + { + return parent->segments_.end(); + } + + //------------------------------------------------------------------------------ + std::vector>::const_iterator begin() const + { + return parent->segments_.cbegin(); + } + + //------------------------------------------------------------------------------ + std::vector>::const_iterator end() const + { + return parent->segments_.cend(); + } + + //------------------------------------------------------------------------------ + private: + elfio* parent; + }; + Segments segments; + + //------------------------------------------------------------------------------ + private: + std::unique_ptr pstream = nullptr; + std::unique_ptr header = nullptr; + std::vector> sections_; + std::vector> segments_; + endianess_convertor convertor; + address_translator addr_translator; + std::shared_ptr compression = nullptr; + + Elf_Xword current_file_pos = 0; +}; + +} // namespace ELFIO + +#include +#include +#include +#include +#include +#include +#include + +#endif // ELFIO_HPP diff --git a/include/elfio/elfio_array.hpp b/include/elfio/elfio_array.hpp new file mode 100644 index 0000000..190c075 --- /dev/null +++ b/include/elfio/elfio_array.hpp @@ -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 + +namespace ELFIO { + +//------------------------------------------------------------------------------ +template 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( 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( &temp ), + sizeof( temp ) ); + } + + private: + //------------------------------------------------------------------------------ + const elfio& elf_file; + S* array_section; +}; + +template +using array_section_accessor = array_section_accessor_template; +template +using const_array_section_accessor = + array_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_ARRAY_HPP diff --git a/include/elfio/elfio_dump.hpp b/include/elfio/elfio_dump.hpp new file mode 100644 index 0000000..3310511 --- /dev/null +++ b/include/elfio/elfio_dump.hpp @@ -0,0 +1,1346 @@ +/* +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_DUMP_HPP +#define ELFIO_DUMP_HPP + +#include +#include +#include +#include +#include +#include + +namespace ELFIO { + +static const struct class_table_t +{ + const char key; + const char* str; +} class_table[] = { + { ELFCLASS32, "ELF32" }, + { ELFCLASS64, "ELF64" }, +}; + +static const struct endian_table_t +{ + const char key; + const char* str; +} endian_table[] = { + { ELFDATANONE, "None" }, + { ELFDATA2LSB, "Little endian" }, + { ELFDATA2MSB, "Big endian" }, +}; + +static const struct os_abi_table_t +{ + const unsigned char key; + const char* str; +} os_abi_table[] = { + { ELFOSABI_NONE, "UNIX System V" }, + { ELFOSABI_HPUX, "Hewlett-Packard HP-UX" }, + { ELFOSABI_NETBSD, "NetBSD" }, + { ELFOSABI_LINUX, "Linux" }, + { ELFOSABI_HURD, "GNU Hurd" }, + { ELFOSABI_SOLARIS, "Sun Solaris" }, + { ELFOSABI_AIX, "AIX" }, + { ELFOSABI_IRIX, "IRIX" }, + { ELFOSABI_FREEBSD, "FreeBSD" }, + { ELFOSABI_TRU64, "Compaq TRU64 UNIX" }, + { ELFOSABI_MODESTO, "Novell Modesto" }, + { ELFOSABI_OPENBSD, "Open BSD" }, + { ELFOSABI_OPENVMS, "Open VMS" }, + { ELFOSABI_NSK, "Hewlett-Packard Non-Stop Kernel" }, + { ELFOSABI_AROS, "Amiga Research OS" }, + { ELFOSABI_FENIXOS, "FenixOS" }, + { ELFOSABI_NUXI, "Nuxi CloudABI" }, + { ELFOSABI_OPENVOS, "OpenVOS" }, + { ELFOSABI_ARM, "ARM" }, + { ELFOSABI_STANDALONE, "Standalone (embedded)" }, +}; + +static const struct version_table_t +{ + const Elf64_Word key; + const char* str; +} version_table[] = { + { EV_NONE, "None" }, + { EV_CURRENT, "Current" }, +}; + +static const struct type_table_t +{ + const Elf32_Half key; + const char* str; +} type_table[] = { + { ET_NONE, "No file type" }, { ET_REL, "Relocatable file" }, + { ET_EXEC, "Executable file" }, { ET_DYN, "Shared object file" }, + { ET_CORE, "Core file" }, +}; + +static const struct machine_table_t +{ + const Elf64_Half key; + const char* str; +} machine_table[] = { + { EM_NONE, "No machine" }, + { EM_M32, "AT&T WE 32100" }, + { EM_SPARC, "SUN SPARC" }, + { EM_386, "Intel 80386" }, + { EM_68K, "Motorola m68k family" }, + { EM_88K, "Motorola m88k family" }, + { EM_486, "Intel 80486// Reserved for future use" }, + { EM_860, "Intel 80860" }, + { EM_MIPS, "MIPS R3000 (officially, big-endian only)" }, + { EM_S370, "IBM System/370" }, + { EM_MIPS_RS3_LE, + "MIPS R3000 little-endian (Oct 4 1999 Draft) Deprecated" }, + { EM_res011, "Reserved" }, + { EM_res012, "Reserved" }, + { EM_res013, "Reserved" }, + { EM_res014, "Reserved" }, + { EM_PARISC, "HPPA" }, + { EM_res016, "Reserved" }, + { EM_VPP550, "Fujitsu VPP500" }, + { EM_SPARC32PLUS, "Sun's v8plus" }, + { EM_960, "Intel 80960" }, + { EM_PPC, "PowerPC" }, + { EM_PPC64, "64-bit PowerPC" }, + { EM_S390, "IBM S/390" }, + { EM_SPU, "Sony/Toshiba/IBM SPU" }, + { EM_res024, "Reserved" }, + { EM_res025, "Reserved" }, + { EM_res026, "Reserved" }, + { EM_res027, "Reserved" }, + { EM_res028, "Reserved" }, + { EM_res029, "Reserved" }, + { EM_res030, "Reserved" }, + { EM_res031, "Reserved" }, + { EM_res032, "Reserved" }, + { EM_res033, "Reserved" }, + { EM_res034, "Reserved" }, + { EM_res035, "Reserved" }, + { EM_V800, "NEC V800 series" }, + { EM_FR20, "Fujitsu FR20" }, + { EM_RH32, "TRW RH32" }, + { EM_MCORE, "Motorola M*Core // May also be taken by Fujitsu MMA" }, + { EM_RCE, "Old name for MCore" }, + { EM_ARM, "ARM" }, + { EM_OLD_ALPHA, "Digital Alpha" }, + { EM_SH, "Renesas (formerly Hitachi) / SuperH SH" }, + { EM_SPARCV9, "SPARC v9 64-bit" }, + { EM_TRICORE, "Siemens Tricore embedded processor" }, + { EM_ARC, "ARC Cores" }, + { EM_H8_300, "Renesas (formerly Hitachi) H8/300" }, + { EM_H8_300H, "Renesas (formerly Hitachi) H8/300H" }, + { EM_H8S, "Renesas (formerly Hitachi) H8S" }, + { EM_H8_500, "Renesas (formerly Hitachi) H8/500" }, + { EM_IA_64, "Intel IA-64 Processor" }, + { EM_MIPS_X, "Stanford MIPS-X" }, + { EM_COLDFIRE, "Motorola Coldfire" }, + { EM_68HC12, "Motorola M68HC12" }, + { EM_MMA, "Fujitsu Multimedia Accelerator" }, + { EM_PCP, "Siemens PCP" }, + { EM_NCPU, "Sony nCPU embedded RISC processor" }, + { EM_NDR1, "Denso NDR1 microprocesspr" }, + { EM_STARCORE, "Motorola Star*Core processor" }, + { EM_ME16, "Toyota ME16 processor" }, + { EM_ST100, "STMicroelectronics ST100 processor" }, + { EM_TINYJ, "Advanced Logic Corp. TinyJ embedded processor" }, + { EM_X86_64, "Advanced Micro Devices X86-64 processor" }, + { EM_PDSP, "Sony DSP Processor" }, + { EM_PDP10, "Digital Equipment Corp. PDP-10" }, + { EM_PDP11, "Digital Equipment Corp. PDP-11" }, + { EM_FX66, "Siemens FX66 microcontroller" }, + { EM_ST9PLUS, "STMicroelectronics ST9+ 8/16 bit microcontroller" }, + { EM_ST7, "STMicroelectronics ST7 8-bit microcontroller" }, + { EM_68HC16, "Motorola MC68HC16 Microcontroller" }, + { EM_68HC11, "Motorola MC68HC11 Microcontroller" }, + { EM_68HC08, "Motorola MC68HC08 Microcontroller" }, + { EM_68HC05, "Motorola MC68HC05 Microcontroller" }, + { EM_SVX, "Silicon Graphics SVx" }, + { EM_ST19, "STMicroelectronics ST19 8-bit cpu" }, + { EM_VAX, "Digital VAX" }, + { EM_CRIS, "Axis Communications 32-bit embedded processor" }, + { EM_JAVELIN, "Infineon Technologies 32-bit embedded cpu" }, + { EM_FIREPATH, "Element 14 64-bit DSP processor" }, + { EM_ZSP, "LSI Logic's 16-bit DSP processor" }, + { EM_MMIX, "Donald Knuth's educational 64-bit processor" }, + { EM_HUANY, "Harvard's machine-independent format" }, + { EM_PRISM, "SiTera Prism" }, + { EM_AVR, "Atmel AVR 8-bit microcontroller" }, + { EM_FR30, "Fujitsu FR30" }, + { EM_D10V, "Mitsubishi D10V" }, + { EM_D30V, "Mitsubishi D30V" }, + { EM_V850, "NEC v850" }, + { EM_M32R, "Renesas M32R (formerly Mitsubishi M32R)" }, + { EM_MN10300, "Matsushita MN10300" }, + { EM_MN10200, "Matsushita MN10200" }, + { EM_PJ, "picoJava" }, + { EM_OPENRISC, "OpenRISC 32-bit embedded processor" }, + { EM_ARC_A5, "ARC Cores Tangent-A5" }, + { EM_XTENSA, "Tensilica Xtensa Architecture" }, + { EM_VIDEOCORE, "Alphamosaic VideoCore processor" }, + { EM_TMM_GPP, "Thompson Multimedia General Purpose Processor" }, + { EM_NS32K, "National Semiconductor 32000 series" }, + { EM_TPC, "Tenor Network TPC processor" }, + { EM_SNP1K, "Trebia SNP 1000 processor" }, + { EM_ST200, "STMicroelectronics ST200 microcontroller" }, + { EM_IP2K, "Ubicom IP2022 micro controller" }, + { EM_MAX, "MAX Processor" }, + { EM_CR, "National Semiconductor CompactRISC" }, + { EM_F2MC16, "Fujitsu F2MC16" }, + { EM_MSP430, "TI msp430 micro controller" }, + { EM_BLACKFIN, "ADI Blackfin" }, + { EM_SE_C33, "S1C33 Family of Seiko Epson processors" }, + { EM_SEP, "Sharp embedded microprocessor" }, + { EM_ARCA, "Arca RISC Microprocessor" }, + { EM_UNICORE, "Microprocessor series from PKU-Unity Ltd. and MPRC of " + "Peking University" }, + { EM_EXCESS, "eXcess: 16/32/64-bit configurable embedded CPU" }, + { EM_DXP, "Icera Semiconductor Inc. Deep Execution Processor" }, + { EM_ALTERA_NIOS2, "Altera Nios II soft-core processor" }, + { EM_CRX, "National Semiconductor CRX" }, + { EM_XGATE, "Motorola XGATE embedded processor" }, + { EM_C166, "Infineon C16x/XC16x processor" }, + { EM_M16C, "Renesas M16C series microprocessors" }, + { EM_DSPIC30F, "Microchip Technology dsPIC30F Digital Signal Controller" }, + { EM_CE, "Freescale Communication Engine RISC core" }, + { EM_M32C, "Renesas M32C series microprocessors" }, + { EM_res121, "Reserved" }, + { EM_res122, "Reserved" }, + { EM_res123, "Reserved" }, + { EM_res124, "Reserved" }, + { EM_res125, "Reserved" }, + { EM_res126, "Reserved" }, + { EM_res127, "Reserved" }, + { EM_res128, "Reserved" }, + { EM_res129, "Reserved" }, + { EM_res130, "Reserved" }, + { EM_TSK3000, "Altium TSK3000 core" }, + { EM_RS08, "Freescale RS08 embedded processor" }, + { EM_res133, "Reserved" }, + { EM_ECOG2, "Cyan Technology eCOG2 microprocessor" }, + { EM_SCORE, "Sunplus Score" }, + { EM_SCORE7, "Sunplus S+core7 RISC processor" }, + { EM_DSP24, "New Japan Radio (NJR) 24-bit DSP Processor" }, + { EM_VIDEOCORE3, "Broadcom VideoCore III processor" }, + { EM_LATTICEMICO32, "RISC processor for Lattice FPGA architecture" }, + { EM_SE_C17, "Seiko Epson C17 family" }, + { EM_TI_C6000, "Texas Instruments TMS320C6000 DSP family" }, + { EM_TI_C2000, "Texas Instruments TMS320C2000 DSP family" }, + { EM_TI_C5500, "Texas Instruments TMS320C55x DSP family" }, + { EM_res143, "Reserved" }, + { EM_res144, "Reserved" }, + { EM_res145, "Reserved" }, + { EM_res146, "Reserved" }, + { EM_res147, "Reserved" }, + { EM_res148, "Reserved" }, + { EM_res149, "Reserved" }, + { EM_res150, "Reserved" }, + { EM_res151, "Reserved" }, + { EM_res152, "Reserved" }, + { EM_res153, "Reserved" }, + { EM_res154, "Reserved" }, + { EM_res155, "Reserved" }, + { EM_res156, "Reserved" }, + { EM_res157, "Reserved" }, + { EM_res158, "Reserved" }, + { EM_res159, "Reserved" }, + { EM_MMDSP_PLUS, "STMicroelectronics 64bit VLIW Data Signal Processor" }, + { EM_CYPRESS_M8C, "Cypress M8C microprocessor" }, + { EM_R32C, "Renesas R32C series microprocessors" }, + { EM_TRIMEDIA, "NXP Semiconductors TriMedia architecture family" }, + { EM_QDSP6, "QUALCOMM DSP6 Processor" }, + { EM_8051, "Intel 8051 and variants" }, + { EM_STXP7X, "STMicroelectronics STxP7x family" }, + { EM_NDS32, + "Andes Technology compact code size embedded RISC processor family" }, + { EM_ECOG1, "Cyan Technology eCOG1X family" }, + { EM_ECOG1X, "Cyan Technology eCOG1X family" }, + { EM_MAXQ30, "Dallas Semiconductor MAXQ30 Core Micro-controllers" }, + { EM_XIMO16, "New Japan Radio (NJR) 16-bit DSP Processor" }, + { EM_MANIK, "M2000 Reconfigurable RISC Microprocessor" }, + { EM_CRAYNV2, "Cray Inc. NV2 vector architecture" }, + { EM_RX, "Renesas RX family" }, + { EM_METAG, "Imagination Technologies META processor architecture" }, + { EM_MCST_ELBRUS, "MCST Elbrus general purpose hardware architecture" }, + { EM_ECOG16, "Cyan Technology eCOG16 family" }, + { EM_CR16, "National Semiconductor CompactRISC 16-bit processor" }, + { EM_ETPU, "Freescale Extended Time Processing Unit" }, + { EM_SLE9X, "Infineon Technologies SLE9X core" }, + { EM_L1OM, "Intel L1OM" }, + { EM_INTEL181, "Reserved by Intel" }, + { EM_INTEL182, "Reserved by Intel" }, + { EM_AARCH64, "ARM AArch64" }, + { EM_res184, "Reserved by ARM" }, + { EM_AVR32, "Atmel Corporation 32-bit microprocessor family" }, + { EM_STM8, "STMicroeletronics STM8 8-bit microcontroller" }, + { EM_TILE64, "Tilera TILE64 multicore architecture family" }, + { EM_TILEPRO, "Tilera TILEPro multicore architecture family" }, + { EM_MICROBLAZE, "Xilinx MicroBlaze 32-bit RISC soft processor core" }, + { EM_CUDA, "NVIDIA CUDA architecture " }, + { EM_TILEGX, "Tilera TILE-Gx multicore architecture family" }, + { EM_CLOUDSHIELD, "CloudShield architecture family" }, + { EM_COREA_1ST, "KIPO-KAIST Core-A 1st generation processor family" }, + { EM_COREA_2ND, "KIPO-KAIST Core-A 2nd generation processor family" }, + { EM_ARC_COMPACT2, "Synopsys ARCompact V2" }, + { EM_OPEN8, "Open8 8-bit RISC soft processor core" }, + { EM_RL78, "Renesas RL78 family" }, + { EM_VIDEOCORE5, "Broadcom VideoCore V processor" }, + { EM_78KOR, "Renesas 78KOR family" }, + { EM_56800EX, "Freescale 56800EX Digital Signal Controller (DSC)" }, + { EM_BA1, "Beyond BA1 CPU architecture" }, + { EM_BA2, "Beyond BA2 CPU architecture" }, + { EM_XCORE, "XMOS xCORE processor family" }, + { EM_MCHP_PIC, "Microchip 8-bit PIC(r) family" }, + { EM_INTEL205, "Reserved by Intel" }, + { EM_INTEL206, "Reserved by Intel" }, + { EM_INTEL207, "Reserved by Intel" }, + { EM_INTEL208, "Reserved by Intel" }, + { EM_INTEL209, "Reserved by Intel" }, + { EM_KM32, "KM211 KM32 32-bit processor" }, + { EM_KMX32, "KM211 KMX32 32-bit processor" }, + { EM_KMX16, "KM211 KMX16 16-bit processor" }, + { EM_KMX8, "KM211 KMX8 8-bit processor" }, + { EM_KVARC, "KM211 KVARC processor" }, + { EM_CDP, "Paneve CDP architecture family" }, + { EM_COGE, "Cognitive Smart Memory Processor" }, + { EM_COOL, "iCelero CoolEngine" }, + { EM_NORC, "Nanoradio Optimized RISC" }, + { EM_CSR_KALIMBA, "CSR Kalimba architecture family" }, + { EM_Z80, "Zilog Z80" }, + { EM_VISIUM, "Controls and Data Services VISIUMcore processor" }, + { EM_FT32, "FTDI Chip FT32 high performance 32-bit RISC architecture" }, + { EM_MOXIE, "Moxie processor family" }, + { EM_AMDGPU, "AMD GPU architecture" }, + { EM_RISCV, "RISC-V" }, + { EM_LANAI, "Lanai processor" }, + { EM_CEVA, "CEVA Processor Architecture Family" }, + { EM_CEVA_X2, "CEVA X2 Processor Family" }, + { EM_BPF, "Linux BPF – in-kernel virtual machine" }, + { EM_GRAPHCORE_IPU, "Graphcore Intelligent Processing Unit" }, + { EM_IMG1, "Imagination Technologies" }, + { EM_NFP, "Netronome Flow Processor (P)" }, + { EM_CSKY, "C-SKY processor family" }, + { EM_ARC_COMPACT3_64, "Synopsys ARCv2.3 64-bit" }, + { EM_MCS6502, "MOS Technology MCS 6502 processor" }, + { EM_ARC_COMPACT3, "Synopsys ARCv2.3 32-bit" }, + { EM_KVX, "Kalray VLIW core of the MPPA processor family" }, + { EM_65816, "WDC 65816/65C816" }, + { EM_LOONGARCH, "Loongson Loongarch" }, + { EM_KF32, "ChipON KungFu32" }, + { EM_MT, "Morpho Techologies MT processor" }, + { EM_ALPHA, "Alpha" }, + { EM_WEBASSEMBLY, "Web Assembly" }, + { EM_DLX, "OpenDLX" }, + { EM_XSTORMY16, "Sanyo XStormy16 CPU core" }, + { EM_IQ2000, "Vitesse IQ2000" }, + { EM_M32C_OLD, "M32C_OLD" }, + { EM_NIOS32, "Altera Nios" }, + { EM_CYGNUS_MEP, "Toshiba MeP Media Engine" }, + { EM_ADAPTEVA_EPIPHANY, "Adapteva EPIPHANY" }, + { EM_CYGNUS_FRV, "Fujitsu FR-V" }, + { EM_S12Z, "Freescale S12Z" }, +}; + +static const struct section_type_table_t +{ + const Elf64_Word key; + const char* str; +} section_type_table[] = { + { SHT_NULL, "NULL" }, + { SHT_PROGBITS, "PROGBITS" }, + { SHT_SYMTAB, "SYMTAB" }, + { SHT_STRTAB, "STRTAB" }, + { SHT_RELA, "RELA" }, + { SHT_HASH, "HASH" }, + { SHT_DYNAMIC, "DYNAMIC" }, + { SHT_NOTE, "NOTE" }, + { SHT_NOBITS, "NOBITS" }, + { SHT_REL, "REL" }, + { SHT_SHLIB, "SHLIB" }, + { SHT_DYNSYM, "DYNSYM" }, + { SHT_INIT_ARRAY, "INIT_ARRAY" }, + { SHT_FINI_ARRAY, "FINI_ARRAY" }, + { SHT_PREINIT_ARRAY, "PREINIT_ARRAY" }, + { SHT_GROUP, "GROUP" }, + { SHT_SYMTAB_SHNDX, "SYMTAB_SHNDX" }, + { SHT_GNU_ATTRIBUTES, "GNU_ATTRIBUTES" }, + { SHT_GNU_HASH, "GNU_HASH" }, + { SHT_GNU_LIBLIST, "GNU_LIBLIST" }, + { SHT_CHECKSUM, "CHECKSUM" }, + { SHT_LOSUNW, "LOSUNW" }, + { SHT_SUNW_move, "SUNW_move" }, + { SHT_SUNW_COMDAT, "SUNW_COMDAT" }, + { SHT_SUNW_syminfo, "SUNW_syminfo" }, + { SHT_GNU_verdef, "GNU_verdef" }, + { SHT_GNU_verneed, "GNU_verneed" }, + { SHT_GNU_versym, "GNU_versym" }, + { SHT_ARM_EXIDX, "ARM_EXIDX" }, + { SHT_ARM_PREEMPTMAP, "ARM_PREEMPTMAP" }, + { SHT_ARM_ATTRIBUTES, "ARM_ATTRIBUTES" }, + { SHT_ARM_DEBUGOVERLAY, "ARM_DEBUGOVERLAY" }, + { SHT_ARM_OVERLAYSECTION, "ARM_OVERLAYSECTION" }, + +}; + +static const struct segment_type_table_t +{ + const Elf_Word key; + const char* str; +} segment_type_table[] = { + { PT_NULL, "NULL" }, + { PT_LOAD, "LOAD" }, + { PT_DYNAMIC, "DYNAMIC" }, + { PT_INTERP, "INTERP" }, + { PT_NOTE, "NOTE" }, + { PT_SHLIB, "SHLIB" }, + { PT_PHDR, "PHDR" }, + { PT_TLS, "TLS" }, + { PT_GNU_EH_FRAME, "GNU_EH_FRAME" }, + { PT_GNU_STACK, "GNU_STACK" }, + { PT_GNU_RELRO, "GNU_RELRO" }, + { PT_GNU_PROPERTY, "GNU_PROPERTY" }, + { PT_GNU_MBIND_LO, "GNU_MBIND_LO" }, + { PT_GNU_MBIND_HI, "GNU_MBIND_HI" }, + { PT_PAX_FLAGS, "PAX_FLAGS" }, + { PT_OPENBSD_RANDOMIZE, "OPENBSD_RANDOMIZE" }, + { PT_OPENBSD_WXNEEDED, "OPENBSD_WXNEEDED " }, + { PT_OPENBSD_BOOTDATA, "OPENBSD_BOOTDATA " }, + { PT_SUNWBSS, "PT_SUNWBSS" }, + { PT_SUNWSTACK, "SUNWSTACK" }, +}; + +static const struct segment_flag_table_t +{ + const Elf_Word key; + const char* str; +} segment_flag_table[] = { + { 0, " " }, { 1, " E" }, { 2, " W " }, { 3, " WE" }, + { 4, "R " }, { 5, "R E" }, { 6, "RW " }, { 7, "RWE" }, +}; + +static const struct symbol_bind_t +{ + const Elf_Word key; + const char* str; +} symbol_bind_table[] = { + { STB_LOCAL, "LOCAL" }, { STB_GLOBAL, "GLOBAL" }, + { STB_WEAK, "WEAK" }, { STB_LOOS, "LOOS" }, + { STB_HIOS, "HIOS" }, { STB_MULTIDEF, "MULTIDEF" }, + { STB_LOPROC, "LOPROC" }, { STB_HIPROC, "HIPROC" }, +}; + +static const struct symbol_type_t +{ + const Elf_Word key; + const char* str; +} symbol_type_table[] = { + { STT_NOTYPE, "NOTYPE" }, { STT_OBJECT, "OBJECT" }, + { STT_FUNC, "FUNC" }, { STT_SECTION, "SECTION" }, + { STT_FILE, "FILE" }, { STT_COMMON, "COMMON" }, + { STT_TLS, "TLS" }, { STT_LOOS, "LOOS" }, + { STT_HIOS, "HIOS" }, { STT_LOPROC, "LOPROC" }, + { STT_HIPROC, "HIPROC" }, +}; + +static const struct dynamic_tag_t +{ + const Elf_Word key; + const char* str; +} dynamic_tag_table[] = { + { DT_NULL, "NULL" }, + { DT_NEEDED, "NEEDED" }, + { DT_PLTRELSZ, "PLTRELSZ" }, + { DT_PLTGOT, "PLTGOT" }, + { DT_HASH, "HASH" }, + { DT_STRTAB, "STRTAB" }, + { DT_SYMTAB, "SYMTAB" }, + { DT_RELA, "RELA" }, + { DT_RELASZ, "RELASZ" }, + { DT_RELAENT, "RELAENT" }, + { DT_STRSZ, "STRSZ" }, + { DT_SYMENT, "SYMENT" }, + { DT_INIT, "INIT" }, + { DT_FINI, "FINI" }, + { DT_SONAME, "SONAME" }, + { DT_RPATH, "RPATH" }, + { DT_SYMBOLIC, "SYMBOLIC" }, + { DT_REL, "REL" }, + { DT_RELSZ, "RELSZ" }, + { DT_RELENT, "RELENT" }, + { DT_PLTREL, "PLTREL" }, + { DT_DEBUG, "DEBUG" }, + { DT_TEXTREL, "TEXTREL" }, + { DT_JMPREL, "JMPREL" }, + { DT_BIND_NOW, "BIND_NOW" }, + { DT_INIT_ARRAY, "INIT_ARRAY" }, + { DT_FINI_ARRAY, "FINI_ARRAY" }, + { DT_INIT_ARRAYSZ, "INIT_ARRAYSZ" }, + { DT_FINI_ARRAYSZ, "FINI_ARRAYSZ" }, + { DT_RUNPATH, "RUNPATH" }, + { DT_FLAGS, "FLAGS" }, + { DT_ENCODING, "ENCODING" }, + { DT_PREINIT_ARRAY, "PREINIT_ARRAY" }, + { DT_PREINIT_ARRAYSZ, "PREINIT_ARRAYSZ" }, + { DT_MAXPOSTAGS, "MAXPOSTAGS" }, + { DT_GNU_HASH, "GNU_HASH" }, + { DT_TLSDESC_PLT, "TLSDESC_PLT" }, + { DT_TLSDESC_GOT, "TLSDESC_GOT" }, + { DT_GNU_CONFLICT, "GNU_CONFLICT" }, + { DT_GNU_LIBLIST, "GNU_LIBLIST" }, + { DT_CONFIG, "CONFIG" }, + { DT_DEPAUDIT, "DEPAUDIT" }, + { DT_AUDIT, "AUDIT" }, + { DT_PLTPAD, "PLTPAD" }, + { DT_MOVETAB, "MOVETAB" }, + { DT_SYMINFO, "SYMINFO" }, + { DT_ADDRRNGHI, "ADDRRNGHI" }, + { DT_VERSYM, "VERSYM" }, + { DT_RELACOUNT, "RELACOUNT" }, + { DT_RELCOUNT, "RELCOUNT" }, + { DT_FLAGS_1, "FLAGS_1" }, + { DT_VERDEF, "VERDEF" }, + { DT_VERDEFNUM, "VERDEFNUM" }, + { DT_VERNEED, "VERNEED" }, + { DT_VERNEEDNUM, "VERNEEDNUM" }, +}; + +// clang-format off +static const struct note_tag_t +{ + struct note_values_t + { + Elf64_Word type; + std::string type_str; + std::string description; + }; + std::string name; + std::vector values; +} note_tag_table[] = { + { "", + { { NT_PRSTATUS, "NT_PRSTATUS", "prstatus struct" }, + { NT_FPREGSET, "NT_FPREGSET", "fpregset struct" }, + { NT_PRPSINFO, "NT_PRPSINFO", "prpsinfo struct" }, + { NT_TASKSTRUCT, "NT_TASKSTRUCT", "task struct" }, + { NT_AUXV, "NT_AUXV", "Elfxx_auxv_t" }, + { NT_PSTATUS, "NT_PSTATUS", "pstatus struct" }, + { NT_FPREGS, "NT_FPREGS", "fpregset struct" }, + { NT_PSINFO, "NT_PSINFO", "psinfo struct" }, + { NT_LWPSTATUS, "NT_LWPSTATUS", "lwpstatus_t struct" }, + { NT_LWPSINFO, "NT_LWPSINFO", "lwpsinfo_t struct" }, + { NT_WIN32PSTATUS, "NT_WIN32PSTATUS", "win32_pstatus struct" }, + } }, + { "LINUX", + { { NT_PRXFPREG, "NT_PRXFPREG", "Contains a user_xfpregs_struct;" }, + { NT_PPC_VMX, "NT_PPC_VMX", "PowerPC Altivec/VMX registers" }, + { NT_PPC_VSX, "NT_PPC_VSX", "PowerPC VSX registers" }, + { NT_PPC_TAR, "NT_PPC_TAR", "PowerPC Target Address Register" }, + { NT_PPC_PPR, "NT_PPC_PPR", "PowerPC Program Priority Register" }, + { NT_PPC_DSCR, "NT_PPC_DSCR", "PowerPC Data Stream Control Register" }, + { NT_PPC_EBB, "NT_PPC_EBB", "PowerPC Event Based Branch Registers" }, + { NT_PPC_PMU, "NT_PPC_PMU", "PowerPC Performance Monitor Registers" }, + { NT_PPC_TM_CGPR, "NT_PPC_TM_CGPR", "PowerPC TM checkpointed GPR Registers" }, + { NT_PPC_TM_CFPR, "NT_PPC_TM_CFPR", "PowerPC TM checkpointed FPR Registers" }, + { NT_PPC_TM_CVMX, "NT_PPC_TM_CVMX", "PowerPC TM checkpointed VMX Registers" }, + { NT_PPC_TM_CVSX, "NT_PPC_TM_CVSX", "PowerPC TM checkpointed VSX Registers" }, + { NT_PPC_TM_SPR, "NT_PPC_TM_SPR", "PowerPC TM Special Purpose Registers" }, + { NT_PPC_TM_CTAR, "NT_PPC_TM_CTAR", "PowerPC TM checkpointed TAR" }, + { NT_PPC_TM_CPPR, "NT_PPC_TM_CPPR", "PowerPC TM checkpointed PPR" }, + { NT_PPC_TM_CDSCR, "NT_PPC_TM_CDSCR", "PowerPC TM checkpointed Data SCR" }, + { NT_386_TLS, "NT_386_TLS", "x86 TLS information" }, + { NT_386_IOPERM, "NT_386_IOPERM", "x86 io permissions" }, + { NT_X86_XSTATE, "NT_X86_XSTATE", "x86 XSAVE extended state" }, + { NT_X86_CET, "NT_X86_CET", "x86 CET state" }, + { NT_S390_HIGH_GPRS, "NT_S390_HIGH_GPRS", "S/390 upper halves of GPRs " }, + { NT_S390_TIMER, "NT_S390_TIMER", "S390 timer" }, + { NT_S390_TODCMP, "NT_S390_TODCMP", "S390 TOD clock comparator" }, + { NT_S390_TODPREG, "NT_S390_TODPREG", "S390 TOD programmable register" }, + { NT_S390_CTRS, "NT_S390_CTRS", "S390 control registers" }, + { NT_S390_PREFIX, "NT_S390_PREFIX", "S390 prefix register" }, + { NT_S390_LAST_BREAK, "NT_S390_LAST_BREAK", "S390 breaking event address" }, + { NT_S390_SYSTEM_CALL, "NT_S390_SYSTEM_CALL", "S390 system call restart data" }, + { NT_S390_TDB, "NT_S390_TDB", "S390 transaction diagnostic block" }, + { NT_S390_VXRS_LOW, "NT_S390_VXRS_LOW", "S390 vector registers 0-15 upper half" }, + { NT_S390_VXRS_HIGH, "NT_S390_VXRS_HIGH", "S390 vector registers 16-31" }, + { NT_S390_GS_CB, "NT_S390_GS_CB", "s390 guarded storage registers" }, + { NT_S390_GS_BC, "NT_S390_GS_BC", "s390 guarded storage broadcast control block" }, + { NT_ARM_VFP, "NT_ARM_VFP", "ARM VFP registers" }, + { NT_ARM_TLS, "NT_ARM_TLS", "AArch TLS registers" }, + { NT_ARM_HW_BREAK, "NT_ARM_HW_BREAK", "AArch hardware breakpoint registers" }, + { NT_ARM_HW_WATCH, "NT_ARM_HW_WATCH", "AArch hardware watchpoint registers" }, + { NT_ARM_SVE, "NT_ARM_SVE", "AArch SVE registers. " }, + { NT_ARM_PAC_MASK, "NT_ARM_PAC_MASK", "AArch pointer authentication code masks" }, + { NT_ARM_PACA_KEYS, "NT_ARM_PACA_KEYS", "ARM pointer authentication address keys" }, + { NT_ARM_PACG_KEYS, "NT_ARM_PACG_KEYS", "ARM pointer authentication generic keys" }, + { NT_ARM_TAGGED_ADDR_CTRL, "NT_ARM_TAGGED_ADDR_CTRL", "AArch64 tagged address control (prctl())" }, + { NT_ARM_PAC_ENABLED_KEYS, "NT_ARM_PAC_ENABLED_KEYS", "AArch64 pointer authentication enabled keys (prctl())" }, + { NT_ARC_V2, "NT_ARC_V2", "ARC HS accumulator/extra registers. " }, + { NT_LARCH_CPUCFG, "NT_LARCH_CPUCFG", "LoongArch CPU config registers" }, + { NT_LARCH_CSR, "NT_LARCH_CSR", "LoongArch Control State Registers" }, + { NT_LARCH_LSX, "NT_LARCH_LSX", "LoongArch SIMD eXtension registers" }, + { NT_LARCH_LASX, "NT_LARCH_LASX", "LoongArch Advanced SIMD eXtension registers" }, + { NT_RISCV_CSR, "NT_RISCV_CSR", "RISC-V Control and Status Registers" }, + } }, + { "CORE", + { { NT_LARCH_LBT, "NT_LARCH_LBT", "LoongArch Binary Translation registers" } + } }, + { "FreeBSD", + { { NT_FREEBSD_THRMISC, "NT_FREEBSD_THRMISC", "Thread miscellaneous info." }, + { NT_FREEBSD_PROCSTAT_PROC, "NT_FREEBSD_PROCSTAT_PROC", "Procstat proc data." }, + { NT_FREEBSD_PROCSTAT_FILES, "NT_FREEBSD_PROCSTAT_FILES", "Procstat files data." }, + { NT_FREEBSD_PROCSTAT_VMMAP, "NT_FREEBSD_PROCSTAT_VMMAP", "Procstat vmmap data." }, + { NT_FREEBSD_PROCSTAT_GROUPS, "NT_FREEBSD_PROCSTAT_GROUPS", "Procstat groups data." }, + { NT_FREEBSD_PROCSTAT_UMASK, "NT_FREEBSD_PROCSTAT_UMASK", "Procstat umask data." }, + { NT_FREEBSD_PROCSTAT_RLIMIT, "NT_FREEBSD_PROCSTAT_RLIMIT", "Procstat rlimit data." }, + { NT_FREEBSD_PROCSTAT_OSREL, "NT_FREEBSD_PROCSTAT_OSREL", "Procstat osreldate data." }, + { NT_FREEBSD_PROCSTAT_PSSTRINGS, "NT_FREEBSD_PROCSTAT_PSSTRINGS", "Procstat ps_strings data." }, + { NT_FREEBSD_PROCSTAT_AUXV, "NT_FREEBSD_PROCSTAT_AUXV", "Procstat auxv data." }, + { NT_FREEBSD_PTLWPINFO, "NT_FREEBSD_PTLWPINFO", "Thread ptrace miscellaneous info." }, + } }, + { "NetBSD-CORE", + { { NT_NETBSDCORE_PROCINFO, "NT_NETBSDCORE_PROCINFO", "Has a struct procinfo" }, + { NT_NETBSDCORE_AUXV, "NT_NETBSDCORE_AUXV", "Has auxv data" }, + { NT_NETBSDCORE_LWPSTATUS, "NT_NETBSDCORE_LWPSTATUS", "Has LWPSTATUS data" }, + { NT_NETBSDCORE_FIRSTMACH, "NT_NETBSDCORE_FIRSTMACH", "start of machdep note types" }, + } }, + { "OpenBSD", + { { NT_OPENBSD_PROCINFO, "NT_OPENBSD_PROCINFO", "" }, + { NT_OPENBSD_AUXV, "NT_OPENBSD_AUXV", "" }, + { NT_OPENBSD_REGS, "NT_OPENBSD_REGS", "" }, + { NT_OPENBSD_FPREGS, "NT_OPENBSD_FPREGS", "" }, + { NT_OPENBSD_XFPREGS, "NT_OPENBSD_XFPREGS", "" }, + { NT_OPENBSD_WCOOKIE, "NT_OPENBSD_WCOOKIE", "" }, + } }, + { "SPU", + { { NT_SPU, "NT_SPU", "" } + } }, + { "GNU", + { + { NT_GNU_ABI_TAG, "NT_GNU_ABI_TAG", "GNU ABI tag" }, + { NT_GNU_HWCAP, "NT_GNU_HWCAP", "Used by ld.so and kernel vDSO" }, + { NT_GNU_BUILD_ID, "NT_GNU_BUILD_ID", "Build ID of the binary" }, + { NT_GNU_GOLD_VERSION, "NT_GNU_GOLD_VERSION", "Version of GNU gold used to link the binary" }, + { NT_GNU_PROPERTY_TYPE_0, "NT_GNU_PROPERTY_TYPE_0", "Property type 0" }, + // { NT_GNU_PROPERTY_TYPE_1, "NT_GNU_PROPERTY_TYPE_1", "Property type 1" }, + // { NT_GNU_PROPERTY_TYPE_2, "NT_GNU_PROPERTY_TYPE_2", "Property type 2" }, + // { NT_GNU_PROPERTY_TYPE_3, "NT_GNU_PROPERTY_TYPE_3", "Property type 3" }, + // { NT_GNU_PROPERTY_TYPE_4, "NT_GNU_PROPERTY_TYPE_4", "Property type 4" }, + // { NT_GNU_PROPERTY_TYPE_5, "NT_GNU_PROPERTY_TYPE_5", "Property type 5" }, + // { NT_GNU_PROPERTY_TYPE_6, "NT_GNU_PROPERTY_TYPE_6", "Property type 6" }, + // { NT_GNU_PROPERTY_TYPE_7, "NT_GNU_PROPERTY_TYPE_7", "Property type 7" }, + // { NT_GNU_PROPERTY_TYPE_8, "NT_GNU_PROPERTY_TYPE_8", "Property type 8" }, + // { NT_GNU_PROPERTY_TYPE_9, "NT_GNU_PROPERTY_TYPE_9", "Property type 9" }, + // { NT_GNU_PROPERTY_TYPE_10, "NT_GNU_PROPERTY_TYPE_10", "Property type 10" }, + // { NT_GNU_PROPERTY_TYPE_11, "NT_GNU_PROPERTY_TYPE_11", "Property type 11" }, + // { NT_GNU_PROPERTY_TYPE_12, "NT_GNU_PROPERTY_TYPE_12", "Property type 12" }, + // { NT_GNU_PROPERTY_TYPE_13, "NT_GNU_PROPERTY_TYPE_13", "Property type 13" }, + // { NT_GNU_PROPERTY_TYPE_14, "NT_GNU_PROPERTY_TYPE_14", "Property type 14" }, + } }, + // { "SOLARIS", + // { { NT_SOLARIS_AUXV, "NT_SOLARIS_AUXV", "" } + // } }, + // { "AIX", + // { { NT_AIX_AUXV, "NT_AIX_AUXV", "" } + // } }, + // { "IRIX", + // { { NT_IRIX_FPREGS, "NT_IRIX_FPREGS", "" } + // } }, +}; +// clang-format on + +static const ELFIO::Elf_Xword MAX_DATA_ENTRIES = 64; + +//------------------------------------------------------------------------------ +class dump +{ +#define DUMP_DEC_FORMAT( width ) \ + std::setw( width ) << std::setfill( ' ' ) << std::dec << std::right +#define DUMP_HEX0x_FORMAT( width ) \ + "0x" << std::setw( width ) << std::setfill( '0' ) << std::hex << std::right +#define DUMP_HEX_FORMAT( width ) \ + std::setw( width ) << std::setfill( '0' ) << std::hex << std::right +#define DUMP_STR_FORMAT( width ) \ + std::setw( width ) << std::setfill( ' ' ) << std::hex << std::left + + public: + //------------------------------------------------------------------------------ + static void header( std::ostream& out, const elfio& reader ) + { + if ( !reader.get_header_size() ) { + return; + } + out << "ELF Header" << std::endl + << std::endl + << " Class: " << str_class( reader.get_class() ) << std::endl + << " Encoding: " << str_endian( reader.get_encoding() ) + << std::endl + << " ELFVersion: " << str_version( reader.get_elf_version() ) + << std::endl + << " OS/ABI: " << str_os_abi( reader.get_os_abi() ) + << std::endl + << " ABI Version:" << (int)reader.get_abi_version() << std::endl + << " Type: " << str_type( reader.get_type() ) << std::endl + << " Machine: " << str_machine( reader.get_machine() ) + << std::endl + << " Version: " << str_version( reader.get_version() ) + << std::endl + << " Entry: " + << "0x" << std::hex << reader.get_entry() << std::endl + << " Flags: " + << "0x" << std::hex << reader.get_flags() << std::endl + << std::endl; + } + + //------------------------------------------------------------------------------ + static void section_headers( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.sections.size(); + + if ( n == 0 ) { + return; + } + + out << "Section Headers:" << std::endl; + if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit + out << "[ Nr ] Type Addr Size ES Flg " + "Lk Inf Al Name" + << std::endl; + } + else { // Output for 64-bit + out << "[ Nr ] Type Addr Size " + " Offset Flg" + << std::endl + << " ES Lk Inf Al Name" << std::endl; + } + + for ( Elf_Half i = 0; i < n; ++i ) { // For all sections + const section* sec = reader.sections[i]; + section_header( out, i, sec, reader.get_class() ); + } + + out << "Key to Flags: W (write), A (alloc), X (execute), " << std::endl; + out << " M (merge), S (strings), I (info)," << std::endl; + out << " L (link order), O (extra OS processing required)," + << std::endl; + out << " G (group), T (TLS), C (compressed), E (exclude)" + << std::endl; + } + + //------------------------------------------------------------------------------ + static void section_header( std::ostream& out, + Elf_Half no, + const section* sec, + unsigned char elf_class ) + { + std::ios_base::fmtflags original_flags = out.flags(); + + // clang-format off + if ( elf_class == ELFCLASS32 ) { // Output for 32-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 17 ) << str_section_type( sec->get_type() ) + << " " << DUMP_HEX0x_FORMAT( 8 ) << sec->get_address() << " " + << DUMP_HEX0x_FORMAT( 8 ) << sec->get_size() << " " + << DUMP_HEX0x_FORMAT( 2 ) << sec->get_entry_size() << " " + << DUMP_STR_FORMAT( 3 ) << section_flags( sec->get_flags() ) + << " " << DUMP_HEX0x_FORMAT( 2 ) << sec->get_link() << " " + << DUMP_HEX0x_FORMAT( 3 ) << sec->get_info() << " " + << DUMP_HEX0x_FORMAT( 2 ) << sec->get_addr_align() << " " + << DUMP_STR_FORMAT( 17 ) << sec->get_name() << " " << std::endl; + } + else { // Output for 64-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 17 ) << str_section_type( sec->get_type() ) << " " + << DUMP_HEX0x_FORMAT( 16 ) << sec->get_address() << " " + << DUMP_HEX0x_FORMAT( 16 ) << sec->get_size() << " " + << DUMP_HEX0x_FORMAT( 8 ) << sec->get_offset() << " " + << DUMP_STR_FORMAT( 3) << section_flags( sec->get_flags() ) + << std::endl + << DUMP_STR_FORMAT( 8 ) << " " + << DUMP_HEX0x_FORMAT( 4 ) << sec->get_entry_size() << " " + << DUMP_HEX0x_FORMAT( 4 ) << sec->get_link() << " " + << DUMP_HEX0x_FORMAT( 4 ) << sec->get_info() << " " + << DUMP_HEX0x_FORMAT( 4 ) << sec->get_addr_align() << " " + << DUMP_STR_FORMAT( 17 ) << sec->get_name() + << std::endl; + } + // clang-format on + + out.flags( original_flags ); + + return; + } + + //------------------------------------------------------------------------------ + static void segment_headers( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.segments.size(); + if ( n == 0 ) { + return; + } + + out << "Program Headers:" << std::endl; + if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit + out << "[ Nr ] Type VirtAddr PhysAddr FileSize " + "Mem.Size Flags Align" + << std::endl; + } + else { // Output for 64-bit + out << "[ Nr ] Type Offset VirtAddr " + " PhysAddr" + + << std::endl + << " FileSize MemSize " + " Flags Align" + << std::endl; + } + + for ( Elf_Half i = 0; i < n; ++i ) { + const segment* seg = reader.segments[i]; + segment_header( out, i, seg, reader.get_class() ); + } + + out << std::endl; + } + + //------------------------------------------------------------------------------ + static void segment_header( std::ostream& out, + Elf_Half no, + const segment* seg, + unsigned int elf_class ) + { + std::ios_base::fmtflags original_flags = out.flags(); + // clang-format off + if ( elf_class == ELFCLASS32 ) { // Output for 32-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 14 ) << str_segment_type( seg->get_type() ) + << " " << DUMP_HEX0x_FORMAT( 8 ) << seg->get_virtual_address() + << " " << DUMP_HEX0x_FORMAT( 8 ) << seg->get_physical_address() + << " " << DUMP_HEX0x_FORMAT( 8 ) << seg->get_file_size() << " " + << DUMP_HEX0x_FORMAT( 8 ) << seg->get_memory_size() << " " + << DUMP_STR_FORMAT( 8 ) << str_segment_flag( seg->get_flags() ) + << " " << DUMP_HEX0x_FORMAT( 8 ) << seg->get_align() << " " + << std::endl; + } + else { // Output for 64-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 14 ) << str_segment_type( seg->get_type() ) << " " + << DUMP_HEX0x_FORMAT( 16 ) << seg->get_offset() << " " + << DUMP_HEX0x_FORMAT( 16 ) << seg->get_virtual_address() << " " + << DUMP_HEX0x_FORMAT( 16 ) << seg->get_physical_address() + << std::endl + << DUMP_STR_FORMAT( 23 ) << " " + << DUMP_HEX0x_FORMAT( 16 ) << seg->get_file_size() << " " + << DUMP_HEX0x_FORMAT( 16 ) << seg->get_memory_size() << " " + << DUMP_STR_FORMAT( 3 ) << str_segment_flag( seg->get_flags() ) << " " + << DUMP_HEX0x_FORMAT( 1 ) << seg->get_align() + << std::endl; + } + // clang-format on + + out.flags( original_flags ); + } + + //------------------------------------------------------------------------------ + static void symbol_tables( std::ostream& out, const elfio& reader ) + { + for ( const auto& sec : reader.sections ) { // For all sections + if ( SHT_SYMTAB == sec->get_type() || + SHT_DYNSYM == sec->get_type() ) { + const_symbol_section_accessor symbols( reader, sec.get() ); + + Elf_Xword sym_no = symbols.get_symbols_num(); + if ( sym_no == 0 ) { + continue; + } + + out << "Symbol table (" << sec->get_name() << ")" << std::endl; + if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit + out << "[ Nr ] Value Size Type Bind " + " Sect Name" + << std::endl; + } + else { // Output for 64-bit + out << "[ Nr ] Value Size " + "Type Bind Sect" + << std::endl + << " Name" << std::endl; + } + for ( Elf_Xword i = 0; i < sym_no; ++i ) { + std::string name; + Elf64_Addr value = 0; + Elf_Xword size = 0; + unsigned char bind = 0; + unsigned char type = 0; + Elf_Half section = 0; + unsigned char other = 0; + symbols.get_symbol( i, name, value, size, bind, type, + section, other ); + symbol_table( out, i, name, value, size, bind, type, + section, reader.get_class() ); + } + + out << std::endl; + } + } + } + + //------------------------------------------------------------------------------ + static void symbol_table( std::ostream& out, + Elf_Xword no, + const std::string& name, + Elf64_Addr value, + Elf_Xword size, + unsigned char bind, + unsigned char type, + Elf_Half section, + unsigned int elf_class ) + { + std::ios_base::fmtflags original_flags = out.flags(); + + if ( elf_class == ELFCLASS32 ) { // Output for 32-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_HEX0x_FORMAT( 8 ) << value << " " + << DUMP_HEX0x_FORMAT( 8 ) << size << " " << DUMP_STR_FORMAT( 7 ) + << str_symbol_type( type ) << " " << DUMP_STR_FORMAT( 8 ) + << str_symbol_bind( bind ) << " " << DUMP_DEC_FORMAT( 5 ) + << section << " " << DUMP_STR_FORMAT( 1 ) << name << " " + << std::endl; + } + else { // Output for 64-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_HEX0x_FORMAT( 16 ) << value << " " + << DUMP_HEX0x_FORMAT( 16 ) << size << " " + << DUMP_STR_FORMAT( 7 ) << str_symbol_type( type ) << " " + << DUMP_STR_FORMAT( 8 ) << str_symbol_bind( bind ) << " " + << DUMP_DEC_FORMAT( 5 ) << section << " " << std::endl + << " " << DUMP_STR_FORMAT( 1 ) << name << " " + << std::endl; + } + + out.flags( original_flags ); + } + + //------------------------------------------------------------------------------ + static void notes( std::ostream& out, const elfio& reader ) + { + for ( const auto& sec : reader.sections ) { // For all sections + if ( SHT_NOTE == sec->get_type() ) { // Look at notes + note_section_accessor notes( reader, sec.get() ); + Elf_Word no_notes = notes.get_notes_num(); + + if ( no_notes == 0 ) + continue; + + out << "Note section (" << sec->get_name() << ")" << std::endl + << " No Name Data size Description" + << std::endl; + for ( Elf_Word j = 0; j < no_notes; ++j ) { // For all notes + Elf_Word type; + std::string name; + char* desc; + Elf_Word descsz; + + if ( notes.get_note( j, type, name, desc, descsz ) ) { + // 'name' usually contains \0 at the end. Remove it + name = name.c_str(); + note( out, j, type, name, desc, descsz ); + out << std::endl; + } + } + + out << std::endl; + } + } + + Elf_Half no = reader.segments.size(); + for ( Elf_Half i = 0; i < no; ++i ) { // For all segments + segment* seg = reader.segments[i]; + if ( PT_NOTE == seg->get_type() ) { // Look at notes + note_segment_accessor notes( reader, seg ); + Elf_Word no_notes = notes.get_notes_num(); + + if ( no_notes == 0 ) + continue; + + out << "Note segment (" << i << ")" << std::endl + << " No Name Data size Description" + << std::endl; + for ( Elf_Word j = 0; j < no_notes; ++j ) { // For all notes + Elf_Word type; + std::string name; + char* desc; + Elf_Word descsz; + + if ( notes.get_note( j, type, name, desc, descsz ) ) { + // 'name' usually contains \0 at the end. Remove it + name = name.c_str(); + note( out, j, type, name, desc, descsz ); + out << std::endl; + } + } + + out << std::endl; + } + } + } + + //------------------------------------------------------------------------------ + static void note( std::ostream& out, + int no, + Elf_Word type, + const std::string& name, + void* desc, + Elf_Word descsz ) + { + out << " [" << DUMP_DEC_FORMAT( 2 ) << no << "] "; + + const auto name_group = std::find_if( + std::begin( note_tag_table ), std::end( note_tag_table ), + [&name]( const note_tag_t& entry ) { return entry.name == name; } ); + + std::vector::const_iterator type_value; + if ( name_group != std::end( note_tag_table ) ) { + type_value = std::find_if( + name_group->values.begin(), name_group->values.end(), + [&type]( const note_tag_t::note_values_t& e ) { + return e.type == type; + } ); + } + + if ( name_group != std::end( note_tag_table ) && + type_value != name_group->values.end() ) { + out << DUMP_STR_FORMAT( 12 ) << name_group->name << " " + << DUMP_HEX0x_FORMAT( 8 ) << descsz << " " + << type_value->type_str << " (" << type_value->description + << ")"; + } + else { + out << DUMP_STR_FORMAT( 12 ) << name << " " + << DUMP_HEX0x_FORMAT( 8 ) << descsz << " " + << DUMP_HEX0x_FORMAT( 8 ) << type; + } + + if ( descsz != 0 ) { + for ( Elf_Word i = 0; i < descsz; ++i ) { + if ( i % 16 == 0 ) { + out << std::endl << " "; + } + out << DUMP_HEX_FORMAT( 2 ) + << (uint32_t)( (uint8_t*)( desc ) )[i]; + } + } + } + + //------------------------------------------------------------------------------ + static void modinfo( std::ostream& out, const elfio& reader ) + { + for ( const auto& sec : reader.sections ) { // For all sections + if ( ".modinfo" == sec->get_name() ) { // Look for the section + out << "Section .modinfo" << std::endl; + + const_modinfo_section_accessor modinfo( sec.get() ); + for ( Elf_Word i = 0; i < modinfo.get_attribute_num(); i++ ) { + std::string field; + std::string value; + if ( modinfo.get_attribute( i, field, value ) ) { + out << " " << std::setw( 20 ) << field + << std::setw( 0 ) << " = " << value << std::endl; + } + } + + out << std::endl; + break; + } + } + } + + //------------------------------------------------------------------------------ + static void dynamic_tags( std::ostream& out, const elfio& reader ) + { + for ( const auto& sec : reader.sections ) { // For all sections + if ( SHT_DYNAMIC == sec->get_type() ) { + dynamic_section_accessor dynamic( reader, sec.get() ); + + Elf_Xword dyn_no = dynamic.get_entries_num(); + if ( dyn_no == 0 ) + continue; + + out << "Dynamic section (" << sec->get_name() << ")" + << std::endl; + out << "[ Nr ] Tag Name/Value" << std::endl; + for ( Elf_Xword i = 0; i < dyn_no; ++i ) { + Elf_Xword tag = 0; + Elf_Xword value = 0; + std::string str; + dynamic.get_entry( i, tag, value, str ); + dynamic_tag( out, i, tag, value, str, reader.get_class() ); + if ( DT_NULL == tag ) { + break; + } + } + + out << std::endl; + } + } + } + + //------------------------------------------------------------------------------ + static void dynamic_tag( std::ostream& out, + Elf_Xword no, + Elf_Xword tag, + Elf_Xword value, + const std::string& str, + unsigned int /*elf_class*/ ) + { + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 16 ) << str_dynamic_tag( tag ) << " "; + if ( str.empty() ) { + out << DUMP_HEX0x_FORMAT( 16 ) << value << " "; + } + else { + out << DUMP_STR_FORMAT( 32 ) << str << " "; + } + out << std::endl; + } + + //------------------------------------------------------------------------------ + static void section_data( std::ostream& out, const section* sec ) + { + std::ios_base::fmtflags original_flags = out.flags(); + + out << sec->get_name() << std::endl; + const char* pdata = sec->get_data(); + if ( pdata ) { + ELFIO::Elf_Xword i; + for ( i = 0; i < std::min( sec->get_size(), MAX_DATA_ENTRIES ); + ++i ) { + if ( i % 16 == 0 ) { + out << "[" << DUMP_HEX0x_FORMAT( 8 ) << i << "]"; + } + + out << " " << DUMP_HEX0x_FORMAT( 2 ) + << ( pdata[i] & 0x000000FF ); + + if ( i % 16 == 15 ) { + out << std::endl; + } + } + if ( i % 16 != 0 ) { + out << std::endl; + } + + out.flags( original_flags ); + } + + return; + } + + //------------------------------------------------------------------------------ + static void section_datas( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.sections.size(); + + if ( n == 0 ) { + return; + } + + out << "Section Data:" << std::endl; + + for ( Elf_Half i = 1; i < n; ++i ) { // For all sections + const section* sec = reader.sections[i]; + if ( sec->get_type() == SHT_NOBITS ) { + continue; + } + section_data( out, sec ); + } + + out << std::endl; + } + + //------------------------------------------------------------------------------ + static void + segment_data( std::ostream& out, Elf_Half no, const segment* seg ) + { + std::ios_base::fmtflags original_flags = out.flags(); + + out << "Segment # " << no << std::endl; + const char* pdata = seg->get_data(); + if ( pdata ) { + ELFIO::Elf_Xword i; + for ( i = 0; i < std::min( seg->get_file_size(), MAX_DATA_ENTRIES ); + ++i ) { + if ( i % 16 == 0 ) { + out << "[" << DUMP_HEX0x_FORMAT( 8 ) << i << "]"; + } + + out << " " << DUMP_HEX0x_FORMAT( 2 ) + << ( pdata[i] & 0x000000FF ); + + if ( i % 16 == 15 ) { + out << std::endl; + } + } + if ( i % 16 != 0 ) { + out << std::endl; + } + + out.flags( original_flags ); + } + + return; + } + + //------------------------------------------------------------------------------ + static void segment_datas( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.segments.size(); + + if ( n == 0 ) { + return; + } + + out << "Segment Data:" << std::endl; + + for ( Elf_Half i = 0; i < n; ++i ) { // For all sections + const segment* seg = reader.segments[i]; + segment_data( out, i, seg ); + } + + out << std::endl; + } + +//------------------------------------------------------------------------------ +#define STR_FUNC_TABLE( name ) \ + template static std::string str_##name( const T key ) \ + { \ + return format_assoc( name##_table, key ); \ + } + + STR_FUNC_TABLE( class ) + STR_FUNC_TABLE( endian ) + STR_FUNC_TABLE( version ) + STR_FUNC_TABLE( os_abi ) + STR_FUNC_TABLE( type ) + STR_FUNC_TABLE( machine ) + STR_FUNC_TABLE( section_type ) + STR_FUNC_TABLE( segment_type ) + STR_FUNC_TABLE( segment_flag ) + STR_FUNC_TABLE( symbol_bind ) + STR_FUNC_TABLE( symbol_type ) + STR_FUNC_TABLE( dynamic_tag ) + +#undef STR_FUNC_TABLE + + private: + //------------------------------------------------------------------------------ + template + std::string static find_value_in_table( const T& table, const K& key ) + { + std::string res = "?"; + for ( unsigned int i = 0; i < sizeof( table ) / sizeof( table[0] ); + ++i ) { + if ( table[i].key == key ) { + res = table[i].str; + break; + } + } + + return res; + } + + //------------------------------------------------------------------------------ + template + static std::string format_assoc( const T& table, const K& key ) + { + std::string str = find_value_in_table( table, key ); + if ( str == "?" ) { + std::ostringstream oss; + oss << str << " (0x" << std::hex << key << ")"; + str = oss.str(); + } + + return str; + } + + //------------------------------------------------------------------------------ + template + static std::string format_assoc( const T& table, const char key ) + { + return format_assoc( table, (const int)key ); + } + + //------------------------------------------------------------------------------ + static std::string section_flags( Elf_Xword flags ) + { + std::string ret = ""; + if ( flags & SHF_WRITE ) { + ret += "W"; + } + if ( flags & SHF_ALLOC ) { + ret += "A"; + } + if ( flags & SHF_EXECINSTR ) { + ret += "X"; + } + if ( flags & SHF_MERGE ) { + ret += "M"; + } + if ( flags & SHF_STRINGS ) { + ret += "S"; + } + if ( flags & SHF_INFO_LINK ) { + ret += "I"; + } + if ( flags & SHF_LINK_ORDER ) { + ret += "L"; + } + if ( flags & SHF_OS_NONCONFORMING ) { + ret += "O"; + } + if ( flags & SHF_GROUP ) { + ret += "G"; + } + if ( flags & SHF_TLS ) { + ret += "T"; + } + if ( flags & SHF_COMPRESSED ) { + ret += "C"; + } + if ( flags & SHF_EXCLUDE ) { + ret += "E"; + } + if ( flags & SHF_GNU_MBIND ) { + ret += "D"; + } + + return ret; + } + +#undef DUMP_DEC_FORMAT +#undef DUMP_HEX0x_FORMAT +#undef DUMP_STR_FORMAT +}; // class dump +} // namespace ELFIO + +#endif // ELFIO_DUMP_HPP diff --git a/include/elfio/elfio_dynamic.hpp b/include/elfio/elfio_dynamic.hpp new file mode 100644 index 0000000..035e67d --- /dev/null +++ b/include/elfio/elfio_dynamic.hpp @@ -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 + +namespace ELFIO { + +//------------------------------------------------------------------------------ +template 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( 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( index, tag, value ); + } + else { + generic_get_entry_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( tag, value ); + } + else { + generic_add_entry_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 + 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( + 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 + 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( &entry ), + sizeof( entry ) ); + } + + //------------------------------------------------------------------------------ + private: + const elfio& elf_file; + S* dynamic_section; + mutable Elf_Xword entries_num; +}; + +using dynamic_section_accessor = dynamic_section_accessor_template
; +using const_dynamic_section_accessor = + dynamic_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_DYNAMIC_HPP diff --git a/include/elfio/elfio_header.hpp b/include/elfio/elfio_header.hpp new file mode 100644 index 0000000..eccae13 --- /dev/null +++ b/include/elfio/elfio_header.hpp @@ -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 + +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 struct elf_header_impl_types; +template <> struct elf_header_impl_types +{ + using Phdr_type = Elf32_Phdr; + using Shdr_type = Elf32_Shdr; + static const unsigned char file_class = ELFCLASS32; +}; +template <> struct elf_header_impl_types +{ + using Phdr_type = Elf64_Phdr; + using Shdr_type = Elf64_Shdr; + static const unsigned char file_class = ELFCLASS64; +}; + +template 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::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::Phdr_type ); + header.e_shentsize = + sizeof( typename elf_header_impl_types::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( &header ), sizeof( header ) ); + + return ( stream.gcount() == sizeof( header ) ); + } + + //------------------------------------------------------------------------------ + bool save( std::ostream& stream ) const override + { + stream.seekp( ( *translator )[0] ); + stream.write( reinterpret_cast( &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 diff --git a/include/elfio/elfio_modinfo.hpp b/include/elfio/elfio_modinfo.hpp new file mode 100644 index 0000000..2d42ba3 --- /dev/null +++ b/include/elfio/elfio_modinfo.hpp @@ -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 +#include + +namespace ELFIO { + +//------------------------------------------------------------------------------ +template 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> content; +}; + +using modinfo_section_accessor = modinfo_section_accessor_template
; +using const_modinfo_section_accessor = + modinfo_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_MODINFO_HPP diff --git a/include/elfio/elfio_note.hpp b/include/elfio/elfio_note.hpp new file mode 100644 index 0000000..63ef102 --- /dev/null +++ b/include/elfio/elfio_note.hpp @@ -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 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( 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( &nameLenConv ), align ); + Elf_Word descSizeConv = convertor( descSize ); + + buffer.append( reinterpret_cast( &descSizeConv ), align ); + type = convertor( type ); + buffer.append( reinterpret_cast( &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 note_start_positions; +}; + +using note_section_accessor = + note_section_accessor_template; +using const_note_section_accessor = + note_section_accessor_template; +using note_segment_accessor = + note_section_accessor_template; +using const_note_segment_accessor = + note_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_NOTE_HPP diff --git a/include/elfio/elfio_relocation.hpp b/include/elfio/elfio_relocation.hpp new file mode 100644 index 0000000..2f39a36 --- /dev/null +++ b/include/elfio/elfio_relocation.hpp @@ -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 struct get_sym_and_type; +template <> struct get_sym_and_type +{ + 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 +{ + 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 +{ + 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 +{ + 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 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( index, offset, symbol, type, + addend ); + } + else if ( SHT_RELA == relocation_section->get_type() ) { + generic_get_entry_rela( index, offset, symbol, type, + addend ); + } + } + else { + if ( SHT_REL == relocation_section->get_type() ) { + generic_get_entry_rel( index, offset, symbol, type, + addend ); + } + else if ( SHT_RELA == relocation_section->get_type() ) { + generic_get_entry_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( index, offset, symbol, type, + addend ); + } + else if ( SHT_RELA == relocation_section->get_type() ) { + generic_set_entry_rela( index, offset, symbol, type, + addend ); + } + } + else { + if ( SHT_REL == relocation_section->get_type() ) { + generic_set_entry_rel( index, offset, symbol, type, + addend ); + } + else if ( SHT_RELA == relocation_section->get_type() ) { + generic_set_entry_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( offset, info ); + } + else { + generic_add_entry( 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( offset, info, addend ); + } + else { + generic_add_entry( 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 + 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( + 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::get_r_sym( tmp ); + type = get_sym_and_type::get_r_type( tmp ); + addend = 0; + } + + //------------------------------------------------------------------------------ + template + 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( + 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::get_r_sym( tmp ); + type = get_sym_and_type::get_r_type( tmp ); + addend = convertor( pEntry->r_addend ); + } + + //------------------------------------------------------------------------------ + template + 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( reinterpret_cast( + 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 + 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( reinterpret_cast( + 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 + 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( &entry ), + sizeof( entry ) ); + } + + //------------------------------------------------------------------------------ + template + 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( &entry ), + sizeof( entry ) ); + } + + //------------------------------------------------------------------------------ + private: + const elfio& elf_file; + S* relocation_section = nullptr; +}; + +using relocation_section_accessor = + relocation_section_accessor_template
; +using const_relocation_section_accessor = + relocation_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_RELOCATION_HPP diff --git a/include/elfio/elfio_section.hpp b/include/elfio/elfio_section.hpp new file mode 100644 index 0000000..8c6aed4 --- /dev/null +++ b/include/elfio/elfio_section.hpp @@ -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 +#include +#include +#include + +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 section_impl : public section +{ + public: + //------------------------------------------------------------------------------ + section_impl( const endianess_convertor* convertor, + const address_translator* translator, + const std::shared_ptr& 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( 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 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::max() ); + } + + stream.seekg( ( *translator )[header_offset] ); + stream.read( reinterpret_cast( &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( 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( &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 data; + mutable Elf_Word data_size = 0; + const endianess_convertor* convertor = nullptr; + const address_translator* translator = nullptr; + const std::shared_ptr compression = nullptr; + bool is_address_set = false; + size_t stream_size = 0; + mutable bool is_lazy = false; +}; + +} // namespace ELFIO + +#endif // ELFIO_SECTION_HPP diff --git a/include/elfio/elfio_segment.hpp b/include/elfio/elfio_segment.hpp new file mode 100644 index 0000000..6b0f81d --- /dev/null +++ b/include/elfio/elfio_segment.hpp @@ -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 +#include +#include +#include + +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& 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 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& 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::max() ); + } + + stream.seekg( ( *translator )[header_offset] ); + stream.read( reinterpret_cast( &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( &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 data; + std::vector 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 diff --git a/include/elfio/elfio_strings.hpp b/include/elfio/elfio_strings.hpp new file mode 100644 index 0000000..09f5729 --- /dev/null +++ b/include/elfio/elfio_strings.hpp @@ -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 +#include +#include + +namespace ELFIO { + +//------------------------------------------------------------------------------ +template 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( 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( 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
; +using const_string_section_accessor = + string_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_STRINGS_HPP diff --git a/include/elfio/elfio_symbols.hpp b/include/elfio/elfio_symbols.hpp new file mode 100644 index 0000000..d868500 --- /dev/null +++ b/include/elfio/elfio_symbols.hpp @@ -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 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( index, name, value, size, bind, + type, section_index, other ); + } + else { + ret = generic_get_symbol( 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( + name, value, size, bind, type, section_index, other ); + } + else { + ret = gnu_hash_lookup( + 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( + [&]( const Elf32_Sym* sym ) { + return convertor( sym->st_value ) == value; + }, + idx ); + } + else { + match = generic_search_symbols( + [&]( 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( 0, 0, 0, 0, 0, 0 ); + } + else { + nRet = generic_add_symbol( 0, 0, 0, 0, 0, 0 ); + } + } + + if ( elf_file.get_class() == ELFCLASS32 ) { + nRet = generic_add_symbol( name, value, size, info, + other, shndx ); + } + else { + nRet = generic_add_symbol( 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 func = + nullptr ) + { + Elf_Xword nRet = 0; + + if ( elf_file.get_class() == ELFCLASS32 ) { + nRet = generic_arrange_local_symbols( func ); + } + else { + nRet = generic_arrange_local_symbols( 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 + 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 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( + symbol_section->get_data() + + index * symbol_section->get_entry_size() ); + + return pSym; + } + + return nullptr; + } + + //------------------------------------------------------------------------------ + template + bool generic_search_symbols( std::function match, + Elf_Xword& idx ) const + { + for ( Elf_Xword i = 0; i < get_symbols_num(); i++ ) { + const T* symPtr = generic_get_symbol_ptr( i ); + + if ( symPtr == nullptr ) + return false; + + if ( match( symPtr ) ) { + idx = i; + return true; + } + } + + return false; + } + + //------------------------------------------------------------------------------ + template + 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( + 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 + 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( &entry ), + sizeof( entry ) ); + + Elf_Word nRet = + Elf_Word( symbol_section->get_size() / sizeof( entry ) - 1 ); + + return nRet; + } + + //------------------------------------------------------------------------------ + template + Elf_Xword generic_arrange_local_symbols( + std::function 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( + generic_get_symbol_ptr( 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( generic_get_symbol_ptr( 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
; +using const_symbol_section_accessor = + symbol_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_SYMBOLS_HPP diff --git a/include/elfio/elfio_utils.hpp b/include/elfio/elfio_utils.hpp new file mode 100644 index 0000000..ecb0961 --- /dev/null +++ b/include/elfio/elfio_utils.hpp @@ -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 +#include + +#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( &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& 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 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 + 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 + deflate( const char* data, + const endianess_convertor* convertor, + Elf_Xword decompressed_size, + Elf_Xword& compressed_size ) const = 0; +}; + +} // namespace ELFIO + +#endif // ELFIO_UTILS_HPP diff --git a/include/elfio/elfio_version.hpp b/include/elfio/elfio_version.hpp new file mode 100644 index 0000000..5b62d0d --- /dev/null +++ b/include/elfio/elfio_version.hpp @@ -0,0 +1 @@ +#define ELFIO_VERSION "3.12" diff --git a/include/elfio/elfio_versym.hpp b/include/elfio/elfio_versym.hpp new file mode 100644 index 0000000..9971caf --- /dev/null +++ b/include/elfio/elfio_versym.hpp @@ -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 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
; +using const_versym_section_accessor = + versym_section_accessor_template; + +//------------------------------------------------------------------------------ +template 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
; +using const_versym_r_section_accessor = + versym_r_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_VERSYM_HPP diff --git a/include/elna/backend/riscv.hpp b/include/elna/backend/riscv.hpp new file mode 100644 index 0000000..cb5a68b --- /dev/null +++ b/include/elna/backend/riscv.hpp @@ -0,0 +1,163 @@ +#pragma once + +#include +#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 generate(source::intermediate_code_generator generator, + std::shared_ptr table, std::shared_ptr> writer); +} diff --git a/include/elna/backend/target.hpp b/include/elna/backend/target.hpp new file mode 100644 index 0000000..2b3a1af --- /dev/null +++ b/include/elna/backend/target.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include "elna/source/parser.hpp" +#include "elna/source/optimizer.hpp" +#include +#include + +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::const_iterator labels; + std::vector::const_iterator sizes; + value_type payload; + + iterator(std::vector::const_iterator labels, std::vector::const_iterator sizes, + const std::byte *data) + : labels(labels), sizes(sizes) + { + if (data != nullptr) + { + payload = { *this->labels, data, *this->sizes}; + } + } + + iterator(std::vector::const_iterator labels, std::vector::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 operator()(const std::byte *data, std::size_t size); + + iterator begin() const; + iterator end() const; + + ELFIO::section *section() noexcept; + + private: + std::vector labels; + std::vector sizes; + ELFIO::section *m_section; + }; + + class elfio_writer final : public source::writer + { + 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 table, const std::filesystem::path& out_file); +} diff --git a/include/elna/cli/cl.hpp b/include/elna/cli/cl.hpp new file mode 100644 index 0000000..89492e2 --- /dev/null +++ b/include/elna/cli/cl.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#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& 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 + 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); +} diff --git a/include/elna/source/lexer.hpp b/include/elna/source/lexer.hpp new file mode 100644 index 0000000..bcf288a --- /dev/null +++ b/include/elna/source/lexer.hpp @@ -0,0 +1,212 @@ +#pragma once + +#include +#include +#include +#include +#include +#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&& 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> 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>& errors() const noexcept; + + private: + std::vector tokens; + std::vector::const_iterator iterator; + std::list> 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 tokenize(const std::filesystem::path& path); +} diff --git a/include/elna/source/optimizer.hpp b/include/elna/source/optimizer.hpp new file mode 100644 index 0000000..e276c0b --- /dev/null +++ b/include/elna/source/optimizer.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include +#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 operand1 = nullptr, + std::shared_ptr operand2 = nullptr, std::shared_ptr operand3 = nullptr); + + quadruple_operator operation() const noexcept; + std::shared_ptr operand1(); + std::shared_ptr operand2(); + std::shared_ptr operand3(); + + private: + quadruple_operator m_operation; + std::shared_ptr m_operand1; + std::shared_ptr m_operand2; + std::shared_ptr m_operand3; + }; + + class intermediate_code final + { + std::vector 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 operand1 = nullptr, + std::shared_ptr operand2 = nullptr, std::shared_ptr operand3 = nullptr); + void clear(); + + std::vector::iterator begin(); + std::vector::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 code; + intermediate_code current; + std::shared_ptr table; + + quadruple_operator convert(const binary_operator operation) const; + quadruple_operator convert(const unary_operator operation) const; + + public: + intermediate_code_generator(std::shared_ptr table); + + std::unordered_map::iterator begin(); + std::unordered_map::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; + }; +} diff --git a/include/elna/source/parser.hpp b/include/elna/source/parser.hpp new file mode 100644 index 0000000..29c901c --- /dev/null +++ b/include/elna/source/parser.hpp @@ -0,0 +1,520 @@ +#pragma once + +#include +#include +#include + +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 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 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); + virtual void accept(parser_visitor *visitor) override; + + type_expression& type() noexcept; + }; + + /** + * Constant definition. + */ + class constant_definition : public definition + { + std::unique_ptr 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&& body); + virtual void accept(parser_visitor *visitor) override; + + integer_literal& body(); + }; + + /** + * Procedure definition. + */ + class procedure_definition : public definition + { + std::unique_ptr m_body; + std::vector> 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&& body); + virtual void accept(parser_visitor *visitor) override; + + block& body(); + std::vector>& parameters() noexcept; + }; + + /** + * Call statement. + */ + class call_statement : public statement + { + std::string m_name; + std::vector> 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>& arguments() noexcept; + }; + + class compound_statement : public statement + { + std::vector> m_statements; + + public: + explicit compound_statement(const struct position position); + virtual void accept(parser_visitor *visitor) override; + + std::vector>& statements(); + }; + + class assign_statement : public statement + { + std::string m_lvalue; + std::unique_ptr 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&& rvalue); + virtual void accept(parser_visitor *visitor) override; + + std::string& lvalue() noexcept; + expression& rvalue(); + }; + + /** + * If-statement. + */ + class if_statement : public statement + { + std::unique_ptr m_prerequisite; + std::unique_ptr 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&& prerequisite, + std::unique_ptr&& body); + virtual void accept(parser_visitor *visitor) override; + + expression& prerequisite(); + statement& body(); + }; + + /** + * While-statement. + */ + class while_statement : public statement + { + std::unique_ptr m_prerequisite; + std::unique_ptr 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&& prerequisite, + std::unique_ptr&& body); + virtual void accept(parser_visitor *visitor) override; + + expression& prerequisite(); + statement& body(); + }; + + class block : public node + { + std::unique_ptr m_body; + std::vector> m_definitions; + std::vector> m_declarations; + + public: + block(const struct position position, std::vector>&& definitions, + std::vector>&& declarations, + std::unique_ptr&& body); + virtual void accept(parser_visitor *visitor) override; + + statement& body(); + std::vector>& definitions() noexcept; + std::vector>& declarations() noexcept; + }; + + class program : public block + { + public: + program(const struct position position, std::vector>&& definitions, + std::vector>&& declarations, + std::unique_ptr&& 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 m_lhs; + std::unique_ptr m_rhs; + binary_operator m_operator; + + public: + binary_expression(const struct position position, std::unique_ptr&& lhs, + std::unique_ptr&& 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 m_operand; + unary_operator m_operator; + + public: + unary_expression(const struct position position, std::unique_ptr&& 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 parse_unary_expression(); + std::unique_ptr parse_factor(); + std::unique_ptr parse_term(); + std::unique_ptr parse_expression(); + std::unique_ptr parse_condition(); + std::unique_ptr parse_constant_definition(); + std::unique_ptr parse_procedure_definition(); + std::unique_ptr parse_type_expression(); + std::unique_ptr parse_declaration(); + std::unique_ptr parse_statement(); + std::unique_ptr parse_call_statement(); + std::unique_ptr parse_compound_statement(); + std::unique_ptr parse_assign_statement(); + std::unique_ptr parse_if_statement(); + std::unique_ptr parse_while_statement(); + std::vector> parse_constant_definitions(); + std::vector> parse_procedure_definitions(); + std::vector> parse_declarations(); + std::unique_ptr parse_block(); + + lexer iterator; + + public: + parser(lexer&& tokens); + + /** + * Parses a source text. + * + * \return Parsed program or nothing if an error occurred. + */ + std::unique_ptr parse(); + + /** + * Gets produced errors. + * + * \return Produced error list. + */ + const std::list>& errors() const noexcept; + }; +} diff --git a/include/elna/source/result.hpp b/include/elna/source/result.hpp new file mode 100644 index 0000000..4ba775a --- /dev/null +++ b/include/elna/source/result.hpp @@ -0,0 +1,160 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +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 + struct result + { + using E = std::list>; + + template>> + explicit result(std::in_place_t, Args&&... arguments) + : payload(std::in_place_type, std::forward(arguments)...) + { + } + + explicit result(T&& result) + : payload(std::move(result)) + { + } + + template>> + explicit result(U&& first) + : payload(E()) + { + errors().emplace_back(std::make_unique(first)); + } + + explicit result(E&& errors) + : payload(std::move(errors)) + { + } + + bool has_errors() const noexcept + { + return std::holds_alternative(payload); + } + + bool is_success() const noexcept + { + return std::holds_alternative(payload); + } + + T& success() + { + return std::get(payload); + } + + E& errors() + { + return std::get(payload); + } + + private: + std::variant 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 + 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; + }; +} diff --git a/include/elna/source/semantic.hpp b/include/elna/source/semantic.hpp new file mode 100644 index 0000000..6e6e42b --- /dev/null +++ b/include/elna/source/semantic.hpp @@ -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 table = std::make_shared(); + + std::shared_ptr convert_declaration_type(const type_expression& ast_type) const; + + public: + name_analysis_visitor(std::shared_ptr 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 table; + + public: + allocator_visitor(std::shared_ptr 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 + { + }; +} diff --git a/include/elna/source/symbol_table.hpp b/include/elna/source/symbol_table.hpp new file mode 100644 index 0000000..73b4110 --- /dev/null +++ b/include/elna/source/symbol_table.hpp @@ -0,0 +1,151 @@ +#pragma once + +#include +#include +#include +#include +#include + +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 m_type; + + public: + explicit type_info(const class type& type); + ~type_info() override; + std::shared_ptr 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 m_type; + + public: + std::ptrdiff_t offset{ 0 }; + + explicit variable_info(std::shared_ptr type); + ~variable_info() override; + std::shared_ptr type() noexcept; + }; + + /** + * Procedure parameter information. + */ + class parameter_info final : public info + { + std::shared_ptr m_type; + + public: + std::ptrdiff_t offset{ 0 }; + + explicit parameter_info(std::shared_ptr type); + ~parameter_info() override; + std::shared_ptr type() const noexcept; + }; + + /** + * Intrinsic and external procedure information. + */ + class intrinsic_info : public info + { + public: + std::vector 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 local_table; + + public: + std::size_t local_stack_size{ 0 }; + std::size_t argument_stack_size{ 0 }; + + explicit procedure_info(std::shared_ptr outer_scope); + ~procedure_info() override; + + std::shared_ptr scope(); + std::size_t stack_size() const noexcept; + }; + + /** + * Symbol table. + */ + class symbol_table + { + std::unordered_map> entries; + std::shared_ptr outer_scope; + + public: + /** + * Constructs a new symbol with an optional outer scope. + * + * \param scope Outer scope. + */ + explicit symbol_table(std::shared_ptr 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 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 entry); + + /** + * Returns the outer scope or nullptr if the this is the global scope. + * + * \return Outer scope. + */ + std::shared_ptr scope(); + }; +} diff --git a/include/elna/source/types.hpp b/include/elna/source/types.hpp new file mode 100644 index 0000000..2e7043d --- /dev/null +++ b/include/elna/source/types.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +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 base_type; + + pointer_type(std::shared_ptr base_type, const std::size_t byte_size); + }; + + inline const primitive_type boolean_type{ "Boolean", 1 }; + inline const primitive_type int_type{ "Int", 4 }; +} diff --git a/include/elna/tester.hpp b/include/elna/tester.hpp new file mode 100644 index 0000000..e483ce5 --- /dev/null +++ b/include/elna/tester.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include + +#define BOOST_PROCESS_USE_STD_FS + +#include +#include +#include + +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 arguments); + std::string find_object(const std::vector& 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); +} diff --git a/source/elna/extended.d b/source/elna/extended.d deleted file mode 100644 index 0473cee..0000000 --- a/source/elna/extended.d +++ /dev/null @@ -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); -} diff --git a/source/elna/generator.d b/source/elna/generator.d deleted file mode 100644 index ce2b3d1..0000000 --- a/source/elna/generator.d +++ /dev/null @@ -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; -} diff --git a/source/elna/ir.d b/source/elna/ir.d deleted file mode 100644 index e2a8df4..0000000 --- a/source/elna/ir.d +++ /dev/null @@ -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; -} diff --git a/source/elna/lexer.d b/source/elna/lexer.d deleted file mode 100644 index c47aae0..0000000 --- a/source/elna/lexer.d +++ /dev/null @@ -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; -} diff --git a/source/elna/parser.d b/source/elna/parser.d deleted file mode 100644 index 28a090b..0000000 --- a/source/elna/parser.d +++ /dev/null @@ -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); -} diff --git a/source/elna/result.d b/source/elna/result.d deleted file mode 100644 index 049c453..0000000 --- a/source/elna/result.d +++ /dev/null @@ -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; - } -} diff --git a/source/lexer.cpp b/source/lexer.cpp new file mode 100644 index 0000000..cc414de --- /dev/null +++ b/source/lexer.cpp @@ -0,0 +1,325 @@ +#include "elna/source/lexer.hpp" +#include +#include + +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&& 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(expected, this->source_file)); + } + + std::optional> 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>(); + } + + 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>& lexer::errors() const noexcept + { + return m_errors; + } +} diff --git a/source/main.d b/source/main.d deleted file mode 100644 index 71d23cd..0000000 --- a/source/main.d +++ /dev/null @@ -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; -} diff --git a/source/optimizer.cpp b/source/optimizer.cpp new file mode 100644 index 0000000..1463d4d --- /dev/null +++ b/source/optimizer.cpp @@ -0,0 +1,267 @@ +#include "elna/source/optimizer.hpp" +#include + +namespace elna::source +{ + quadruple::quadruple(const quadruple_operator operation, std::shared_ptr operand1, + std::shared_ptr operand2, std::shared_ptr 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 quadruple::operand1() + { + return m_operand1; + } + + std::shared_ptr quadruple::operand2() + { + return m_operand2; + } + + std::shared_ptr quadruple::operand3() + { + return m_operand3; + } + + intermediate_code::intermediate_code() + { + clear(); + } + + void intermediate_code::emplace_back(const quadruple_operator operation, std::shared_ptr operand1, + std::shared_ptr operand2, std::shared_ptr 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::iterator intermediate_code::begin() + { + return this->instructions.begin(); + } + + std::vector::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 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::iterator intermediate_code_generator::begin() + { + return code.begin(); + } + + std::unordered_map::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(statement->name()), + std::make_shared(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(statement->lvalue())); + } + + void intermediate_code_generator::visit(if_statement *statement) + { + statement->prerequisite().accept(this); + + auto end_label = std::make_shared(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(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(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(symbol)) + { + variable->place = std::make_shared(constant_symbol->value()); + } + else + { + variable->place = std::make_shared(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(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(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(number->number()); + } + + void intermediate_code_generator::visit(boolean_literal *number) + { + number->place = std::make_shared(number->boolean()); + } +} diff --git a/source/parser.cpp b/source/parser.cpp new file mode 100644 index 0000000..05ac40d --- /dev/null +++ b/source/parser.cpp @@ -0,0 +1,1063 @@ +#include "elna/source/parser.hpp" +#include + +namespace elna::source +{ + void empty_visitor::visit(declaration *declaration) + { + } + + void empty_visitor::visit(constant_definition *definition) + { + definition->body().accept(this); + } + + void empty_visitor::visit(procedure_definition *definition) + { + for (auto& parameter : definition->parameters()) + { + parameter->accept(this); + } + definition->body().accept(this); + } + + void empty_visitor::visit(call_statement *statement) + { + for (auto& argument : statement->arguments()) + { + argument->accept(this); + } + } + + void empty_visitor::visit(compound_statement *statement) + { + for (auto& nested_statement : statement->statements()) + { + nested_statement->accept(this); + } + } + + void empty_visitor::visit(assign_statement *statement) + { + statement->rvalue().accept(this); + } + + void empty_visitor::visit(if_statement *statement) + { + statement->prerequisite().accept(this); + statement->body().accept(this); + } + + void empty_visitor::visit(while_statement *statement) + { + statement->prerequisite().accept(this); + statement->body().accept(this); + } + + void empty_visitor::visit(block *block) + { + for (const auto& constant : block->definitions()) + { + constant->accept(this); + } + for (const auto& block_declaration : block->declarations()) + { + block_declaration->accept(this); + } + block->body().accept(this); + } + + void empty_visitor::visit(program *program) + { + visit(dynamic_cast(program)); + } + + void empty_visitor::visit(binary_expression *expression) + { + expression->lhs().accept(this); + expression->rhs().accept(this); + } + + void empty_visitor::visit(unary_expression *expression) + { + expression->operand().accept(this); + } + + void empty_visitor::visit(type_expression *variable) + { + } + + void empty_visitor::visit(variable_expression *variable) + { + } + + void empty_visitor::visit(integer_literal *number) + { + } + + void empty_visitor::visit(boolean_literal *boolean) + { + } + + operand::~operand() noexcept + { + } + + integer_operand::integer_operand(const std::int32_t value) + : m_value(value) + { + } + + std::int32_t integer_operand::value() const noexcept + { + return m_value; + } + + variable_operand::variable_operand(const std::string& name) + : m_name(name) + { + } + + const std::string& variable_operand::name() const noexcept + { + return m_name; + } + + temporary_variable::temporary_variable(const std::size_t counter) + : m_counter(counter) + { + } + + std::size_t temporary_variable::counter() const noexcept + { + return m_counter; + } + + label_operand::label_operand(const std::size_t counter) + : m_counter(counter) + { + } + + std::size_t label_operand::counter() const noexcept + { + return m_counter; + } + + node::node(const struct position position) + : source_position(position) + { + } + + const struct position& node::position() const noexcept + { + return this->source_position; + } + + statement::statement(const struct position position) + : node(position) + { + } + + expression::expression(const struct position position) + : node(position) + { + } + + type_expression::type_expression(const struct position position, const std::string& name, const bool is_pointer) + : node(position), m_base(name), m_pointer(is_pointer) + { + } + + void type_expression::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + const std::string& type_expression::base() const noexcept + { + return m_base; + } + + bool type_expression::is_pointer() const noexcept + { + return m_pointer; + } + + declaration::declaration(const struct position position, const std::string& identifier, + std::unique_ptr&& type) + : definition(position, identifier), m_type(std::move(type)) + { + } + + void declaration::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + type_expression& declaration::type() noexcept + { + return *m_type; + } + + definition::definition(const struct position position, const std::string& identifier) + : node(position), m_identifier(identifier) + { + } + + std::string& definition::identifier() noexcept + { + return m_identifier; + } + + constant_definition::constant_definition(const struct position position, const std::string& identifier, + std::unique_ptr&& body) + : definition(position, identifier), m_body(std::move(body)) + { + } + + void constant_definition::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + integer_literal& constant_definition::body() + { + return *m_body; + } + + procedure_definition::procedure_definition(const struct position position, const std::string& identifier, + std::unique_ptr&& body) + : definition(position, identifier), m_body(std::move(body)) + { + } + + void procedure_definition::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + block& procedure_definition::body() + { + return *m_body; + } + + std::vector>& procedure_definition::parameters() noexcept + { + return m_parameters; + } + + block::block(const struct position position, std::vector>&& definitions, + std::vector>&& declarations, + std::unique_ptr&& body) + : node(position), m_definitions(std::move(definitions)), + m_declarations(std::move(declarations)), m_body(std::move(body)) + { + } + + void block::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + statement& block::body() + { + return *m_body; + } + + std::vector>& block::definitions() noexcept + { + return m_definitions; + } + + std::vector>& block::declarations() noexcept + { + return m_declarations; + } + + program::program(const struct position position, std::vector>&& definitions, + std::vector>&& declarations, + std::unique_ptr&& body) + : block(position, std::move(definitions), std::move(declarations), std::move(body)) + { + } + + void program::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + integer_literal::integer_literal(const struct position position, const std::int32_t value) + : expression(position), m_number(value) + { + } + + void integer_literal::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + std::int32_t integer_literal::number() const noexcept + { + return m_number; + } + + boolean_literal::boolean_literal(const struct position position, const bool value) + : expression(position), m_boolean(value) + { + } + + void boolean_literal::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + bool boolean_literal::boolean() const noexcept + { + return m_boolean; + } + + variable_expression::variable_expression(const struct position position, const std::string& name) + : expression(position), m_name(name) + { + } + + void variable_expression::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + const std::string& variable_expression::name() const noexcept + { + return m_name; + } + + binary_expression::binary_expression(const struct position position, std::unique_ptr&& lhs, + std::unique_ptr&& rhs, const unsigned char operation) + : expression(position), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)) + { + switch (operation) + { + case '+': + this->m_operator = binary_operator::sum; + break; + case '-': + this->m_operator = binary_operator::subtraction; + break; + case '*': + this->m_operator = binary_operator::multiplication; + break; + case '/': + this->m_operator = binary_operator::division; + break; + case '=': + this->m_operator = binary_operator::equals; + break; + case 'n': + this->m_operator = binary_operator::not_equals; + break; + case '<': + this->m_operator = binary_operator::less; + break; + case 'l': + this->m_operator = binary_operator::less_equal; + break; + case '>': + this->m_operator = binary_operator::greater; + break; + case 'g': + this->m_operator = binary_operator::greater_equal; + break; + default: + throw std::logic_error("Invalid binary operator"); + } + } + + void binary_expression::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + expression& binary_expression::lhs() + { + return *m_lhs; + } + + expression& binary_expression::rhs() + { + return *m_rhs; + } + + binary_operator binary_expression::operation() const noexcept + { + return m_operator; + } + + unary_expression::unary_expression(const struct position position, std::unique_ptr&& operand, + const unsigned char operation) + : expression(position), m_operand(std::move(operand)) + { + switch (operation) + { + case '@': + this->m_operator = unary_operator::reference; + break; + case '^': + this->m_operator = unary_operator::dereference; + break; + default: + throw std::logic_error("Invalid unary operator"); + } + } + + void unary_expression::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + expression& unary_expression::operand() + { + return *m_operand; + } + + unary_operator unary_expression::operation() const noexcept + { + return this->m_operator; + } + + call_statement::call_statement(const struct position position, const std::string& name) + : statement(position), m_name(name) + { + } + + void call_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + std::string& call_statement::name() noexcept + { + return m_name; + } + + std::vector>& call_statement::arguments() noexcept + { + return m_arguments; + } + + compound_statement::compound_statement(const struct position position) + : statement(position) + { + } + + void compound_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + std::vector>& compound_statement::statements() + { + return m_statements; + } + + void assign_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + assign_statement::assign_statement(const struct position position, const std::string& lvalue, + std::unique_ptr&& rvalue) + : statement(position), m_lvalue(lvalue), m_rvalue(std::move(rvalue)) + { + } + + std::string& assign_statement::lvalue() noexcept + { + return m_lvalue; + } + + expression& assign_statement::rvalue() + { + return *m_rvalue; + } + + if_statement::if_statement(const struct position position, std::unique_ptr&& prerequisite, + std::unique_ptr&& body) + : statement(position), m_prerequisite(std::move(prerequisite)), m_body(std::move(body)) + { + } + + void if_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + expression& if_statement::prerequisite() + { + return *m_prerequisite; + } + + statement& if_statement::body() + { + return *m_body; + } + + while_statement::while_statement(const struct position position, std::unique_ptr&& prerequisite, + std::unique_ptr&& body) + : statement(position), m_prerequisite(std::move(prerequisite)), m_body(std::move(body)) + { + } + + void while_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + expression& while_statement::prerequisite() + { + return *m_prerequisite; + } + + statement& while_statement::body() + { + return *m_body; + } + + parser::parser(lexer&& tokens) + : iterator(std::move(tokens)) + { + } + + std::unique_ptr parser::parse() + { + auto constants = parse_constant_definitions(); + auto declarations = parse_declarations(); + auto procedures = parse_procedure_definitions(); + auto parsed_statement = parse_statement(); + + if (parsed_statement == nullptr) + { + return nullptr; + } + std::vector> definitions(constants.size() + procedures.size()); + std::vector>::iterator definition = definitions.begin(); + + for (auto& constant : constants) + { + *definition++ = std::move(constant); + } + for (auto& procedure : procedures) + { + *definition++ = std::move(procedure); + } + return std::make_unique(position(), std::move(definitions), + std::move(declarations), std::move(parsed_statement)); + } + + const std::list>& parser::errors() const noexcept + { + return iterator.errors(); + } + + std::unique_ptr parser::parse_unary_expression() + { + std::unique_ptr result; + + if (iterator.current(token::type::at)) + { + std::unique_ptr body_expression; + + ++iterator; + if ((body_expression = parse_factor()) == nullptr) + { + return nullptr; + } + result = std::make_unique(iterator->position(), std::move(body_expression), '@'); + } + else + { + if ((result = parse_factor()) == nullptr) + { + return nullptr; + } + } + if (iterator.current(token::type::hat)) + { + ++iterator; + result = std::make_unique(iterator->position(), std::move(result), '^'); + } + return result; + } + + std::unique_ptr parser::parse_factor() + { + if (iterator->of() == token::type::identifier) + { + auto result = std::make_unique(iterator->position(), iterator->identifier()); + ++iterator; + return result; + } + else if (iterator->of() == token::token::type::number) + { + auto result = std::make_unique(iterator->position(), iterator->number()); + ++iterator; + return result; + } + else if (iterator->of() == token::token::type::boolean) + { + auto result = std::make_unique(iterator->position(), iterator->number()); + ++iterator; + return result; + } + else if (iterator->of() == token::type::left_paren) + { + ++iterator; + + auto expression = parse_condition(); + + iterator.advance(token::type::right_paren); + + return expression; + } + return nullptr; + } + + std::unique_ptr parser::parse_term() + { + auto lhs = parse_unary_expression(); + if (lhs == nullptr || iterator.current().of() != source::token::type::factor_operator) + { + return lhs; + } + while (iterator->of() == source::token::type::factor_operator) + { + auto _operator = iterator->identifier()[0]; + const auto operator_position = iterator->position(); + ++iterator; + + auto rhs = parse_unary_expression(); + lhs = std::make_unique(operator_position, std::move(lhs), + std::move(rhs), _operator); + } + return lhs; + } + + std::unique_ptr parser::parse_expression() + { + auto term = parse_term(); + if (term == nullptr || iterator.current().of() != source::token::type::term_operator) + { + return term; + } + while (iterator->of() == source::token::type::term_operator) + { + auto _operator = iterator->identifier()[0]; + const auto operator_position = iterator->position(); + ++iterator; + + auto rhs = parse_term(); + term = std::make_unique(operator_position, std::move(term), + std::move(rhs), _operator); + } + return term; + } + + std::unique_ptr parser::parse_condition() + { + std::unique_ptr lhs; + + if ((lhs = parse_expression()) == nullptr) + { + return lhs; + } + unsigned char _operator{ 0 }; + + if (iterator.current().of() == source::token::type::equals) + { + _operator = '='; + } + else if (iterator.current().of() == source::token::type::comparison_operator) + { + _operator = iterator->identifier()[0]; + } + else + { + return lhs; + } + const auto operator_position = iterator->position(); + ++iterator; + auto rhs = parse_expression(); + + if (rhs == nullptr) + { + return nullptr; + } + return std::make_unique(operator_position, std::move(lhs), std::move(rhs), _operator); + } + + std::unique_ptr parser::parse_constant_definition() + { + auto definition_identifier = iterator.advance(token::type::identifier); + const auto identifier_position = iterator->position(); + + if (!definition_identifier.has_value()) + { + return nullptr; + } + if (!iterator.skip(token::type::equals)) + { + return nullptr; + } + + if (iterator->of() == source::token::type::number) + { + auto result = std::make_unique(identifier_position, + definition_identifier.value().get().identifier(), + std::make_unique(iterator->position(), iterator->number())); + ++iterator; + return result; + } + return nullptr; + } + + std::unique_ptr parser::parse_procedure_definition() + { + const auto proc_position = iterator->position(); + if (!iterator.skip(token::type::procedure)) + { + return nullptr; + } + auto definition_identifier = iterator.advance(token::type::identifier); + + if (!definition_identifier.has_value() || !iterator.skip(token::type::left_paren)) + { + return nullptr; + } + std::vector> declarations; + while (!iterator.current(token::type::right_paren)) + { + std::unique_ptr parsed_declaration = parse_declaration(); + if (parsed_declaration == nullptr) + { + return nullptr; + } + declarations.push_back(std::move(parsed_declaration)); + + if (iterator->of() == token::type::comma) + { + ++iterator; + continue; + } + else if (iterator->of() != token::type::right_paren) + { + iterator.add_error(*iterator); + return nullptr; + } + } + iterator.skip(token::type::right_paren); + auto definition_body = parse_block(); + + if (definition_body == nullptr || !iterator.skip(token::type::semicolon)) + { + return nullptr; + } + auto procedure = std::make_unique(proc_position, + definition_identifier->get().identifier(), std::move(definition_body)); + procedure->parameters() = std::move(declarations); + + return procedure; + } + + std::unique_ptr parser::parse_type_expression() + { + const auto type_position = iterator->position(); + bool is_pointer{ false }; + if (iterator.current(token::type::hat)) + { + is_pointer = true; + ++iterator; + } + auto type_identifier = iterator.advance(token::type::identifier); + + if (!type_identifier.has_value()) + { + return nullptr; + } + return std::make_unique(type_position, type_identifier.value().get().identifier(), + is_pointer); + } + + std::unique_ptr parser::parse_declaration() + { + auto declaration_identifier = iterator.advance(token::type::identifier); + + if (!declaration_identifier.has_value() || !iterator.skip(token::type::colon)) + { + return nullptr; + } + auto type_identifier = parse_type_expression(); + + if (type_identifier == nullptr) + { + return nullptr; + } + return std::make_unique(declaration_identifier.value().get().position(), + declaration_identifier.value().get().identifier(), std::move(type_identifier)); + } + + std::unique_ptr parser::parse_statement() + { + if (iterator.look_ahead(token::type::assignment)) + { + return parse_assign_statement(); + } + else if (iterator.current(token::type::identifier) && iterator.look_ahead(token::type::left_paren)) + { + return parse_call_statement(); + } + else if (iterator.current(token::type::begin)) + { + return parse_compound_statement(); + } + else if (iterator.current(token::type::when)) + { + return parse_if_statement(); + } + else if (iterator.current(token::type::loop)) + { + return parse_while_statement(); + } + iterator.add_error(*iterator); + return nullptr; + } + + std::unique_ptr parser::parse_call_statement() + { + auto function_name = iterator.advance(token::type::identifier); + if (function_name.has_value() && !iterator.skip(token::type::left_paren)) + { + return nullptr; + } + auto call = std::make_unique(function_name->get().position(), + function_name->get().identifier()); + std::unique_ptr argument_expression; + + if (iterator.current(token::type::right_paren)) + { + ++iterator; + return call; + } + while ((argument_expression = parse_condition()) != nullptr) + { + call->arguments().push_back(std::move(argument_expression)); + + if (iterator.current(token::type::right_paren)) + { + ++iterator; + return call; + } + if (!iterator.skip(token::type::comma)) + { + break; + } + } + return nullptr; + } + + std::unique_ptr parser::parse_compound_statement() + { + const auto begin_position = iterator->position(); + if (!iterator.advance(token::type::begin)) + { + return nullptr; + } + auto result = std::make_unique(begin_position); + std::unique_ptr next_statement; + + while ((next_statement = parse_statement()) != nullptr) + { + result->statements().push_back(std::move(next_statement)); + + if (iterator->of() == token::type::semicolon) + { + ++iterator; + } + else if (iterator->of() == token::type::end) + { + ++iterator; + break; + } + else + { + iterator.add_error(*iterator); + return nullptr; + } + } + + return result; + } + + std::unique_ptr parser::parse_assign_statement() + { + auto name = iterator.advance(token::type::identifier); + if (!name.has_value() || !iterator.skip(token::type::assignment)) + { + return nullptr; + } + auto rvalue = parse_condition(); + + if (rvalue == nullptr) + { + return nullptr; + } + return std::make_unique(name.value().get().position(), name.value().get().identifier(), + std::move(rvalue)); + } + + std::unique_ptr parser::parse_if_statement() + { + const auto if_position = iterator->position(); + if (!iterator.skip(token::type::when)) + { + return nullptr; + } + auto condition = parse_condition(); + + if (condition == nullptr || !iterator.skip(token::type::then)) + { + return nullptr; + } + auto body = parse_statement(); + + if (body == nullptr) + { + return nullptr; + } + return std::make_unique(if_position, std::move(condition), std::move(body)); + } + + std::unique_ptr parser::parse_while_statement() + { + const auto while_position = iterator->position(); + if (!iterator.skip(token::type::loop)) + { + return nullptr; + } + auto condition = parse_condition(); + + if (condition == nullptr || !iterator.skip(token::type::_do)) + { + return nullptr; + } + auto body = parse_statement(); + + if (body == nullptr) + { + return nullptr; + } + return std::make_unique(while_position, std::move(condition), std::move(body)); + } + + std::vector> parser::parse_constant_definitions() + { + std::vector> definitions; + + if (iterator->of() != token::type::let) + { + return definitions; + } + ++iterator; // Skip const. + + std::unique_ptr parsed_definition; + while ((parsed_definition = parse_constant_definition()) != nullptr) + { + definitions.push_back(std::move(parsed_definition)); + + if (iterator->of() == source::token::type::comma) + { + ++iterator; + } + else if (iterator->of() == source::token::type::semicolon) + { + ++iterator; + break; + } + else + { + iterator.add_error(*iterator); + break; + } + } + return definitions; + } + + std::vector> parser::parse_procedure_definitions() + { + std::vector> definitions; + + while (iterator.current(token::type::procedure)) + { + auto parsed_definition = parse_procedure_definition(); + + if (parsed_definition == nullptr) + { + break; + } + definitions.push_back(std::move(parsed_definition)); + } + return definitions; + } + + std::vector> parser::parse_declarations() + { + std::vector> declarations; + + if (iterator->of() != token::type::var) + { + return declarations; + } + ++iterator; // Skip var. + + std::unique_ptr parsed_declaration; + while ((parsed_declaration = parse_declaration()) != nullptr) + { + declarations.push_back(std::move(parsed_declaration)); + + if (iterator->of() == token::type::comma) + { + ++iterator; + } + else if (iterator->of() == token::type::semicolon) + { + ++iterator; + break; + } + else + { + iterator.add_error(*iterator); + break; + } + } + return declarations; + } + + std::unique_ptr parser::parse_block() + { + auto constants = parse_constant_definitions(); + auto declarations = parse_declarations(); + auto parsed_statement = parse_statement(); + + if (parsed_statement == nullptr) + { + return nullptr; + } + std::vector> definitions(constants.size()); + std::vector>::iterator definition = definitions.begin(); + + for (auto& constant : constants) + { + *definition++ = std::move(constant); + } + return std::make_unique(parsed_statement->position(), std::move(definitions), + std::move(declarations), std::move(parsed_statement)); + } +} diff --git a/source/result.cpp b/source/result.cpp new file mode 100644 index 0000000..3740254 --- /dev/null +++ b/source/result.cpp @@ -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"; + } +} diff --git a/source/scanner.l b/source/scanner.l new file mode 100644 index 0000000..5814000 --- /dev/null +++ b/source/scanner.l @@ -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 +#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(elna::source::token::type::when); + } +then { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::then); + } +while { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::loop); + } +do { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::_do); + } +proc { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::procedure); + } +begin { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::begin); + } +end { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::end); + } +const { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::let); + } +var { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::var); + } +True { + yylval.number = 1; + + return static_cast(elna::source::token::type::boolean); + } +False { + yylval.number = 0; + + return static_cast(elna::source::token::type::boolean); + } +[A-Za-z_][A-Za-z0-9_]* { + new((void *) &yylval.identifier) std::string(yytext); + + return static_cast(elna::source::token::type::identifier); + } +[0-9]+ { + yylval.number = strtol(yytext, NULL, 10); + + return static_cast(elna::source::token::type::number); + } +\( { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::left_paren); + } +\) { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::right_paren); + } +\>= { + new((void *) &yylval.identifier) std::string(1, 'g'); + + return static_cast(elna::source::token::type::comparison_operator); + } +\<= { + new((void *) &yylval.identifier) std::string(1, 'l'); + + return static_cast(elna::source::token::type::comparison_operator); + } +(>|<) { + new((void *) &yylval.identifier) std::string(yytext); + + return static_cast(elna::source::token::type::comparison_operator); + } +\/= { + new((void *) &yylval.identifier) std::string(1, 'n'); + + return static_cast(elna::source::token::type::comparison_operator); + } += { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::equals); + } +; { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::semicolon); + } +\. { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::dot); + } +, { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::comma); + } +(\+|\-) { + new((void *) &yylval.identifier) std::string(yytext); + + return static_cast(elna::source::token::type::term_operator); + } +(\*|\/) { + new((void *) &yylval.identifier) std::string(yytext); + + return static_cast(elna::source::token::type::factor_operator); + } +:= { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::assignment); + } +: { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::colon); + } +\^ { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::hat); + } +@ { + yylval.nil = nullptr; + + return static_cast(elna::source::token::type::at); + } +. { + return -1; + } +%% +namespace elna::source +{ + +result tokenize(const std::filesystem::path& path) +{ + int yytoken; + std::vector 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(unexpected_character{ std::string{ yytext[0] }, path, token_position }); + } + tokens.emplace_back(static_cast(yytoken), std::move(yylval), token_position); + } + while (yytoken != 0); + + return result(std::in_place, std::move(tokens), position{ line_no, column_no }, path); +} + +} diff --git a/source/semantic.cpp b/source/semantic.cpp new file mode 100644 index 0000000..2b694ef --- /dev/null +++ b/source/semantic.cpp @@ -0,0 +1,118 @@ +#include "elna/source/semantic.hpp" +#include "elna/source/types.hpp" +#include + +namespace elna::source +{ + name_analysis_visitor::name_analysis_visitor(std::shared_ptr table) + : table(table) + { + } + + void name_analysis_visitor::visit(constant_definition *definition) + { + this->table->enter(definition->identifier(), + std::make_shared(constant_info(definition->body().number()))); + } + + std::shared_ptr name_analysis_visitor::convert_declaration_type(const type_expression& ast_type) const + { + auto variable_type = std::dynamic_pointer_cast(table->lookup(ast_type.base())) + ->type(); + std::shared_ptr declaration_type; + + if (ast_type.is_pointer()) + { + return std::make_shared(variable_type, 4); + } + else + { + return variable_type; + } + } + + void name_analysis_visitor::visit(declaration *declaration) + { + std::shared_ptr declaration_type = convert_declaration_type(declaration->type()); + + this->table->enter(declaration->identifier(), + std::make_shared(declaration_type)); + } + + void name_analysis_visitor::visit(program *program) + { + this->table->enter("main", std::make_shared(this->table)); + empty_visitor::visit(program); + } + + void name_analysis_visitor::visit(procedure_definition *procedure) + { + auto info = std::make_shared(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(declaration_type)); + } + procedure->body().accept(this); + + this->table = info->scope()->scope(); + } + + allocator_visitor::allocator_visitor(std::shared_ptr table) + : table(table) + { + } + + void allocator_visitor::visit(declaration *declaration) + { + auto declaration_info = this->table->lookup(declaration->identifier()); + + if (auto variable = std::dynamic_pointer_cast(declaration_info)) + { + this->local_offset -= sizeof(std::int32_t); + variable->offset = this->local_offset; + } + else if (auto parameter = std::dynamic_pointer_cast(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(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(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(this->table->lookup(statement->name())); + + this->argument_offset = std::max(static_cast(this->argument_offset), + call_info->parameter_stack_size()); + } +} diff --git a/source/symbol_table.cpp b/source/symbol_table.cpp new file mode 100644 index 0000000..fe6ae7e --- /dev/null +++ b/source/symbol_table.cpp @@ -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 scope) + : outer_scope(scope) + { + if (scope == nullptr) + { + auto boolean_info = std::make_shared(boolean_type); + auto int_info = std::make_shared(int_type); + enter("Boolean", boolean_info); + enter("Int", int_info); + + auto writei = std::make_shared(); + writei->parameter_infos.emplace_back(int_info->type()); + enter("writei", writei); + + auto writeb = std::make_shared(); + writeb->parameter_infos.emplace_back(boolean_info->type()); + enter("writeb", writeb); + } + } + + std::shared_ptr 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 entry) + { + entries.insert_or_assign(name, entry); + } + + std::shared_ptr 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(type)) + { + } + + type_info::~type_info() + { + } + + std::shared_ptr 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 type) + : m_type(type) + { + } + + variable_info::~variable_info() + { + } + + std::shared_ptr variable_info::type() noexcept + { + return m_type; + } + + parameter_info::parameter_info(std::shared_ptr type) + : m_type(type) + { + } + + parameter_info::~parameter_info() + { + } + + std::shared_ptr 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 outer_scope) + : local_table(std::make_shared(outer_scope)) + { + } + + procedure_info::~procedure_info() + { + } + + std::shared_ptr procedure_info::scope() + { + return local_table; + } + + std::size_t procedure_info::stack_size() const noexcept + { + return local_stack_size + argument_stack_size; + } +} diff --git a/source/types.cpp b/source/types.cpp new file mode 100644 index 0000000..1daa363 --- /dev/null +++ b/source/types.cpp @@ -0,0 +1,19 @@ +#include + +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 base_type, const std::size_t byte_size) + : type(byte_size), base_type(base_type) + { + } +} diff --git a/tests/7_member_sum.eln b/tests/7_member_sum.eln new file mode 100644 index 0000000..8c56a1b --- /dev/null +++ b/tests/7_member_sum.eln @@ -0,0 +1,2 @@ +writei(3 + 4 + 5 + 1 + 2 + 4 + 3) +. diff --git a/tests/const_list.elna b/tests/const_list.eln similarity index 61% rename from tests/const_list.elna rename to tests/const_list.eln index 18a6711..e65a53b 100644 --- a/tests/const_list.elna +++ b/tests/const_list.eln @@ -1,3 +1,3 @@ const a = 1, b = 2; -! + a b +writei(a + b) . diff --git a/tests/declare_variable.eln b/tests/declare_variable.eln new file mode 100644 index 0000000..24d7359 --- /dev/null +++ b/tests/declare_variable.eln @@ -0,0 +1,5 @@ +var x: Int; +begin + x := 5; + writei(x) +end. diff --git a/tests/divide.eln b/tests/divide.eln new file mode 100644 index 0000000..2a1343b --- /dev/null +++ b/tests/divide.eln @@ -0,0 +1 @@ +writei(6 / 3) diff --git a/tests/expectations/7_member_sum.txt b/tests/expectations/7_member_sum.txt new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/tests/expectations/7_member_sum.txt @@ -0,0 +1 @@ +22 diff --git a/tests/expectations/declare_variable.txt b/tests/expectations/declare_variable.txt new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/tests/expectations/declare_variable.txt @@ -0,0 +1 @@ +5 diff --git a/tests/expectations/divide.txt b/tests/expectations/divide.txt new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/tests/expectations/divide.txt @@ -0,0 +1 @@ +2 diff --git a/tests/expectations/if_condition.txt b/tests/expectations/if_condition.txt new file mode 100644 index 0000000..512858e --- /dev/null +++ b/tests/expectations/if_condition.txt @@ -0,0 +1,2 @@ +8 +9 diff --git a/tests/expectations/left_nested_sum.txt b/tests/expectations/left_nested_sum.txt new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/tests/expectations/left_nested_sum.txt @@ -0,0 +1 @@ +8 diff --git a/tests/expectations/multiline_output.txt b/tests/expectations/multiline_output.txt new file mode 100644 index 0000000..b3172d1 --- /dev/null +++ b/tests/expectations/multiline_output.txt @@ -0,0 +1,2 @@ +5 +7 diff --git a/tests/expectations/multiply.txt b/tests/expectations/multiply.txt new file mode 100644 index 0000000..f599e28 --- /dev/null +++ b/tests/expectations/multiply.txt @@ -0,0 +1 @@ +10 diff --git a/tests/expectations/multiply_3.txt b/tests/expectations/multiply_3.txt new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/tests/expectations/multiply_3.txt @@ -0,0 +1 @@ +24 diff --git a/tests/expectations/plus_minus.txt b/tests/expectations/plus_minus.txt new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/tests/expectations/plus_minus.txt @@ -0,0 +1 @@ +4 diff --git a/tests/expectations/pointer_in_expression.txt b/tests/expectations/pointer_in_expression.txt new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/tests/expectations/pointer_in_expression.txt @@ -0,0 +1 @@ +5 diff --git a/tests/expectations/print_boolean.txt b/tests/expectations/print_boolean.txt new file mode 100644 index 0000000..532e7b0 --- /dev/null +++ b/tests/expectations/print_boolean.txt @@ -0,0 +1,2 @@ +t +f diff --git a/tests/expectations/print_equals.txt b/tests/expectations/print_equals.txt new file mode 100644 index 0000000..f7c64cb --- /dev/null +++ b/tests/expectations/print_equals.txt @@ -0,0 +1,12 @@ +t +f +t +f +f +t +t +f +t +f +f +t diff --git a/tests/expectations/print_in_loop.txt b/tests/expectations/print_in_loop.txt new file mode 100644 index 0000000..8a1218a --- /dev/null +++ b/tests/expectations/print_in_loop.txt @@ -0,0 +1,5 @@ +1 +2 +3 +4 +5 diff --git a/tests/expectations/print_number.txt b/tests/expectations/print_number.txt new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/tests/expectations/print_number.txt @@ -0,0 +1 @@ +3 diff --git a/tests/expectations/procedure_2_statements.txt b/tests/expectations/procedure_2_statements.txt new file mode 100644 index 0000000..b087d44 --- /dev/null +++ b/tests/expectations/procedure_2_statements.txt @@ -0,0 +1,2 @@ +t +5 diff --git a/tests/expectations/procedure_definition.txt b/tests/expectations/procedure_definition.txt new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/tests/expectations/procedure_definition.txt @@ -0,0 +1 @@ +5 diff --git a/tests/expectations/procedure_print_argument.txt b/tests/expectations/procedure_print_argument.txt new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/tests/expectations/procedure_print_argument.txt @@ -0,0 +1 @@ +5 diff --git a/tests/expectations/subtraction.txt b/tests/expectations/subtraction.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/expectations/subtraction.txt @@ -0,0 +1 @@ +1 diff --git a/tests/failures/missing_semicolon.txt b/tests/failures/missing_semicolon.txt new file mode 100644 index 0000000..00953c6 --- /dev/null +++ b/tests/failures/missing_semicolon.txt @@ -0,0 +1 @@ +tests/missing_semicolon.eln:3:3: Unexpected token «identifier» diff --git a/tests/failures/single_word_error.txt b/tests/failures/single_word_error.txt new file mode 100644 index 0000000..5cd16c0 --- /dev/null +++ b/tests/failures/single_word_error.txt @@ -0,0 +1 @@ +tests/single_word_error.eln:1:1: Unexpected token «identifier» diff --git a/tests/if_condition.eln b/tests/if_condition.eln new file mode 100644 index 0000000..1dcc3cb --- /dev/null +++ b/tests/if_condition.eln @@ -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. diff --git a/tests/left_nested_sum.eln b/tests/left_nested_sum.eln new file mode 100644 index 0000000..cbec749 --- /dev/null +++ b/tests/left_nested_sum.eln @@ -0,0 +1,2 @@ +writei((3 + 4) + 1) +. diff --git a/tests/missing_semicolon.eln b/tests/missing_semicolon.eln new file mode 100644 index 0000000..02cd556 --- /dev/null +++ b/tests/missing_semicolon.eln @@ -0,0 +1,4 @@ +begin + writei(1) + writei(2) +end. diff --git a/tests/multiline_output.eln b/tests/multiline_output.eln new file mode 100644 index 0000000..30ddf27 --- /dev/null +++ b/tests/multiline_output.eln @@ -0,0 +1,4 @@ +begin + writei(5); + writei(7) +end. diff --git a/tests/multiply.eln b/tests/multiply.eln new file mode 100644 index 0000000..08ff32b --- /dev/null +++ b/tests/multiply.eln @@ -0,0 +1,2 @@ +writei(5 * 2) +. diff --git a/tests/multiply_3.eln b/tests/multiply_3.eln new file mode 100644 index 0000000..6b93f7c --- /dev/null +++ b/tests/multiply_3.eln @@ -0,0 +1,2 @@ +writei(3 * 4 * 2) +. diff --git a/tests/plus_minus.eln b/tests/plus_minus.eln new file mode 100644 index 0000000..5c27988 --- /dev/null +++ b/tests/plus_minus.eln @@ -0,0 +1,2 @@ +writei(2 + 3 - 1) +. diff --git a/tests/pointer_in_expression.eln b/tests/pointer_in_expression.eln new file mode 100644 index 0000000..8542d3b --- /dev/null +++ b/tests/pointer_in_expression.eln @@ -0,0 +1,6 @@ +var a: Int, b: ^Int; +begin + a := 5; + b := @a; + writei(b^) +end. diff --git a/tests/print_boolean.eln b/tests/print_boolean.eln new file mode 100644 index 0000000..7b24a1e --- /dev/null +++ b/tests/print_boolean.eln @@ -0,0 +1,4 @@ +begin + writeb(True); + writeb(False) +end. diff --git a/tests/print_equals.eln b/tests/print_equals.eln new file mode 100644 index 0000000..1e1cad4 --- /dev/null +++ b/tests/print_equals.eln @@ -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. diff --git a/tests/print_in_loop.eln b/tests/print_in_loop.eln new file mode 100644 index 0000000..b398e7d --- /dev/null +++ b/tests/print_in_loop.eln @@ -0,0 +1,10 @@ +var i: Int; +begin + i := 1; + + while i < 6 do + begin + writei(i); + i := i + 1 + end; +end. diff --git a/tests/print_number.eln b/tests/print_number.eln new file mode 100644 index 0000000..cab7751 --- /dev/null +++ b/tests/print_number.eln @@ -0,0 +1,2 @@ +writei(3) +. diff --git a/tests/procedure_2_statements.eln b/tests/procedure_2_statements.eln new file mode 100644 index 0000000..5f4a07c --- /dev/null +++ b/tests/procedure_2_statements.eln @@ -0,0 +1,9 @@ +proc g(a: Boolean, b: Int) +begin + writeb(a); + writei(b) +end; + +begin + g(True, 5) +end. diff --git a/tests/procedure_definition.eln b/tests/procedure_definition.eln new file mode 100644 index 0000000..fe500f5 --- /dev/null +++ b/tests/procedure_definition.eln @@ -0,0 +1,5 @@ +proc f() writei(5); + +begin + f() +end. diff --git a/tests/procedure_print_argument.eln b/tests/procedure_print_argument.eln new file mode 100644 index 0000000..92ab218 --- /dev/null +++ b/tests/procedure_print_argument.eln @@ -0,0 +1,5 @@ +proc f(a: Int) writei(a); + +begin + f(5) +end. diff --git a/tests/single_word_error.eln b/tests/single_word_error.eln new file mode 100644 index 0000000..8bd6648 --- /dev/null +++ b/tests/single_word_error.eln @@ -0,0 +1 @@ +asdf diff --git a/tests/subtraction.eln b/tests/subtraction.eln new file mode 100644 index 0000000..05c22fc --- /dev/null +++ b/tests/subtraction.eln @@ -0,0 +1,2 @@ +writei(5 - 4) +. diff --git a/tests/sum.eln b/tests/sum.eln new file mode 100644 index 0000000..19bcd0b --- /dev/null +++ b/tests/sum.eln @@ -0,0 +1,2 @@ +writei(1 + 7) +. diff --git a/tests/sum.elna b/tests/sum.elna deleted file mode 100644 index 12343f0..0000000 --- a/tests/sum.elna +++ /dev/null @@ -1,2 +0,0 @@ -! + 1 7 -. diff --git a/tests/sums.eln b/tests/sums.eln new file mode 100644 index 0000000..924c0aa --- /dev/null +++ b/tests/sums.eln @@ -0,0 +1,2 @@ +writei(1 + (3 + 4)) +. diff --git a/tests/sums.elna b/tests/sums.elna deleted file mode 100644 index bf80ecc..0000000 --- a/tests/sums.elna +++ /dev/null @@ -1,2 +0,0 @@ -! + 1 (+ 3 4) -. diff --git a/tests/tester.cpp b/tests/tester.cpp new file mode 100755 index 0000000..8e53b0e --- /dev/null +++ b/tests/tester.cpp @@ -0,0 +1,263 @@ +#include "elna/tester.hpp" + +#include +#include +#include + +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 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(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& 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 environment; + std::vector 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(); +}