From 49f764c3bc27a88d7ed34b15002b3ffdbc37e4a3 Mon Sep 17 00:00:00 2001 From: Nathan Sidwell Date: Thu, 10 Oct 2024 09:20:10 -0400 Subject: [PATCH] #17910: Longer-lived ElfFile implementation state (#13632) --- tt_metal/llrt/tt_elffile.cpp | 80 +++++++++++++++++++++++++++--------- tt_metal/llrt/tt_elffile.hpp | 29 ++++++++++--- tt_metal/llrt/tt_memory.cpp | 8 ++-- 3 files changed, 88 insertions(+), 29 deletions(-) diff --git a/tt_metal/llrt/tt_elffile.cpp b/tt_metal/llrt/tt_elffile.cpp index 56ad4a62b46..edd49f2205f 100644 --- a/tt_metal/llrt/tt_elffile.cpp +++ b/tt_metal/llrt/tt_elffile.cpp @@ -47,7 +47,6 @@ class ElfFile::Impl { private: std::span phdrs_; std::span shdrs_; - std::span strtab_; std::string const &path_; private: @@ -64,6 +63,7 @@ class ElfFile::Impl { Elf32_Ehdr const &GetHeader() const { return *reinterpret_cast(GetContents().data()); } std::span GetPhdrs() const { return phdrs_; } std::span GetShdrs() const { return shdrs_; } + Elf32_Shdr const &GetShdr(unsigned ix) const { return shdrs_[ix]; } std::vector &GetSegments() const { return owner_.segments_; } std::span &GetContents() const { return owner_.contents_; } std::span GetContents(Elf32_Phdr const &phdr) { @@ -72,13 +72,49 @@ class ElfFile::Impl { std::span 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(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(strings.data(), offset); + } + char const *GetName(Elf32_Shdr const &shdr) { return GetString(shdr.sh_name, GetHeader().e_shstrndx); } + std::span GetSymbols(Elf32_Shdr const &shdr) { + auto section = GetContents(shdr); + return std::span(ByteOffset(section.data()), section.size() / shdr.sh_entsize); + } + char const *GetName(Elf32_Sym const &sym, unsigned lk) { return GetString(sym.st_name, lk); } + std::span GetRelocations(Elf32_Shdr const &shdr) { + auto section = GetContents(shdr); + return std::span(ByteOffset(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 @@ -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; @@ -105,14 +152,8 @@ ElfFile::ElfFile(std::string const &path) { contents_ = std::span(reinterpret_cast(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() { @@ -147,7 +188,6 @@ void ElfFile::Impl::LoadImage() { shdrs_ = std::span(ByteOffset(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 §ion : GetShdrs()) @@ -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(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)); } } diff --git a/tt_metal/llrt/tt_elffile.hpp b/tt_metal/llrt/tt_elffile.hpp index e45f6aab97a..edd85599286 100644 --- a/tt_metal/llrt/tt_elffile.hpp +++ b/tt_metal/llrt/tt_elffile.hpp @@ -26,7 +26,7 @@ class ElfFile { struct Segment { std::span 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: @@ -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(); + 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 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 contents_; // Owning buffer + + // The first segment is the text segment, regardless of VMA ordering. std::vector segments_; }; diff --git a/tt_metal/llrt/tt_memory.cpp b/tt_metal/llrt/tt_memory.cpp index 49c11b2b688..2fe87904273 100644 --- a/tt_metal/llrt/tt_memory.cpp +++ b/tt_metal/llrt/tt_memory.cpp @@ -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];