Skip to content

Commit

Permalink
#17910: Longer-lived ElfFile implementation state (#13632)
Browse files Browse the repository at this point in the history
  • Loading branch information
nathan-TT authored Oct 10, 2024
1 parent f4ac0bc commit 49f764c
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 29 deletions.
80 changes: 60 additions & 20 deletions tt_metal/llrt/tt_elffile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ class ElfFile::Impl {
private:
std::span<Elf32_Phdr const> phdrs_;
std::span<Elf32_Shdr const> shdrs_;
std::span<std::byte const> strtab_;
std::string const &path_;

private:
Expand All @@ -64,6 +63,7 @@ class ElfFile::Impl {
Elf32_Ehdr const &GetHeader() const { return *reinterpret_cast<Elf32_Ehdr const *>(GetContents().data()); }
std::span<Elf32_Phdr const> GetPhdrs() const { return phdrs_; }
std::span<Elf32_Shdr const> GetShdrs() const { return shdrs_; }
Elf32_Shdr const &GetShdr(unsigned ix) const { return shdrs_[ix]; }
std::vector<Segment> &GetSegments() const { return owner_.segments_; }
std::span<std::byte> &GetContents() const { return owner_.contents_; }
std::span<std::byte> GetContents(Elf32_Phdr const &phdr) {
Expand All @@ -72,13 +72,49 @@ class ElfFile::Impl {
std::span<std::byte> GetContents(Elf32_Shdr const &shdr) {
return GetContents().subspan(shdr.sh_offset, shdr.sh_size);
}
char const *GetString(size_t offset) {
if (offset < strtab_.size())
return ByteOffset<char const>(strtab_.data(), offset);
else
return "*BADSTRING*";
char const *GetString(size_t offset, unsigned ix) {
if (ix >= GetShdrs().size())
bad:
return "*bad*";
auto &shdr = GetShdr(ix);
if (shdr.sh_type != SHT_STRTAB)
goto bad;
auto strings = GetContents(GetShdr(ix));
if (offset >= strings.size())
goto bad;
return ByteOffset<char const>(strings.data(), offset);
}
char const *GetName(Elf32_Shdr const &shdr) { return GetString(shdr.sh_name, GetHeader().e_shstrndx); }
std::span<Elf32_Sym> GetSymbols(Elf32_Shdr const &shdr) {
auto section = GetContents(shdr);
return std::span(ByteOffset<Elf32_Sym>(section.data()), section.size() / shdr.sh_entsize);
}
char const *GetName(Elf32_Sym const &sym, unsigned lk) { return GetString(sym.st_name, lk); }
std::span<Elf32_Rela> GetRelocations(Elf32_Shdr const &shdr) {
auto section = GetContents(shdr);
return std::span(ByteOffset<Elf32_Rela>(section.data()), section.size() / shdr.sh_entsize);
}

static bool IsInSegment(Segment const &segment, Elf32_Shdr const &shdr) {
// Remember, Segments use word_t sizes
return shdr.sh_flags & SHF_ALLOC && shdr.sh_addr >= segment.address &&
shdr.sh_addr + shdr.sh_size <=
segment.address + (segment.contents.size() + segment.bss) * sizeof (word_t);
}
bool IsInSegment(unsigned ix, Elf32_Shdr const &shdr) const { return IsInSegment(GetSegments()[ix], shdr); }
bool IsInText(Elf32_Shdr const &shdr) const { return IsInSegment(GetSegments().front(), shdr); };
int GetSegmentIx(Elf32_Shdr const &shdr) const {
for (unsigned ix = GetSegments().size(); ix--;)
if (IsInSegment(ix, shdr))
return ix;
return -1;
};
bool IsTextSymbol(Elf32_Sym const &symbol) const {
return symbol.st_shndx < GetShdrs().size() && IsInText(GetShdr(symbol.st_shndx));
}
bool IsDataSymbol(Elf32_Sym const &symbol) const {
return symbol.st_shndx < GetShdrs().size() && GetSegmentIx(GetShdr(symbol.st_shndx)) > 0;
}
char const *GetName(Elf32_Shdr const &shdr) { return GetString(shdr.sh_name); }

private:
template <typename T = std::byte>
Expand All @@ -91,7 +127,18 @@ class ElfFile::Impl {
}
};

ElfFile::ElfFile(std::string const &path) {
ElfFile::~ElfFile() {
ReleaseImpl();
if (!contents_.empty())
munmap(contents_.data(), contents_.size());
}

void ElfFile::ReleaseImpl() {
delete pimpl_;
pimpl_ = nullptr;
}

void ElfFile::ReadImage(std::string const &path) {
int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
struct stat st;
void *buffer = MAP_FAILED;
Expand All @@ -105,14 +152,8 @@ ElfFile::ElfFile(std::string const &path) {

contents_ = std::span(reinterpret_cast<std::byte *>(buffer), st.st_size);

Impl impl(*this, path);

impl.LoadImage();
}

ElfFile::~ElfFile() {
if (!contents_.empty())
munmap(contents_.data(), contents_.size());
pimpl_ = new Impl(*this, path);
pimpl_->LoadImage();
}

void ElfFile::Impl::LoadImage() {
Expand Down Expand Up @@ -147,7 +188,6 @@ void ElfFile::Impl::LoadImage() {
shdrs_ = std::span(ByteOffset<Elf32_Shdr const>(GetContents().data(), hdr.e_shoff), hdr.e_shnum);
if (!hdr.e_shstrndx || hdr.e_shstrndx >= GetShdrs().size())
TT_THROW("{}: string table is missing or malformed", path_);
strtab_ = GetContents(GetShdrs()[hdr.e_shstrndx]);

// We care about the location of some sections.
for (auto const &section : GetShdrs())
Expand Down Expand Up @@ -186,13 +226,13 @@ void ElfFile::Impl::LoadImage() {
offset_t mem_size = (phdr.p_memsz + sizeof(word_t) - 1) / sizeof(word_t);
GetSegments().emplace_back(
std::span(reinterpret_cast<word_t const *>(contents.data()), file_size),
phdr.p_vaddr / sizeof(word_t),
mem_size - file_size);
phdr.p_vaddr, mem_size - file_size);
}
if (textIx < 0)
TT_THROW("{}: cannot find text segment", path_);
if (textIx > 0) {
auto &segments = GetSegments();
std::rotate(segments.begin(), std::next(segments.begin(), textIx), segments.end());
auto text = std::next(segments.begin(), textIx);
std::rotate(segments.begin(), text, std::next(text, 1));
}
}
29 changes: 24 additions & 5 deletions tt_metal/llrt/tt_elffile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class ElfFile {

struct Segment {
std::span<word_t const> contents; // Non-owning span
address_t address = 0; // word address or 0 for XIP
address_t address = 0; // byte address or 0 for XIP
offset_t bss = 0; // words of BSS

public:
Expand All @@ -35,29 +35,48 @@ class ElfFile {
};

public:
ElfFile(std::string const &path);
ElfFile() = default;
~ElfFile();

// Uncopyable -- because of the owning buffer
// Uncopyable -- because of the owning buffer & pimpl object.
ElfFile(ElfFile const &) = delete;
ElfFile operator=(ElfFile const &) = delete;

// Move constructable -- take ownership
ElfFile(ElfFile &&s) : contents_(std::move(s.contents_)), segments_(std::move(s.segments_)) {
// Move constructable & assignable -- take ownership
ElfFile(ElfFile &&s) : pimpl_(s.pimpl_),
contents_(std::move(s.contents_)), segments_(std::move(s.segments_)) {
s.contents_ = std::span<std::byte>();
s.pimpl_ = nullptr;
}
ElfFile &operator=(ElfFile &&s) {
std::swap(contents_, s.contents_);
segments_ = std::move(s.segments_);
std::swap(pimpl_, s.pimpl_);
return *this;
}

public:
std::vector<Segment> const &GetSegments() const { return segments_; }

public:
// Release the implementation data, leaving the segments and
// contents. Use this, after processing, if the elf object is long-lived.
void ReleaseImpl();

// Read an elf file, populate segments vector.
// Path must remain live throughout processing.
void ReadImage(std::string const &path);

private:
class Impl;
// We can't use unique_ptr here, because the above move semantics
// would require Impl be complete at this point, which is what
// we're trying to avoid.
Impl *pimpl_ = nullptr;

std::span<std::byte> contents_; // Owning buffer

// The first segment is the text segment, regardless of VMA ordering.
std::vector<Segment> segments_;
};

Expand Down
8 changes: 4 additions & 4 deletions tt_metal/llrt/tt_memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@ memory::memory() {
}

memory::memory(std::string const &path) : memory() {
ElfFile elf(path);
ElfFile elf;

elf.ReadImage(path);

// The ELF file puts the text segment first, but memory wants
// ordered spans.
// FIXME: Perhaps we can relax that?
auto emit_segment = [&](ElfFile::Segment const& segment) {
link_spans_.emplace_back(
// We want the byte address, not the word address
segment.address * sizeof(decltype(data_)::value_type),
segment.contents.size());
segment.address, segment.contents.size());
data_.insert(data_.end(), segment.contents.begin(), segment.contents.end());
};
auto* text = &elf.GetSegments()[0];
Expand Down

0 comments on commit 49f764c

Please sign in to comment.