reman3/third_party/coffi-1.1/coffi/coffi.hpp

1019 lines
35 KiB
C++

/*
Copyright (C) 2014-2014 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.
*/
/*! @file coffi.hpp
* @brief The COFFI library include file
*
* The only file required to include when using the COFFI library.
* It includes the other header files.
*/
#ifndef COFFI_HPP
#define COFFI_HPP
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
//#pragma warning(disable:4355)
//#pragma warning(disable:4244)
#endif
#include <ios>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <coffi/coffi_types.hpp>
#include <coffi/coffi_utils.hpp>
#include <coffi/coffi_section.hpp>
#include <coffi/coffi_headers.hpp>
#include <coffi/coffi_strings.hpp>
#include <coffi/coffi_symbols.hpp>
#include <coffi/coffi_directory.hpp>
//! COFFI library namespace
namespace COFFI {
//-------------------------------------------------------------------------
//! The COFFI library's main class.
class coffi : public coffi_strings,
public coffi_symbols,
public virtual sections_provider
{
public:
//---------------------------------------------------------------------
coffi()
: coffi_strings{}, architecture_{COFFI_ARCHITECTURE_NONE},
dos_header_{0}, coff_header_{0}, optional_header_{0}, win_header_{0},
directories_{this}
{
create(COFFI_ARCHITECTURE_PE);
}
//---------------------------------------------------------------------
//! Discards the copy constructor
coffi(const coffi&) = delete;
//---------------------------------------------------------------------
~coffi() { clean(); }
//---------------------------------------------------------------------
/*! @brief Initializes the coffi object by loading data from COFF binary file.
*
* @param[in] file_name File path of the file to load.
* @return true if the file was processed successfully, false otherwise.
*/
bool load(const std::string& file_name)
{
std::ifstream stream;
stream.open(file_name, std::ios::in | std::ios::binary);
if (!stream) {
return false;
}
return load(stream);
}
//---------------------------------------------------------------------
/*! @brief @copybrief load(const std::string&)
*
* See load(const std::string&).
*
* @param[in] stream File to load, as an opened stream.
*/
bool load(std::istream& stream)
{
clean();
stream.seekg(0);
architecture_ = COFFI_ARCHITECTURE_NONE;
dos_header_ = new dos_header;
if (dos_header_->load(stream)) {
// It is an EXE
architecture_ = COFFI_ARCHITECTURE_PE;
}
else {
// It is not an EXE file, but it might be a PE OBJ file, or another type of COFF file
clean();
stream.seekg(0);
}
// Try to read a PE header
coff_header_ = new coff_header_impl;
if (coff_header_->load(stream)) {
// Check the machine
static const std::vector<uint16_t> machines = {
IMAGE_FILE_MACHINE_AM33, IMAGE_FILE_MACHINE_AMD64,
IMAGE_FILE_MACHINE_ARM, IMAGE_FILE_MACHINE_ARMNT,
IMAGE_FILE_MACHINE_ARM64, IMAGE_FILE_MACHINE_EBC,
IMAGE_FILE_MACHINE_I386, IMAGE_FILE_MACHINE_IA64,
IMAGE_FILE_MACHINE_M32R, IMAGE_FILE_MACHINE_MIPS16,
IMAGE_FILE_MACHINE_MIPSFPU, IMAGE_FILE_MACHINE_MIPSFPU16,
IMAGE_FILE_MACHINE_POWERPC, IMAGE_FILE_MACHINE_POWERPCFP,
IMAGE_FILE_MACHINE_R4000, IMAGE_FILE_MACHINE_SH3,
IMAGE_FILE_MACHINE_SH3DSP, IMAGE_FILE_MACHINE_SH4,
IMAGE_FILE_MACHINE_SH5, IMAGE_FILE_MACHINE_THUMB,
IMAGE_FILE_MACHINE_WCEMIPSV2,
};
if (std::find(machines.begin(), machines.end(),
coff_header_->get_machine()) == machines.end()) {
if (architecture_ == COFFI_ARCHITECTURE_PE) {
// The DOS header was detected, but the machine is not recognized
// This is an error
clean();
return false;
}
}
else {
// The machine is recognized, it is a PE file
architecture_ = COFFI_ARCHITECTURE_PE;
}
// Try to read a CEVA header
if (architecture_ == COFFI_ARCHITECTURE_NONE) {
// Check the target ID
static const std::vector<uint16_t> machines = {
CEVA_MACHINE_XC4210_LIB,
CEVA_MACHINE_XC4210_OBJ,
};
if (std::find(machines.begin(), machines.end(),
coff_header_->get_machine()) != machines.end()) {
architecture_ = COFFI_ARCHITECTURE_CEVA;
}
}
}
// Try to read a TI header
if (architecture_ == COFFI_ARCHITECTURE_NONE) {
coff_header_ = new coff_header_impl_ti;
stream.seekg(0);
if (!coff_header_->load(stream)) {
clean();
return false;
}
// Check the target ID
static const std::vector<uint16_t> target_ids = {
TMS470, TMS320C5400, TMS320C6000, TMS320C5500,
TMS320C2800, MSP430, TMS320C5500plus,
};
if (std::find(target_ids.begin(), target_ids.end(),
coff_header_->get_target_id()) != target_ids.end()) {
architecture_ = COFFI_ARCHITECTURE_TI;
}
}
if (architecture_ == COFFI_ARCHITECTURE_NONE) {
// The format is not recognized, default to PE format header
coff_header_ = new coff_header_impl;
stream.seekg(0);
if (!coff_header_->load(stream)) {
clean();
return false;
}
}
if (coff_header_->get_optional_header_size()) {
if (architecture_ == COFFI_ARCHITECTURE_PE) {
std::streampos pos = stream.tellg();
optional_header_ = new optional_header_impl_pe;
if (!optional_header_->load(stream)) {
clean();
return false;
}
if (optional_header_->get_magic() == OH_MAGIC_PE32PLUS) {
delete optional_header_;
stream.seekg(pos);
optional_header_ = new optional_header_impl_pe_plus;
if (!optional_header_->load(stream)) {
clean();
return false;
}
}
}
else {
if (architecture_ == COFFI_ARCHITECTURE_NONE) {
optional_header_ = new optional_header_impl_pe;
}
if (architecture_ == COFFI_ARCHITECTURE_CEVA) {
optional_header_ = new optional_header_impl_pe;
}
if (architecture_ == COFFI_ARCHITECTURE_TI) {
optional_header_ = new optional_header_impl_ti;
}
if (!optional_header_->load(stream)) {
clean();
return false;
}
}
if (architecture_ == COFFI_ARCHITECTURE_PE) {
if (optional_header_->get_magic() == OH_MAGIC_PE32PLUS) {
win_header_ = new win_header_impl<win_header_pe_plus>;
}
else if (optional_header_->get_magic() == OH_MAGIC_PE32 ||
optional_header_->get_magic() == OH_MAGIC_PE32ROM) {
win_header_ = new win_header_impl<win_header_pe>;
}
if (win_header_) {
if (!win_header_->load(stream)) {
delete optional_header_;
optional_header_ = 0;
delete win_header_;
win_header_ = 0;
clean();
return false;
}
directories_.load(stream);
}
}
if (architecture_ == COFFI_ARCHITECTURE_TI) {
// No documented optional_header_->get_magic() values
// Cannot check anything
}
}
std::streampos pos = stream.tellg();
if (!load_strings(stream, coff_header_)) {
clean();
return false;
}
if (!load_symbols(stream, coff_header_)) {
clean();
return false;
}
stream.seekg(pos);
if (!load_section_headers(stream)) {
clean();
return false;
}
if (!directories_.load_data(stream)) {
clean();
return false;
}
return true;
}
//---------------------------------------------------------------------
/*! @brief Creates a file in COFF binary format.
*
* Before saving, performs the following modififications:
* - Layout (see layout()): Compute the alignement, offsets, etc.
* - Compute the headers fields than can be guessed from the data, like:
* - Number of sections, directories, etc.
* - Sizes of headers
*
* @param[in] file_name File path of the file to create.
* @return true if the file has been created successfully, false otherwise.
*/
bool save(const std::string& file_name)
{
std::ofstream stream;
stream.open(file_name, std::ios::out | std::ios::binary);
if (!stream) {
return false;
}
return save(stream);
}
//---------------------------------------------------------------------
/*! @brief @copybrief save(const std::string&)
*
* See save(const std::string&).
*
* @param[in] stream File to create, as an opened stream.
*/
bool save(std::ostream& stream)
{
if (win_header_) {
std::stringstream tmp_stream(std::ios::in | std::ios::out |
std::ios::binary);
if (!tmp_stream) {
return false;
}
if (!save_to_stream(tmp_stream)) {
return false;
}
tmp_stream.seekg(0);
return compute_win_header_checksum(tmp_stream, stream);
}
else {
return save_to_stream(stream);
}
return true;
}
//---------------------------------------------------------------------
/*! @brief Cleans and/or initializes the coffi object.
*
* @param[in] architecture COFF architecture, see #coffi_architecture_t for the list of supported architectures
*/
void create(coffi_architecture_t architecture)
{
clean();
architecture_ = architecture;
if (architecture_ == COFFI_ARCHITECTURE_PE) {
coff_header_ = new coff_header_impl;
coff_header_->set_machine(IMAGE_FILE_MACHINE_I386);
}
if (architecture_ == COFFI_ARCHITECTURE_CEVA) {
coff_header_ = new coff_header_impl;
coff_header_->set_machine(CEVA_MACHINE_XC4210_OBJ);
}
if (architecture_ == COFFI_ARCHITECTURE_TI) {
coff_header_ = new coff_header_impl_ti;
coff_header_->set_target_id(TMS320C2800);
}
}
//---------------------------------------------------------------------
/*! @brief Initializes an optional header for the coffi object.
*
* The optional header format depends on the architecture:
* - For PE files (#COFFI_ARCHITECTURE_PE), The following headers are created:
* - MS-DOS header: msdos_header
* - COFF optional header: coff_optional_header_pe or optional_header_impl_pe_plus (depending on **magic**)
* - Windows NT header: win_header_pe or win_header_pe_plus (depending on **magic**)
* - For TI files (#COFFI_ARCHITECTURE_TI): see common_optional_header_ti.
* - For CEVA files (#COFFI_ARCHITECTURE_CEVA): see coff_optional_header_pe.
*
* @param[in] magic Used only for the PE files (#COFFI_ARCHITECTURE_PE):
* #OH_MAGIC_PE32 for PE32 format, #OH_MAGIC_PE32PLUS for PE32+ format.
*/
void create_optional_header(uint16_t magic = OH_MAGIC_PE32)
{
if (dos_header_) {
delete dos_header_;
}
if (optional_header_) {
delete optional_header_;
}
if (architecture_ == COFFI_ARCHITECTURE_PE) {
dos_header_ = new dos_header;
if (magic == OH_MAGIC_PE32PLUS) {
optional_header_ = new optional_header_impl_pe_plus;
}
else {
optional_header_ = new optional_header_impl_pe;
}
optional_header_->set_magic(magic);
create_win_header();
}
if (architecture_ == COFFI_ARCHITECTURE_CEVA) {
optional_header_ = new optional_header_impl_pe;
}
if (architecture_ == COFFI_ARCHITECTURE_TI) {
optional_header_ = new optional_header_impl_ti;
}
coff_header_->set_optional_header_size(
optional_header_->get_sizeof() + win_header_->get_sizeof() +
directories_.size() * sizeof(image_data_directory));
}
//---------------------------------------------------------------------
/*! @brief Returns the MS-DOS header
*
* @return nullptr if the MS-DOS header is not initialized (see create_optional_header()), or not loaded (see load()), or not relevant for the architecture.
*/
dos_header* get_msdos_header() { return dos_header_; }
//---------------------------------------------------------------------
/*! @copydoc get_msdos_header()
*/
const dos_header* get_msdos_header() const { return dos_header_; }
//---------------------------------------------------------------------
/*! @brief Returns the COFF header
*
* @return nullptr if the coffi object is not initialized (see create()), or not loaded (see load()).
*/
coff_header* get_header() { return coff_header_; }
//---------------------------------------------------------------------
/*! @copydoc get_header()
*/
const coff_header* get_header() const { return coff_header_; }
//---------------------------------------------------------------------
/*! @brief Returns the optional COFF header
*
* @return nullptr if the optional COFF header is not initialized (see create_optional_header()), or not loaded (see load()).
*/
optional_header* get_optional_header() { return optional_header_; }
//---------------------------------------------------------------------
/*! @copydoc get_optional_header()
*/
const optional_header* get_optional_header() const
{
return optional_header_;
}
//---------------------------------------------------------------------
/*! @brief Returns the Windows NT header
*
* @return nullptr if the Windows NT header is not initialized (see create_optional_header()), or not loaded (see load()), or not relevant for the architecture.
*/
win_header* get_win_header() { return win_header_; }
//---------------------------------------------------------------------
/*! @copydoc get_win_header()
*/
const win_header* get_win_header() const { return win_header_; }
//---------------------------------------------------------------------
/*! @brief Returns a list of the COFF sections
*
* @return Empty list if the coffi object is not initialized.
*/
sections& get_sections() { return sections_; }
//---------------------------------------------------------------------
/*! @copydoc get_sections()
*/
const sections& get_sections() const { return sections_; }
//---------------------------------------------------------------------
/*! @brief Add a COFF section
*
* @param[in] name The section name
* @return A pointer to the newly created section.
*/
section* add_section(const std::string& name)
{
section* sec;
if (architecture_ == COFFI_ARCHITECTURE_TI) {
sec = new section_impl_ti{this, this, this};
}
else {
sec = new section_impl{this, this, this};
}
sec->set_index(sections_.size());
sec->set_name(name);
sections_.push_back(sec);
return sec;
}
//---------------------------------------------------------------------
/*! @brief Returns a list of the PE data directories.
*
* This function is releveant only for the PE architecture (see #COFFI_ARCHITECTURE_PE).
*
* @return Empty list if the PE data directories are not initialized, or not relevant for the architecture.
*/
directories& get_directories() { return directories_; }
//---------------------------------------------------------------------
/*! @copydoc get_directories()
*/
const directories& get_directories() const { return directories_; }
//---------------------------------------------------------------------
/*! @brief Add a PE data directory.
*
* This function is releveant only for the PE architecture (see #COFFI_ARCHITECTURE_PE).
*
* @param[in] rva_and_size Relative virtual address (RVA) and size
* @return A pointer to the newly created directory.
*/
directory* add_directory(const image_data_directory& rva_and_size)
{
directory* d = new directory(directories_.size());
d->set_virtual_address(rva_and_size.virtual_address);
d->set_size(rva_and_size.size);
directories_.push_back(d);
return d;
}
//---------------------------------------------------------------------
/*! @brief PE32+ format check.
*
* @return true if the file is initialized and is a PE file (see #COFFI_ARCHITECTURE_PE), with a magic number indicating a PE32+ file (see #OH_MAGIC_PE32PLUS).
*/
bool is_PE32_plus()
{
bool ret = false;
if (optional_header_ &&
optional_header_->get_magic() == OH_MAGIC_PE32PLUS) {
ret = true;
}
return ret;
}
//---------------------------------------------------------------------
//! @copydoc architecture_provider::get_addressable_unit()
int get_addressable_unit() const
{
if (!coff_header_) {
return 0;
}
if (architecture_ == COFFI_ARCHITECTURE_TI) {
switch (coff_header_->get_target_id()) {
case TMS320C5400:
case TMS320C5500:
case TMS320C2800:
return 2;
default:
return 1;
}
}
return 1;
}
//---------------------------------------------------------------------
//! @copydoc architecture_provider::get_architecture()
coffi_architecture_t get_architecture() const { return architecture_; }
//---------------------------------------------------------------------
/*! @brief Performs the layout of the file.
*
* The layout consists in:
* - Compute the sections alignement,
* - Compute the offsets for: sections, directories, relocations, line numbers, string table offset, etc.
*/
void layout()
{
// Order all the data pages to be written
clean_unused_spaces();
populate_data_pages();
compute_offsets();
// Eventually add unused spaces to respect the file alignment
populate_data_pages();
apply_file_alignment();
populate_data_pages();
compute_offsets();
}
//---------------------------------------------------------------------
private:
//---------------------------------------------------------------------
void clean()
{
if (dos_header_) {
delete dos_header_;
dos_header_ = 0;
}
if (coff_header_) {
delete coff_header_;
coff_header_ = 0;
}
if (optional_header_) {
delete optional_header_;
optional_header_ = 0;
}
if (win_header_) {
delete win_header_;
win_header_ = 0;
}
clean_unused_spaces();
sections_.clean();
directories_.clean();
clean_symbols();
clean_strings();
}
//---------------------------------------------------------------------
void create_win_header()
{
if (win_header_) {
delete win_header_;
}
if (optional_header_->get_magic() == OH_MAGIC_PE32PLUS) {
win_header_ = new win_header_impl<win_header_pe_plus>;
}
else if (optional_header_->get_magic() == OH_MAGIC_PE32 ||
optional_header_->get_magic() == OH_MAGIC_PE32ROM) {
win_header_ = new win_header_impl<win_header_pe>;
}
}
//---------------------------------------------------------------------
bool load_section_headers(std::istream& stream)
{
std::streampos pos = stream.tellg();
for (int i = 0; i < coff_header_->get_sections_count(); ++i) {
section* sec;
switch (architecture_) {
case COFFI_ARCHITECTURE_PE:
case COFFI_ARCHITECTURE_CEVA:
sec = new section_impl(this, this, this);
break;
case COFFI_ARCHITECTURE_TI:
sec = new section_impl_ti(this, this, this);
break;
default:
sec = new section_impl(this, this, this);
break;
}
if (!(sec->load(stream, i * sec->get_sizeof() + pos))) {
return false;
}
sec->set_index(i);
sections_.push_back(sec);
}
return true;
}
//---------------------------------------------------------------------
bool save_to_stream(std::ostream& stream)
{
// Layout the sections and other data pages
layout();
// Compute the header fields
coff_header_->set_sections_count(sections_.size());
if (symbols_.size() > 0) {
coff_header_->set_symbols_count(
symbols_.back().get_index() +
symbols_.back().get_aux_symbols_number() + 1);
}
else {
coff_header_->set_symbols_count(0);
}
coff_header_->set_optional_header_size(0);
if (optional_header_) {
coff_header_->set_optional_header_size(
coff_header_->get_optional_header_size() +
optional_header_->get_sizeof());
}
if (win_header_) {
win_header_->set_number_of_rva_and_sizes(directories_.size());
coff_header_->set_optional_header_size(
coff_header_->get_optional_header_size() +
win_header_->get_sizeof() + directories_.get_sizeof());
}
if ((architecture_ == COFFI_ARCHITECTURE_PE) && dos_header_) {
dos_header_->save(stream);
}
coff_header_->save(stream);
if (optional_header_) {
optional_header_->save(stream);
if (architecture_ == COFFI_ARCHITECTURE_PE) {
if (win_header_) {
win_header_->save(stream);
directories_.save(stream);
}
}
}
for (auto sec : sections_) {
sec->save_header(stream);
}
for (auto dp : data_pages_) {
section* sec;
switch (dp.type) {
case DATA_PAGE_RAW:
sec = sections_[dp.index];
sec->save_data(stream);
break;
case DATA_PAGE_RELOCATIONS:
sec = sections_[dp.index];
sec->save_relocations(stream);
break;
case DATA_PAGE_LINE_NUMBERS:
sec = sections_[dp.index];
sec->save_line_numbers(stream);
break;
case DATA_PAGE_DIRECTORY:
directories_[dp.index]->save_data(stream);
break;
case DATA_PAGE_UNUSED:
stream.write(unused_spaces_[dp.index].data,
unused_spaces_[dp.index].size);
break;
}
}
save_symbols(stream);
save_strings(stream);
return true;
}
//---------------------------------------------------------------------
bool compute_win_header_checksum(std::istream& src, std::ostream& dst)
{
if (!dos_header_ || !coff_header_ || !optional_header_ ||
!win_header_) {
return false;
}
// Get the file size
src.seekg(0, std::ios::end);
uint32_t file_size = src.tellg();
src.seekg(0);
// Compute the checksum offset
uint32_t chk_offset = dos_header_->get_pe_sign_location() + 4 +
coff_header_->get_sizeof() +
optional_header_->get_sizeof() +
(is_PE32_plus() ? 40 : 36);
// Copy the file and compute the checksum
uint32_t chk = 0;
for (uint32_t i = 0; i < file_size; i += 2) {
char bytes[2] = {0, 0};
src.read(bytes, 2);
dst.write(bytes, src.gcount());
uint16_t word = *(reinterpret_cast<uint16_t*>(bytes));
if ((i >= chk_offset) && (i < chk_offset + 4)) {
word = 0;
}
chk += word;
chk = (chk >> 16) + (chk & 0xffff);
}
chk = (uint16_t)(((chk >> 16) + chk) & 0xffff);
chk += file_size;
// Update the checksum in the dst stream
dst.seekp(chk_offset);
dst.write(reinterpret_cast<char*>(&chk), 4);
return true;
}
//---------------------------------------------------------------------
void populate_data_pages()
{
data_pages_.clear();
for (auto sec : sections_) {
if (sec->get_data_offset() > 0 || sec->get_data()) {
data_pages_.push_back(
data_page{DATA_PAGE_RAW, sec->get_data_offset(),
sec->get_data_size(), sec->get_index()});
}
if (sec->get_reloc_offset() > 0 || sec->get_reloc_count() > 0) {
data_pages_.push_back(data_page{
DATA_PAGE_RELOCATIONS, sec->get_reloc_offset(),
sec->get_relocations_filesize(), sec->get_index()});
}
if ((architecture_ != COFFI_ARCHITECTURE_TI) &&
(sec->get_line_num_count() > 0)) {
data_pages_.push_back(data_page{
DATA_PAGE_LINE_NUMBERS, sec->get_line_num_offset(),
sec->get_line_numbers_filesize(), sec->get_index()});
}
}
for (auto d : directories_) {
if (d->get_data_filesize() > 0) {
data_pages_.push_back(data_page{DATA_PAGE_DIRECTORY,
d->get_virtual_address(),
d->get_size(), d->get_index()});
}
}
for (uint32_t i = 0; i < unused_spaces_.size(); i++) {
data_pages_.push_back(data_page{DATA_PAGE_UNUSED,
unused_spaces_[i].offset,
unused_spaces_[i].size, i});
}
std::sort(data_pages_.begin(), data_pages_.end(),
[](const data_page& a, const data_page& b) {
if (a.offset != b.offset) {
return a.offset < b.offset;
}
if (a.type != b.type) {
return a.type < b.type;
}
return a.index < b.index;
});
}
//---------------------------------------------------------------------
uint32_t get_header_end_offset()
{
uint32_t offset = 0;
if (dos_header_) {
offset = dos_header_->get_pe_sign_location();
offset += 4;
}
if (coff_header_) {
offset += coff_header_->get_sizeof();
}
if (optional_header_) {
offset += optional_header_->get_sizeof();
}
if (win_header_) {
offset += win_header_->get_sizeof();
}
offset += directories_.get_sizeof();
for (auto sec : sections_) {
offset += sec->get_sizeof();
}
return offset;
}
//---------------------------------------------------------------------
void compute_offsets()
{
uint32_t offset = get_header_end_offset();
for (auto dp : data_pages_) {
auto sec = sections_[dp.index];
switch (dp.type) {
case DATA_PAGE_RAW:
if (sec->get_data()) {
sec->set_data_offset(offset);
offset += sec->get_data_size();
}
else if (sec->get_data_offset() != 0) {
sec->set_data_offset(offset);
}
break;
case DATA_PAGE_RELOCATIONS:
if (sec->get_reloc_count() > 0) {
sec->set_reloc_offset(offset);
}
else {
sec->set_reloc_offset(0);
}
offset += sec->get_relocations_filesize();
break;
case DATA_PAGE_LINE_NUMBERS:
if (sec->get_line_num_count() > 0) {
sec->set_line_num_offset(offset);
}
else {
sec->set_line_num_offset(0);
}
offset += sec->get_line_numbers_filesize();
break;
case DATA_PAGE_DIRECTORY:
if (directories_[dp.index]->get_data_filesize() > 0) {
if (directories_[dp.index]->get_virtual_address() != 0) {
directories_[dp.index]->set_virtual_address(offset);
}
offset += directories_[dp.index]->get_data_filesize();
}
break;
case DATA_PAGE_UNUSED:
offset += unused_spaces_[dp.index].size;
break;
}
}
if (symbols_.size() == 0) {
offset = 0;
}
coff_header_->set_symbol_table_offset(offset);
}
//---------------------------------------------------------------------
void apply_file_alignment()
{
if (!win_header_) {
return;
}
uint32_t file_alignment = win_header_->get_file_alignment();
uint32_t previous_offset = get_header_end_offset();
uint32_t previous_size = previous_offset;
const data_page* previous_dp = nullptr;
for (auto dp = data_pages_.begin(); dp != data_pages_.end(); dp++) {
if ((previous_size % file_alignment) != 0) {
uint32_t size =
file_alignment - (previous_size % file_alignment);
if (previous_dp && previous_dp->type == DATA_PAGE_RAW) {
// Extend the previous section data
char* padding = new char[size];
if (padding) {
std::memset(padding, 0, size);
sections_[previous_dp->index]->append_data(padding,
size);
}
}
else {
// Add an unused space
add_unused_space(previous_offset, size);
}
}
previous_dp = &*dp;
previous_size = dp->size;
if (dp->offset > 0) {
previous_offset = dp->offset + dp->size;
}
}
}
//---------------------------------------------------------------------
void clean_unused_spaces()
{
for (auto us : unused_spaces_) {
delete[] us.data;
}
unused_spaces_.clear();
}
//---------------------------------------------------------------------
void
add_unused_space(uint32_t offset, uint32_t size, uint8_t padding_byte = 0)
{
unused_space us;
us.data = new char[size];
if (us.data) {
std::memset(us.data, padding_byte, size);
us.size = size;
us.offset = offset;
unused_spaces_.push_back(us);
}
}
//---------------------------------------------------------------------
enum data_page_type
{
DATA_PAGE_UNUSED,
DATA_PAGE_RAW,
DATA_PAGE_RELOCATIONS,
DATA_PAGE_LINE_NUMBERS,
DATA_PAGE_DIRECTORY,
};
//---------------------------------------------------------------------
struct data_page
{
data_page_type type;
uint32_t offset;
uint32_t size;
// Section index, directory index, index in unused_spaces_
uint32_t index;
};
//---------------------------------------------------------------------
struct unused_space
{
uint32_t offset;
uint32_t size;
char* data;
};
//---------------------------------------------------------------------
coffi_architecture_t architecture_;
dos_header* dos_header_;
coff_header* coff_header_;
optional_header* optional_header_;
win_header* win_header_;
directories directories_;
sections sections_;
std::vector<unused_space> unused_spaces_;
std::vector<data_page> data_pages_;
};
} // namespace COFFI
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif //COFFI_HPP