From d605b7ff816d8b59727d54cfc682d08170f8f1c5 Mon Sep 17 00:00:00 2001 From: Peter Edwards Date: Thu, 13 Jun 2024 02:05:39 -0700 Subject: [PATCH] Fix FDE printing Iterating over all the FDEs in the dump was broken by the lazy eval of FDEs. This is a quick-and-dirty fix. We need to make the cie and fde sets mutable, which is in-keeping with other similar lazy caches, and, for now, call "ensureFDEs" to make sure all FDEs are available. It would be better to have a proper iterable/iterator type that would do this behind the scenes. --- dump.cc | 1 + dwarf_frame.cc | 61 ++++++++++++++++++++++++++++------------------- libpstack/dwarf.h | 17 +++++++------ reader.cc | 1 + 4 files changed, 48 insertions(+), 32 deletions(-) diff --git a/dump.cc b/dump.cc index 56d9632..02f1527 100644 --- a/dump.cc +++ b/dump.cc @@ -499,6 +499,7 @@ operator << (std::ostream &os, const JSON &info) { Mapper ciesByString(info.object.cies); + info.object.ensureFDEs(); return JObject(os) .field("cielist", ciesByString, &info.object) .field("fdelist", info.object.fdes, &info.object) diff --git a/dwarf_frame.cc b/dwarf_frame.cc index 38254e4..0af5fdc 100644 --- a/dwarf_frame.cc +++ b/dwarf_frame.cc @@ -97,7 +97,7 @@ static size_t sizeForEncoding( ExceptionHandlingEncoding ehe ) { } void -CFI::putCIE(Elf::Addr offset, DWARFReader &r, Elf::Addr end) { +CFI::putCIE(Elf::Addr offset, DWARFReader &r, Elf::Addr end) const { cies.emplace(std::piecewise_construct, std::forward_as_tuple(offset), std::forward_as_tuple(this, r, end)); @@ -107,7 +107,7 @@ CFI::putCIE(Elf::Addr offset, DWARFReader &r, Elf::Addr end) { // CIE/FDE The header indicates if its a CIE or FDE - an FDE starts with a // reference to the CIE, while a CIE starts with a reference of "-1" std::pair> -CFI::putFDEorCIE( DWARFReader &reader ) { +CFI::putFDEorCIE( DWARFReader &reader ) const { size_t startOffset = reader.getOffset(); Elf::Off associatedCIE; Elf::Off nextoff = decodeCIEFDEHdr(reader, type, &associatedCIE); @@ -123,7 +123,7 @@ CFI::putFDEorCIE( DWARFReader &reader ) { auto [ success, notAnFde ] = putFDEorCIE(r2); assert(success && notAnFde == nullptr); } - std::unique_ptr fde = std::make_unique(this, reader, associatedCIE, nextoff); + std::unique_ptr fde = std::make_unique(*this, reader, associatedCIE, nextoff); reader.setOffset( nextoff ); return {true, std::move(fde) }; } @@ -212,33 +212,44 @@ CFI::CFI(const Info *info, FIType type_) return l->iloc < r->iloc; }); } + +void +CFI::ensureFDE(size_t idx) const { + auto &entry = fdes[idx]; + if (entry != nullptr) + return; + size_t encodingSize = sizeForEncoding( ExceptionHandlingEncoding(fdeTableEnc) ); + DWARFReader tableReader( fdeTable, encodingSize * 2 * idx ); + auto [fdeAddr,indirectAddr] = decodeAddress(tableReader, fdeTableEnc, ehFrameHdrAddr); + (void)fdeAddr; + (void)indirectAddr; + auto [fdeOff,indirectOff] = decodeAddress(tableReader, fdeTableEnc, ehFrameHdrAddr); + DWARFReader fdeReader( io, fdeOff - sectionAddr ); + auto [ success, newEntry ] = putFDEorCIE( fdeReader ); + entry = std::move(newEntry); + assert(fdeAddr == entry->iloc); +} + +void +CFI::ensureFDEs() const { + if (fdeTable == nullptr) + return; + for (size_t i = 0; i < fdes.size(); ++i) + ensureFDE(i); + fdeTable.reset(); // We don't need this anymore, as we've read all the FDEs. +} + const FDE * -CFI::findFDE(Elf::Addr addr) -{ +CFI::findFDE(Elf::Addr addr) const { // No FDE found. Check the lookup table. uintptr_t start = 0; uintptr_t end = fdes.size(); - DWARFReader fdeReader( io ); - - size_t encodingSize = sizeForEncoding( ExceptionHandlingEncoding(fdeTableEnc) ); while (start < end) { auto mid = start + (end - start) / 2; + ensureFDE(mid); auto &entry = fdes[mid]; - if (entry == nullptr) { - DWARFReader tableReader( fdeTable, encodingSize * 2 * mid ); - auto [fdeAddr,indirectAddr] = decodeAddress(tableReader, fdeTableEnc, ehFrameHdrAddr); - - (void)fdeAddr; - (void)indirectAddr; - - auto [fdeOff,indirectOff] = decodeAddress(tableReader, fdeTableEnc, ehFrameHdrAddr); - fdeReader.setOffset(fdeOff - sectionAddr); - auto [ success, newEntry ] = putFDEorCIE( fdeReader ); - entry = std::move(newEntry); - assert(fdeAddr == entry->iloc); - } if (entry->iloc <= addr) { start = mid + 1; if (addr < entry->iloc + entry->irange) @@ -451,16 +462,16 @@ struct FdeCounter { static FdeCounter fdeCounter; -FDE::FDE(CFI *fi, DWARFReader &reader, Elf::Off cieOff_, Elf::Off endOff_) +FDE::FDE(const CFI &fi, DWARFReader &reader, Elf::Off cieOff_, Elf::Off endOff_) : end(endOff_) , cieOff(cieOff_) { - auto &cie = fi->cies[cieOff]; + auto &cie = fi.cies.at( cieOff ); bool indirect; - std::tie(iloc, indirect) = fi->decodeAddress(reader, cie.addressEncoding, fi->sectionAddr); + std::tie(iloc, indirect) = fi.decodeAddress(reader, cie.addressEncoding, fi.sectionAddr); if (indirect) throw (Exception() << "FDE has indirect encoding for location"); - std::tie(irange, indirect) = fi->decodeAddress(reader, cie.addressEncoding & 0xf, fi->sectionAddr); + std::tie(irange, indirect) = fi.decodeAddress(reader, cie.addressEncoding & 0xf, fi.sectionAddr); assert(!indirect); // we've anded out the indirect encoding flag. if (!cie.augmentation.empty() && cie.augmentation[0] == 'z') { size_t alen = reader.getuleb128(); diff --git a/libpstack/dwarf.h b/libpstack/dwarf.h index f703415..94858dd 100644 --- a/libpstack/dwarf.h +++ b/libpstack/dwarf.h @@ -480,7 +480,7 @@ struct FDE { Elf::Off end; Elf::Off cieOff; std::vector augmentation; - FDE(CFI *, DWARFReader &, Elf::Off cieOff_, Elf::Off endOff_); + FDE(const CFI &, DWARFReader &, Elf::Off cieOff_, Elf::Off endOff_); }; enum RegisterType { @@ -548,7 +548,7 @@ class CFI { Elf::Addr sectionAddr; // virtual address of section (either eh_frame or debug_frame. Elf::Addr ehFrameHdrAddr; // virtual address of eh_frame_hdr FIType type; - std::map cies; + mutable std::map cies; // FDEs are sorted by their iloc field. If we have an fdeTable, then the // table starts out with the correct size, but unpopulated, and searching @@ -558,23 +558,26 @@ class CFI { // with al the FDEs. Currently, this happens for the VDSO in aarch64 // platforms (where there are just a handful of FDEs), and pretty much // everything else has an eh_frame_hdr. - std::vector> fdes; + mutable std::vector> fdes; ExceptionHandlingEncoding fdeTableEnc; // the encoding format of the entries in fdeTable. - Reader::csptr fdeTable; // the start of the table in the eh_frame_hdr section. - std::pair> putFDEorCIE( DWARFReader &reader ); + mutable Reader::csptr fdeTable; // the start of the table in the eh_frame_hdr section. + std::pair> putFDEorCIE( DWARFReader &reader ) const; // cieOFF set to -1 if this is CIE, set to offset of associated CIE for an FDE Elf::Addr decodeCIEFDEHdr(DWARFReader &, FIType, Elf::Off *cieOff) const; bool isCIE(Elf::Addr) const noexcept; //void putCIE(DWARFReader &r); // Put CIE from current offset. - void putCIE(Elf::Addr offset, DWARFReader &r, Elf::Addr end); // put CIE who's header we already decoded. + void putCIE(Elf::Addr offset, DWARFReader &r, Elf::Addr end) const; // put CIE who's header we already decoded. std::pair decodeAddress(DWARFReader &, uint8_t encoding, uintptr_t sectionVa) const; friend std::ostream & ::operator << (std::ostream &os, const JSON &); friend struct FDE; friend struct CIE; + void ensureFDE(size_t idx) const; // ensures that the fde at index idx is preloaded. + void ensureFDEs() const; // ensure all FDEs are pre-loaded. + public: const CIE &getCIE(Elf::Addr off) const { return cies.at(off); @@ -588,7 +591,7 @@ class CFI { CFI &operator = (CFI &&) = delete; ~CFI() = default; - [[nodiscard]] const FDE *findFDE(Elf::Addr); + [[nodiscard]] const FDE *findFDE(Elf::Addr) const; operator bool() const noexcept { return io != nullptr; } // If we know the VA of the byte addressed by the start of the dwarf reader, psas it in "va". }; diff --git a/reader.cc b/reader.cc index acf1682..58ad93c 100644 --- a/reader.cc +++ b/reader.cc @@ -8,6 +8,7 @@ #include "libpstack/fs.h" #include "libpstack/global.h" #include +#include namespace pstack { using std::string;