You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
480 lines
13 KiB
480 lines
13 KiB
/*
|
|
* FCML - Free Code Manipulation Library.
|
|
* Copyright (C) 2010-2019 Slawomir Wojtasiak
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/** @file fcml_lag_assembler.hpp
|
|
* C++ wrapper for the Multi-pass FCML assembler.
|
|
*
|
|
* @copyright Copyright (C) 2010-2015 Slawomir Wojtasiak. All rights reserved.
|
|
* This project is released under the GNU Lesser General Public License.
|
|
*/
|
|
|
|
#ifndef FCML_LAG_ASSEMBLER_HPP_
|
|
#define FCML_LAG_ASSEMBLER_HPP_
|
|
|
|
#include "fcml_assembler.hpp"
|
|
#include "fcml_symbols.hpp"
|
|
|
|
#include "fcml_lag_assembler.h"
|
|
|
|
namespace fcml {
|
|
|
|
/** Assembler result which contains all assembled instructions. */
|
|
class MultiPassAssemblerResult {
|
|
public:
|
|
|
|
/**
|
|
* Default constructor.
|
|
* @since 1.1.0
|
|
*/
|
|
MultiPassAssemblerResult() {
|
|
}
|
|
|
|
/**
|
|
* Virtual destructor.
|
|
* @since 1.1.0
|
|
*/
|
|
virtual ~MultiPassAssemblerResult() {
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
* Gets error container.
|
|
*
|
|
* @return Error container.
|
|
* @since 1.1.0
|
|
*/
|
|
const ErrorContainer& getErrorContainer() const {
|
|
return _errorContainer;
|
|
}
|
|
|
|
/**
|
|
* Gets number of assembled instructions.
|
|
* @return Number of assembled instructions.
|
|
* @since 1.1.0
|
|
*/
|
|
fcml_usize getSize() const {
|
|
return static_cast<fcml_usize>(_assembledInstructions.size());
|
|
}
|
|
|
|
/**
|
|
* Gets one assembled instruction by its index.
|
|
*
|
|
* @param index The index of the assembled instruction.
|
|
* @return One assembled instruction at given index.
|
|
* @throw BadArgumentException
|
|
* @since 1.1.0
|
|
*/
|
|
const AssembledInstruction& operator[](fcml_usize index) const {
|
|
if (index > _assembledInstructions.size()) {
|
|
throw BadArgumentException(FCML_TEXT("Array index out of bound."),
|
|
FCML_CEH_GEC_VALUE_OUT_OF_RANGE);
|
|
}
|
|
return _assembledInstructions[index];
|
|
}
|
|
|
|
/**
|
|
* Gets constant vector of all assembled instructions.
|
|
*
|
|
* @return Assembled instructions.
|
|
* @since 1.1.0
|
|
*/
|
|
const std::vector<AssembledInstruction>& getAssembledInstructions() const {
|
|
return _assembledInstructions;
|
|
}
|
|
|
|
/**
|
|
* Gets iterator which allows to iterate through the whole machine code
|
|
* byte by byte.
|
|
*
|
|
* @return Iterator instance.
|
|
* @since 1.1.0
|
|
*/
|
|
CodeIterator getCodeIterator() {
|
|
return CodeIterator(_assembledInstructions);
|
|
}
|
|
|
|
/**
|
|
* Clears the result. Remember not to use the CodeIterator after the
|
|
* result is cleared up.
|
|
* @since 1.1.0
|
|
*/
|
|
void clear() {
|
|
_errorContainer.clean();
|
|
_assembledInstructions.clear();
|
|
}
|
|
|
|
protected:
|
|
|
|
/** Only the assembler can modify these objects. */
|
|
friend class MultiPassAssembler;
|
|
|
|
/**
|
|
* Gets vector of all assembled instructions.
|
|
*
|
|
* @return Assembled instructions.
|
|
* @since 1.1.0
|
|
*/
|
|
std::vector<AssembledInstruction>& getAssembledInstructions() {
|
|
return _assembledInstructions;
|
|
}
|
|
|
|
/**
|
|
* Sets new error container for the result.
|
|
*
|
|
* @param errorContainer The new error container to be set.
|
|
* @since 1.1.0
|
|
*/
|
|
void setErrorContainer(const ErrorContainer &errorContainer) {
|
|
_errorContainer = errorContainer;
|
|
}
|
|
|
|
private:
|
|
|
|
/** Errors container. */
|
|
ErrorContainer _errorContainer;
|
|
/** Vector of all assembled instructions. */
|
|
std::vector<AssembledInstruction> _assembledInstructions;
|
|
|
|
};
|
|
|
|
/** Assembler context.
|
|
* @since 1.1.0
|
|
*/
|
|
class MultiPassAssemblerContext {
|
|
|
|
public:
|
|
|
|
/**
|
|
* Default constructor.
|
|
* @since 1.1.0
|
|
*/
|
|
MultiPassAssemblerContext() :
|
|
_symbolTable( NULL) {
|
|
}
|
|
|
|
/**
|
|
* Creates assembler context for given operating mode and instruction pointer.
|
|
* @param operatingMode The operating mode.
|
|
* @param ip The instruction pointer.
|
|
* @since 1.1.0
|
|
*/
|
|
MultiPassAssemblerContext(EntryPoint::OperatingMode operatingMode,
|
|
fcml_ip ip = 0) :
|
|
_entryPoint(operatingMode, ip), _symbolTable( NULL) {
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
* Gets constant reference to assembler configuration.
|
|
*
|
|
* @return Assembler configuration.
|
|
* @since 1.1.0
|
|
*/
|
|
const AssemblerConf& getConfig() const {
|
|
return _config;
|
|
}
|
|
|
|
/**
|
|
* Gets reference to the assembler configuration.
|
|
*
|
|
* @return Assembler configuration.
|
|
* @since 1.1.0
|
|
*/
|
|
AssemblerConf& getConfig() {
|
|
return _config;
|
|
}
|
|
|
|
/**
|
|
* Sets a new assembler configuration.
|
|
*
|
|
* @return Assembler configuration.
|
|
* @since 1.1.0
|
|
*/
|
|
void setConfig(const AssemblerConf &config) {
|
|
_config = config;
|
|
}
|
|
|
|
/**
|
|
* Gets constant reference to the entry point.
|
|
*
|
|
* @return Entry point.
|
|
* @since 1.1.0
|
|
*/
|
|
const EntryPoint& getEntryPoint() const {
|
|
return _entryPoint;
|
|
}
|
|
|
|
/**
|
|
* Gets reference to the entry point.
|
|
*
|
|
* @return Entry point.
|
|
* @since 1.1.0
|
|
*/
|
|
EntryPoint& getEntryPoint() {
|
|
return _entryPoint;
|
|
}
|
|
|
|
/**
|
|
* Sets a new entry point.
|
|
*
|
|
* @param entryPoint Entry point to be set.
|
|
* @since 1.1.0
|
|
*/
|
|
void setEntryPoint(const EntryPoint &entryPoint) {
|
|
_entryPoint = entryPoint;
|
|
}
|
|
|
|
/**
|
|
* Sets a new instruction pointer.
|
|
*
|
|
* @param ip A new instruction pointer.
|
|
* @since 1.1.0
|
|
*/
|
|
void setIP(fcml_ip ip) {
|
|
_entryPoint.setIP(ip);
|
|
}
|
|
|
|
/**
|
|
* Increments the instruction pointer by given number of bytes.
|
|
*
|
|
* @param ip The number of bytes the instruction pointer should
|
|
* be incremented by.
|
|
* @since 1.1.0
|
|
*/
|
|
void incrementIP(fcml_ip ip) {
|
|
_entryPoint.incrementIP(ip);
|
|
}
|
|
|
|
/**
|
|
* Sets a new processor operating mode for the entry point.
|
|
*
|
|
* @param operatingMode The new operating mode.
|
|
* @since 1.1.0
|
|
*/
|
|
void setOperatingMode(EntryPoint::OperatingMode operatingMode) {
|
|
_entryPoint.setOpMode(operatingMode);
|
|
}
|
|
|
|
/**
|
|
* Sets a new address size attribute for the entry point.
|
|
*
|
|
* @param addressSizeAttribute The address size attribute.
|
|
* @since 1.1.0
|
|
*/
|
|
void setAddressSizeAttribute(fcml_usize addressSizeAttribute) {
|
|
_entryPoint.setAddressSizeAttribute(addressSizeAttribute);
|
|
}
|
|
|
|
/**
|
|
* Sets a new operand size attribute for the entry point.
|
|
*
|
|
* @param operandSizeAttribute The operand size attribute.
|
|
* @since 1.1.0
|
|
*/
|
|
void setOperandSizeAttribute(fcml_usize operandSizeAttribute) {
|
|
_entryPoint.setOperandSizeAttribute(operandSizeAttribute);
|
|
}
|
|
|
|
/**
|
|
* Gets a pointer to the constant symbol table stored in the context.
|
|
*
|
|
* @return The pointer to the symbol table.
|
|
* @since 1.1.0
|
|
*/
|
|
const SymbolTable* getSymbolTable() const {
|
|
return _symbolTable;
|
|
}
|
|
|
|
/**
|
|
* Gets a pointer to the symbol table stored in the context.
|
|
*
|
|
* @return The pointer to the symbol table.
|
|
* @since 1.1.0
|
|
*/
|
|
SymbolTable* getSymbolTable() {
|
|
return _symbolTable;
|
|
}
|
|
|
|
/**
|
|
* Sets a new symbol table for the context.
|
|
*
|
|
* @param symbolTable The new symbol table to be set.
|
|
* @since 1.1.0
|
|
*/
|
|
void setSymbolTable(SymbolTable *symbolTable) {
|
|
_symbolTable = symbolTable;
|
|
}
|
|
|
|
private:
|
|
/** The entry point. */
|
|
EntryPoint _entryPoint;
|
|
/** The assembler configuration. */
|
|
AssemblerConf _config;
|
|
/** The symbol table assigned to the context. */
|
|
SymbolTable *_symbolTable;
|
|
};
|
|
|
|
/**
|
|
* An assembler wrapper, as you can see the assembler context is
|
|
* managed internally and
|
|
* is not exposed outside.
|
|
*/
|
|
class MultiPassAssembler: public NonCopyable,
|
|
protected DialectAware,
|
|
protected SymbolTableAware {
|
|
public:
|
|
|
|
/**
|
|
* Creates multi-pass assembler for a dialect.
|
|
* @param dialect The dialect for the assembler.
|
|
* @throw InitException Cannot initialize the assembler.
|
|
* @since 1.1.0
|
|
*/
|
|
MultiPassAssembler(Dialect &dialect) :
|
|
_dialect(dialect) {
|
|
fcml_ceh_error error = ::fcml_fn_assembler_init(extractDialect(dialect),
|
|
&_assembler);
|
|
if (error) {
|
|
throw InitException(FCML_TEXT("Cannot initialize the assembler."),
|
|
error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Virtual destructor.
|
|
* @since 1.1.0
|
|
*/
|
|
virtual ~MultiPassAssembler() {
|
|
if (_assembler) {
|
|
::fcml_fn_assembler_free(_assembler);
|
|
_assembler = NULL;
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
* Assembles given instruction model.
|
|
*
|
|
* @param ctx Assembler context.
|
|
* @param instructions A pointer to the NULL terminated array of
|
|
* the instructions.
|
|
* @param[out] result Assembler result.
|
|
* @throw AssemblingFailedException Assembler failed.
|
|
* @return Error code.
|
|
* @since 1.1.0
|
|
*/
|
|
fcml_ceh_error assemble(MultiPassAssemblerContext &ctx,
|
|
const fcml_string *instructions, MultiPassAssemblerResult &result) {
|
|
|
|
/* Prepare assembler context. */
|
|
fcml_st_lag_assembler_context context = { 0 };
|
|
|
|
AssemblerTypeConverter::convert(ctx.getConfig(), context.configuration);
|
|
TypeConverter::convert(ctx.getEntryPoint(), context.entry_point);
|
|
|
|
SymbolTable *symbolTable = ctx.getSymbolTable();
|
|
context.symbol_table =
|
|
symbolTable ? extractSymbolTable(*symbolTable) : NULL;
|
|
context.assembler = _assembler;
|
|
|
|
/* Prepare assembler result. */
|
|
fcml_st_lag_assembler_result res;
|
|
::fcml_fn_lag_assembler_result_prepare(&res);
|
|
|
|
fcml_ceh_error error = FCML_CEH_GEC_NO_ERROR;
|
|
|
|
try {
|
|
|
|
result.clear();
|
|
|
|
error = ::fcml_fn_lag_assemble(&context, instructions, &res);
|
|
|
|
/* Failed or not, convert assembler errors. */
|
|
|
|
ErrorContainer errorContainer;
|
|
ErrorTypeConverter::convert(res.errors, errorContainer);
|
|
|
|
/* Prepares assembler result. */
|
|
|
|
result.setErrorContainer(errorContainer);
|
|
|
|
if (error && ctx.getConfig().isThrowExceptionOnError()) {
|
|
::fcml_fn_lag_assembler_result_free(&res);
|
|
throw AssemblingFailedException(
|
|
errorContainer.prepareErrorMessage(
|
|
FCML_TEXT("Assembling failed")), errorContainer,
|
|
error);
|
|
}
|
|
|
|
if (!error) {
|
|
|
|
std::vector<AssembledInstruction> &assembledInstructions =
|
|
result.getAssembledInstructions();
|
|
|
|
assembledInstructions.clear();
|
|
|
|
ErrorContainer instructionWarnings;
|
|
fcml_st_assembled_instruction *next_instruction =
|
|
res.instructions;
|
|
while (next_instruction) {
|
|
fcml_st_ceh_error_container &instruction_warnings =
|
|
next_instruction->warnings;
|
|
ErrorTypeConverter::convert(instruction_warnings,
|
|
instructionWarnings);
|
|
const AssembledInstruction assembledInstruction(
|
|
next_instruction->code,
|
|
next_instruction->code_length, instructionWarnings);
|
|
assembledInstructions.push_back(assembledInstruction);
|
|
next_instruction = next_instruction->next;
|
|
}
|
|
|
|
// Convert it back to the context because it might have been
|
|
// modified during assembling process (IP incrementation etc).
|
|
TypeConverter::convert(context.entry_point,
|
|
ctx.getEntryPoint());
|
|
|
|
}
|
|
|
|
::fcml_fn_lag_assembler_result_free(&res);
|
|
|
|
} catch (std::exception &exc) {
|
|
// If anything failed, free assembler results.
|
|
::fcml_fn_lag_assembler_result_free(&res);
|
|
throw exc;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
private:
|
|
|
|
// The dialect used by the assembler.
|
|
Dialect &_dialect;
|
|
// The initialized assembler instance used by the wrapper.
|
|
fcml_st_assembler *_assembler;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#endif /* FCML_LAG_ASSEMBLER_HPP_ */
|