Browse Source

Checkpoint: playing with ELF reader, it's broken

The offset of the section header table looks correct, but the read is
failing. Need to investigate further.
assembler
Zack Marvel 8 months ago
parent
commit
15a4903fa4
9 changed files with 281 additions and 30 deletions
  1. +3
    -0
      .gitmodules
  2. +6
    -4
      Makefile
  3. +73
    -12
      include/elf.hpp
  4. +27
    -0
      include/elf_reader.hpp
  5. +25
    -0
      include/elf_writer.hpp
  6. +1
    -0
      lib/expected
  7. +82
    -0
      src/elf_reader.cpp
  8. +51
    -0
      src/elf_writer.cpp
  9. +13
    -14
      src/main.cpp

+ 3
- 0
.gitmodules View File

@@ -0,0 +1,3 @@
[submodule "lib/expected"]
path = lib/expected
url = https://github.com/TartanLlama/expected.git

+ 6
- 4
Makefile View File

@@ -1,6 +1,6 @@

CXXFLAGS += -ggdb -Wextra -Wall
LDFLAGS += -ggdb -Wextra -Wall
CXXFLAGS += -ggdb -Wextra -Wall -m32
LDFLAGS += -ggdb -Wextra -Wall -m32

EXE_SRC = src/main.cpp
EXE = gbas
@@ -9,13 +9,15 @@ SRCS = src/tokenizer.cpp \
src/parser.cpp \
src/assembler.cpp \
src/elf.cpp \
src/elf_writer.cpp \
src/elf_reader.cpp \

OBJS = $(SRCS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d)
COVS = $(SRCS:.cpp=.gcda) \
$(SRCS:.cpp=.gcno)
EXE_OBJS = $(OBJS) $(EXE_SRC:.cpp=.o)
INC = -Iinclude
INC = -Iinclude -Ilib/expected/include

TEST_SRCS = test/char_utils_test.cpp \
test/tokenizer_test.cpp \
@@ -48,7 +50,7 @@ check: $(TEST_EXE)
ifdef CHECK_LOG
-./$(TEST_EXE) $(CHECK_OPTIONS) > $(CHECK_LOG)
else
-./$(TEST_EXE) $(CHECK_OPTIONS)
-./$(TEST_EXE) $(CHECK_OPTIONS)
endif

%.o: %.cpp Makefile


+ 73
- 12
include/elf.hpp View File

@@ -7,6 +7,7 @@
#include <unordered_set>
#include <vector>
#include <ostream>
#include <numeric>

#include <elf.h>

