Skip to content

Commit

Permalink
Add CLI Support for Executing ELF and C Files (#348)
Browse files Browse the repository at this point in the history
* Add CLI Support for Executing ELF and C Files

In the Command Line Interface mode, although the possible execution file types are `c, asm, bin` (as shown by the command-line option `--t`), it is not possible to simulate `C` programs.

```
int Ripes::CLIRunner::processInput(): Assertion `false && "Command-line support for this source type is not yet implemented"' failed.
Aborted (core dumped)
```

This has been fixed with the possibility to execute *RISC-V* `ELF` files too.
In the `clirunner.cpp` file I've added the logic to load three new source types: `C`, `InternalELF` and `ExternalELF`.
For an ELF file first a check is done to be sure that the specified file is valid for the selected processor and then the file is loaded with the method `loadElfFile`. I moved this method to the `programutilities.cpp` file, following what was done for loading binary files with the method `loadFlatBinaryFile`. The function was moved so that it can be used both by CLI and GUI.
To execute `C` programs first they are compiled using the already existing methods (such as `compileRaw()` from `ccmanager.cpp`), then they are converted as `ExternalELF` and loaded as mentioned above.

Since I've noticed that running CLI programs can sometimes have problems with large addresses (that cannot fit in an `int` variable), I've changed the type of the `byteAddress` variable to `long`.

* Added Documentation and Minor Improvements

Added detailed comments to the methods in `programutilities.h` and `programutilities.cpp`.
Applied suggested improvements, such as adding `static` to a local function definition.
  • Loading branch information
federicovilla55 committed Apr 4, 2024
1 parent 027e678 commit 8780873
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 132 deletions.
2 changes: 1 addition & 1 deletion src/cli/clioptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace Ripes {
void addCLIOptions(QCommandLineParser &parser, Ripes::CLIModeOptions &options) {
parser.addOption(QCommandLineOption("src", "Path to source file.", "path"));
parser.addOption(QCommandLineOption(
"t", "Source file type. Options: [c, asm, bin]", "type", "asm"));
"t", "Source file type. Options: [c, asm, bin, elf]", "type", "asm"));

// Processor models. Generate information from processor registry.
QStringList processorOptions;
Expand Down
55 changes: 55 additions & 0 deletions src/cli/clirunner.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "clirunner.h"
#include "ccmanager.h"
#include "io/iomanager.h"
#include "loaddialog.h"
#include "processorhandler.h"
#include "programutilities.h"
#include "syscall/systemio.h"
Expand Down Expand Up @@ -94,6 +96,59 @@ int CLIRunner::processInput() {
ProcessorHandler::loadProgram(std::make_shared<Program>(p));
break;
}
case SourceType::InternalELF:
case SourceType::ExternalELF: {
// Combining cases for InternalELF and ExternalELF because they share the
// same actions
info("Loading elf file '" + m_options.src + "'");
Program p;
QFile file(m_options.src);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
error("Could not open file " + file.fileName());
return 1;
}
auto info = LoadDialog::validateELFFile(
file); // Checking the validity of the ELF file.
if (!info.valid) {
error(info.errorMessage);
return 1;
}
if (!loadElfFile(p, file)) {
error("Error while loading ELF file: '" + m_options.src + "'");
return 1;
}
ProcessorHandler::loadProgram(std::make_shared<Program>(p));
break;
}
case SourceType::C: {
// For SourceType::C:
// - Compile the C file, disabling graphical components.
// - Treat the compiled file as an ExternalELF and process it.
// - Display error output if compilation fails.
info("Loading C file '" + m_options.src + "'");
if (!CCManager::get().hasValidCC()) {
error("No C compiler set.");
return 1;
}
QFile file(m_options.src);
if (!file.open(QIODevice::ReadOnly)) {
error("Could not open file " + file.fileName());
return 1;
}
QString fileContent = file.readAll();
file.close();
auto res = CCManager::get().compileRaw(fileContent, QString(),
/* enableGUI = */ false);
if (res.success) {
m_options.src = res.outFile;
m_options.srcType = SourceType::ExternalELF;
processInput();
} else if (!res.aborted) {
error("Compilation failed. Error output was: " + CCManager::getError());
}
res.clean();
break;
}
default:
assert(false &&
"Command-line support for this source type is not yet implemented");
Expand Down
139 changes: 139 additions & 0 deletions src/cli/programutilities.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
#include "programutilities.h"
#include "elfio/elfio.hpp"
#include "libelfin/dwarf/dwarf++.hh"
#include "statusmanager.h"

#include <QRegularExpression>

namespace Ripes {

Expand All @@ -18,4 +23,138 @@ QString loadFlatBinaryFile(Program &program, const QString &filepath,
return QString();
}

using namespace ELFIO;
/**
* @brief The ELFIODwarfLoader class provides
* a loader implementation for Dwarf sections
* using the ELFIO library.
*/
class ELFIODwarfLoader : public ::dwarf::loader {
public:
ELFIODwarfLoader(elfio &reader) : reader(reader) {}

const void *load(::dwarf::section_type section, size_t *size_out) override {
auto sec = reader.sections[::dwarf::elf::section_type_to_name(section)];
if (sec == nullptr)
return nullptr;
*size_out = sec->get_size();
return sec->get_data();
}

private:
elfio &reader;
};

/**
* @brief createDwarfLoader
* Creates a Dwarf loader for the provided elfio reader.
* @param reader The elfio reader to create the Dwarf loader for.
* @return A shared pointer to the newly created Dwarf loader.
*/
static std::shared_ptr<ELFIODwarfLoader> createDwarfLoader(elfio &reader) {
return std::make_shared<ELFIODwarfLoader>(reader);
}

/**
* @brief isInternalSourceFile
* Determines if the given filename is likely originated from within the Ripes
* editor. These files are typically temporary files like /.../Ripes.abc123.c.
*
* @param filename A string containing the filename to check.
* @return True if the filename is likely originated from within the Ripes
* editor, otherwise false.
*/
static bool isInternalSourceFile(const QString &filename) {
static QRegularExpression re("Ripes.[a-zA-Z0-9]+.c");
return re.match(filename).hasMatch();
}

bool loadElfFile(Program &program, QFile &file) {
ELFIO::elfio reader;

// No file validity checking is performed - it is expected that Loaddialog has
// done all validity checking.
if (!reader.load(file.fileName().toStdString())) {
assert(false);
}

for (const auto &elfSection : reader.sections) {
// Do not load .debug sections
if (!QString::fromStdString(elfSection->get_name()).startsWith(".debug")) {
ProgramSection section;
section.name = QString::fromStdString(elfSection->get_name());
section.address = elfSection->get_address();
// QByteArray performs a deep copy of the data when the data array is
// initialized at construction
section.data = QByteArray(elfSection->get_data(),
static_cast<int>(elfSection->get_size()));
program.sections[section.name] = section;
}

if (elfSection->get_type() == SHT_SYMTAB) {
// Collect function symbols
const ELFIO::symbol_section_accessor symbols(reader, elfSection);
for (unsigned int j = 0; j < symbols.get_symbols_num(); ++j) {
std::string name;
ELFIO::Elf64_Addr value = 0;
ELFIO::Elf_Xword size;
unsigned char bind;
unsigned char type = STT_NOTYPE;
ELFIO::Elf_Half section_index;
unsigned char other;
symbols.get_symbol(j, name, value, size, bind, type, section_index,
other);

if (type != STT_FUNC)
continue;
program.symbols[value] = QString::fromStdString(name);
}
}
}

// Load DWARF information into the source mapping of the program.
// We'll only load information from compilation units which originated from a
// source file that plausibly arrived from within the Ripes editor.
QString editorSrcFile;
try {
::dwarf::dwarf dw(createDwarfLoader(reader));
for (auto &cu : dw.compilation_units()) {
for (auto &line : cu.get_line_table()) {
if (!line.file)
continue;
QString filePath = QString::fromStdString(line.file->path);
if (editorSrcFile.isEmpty()) {
// Try to see if this compilation unit is from the Ripes editor:
if (isInternalSourceFile(filePath))
editorSrcFile = filePath;
}
if (editorSrcFile != filePath)
continue;
program.sourceMapping[line.address].insert(line.line - 1);
}
}
if (!editorSrcFile.isEmpty()) {
// Finally, we need to generate a hash of the source file that we've
// loaded source mappings from, so the editor knows what editor contents
// applies to this program.
QFile srcFile(editorSrcFile);
if (srcFile.open(QFile::ReadOnly))
program.sourceHash = Program::calculateHash(srcFile.readAll());
else
throw ::dwarf::format_error("Could not find source file " +
editorSrcFile.toStdString());
}
} catch (::dwarf::format_error &e) {
std::string msg = "Could not load debug information: ";
msg += e.what();
GeneralStatusManager::setStatusTimed(QString::fromStdString(msg), 2500);
} catch (...) {
// Something else went wrong.
}

program.entryPoint = reader.get_entry();

return true;
}

} // namespace Ripes
20 changes: 20 additions & 0 deletions src/cli/programutilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,27 @@

namespace Ripes {

/**
* @brief loadFlatBinaryFile
* Loads a flat binary file into the program's text section.
* @param program The program object to load the binary file into.
* @param filepath The string containing the path to the binary file.
* @param entryPoint The entry point address of the program.
* @param loadAt The address at which to load the binary file.
* @return An error message if an error occurred during loading; otherwise, an
* empty string.
*
*/
QString loadFlatBinaryFile(Program &program, const QString &filepath,
unsigned long entryPoint, unsigned long loadAt);

/**
* @brief loadElfFile
* Loads an ELF file into the program object passed as parameter.
* @param program The program object to load the ELF file into.
* @param file The QFile object containing the ELF file.
* @return True if the ELF file was loaded successfully; otherwise, false.
*/
bool loadElfFile(Program &program, QFile &file);

} // namespace Ripes

0 comments on commit 8780873

Please sign in to comment.