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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ find_package(yaml-cpp REQUIRED)
find_package(Catch2 QUIET)
find_package(Threads REQUIRED)
find_package(ZLIB REQUIRED)
find_package(elfio QUIET)

if(MSVC)
add_compile_options(/vmg /MP /W3 /wd4244 /wd4267 /wd4996 -DNOMINMAX /EHsc)
Expand Down
8 changes: 7 additions & 1 deletion src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
project(scc-util VERSION 0.0.1 LANGUAGES CXX)

set(SRC util/io-redirector.cpp util/watchdog.cpp util/ihex_parser.cpp)
set(SRC util/io-redirector.cpp util/watchdog.cpp util/ihex.cpp)
if(TARGET lz4::lz4)
list(APPEND SRC util/lz4_streambuf.cpp)
endif()
if(TARGET elfio::elfio)
list(APPEND SRC util/elf.cpp)
endif()
add_library(${PROJECT_NAME} ${SRC})
add_library(scc::${PROJECT_NAME} ALIAS ${PROJECT_NAME})

Expand All @@ -18,6 +21,9 @@ endif()
if(TARGET lz4::lz4)
target_link_libraries(${PROJECT_NAME} PUBLIC lz4::lz4)
endif()
if(TARGET elfio::elfio)
target_link_libraries(${PROJECT_NAME} PRIVATE elfio::elfio)
endif()

if(CLANG_TIDY_EXE)
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}" )
Expand Down
73 changes: 73 additions & 0 deletions src/common/util/elf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include "elf.h"
#include <elfio/elfio.hpp>
#include <sstream>
#include <stdexcept>

namespace util {
uint64_t load_elf_file(std::string const& name, std::function<bool(uint64_t, uint64_t, const uint8_t*)> cb, uint8_t expected_elf_class,
uint16_t expected_elf_machine) {
// Create elfio reader
ELFIO::elfio reader;
// Load ELF data
if(!reader.load(name))
throw std::runtime_error("Could not load file");
// check elf properties
if(reader.get_class() != expected_elf_class)
throw std::runtime_error("ELF Class missmatch");
if(reader.get_type() != ELFIO::ET_EXEC && reader.get_type() != ELFIO::ET_DYN)
throw std::runtime_error("Input is neither an executable nor a pie executable (dyn)");
if(reader.get_machine() != expected_elf_machine)
throw std::runtime_error("ELF Machine type missmatch");
auto entry_address = reader.get_entry();
for(const auto& pseg : reader.segments) {
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
const auto seg_data = pseg->get_data();
const auto type = pseg->get_type();
if(type == ELFIO::PT_LOAD && fsize > 0) {
if(cb(pseg->get_physical_address(), fsize, reinterpret_cast<const uint8_t* const>(seg_data))) {
std::ostringstream oss;
oss << "Problem writing " << fsize << " bytes to 0x" << std::hex << pseg->get_physical_address();
throw std::runtime_error(oss.str());
}
}
}
return entry_address;
};

std::unordered_map<std::string, uint64_t> read_elf_symbols(std::string const& name, uint8_t expected_elf_class,
uint16_t expected_elf_machine) {
// Create elfio reader
ELFIO::elfio reader;
// Load ELF data
if(!reader.load(name))
throw std::runtime_error("Could not load file");
// check elf properties
if(reader.get_class() != expected_elf_class)
throw std::runtime_error("ELF Class missmatch");
if(reader.get_type() != ELFIO::ET_EXEC && reader.get_type() != ELFIO::ET_DYN)
throw std::runtime_error("Input is neither an executable nor a pie executable (dyn)");
if(reader.get_machine() != expected_elf_machine)
throw std::runtime_error("ELF Machine type missmatch");
std::unordered_map<std::string, uint64_t> symbol_table;
const auto sym_sec = reader.sections[".symtab"];
if(ELFIO::SHT_SYMTAB == sym_sec->get_type() || ELFIO::SHT_DYNSYM == sym_sec->get_type()) {
ELFIO::symbol_section_accessor symbols(reader, sym_sec);
auto sym_no = symbols.get_symbols_num();
std::string name;
ELFIO::Elf64_Addr value = 0;
ELFIO::Elf_Xword size = 0;
unsigned char bind = 0;
unsigned char type = 0;
ELFIO::Elf_Half section = 0;
unsigned char other = 0;
for(auto i = 0U; i < sym_no; ++i) {
symbols.get_symbol(i, name, value, size, bind, type, section, other);
if(name != "") {
symbol_table[name] = value;
}
}
}
return std::move(symbol_table);
};

} // namespace util
14 changes: 14 additions & 0 deletions src/common/util/elf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef UTIL_ELF_IO_H_
#define UTIL_ELF_IO_H_
#include <cstdint>
#include <elfio/elf_types.hpp>
#include <functional>
#include <string>

namespace util {
uint64_t load_elf_file(std::string const& name, std::function<bool(uint64_t, uint64_t, const uint8_t*)>,
uint8_t expected_elf_class = ELFIO::ELFCLASS32, uint16_t expected_elf_machine = ELFIO::EM_RISCV);
std::unordered_map<std::string, uint64_t> read_elf_symbols(std::string const& name, uint8_t expected_elf_class = ELFIO::ELFCLASS32,
uint16_t expected_elf_machine = ELFIO::EM_RISCV);
} // namespace util
#endif
87 changes: 85 additions & 2 deletions src/common/util/ihex_parser.cpp → src/common/util/ihex.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@

#include "ihex_parser.h"
#include "ihex.h"
#include <array>
#include <cstdint>
#include <cstring>
#include <iomanip>
#include <ostream>
#include <stdbool.h>
#include <stdexcept>
#include <stdint.h>
#include <vector>