@@ -51,6 +52,8 @@ class ISection {

Elf32_Shdr& header() { return mHeader; }

virtual size_t size() const = 0;

class Type {
public:
// no type by default
@@ -132,6 +135,13 @@ class ProgramSection : public Section<SectionType::PROGBITS> {
, mData{}
{ }

virtual size_t size() const override { return data().size(); }

const std::vector<uint8_t>& data() const {
return mData;
}


std::vector<uint8_t>& data() {
return mData;
}
@@ -156,6 +166,22 @@ class StrTabSection : public Section<SectionType::STRTAB> {
, mStrings{}
{ }

virtual size_t size() const override {
// TODO maybe we should add 1 to the length of every string to account for
// the terminating byte
return std::accumulate(strings().begin(),
strings().end(),
0,
[](size_t total_len, std::string st) -> size_t {
return total_len + st.size();
});
}

const StringTable& strings() const {
return mStrings;
}


StringTable& strings() {
return mStrings;
}
@@ -174,6 +200,7 @@ class SymTabSection : public Section<SectionType::SYMTAB> {
: Section<SectionType::SYMTAB>()
, mSymbols{}
{
// First entry is always full of zeros.
mSymbols.emplace_back(
Elf32_Sym{.st_name = 0,
.st_value = 0,
@@ -196,6 +223,16 @@ class SymTabSection : public Section<SectionType::SYMTAB> {
.st_shndx = 0});
}

virtual size_t size() const override {
// TODO maybe we should add 1 to the length of every string to account for
// the terminating byte
return sizeof(Symbol) * symbols().size();
}

const SymbolTable& symbols() const {
return mSymbols;
}

SymbolTable& symbols() {
return mSymbols;
}
@@ -220,6 +257,10 @@ class RelSection : public Section<SectionType::REL> {
, mRelocations{}
{ }

virtual size_t size() const override {
return sizeof(Relocation) * mRelocations.size();
}

const std::string& other() { return mOther; }

RelocationTable& relocations() {
@@ -237,12 +278,11 @@ using SectionList = std::vector<std::unique_ptr<ISection>>;
* Models an ELF file, for the purposes of Game Boy programs. This means there
* is currently no support for e.g. dynamic linking or executable files.
*/
class ELF {
public:
class ELF
{
public:
ELF();

void write(std::ostream& out);

/**
* Add a symbol to the current section.
*
@@ -261,9 +301,13 @@ class ELF {
* @returns a reference to the symbol in its symbol table.
* @throws ELFException if a symbol with that name already exists.
*/
Elf32_Sym& addSymbol(const std::string name, uint32_t value, uint32_t size,
ISection::Type type, ISection::Binding bind,
ISection::Visibility visibility, bool relocatable);
Elf32_Sym& addSymbol(const std::string name,
uint32_t value,
uint32_t size,
ISection::Type type,
ISection::Binding bind,
ISection::Visibility visibility,
bool relocatable);

/**
* Add str to the string table.
@@ -299,8 +343,11 @@ class ELF {
*/
void computeSectionOffsets();

protected:
Elf32_Ehdr& header() { return mHeader; }

const SectionList& sections() { return mSections; }

protected:
/**
* Add a section. Only in-place construction supported.
*
@@ -343,14 +390,22 @@ class ELF {
*/
uint32_t mShStrTabIdx;

StrTabSection& shStringTable() { return dynamic_cast<StrTabSection&>(*mSections.at(mShStrTabIdx)); }
public:
StrTabSection& shStringTable()
{
return dynamic_cast<StrTabSection&>(*mSections.at(mShStrTabIdx));
}

protected:
/**
* Index of the current string table in mSections.
*/
uint32_t mStrTabIdx;

StrTabSection& stringTable() { return dynamic_cast<StrTabSection&>(*mSections.at(mStrTabIdx)); }
StrTabSection& stringTable()
{
return dynamic_cast<StrTabSection&>(*mSections.at(mStrTabIdx));
}

/**
* Index of the current section in mSections.
@@ -365,14 +420,20 @@ class ELF {
*/
uint32_t mCurrRelIdx;

RelSection& currentRelocationSection() { return dynamic_cast<RelSection&>(*mSections.at(mCurrRelIdx)); }
RelSection& currentRelocationSection()
{
return dynamic_cast<RelSection&>(*mSections.at(mCurrRelIdx));
}

/**
* Index of current symbol table in mSections.
*/
uint32_t mCurrSymTabIdx;

SymTabSection& currentSymbolTable() { return dynamic_cast<SymTabSection&>(*mSections.at(mCurrSymTabIdx)); }
SymTabSection& currentSymbolTable()
{
return dynamic_cast<SymTabSection&>(*mSections.at(mCurrSymTabIdx));
}

/**
* While this could eat up a lot of memory for a huge program, it's a lot


+ 27
- 0
include/elf_reader.hpp View File

@@ -0,0 +1,27 @@

#ifndef ELF_READER_HPP
#define ELF_READER_HPP

#include <iostream>
#include <memory>

#include "tl/expected.hpp"

#include "elf.hpp"

namespace GBAS {

class ELFReader {
public:
ELFReader() : elf_{} { }

static tl::expected<std::unique_ptr<ELFReader>, std::string> read(
std::istream& is);

private:
ELF elf_;
};

}

#endif // ELF_READER_HPP

+ 25
- 0
include/elf_writer.hpp View File

@@ -0,0 +1,25 @@

#ifndef ELF_WRITER_HPP
#define ELF_WRITER_HPP

#include <iostream>

#include "elf.hpp"

namespace GBAS {

class ELFWriter {
public:
ELFWriter(ELF& elf) : elf_{elf} { }
ELFWriter() = delete;

void write(std::ostream& os);
size_t populate_section_headers(size_t starting_offs);

private:
ELF& elf_;
};

}

#endif // ELF_WRITER_HPP

+ 1
- 0
lib/expected

@@ -0,0 +1 @@
Subproject commit 1d9c5d8c0da84b8ddc54bd3d90d632eec95c1f13

+ 82
- 0
src/elf_reader.cpp View File

@@ -0,0 +1,82 @@

#include "elf_reader.hpp"

using namespace GBAS;

bool check_ident(unsigned char ident[EI_NIDENT]) {
const char expected[] = {
0x7f, 'E', 'L', 'F',
};

return strncmp(reinterpret_cast<char*>(ident), expected, sizeof(expected)) == 0;
}

tl::expected<std::unique_ptr<ELFReader>, std::string>
ELFReader::read(std::istream& is)
{
auto pelf = std::make_unique<ELF>();

auto& header = pelf->header();

{
auto buf = std::make_unique<char[]>(sizeof(header));
if (is.read(buf.get(), sizeof(header))) {
memcpy(&header, buf.get(), sizeof(header));
} else {
return tl::make_unexpected("Failed to read header from input stream");
}
}

if (!check_ident(header.e_ident)) {
std::stringstream ss;
ss << "Invalid file header: ";
ss << header.e_ident;
return tl::make_unexpected(ss.str());
}

if (header.e_ident[4] != ELFCLASS32) {
return tl::make_unexpected("Only 32-bit ELF is supported");
}

off_t shoff = header.e_shoff;
// This assumes all the content we care about in the file follows the section
// header table
std::cout << "shoff: " << header.e_shoff << std::endl;

if (!is.seekg(shoff, std::ios_base::cur)) {
return tl::make_unexpected("Unable to seek to section header table");
}

std::cout << "shentsize: " << header.e_shentsize << std::endl;
std::cout << "shnum: " << header.e_shnum << std::endl;
std::vector<Elf32_Shdr> shtab{};
{
size_t shsize = header.e_shentsize * header.e_shnum;
auto pshbuf = std::make_unique<char[]>(shsize);
if (is.read(pshbuf.get(), shsize)) {
for (size_t i = 0; i < header.e_shnum; i++) {
Elf32_Shdr shdr;
memcpy(&shdr, pshbuf.get() + i*sizeof(shdr), sizeof(shdr));
shtab.push_back(shdr);
}
} else {
return tl::make_unexpected("Unable to read section header table");
}
}

for (auto it = shtab.begin(); it != shtab.end(); it++) {
std::cout
<< "name: " << it->sh_name << std::endl
<< "type: " << it->sh_type << std::endl
<< "flags: " << it->sh_flags << std::endl
<< "addr: " << it->sh_addr << std::endl
<< "offset: " << it->sh_offset << std::endl
<< "size: " << it->sh_size << std::endl
<< "link: " << it->sh_link << std::endl
<< "info: " << it->sh_info << std::endl
<< "addralign: " << it->sh_addralign << std::endl
<< "entsize: " << it->sh_entsize << std::endl << std::endl;
}


}

+ 51
- 0
src/elf_writer.cpp View File

@@ -0,0 +1,51 @@

#include "elf_writer.hpp"

using namespace GBAS;

void ELFWriter::write(std::ostream& os) {
// There are some sections of headers that we need to fill out or verify as
// we go. In the ELF header:
// - e_shoff, the section header table's offset
// - e_shnum, the number of entries in the section header table
// - e_shstrndx, the section header table index of the section name string
// table's header
// In section headers:
// - sh_name, an index into the section name string table where the section's
// name is stored.
// - sh_addr, the memory address where the section should reside
// - sh_offset, the offset from the beginning of the file to the section
// - sh_size, the size of the section in bytes
//
// Just as importantly, the section header table and section name string table
// need to be constructed.

// Let the section header table immediate follow the ELF header.
auto& elf_hdr = elf_.header();
elf_hdr.e_shoff = sizeof(elf_hdr);
elf_hdr.e_shnum = elf_.sections().size();
}

size_t ELFWriter::populate_section_headers(size_t starting_offs) {
size_t offs = starting_offs;
auto& shstrtab = elf_.shStringTable();
for (auto section_it = elf_.sections().begin();
section_it != elf_.sections().end();
section_it++) {
auto& section = *section_it;
auto hdr = section->header();

// populate index of section's name
const auto name_idx = shstrtab.strings().size();
shstrtab.strings().push_back(section->name());
hdr.sh_name = name_idx;

// TODO hdr.sh_addr

hdr.sh_offset = offs;

hdr.sh_size = section->size();

offs += hdr.sh_size;
}
}

+ 13
- 14
src/main.cpp View File

@@ -3,22 +3,25 @@
#include <fstream>

#include "tokenizer.hpp"
#include "elf_reader.hpp"

class InputFile {
public:
InputFile(std::string filename) : filename{filename}, stream{filename} {}
InputFile(std::string filename) : filename_{filename}, stream_{filename} {}

~InputFile() { stream.close(); }
~InputFile() { stream_.close(); }

bool exists() { return stream.is_open(); }
bool exists() { return stream_.is_open(); }

std::ifstream& getStream() { return stream; }
std::ifstream& stream() { return stream_; }

private:
std::string filename;
std::ifstream stream;
std::string filename_;
std::ifstream stream_;
};

using namespace GBAS;

static const std::string USAGE = " <input file>";
int main(int argc, char* argv[]) {
if (argc < 2) {
@@ -32,14 +35,10 @@ int main(int argc, char* argv[]) {
return -1;
}

auto tokenizer = Tokenizer{};
// Have to explicitly type because tokens is inferred wrong
// TODO investigate
TokenList tokens = tokenizer.tokenize(infile.getStream());
std::cout << tokens.size() << std::endl;
for (auto it = tokens.begin(); it != tokens.end(); it++) {
std::cout << *it << std::endl;
}
ELFReader::read(infile.stream()).or_else([](std::string msg) {
std::cerr << msg << std::endl;
});


return 0;
}

Loading…
Cancel
Save