From 055492de6590cfad05704997de59dd4429bc0a32 Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Mon, 2 Jun 2025 22:31:07 +0000 Subject: [PATCH 01/18] Remove UnwindTable dependency on CIE, and FDE (transitively on llvm/Object) For creating new UnwindTable, two static methods was implemented inside it, to create an instance of it from a CIE or FDE. This static methods are moved out of the class as a library functions. --- .../llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 65 ++++++++++--------- llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp | 14 ++-- .../DebugInfo/DWARF/DWARFDebugFrameTest.cpp | 56 ++++++++-------- 3 files changed, 71 insertions(+), 64 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h index 051ea6e11e351..d48d3b628a9c8 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -311,9 +311,6 @@ class UnwindRow { LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row); -class CIE; -class FDE; - /// A class that contains all UnwindRow objects for an FDE or a single unwind /// row for a CIE. To unwind an address the rows, which are sorted by start /// address, can be searched to find the UnwindRow with the lowest starting @@ -334,6 +331,12 @@ class UnwindTable { assert(Index < size()); return Rows[Index]; } + void insertRow(const UnwindRow &Row) { Rows.push_back(Row); } + + /// Set the last address that this unwinding table refers to. + /// + /// This is used when this table is created based on a FDE. + void setEndAddress(uint64_t Addr) { EndAddress = Addr; } /// Dump the UnwindTable to the stream. /// @@ -352,32 +355,6 @@ class UnwindTable { LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, unsigned IndentLevel = 0) const; - /// Create an UnwindTable from a Common Information Entry (CIE). - /// - /// \param Cie The Common Information Entry to extract the table from. The - /// CFIProgram is retrieved from the \a Cie object and used to create the - /// UnwindTable. - /// - /// \returns An error if the DWARF Call Frame Information opcodes have state - /// machine errors, or a valid UnwindTable otherwise. - LLVM_ABI static Expected create(const CIE *Cie); - - /// Create an UnwindTable from a Frame Descriptor Entry (FDE). - /// - /// \param Fde The Frame Descriptor Entry to extract the table from. The - /// CFIProgram is retrieved from the \a Fde object and used to create the - /// UnwindTable. - /// - /// \returns An error if the DWARF Call Frame Information opcodes have state - /// machine errors, or a valid UnwindTable otherwise. - LLVM_ABI static Expected create(const FDE *Fde); - -private: - RowContainer Rows; - /// The end address when data is extracted from a FDE. This value will be - /// invalid when a UnwindTable is extracted from a CIE. - std::optional EndAddress; - /// Parse the information in the CFIProgram and update the CurrRow object /// that the state machine describes. /// @@ -395,10 +372,40 @@ class UnwindTable { /// DW_CFA_restore and DW_CFA_restore_extended opcodes. Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, const RegisterLocations *InitialLocs); + +private: + RowContainer Rows; + /// The end address when data is extracted from a FDE. This value will be + /// invalid when a UnwindTable is extracted from a CIE. + std::optional EndAddress; }; LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows); +class CIE; + +/// Create an UnwindTable from a Common Information Entry (CIE). +/// +/// \param Cie The Common Information Entry to extract the table from. The +/// CFIProgram is retrieved from the \a Cie object and used to create the +/// UnwindTable. +/// +/// \returns An error if the DWARF Call Frame Information opcodes have state +/// machine errors, or a valid UnwindTable otherwise. +Expected createUnwindTable(const CIE *Cie); + +class FDE; + +/// Create an UnwindTable from a Frame Descriptor Entry (FDE). +/// +/// \param Fde The Frame Descriptor Entry to extract the table from. The +/// CFIProgram is retrieved from the \a Fde object and used to create the +/// UnwindTable. +/// +/// \returns An error if the DWARF Call Frame Information opcodes have state +/// machine errors, or a valid UnwindTable otherwise. +Expected createUnwindTable(const FDE *Fde); + /// An entry in either debug_frame or eh_frame. This entry can be a CIE or an /// FDE. class FrameEntry { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index c46b14b4446f7..cf42151bb84aa 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -203,7 +203,7 @@ raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) { return OS; } -Expected UnwindTable::create(const FDE *Fde) { +Expected llvm::dwarf::createUnwindTable(const FDE *Fde) { const CIE *Cie = Fde->getLinkedCIE(); if (Cie == nullptr) return createStringError(errc::invalid_argument, @@ -217,7 +217,7 @@ Expected UnwindTable::create(const FDE *Fde) { UnwindTable UT; UnwindRow Row; Row.setAddress(Fde->getInitialLocation()); - UT.EndAddress = Fde->getInitialLocation() + Fde->getAddressRange(); + UT.setEndAddress(Fde->getInitialLocation() + Fde->getAddressRange()); if (Error CieError = UT.parseRows(Cie->cfis(), Row, nullptr)) return std::move(CieError); // We need to save the initial locations of registers from the CIE parsing @@ -229,11 +229,11 @@ Expected UnwindTable::create(const FDE *Fde) { // Do not add that to the unwind table. if (Row.getRegisterLocations().hasLocations() || Row.getCFAValue().getLocation() != UnwindLocation::Unspecified) - UT.Rows.push_back(Row); + UT.insertRow(Row); return UT; } -Expected UnwindTable::create(const CIE *Cie) { +Expected llvm::dwarf::createUnwindTable(const CIE *Cie) { // Rows will be empty if there are no CFI instructions. if (Cie->cfis().empty()) return UnwindTable(); @@ -246,7 +246,7 @@ Expected UnwindTable::create(const CIE *Cie) { // Do not add that to the unwind table. if (Row.getRegisterLocations().hasLocations() || Row.getCFAValue().getLocation() != UnwindLocation::Unspecified) - UT.Rows.push_back(Row); + UT.insertRow(Row); return UT; } @@ -605,7 +605,7 @@ void CIE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { CFIs.dump(OS, DumpOpts, /*IndentLevel=*/1, /*InitialLocation=*/{}); OS << "\n"; - if (Expected RowsOrErr = UnwindTable::create(this)) + if (Expected RowsOrErr = createUnwindTable(this)) RowsOrErr->dump(OS, DumpOpts, 1); else { DumpOpts.RecoverableErrorHandler(joinErrors( @@ -633,7 +633,7 @@ void FDE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { CFIs.dump(OS, DumpOpts, /*IndentLevel=*/1, InitialLocation); OS << "\n"; - if (Expected RowsOrErr = UnwindTable::create(this)) + if (Expected RowsOrErr = createUnwindTable(this)) RowsOrErr->dump(OS, DumpOpts, 1); else { DumpOpts.RecoverableErrorHandler(joinErrors( diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp index 2be656547c92e..2f91b1f86f6dd 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp @@ -465,9 +465,9 @@ TEST(DWARFDebugFrame, UnwindTableEmptyRows) { EXPECT_THAT_ERROR(parseCFI(TestCIE, {}), Succeeded()); EXPECT_TRUE(TestCIE.cfis().empty()); - // Verify dwarf::UnwindTable::create() won't result in errors and + // Verify dwarf::createUnwindTable() won't result in errors and // and empty rows are not added to CIE UnwindTable. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestCIE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestCIE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const size_t ExpectedNumOfRows = 0; EXPECT_EQ(RowsOrErr->size(), ExpectedNumOfRows); @@ -486,9 +486,9 @@ TEST(DWARFDebugFrame, UnwindTableEmptyRows) { EXPECT_THAT_ERROR(parseCFI(TestFDE, {}), Succeeded()); EXPECT_TRUE(TestFDE.cfis().empty()); - // Verify dwarf::UnwindTable::create() won't result in errors and + // Verify dwarf::createUnwindTable() won't result in errors and // and empty rows are not added to FDE UnwindTable. - RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); EXPECT_EQ(RowsOrErr->size(), ExpectedNumOfRows); } @@ -504,9 +504,9 @@ TEST(DWARFDebugFrame, UnwindTableEmptyRows_NOPs) { EXPECT_THAT_ERROR(parseCFI(TestCIE, {dwarf::DW_CFA_nop}), Succeeded()); EXPECT_TRUE(!TestCIE.cfis().empty()); - // Verify dwarf::UnwindTable::create() won't result in errors and + // Verify dwarf::createUnwindTable() won't result in errors and // and empty rows are not added to CIE UnwindTable. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestCIE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestCIE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const size_t ExpectedNumOfRows = 0; EXPECT_EQ(RowsOrErr->size(), ExpectedNumOfRows); @@ -525,9 +525,9 @@ TEST(DWARFDebugFrame, UnwindTableEmptyRows_NOPs) { EXPECT_THAT_ERROR(parseCFI(TestFDE, {dwarf::DW_CFA_nop}), Succeeded()); EXPECT_TRUE(!TestFDE.cfis().empty()); - // Verify dwarf::UnwindTable::create() won't result in errors and + // Verify dwarf::createUnwindTable() won't result in errors and // and empty rows are not added to FDE UnwindTable. - RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); EXPECT_EQ(RowsOrErr->size(), ExpectedNumOfRows); } @@ -567,7 +567,7 @@ TEST(DWARFDebugFrame, UnwindTableErrorNonAscendingFDERows) { Succeeded()); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), FailedWithMessage("DW_CFA_set_loc with adrress 0x1000 which" " must be greater than the current row " @@ -603,7 +603,7 @@ TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_restore_state) { Succeeded()); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), FailedWithMessage("DW_CFA_restore_state without a matching " "previous DW_CFA_remember_state")); @@ -639,7 +639,7 @@ TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_GNU_window_save) { Succeeded()); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), FailedWithMessage("DW_CFA opcode 0x2d is not supported for " "architecture x86_64")); @@ -674,7 +674,7 @@ TEST(DWARFDebugFrame, UnwindTableError_DW_CFA_def_cfa_offset) { Succeeded()); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), FailedWithMessage("DW_CFA_def_cfa_offset found when CFA " "rule was not RegPlusOffset")); @@ -709,7 +709,7 @@ TEST(DWARFDebugFrame, UnwindTableDefCFAOffsetSFCFAError) { Succeeded()); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), FailedWithMessage("DW_CFA_def_cfa_offset_sf found when CFA " "rule was not RegPlusOffset")); @@ -745,7 +745,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_def_cfa_register) { EXPECT_THAT_ERROR(parseCFI(TestFDE, {}), Succeeded()); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 1u); @@ -817,7 +817,7 @@ TEST(DWARFDebugFrame, UnwindTableRowPushingOpcodes) { Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0)); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); ASSERT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 6u); @@ -892,7 +892,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_restore) { Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0)); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 2u); @@ -955,7 +955,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_restore_extended) { Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0)); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 2u); @@ -1016,7 +1016,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_offset) { Reg3, dwarf::UnwindLocation::createAtCFAPlusOffset(8)); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 1u); @@ -1068,7 +1068,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_val_offset) { Reg2, dwarf::UnwindLocation::createIsCFAPlusOffset(8)); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 1u); @@ -1113,7 +1113,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_nop) { Reg1, dwarf::UnwindLocation::createAtCFAPlusOffset(-8)); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 1u); @@ -1203,7 +1203,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_remember_state) { Reg3, dwarf::UnwindLocation::createAtCFAPlusOffset(-24)); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 5u); @@ -1270,7 +1270,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_undefined) { dwarf::UnwindLocation::createUndefined()); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 1u); @@ -1314,7 +1314,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_same_value) { VerifyLocs.setRegisterLocation(Reg1, dwarf::UnwindLocation::createSame()); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 1u); @@ -1360,7 +1360,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_register) { Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0)); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 1u); @@ -1412,7 +1412,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_expression) { Reg, dwarf::UnwindLocation::createAtDWARFExpression(Expr)); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 1u); @@ -1464,7 +1464,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_val_expression) { Reg, dwarf::UnwindLocation::createIsDWARFExpression(Expr)); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 1u); @@ -1527,7 +1527,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_def_cfa) { Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0)); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 5u); @@ -1625,7 +1625,7 @@ TEST(DWARFDebugFrame, UnwindTable_DW_CFA_LLVM_def_aspace_cfa) { Reg, dwarf::UnwindLocation::createIsRegisterPlusOffset(InReg, 0)); // Verify we catch state machine error. - Expected RowsOrErr = dwarf::UnwindTable::create(&TestFDE); + Expected RowsOrErr = dwarf::createUnwindTable(&TestFDE); EXPECT_THAT_ERROR(RowsOrErr.takeError(), Succeeded()); const dwarf::UnwindTable &Rows = RowsOrErr.get(); EXPECT_EQ(Rows.size(), 5u); From 5c807f2854fbf702bb57a91ca7e63c7b3754262c Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Tue, 17 Jun 2025 20:54:21 +0000 Subject: [PATCH 02/18] Annotate the extracted UnwindTable creators with LLVM ABI --- llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h index d48d3b628a9c8..512d8ba0e9035 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -392,7 +392,7 @@ class CIE; /// /// \returns An error if the DWARF Call Frame Information opcodes have state /// machine errors, or a valid UnwindTable otherwise. -Expected createUnwindTable(const CIE *Cie); +LLVM_ABI Expected createUnwindTable(const CIE *Cie); class FDE; @@ -404,7 +404,7 @@ class FDE; /// /// \returns An error if the DWARF Call Frame Information opcodes have state /// machine errors, or a valid UnwindTable otherwise. -Expected createUnwindTable(const FDE *Fde); +LLVM_ABI Expected createUnwindTable(const FDE *Fde); /// An entry in either debug_frame or eh_frame. This entry can be a CIE or an /// FDE. From aac505a7184f35bb075dd48029c16f5285f46143 Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Mon, 2 Jun 2025 22:31:07 +0000 Subject: [PATCH 03/18] Remove UnwindTable dependency on CIE, and FDE (transitively on llvm/Object) For creating new UnwindTable, two static methods was implemented inside it, to create an instance of it from a CIE or FDE. This static methods are moved out of the class as a library functions. --- .../llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h index 512d8ba0e9035..c47d6f137ca3c 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -406,6 +406,30 @@ class FDE; /// machine errors, or a valid UnwindTable otherwise. LLVM_ABI Expected createUnwindTable(const FDE *Fde); +class CIE; + +/// Create an UnwindTable from a Common Information Entry (CIE). +/// +/// \param Cie The Common Information Entry to extract the table from. The +/// CFIProgram is retrieved from the \a Cie object and used to create the +/// UnwindTable. +/// +/// \returns An error if the DWARF Call Frame Information opcodes have state +/// machine errors, or a valid UnwindTable otherwise. +Expected createUnwindTable(const CIE *Cie); + +class FDE; + +/// Create an UnwindTable from a Frame Descriptor Entry (FDE). +/// +/// \param Fde The Frame Descriptor Entry to extract the table from. The +/// CFIProgram is retrieved from the \a Fde object and used to create the +/// UnwindTable. +/// +/// \returns An error if the DWARF Call Frame Information opcodes have state +/// machine errors, or a valid UnwindTable otherwise. +Expected createUnwindTable(const FDE *Fde); + /// An entry in either debug_frame or eh_frame. This entry can be a CIE or an /// FDE. class FrameEntry { From f75586c2b11d96de1e4133b6d7eaf6c0dc07a2f4 Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Tue, 3 Jun 2025 02:37:18 +0000 Subject: [PATCH 04/18] Separate UnwindTable from DWARF Debug Frame --- .../llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 380 +------------ .../llvm/DebugInfo/DWARF/DWARFUnwindTable.h | 377 +++++++++++++ llvm/lib/DebugInfo/DWARF/CMakeLists.txt | 1 + llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp | 480 ----------------- llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp | 500 ++++++++++++++++++ 5 files changed, 879 insertions(+), 859 deletions(-) create mode 100644 llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h create mode 100644 llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h index c47d6f137ca3c..1ea3700690edd 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -9,15 +9,13 @@ #ifndef LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H #define LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H -#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/iterator.h" #include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h" #include "llvm/DebugInfo/DWARF/DWARFExpression.h" -#include "llvm/Support/Compiler.h" +#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h" #include "llvm/Support/Error.h" #include "llvm/TargetParser/Triple.h" -#include #include #include @@ -30,382 +28,6 @@ struct DIDumpOptions; namespace dwarf { -constexpr uint32_t InvalidRegisterNumber = UINT32_MAX; - -/// A class that represents a location for the Call Frame Address (CFA) or a -/// register. This is decoded from the DWARF Call Frame Information -/// instructions and put into an UnwindRow. -class UnwindLocation { -public: - enum Location { - /// Not specified. - Unspecified, - /// Register is not available and can't be recovered. - Undefined, - /// Register value is in the register, nothing needs to be done to unwind - /// it: - /// reg = reg - Same, - /// Register is in or at the CFA plus an offset: - /// reg = CFA + offset - /// reg = defef(CFA + offset) - CFAPlusOffset, - /// Register or CFA is in or at a register plus offset, optionally in - /// an address space: - /// reg = reg + offset [in addrspace] - /// reg = deref(reg + offset [in addrspace]) - RegPlusOffset, - /// Register or CFA value is in or at a value found by evaluating a DWARF - /// expression: - /// reg = eval(dwarf_expr) - /// reg = deref(eval(dwarf_expr)) - DWARFExpr, - /// Value is a constant value contained in "Offset": - /// reg = Offset - Constant, - }; - -private: - Location Kind; /// The type of the location that describes how to unwind it. - uint32_t RegNum; /// The register number for Kind == RegPlusOffset. - int32_t Offset; /// The offset for Kind == CFAPlusOffset or RegPlusOffset. - std::optional AddrSpace; /// The address space for Kind == - /// RegPlusOffset for CFA. - std::optional Expr; /// The DWARF expression for Kind == - /// DWARFExpression. - bool Dereference; /// If true, the resulting location must be dereferenced - /// after the location value is computed. - - // Constructors are private to force people to use the create static - // functions. - UnwindLocation(Location K) - : Kind(K), RegNum(InvalidRegisterNumber), Offset(0), - AddrSpace(std::nullopt), Dereference(false) {} - - UnwindLocation(Location K, uint32_t Reg, int32_t Off, - std::optional AS, bool Deref) - : Kind(K), RegNum(Reg), Offset(Off), AddrSpace(AS), Dereference(Deref) {} - - UnwindLocation(DWARFExpression E, bool Deref) - : Kind(DWARFExpr), RegNum(InvalidRegisterNumber), Offset(0), Expr(E), - Dereference(Deref) {} - -public: - /// Create a location whose rule is set to Unspecified. This means the - /// register value might be in the same register but it wasn't specified in - /// the unwind opcodes. - LLVM_ABI static UnwindLocation createUnspecified(); - /// Create a location where the value is undefined and not available. This can - /// happen when a register is volatile and can't be recovered. - LLVM_ABI static UnwindLocation createUndefined(); - /// Create a location where the value is known to be in the register itself. - LLVM_ABI static UnwindLocation createSame(); - /// Create a location that is in (Deref == false) or at (Deref == true) the - /// CFA plus an offset. Most registers that are spilled onto the stack use - /// this rule. The rule for the register will use this rule and specify a - /// unique offset from the CFA with \a Deref set to true. This value will be - /// relative to a CFA value which is typically defined using the register - /// plus offset location. \see createRegisterPlusOffset(...) for more - /// information. - LLVM_ABI static UnwindLocation createIsCFAPlusOffset(int32_t Off); - LLVM_ABI static UnwindLocation createAtCFAPlusOffset(int32_t Off); - /// Create a location where the saved value is in (Deref == false) or at - /// (Deref == true) a regiser plus an offset and, optionally, in the specified - /// address space (used mostly for the CFA). - /// - /// The CFA is usually defined using this rule by using the stack pointer or - /// frame pointer as the register, with an offset that accounts for all - /// spilled registers and all local variables in a function, and Deref == - /// false. - LLVM_ABI static UnwindLocation - createIsRegisterPlusOffset(uint32_t Reg, int32_t Off, - std::optional AddrSpace = std::nullopt); - LLVM_ABI static UnwindLocation - createAtRegisterPlusOffset(uint32_t Reg, int32_t Off, - std::optional AddrSpace = std::nullopt); - /// Create a location whose value is the result of evaluating a DWARF - /// expression. This allows complex expressions to be evaluated in order to - /// unwind a register or CFA value. - LLVM_ABI static UnwindLocation createIsDWARFExpression(DWARFExpression Expr); - LLVM_ABI static UnwindLocation createAtDWARFExpression(DWARFExpression Expr); - LLVM_ABI static UnwindLocation createIsConstant(int32_t Value); - - Location getLocation() const { return Kind; } - uint32_t getRegister() const { return RegNum; } - int32_t getOffset() const { return Offset; } - uint32_t getAddressSpace() const { - assert(Kind == RegPlusOffset && AddrSpace); - return *AddrSpace; - } - int32_t getConstant() const { return Offset; } - /// Some opcodes will modify the CFA location's register only, so we need - /// to be able to modify the CFA register when evaluating DWARF Call Frame - /// Information opcodes. - void setRegister(uint32_t NewRegNum) { RegNum = NewRegNum; } - /// Some opcodes will modify the CFA location's offset only, so we need - /// to be able to modify the CFA offset when evaluating DWARF Call Frame - /// Information opcodes. - void setOffset(int32_t NewOffset) { Offset = NewOffset; } - /// Some opcodes modify a constant value and we need to be able to update - /// the constant value (DW_CFA_GNU_window_save which is also known as - // DW_CFA_AARCH64_negate_ra_state). - void setConstant(int32_t Value) { Offset = Value; } - - std::optional getDWARFExpressionBytes() const { - return Expr; - } - /// Dump a location expression as text and use the register information if - /// some is provided. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; - - LLVM_ABI bool operator==(const UnwindLocation &RHS) const; -}; - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R); - -/// A class that can track all registers with locations in a UnwindRow object. -/// -/// Register locations use a map where the key is the register number and the -/// the value is a UnwindLocation. -/// -/// The register maps are put into a class so that all register locations can -/// be copied when parsing the unwind opcodes DW_CFA_remember_state and -/// DW_CFA_restore_state. -class RegisterLocations { - std::map Locations; - -public: - /// Return the location for the register in \a RegNum if there is a location. - /// - /// \param RegNum the register number to find a location for. - /// - /// \returns A location if one is available for \a RegNum, or std::nullopt - /// otherwise. - std::optional getRegisterLocation(uint32_t RegNum) const { - auto Pos = Locations.find(RegNum); - if (Pos == Locations.end()) - return std::nullopt; - return Pos->second; - } - - /// Set the location for the register in \a RegNum to \a Location. - /// - /// \param RegNum the register number to set the location for. - /// - /// \param Location the UnwindLocation that describes how to unwind the value. - void setRegisterLocation(uint32_t RegNum, const UnwindLocation &Location) { - Locations.erase(RegNum); - Locations.insert(std::make_pair(RegNum, Location)); - } - - /// Removes any rule for the register in \a RegNum. - /// - /// \param RegNum the register number to remove the location for. - void removeRegisterLocation(uint32_t RegNum) { Locations.erase(RegNum); } - - /// Dump all registers + locations that are currently defined in this object. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; - - /// Returns true if we have any register locations in this object. - bool hasLocations() const { return !Locations.empty(); } - - size_t size() const { return Locations.size(); } - - bool operator==(const RegisterLocations &RHS) const { - return Locations == RHS.Locations; - } -}; - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL); - -/// A class that represents a single row in the unwind table that is decoded by -/// parsing the DWARF Call Frame Information opcodes. -/// -/// The row consists of an optional address, the rule to unwind the CFA and all -/// rules to unwind any registers. If the address doesn't have a value, this -/// row represents the initial instructions for a CIE. If the address has a -/// value the UnwindRow represents a row in the UnwindTable for a FDE. The -/// address is the first address for which the CFA location and register rules -/// are valid within a function. -/// -/// UnwindRow objects are created by parsing opcodes in the DWARF Call Frame -/// Information and UnwindRow objects are lazily populated and pushed onto a -/// stack in the UnwindTable when evaluating this state machine. Accessors are -/// needed for the address, CFA value, and register locations as the opcodes -/// encode a state machine that produces a sorted array of UnwindRow objects -/// \see UnwindTable. -class UnwindRow { - /// The address will be valid when parsing the instructions in a FDE. If - /// invalid, this object represents the initial instructions of a CIE. - std::optional Address; ///< Address for row in FDE, invalid for CIE. - UnwindLocation CFAValue; ///< How to unwind the Call Frame Address (CFA). - RegisterLocations RegLocs; ///< How to unwind all registers in this list. - -public: - UnwindRow() : CFAValue(UnwindLocation::createUnspecified()) {} - - /// Returns true if the address is valid in this object. - bool hasAddress() const { return Address.has_value(); } - - /// Get the address for this row. - /// - /// Clients should only call this function after verifying it has a valid - /// address with a call to \see hasAddress(). - uint64_t getAddress() const { return *Address; } - - /// Set the address for this UnwindRow. - /// - /// The address represents the first address for which the CFAValue and - /// RegLocs are valid within a function. - void setAddress(uint64_t Addr) { Address = Addr; } - - /// Offset the address for this UnwindRow. - /// - /// The address represents the first address for which the CFAValue and - /// RegLocs are valid within a function. Clients must ensure that this object - /// already has an address (\see hasAddress()) prior to calling this - /// function. - void slideAddress(uint64_t Offset) { *Address += Offset; } - UnwindLocation &getCFAValue() { return CFAValue; } - const UnwindLocation &getCFAValue() const { return CFAValue; } - RegisterLocations &getRegisterLocations() { return RegLocs; } - const RegisterLocations &getRegisterLocations() const { return RegLocs; } - - /// Dump the UnwindRow to the stream. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - /// - /// \param IndentLevel specify the indent level as an integer. The UnwindRow - /// will be output to the stream preceded by 2 * IndentLevel number of spaces. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel = 0) const; -}; - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row); - -/// A class that contains all UnwindRow objects for an FDE or a single unwind -/// row for a CIE. To unwind an address the rows, which are sorted by start -/// address, can be searched to find the UnwindRow with the lowest starting -/// address that is greater than or equal to the address that is being looked -/// up. -class UnwindTable { -public: - using RowContainer = std::vector; - using iterator = RowContainer::iterator; - using const_iterator = RowContainer::const_iterator; - - size_t size() const { return Rows.size(); } - iterator begin() { return Rows.begin(); } - const_iterator begin() const { return Rows.begin(); } - iterator end() { return Rows.end(); } - const_iterator end() const { return Rows.end(); } - const UnwindRow &operator[](size_t Index) const { - assert(Index < size()); - return Rows[Index]; - } - void insertRow(const UnwindRow &Row) { Rows.push_back(Row); } - - /// Set the last address that this unwinding table refers to. - /// - /// This is used when this table is created based on a FDE. - void setEndAddress(uint64_t Addr) { EndAddress = Addr; } - - /// Dump the UnwindTable to the stream. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - /// - /// \param IndentLevel specify the indent level as an integer. The UnwindRow - /// will be output to the stream preceded by 2 * IndentLevel number of spaces. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel = 0) const; - - /// Parse the information in the CFIProgram and update the CurrRow object - /// that the state machine describes. - /// - /// This is an internal implementation that emulates the state machine - /// described in the DWARF Call Frame Information opcodes and will push - /// CurrRow onto the Rows container when needed. - /// - /// \param CFIP the CFI program that contains the opcodes from a CIE or FDE. - /// - /// \param CurrRow the current row to modify while parsing the state machine. - /// - /// \param InitialLocs If non-NULL, we are parsing a FDE and this contains - /// the initial register locations from the CIE. If NULL, then a CIE's - /// opcodes are being parsed and this is not needed. This is used for the - /// DW_CFA_restore and DW_CFA_restore_extended opcodes. - Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, - const RegisterLocations *InitialLocs); - -private: - RowContainer Rows; - /// The end address when data is extracted from a FDE. This value will be - /// invalid when a UnwindTable is extracted from a CIE. - std::optional EndAddress; -}; - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows); - -class CIE; - -/// Create an UnwindTable from a Common Information Entry (CIE). -/// -/// \param Cie The Common Information Entry to extract the table from. The -/// CFIProgram is retrieved from the \a Cie object and used to create the -/// UnwindTable. -/// -/// \returns An error if the DWARF Call Frame Information opcodes have state -/// machine errors, or a valid UnwindTable otherwise. -LLVM_ABI Expected createUnwindTable(const CIE *Cie); - -class FDE; - -/// Create an UnwindTable from a Frame Descriptor Entry (FDE). -/// -/// \param Fde The Frame Descriptor Entry to extract the table from. The -/// CFIProgram is retrieved from the \a Fde object and used to create the -/// UnwindTable. -/// -/// \returns An error if the DWARF Call Frame Information opcodes have state -/// machine errors, or a valid UnwindTable otherwise. -LLVM_ABI Expected createUnwindTable(const FDE *Fde); - class CIE; /// Create an UnwindTable from a Common Information Entry (CIE). diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h new file mode 100644 index 0000000000000..7a36b1630e7e1 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h @@ -0,0 +1,377 @@ +//===- DWARFUnwindTable.h ----------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H +#define LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H + +#include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h" +#include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace llvm { + +namespace dwarf { +constexpr uint32_t InvalidRegisterNumber = UINT32_MAX; + +/// A class that represents a location for the Call Frame Address (CFA) or a +/// register. This is decoded from the DWARF Call Frame Information +/// instructions and put into an UnwindRow. +class UnwindLocation { +public: + enum Location { + /// Not specified. + Unspecified, + /// Register is not available and can't be recovered. + Undefined, + /// Register value is in the register, nothing needs to be done to unwind + /// it: + /// reg = reg + Same, + /// Register is in or at the CFA plus an offset: + /// reg = CFA + offset + /// reg = defef(CFA + offset) + CFAPlusOffset, + /// Register or CFA is in or at a register plus offset, optionally in + /// an address space: + /// reg = reg + offset [in addrspace] + /// reg = deref(reg + offset [in addrspace]) + RegPlusOffset, + /// Register or CFA value is in or at a value found by evaluating a DWARF + /// expression: + /// reg = eval(dwarf_expr) + /// reg = deref(eval(dwarf_expr)) + DWARFExpr, + /// Value is a constant value contained in "Offset": + /// reg = Offset + Constant, + }; + +private: + Location Kind; /// The type of the location that describes how to unwind it. + uint32_t RegNum; /// The register number for Kind == RegPlusOffset. + int32_t Offset; /// The offset for Kind == CFAPlusOffset or RegPlusOffset. + std::optional AddrSpace; /// The address space for Kind == + /// RegPlusOffset for CFA. + std::optional Expr; /// The DWARF expression for Kind == + /// DWARFExpression. + bool Dereference; /// If true, the resulting location must be dereferenced + /// after the location value is computed. + + // Constructors are private to force people to use the create static + // functions. + UnwindLocation(Location K) + : Kind(K), RegNum(InvalidRegisterNumber), Offset(0), + AddrSpace(std::nullopt), Dereference(false) {} + + UnwindLocation(Location K, uint32_t Reg, int32_t Off, + std::optional AS, bool Deref) + : Kind(K), RegNum(Reg), Offset(Off), AddrSpace(AS), Dereference(Deref) {} + + UnwindLocation(DWARFExpression E, bool Deref) + : Kind(DWARFExpr), RegNum(InvalidRegisterNumber), Offset(0), Expr(E), + Dereference(Deref) {} + +public: + /// Create a location whose rule is set to Unspecified. This means the + /// register value might be in the same register but it wasn't specified in + /// the unwind opcodes. + static UnwindLocation createUnspecified(); + /// Create a location where the value is undefined and not available. This can + /// happen when a register is volatile and can't be recovered. + static UnwindLocation createUndefined(); + /// Create a location where the value is known to be in the register itself. + static UnwindLocation createSame(); + /// Create a location that is in (Deref == false) or at (Deref == true) the + /// CFA plus an offset. Most registers that are spilled onto the stack use + /// this rule. The rule for the register will use this rule and specify a + /// unique offset from the CFA with \a Deref set to true. This value will be + /// relative to a CFA value which is typically defined using the register + /// plus offset location. \see createRegisterPlusOffset(...) for more + /// information. + static UnwindLocation createIsCFAPlusOffset(int32_t Off); + static UnwindLocation createAtCFAPlusOffset(int32_t Off); + /// Create a location where the saved value is in (Deref == false) or at + /// (Deref == true) a regiser plus an offset and, optionally, in the specified + /// address space (used mostly for the CFA). + /// + /// The CFA is usually defined using this rule by using the stack pointer or + /// frame pointer as the register, with an offset that accounts for all + /// spilled registers and all local variables in a function, and Deref == + /// false. + static UnwindLocation + createIsRegisterPlusOffset(uint32_t Reg, int32_t Off, + std::optional AddrSpace = std::nullopt); + static UnwindLocation + createAtRegisterPlusOffset(uint32_t Reg, int32_t Off, + std::optional AddrSpace = std::nullopt); + /// Create a location whose value is the result of evaluating a DWARF + /// expression. This allows complex expressions to be evaluated in order to + /// unwind a register or CFA value. + static UnwindLocation createIsDWARFExpression(DWARFExpression Expr); + static UnwindLocation createAtDWARFExpression(DWARFExpression Expr); + static UnwindLocation createIsConstant(int32_t Value); + + Location getLocation() const { return Kind; } + uint32_t getRegister() const { return RegNum; } + int32_t getOffset() const { return Offset; } + uint32_t getAddressSpace() const { + assert(Kind == RegPlusOffset && AddrSpace); + return *AddrSpace; + } + int32_t getConstant() const { return Offset; } + /// Some opcodes will modify the CFA location's register only, so we need + /// to be able to modify the CFA register when evaluating DWARF Call Frame + /// Information opcodes. + void setRegister(uint32_t NewRegNum) { RegNum = NewRegNum; } + /// Some opcodes will modify the CFA location's offset only, so we need + /// to be able to modify the CFA offset when evaluating DWARF Call Frame + /// Information opcodes. + void setOffset(int32_t NewOffset) { Offset = NewOffset; } + /// Some opcodes modify a constant value and we need to be able to update + /// the constant value (DW_CFA_GNU_window_save which is also known as + // DW_CFA_AARCH64_negate_ra_state). + void setConstant(int32_t Value) { Offset = Value; } + + std::optional getDWARFExpressionBytes() const { + return Expr; + } + /// Dump a location expression as text and use the register information if + /// some is provided. + /// + /// \param OS the stream to use for output. + /// + /// \param MRI register information that helps emit register names insteead + /// of raw register numbers. + /// + /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame + /// instead of from .debug_frame. This is needed for register number + /// conversion because some register numbers differ between the two sections + /// for certain architectures like x86. + void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; + + bool operator==(const UnwindLocation &RHS) const; +}; + +raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R); + +/// A class that can track all registers with locations in a UnwindRow object. +/// +/// Register locations use a map where the key is the register number and the +/// the value is a UnwindLocation. +/// +/// The register maps are put into a class so that all register locations can +/// be copied when parsing the unwind opcodes DW_CFA_remember_state and +/// DW_CFA_restore_state. +class RegisterLocations { + std::map Locations; + +public: + /// Return the location for the register in \a RegNum if there is a location. + /// + /// \param RegNum the register number to find a location for. + /// + /// \returns A location if one is available for \a RegNum, or std::nullopt + /// otherwise. + std::optional getRegisterLocation(uint32_t RegNum) const { + auto Pos = Locations.find(RegNum); + if (Pos == Locations.end()) + return std::nullopt; + return Pos->second; + } + + /// Set the location for the register in \a RegNum to \a Location. + /// + /// \param RegNum the register number to set the location for. + /// + /// \param Location the UnwindLocation that describes how to unwind the value. + void setRegisterLocation(uint32_t RegNum, const UnwindLocation &Location) { + Locations.erase(RegNum); + Locations.insert(std::make_pair(RegNum, Location)); + } + + /// Removes any rule for the register in \a RegNum. + /// + /// \param RegNum the register number to remove the location for. + void removeRegisterLocation(uint32_t RegNum) { Locations.erase(RegNum); } + + /// Dump all registers + locations that are currently defined in this object. + /// + /// \param OS the stream to use for output. + /// + /// \param MRI register information that helps emit register names insteead + /// of raw register numbers. + /// + /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame + /// instead of from .debug_frame. This is needed for register number + /// conversion because some register numbers differ between the two sections + /// for certain architectures like x86. + void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; + + /// Returns true if we have any register locations in this object. + bool hasLocations() const { return !Locations.empty(); } + + size_t size() const { return Locations.size(); } + + bool operator==(const RegisterLocations &RHS) const { + return Locations == RHS.Locations; + } +}; + +raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL); + +/// A class that represents a single row in the unwind table that is decoded by +/// parsing the DWARF Call Frame Information opcodes. +/// +/// The row consists of an optional address, the rule to unwind the CFA and all +/// rules to unwind any registers. If the address doesn't have a value, this +/// row represents the initial instructions for a CIE. If the address has a +/// value the UnwindRow represents a row in the UnwindTable for a FDE. The +/// address is the first address for which the CFA location and register rules +/// are valid within a function. +/// +/// UnwindRow objects are created by parsing opcodes in the DWARF Call Frame +/// Information and UnwindRow objects are lazily populated and pushed onto a +/// stack in the UnwindTable when evaluating this state machine. Accessors are +/// needed for the address, CFA value, and register locations as the opcodes +/// encode a state machine that produces a sorted array of UnwindRow objects +/// \see UnwindTable. +class UnwindRow { + /// The address will be valid when parsing the instructions in a FDE. If + /// invalid, this object represents the initial instructions of a CIE. + std::optional Address; ///< Address for row in FDE, invalid for CIE. + UnwindLocation CFAValue; ///< How to unwind the Call Frame Address (CFA). + RegisterLocations RegLocs; ///< How to unwind all registers in this list. + +public: + UnwindRow() : CFAValue(UnwindLocation::createUnspecified()) {} + + /// Returns true if the address is valid in this object. + bool hasAddress() const { return Address.has_value(); } + + /// Get the address for this row. + /// + /// Clients should only call this function after verifying it has a valid + /// address with a call to \see hasAddress(). + uint64_t getAddress() const { return *Address; } + + /// Set the address for this UnwindRow. + /// + /// The address represents the first address for which the CFAValue and + /// RegLocs are valid within a function. + void setAddress(uint64_t Addr) { Address = Addr; } + + /// Offset the address for this UnwindRow. + /// + /// The address represents the first address for which the CFAValue and + /// RegLocs are valid within a function. Clients must ensure that this object + /// already has an address (\see hasAddress()) prior to calling this + /// function. + void slideAddress(uint64_t Offset) { *Address += Offset; } + UnwindLocation &getCFAValue() { return CFAValue; } + const UnwindLocation &getCFAValue() const { return CFAValue; } + RegisterLocations &getRegisterLocations() { return RegLocs; } + const RegisterLocations &getRegisterLocations() const { return RegLocs; } + + /// Dump the UnwindRow to the stream. + /// + /// \param OS the stream to use for output. + /// + /// \param MRI register information that helps emit register names insteead + /// of raw register numbers. + /// + /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame + /// instead of from .debug_frame. This is needed for register number + /// conversion because some register numbers differ between the two sections + /// for certain architectures like x86. + /// + /// \param IndentLevel specify the indent level as an integer. The UnwindRow + /// will be output to the stream preceded by 2 * IndentLevel number of spaces. + void dump(raw_ostream &OS, DIDumpOptions DumpOpts, + unsigned IndentLevel = 0) const; +}; + +raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row); + +/// A class that contains all UnwindRow objects for an FDE or a single unwind +/// row for a CIE. To unwind an address the rows, which are sorted by start +/// address, can be searched to find the UnwindRow with the lowest starting +/// address that is greater than or equal to the address that is being looked +/// up. +class UnwindTable { +public: + using RowContainer = std::vector; + using iterator = RowContainer::iterator; + using const_iterator = RowContainer::const_iterator; + + size_t size() const { return Rows.size(); } + iterator begin() { return Rows.begin(); } + const_iterator begin() const { return Rows.begin(); } + iterator end() { return Rows.end(); } + const_iterator end() const { return Rows.end(); } + const UnwindRow &operator[](size_t Index) const { + assert(Index < size()); + return Rows[Index]; + } + void insertRow(const UnwindRow &Row) { Rows.push_back(Row); } + + /// Set the last address that this unwinding table refers to. + /// + /// This is used when this table is created based on a FDE. + void setEndAddress(uint64_t Addr) { EndAddress = Addr; } + + /// Dump the UnwindTable to the stream. + /// + /// \param OS the stream to use for output. + /// + /// \param MRI register information that helps emit register names insteead + /// of raw register numbers. + /// + /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame + /// instead of from .debug_frame. This is needed for register number + /// conversion because some register numbers differ between the two sections + /// for certain architectures like x86. + /// + /// \param IndentLevel specify the indent level as an integer. The UnwindRow + /// will be output to the stream preceded by 2 * IndentLevel number of spaces. + void dump(raw_ostream &OS, DIDumpOptions DumpOpts, + unsigned IndentLevel = 0) const; + + /// Parse the information in the CFIProgram and update the CurrRow object + /// that the state machine describes. + /// + /// This is an internal implementation that emulates the state machine + /// described in the DWARF Call Frame Information opcodes and will push + /// CurrRow onto the Rows container when needed. + /// + /// \param CFIP the CFI program that contains the opcodes from a CIE or FDE. + /// + /// \param CurrRow the current row to modify while parsing the state machine. + /// + /// \param InitialLocs If non-NULL, we are parsing a FDE and this contains + /// the initial register locations from the CIE. If NULL, then a CIE's + /// opcodes are being parsed and this is not needed. This is used for the + /// DW_CFA_restore and DW_CFA_restore_extended opcodes. + Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, + const RegisterLocations *InitialLocs); + +private: + RowContainer Rows; + /// The end address when data is extracted from a FDE. This value will be + /// invalid when a UnwindTable is extracted from a CIE. + std::optional EndAddress; +}; + +raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows); + +} // end namespace dwarf + +} // end namespace llvm + +#endif \ No newline at end of file diff --git a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt index cc9734f9f22be..b9ff0d676c4c3 100644 --- a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt @@ -26,6 +26,7 @@ add_llvm_component_library(LLVMDebugInfoDWARF DWARFTypeUnit.cpp DWARFUnitIndex.cpp DWARFUnit.cpp + DWARFUnwindTable.cpp DWARFVerifier.cpp ADDITIONAL_HEADER_DIRS diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index cf42151bb84aa..e6c37203dd557 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -29,180 +29,6 @@ using namespace llvm; using namespace dwarf; -static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned RegNum) { - if (DumpOpts.GetNameForDWARFReg) { - auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH); - if (!RegName.empty()) { - OS << RegName; - return; - } - } - OS << "reg" << RegNum; -} - -UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; } - -UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; } - -UnwindLocation UnwindLocation::createSame() { return {Same}; } - -UnwindLocation UnwindLocation::createIsConstant(int32_t Value) { - return {Constant, InvalidRegisterNumber, Value, std::nullopt, false}; -} - -UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) { - return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false}; -} - -UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) { - return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true}; -} - -UnwindLocation -UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset, - std::optional AddrSpace) { - return {RegPlusOffset, RegNum, Offset, AddrSpace, false}; -} - -UnwindLocation -UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset, - std::optional AddrSpace) { - return {RegPlusOffset, RegNum, Offset, AddrSpace, true}; -} - -UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) { - return {Expr, false}; -} - -UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) { - return {Expr, true}; -} - -void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { - if (Dereference) - OS << '['; - switch (Kind) { - case Unspecified: - OS << "unspecified"; - break; - case Undefined: - OS << "undefined"; - break; - case Same: - OS << "same"; - break; - case CFAPlusOffset: - OS << "CFA"; - if (Offset == 0) - break; - if (Offset > 0) - OS << "+"; - OS << Offset; - break; - case RegPlusOffset: - printRegister(OS, DumpOpts, RegNum); - if (Offset == 0 && !AddrSpace) - break; - if (Offset >= 0) - OS << "+"; - OS << Offset; - if (AddrSpace) - OS << " in addrspace" << *AddrSpace; - break; - case DWARFExpr: { - if (Expr) - DWARFExpressionPrinter::print(&(*Expr), OS, DumpOpts, nullptr); - break; - } - case Constant: - OS << Offset; - break; - } - if (Dereference) - OS << ']'; -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, - const UnwindLocation &UL) { - auto DumpOpts = DIDumpOptions(); - UL.dump(OS, DumpOpts); - return OS; -} - -bool UnwindLocation::operator==(const UnwindLocation &RHS) const { - if (Kind != RHS.Kind) - return false; - switch (Kind) { - case Unspecified: - case Undefined: - case Same: - return true; - case CFAPlusOffset: - return Offset == RHS.Offset && Dereference == RHS.Dereference; - case RegPlusOffset: - return RegNum == RHS.RegNum && Offset == RHS.Offset && - Dereference == RHS.Dereference; - case DWARFExpr: - return *Expr == *RHS.Expr && Dereference == RHS.Dereference; - case Constant: - return Offset == RHS.Offset; - } - return false; -} - -void RegisterLocations::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { - bool First = true; - for (const auto &RegLocPair : Locations) { - if (First) - First = false; - else - OS << ", "; - printRegister(OS, DumpOpts, RegLocPair.first); - OS << '='; - RegLocPair.second.dump(OS, DumpOpts); - } -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, - const RegisterLocations &RL) { - auto DumpOpts = DIDumpOptions(); - RL.dump(OS, DumpOpts); - return OS; -} - -void UnwindRow::dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel) const { - OS.indent(2 * IndentLevel); - if (hasAddress()) - OS << format("0x%" PRIx64 ": ", *Address); - OS << "CFA="; - CFAValue.dump(OS, DumpOpts); - if (RegLocs.hasLocations()) { - OS << ": "; - RegLocs.dump(OS, DumpOpts); - } - OS << "\n"; -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) { - auto DumpOpts = DIDumpOptions(); - Row.dump(OS, DumpOpts, 0); - return OS; -} - -void UnwindTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel) const { - for (const UnwindRow &Row : Rows) - Row.dump(OS, DumpOpts, IndentLevel); -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) { - auto DumpOpts = DIDumpOptions(); - Rows.dump(OS, DumpOpts, 0); - return OS; -} - Expected llvm::dwarf::createUnwindTable(const FDE *Fde) { const CIE *Cie = Fde->getLinkedCIE(); if (Cie == nullptr) @@ -250,312 +76,6 @@ Expected llvm::dwarf::createUnwindTable(const CIE *Cie) { return UT; } -Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row, - const RegisterLocations *InitialLocs) { - // State consists of CFA value and register locations. - std::vector> States; - for (const CFIProgram::Instruction &Inst : CFIP) { - switch (Inst.Opcode) { - case dwarf::DW_CFA_set_loc: { - // The DW_CFA_set_loc instruction takes a single operand that - // represents a target address. The required action is to create a new - // table row using the specified address as the location. All other - // values in the new row are initially identical to the current row. - // The new location value is always greater than the current one. If - // the segment_size field of this FDE's CIE is non- zero, the initial - // location is preceded by a segment selector of the given length - llvm::Expected NewAddress = Inst.getOperandAsUnsigned(CFIP, 0); - if (!NewAddress) - return NewAddress.takeError(); - if (*NewAddress <= Row.getAddress()) - return createStringError( - errc::invalid_argument, - "%s with adrress 0x%" PRIx64 " which must be greater than the " - "current row address 0x%" PRIx64, - CFIP.callFrameString(Inst.Opcode).str().c_str(), *NewAddress, - Row.getAddress()); - Rows.push_back(Row); - Row.setAddress(*NewAddress); - break; - } - - case dwarf::DW_CFA_advance_loc: - case dwarf::DW_CFA_advance_loc1: - case dwarf::DW_CFA_advance_loc2: - case dwarf::DW_CFA_advance_loc4: { - // The DW_CFA_advance instruction takes a single operand that - // represents a constant delta. The required action is to create a new - // table row with a location value that is computed by taking the - // current entry’s location value and adding the value of delta * - // code_alignment_factor. All other values in the new row are initially - // identical to the current row. - Rows.push_back(Row); - llvm::Expected Offset = Inst.getOperandAsUnsigned(CFIP, 0); - if (!Offset) - return Offset.takeError(); - Row.slideAddress(*Offset); - break; - } - - case dwarf::DW_CFA_restore: - case dwarf::DW_CFA_restore_extended: { - // The DW_CFA_restore instruction takes a single operand (encoded with - // the opcode) that represents a register number. The required action - // is to change the rule for the indicated register to the rule - // assigned it by the initial_instructions in the CIE. - if (InitialLocs == nullptr) - return createStringError( - errc::invalid_argument, "%s encountered while parsing a CIE", - CFIP.callFrameString(Inst.Opcode).str().c_str()); - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - if (std::optional O = - InitialLocs->getRegisterLocation(*RegNum)) - Row.getRegisterLocations().setRegisterLocation(*RegNum, *O); - else - Row.getRegisterLocations().removeRegisterLocation(*RegNum); - break; - } - - case dwarf::DW_CFA_offset: - case dwarf::DW_CFA_offset_extended: - case dwarf::DW_CFA_offset_extended_sf: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); - if (!Offset) - return Offset.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createAtCFAPlusOffset(*Offset)); - break; - } - - case dwarf::DW_CFA_nop: - break; - - case dwarf::DW_CFA_remember_state: - States.push_back( - std::make_pair(Row.getCFAValue(), Row.getRegisterLocations())); - break; - - case dwarf::DW_CFA_restore_state: - if (States.empty()) - return createStringError(errc::invalid_argument, - "DW_CFA_restore_state without a matching " - "previous DW_CFA_remember_state"); - Row.getCFAValue() = States.back().first; - Row.getRegisterLocations() = States.back().second; - States.pop_back(); - break; - - case dwarf::DW_CFA_GNU_window_save: - switch (CFIP.triple()) { - case Triple::aarch64: - case Triple::aarch64_be: - case Triple::aarch64_32: { - // DW_CFA_GNU_window_save is used for different things on different - // architectures. For aarch64 it is known as - // DW_CFA_AARCH64_negate_ra_state. The action is to toggle the - // value of the return address state between 1 and 0. If there is - // no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it - // should be initially set to 1. - constexpr uint32_t AArch64DWARFPAuthRaState = 34; - auto LRLoc = Row.getRegisterLocations().getRegisterLocation( - AArch64DWARFPAuthRaState); - if (LRLoc) { - if (LRLoc->getLocation() == UnwindLocation::Constant) { - // Toggle the constant value from 0 to 1 or 1 to 0. - LRLoc->setConstant(LRLoc->getConstant() ^ 1); - Row.getRegisterLocations().setRegisterLocation( - AArch64DWARFPAuthRaState, *LRLoc); - } else { - return createStringError( - errc::invalid_argument, - "%s encountered when existing rule for this register is not " - "a constant", - CFIP.callFrameString(Inst.Opcode).str().c_str()); - } - } else { - Row.getRegisterLocations().setRegisterLocation( - AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(1)); - } - break; - } - - case Triple::sparc: - case Triple::sparcv9: - case Triple::sparcel: - for (uint32_t RegNum = 16; RegNum < 32; ++RegNum) { - Row.getRegisterLocations().setRegisterLocation( - RegNum, UnwindLocation::createAtCFAPlusOffset((RegNum - 16) * 8)); - } - break; - - default: { - return createStringError( - errc::not_supported, - "DW_CFA opcode %#x is not supported for architecture %s", - Inst.Opcode, Triple::getArchTypeName(CFIP.triple()).str().c_str()); - - break; - } - } - break; - - case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: { - constexpr uint32_t AArch64DWARFPAuthRaState = 34; - auto LRLoc = Row.getRegisterLocations().getRegisterLocation( - AArch64DWARFPAuthRaState); - if (LRLoc) { - if (LRLoc->getLocation() == UnwindLocation::Constant) { - // Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0. - LRLoc->setConstant(LRLoc->getConstant() ^ 0x3); - } else { - return createStringError( - errc::invalid_argument, - "%s encountered when existing rule for this register is not " - "a constant", - CFIP.callFrameString(Inst.Opcode).str().c_str()); - } - } else { - Row.getRegisterLocations().setRegisterLocation( - AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(0x3)); - } - break; - } - - case dwarf::DW_CFA_undefined: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createUndefined()); - break; - } - - case dwarf::DW_CFA_same_value: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createSame()); - break; - } - - case dwarf::DW_CFA_GNU_args_size: - break; - - case dwarf::DW_CFA_register: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected NewRegNum = Inst.getOperandAsUnsigned(CFIP, 1); - if (!NewRegNum) - return NewRegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createIsRegisterPlusOffset(*NewRegNum, 0)); - break; - } - - case dwarf::DW_CFA_val_offset: - case dwarf::DW_CFA_val_offset_sf: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); - if (!Offset) - return Offset.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createIsCFAPlusOffset(*Offset)); - break; - } - - case dwarf::DW_CFA_expression: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createAtDWARFExpression(*Inst.Expression)); - break; - } - - case dwarf::DW_CFA_val_expression: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createIsDWARFExpression(*Inst.Expression)); - break; - } - - case dwarf::DW_CFA_def_cfa_register: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) - Row.getCFAValue() = - UnwindLocation::createIsRegisterPlusOffset(*RegNum, 0); - else - Row.getCFAValue().setRegister(*RegNum); - break; - } - - case dwarf::DW_CFA_def_cfa_offset: - case dwarf::DW_CFA_def_cfa_offset_sf: { - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 0); - if (!Offset) - return Offset.takeError(); - if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) { - return createStringError( - errc::invalid_argument, - "%s found when CFA rule was not RegPlusOffset", - CFIP.callFrameString(Inst.Opcode).str().c_str()); - } - Row.getCFAValue().setOffset(*Offset); - break; - } - - case dwarf::DW_CFA_def_cfa: - case dwarf::DW_CFA_def_cfa_sf: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); - if (!Offset) - return Offset.takeError(); - Row.getCFAValue() = - UnwindLocation::createIsRegisterPlusOffset(*RegNum, *Offset); - break; - } - - case dwarf::DW_CFA_LLVM_def_aspace_cfa: - case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); - if (!Offset) - return Offset.takeError(); - llvm::Expected CFAAddrSpace = - Inst.getOperandAsUnsigned(CFIP, 2); - if (!CFAAddrSpace) - return CFAAddrSpace.takeError(); - Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset( - *RegNum, *Offset, *CFAAddrSpace); - break; - } - - case dwarf::DW_CFA_def_cfa_expression: - Row.getCFAValue() = - UnwindLocation::createIsDWARFExpression(*Inst.Expression); - break; - } - } - return Error::success(); -} - // Returns the CIE identifier to be used by the requested format. // CIE ids for .debug_frame sections are defined in Section 7.24 of DWARFv5. // For CIE ID in .eh_frame sections see diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp new file mode 100644 index 0000000000000..330eed900221e --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp @@ -0,0 +1,500 @@ +//=== DWARFUnwindTable.cpp - Parsing CFI instructions into unwinding table ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include + +using namespace llvm; +using namespace dwarf; + +static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts, + unsigned RegNum) { + if (DumpOpts.GetNameForDWARFReg) { + auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH); + if (!RegName.empty()) { + OS << RegName; + return; + } + } + OS << "reg" << RegNum; +} + +UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; } + +UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; } + +UnwindLocation UnwindLocation::createSame() { return {Same}; } + +UnwindLocation UnwindLocation::createIsConstant(int32_t Value) { + return {Constant, InvalidRegisterNumber, Value, std::nullopt, false}; +} + +UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) { + return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false}; +} + +UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) { + return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true}; +} + +UnwindLocation +UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset, + std::optional AddrSpace) { + return {RegPlusOffset, RegNum, Offset, AddrSpace, false}; +} + +UnwindLocation +UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset, + std::optional AddrSpace) { + return {RegPlusOffset, RegNum, Offset, AddrSpace, true}; +} + +UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) { + return {Expr, false}; +} + +UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) { + return {Expr, true}; +} + +void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { + if (Dereference) + OS << '['; + switch (Kind) { + case Unspecified: + OS << "unspecified"; + break; + case Undefined: + OS << "undefined"; + break; + case Same: + OS << "same"; + break; + case CFAPlusOffset: + OS << "CFA"; + if (Offset == 0) + break; + if (Offset > 0) + OS << "+"; + OS << Offset; + break; + case RegPlusOffset: + printRegister(OS, DumpOpts, RegNum); + if (Offset == 0 && !AddrSpace) + break; + if (Offset >= 0) + OS << "+"; + OS << Offset; + if (AddrSpace) + OS << " in addrspace" << *AddrSpace; + break; + case DWARFExpr: { + Expr->print(OS, DumpOpts, nullptr); + break; + } + case Constant: + OS << Offset; + break; + } + if (Dereference) + OS << ']'; +} + +raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, + const UnwindLocation &UL) { + auto DumpOpts = DIDumpOptions(); + UL.dump(OS, DumpOpts); + return OS; +} + +bool UnwindLocation::operator==(const UnwindLocation &RHS) const { + if (Kind != RHS.Kind) + return false; + switch (Kind) { + case Unspecified: + case Undefined: + case Same: + return true; + case CFAPlusOffset: + return Offset == RHS.Offset && Dereference == RHS.Dereference; + case RegPlusOffset: + return RegNum == RHS.RegNum && Offset == RHS.Offset && + Dereference == RHS.Dereference; + case DWARFExpr: + return *Expr == *RHS.Expr && Dereference == RHS.Dereference; + case Constant: + return Offset == RHS.Offset; + } + return false; +} + +void RegisterLocations::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { + bool First = true; + for (const auto &RegLocPair : Locations) { + if (First) + First = false; + else + OS << ", "; + printRegister(OS, DumpOpts, RegLocPair.first); + OS << '='; + RegLocPair.second.dump(OS, DumpOpts); + } +} + +raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, + const RegisterLocations &RL) { + auto DumpOpts = DIDumpOptions(); + RL.dump(OS, DumpOpts); + return OS; +} + +void UnwindRow::dump(raw_ostream &OS, DIDumpOptions DumpOpts, + unsigned IndentLevel) const { + OS.indent(2 * IndentLevel); + if (hasAddress()) + OS << format("0x%" PRIx64 ": ", *Address); + OS << "CFA="; + CFAValue.dump(OS, DumpOpts); + if (RegLocs.hasLocations()) { + OS << ": "; + RegLocs.dump(OS, DumpOpts); + } + OS << "\n"; +} + +raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) { + auto DumpOpts = DIDumpOptions(); + Row.dump(OS, DumpOpts, 0); + return OS; +} + +void UnwindTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts, + unsigned IndentLevel) const { + for (const UnwindRow &Row : Rows) + Row.dump(OS, DumpOpts, IndentLevel); +} + +raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) { + auto DumpOpts = DIDumpOptions(); + Rows.dump(OS, DumpOpts, 0); + return OS; +} + +Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row, + const RegisterLocations *InitialLocs) { + // State consists of CFA value and register locations. + std::vector> States; + for (const CFIProgram::Instruction &Inst : CFIP) { + switch (Inst.Opcode) { + case dwarf::DW_CFA_set_loc: { + // The DW_CFA_set_loc instruction takes a single operand that + // represents a target address. The required action is to create a new + // table row using the specified address as the location. All other + // values in the new row are initially identical to the current row. + // The new location value is always greater than the current one. If + // the segment_size field of this FDE's CIE is non- zero, the initial + // location is preceded by a segment selector of the given length + llvm::Expected NewAddress = Inst.getOperandAsUnsigned(CFIP, 0); + if (!NewAddress) + return NewAddress.takeError(); + if (*NewAddress <= Row.getAddress()) + return createStringError( + errc::invalid_argument, + "%s with adrress 0x%" PRIx64 " which must be greater than the " + "current row address 0x%" PRIx64, + CFIP.callFrameString(Inst.Opcode).str().c_str(), *NewAddress, + Row.getAddress()); + Rows.push_back(Row); + Row.setAddress(*NewAddress); + break; + } + + case dwarf::DW_CFA_advance_loc: + case dwarf::DW_CFA_advance_loc1: + case dwarf::DW_CFA_advance_loc2: + case dwarf::DW_CFA_advance_loc4: { + // The DW_CFA_advance instruction takes a single operand that + // represents a constant delta. The required action is to create a new + // table row with a location value that is computed by taking the + // current entry’s location value and adding the value of delta * + // code_alignment_factor. All other values in the new row are initially + // identical to the current row. + Rows.push_back(Row); + llvm::Expected Offset = Inst.getOperandAsUnsigned(CFIP, 0); + if (!Offset) + return Offset.takeError(); + Row.slideAddress(*Offset); + break; + } + + case dwarf::DW_CFA_restore: + case dwarf::DW_CFA_restore_extended: { + // The DW_CFA_restore instruction takes a single operand (encoded with + // the opcode) that represents a register number. The required action + // is to change the rule for the indicated register to the rule + // assigned it by the initial_instructions in the CIE. + if (InitialLocs == nullptr) + return createStringError( + errc::invalid_argument, "%s encountered while parsing a CIE", + CFIP.callFrameString(Inst.Opcode).str().c_str()); + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + if (std::optional O = + InitialLocs->getRegisterLocation(*RegNum)) + Row.getRegisterLocations().setRegisterLocation(*RegNum, *O); + else + Row.getRegisterLocations().removeRegisterLocation(*RegNum); + break; + } + + case dwarf::DW_CFA_offset: + case dwarf::DW_CFA_offset_extended: + case dwarf::DW_CFA_offset_extended_sf: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); + if (!Offset) + return Offset.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createAtCFAPlusOffset(*Offset)); + break; + } + + case dwarf::DW_CFA_nop: + break; + + case dwarf::DW_CFA_remember_state: + States.push_back( + std::make_pair(Row.getCFAValue(), Row.getRegisterLocations())); + break; + + case dwarf::DW_CFA_restore_state: + if (States.empty()) + return createStringError(errc::invalid_argument, + "DW_CFA_restore_state without a matching " + "previous DW_CFA_remember_state"); + Row.getCFAValue() = States.back().first; + Row.getRegisterLocations() = States.back().second; + States.pop_back(); + break; + + case dwarf::DW_CFA_GNU_window_save: + switch (CFIP.triple()) { + case Triple::aarch64: + case Triple::aarch64_be: + case Triple::aarch64_32: { + // DW_CFA_GNU_window_save is used for different things on different + // architectures. For aarch64 it is known as + // DW_CFA_AARCH64_negate_ra_state. The action is to toggle the + // value of the return address state between 1 and 0. If there is + // no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it + // should be initially set to 1. + constexpr uint32_t AArch64DWARFPAuthRaState = 34; + auto LRLoc = Row.getRegisterLocations().getRegisterLocation( + AArch64DWARFPAuthRaState); + if (LRLoc) { + if (LRLoc->getLocation() == UnwindLocation::Constant) { + // Toggle the constant value from 0 to 1 or 1 to 0. + LRLoc->setConstant(LRLoc->getConstant() ^ 1); + Row.getRegisterLocations().setRegisterLocation( + AArch64DWARFPAuthRaState, *LRLoc); + } else { + return createStringError( + errc::invalid_argument, + "%s encountered when existing rule for this register is not " + "a constant", + CFIP.callFrameString(Inst.Opcode).str().c_str()); + } + } else { + Row.getRegisterLocations().setRegisterLocation( + AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(1)); + } + break; + } + + case Triple::sparc: + case Triple::sparcv9: + case Triple::sparcel: + for (uint32_t RegNum = 16; RegNum < 32; ++RegNum) { + Row.getRegisterLocations().setRegisterLocation( + RegNum, UnwindLocation::createAtCFAPlusOffset((RegNum - 16) * 8)); + } + break; + + default: { + return createStringError( + errc::not_supported, + "DW_CFA opcode %#x is not supported for architecture %s", + Inst.Opcode, Triple::getArchTypeName(CFIP.triple()).str().c_str()); + + break; + } + } + break; + + case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: { + constexpr uint32_t AArch64DWARFPAuthRaState = 34; + auto LRLoc = Row.getRegisterLocations().getRegisterLocation( + AArch64DWARFPAuthRaState); + if (LRLoc) { + if (LRLoc->getLocation() == UnwindLocation::Constant) { + // Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0. + LRLoc->setConstant(LRLoc->getConstant() ^ 0x3); + } else { + return createStringError( + errc::invalid_argument, + "%s encountered when existing rule for this register is not " + "a constant", + CFIP.callFrameString(Inst.Opcode).str().c_str()); + } + } else { + Row.getRegisterLocations().setRegisterLocation( + AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(0x3)); + } + break; + } + + case dwarf::DW_CFA_undefined: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createUndefined()); + break; + } + + case dwarf::DW_CFA_same_value: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createSame()); + break; + } + + case dwarf::DW_CFA_GNU_args_size: + break; + + case dwarf::DW_CFA_register: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + llvm::Expected NewRegNum = Inst.getOperandAsUnsigned(CFIP, 1); + if (!NewRegNum) + return NewRegNum.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createIsRegisterPlusOffset(*NewRegNum, 0)); + break; + } + + case dwarf::DW_CFA_val_offset: + case dwarf::DW_CFA_val_offset_sf: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); + if (!Offset) + return Offset.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createIsCFAPlusOffset(*Offset)); + break; + } + + case dwarf::DW_CFA_expression: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createAtDWARFExpression(*Inst.Expression)); + break; + } + + case dwarf::DW_CFA_val_expression: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + Row.getRegisterLocations().setRegisterLocation( + *RegNum, UnwindLocation::createIsDWARFExpression(*Inst.Expression)); + break; + } + + case dwarf::DW_CFA_def_cfa_register: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) + Row.getCFAValue() = + UnwindLocation::createIsRegisterPlusOffset(*RegNum, 0); + else + Row.getCFAValue().setRegister(*RegNum); + break; + } + + case dwarf::DW_CFA_def_cfa_offset: + case dwarf::DW_CFA_def_cfa_offset_sf: { + llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 0); + if (!Offset) + return Offset.takeError(); + if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) { + return createStringError( + errc::invalid_argument, + "%s found when CFA rule was not RegPlusOffset", + CFIP.callFrameString(Inst.Opcode).str().c_str()); + } + Row.getCFAValue().setOffset(*Offset); + break; + } + + case dwarf::DW_CFA_def_cfa: + case dwarf::DW_CFA_def_cfa_sf: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); + if (!Offset) + return Offset.takeError(); + Row.getCFAValue() = + UnwindLocation::createIsRegisterPlusOffset(*RegNum, *Offset); + break; + } + + case dwarf::DW_CFA_LLVM_def_aspace_cfa: + case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: { + llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); + if (!RegNum) + return RegNum.takeError(); + llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); + if (!Offset) + return Offset.takeError(); + llvm::Expected CFAAddrSpace = + Inst.getOperandAsUnsigned(CFIP, 2); + if (!CFAAddrSpace) + return CFAAddrSpace.takeError(); + Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset( + *RegNum, *Offset, *CFAAddrSpace); + break; + } + + case dwarf::DW_CFA_def_cfa_expression: + Row.getCFAValue() = + UnwindLocation::createIsDWARFExpression(*Inst.Expression); + break; + } + } + return Error::success(); +} From 80ac83c369506f733f0461ead993849551ca5fbd Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Tue, 17 Jun 2025 21:26:16 +0000 Subject: [PATCH 05/18] Annotate UnwindTable exported API with LLVM_ABI --- .../llvm/DebugInfo/DWARF/DWARFUnwindTable.h | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h index 7a36b1630e7e1..e91c5175a3e88 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h @@ -11,6 +11,7 @@ #include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h" #include "llvm/DebugInfo/DWARF/DWARFExpression.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/Error.h" #include #include @@ -82,12 +83,12 @@ class UnwindLocation { /// Create a location whose rule is set to Unspecified. This means the /// register value might be in the same register but it wasn't specified in /// the unwind opcodes. - static UnwindLocation createUnspecified(); + LLVM_ABI static UnwindLocation createUnspecified(); /// Create a location where the value is undefined and not available. This can /// happen when a register is volatile and can't be recovered. - static UnwindLocation createUndefined(); + LLVM_ABI static UnwindLocation createUndefined(); /// Create a location where the value is known to be in the register itself. - static UnwindLocation createSame(); + LLVM_ABI static UnwindLocation createSame(); /// Create a location that is in (Deref == false) or at (Deref == true) the /// CFA plus an offset. Most registers that are spilled onto the stack use /// this rule. The rule for the register will use this rule and specify a @@ -95,8 +96,8 @@ class UnwindLocation { /// relative to a CFA value which is typically defined using the register /// plus offset location. \see createRegisterPlusOffset(...) for more /// information. - static UnwindLocation createIsCFAPlusOffset(int32_t Off); - static UnwindLocation createAtCFAPlusOffset(int32_t Off); + LLVM_ABI static UnwindLocation createIsCFAPlusOffset(int32_t Off); + LLVM_ABI static UnwindLocation createAtCFAPlusOffset(int32_t Off); /// Create a location where the saved value is in (Deref == false) or at /// (Deref == true) a regiser plus an offset and, optionally, in the specified /// address space (used mostly for the CFA). @@ -105,18 +106,18 @@ class UnwindLocation { /// frame pointer as the register, with an offset that accounts for all /// spilled registers and all local variables in a function, and Deref == /// false. - static UnwindLocation + LLVM_ABI static UnwindLocation createIsRegisterPlusOffset(uint32_t Reg, int32_t Off, std::optional AddrSpace = std::nullopt); - static UnwindLocation + LLVM_ABI static UnwindLocation createAtRegisterPlusOffset(uint32_t Reg, int32_t Off, std::optional AddrSpace = std::nullopt); /// Create a location whose value is the result of evaluating a DWARF /// expression. This allows complex expressions to be evaluated in order to /// unwind a register or CFA value. - static UnwindLocation createIsDWARFExpression(DWARFExpression Expr); - static UnwindLocation createAtDWARFExpression(DWARFExpression Expr); - static UnwindLocation createIsConstant(int32_t Value); + LLVM_ABI static UnwindLocation createIsDWARFExpression(DWARFExpression Expr); + LLVM_ABI static UnwindLocation createAtDWARFExpression(DWARFExpression Expr); + LLVM_ABI static UnwindLocation createIsConstant(int32_t Value); Location getLocation() const { return Kind; } uint32_t getRegister() const { return RegNum; } @@ -154,12 +155,12 @@ class UnwindLocation { /// instead of from .debug_frame. This is needed for register number /// conversion because some register numbers differ between the two sections /// for certain architectures like x86. - void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; + LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; - bool operator==(const UnwindLocation &RHS) const; + LLVM_ABI bool operator==(const UnwindLocation &RHS) const; }; -raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R); +LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R); /// A class that can track all registers with locations in a UnwindRow object. /// @@ -212,7 +213,7 @@ class RegisterLocations { /// instead of from .debug_frame. This is needed for register number /// conversion because some register numbers differ between the two sections /// for certain architectures like x86. - void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; + LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; /// Returns true if we have any register locations in this object. bool hasLocations() const { return !Locations.empty(); } @@ -224,7 +225,7 @@ class RegisterLocations { } }; -raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL); +LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL); /// A class that represents a single row in the unwind table that is decoded by /// parsing the DWARF Call Frame Information opcodes. @@ -293,11 +294,11 @@ class UnwindRow { /// /// \param IndentLevel specify the indent level as an integer. The UnwindRow /// will be output to the stream preceded by 2 * IndentLevel number of spaces. - void dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel = 0) const; + LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, + unsigned IndentLevel = 0) const; }; -raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row); +LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row); /// A class that contains all UnwindRow objects for an FDE or a single unwind /// row for a CIE. To unwind an address the rows, which are sorted by start @@ -340,8 +341,8 @@ class UnwindTable { /// /// \param IndentLevel specify the indent level as an integer. The UnwindRow /// will be output to the stream preceded by 2 * IndentLevel number of spaces. - void dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel = 0) const; + LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, + unsigned IndentLevel = 0) const; /// Parse the information in the CFIProgram and update the CurrRow object /// that the state machine describes. @@ -358,8 +359,8 @@ class UnwindTable { /// the initial register locations from the CIE. If NULL, then a CIE's /// opcodes are being parsed and this is not needed. This is used for the /// DW_CFA_restore and DW_CFA_restore_extended opcodes. - Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, - const RegisterLocations *InitialLocs); + LLVM_ABI Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, + const RegisterLocations *InitialLocs); private: RowContainer Rows; @@ -368,7 +369,7 @@ class UnwindTable { std::optional EndAddress; }; -raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows); +LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows); } // end namespace dwarf From 8a4f86149fbf6a98acb2d8e1a3e26b7a751cf6c5 Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Tue, 17 Jun 2025 21:33:01 +0000 Subject: [PATCH 06/18] Annotate exported API of DebugFrame with LLVM_ABI --- llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h index 1ea3700690edd..4d2e6fd6bb4e0 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -14,6 +14,7 @@ #include "llvm/DebugInfo/DWARF/DWARFCFIProgram.h" #include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/Error.h" #include "llvm/TargetParser/Triple.h" #include @@ -38,7 +39,7 @@ class CIE; /// /// \returns An error if the DWARF Call Frame Information opcodes have state /// machine errors, or a valid UnwindTable otherwise. -Expected createUnwindTable(const CIE *Cie); +LLVM_ABI Expected createUnwindTable(const CIE *Cie); class FDE; @@ -50,7 +51,7 @@ class FDE; /// /// \returns An error if the DWARF Call Frame Information opcodes have state /// machine errors, or a valid UnwindTable otherwise. -Expected createUnwindTable(const FDE *Fde); +LLVM_ABI Expected createUnwindTable(const FDE *Fde); /// An entry in either debug_frame or eh_frame. This entry can be a CIE or an /// FDE. From fbadafcb02f1a25fa96a4e5e0b3644ddc241e336 Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Wed, 25 Jun 2025 18:39:01 +0000 Subject: [PATCH 07/18] Redo the separation with the updates --- .../llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 348 ------------- .../llvm/DebugInfo/DWARF/DWARFUnwindTable.h | 52 +- llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp | 484 ------------------ llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp | 13 +- 4 files changed, 33 insertions(+), 864 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h index cd467d6c8c771..4d2e6fd6bb4e0 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -29,354 +29,6 @@ struct DIDumpOptions; namespace dwarf { -constexpr uint32_t InvalidRegisterNumber = UINT32_MAX; - -/// A class that represents a location for the Call Frame Address (CFA) or a -/// register. This is decoded from the DWARF Call Frame Information -/// instructions and put into an UnwindRow. -class UnwindLocation { -public: - enum Location { - /// Not specified. - Unspecified, - /// Register is not available and can't be recovered. - Undefined, - /// Register value is in the register, nothing needs to be done to unwind - /// it: - /// reg = reg - Same, - /// Register is in or at the CFA plus an offset: - /// reg = CFA + offset - /// reg = defef(CFA + offset) - CFAPlusOffset, - /// Register or CFA is in or at a register plus offset, optionally in - /// an address space: - /// reg = reg + offset [in addrspace] - /// reg = deref(reg + offset [in addrspace]) - RegPlusOffset, - /// Register or CFA value is in or at a value found by evaluating a DWARF - /// expression: - /// reg = eval(dwarf_expr) - /// reg = deref(eval(dwarf_expr)) - DWARFExpr, - /// Value is a constant value contained in "Offset": - /// reg = Offset - Constant, - }; - -private: - Location Kind; /// The type of the location that describes how to unwind it. - uint32_t RegNum; /// The register number for Kind == RegPlusOffset. - int32_t Offset; /// The offset for Kind == CFAPlusOffset or RegPlusOffset. - std::optional AddrSpace; /// The address space for Kind == - /// RegPlusOffset for CFA. - std::optional Expr; /// The DWARF expression for Kind == - /// DWARFExpression. - bool Dereference; /// If true, the resulting location must be dereferenced - /// after the location value is computed. - - // Constructors are private to force people to use the create static - // functions. - UnwindLocation(Location K) - : Kind(K), RegNum(InvalidRegisterNumber), Offset(0), - AddrSpace(std::nullopt), Dereference(false) {} - - UnwindLocation(Location K, uint32_t Reg, int32_t Off, - std::optional AS, bool Deref) - : Kind(K), RegNum(Reg), Offset(Off), AddrSpace(AS), Dereference(Deref) {} - - UnwindLocation(DWARFExpression E, bool Deref) - : Kind(DWARFExpr), RegNum(InvalidRegisterNumber), Offset(0), Expr(E), - Dereference(Deref) {} - -public: - /// Create a location whose rule is set to Unspecified. This means the - /// register value might be in the same register but it wasn't specified in - /// the unwind opcodes. - LLVM_ABI static UnwindLocation createUnspecified(); - /// Create a location where the value is undefined and not available. This can - /// happen when a register is volatile and can't be recovered. - LLVM_ABI static UnwindLocation createUndefined(); - /// Create a location where the value is known to be in the register itself. - LLVM_ABI static UnwindLocation createSame(); - /// Create a location that is in (Deref == false) or at (Deref == true) the - /// CFA plus an offset. Most registers that are spilled onto the stack use - /// this rule. The rule for the register will use this rule and specify a - /// unique offset from the CFA with \a Deref set to true. This value will be - /// relative to a CFA value which is typically defined using the register - /// plus offset location. \see createRegisterPlusOffset(...) for more - /// information. - LLVM_ABI static UnwindLocation createIsCFAPlusOffset(int32_t Off); - LLVM_ABI static UnwindLocation createAtCFAPlusOffset(int32_t Off); - /// Create a location where the saved value is in (Deref == false) or at - /// (Deref == true) a regiser plus an offset and, optionally, in the specified - /// address space (used mostly for the CFA). - /// - /// The CFA is usually defined using this rule by using the stack pointer or - /// frame pointer as the register, with an offset that accounts for all - /// spilled registers and all local variables in a function, and Deref == - /// false. - LLVM_ABI static UnwindLocation - createIsRegisterPlusOffset(uint32_t Reg, int32_t Off, - std::optional AddrSpace = std::nullopt); - LLVM_ABI static UnwindLocation - createAtRegisterPlusOffset(uint32_t Reg, int32_t Off, - std::optional AddrSpace = std::nullopt); - /// Create a location whose value is the result of evaluating a DWARF - /// expression. This allows complex expressions to be evaluated in order to - /// unwind a register or CFA value. - LLVM_ABI static UnwindLocation createIsDWARFExpression(DWARFExpression Expr); - LLVM_ABI static UnwindLocation createAtDWARFExpression(DWARFExpression Expr); - LLVM_ABI static UnwindLocation createIsConstant(int32_t Value); - - Location getLocation() const { return Kind; } - uint32_t getRegister() const { return RegNum; } - int32_t getOffset() const { return Offset; } - uint32_t getAddressSpace() const { - assert(Kind == RegPlusOffset && AddrSpace); - return *AddrSpace; - } - int32_t getConstant() const { return Offset; } - /// Some opcodes will modify the CFA location's register only, so we need - /// to be able to modify the CFA register when evaluating DWARF Call Frame - /// Information opcodes. - void setRegister(uint32_t NewRegNum) { RegNum = NewRegNum; } - /// Some opcodes will modify the CFA location's offset only, so we need - /// to be able to modify the CFA offset when evaluating DWARF Call Frame - /// Information opcodes. - void setOffset(int32_t NewOffset) { Offset = NewOffset; } - /// Some opcodes modify a constant value and we need to be able to update - /// the constant value (DW_CFA_GNU_window_save which is also known as - // DW_CFA_AARCH64_negate_ra_state). - void setConstant(int32_t Value) { Offset = Value; } - - std::optional getDWARFExpressionBytes() const { - return Expr; - } - /// Dump a location expression as text and use the register information if - /// some is provided. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; - - LLVM_ABI bool operator==(const UnwindLocation &RHS) const; -}; - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R); - -/// A class that can track all registers with locations in a UnwindRow object. -/// -/// Register locations use a map where the key is the register number and the -/// the value is a UnwindLocation. -/// -/// The register maps are put into a class so that all register locations can -/// be copied when parsing the unwind opcodes DW_CFA_remember_state and -/// DW_CFA_restore_state. -class RegisterLocations { - std::map Locations; - -public: - /// Return the location for the register in \a RegNum if there is a location. - /// - /// \param RegNum the register number to find a location for. - /// - /// \returns A location if one is available for \a RegNum, or std::nullopt - /// otherwise. - std::optional getRegisterLocation(uint32_t RegNum) const { - auto Pos = Locations.find(RegNum); - if (Pos == Locations.end()) - return std::nullopt; - return Pos->second; - } - - /// Set the location for the register in \a RegNum to \a Location. - /// - /// \param RegNum the register number to set the location for. - /// - /// \param Location the UnwindLocation that describes how to unwind the value. - void setRegisterLocation(uint32_t RegNum, const UnwindLocation &Location) { - Locations.erase(RegNum); - Locations.insert(std::make_pair(RegNum, Location)); - } - - /// Removes any rule for the register in \a RegNum. - /// - /// \param RegNum the register number to remove the location for. - void removeRegisterLocation(uint32_t RegNum) { Locations.erase(RegNum); } - - /// Dump all registers + locations that are currently defined in this object. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; - - /// Returns true if we have any register locations in this object. - bool hasLocations() const { return !Locations.empty(); } - - size_t size() const { return Locations.size(); } - - bool operator==(const RegisterLocations &RHS) const { - return Locations == RHS.Locations; - } -}; - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL); - -/// A class that represents a single row in the unwind table that is decoded by -/// parsing the DWARF Call Frame Information opcodes. -/// -/// The row consists of an optional address, the rule to unwind the CFA and all -/// rules to unwind any registers. If the address doesn't have a value, this -/// row represents the initial instructions for a CIE. If the address has a -/// value the UnwindRow represents a row in the UnwindTable for a FDE. The -/// address is the first address for which the CFA location and register rules -/// are valid within a function. -/// -/// UnwindRow objects are created by parsing opcodes in the DWARF Call Frame -/// Information and UnwindRow objects are lazily populated and pushed onto a -/// stack in the UnwindTable when evaluating this state machine. Accessors are -/// needed for the address, CFA value, and register locations as the opcodes -/// encode a state machine that produces a sorted array of UnwindRow objects -/// \see UnwindTable. -class UnwindRow { - /// The address will be valid when parsing the instructions in a FDE. If - /// invalid, this object represents the initial instructions of a CIE. - std::optional Address; ///< Address for row in FDE, invalid for CIE. - UnwindLocation CFAValue; ///< How to unwind the Call Frame Address (CFA). - RegisterLocations RegLocs; ///< How to unwind all registers in this list. - -public: - UnwindRow() : CFAValue(UnwindLocation::createUnspecified()) {} - - /// Returns true if the address is valid in this object. - bool hasAddress() const { return Address.has_value(); } - - /// Get the address for this row. - /// - /// Clients should only call this function after verifying it has a valid - /// address with a call to \see hasAddress(). - uint64_t getAddress() const { return *Address; } - - /// Set the address for this UnwindRow. - /// - /// The address represents the first address for which the CFAValue and - /// RegLocs are valid within a function. - void setAddress(uint64_t Addr) { Address = Addr; } - - /// Offset the address for this UnwindRow. - /// - /// The address represents the first address for which the CFAValue and - /// RegLocs are valid within a function. Clients must ensure that this object - /// already has an address (\see hasAddress()) prior to calling this - /// function. - void slideAddress(uint64_t Offset) { *Address += Offset; } - UnwindLocation &getCFAValue() { return CFAValue; } - const UnwindLocation &getCFAValue() const { return CFAValue; } - RegisterLocations &getRegisterLocations() { return RegLocs; } - const RegisterLocations &getRegisterLocations() const { return RegLocs; } - - /// Dump the UnwindRow to the stream. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - /// - /// \param IndentLevel specify the indent level as an integer. The UnwindRow - /// will be output to the stream preceded by 2 * IndentLevel number of spaces. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel = 0) const; -}; - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row); - -/// A class that contains all UnwindRow objects for an FDE or a single unwind -/// row for a CIE. To unwind an address the rows, which are sorted by start -/// address, can be searched to find the UnwindRow with the lowest starting -/// address that is greater than or equal to the address that is being looked -/// up. -class UnwindTable { -public: - using RowContainer = std::vector; - using iterator = RowContainer::iterator; - using const_iterator = RowContainer::const_iterator; - - UnwindTable(RowContainer &&Rows) : Rows(std::move(Rows)) {} - - size_t size() const { return Rows.size(); } - iterator begin() { return Rows.begin(); } - const_iterator begin() const { return Rows.begin(); } - iterator end() { return Rows.end(); } - const_iterator end() const { return Rows.end(); } - const UnwindRow &operator[](size_t Index) const { - assert(Index < size()); - return Rows[Index]; - } - - /// Dump the UnwindTable to the stream. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names instead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - /// - /// \param IndentLevel specify the indent level as an integer. The UnwindRow - /// will be output to the stream preceded by 2 * IndentLevel number of spaces. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel = 0) const; - -private: - RowContainer Rows; -}; - -/// Parse the information in the CFIProgram and update the CurrRow object -/// that the state machine describes. -/// -/// This function emulates the state machine described in the DWARF Call Frame -/// Information opcodes and will push CurrRow onto a RowContainer when needed. -/// -/// \param CFIP the CFI program that contains the opcodes from a CIE or FDE. -/// -/// \param CurrRow the current row to modify while parsing the state machine. -/// -/// \param InitialLocs If non-NULL, we are parsing a FDE and this contains -/// the initial register locations from the CIE. If NULL, then a CIE's -/// opcodes are being parsed and this is not needed. This is used for the -/// DW_CFA_restore and DW_CFA_restore_extended opcodes. -/// -/// \returns An error if the DWARF Call Frame Information opcodes have state -/// machine errors, or the accumulated rows otherwise. -LLVM_ABI Expected -parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, - const RegisterLocations *InitialLocs); - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows); - class CIE; /// Create an UnwindTable from a Common Information Entry (CIE). diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h index e91c5175a3e88..802ff40419f49 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h @@ -311,6 +311,8 @@ class UnwindTable { using iterator = RowContainer::iterator; using const_iterator = RowContainer::const_iterator; + UnwindTable(RowContainer &&Rows) : Rows(std::move(Rows)) {} + size_t size() const { return Rows.size(); } iterator begin() { return Rows.begin(); } const_iterator begin() const { return Rows.begin(); } @@ -320,18 +322,12 @@ class UnwindTable { assert(Index < size()); return Rows[Index]; } - void insertRow(const UnwindRow &Row) { Rows.push_back(Row); } - - /// Set the last address that this unwinding table refers to. - /// - /// This is used when this table is created based on a FDE. - void setEndAddress(uint64_t Addr) { EndAddress = Addr; } /// Dump the UnwindTable to the stream. /// /// \param OS the stream to use for output. /// - /// \param MRI register information that helps emit register names insteead + /// \param MRI register information that helps emit register names instead /// of raw register numbers. /// /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame @@ -344,31 +340,31 @@ class UnwindTable { LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, unsigned IndentLevel = 0) const; - /// Parse the information in the CFIProgram and update the CurrRow object - /// that the state machine describes. - /// - /// This is an internal implementation that emulates the state machine - /// described in the DWARF Call Frame Information opcodes and will push - /// CurrRow onto the Rows container when needed. - /// - /// \param CFIP the CFI program that contains the opcodes from a CIE or FDE. - /// - /// \param CurrRow the current row to modify while parsing the state machine. - /// - /// \param InitialLocs If non-NULL, we are parsing a FDE and this contains - /// the initial register locations from the CIE. If NULL, then a CIE's - /// opcodes are being parsed and this is not needed. This is used for the - /// DW_CFA_restore and DW_CFA_restore_extended opcodes. - LLVM_ABI Error parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, - const RegisterLocations *InitialLocs); - private: RowContainer Rows; - /// The end address when data is extracted from a FDE. This value will be - /// invalid when a UnwindTable is extracted from a CIE. - std::optional EndAddress; }; +/// Parse the information in the CFIProgram and update the CurrRow object +/// that the state machine describes. +/// +/// This function emulates the state machine described in the DWARF Call Frame +/// Information opcodes and will push CurrRow onto a RowContainer when needed. +/// +/// \param CFIP the CFI program that contains the opcodes from a CIE or FDE. +/// +/// \param CurrRow the current row to modify while parsing the state machine. +/// +/// \param InitialLocs If non-NULL, we are parsing a FDE and this contains +/// the initial register locations from the CIE. If NULL, then a CIE's +/// opcodes are being parsed and this is not needed. This is used for the +/// DW_CFA_restore and DW_CFA_restore_extended opcodes. +/// +/// \returns An error if the DWARF Call Frame Information opcodes have state +/// machine errors, or the accumulated rows otherwise. +LLVM_ABI Expected +parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, + const RegisterLocations *InitialLocs); + LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows); } // end namespace dwarf diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index 9184502adc7cf..de6c279d71557 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -31,180 +31,6 @@ using namespace llvm; using namespace dwarf; -static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned RegNum) { - if (DumpOpts.GetNameForDWARFReg) { - auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH); - if (!RegName.empty()) { - OS << RegName; - return; - } - } - OS << "reg" << RegNum; -} - -UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; } - -UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; } - -UnwindLocation UnwindLocation::createSame() { return {Same}; } - -UnwindLocation UnwindLocation::createIsConstant(int32_t Value) { - return {Constant, InvalidRegisterNumber, Value, std::nullopt, false}; -} - -UnwindLocation UnwindLocation::createIsCFAPlusOffset(int32_t Offset) { - return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, false}; -} - -UnwindLocation UnwindLocation::createAtCFAPlusOffset(int32_t Offset) { - return {CFAPlusOffset, InvalidRegisterNumber, Offset, std::nullopt, true}; -} - -UnwindLocation -UnwindLocation::createIsRegisterPlusOffset(uint32_t RegNum, int32_t Offset, - std::optional AddrSpace) { - return {RegPlusOffset, RegNum, Offset, AddrSpace, false}; -} - -UnwindLocation -UnwindLocation::createAtRegisterPlusOffset(uint32_t RegNum, int32_t Offset, - std::optional AddrSpace) { - return {RegPlusOffset, RegNum, Offset, AddrSpace, true}; -} - -UnwindLocation UnwindLocation::createIsDWARFExpression(DWARFExpression Expr) { - return {Expr, false}; -} - -UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) { - return {Expr, true}; -} - -void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { - if (Dereference) - OS << '['; - switch (Kind) { - case Unspecified: - OS << "unspecified"; - break; - case Undefined: - OS << "undefined"; - break; - case Same: - OS << "same"; - break; - case CFAPlusOffset: - OS << "CFA"; - if (Offset == 0) - break; - if (Offset > 0) - OS << "+"; - OS << Offset; - break; - case RegPlusOffset: - printRegister(OS, DumpOpts, RegNum); - if (Offset == 0 && !AddrSpace) - break; - if (Offset >= 0) - OS << "+"; - OS << Offset; - if (AddrSpace) - OS << " in addrspace" << *AddrSpace; - break; - case DWARFExpr: { - if (Expr) - DWARFExpressionPrinter::print(&(*Expr), OS, DumpOpts, nullptr); - break; - } - case Constant: - OS << Offset; - break; - } - if (Dereference) - OS << ']'; -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, - const UnwindLocation &UL) { - auto DumpOpts = DIDumpOptions(); - UL.dump(OS, DumpOpts); - return OS; -} - -bool UnwindLocation::operator==(const UnwindLocation &RHS) const { - if (Kind != RHS.Kind) - return false; - switch (Kind) { - case Unspecified: - case Undefined: - case Same: - return true; - case CFAPlusOffset: - return Offset == RHS.Offset && Dereference == RHS.Dereference; - case RegPlusOffset: - return RegNum == RHS.RegNum && Offset == RHS.Offset && - Dereference == RHS.Dereference; - case DWARFExpr: - return *Expr == *RHS.Expr && Dereference == RHS.Dereference; - case Constant: - return Offset == RHS.Offset; - } - return false; -} - -void RegisterLocations::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { - bool First = true; - for (const auto &RegLocPair : Locations) { - if (First) - First = false; - else - OS << ", "; - printRegister(OS, DumpOpts, RegLocPair.first); - OS << '='; - RegLocPair.second.dump(OS, DumpOpts); - } -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, - const RegisterLocations &RL) { - auto DumpOpts = DIDumpOptions(); - RL.dump(OS, DumpOpts); - return OS; -} - -void UnwindRow::dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel) const { - OS.indent(2 * IndentLevel); - if (hasAddress()) - OS << format("0x%" PRIx64 ": ", *Address); - OS << "CFA="; - CFAValue.dump(OS, DumpOpts); - if (RegLocs.hasLocations()) { - OS << ": "; - RegLocs.dump(OS, DumpOpts); - } - OS << "\n"; -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) { - auto DumpOpts = DIDumpOptions(); - Row.dump(OS, DumpOpts, 0); - return OS; -} - -void UnwindTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel) const { - for (const UnwindRow &Row : Rows) - Row.dump(OS, DumpOpts, IndentLevel); -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) { - auto DumpOpts = DIDumpOptions(); - Rows.dump(OS, DumpOpts, 0); - return OS; -} - Expected llvm::dwarf::createUnwindTable(const FDE *Fde) { const CIE *Cie = Fde->getLinkedCIE(); if (Cie == nullptr) @@ -258,316 +84,6 @@ Expected llvm::dwarf::createUnwindTable(const CIE *Cie) { return UnwindTable(std::move(Rows)); } -Expected -llvm::dwarf::parseRows(const CFIProgram &CFIP, UnwindRow &Row, - const RegisterLocations *InitialLocs) { - // All the unwinding rows parsed during processing of the CFI program. - UnwindTable::RowContainer Rows; - - // State consists of CFA value and register locations. - std::vector> States; - for (const CFIProgram::Instruction &Inst : CFIP) { - switch (Inst.Opcode) { - case dwarf::DW_CFA_set_loc: { - // The DW_CFA_set_loc instruction takes a single operand that - // represents a target address. The required action is to create a new - // table row using the specified address as the location. All other - // values in the new row are initially identical to the current row. - // The new location value is always greater than the current one. If - // the segment_size field of this FDE's CIE is non- zero, the initial - // location is preceded by a segment selector of the given length - llvm::Expected NewAddress = Inst.getOperandAsUnsigned(CFIP, 0); - if (!NewAddress) - return NewAddress.takeError(); - if (*NewAddress <= Row.getAddress()) - return createStringError( - errc::invalid_argument, - "%s with adrress 0x%" PRIx64 " which must be greater than the " - "current row address 0x%" PRIx64, - CFIP.callFrameString(Inst.Opcode).str().c_str(), *NewAddress, - Row.getAddress()); - Rows.push_back(Row); - Row.setAddress(*NewAddress); - break; - } - - case dwarf::DW_CFA_advance_loc: - case dwarf::DW_CFA_advance_loc1: - case dwarf::DW_CFA_advance_loc2: - case dwarf::DW_CFA_advance_loc4: { - // The DW_CFA_advance instruction takes a single operand that - // represents a constant delta. The required action is to create a new - // table row with a location value that is computed by taking the - // current entry’s location value and adding the value of delta * - // code_alignment_factor. All other values in the new row are initially - // identical to the current row. - Rows.push_back(Row); - llvm::Expected Offset = Inst.getOperandAsUnsigned(CFIP, 0); - if (!Offset) - return Offset.takeError(); - Row.slideAddress(*Offset); - break; - } - - case dwarf::DW_CFA_restore: - case dwarf::DW_CFA_restore_extended: { - // The DW_CFA_restore instruction takes a single operand (encoded with - // the opcode) that represents a register number. The required action - // is to change the rule for the indicated register to the rule - // assigned it by the initial_instructions in the CIE. - if (InitialLocs == nullptr) - return createStringError( - errc::invalid_argument, "%s encountered while parsing a CIE", - CFIP.callFrameString(Inst.Opcode).str().c_str()); - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - if (std::optional O = - InitialLocs->getRegisterLocation(*RegNum)) - Row.getRegisterLocations().setRegisterLocation(*RegNum, *O); - else - Row.getRegisterLocations().removeRegisterLocation(*RegNum); - break; - } - - case dwarf::DW_CFA_offset: - case dwarf::DW_CFA_offset_extended: - case dwarf::DW_CFA_offset_extended_sf: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); - if (!Offset) - return Offset.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createAtCFAPlusOffset(*Offset)); - break; - } - - case dwarf::DW_CFA_nop: - break; - - case dwarf::DW_CFA_remember_state: - States.push_back( - std::make_pair(Row.getCFAValue(), Row.getRegisterLocations())); - break; - - case dwarf::DW_CFA_restore_state: - if (States.empty()) - return createStringError(errc::invalid_argument, - "DW_CFA_restore_state without a matching " - "previous DW_CFA_remember_state"); - Row.getCFAValue() = States.back().first; - Row.getRegisterLocations() = States.back().second; - States.pop_back(); - break; - - case dwarf::DW_CFA_GNU_window_save: - switch (CFIP.triple()) { - case Triple::aarch64: - case Triple::aarch64_be: - case Triple::aarch64_32: { - // DW_CFA_GNU_window_save is used for different things on different - // architectures. For aarch64 it is known as - // DW_CFA_AARCH64_negate_ra_state. The action is to toggle the - // value of the return address state between 1 and 0. If there is - // no rule for the AARCH64_DWARF_PAUTH_RA_STATE register, then it - // should be initially set to 1. - constexpr uint32_t AArch64DWARFPAuthRaState = 34; - auto LRLoc = Row.getRegisterLocations().getRegisterLocation( - AArch64DWARFPAuthRaState); - if (LRLoc) { - if (LRLoc->getLocation() == UnwindLocation::Constant) { - // Toggle the constant value from 0 to 1 or 1 to 0. - LRLoc->setConstant(LRLoc->getConstant() ^ 1); - Row.getRegisterLocations().setRegisterLocation( - AArch64DWARFPAuthRaState, *LRLoc); - } else { - return createStringError( - errc::invalid_argument, - "%s encountered when existing rule for this register is not " - "a constant", - CFIP.callFrameString(Inst.Opcode).str().c_str()); - } - } else { - Row.getRegisterLocations().setRegisterLocation( - AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(1)); - } - break; - } - - case Triple::sparc: - case Triple::sparcv9: - case Triple::sparcel: - for (uint32_t RegNum = 16; RegNum < 32; ++RegNum) { - Row.getRegisterLocations().setRegisterLocation( - RegNum, UnwindLocation::createAtCFAPlusOffset((RegNum - 16) * 8)); - } - break; - - default: { - return createStringError( - errc::not_supported, - "DW_CFA opcode %#x is not supported for architecture %s", - Inst.Opcode, Triple::getArchTypeName(CFIP.triple()).str().c_str()); - - break; - } - } - break; - - case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: { - constexpr uint32_t AArch64DWARFPAuthRaState = 34; - auto LRLoc = Row.getRegisterLocations().getRegisterLocation( - AArch64DWARFPAuthRaState); - if (LRLoc) { - if (LRLoc->getLocation() == UnwindLocation::Constant) { - // Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0. - LRLoc->setConstant(LRLoc->getConstant() ^ 0x3); - } else { - return createStringError( - errc::invalid_argument, - "%s encountered when existing rule for this register is not " - "a constant", - CFIP.callFrameString(Inst.Opcode).str().c_str()); - } - } else { - Row.getRegisterLocations().setRegisterLocation( - AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(0x3)); - } - break; - } - - case dwarf::DW_CFA_undefined: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createUndefined()); - break; - } - - case dwarf::DW_CFA_same_value: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createSame()); - break; - } - - case dwarf::DW_CFA_GNU_args_size: - break; - - case dwarf::DW_CFA_register: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected NewRegNum = Inst.getOperandAsUnsigned(CFIP, 1); - if (!NewRegNum) - return NewRegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createIsRegisterPlusOffset(*NewRegNum, 0)); - break; - } - - case dwarf::DW_CFA_val_offset: - case dwarf::DW_CFA_val_offset_sf: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); - if (!Offset) - return Offset.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createIsCFAPlusOffset(*Offset)); - break; - } - - case dwarf::DW_CFA_expression: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createAtDWARFExpression(*Inst.Expression)); - break; - } - - case dwarf::DW_CFA_val_expression: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - Row.getRegisterLocations().setRegisterLocation( - *RegNum, UnwindLocation::createIsDWARFExpression(*Inst.Expression)); - break; - } - - case dwarf::DW_CFA_def_cfa_register: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) - Row.getCFAValue() = - UnwindLocation::createIsRegisterPlusOffset(*RegNum, 0); - else - Row.getCFAValue().setRegister(*RegNum); - break; - } - - case dwarf::DW_CFA_def_cfa_offset: - case dwarf::DW_CFA_def_cfa_offset_sf: { - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 0); - if (!Offset) - return Offset.takeError(); - if (Row.getCFAValue().getLocation() != UnwindLocation::RegPlusOffset) { - return createStringError( - errc::invalid_argument, - "%s found when CFA rule was not RegPlusOffset", - CFIP.callFrameString(Inst.Opcode).str().c_str()); - } - Row.getCFAValue().setOffset(*Offset); - break; - } - - case dwarf::DW_CFA_def_cfa: - case dwarf::DW_CFA_def_cfa_sf: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); - if (!Offset) - return Offset.takeError(); - Row.getCFAValue() = - UnwindLocation::createIsRegisterPlusOffset(*RegNum, *Offset); - break; - } - - case dwarf::DW_CFA_LLVM_def_aspace_cfa: - case dwarf::DW_CFA_LLVM_def_aspace_cfa_sf: { - llvm::Expected RegNum = Inst.getOperandAsUnsigned(CFIP, 0); - if (!RegNum) - return RegNum.takeError(); - llvm::Expected Offset = Inst.getOperandAsSigned(CFIP, 1); - if (!Offset) - return Offset.takeError(); - llvm::Expected CFAAddrSpace = - Inst.getOperandAsUnsigned(CFIP, 2); - if (!CFAAddrSpace) - return CFAAddrSpace.takeError(); - Row.getCFAValue() = UnwindLocation::createIsRegisterPlusOffset( - *RegNum, *Offset, *CFAAddrSpace); - break; - } - - case dwarf::DW_CFA_def_cfa_expression: - Row.getCFAValue() = - UnwindLocation::createIsDWARFExpression(*Inst.Expression); - break; - } - } - return Rows; -} - // Returns the CIE identifier to be used by the requested format. // CIE ids for .debug_frame sections are defined in Section 7.24 of DWARFv5. // For CIE ID in .eh_frame sections see diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp index 330eed900221e..429eed77384a8 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp @@ -102,7 +102,8 @@ void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { OS << " in addrspace" << *AddrSpace; break; case DWARFExpr: { - Expr->print(OS, DumpOpts, nullptr); + if (Expr) + DWARFExpressionPrinter::print(&(*Expr), OS, DumpOpts, nullptr); break; } case Constant: @@ -193,8 +194,12 @@ raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) { return OS; } -Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row, - const RegisterLocations *InitialLocs) { +Expected +llvm::dwarf::parseRows(const CFIProgram &CFIP, UnwindRow &Row, + const RegisterLocations *InitialLocs) { + // All the unwinding rows parsed during processing of the CFI program. + UnwindTable::RowContainer Rows; + // State consists of CFA value and register locations. std::vector> States; for (const CFIProgram::Instruction &Inst : CFIP) { @@ -496,5 +501,5 @@ Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row, break; } } - return Error::success(); + return Rows; } From 1b6686ab0599f0aafb16523bbae5b140e758c616 Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Thu, 26 Jun 2025 19:08:51 +0000 Subject: [PATCH 08/18] Use the new expression printer function --- llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp index 429eed77384a8..253e5a9f0410d 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp @@ -8,6 +8,7 @@ #include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h" #include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" @@ -103,7 +104,7 @@ void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { break; case DWARFExpr: { if (Expr) - DWARFExpressionPrinter::print(&(*Expr), OS, DumpOpts, nullptr); + printDwarfExpression(&(*Expr), OS, DumpOpts, nullptr); break; } case Constant: From 571edd81c000caea0c05d99eb4bf5552aedb6141 Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Thu, 26 Jun 2025 19:14:35 +0000 Subject: [PATCH 09/18] Lint --- llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h index 78b4c1af44f32..a821013c70a45 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -11,9 +11,9 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/iterator.h" +#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h" #include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h" #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h" -#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Error.h" #include "llvm/TargetParser/Triple.h" From 5d34629826d0e91679954ebac017868d78a55789 Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Wed, 2 Jul 2025 00:43:56 +0000 Subject: [PATCH 10/18] Merge main --- .../llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 350 ------------------ .../llvm/DebugInfo/DWARF/DWARFUnwindTable.h | 6 +- 2 files changed, 4 insertions(+), 352 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h index ee9e3621e64c0..a821013c70a45 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -29,356 +29,6 @@ struct DIDumpOptions; namespace dwarf { -constexpr uint32_t InvalidRegisterNumber = UINT32_MAX; - -/// A class that represents a location for the Call Frame Address (CFA) or a -/// register. This is decoded from the DWARF Call Frame Information -/// instructions and put into an UnwindRow. -class UnwindLocation { -public: - enum Location { - /// Not specified. - Unspecified, - /// Register is not available and can't be recovered. - Undefined, - /// Register value is in the register, nothing needs to be done to unwind - /// it: - /// reg = reg - Same, - /// Register is in or at the CFA plus an offset: - /// reg = CFA + offset - /// reg = defef(CFA + offset) - CFAPlusOffset, - /// Register or CFA is in or at a register plus offset, optionally in - /// an address space: - /// reg = reg + offset [in addrspace] - /// reg = deref(reg + offset [in addrspace]) - RegPlusOffset, - /// Register or CFA value is in or at a value found by evaluating a DWARF - /// expression: - /// reg = eval(dwarf_expr) - /// reg = deref(eval(dwarf_expr)) - DWARFExpr, - /// Value is a constant value contained in "Offset": - /// reg = Offset - Constant, - }; - -private: - Location Kind; /// The type of the location that describes how to unwind it. - uint32_t RegNum; /// The register number for Kind == RegPlusOffset. - int32_t Offset; /// The offset for Kind == CFAPlusOffset or RegPlusOffset. - std::optional AddrSpace; /// The address space for Kind == - /// RegPlusOffset for CFA. - std::optional Expr; /// The DWARF expression for Kind == - /// DWARFExpression. - bool Dereference; /// If true, the resulting location must be dereferenced - /// after the location value is computed. - - // Constructors are private to force people to use the create static - // functions. - UnwindLocation(Location K) - : Kind(K), RegNum(InvalidRegisterNumber), Offset(0), - AddrSpace(std::nullopt), Dereference(false) {} - - UnwindLocation(Location K, uint32_t Reg, int32_t Off, - std::optional AS, bool Deref) - : Kind(K), RegNum(Reg), Offset(Off), AddrSpace(AS), Dereference(Deref) {} - - UnwindLocation(DWARFExpression E, bool Deref) - : Kind(DWARFExpr), RegNum(InvalidRegisterNumber), Offset(0), Expr(E), - Dereference(Deref) {} - -public: - /// Create a location whose rule is set to Unspecified. This means the - /// register value might be in the same register but it wasn't specified in - /// the unwind opcodes. - LLVM_ABI static UnwindLocation createUnspecified(); - /// Create a location where the value is undefined and not available. This can - /// happen when a register is volatile and can't be recovered. - LLVM_ABI static UnwindLocation createUndefined(); - /// Create a location where the value is known to be in the register itself. - LLVM_ABI static UnwindLocation createSame(); - /// Create a location that is in (Deref == false) or at (Deref == true) the - /// CFA plus an offset. Most registers that are spilled onto the stack use - /// this rule. The rule for the register will use this rule and specify a - /// unique offset from the CFA with \a Deref set to true. This value will be - /// relative to a CFA value which is typically defined using the register - /// plus offset location. \see createRegisterPlusOffset(...) for more - /// information. - LLVM_ABI static UnwindLocation createIsCFAPlusOffset(int32_t Off); - LLVM_ABI static UnwindLocation createAtCFAPlusOffset(int32_t Off); - /// Create a location where the saved value is in (Deref == false) or at - /// (Deref == true) a regiser plus an offset and, optionally, in the specified - /// address space (used mostly for the CFA). - /// - /// The CFA is usually defined using this rule by using the stack pointer or - /// frame pointer as the register, with an offset that accounts for all - /// spilled registers and all local variables in a function, and Deref == - /// false. - LLVM_ABI static UnwindLocation - createIsRegisterPlusOffset(uint32_t Reg, int32_t Off, - std::optional AddrSpace = std::nullopt); - LLVM_ABI static UnwindLocation - createAtRegisterPlusOffset(uint32_t Reg, int32_t Off, - std::optional AddrSpace = std::nullopt); - /// Create a location whose value is the result of evaluating a DWARF - /// expression. This allows complex expressions to be evaluated in order to - /// unwind a register or CFA value. - LLVM_ABI static UnwindLocation createIsDWARFExpression(DWARFExpression Expr); - LLVM_ABI static UnwindLocation createAtDWARFExpression(DWARFExpression Expr); - LLVM_ABI static UnwindLocation createIsConstant(int32_t Value); - - Location getLocation() const { return Kind; } - uint32_t getRegister() const { return RegNum; } - int32_t getOffset() const { return Offset; } - uint32_t getAddressSpace() const { - assert(Kind == RegPlusOffset && AddrSpace); - return *AddrSpace; - } - int32_t getConstant() const { return Offset; } - bool getDereference() const { return Dereference; } - - /// Some opcodes will modify the CFA location's register only, so we need - /// to be able to modify the CFA register when evaluating DWARF Call Frame - /// Information opcodes. - void setRegister(uint32_t NewRegNum) { RegNum = NewRegNum; } - /// Some opcodes will modify the CFA location's offset only, so we need - /// to be able to modify the CFA offset when evaluating DWARF Call Frame - /// Information opcodes. - void setOffset(int32_t NewOffset) { Offset = NewOffset; } - /// Some opcodes modify a constant value and we need to be able to update - /// the constant value (DW_CFA_GNU_window_save which is also known as - // DW_CFA_AARCH64_negate_ra_state). - void setConstant(int32_t Value) { Offset = Value; } - - std::optional getDWARFExpressionBytes() const { - return Expr; - } - /// Dump a location expression as text and use the register information if - /// some is provided. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; - - LLVM_ABI bool operator==(const UnwindLocation &RHS) const; -}; - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R); - -/// A class that can track all registers with locations in a UnwindRow object. -/// -/// Register locations use a map where the key is the register number and the -/// the value is a UnwindLocation. -/// -/// The register maps are put into a class so that all register locations can -/// be copied when parsing the unwind opcodes DW_CFA_remember_state and -/// DW_CFA_restore_state. -class RegisterLocations { - std::map Locations; - -public: - /// Return the location for the register in \a RegNum if there is a location. - /// - /// \param RegNum the register number to find a location for. - /// - /// \returns A location if one is available for \a RegNum, or std::nullopt - /// otherwise. - std::optional getRegisterLocation(uint32_t RegNum) const { - auto Pos = Locations.find(RegNum); - if (Pos == Locations.end()) - return std::nullopt; - return Pos->second; - } - - /// Set the location for the register in \a RegNum to \a Location. - /// - /// \param RegNum the register number to set the location for. - /// - /// \param Location the UnwindLocation that describes how to unwind the value. - void setRegisterLocation(uint32_t RegNum, const UnwindLocation &Location) { - Locations.erase(RegNum); - Locations.insert(std::make_pair(RegNum, Location)); - } - - /// Removes any rule for the register in \a RegNum. - /// - /// \param RegNum the register number to remove the location for. - void removeRegisterLocation(uint32_t RegNum) { Locations.erase(RegNum); } - - /// Dump all registers + locations that are currently defined in this object. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; - - /// Returns true if we have any register locations in this object. - bool hasLocations() const { return !Locations.empty(); } - - size_t size() const { return Locations.size(); } - - bool operator==(const RegisterLocations &RHS) const { - return Locations == RHS.Locations; - } -}; - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL); - -/// A class that represents a single row in the unwind table that is decoded by -/// parsing the DWARF Call Frame Information opcodes. -/// -/// The row consists of an optional address, the rule to unwind the CFA and all -/// rules to unwind any registers. If the address doesn't have a value, this -/// row represents the initial instructions for a CIE. If the address has a -/// value the UnwindRow represents a row in the UnwindTable for a FDE. The -/// address is the first address for which the CFA location and register rules -/// are valid within a function. -/// -/// UnwindRow objects are created by parsing opcodes in the DWARF Call Frame -/// Information and UnwindRow objects are lazily populated and pushed onto a -/// stack in the UnwindTable when evaluating this state machine. Accessors are -/// needed for the address, CFA value, and register locations as the opcodes -/// encode a state machine that produces a sorted array of UnwindRow objects -/// \see UnwindTable. -class UnwindRow { - /// The address will be valid when parsing the instructions in a FDE. If - /// invalid, this object represents the initial instructions of a CIE. - std::optional Address; ///< Address for row in FDE, invalid for CIE. - UnwindLocation CFAValue; ///< How to unwind the Call Frame Address (CFA). - RegisterLocations RegLocs; ///< How to unwind all registers in this list. - -public: - UnwindRow() : CFAValue(UnwindLocation::createUnspecified()) {} - - /// Returns true if the address is valid in this object. - bool hasAddress() const { return Address.has_value(); } - - /// Get the address for this row. - /// - /// Clients should only call this function after verifying it has a valid - /// address with a call to \see hasAddress(). - uint64_t getAddress() const { return *Address; } - - /// Set the address for this UnwindRow. - /// - /// The address represents the first address for which the CFAValue and - /// RegLocs are valid within a function. - void setAddress(uint64_t Addr) { Address = Addr; } - - /// Offset the address for this UnwindRow. - /// - /// The address represents the first address for which the CFAValue and - /// RegLocs are valid within a function. Clients must ensure that this object - /// already has an address (\see hasAddress()) prior to calling this - /// function. - void slideAddress(uint64_t Offset) { *Address += Offset; } - UnwindLocation &getCFAValue() { return CFAValue; } - const UnwindLocation &getCFAValue() const { return CFAValue; } - RegisterLocations &getRegisterLocations() { return RegLocs; } - const RegisterLocations &getRegisterLocations() const { return RegLocs; } - - /// Dump the UnwindRow to the stream. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - /// - /// \param IndentLevel specify the indent level as an integer. The UnwindRow - /// will be output to the stream preceded by 2 * IndentLevel number of spaces. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel = 0) const; -}; - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row); - -/// A class that contains all UnwindRow objects for an FDE or a single unwind -/// row for a CIE. To unwind an address the rows, which are sorted by start -/// address, can be searched to find the UnwindRow with the lowest starting -/// address that is greater than or equal to the address that is being looked -/// up. -class UnwindTable { -public: - using RowContainer = std::vector; - using iterator = RowContainer::iterator; - using const_iterator = RowContainer::const_iterator; - - UnwindTable(RowContainer &&Rows) : Rows(std::move(Rows)) {} - - size_t size() const { return Rows.size(); } - iterator begin() { return Rows.begin(); } - const_iterator begin() const { return Rows.begin(); } - iterator end() { return Rows.end(); } - const_iterator end() const { return Rows.end(); } - const UnwindRow &operator[](size_t Index) const { - assert(Index < size()); - return Rows[Index]; - } - - /// Dump the UnwindTable to the stream. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names instead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - /// - /// \param IndentLevel specify the indent level as an integer. The UnwindRow - /// will be output to the stream preceded by 2 * IndentLevel number of spaces. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel = 0) const; - -private: - RowContainer Rows; -}; - -/// Parse the information in the CFIProgram and update the CurrRow object -/// that the state machine describes. -/// -/// This function emulates the state machine described in the DWARF Call Frame -/// Information opcodes and will push CurrRow onto a RowContainer when needed. -/// -/// \param CFIP the CFI program that contains the opcodes from a CIE or FDE. -/// -/// \param CurrRow the current row to modify while parsing the state machine. -/// -/// \param InitialLocs If non-NULL, we are parsing a FDE and this contains -/// the initial register locations from the CIE. If NULL, then a CIE's -/// opcodes are being parsed and this is not needed. This is used for the -/// DW_CFA_restore and DW_CFA_restore_extended opcodes. -/// -/// \returns An error if the DWARF Call Frame Information opcodes have state -/// machine errors, or the accumulated rows otherwise. -LLVM_ABI Expected -parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, - const RegisterLocations *InitialLocs); - -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows); - class CIE; /// Create an UnwindTable from a Common Information Entry (CIE). diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h index 143f668824227..b7a1c77b35ebb 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h @@ -127,6 +127,8 @@ class UnwindLocation { return *AddrSpace; } int32_t getConstant() const { return Offset; } + bool getDereference() const { return Dereference; } + /// Some opcodes will modify the CFA location's register only, so we need /// to be able to modify the CFA register when evaluating DWARF Call Frame /// Information opcodes. @@ -247,8 +249,8 @@ class UnwindRow { /// The address will be valid when parsing the instructions in a FDE. If /// invalid, this object represents the initial instructions of a CIE. std::optional Address; ///< Address for row in FDE, invalid for CIE. - UnwindLocation CFAValue; ///< How to unwind the Call Frame Address (CFA). - RegisterLocations RegLocs; ///< How to unwind all registers in this list. + UnwindLocation CFAValue; ///< How to unwind the Call Frame Address (CFA). + RegisterLocations RegLocs; ///< How to unwind all registers in this list. public: UnwindRow() : CFAValue(UnwindLocation::createUnspecified()) {} From 682dcae0147d9298061ab65b7b9324c1f7512ca5 Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Wed, 2 Jul 2025 01:20:40 +0000 Subject: [PATCH 11/18] Lint --- llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h index b7a1c77b35ebb..d324e4dc8329d 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h @@ -249,8 +249,8 @@ class UnwindRow { /// The address will be valid when parsing the instructions in a FDE. If /// invalid, this object represents the initial instructions of a CIE. std::optional Address; ///< Address for row in FDE, invalid for CIE. - UnwindLocation CFAValue; ///< How to unwind the Call Frame Address (CFA). - RegisterLocations RegLocs; ///< How to unwind all registers in this list. + UnwindLocation CFAValue; ///< How to unwind the Call Frame Address (CFA). + RegisterLocations RegLocs; ///< How to unwind all registers in this list. public: UnwindRow() : CFAValue(UnwindLocation::createUnspecified()) {} From 1ed0df26166199c0081f54b06f4d606f34c01834 Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Wed, 2 Jul 2025 20:53:29 +0000 Subject: [PATCH 12/18] [TEMP] move unwind table to where it should be, the layering is not correct --- llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 2 +- .../llvm/DebugInfo/DWARF/{ => LowLevel}/DWARFUnwindTable.h | 0 llvm/lib/DebugInfo/DWARF/CMakeLists.txt | 1 - llvm/lib/DebugInfo/DWARF/LowLevel/CMakeLists.txt | 1 + llvm/lib/DebugInfo/DWARF/{ => LowLevel}/DWARFUnwindTable.cpp | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) rename llvm/include/llvm/DebugInfo/DWARF/{ => LowLevel}/DWARFUnwindTable.h (100%) rename llvm/lib/DebugInfo/DWARF/{ => LowLevel}/DWARFUnwindTable.cpp (99%) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h index a821013c70a45..65189916b41f1 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -11,9 +11,9 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/iterator.h" -#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h" #include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h" #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h" +#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Error.h" #include "llvm/TargetParser/Triple.h" diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h similarity index 100% rename from llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTable.h rename to llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h diff --git a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt index da2e2896faeae..ce426aeba3f3f 100644 --- a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt @@ -28,7 +28,6 @@ add_llvm_component_library(LLVMDebugInfoDWARF DWARFTypeUnit.cpp DWARFUnitIndex.cpp DWARFUnit.cpp - DWARFUnwindTable.cpp DWARFVerifier.cpp ADDITIONAL_HEADER_DIRS diff --git a/llvm/lib/DebugInfo/DWARF/LowLevel/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/LowLevel/CMakeLists.txt index c11a2589a552d..4e920aa7ae1a6 100644 --- a/llvm/lib/DebugInfo/DWARF/LowLevel/CMakeLists.txt +++ b/llvm/lib/DebugInfo/DWARF/LowLevel/CMakeLists.txt @@ -1,6 +1,7 @@ add_llvm_component_library(LLVMDebugInfoDWARFLowLevel DWARFCFIProgram.cpp DWARFExpression.cpp + DWARFUnwindTable.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/DWARF/LowLevel diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp similarity index 99% rename from llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp rename to llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp index 253e5a9f0410d..6e57d3344b95e 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "llvm/DebugInfo/DWARF/DWARFUnwindTable.h" +#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h" #include "llvm/Support/Errc.h" From 4042154200ff5ac6615ad3bd18a8c150ea524062 Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Thu, 3 Jul 2025 07:04:00 +0000 Subject: [PATCH 13/18] Move the printing logic of UnwindTable to UnwindTablePrinter --- .../DebugInfo/DWARF/DWARFUnwindTablePrinter.h | 102 +++++++++++++ .../DWARF/LowLevel/DWARFUnwindTable.h | 81 ++-------- llvm/lib/DebugInfo/DWARF/CMakeLists.txt | 1 + llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp | 5 +- .../DWARF/DWARFUnwindTablePrinter.cpp | 143 ++++++++++++++++++ .../DWARF/LowLevel/DWARFUnwindTable.cpp | 118 --------------- .../DebugInfo/DWARF/DWARFDebugFrameTest.cpp | 1 + 7 files changed, 263 insertions(+), 188 deletions(-) create mode 100644 llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h create mode 100644 llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h new file mode 100644 index 0000000000000..8498077ea4bd8 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h @@ -0,0 +1,102 @@ +//===- DWARFUnwindTablePrinter.h --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLEPRINTER_H +#define LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLEPRINTER_H + +#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h" +#include "llvm/Support/Compiler.h" + +namespace llvm { + +struct DIDumpOptions; + +namespace dwarf { + +/// Print an unwind location expression as text and use the register information +/// if some is provided. +/// +/// \param R the unwind location to print. +/// +/// \param OS the stream to use for output. +/// +/// \param MRI register information that helps emit register names insteead +/// of raw register numbers. +/// +/// \param IsEH true if the DWARF Call Frame Information is from .eh_frame +/// instead of from .debug_frame. This is needed for register number +/// conversion because some register numbers differ between the two sections +/// for certain architectures like x86. +LLVM_ABI void printUnwindLocation(const UnwindLocation &R, raw_ostream &OS, + DIDumpOptions DumpOpts); +LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R); + +/// Print all registers + locations that are currently defined in a register +/// locations. +/// +/// \param RL the register locations to print. +/// +/// \param OS the stream to use for output. +/// +/// \param MRI register information that helps emit register names insteead +/// of raw register numbers. +/// +/// \param IsEH true if the DWARF Call Frame Information is from .eh_frame +/// instead of from .debug_frame. This is needed for register number +/// conversion because some register numbers differ between the two sections +/// for certain architectures like x86. +LLVM_ABI void printRegisterLocations(const RegisterLocations &RL, + raw_ostream &OS, DIDumpOptions DumpOpts); +LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL); + +/// Print an UnwindRow to the stream. +/// +/// \param Row the UnwindRow to print. +/// +/// \param OS the stream to use for output. +/// +/// \param MRI register information that helps emit register names insteead +/// of raw register numbers. +/// +/// \param IsEH true if the DWARF Call Frame Information is from .eh_frame +/// instead of from .debug_frame. This is needed for register number +/// conversion because some register numbers differ between the two sections +/// for certain architectures like x86. +/// +/// \param IndentLevel specify the indent level as an integer. The UnwindRow +/// will be output to the stream preceded by 2 * IndentLevel number of spaces. +LLVM_ABI void printUnwindRow(const UnwindRow &Row, raw_ostream &OS, + DIDumpOptions DumpOpts, unsigned IndentLevel = 0); +LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row); + +/// Print a UnwindTable to the stream. +/// +/// \param Rows the UnwindTable to print. +/// +/// \param OS the stream to use for output. +/// +/// \param MRI register information that helps emit register names instead +/// of raw register numbers. +/// +/// \param IsEH true if the DWARF Call Frame Information is from .eh_frame +/// instead of from .debug_frame. This is needed for register number +/// conversion because some register numbers differ between the two sections +/// for certain architectures like x86. +/// +/// \param IndentLevel specify the indent level as an integer. The UnwindRow +/// will be output to the stream preceded by 2 * IndentLevel number of spaces. +LLVM_ABI void printUnwindTable(const UnwindTable &Rows, raw_ostream &OS, + DIDumpOptions DumpOpts, + unsigned IndentLevel = 0); +LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows); + +} // end namespace dwarf + +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLEPRINTER_H diff --git a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h index d324e4dc8329d..c8cad196cc2b5 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h +++ b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h @@ -9,6 +9,7 @@ #ifndef LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H #define LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H +#include "llvm/ADT/SmallVector.h" #include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h" #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h" #include "llvm/Support/Compiler.h" @@ -122,6 +123,11 @@ class UnwindLocation { Location getLocation() const { return Kind; } uint32_t getRegister() const { return RegNum; } int32_t getOffset() const { return Offset; } + bool hasAddressSpace() const { + if(AddrSpace) + return true; + return false; + } uint32_t getAddressSpace() const { assert(Kind == RegPlusOffset && AddrSpace); return *AddrSpace; @@ -145,25 +151,10 @@ class UnwindLocation { std::optional getDWARFExpressionBytes() const { return Expr; } - /// Dump a location expression as text and use the register information if - /// some is provided. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; LLVM_ABI bool operator==(const UnwindLocation &RHS) const; }; -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R); - /// A class that can track all registers with locations in a UnwindRow object. /// /// Register locations use a map where the key is the register number and the @@ -189,6 +180,13 @@ class RegisterLocations { return Pos->second; } + SmallVector getRegisters() const { + SmallVector Registers; + for(auto &&[Register, _]: Locations) + Registers.push_back(Register); + return Registers; + } + /// Set the location for the register in \a RegNum to \a Location. /// /// \param RegNum the register number to set the location for. @@ -204,19 +202,6 @@ class RegisterLocations { /// \param RegNum the register number to remove the location for. void removeRegisterLocation(uint32_t RegNum) { Locations.erase(RegNum); } - /// Dump all registers + locations that are currently defined in this object. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; - /// Returns true if we have any register locations in this object. bool hasLocations() const { return !Locations.empty(); } @@ -227,8 +212,6 @@ class RegisterLocations { } }; -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL); - /// A class that represents a single row in the unwind table that is decoded by /// parsing the DWARF Call Frame Information opcodes. /// @@ -281,27 +264,8 @@ class UnwindRow { const UnwindLocation &getCFAValue() const { return CFAValue; } RegisterLocations &getRegisterLocations() { return RegLocs; } const RegisterLocations &getRegisterLocations() const { return RegLocs; } - - /// Dump the UnwindRow to the stream. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names insteead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - /// - /// \param IndentLevel specify the indent level as an integer. The UnwindRow - /// will be output to the stream preceded by 2 * IndentLevel number of spaces. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel = 0) const; }; -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row); - /// A class that contains all UnwindRow objects for an FDE or a single unwind /// row for a CIE. To unwind an address the rows, which are sorted by start /// address, can be searched to find the UnwindRow with the lowest starting @@ -325,23 +289,6 @@ class UnwindTable { return Rows[Index]; } - /// Dump the UnwindTable to the stream. - /// - /// \param OS the stream to use for output. - /// - /// \param MRI register information that helps emit register names instead - /// of raw register numbers. - /// - /// \param IsEH true if the DWARF Call Frame Information is from .eh_frame - /// instead of from .debug_frame. This is needed for register number - /// conversion because some register numbers differ between the two sections - /// for certain architectures like x86. - /// - /// \param IndentLevel specify the indent level as an integer. The UnwindRow - /// will be output to the stream preceded by 2 * IndentLevel number of spaces. - LLVM_ABI void dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel = 0) const; - private: RowContainer Rows; }; @@ -367,8 +314,6 @@ LLVM_ABI Expected parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, const RegisterLocations *InitialLocs); -LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindTable &Rows); - } // end namespace dwarf } // end namespace llvm diff --git a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt index ce426aeba3f3f..3755be256a305 100644 --- a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt @@ -28,6 +28,7 @@ add_llvm_component_library(LLVMDebugInfoDWARF DWARFTypeUnit.cpp DWARFUnitIndex.cpp DWARFUnit.cpp + DWARFUnwindTablePrinter.cpp DWARFVerifier.cpp ADDITIONAL_HEADER_DIRS diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index f8505743aace0..6610eef54801b 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -15,6 +15,7 @@ #include "llvm/DebugInfo/DWARF/DWARFCFIPrinter.h" #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" #include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h" +#include "llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h" #include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h" #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h" #include "llvm/Support/Compiler.h" @@ -136,7 +137,7 @@ void CIE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { OS << "\n"; if (Expected RowsOrErr = createUnwindTable(this)) - RowsOrErr->dump(OS, DumpOpts, 1); + printUnwindTable(*RowsOrErr, OS, DumpOpts, 1); else { DumpOpts.RecoverableErrorHandler(joinErrors( createStringError(errc::invalid_argument, @@ -164,7 +165,7 @@ void FDE::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { OS << "\n"; if (Expected RowsOrErr = createUnwindTable(this)) - RowsOrErr->dump(OS, DumpOpts, 1); + printUnwindTable(*RowsOrErr, OS, DumpOpts, 1); else { DumpOpts.RecoverableErrorHandler(joinErrors( createStringError(errc::invalid_argument, diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp new file mode 100644 index 0000000000000..607c6f70eac52 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp @@ -0,0 +1,143 @@ +//=== DWARFUnwindTablePrinter.cpp - Print the cfi-portions of .debug_frame ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include + +using namespace llvm; +using namespace dwarf; + +static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts, + unsigned RegNum) { + if (DumpOpts.GetNameForDWARFReg) { + auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH); + if (!RegName.empty()) { + OS << RegName; + return; + } + } + OS << "reg" << RegNum; +} + +void llvm::dwarf::printUnwindLocation(const UnwindLocation &UL, raw_ostream &OS, + DIDumpOptions DumpOpts) { + if (UL.getDereference()) + OS << '['; + switch (UL.getLocation()) { + case UnwindLocation::Unspecified: + OS << "unspecified"; + break; + case UnwindLocation::Undefined: + OS << "undefined"; + break; + case UnwindLocation::Same: + OS << "same"; + break; + case UnwindLocation::CFAPlusOffset: + OS << "CFA"; + if (UL.getOffset() == 0) + break; + if (UL.getOffset() > 0) + OS << "+"; + OS << UL.getOffset(); + break; + case UnwindLocation::RegPlusOffset: + printRegister(OS, DumpOpts, UL.getRegister()); + if (UL.getOffset() == 0 && !UL.hasAddressSpace()) + break; + if (UL.getOffset() >= 0) + OS << "+"; + OS << UL.getOffset(); + if (UL.hasAddressSpace()) + OS << " in addrspace" << UL.getAddressSpace(); + break; + case UnwindLocation::DWARFExpr: { + if (UL.getDWARFExpressionBytes()) { + auto Expr = *UL.getDWARFExpressionBytes(); + printDwarfExpression(&Expr, OS, DumpOpts, nullptr); + } + break; + } + case UnwindLocation::Constant: + OS << UL.getOffset(); + break; + } + if (UL.getDereference()) + OS << ']'; +} + +raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, + const UnwindLocation &UL) { + auto DumpOpts = DIDumpOptions(); + printUnwindLocation(UL, OS, DumpOpts); + return OS; +} + +void llvm::dwarf::printRegisterLocations(const RegisterLocations &RL, + raw_ostream &OS, + DIDumpOptions DumpOpts) { + bool First = true; + for (uint32_t Reg : RL.getRegisters()) { + auto Loc = *RL.getRegisterLocation(Reg); + if (First) + First = false; + else + OS << ", "; + printRegister(OS, DumpOpts, Reg); + OS << '='; + printUnwindLocation(Loc, OS, DumpOpts); + } +} + +raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, + const RegisterLocations &RL) { + auto DumpOpts = DIDumpOptions(); + printRegisterLocations(RL, OS, DumpOpts); + return OS; +} + +void llvm::dwarf::printUnwindRow(const UnwindRow &Row, raw_ostream &OS, + DIDumpOptions DumpOpts, unsigned IndentLevel) { + OS.indent(2 * IndentLevel); + if (Row.hasAddress()) + OS << format("0x%" PRIx64 ": ", Row.getAddress()); + OS << "CFA="; + printUnwindLocation(Row.getCFAValue(), OS, DumpOpts); + if (Row.getRegisterLocations().hasLocations()) { + OS << ": "; + printRegisterLocations(Row.getRegisterLocations(), OS, DumpOpts); + } + OS << "\n"; +} + +raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) { + auto DumpOpts = DIDumpOptions(); + printUnwindRow(Row, OS, DumpOpts, 0); + return OS; +} + +void llvm::dwarf::printUnwindTable(const UnwindTable &Rows, raw_ostream &OS, + DIDumpOptions DumpOpts, + unsigned IndentLevel) { + for (const UnwindRow &Row : Rows) + printUnwindRow(Row, OS, DumpOpts, IndentLevel); +} + +raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) { + auto DumpOpts = DIDumpOptions(); + printUnwindTable(Rows, OS, DumpOpts, 0); + return OS; +} diff --git a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp index 6e57d3344b95e..bcf89bf58ea71 100644 --- a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp @@ -7,11 +7,8 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h" -#include "llvm/DebugInfo/DIContext.h" -#include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -21,18 +18,6 @@ using namespace llvm; using namespace dwarf; -static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned RegNum) { - if (DumpOpts.GetNameForDWARFReg) { - auto RegName = DumpOpts.GetNameForDWARFReg(RegNum, DumpOpts.IsEH); - if (!RegName.empty()) { - OS << RegName; - return; - } - } - OS << "reg" << RegNum; -} - UnwindLocation UnwindLocation::createUnspecified() { return {Unspecified}; } UnwindLocation UnwindLocation::createUndefined() { return {Undefined}; } @@ -71,57 +56,6 @@ UnwindLocation UnwindLocation::createAtDWARFExpression(DWARFExpression Expr) { return {Expr, true}; } -void UnwindLocation::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { - if (Dereference) - OS << '['; - switch (Kind) { - case Unspecified: - OS << "unspecified"; - break; - case Undefined: - OS << "undefined"; - break; - case Same: - OS << "same"; - break; - case CFAPlusOffset: - OS << "CFA"; - if (Offset == 0) - break; - if (Offset > 0) - OS << "+"; - OS << Offset; - break; - case RegPlusOffset: - printRegister(OS, DumpOpts, RegNum); - if (Offset == 0 && !AddrSpace) - break; - if (Offset >= 0) - OS << "+"; - OS << Offset; - if (AddrSpace) - OS << " in addrspace" << *AddrSpace; - break; - case DWARFExpr: { - if (Expr) - printDwarfExpression(&(*Expr), OS, DumpOpts, nullptr); - break; - } - case Constant: - OS << Offset; - break; - } - if (Dereference) - OS << ']'; -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, - const UnwindLocation &UL) { - auto DumpOpts = DIDumpOptions(); - UL.dump(OS, DumpOpts); - return OS; -} - bool UnwindLocation::operator==(const UnwindLocation &RHS) const { if (Kind != RHS.Kind) return false; @@ -143,58 +77,6 @@ bool UnwindLocation::operator==(const UnwindLocation &RHS) const { return false; } -void RegisterLocations::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { - bool First = true; - for (const auto &RegLocPair : Locations) { - if (First) - First = false; - else - OS << ", "; - printRegister(OS, DumpOpts, RegLocPair.first); - OS << '='; - RegLocPair.second.dump(OS, DumpOpts); - } -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, - const RegisterLocations &RL) { - auto DumpOpts = DIDumpOptions(); - RL.dump(OS, DumpOpts); - return OS; -} - -void UnwindRow::dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel) const { - OS.indent(2 * IndentLevel); - if (hasAddress()) - OS << format("0x%" PRIx64 ": ", *Address); - OS << "CFA="; - CFAValue.dump(OS, DumpOpts); - if (RegLocs.hasLocations()) { - OS << ": "; - RegLocs.dump(OS, DumpOpts); - } - OS << "\n"; -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindRow &Row) { - auto DumpOpts = DIDumpOptions(); - Row.dump(OS, DumpOpts, 0); - return OS; -} - -void UnwindTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts, - unsigned IndentLevel) const { - for (const UnwindRow &Row : Rows) - Row.dump(OS, DumpOpts, IndentLevel); -} - -raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, const UnwindTable &Rows) { - auto DumpOpts = DIDumpOptions(); - Rows.dump(OS, DumpOpts, 0); - return OS; -} - Expected llvm::dwarf::parseRows(const CFIProgram &CFIP, UnwindRow &Row, const RegisterLocations *InitialLocs) { diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp index 2f91b1f86f6dd..d70e13dec6a62 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp @@ -13,6 +13,7 @@ #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" +#include "llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" From 87f41c85ea0a396a3c9f1141eff2ce41785f9181 Mon Sep 17 00:00:00 2001 From: Amirhossein Pashaeehir Date: Thu, 3 Jul 2025 07:06:06 +0000 Subject: [PATCH 14/18] Lint --- llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h index c8cad196cc2b5..703cfd06a00b1 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h +++ b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h @@ -124,7 +124,7 @@ class UnwindLocation { uint32_t getRegister() const { return RegNum; } int32_t getOffset() const { return Offset; } bool hasAddressSpace() const { - if(AddrSpace) + if (AddrSpace) return true; return false; } @@ -182,7 +182,7 @@ class RegisterLocations { SmallVector getRegisters() const { SmallVector Registers; - for(auto &&[Register, _]: Locations) + for (auto &&[Register, _] : Locations) Registers.push_back(Register); return Registers; } From 859bfb6f1beabc2690dd9ca72b673c587eb2b0f4 Mon Sep 17 00:00:00 2001 From: amsen Date: Sat, 5 Jul 2025 10:16:15 -0700 Subject: [PATCH 15/18] Remove unnecessary print functions from library --- .../DebugInfo/DWARF/DWARFUnwindTablePrinter.h | 50 ---------------- .../DWARF/DWARFUnwindTablePrinter.cpp | 57 ++++++++++++++++--- 2 files changed, 50 insertions(+), 57 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h index 8498077ea4bd8..f754e13614eab 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h @@ -18,60 +18,10 @@ struct DIDumpOptions; namespace dwarf { -/// Print an unwind location expression as text and use the register information -/// if some is provided. -/// -/// \param R the unwind location to print. -/// -/// \param OS the stream to use for output. -/// -/// \param MRI register information that helps emit register names insteead -/// of raw register numbers. -/// -/// \param IsEH true if the DWARF Call Frame Information is from .eh_frame -/// instead of from .debug_frame. This is needed for register number -/// conversion because some register numbers differ between the two sections -/// for certain architectures like x86. -LLVM_ABI void printUnwindLocation(const UnwindLocation &R, raw_ostream &OS, - DIDumpOptions DumpOpts); LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindLocation &R); -/// Print all registers + locations that are currently defined in a register -/// locations. -/// -/// \param RL the register locations to print. -/// -/// \param OS the stream to use for output. -/// -/// \param MRI register information that helps emit register names insteead -/// of raw register numbers. -/// -/// \param IsEH true if the DWARF Call Frame Information is from .eh_frame -/// instead of from .debug_frame. This is needed for register number -/// conversion because some register numbers differ between the two sections -/// for certain architectures like x86. -LLVM_ABI void printRegisterLocations(const RegisterLocations &RL, - raw_ostream &OS, DIDumpOptions DumpOpts); LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const RegisterLocations &RL); -/// Print an UnwindRow to the stream. -/// -/// \param Row the UnwindRow to print. -/// -/// \param OS the stream to use for output. -/// -/// \param MRI register information that helps emit register names insteead -/// of raw register numbers. -/// -/// \param IsEH true if the DWARF Call Frame Information is from .eh_frame -/// instead of from .debug_frame. This is needed for register number -/// conversion because some register numbers differ between the two sections -/// for certain architectures like x86. -/// -/// \param IndentLevel specify the indent level as an integer. The UnwindRow -/// will be output to the stream preceded by 2 * IndentLevel number of spaces. -LLVM_ABI void printUnwindRow(const UnwindRow &Row, raw_ostream &OS, - DIDumpOptions DumpOpts, unsigned IndentLevel = 0); LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, const UnwindRow &Row); /// Print a UnwindTable to the stream. diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp index 607c6f70eac52..8248b142bf574 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp @@ -32,8 +32,22 @@ static void printRegister(raw_ostream &OS, DIDumpOptions DumpOpts, OS << "reg" << RegNum; } -void llvm::dwarf::printUnwindLocation(const UnwindLocation &UL, raw_ostream &OS, - DIDumpOptions DumpOpts) { +/// Print an unwind location expression as text and use the register information +/// if some is provided. +/// +/// \param R the unwind location to print. +/// +/// \param OS the stream to use for output. +/// +/// \param MRI register information that helps emit register names insteead +/// of raw register numbers. +/// +/// \param IsEH true if the DWARF Call Frame Information is from .eh_frame +/// instead of from .debug_frame. This is needed for register number +/// conversion because some register numbers differ between the two sections +/// for certain architectures like x86. +static void printUnwindLocation(const UnwindLocation &UL, raw_ostream &OS, + DIDumpOptions DumpOpts) { if (UL.getDereference()) OS << '['; switch (UL.getLocation()) { @@ -86,9 +100,22 @@ raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, return OS; } -void llvm::dwarf::printRegisterLocations(const RegisterLocations &RL, - raw_ostream &OS, - DIDumpOptions DumpOpts) { +/// Print all registers + locations that are currently defined in a register +/// locations. +/// +/// \param RL the register locations to print. +/// +/// \param OS the stream to use for output. +/// +/// \param MRI register information that helps emit register names insteead +/// of raw register numbers. +/// +/// \param IsEH true if the DWARF Call Frame Information is from .eh_frame +/// instead of from .debug_frame. This is needed for register number +/// conversion because some register numbers differ between the two sections +/// for certain architectures like x86. +static void printRegisterLocations(const RegisterLocations &RL, raw_ostream &OS, + DIDumpOptions DumpOpts) { bool First = true; for (uint32_t Reg : RL.getRegisters()) { auto Loc = *RL.getRegisterLocation(Reg); @@ -109,8 +136,24 @@ raw_ostream &llvm::dwarf::operator<<(raw_ostream &OS, return OS; } -void llvm::dwarf::printUnwindRow(const UnwindRow &Row, raw_ostream &OS, - DIDumpOptions DumpOpts, unsigned IndentLevel) { +/// Print an UnwindRow to the stream. +/// +/// \param Row the UnwindRow to print. +/// +/// \param OS the stream to use for output. +/// +/// \param MRI register information that helps emit register names insteead +/// of raw register numbers. +/// +/// \param IsEH true if the DWARF Call Frame Information is from .eh_frame +/// instead of from .debug_frame. This is needed for register number +/// conversion because some register numbers differ between the two sections +/// for certain architectures like x86. +/// +/// \param IndentLevel specify the indent level as an integer. The UnwindRow +/// will be output to the stream preceded by 2 * IndentLevel number of spaces. +static void printUnwindRow(const UnwindRow &Row, raw_ostream &OS, + DIDumpOptions DumpOpts, unsigned IndentLevel) { OS.indent(2 * IndentLevel); if (Row.hasAddress()) OS << format("0x%" PRIx64 ": ", Row.getAddress()); From b7b47cd5eeac324fb758aa6c6fde999d15f86c0f Mon Sep 17 00:00:00 2001 From: amsen Date: Sat, 5 Jul 2025 21:20:31 -0700 Subject: [PATCH 16/18] Fix header guards match folder paths --- .../llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h index 703cfd06a00b1..5874810201607 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h +++ b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H -#define LLVM_DEBUGINFO_DWARF_DWARFUNWINDTABLE_H +#ifndef LLVM_DEBUGINFO_DWARF_LOWLEVEL_DWARFUNWINDTABLE_H +#define LLVM_DEBUGINFO_DWARF_LOWLEVEL_DWARFUNWINDTABLE_H #include "llvm/ADT/SmallVector.h" #include "llvm/DebugInfo/DWARF/LowLevel/DWARFCFIProgram.h" @@ -318,4 +318,4 @@ parseRows(const CFIProgram &CFIP, UnwindRow &CurrRow, } // end namespace llvm -#endif +#endif // LLVM_DEBUGINFO_DWARF_LOWLEVEL_DWARFUNWINDTABLE_H From 55e98e61d02f3abf84c1265e667d57ee2556d533 Mon Sep 17 00:00:00 2001 From: amsen Date: Sun, 6 Jul 2025 16:41:18 -0700 Subject: [PATCH 17/18] Sync new file header with new style --- llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h | 2 +- llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h index f754e13614eab..242eceeed604a 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnwindTablePrinter.h @@ -1,4 +1,4 @@ -//===- DWARFUnwindTablePrinter.h --------------------------------*- C++ -*-===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h index 5874810201607..828dea12d168c 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h +++ b/llvm/include/llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h @@ -1,4 +1,4 @@ -//===- DWARFUnwindTable.h ----------------------------------------*- C++-*-===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. From 272d28b2dbf504cc6440e8b7b6dd24c7366d14c4 Mon Sep 17 00:00:00 2001 From: amsen Date: Sun, 6 Jul 2025 17:43:59 -0700 Subject: [PATCH 18/18] Match starting docs for cpp files with new style guide --- llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp | 2 +- llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp index 8248b142bf574..a88f4a554bcf0 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnwindTablePrinter.cpp @@ -1,4 +1,4 @@ -//=== DWARFUnwindTablePrinter.cpp - Print the cfi-portions of .debug_frame ===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp index bcf89bf58ea71..20f52673eefe1 100644 --- a/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.cpp @@ -1,4 +1,4 @@ -//=== DWARFUnwindTable.cpp - Parsing CFI instructions into unwinding table ===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information.