// IHEX file parser state machine
enum {
Expand All @@ -25,6 +30,7 @@ enum record_type_e { DATA, END_OF_FILE, EXTENDED_SEGMENT_ADDRESS, START_SEGMENT_
#define INVALID_HEX_CHAR 'x'

namespace util {
namespace ihex {
namespace {
uint8_t hex2dec(uint8_t h) {
if(h >= '0' && h <= '9')
Expand All @@ -36,9 +42,40 @@ uint8_t hex2dec(uint8_t h) {
else
return INVALID_HEX_CHAR;
}

static uint8_t ihex_checksum(const std::vector<uint8_t>& bytes) {
// Checksum is two's complement of the LSB of the sum
uint32_t sum = 0;
for(uint8_t b : bytes)
sum += b;
return static_cast<uint8_t>(0u - static_cast<uint8_t>(sum));
}

static void write_record(std::ostream& os, uint8_t len, uint16_t addr, uint8_t type, const uint8_t* data) {
std::vector<uint8_t> payload;
payload.reserve(4 + len);
payload.push_back(len);
payload.push_back(static_cast<uint8_t>((addr >> 8) & 0xFF));
payload.push_back(static_cast<uint8_t>(addr & 0xFF));
payload.push_back(type);
for(uint8_t i = 0; i < len; ++i)
payload.push_back(data[i]);

uint8_t csum = ihex_checksum(payload);

os << ':' << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(len) << std::setw(4)
<< static_cast<int>(addr) << std::setw(2) << static_cast<int>(type);

for(uint8_t i = 0; i < len; ++i) {
os << std::setw(2) << static_cast<int>(data[i]);
}

os << std::setw(2) << static_cast<int>(csum) << "\n";
}

} // namespace

bool ihex_parser::parse(std::istream& is, std::function<bool(uint64_t, uint64_t, const uint8_t*)> cb) {
bool parse(std::istream& is, std::function<bool(uint64_t, uint64_t, const uint8_t*)> cb) {
char c;
uint8_t i;
unsigned state{0};
Expand Down Expand Up @@ -149,4 +186,50 @@ bool ihex_parser::parse(std::istream& is, std::function<bool(uint64_t, uint64_t,
}
return true;
}

void dump(std::ostream& os, nonstd::span<uint8_t> const& bytes, uint32_t base_address, uint32_t record_len) {
if(record_len == 0 || record_len > 255) {
throw std::invalid_argument("record_len must be 1..255");
}

uint32_t addr = base_address;
uint32_t upper = 0xFFFFFFFFu; // force first extended address if needed

size_t i = 0;
while(i < bytes.size()) {
uint32_t new_upper = (addr >> 16) & 0xFFFF;

// Emit Extended Linear Address record when upper 16 bits change
if(new_upper != upper) {
upper = new_upper;
uint8_t ext[2] = {static_cast<uint8_t>((upper >> 8) & 0xFF), static_cast<uint8_t>(upper & 0xFF)};
write_record(os, 2, 0x0000, 0x04, ext);
}

uint16_t low = static_cast<uint16_t>(addr & 0xFFFF);

// Don't cross a 64KiB boundary inside a data record
size_t max_before_boundary = 0x10000u - low;
size_t chunk = record_len;
if(chunk > bytes.size() - i)
chunk = bytes.size() - i;
if(chunk > max_before_boundary)
chunk = max_before_boundary;

// Copy to uint8_t buffer for record writer
std::vector<uint8_t> buf(chunk);
for(size_t k = 0; k < chunk; ++k)
buf[k] = static_cast<uint8_t>(bytes[i + k]);

write_record(os, static_cast<uint8_t>(chunk), low, 0x00, buf.data());

i += chunk;
addr += static_cast<uint32_t>(chunk);
}

// End-of-file record
write_record(os, 0, 0x0000, 0x01, nullptr);
}

} // namespace ihex
} // namespace util
36 changes: 20 additions & 16 deletions src/common/util/ihex_parser.h → src/common/util/ihex.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#ifndef _UTIL_IHEX_PARSER_H_
#define _UTIL_IHEX_PARSER_H_

#include "nonstd/span.h"
#include <cstdint>
#include <functional>
#include <iostream>
Expand All @@ -36,21 +37,24 @@ namespace util {
* @author Your Name
* @date YYYY-MM-DD
*/
struct ihex_parser {
/**
* @brief The maximum size of data in a single IHEX record.
*/
enum { IHEX_DATA_SIZE = 255 };
/**
* @brief Parses an IHEX file from the given input stream and invokes a callback function for each data record found.
*
* @param input The input stream containing the IHEX file.
* @param callback A function object that will be invoked for each data record found in the IHEX file.
* The callback function should return true to continue parsing, or false to stop parsing.
*
* @return True if the parsing was successful, false otherwise.
*/
static bool parse(std::istream&, std::function<bool(uint64_t, uint64_t, const uint8_t*)>);
};
namespace ihex {
/**
* @brief The maximum size of data in a single IHEX record.
*/
enum { IHEX_DATA_SIZE = 255 };
/**
* @brief Parses an IHEX file from the given input stream and invokes a callback function for each data record found.
*
* @param input The input stream containing the IHEX file.
* @param callback A function object that will be invoked for each data record found in the IHEX file.
* The callback function takes the address, the length and the data as parameter and should return
* true to continue parsing, or false to stop parsing.
*
* @return True if the parsing was successful, false otherwise.
*/
bool parse(std::istream&, std::function<bool(uint64_t, uint64_t, const uint8_t*)>);

void dump(std::ostream&, nonstd::span<uint8_t> const&, uint32_t = 0, uint32_t = 16);
}; // namespace ihex
} // namespace util
#endif