| 1 | //===-- TestDWARFCallFrameInfo.cpp ----------------------------------------===// |
| 2 | // |
| 3 | // |
| 4 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 5 | // See https://llvm.org/LICENSE.txt for license information. |
| 6 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | |
| 10 | #include "gmock/gmock.h" |
| 11 | #include "gtest/gtest.h" |
| 12 | |
| 13 | #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" |
| 14 | #include "Plugins/Process/Utility/RegisterContext_x86.h" |
| 15 | #include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h" |
| 16 | #include "TestingSupport/SubsystemRAII.h" |
| 17 | #include "TestingSupport/TestUtilities.h" |
| 18 | |
| 19 | #include "lldb/Core/Module.h" |
| 20 | #include "lldb/Core/ModuleSpec.h" |
| 21 | #include "lldb/Core/Section.h" |
| 22 | #include "lldb/Host/FileSystem.h" |
| 23 | #include "lldb/Host/HostInfo.h" |
| 24 | #include "lldb/Symbol/DWARFCallFrameInfo.h" |
| 25 | #include "lldb/Utility/StreamString.h" |
| 26 | #include "llvm/Testing/Support/Error.h" |
| 27 | |
| 28 | #include "llvm/Support/FileUtilities.h" |
| 29 | #include "llvm/Support/Path.h" |
| 30 | #include "llvm/Support/Program.h" |
| 31 | #include "llvm/Support/raw_ostream.h" |
| 32 | |
| 33 | using namespace lldb_private; |
| 34 | using namespace lldb; |
| 35 | |
| 36 | class DWARFCallFrameInfoTest : public testing::Test { |
| 37 | SubsystemRAII<FileSystem, HostInfo, ObjectFileELF, SymbolFileSymtab> |
| 38 | subsystems; |
| 39 | |
| 40 | protected: |
| 41 | void TestBasic(DWARFCallFrameInfo::Type type, llvm::StringRef symbol); |
| 42 | }; |
| 43 | |
| 44 | namespace lldb_private { |
| 45 | static std::ostream &operator<<(std::ostream &OS, const UnwindPlan::Row &row) { |
| 46 | StreamString SS; |
| 47 | row.Dump(s&: SS, unwind_plan: nullptr, thread: nullptr, base_addr: 0); |
| 48 | return OS << SS.GetData(); |
| 49 | } |
| 50 | } // namespace lldb_private |
| 51 | |
| 52 | static UnwindPlan::Row GetExpectedRow0() { |
| 53 | UnwindPlan::Row row; |
| 54 | row.SetOffset(0); |
| 55 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: dwarf_rsp_x86_64, offset: 8); |
| 56 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: dwarf_rip_x86_64, offset: -8, can_replace: false); |
| 57 | return row; |
| 58 | } |
| 59 | |
| 60 | static UnwindPlan::Row GetExpectedRow1() { |
| 61 | UnwindPlan::Row row; |
| 62 | row.SetOffset(1); |
| 63 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: dwarf_rsp_x86_64, offset: 16); |
| 64 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: dwarf_rip_x86_64, offset: -8, can_replace: false); |
| 65 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: dwarf_rbp_x86_64, offset: -16, can_replace: false); |
| 66 | return row; |
| 67 | } |
| 68 | |
| 69 | static UnwindPlan::Row GetExpectedRow2() { |
| 70 | UnwindPlan::Row row; |
| 71 | row.SetOffset(4); |
| 72 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: dwarf_rbp_x86_64, offset: 16); |
| 73 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: dwarf_rip_x86_64, offset: -8, can_replace: false); |
| 74 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: dwarf_rbp_x86_64, offset: -16, can_replace: false); |
| 75 | return row; |
| 76 | } |
| 77 | |
| 78 | void DWARFCallFrameInfoTest::TestBasic(DWARFCallFrameInfo::Type type, |
| 79 | llvm::StringRef symbol) { |
| 80 | auto ExpectedFile = TestFile::fromYaml(Yaml: R"( |
| 81 | --- !ELF |
| 82 | FileHeader: |
| 83 | Class: ELFCLASS64 |
| 84 | Data: ELFDATA2LSB |
| 85 | Type: ET_DYN |
| 86 | Machine: EM_X86_64 |
| 87 | Entry: 0x0000000000000260 |
| 88 | Sections: |
| 89 | - Name: .text |
| 90 | Type: SHT_PROGBITS |
| 91 | Flags: [ SHF_ALLOC, SHF_EXECINSTR ] |
| 92 | Address: 0x0000000000000260 |
| 93 | AddressAlign: 0x0000000000000010 |
| 94 | Content: 554889E5897DFC8B45FC5DC30F1F4000554889E5897DFC8B45FC5DC30F1F4000554889E5897DFC8B45FC5DC3 |
| 95 | #0000000000000260 <eh_frame>: |
| 96 | # 260: 55 push %rbp |
| 97 | # 261: 48 89 e5 mov %rsp,%rbp |
| 98 | # 264: 89 7d fc mov %edi,-0x4(%rbp) |
| 99 | # 267: 8b 45 fc mov -0x4(%rbp),%eax |
| 100 | # 26a: 5d pop %rbp |
| 101 | # 26b: c3 retq |
| 102 | # 26c: 0f 1f 40 00 nopl 0x0(%rax) |
| 103 | # |
| 104 | #0000000000000270 <debug_frame3>: |
| 105 | # 270: 55 push %rbp |
| 106 | # 271: 48 89 e5 mov %rsp,%rbp |
| 107 | # 274: 89 7d fc mov %edi,-0x4(%rbp) |
| 108 | # 277: 8b 45 fc mov -0x4(%rbp),%eax |
| 109 | # 27a: 5d pop %rbp |
| 110 | # 27b: c3 retq |
| 111 | # 27c: 0f 1f 40 00 nopl 0x0(%rax) |
| 112 | # |
| 113 | #0000000000000280 <debug_frame4>: |
| 114 | # 280: 55 push %rbp |
| 115 | # 281: 48 89 e5 mov %rsp,%rbp |
| 116 | # 284: 89 7d fc mov %edi,-0x4(%rbp) |
| 117 | # 287: 8b 45 fc mov -0x4(%rbp),%eax |
| 118 | # 28a: 5d pop %rbp |
| 119 | # 28b: c3 retq |
| 120 | - Name: .eh_frame |
| 121 | Type: SHT_X86_64_UNWIND |
| 122 | Flags: [ SHF_ALLOC ] |
| 123 | Address: 0x0000000000000290 |
| 124 | AddressAlign: 0x0000000000000008 |
| 125 | Content: 1400000000000000017A5200017810011B0C0708900100001C0000001C000000B0FFFFFF0C00000000410E108602430D0600000000000000 |
| 126 | #00000000 0000000000000014 00000000 CIE |
| 127 | # Version: 1 |
| 128 | # Augmentation: "zR" |
| 129 | # Code alignment factor: 1 |
| 130 | # Data alignment factor: -8 |
| 131 | # Return address column: 16 |
| 132 | # Augmentation data: 1b |
| 133 | # |
| 134 | # DW_CFA_def_cfa: r7 (rsp) ofs 8 |
| 135 | # DW_CFA_offset: r16 (rip) at cfa-8 |
| 136 | # DW_CFA_nop |
| 137 | # DW_CFA_nop |
| 138 | # |
| 139 | #00000018 000000000000001c 0000001c FDE cie=00000000 pc=ffffffffffffffd0..ffffffffffffffdc |
| 140 | # DW_CFA_advance_loc: 1 to ffffffffffffffd1 |
| 141 | # DW_CFA_def_cfa_offset: 16 |
| 142 | # DW_CFA_offset: r6 (rbp) at cfa-16 |
| 143 | # DW_CFA_advance_loc: 3 to ffffffffffffffd4 |
| 144 | # DW_CFA_def_cfa_register: r6 (rbp) |
| 145 | # DW_CFA_nop |
| 146 | # DW_CFA_nop |
| 147 | # DW_CFA_nop |
| 148 | # DW_CFA_nop |
| 149 | # DW_CFA_nop |
| 150 | # DW_CFA_nop |
| 151 | # DW_CFA_nop |
| 152 | - Name: .debug_frame |
| 153 | Type: SHT_PROGBITS |
| 154 | AddressAlign: 0x0000000000000008 |
| 155 | Content: 14000000FFFFFFFF03000178100C070890010000000000001C0000000000000070020000000000000C00000000000000410E108602430D0614000000FFFFFFFF040008000178100C07089001000000001C0000003800000080020000000000000C00000000000000410E108602430D06 |
| 156 | #00000000 0000000000000014 ffffffff CIE |
| 157 | # Version: 3 |
| 158 | # Augmentation: "" |
| 159 | # Code alignment factor: 1 |
| 160 | # Data alignment factor: -8 |
| 161 | # Return address column: 16 |
| 162 | # |
| 163 | # DW_CFA_def_cfa: r7 (rsp) ofs 8 |
| 164 | # DW_CFA_offset: r16 (rip) at cfa-8 |
| 165 | # DW_CFA_nop |
| 166 | # DW_CFA_nop |
| 167 | # DW_CFA_nop |
| 168 | # DW_CFA_nop |
| 169 | # DW_CFA_nop |
| 170 | # DW_CFA_nop |
| 171 | # |
| 172 | #00000018 000000000000001c 00000000 FDE cie=00000000 pc=0000000000000270..000000000000027c |
| 173 | # DW_CFA_advance_loc: 1 to 0000000000000271 |
| 174 | # DW_CFA_def_cfa_offset: 16 |
| 175 | # DW_CFA_offset: r6 (rbp) at cfa-16 |
| 176 | # DW_CFA_advance_loc: 3 to 0000000000000274 |
| 177 | # DW_CFA_def_cfa_register: r6 (rbp) |
| 178 | # |
| 179 | #00000038 0000000000000014 ffffffff CIE |
| 180 | # Version: 4 |
| 181 | # Augmentation: "" |
| 182 | # Pointer Size: 8 |
| 183 | # Segment Size: 0 |
| 184 | # Code alignment factor: 1 |
| 185 | # Data alignment factor: -8 |
| 186 | # Return address column: 16 |
| 187 | # |
| 188 | # DW_CFA_def_cfa: r7 (rsp) ofs 8 |
| 189 | # DW_CFA_offset: r16 (rip) at cfa-8 |
| 190 | # DW_CFA_nop |
| 191 | # DW_CFA_nop |
| 192 | # DW_CFA_nop |
| 193 | # DW_CFA_nop |
| 194 | # |
| 195 | #00000050 000000000000001c 00000038 FDE cie=00000038 pc=0000000000000280..000000000000028c |
| 196 | # DW_CFA_advance_loc: 1 to 0000000000000281 |
| 197 | # DW_CFA_def_cfa_offset: 16 |
| 198 | # DW_CFA_offset: r6 (rbp) at cfa-16 |
| 199 | # DW_CFA_advance_loc: 3 to 0000000000000284 |
| 200 | # DW_CFA_def_cfa_register: r6 (rbp) |
| 201 | Symbols: |
| 202 | - Name: eh_frame |
| 203 | Type: STT_FUNC |
| 204 | Section: .text |
| 205 | Value: 0x0000000000000260 |
| 206 | Size: 0x000000000000000C |
| 207 | Binding: STB_GLOBAL |
| 208 | - Name: debug_frame3 |
| 209 | Type: STT_FUNC |
| 210 | Section: .text |
| 211 | Value: 0x0000000000000270 |
| 212 | Size: 0x000000000000000C |
| 213 | Binding: STB_GLOBAL |
| 214 | - Name: debug_frame4 |
| 215 | Type: STT_FUNC |
| 216 | Section: .text |
| 217 | Value: 0x0000000000000280 |
| 218 | Size: 0x000000000000000C |
| 219 | Binding: STB_GLOBAL |
| 220 | ... |
| 221 | )" ); |
| 222 | ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); |
| 223 | |
| 224 | auto module_sp = std::make_shared<Module>(args: ExpectedFile->moduleSpec()); |
| 225 | SectionList *list = module_sp->GetSectionList(); |
| 226 | ASSERT_NE(nullptr, list); |
| 227 | |
| 228 | auto section_sp = list->FindSectionByType(sect_type: type == DWARFCallFrameInfo::EH |
| 229 | ? eSectionTypeEHFrame |
| 230 | : eSectionTypeDWARFDebugFrame, |
| 231 | check_children: false); |
| 232 | ASSERT_NE(nullptr, section_sp); |
| 233 | |
| 234 | DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp, type); |
| 235 | |
| 236 | const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType( |
| 237 | name: ConstString(symbol), symbol_type: eSymbolTypeAny); |
| 238 | ASSERT_NE(nullptr, sym); |
| 239 | |
| 240 | std::unique_ptr<UnwindPlan> plan_up = cfi.GetUnwindPlan(addr: sym->GetAddress()); |
| 241 | ASSERT_TRUE(plan_up); |
| 242 | ASSERT_EQ(3, plan_up->GetRowCount()); |
| 243 | EXPECT_THAT(plan_up->GetRowAtIndex(0), testing::Pointee(GetExpectedRow0())); |
| 244 | EXPECT_THAT(plan_up->GetRowAtIndex(1), testing::Pointee(GetExpectedRow1())); |
| 245 | EXPECT_THAT(plan_up->GetRowAtIndex(2), testing::Pointee(GetExpectedRow2())); |
| 246 | } |
| 247 | |
| 248 | TEST_F(DWARFCallFrameInfoTest, Basic_dwarf3) { |
| 249 | TestBasic(type: DWARFCallFrameInfo::DWARF, symbol: "debug_frame3" ); |
| 250 | } |
| 251 | |
| 252 | TEST_F(DWARFCallFrameInfoTest, Basic_dwarf4) { |
| 253 | TestBasic(type: DWARFCallFrameInfo::DWARF, symbol: "debug_frame4" ); |
| 254 | } |
| 255 | |
| 256 | TEST_F(DWARFCallFrameInfoTest, Basic_eh) { |
| 257 | TestBasic(type: DWARFCallFrameInfo::EH, symbol: "eh_frame" ); |
| 258 | } |
| 259 | |