disassembler_objdump_unittest.cc
1 // Copyright (c) 2022, Google LLC 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google LLC nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 #ifdef HAVE_CONFIG_H 30 #include <config.h> // Must come first 31 #endif 32 33 #include <unistd.h> 34 #include <vector> 35 36 #include "breakpad_googletest_includes.h" 37 38 #include "google_breakpad/common/breakpad_types.h" 39 #include "google_breakpad/common/minidump_cpu_amd64.h" 40 #include "google_breakpad/common/minidump_cpu_x86.h" 41 #include "google_breakpad/processor/dump_context.h" 42 #include "google_breakpad/processor/memory_region.h" 43 #include "processor/disassembler_objdump.h" 44 45 namespace google_breakpad { 46 class DisassemblerObjdumpForTest : public DisassemblerObjdump { 47 public: 48 using DisassemblerObjdump::CalculateAddress; 49 using DisassemblerObjdump::DisassembleInstruction; 50 using DisassemblerObjdump::TokenizeInstruction; 51 }; 52 53 class TestMemoryRegion : public MemoryRegion { 54 public: 55 TestMemoryRegion(uint64_t base, std::vector<uint8_t> bytes); 56 ~TestMemoryRegion() override = default; 57 58 uint64_t GetBase() const override; 59 uint32_t GetSize() const override; 60 61 bool GetMemoryAtAddress(uint64_t address, uint8_t* value) const override; 62 bool GetMemoryAtAddress(uint64_t address, uint16_t* value) const override; 63 bool GetMemoryAtAddress(uint64_t address, uint32_t* value) const override; 64 bool GetMemoryAtAddress(uint64_t address, uint64_t* value) const override; 65 66 void Print() const override; 67 68 private: 69 uint64_t base_; 70 std::vector<uint8_t> bytes_; 71 }; 72 73 TestMemoryRegion::TestMemoryRegion(uint64_t address, std::vector<uint8_t> bytes) 74 : base_(address), bytes_(bytes) {} 75 76 uint64_t TestMemoryRegion::GetBase() const { 77 return base_; 78 } 79 80 uint32_t TestMemoryRegion::GetSize() const { 81 return static_cast<uint32_t>(bytes_.size()); 82 } 83 84 bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address, 85 uint8_t* value) const { 86 if (address < GetBase() || 87 address + sizeof(uint8_t) > GetBase() + GetSize()) { 88 return false; 89 } 90 91 memcpy(value, &bytes_[address - GetBase()], sizeof(uint8_t)); 92 return true; 93 } 94 95 // We don't use the following functions, so no need to implement. 96 bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address, 97 uint16_t* value) const { 98 return false; 99 } 100 101 bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address, 102 uint32_t* value) const { 103 return false; 104 } 105 106 bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address, 107 uint64_t* value) const { 108 return false; 109 } 110 111 void TestMemoryRegion::Print() const {} 112 113 const uint32_t kX86TestDs = 0x01000000; 114 const uint32_t kX86TestEs = 0x02000000; 115 const uint32_t kX86TestFs = 0x03000000; 116 const uint32_t kX86TestGs = 0x04000000; 117 const uint32_t kX86TestEax = 0x00010101; 118 const uint32_t kX86TestEbx = 0x00020202; 119 const uint32_t kX86TestEcx = 0x00030303; 120 const uint32_t kX86TestEdx = 0x00040404; 121 const uint32_t kX86TestEsi = 0x00050505; 122 const uint32_t kX86TestEdi = 0x00060606; 123 const uint32_t kX86TestEsp = 0x00070707; 124 const uint32_t kX86TestEbp = 0x00080808; 125 const uint32_t kX86TestEip = 0x23230000; 126 127 const uint64_t kAMD64TestRax = 0x0000010101010101ul; 128 const uint64_t kAMD64TestRbx = 0x0000020202020202ul; 129 const uint64_t kAMD64TestRcx = 0x0000030303030303ul; 130 const uint64_t kAMD64TestRdx = 0x0000040404040404ul; 131 const uint64_t kAMD64TestRsi = 0x0000050505050505ul; 132 const uint64_t kAMD64TestRdi = 0x0000060606060606ul; 133 const uint64_t kAMD64TestRsp = 0x0000070707070707ul; 134 const uint64_t kAMD64TestRbp = 0x0000080808080808ul; 135 const uint64_t kAMD64TestR8 = 0x0000090909090909ul; 136 const uint64_t kAMD64TestR9 = 0x00000a0a0a0a0a0aul; 137 const uint64_t kAMD64TestR10 = 0x00000b0b0b0b0b0bul; 138 const uint64_t kAMD64TestR11 = 0x00000c0c0c0c0c0cul; 139 const uint64_t kAMD64TestR12 = 0x00000d0d0d0d0d0dul; 140 const uint64_t kAMD64TestR13 = 0x00000e0e0e0e0e0eul; 141 const uint64_t kAMD64TestR14 = 0x00000f0f0f0f0f0ful; 142 const uint64_t kAMD64TestR15 = 0x0000001010101010ul; 143 const uint64_t kAMD64TestRip = 0x0000000023230000ul; 144 145 class TestDumpContext : public DumpContext { 146 public: 147 TestDumpContext(bool x86_64 = false); 148 ~TestDumpContext() override; 149 }; 150 151 TestDumpContext::TestDumpContext(bool x86_64) { 152 if (!x86_64) { 153 MDRawContextX86* raw_context = new MDRawContextX86(); 154 memset(raw_context, 0, sizeof(*raw_context)); 155 156 raw_context->context_flags = MD_CONTEXT_X86_FULL; 157 158 raw_context->ds = kX86TestDs; 159 raw_context->es = kX86TestEs; 160 raw_context->fs = kX86TestFs; 161 raw_context->gs = kX86TestGs; 162 raw_context->eax = kX86TestEax; 163 raw_context->ebx = kX86TestEbx; 164 raw_context->ecx = kX86TestEcx; 165 raw_context->edx = kX86TestEdx; 166 raw_context->esi = kX86TestEsi; 167 raw_context->edi = kX86TestEdi; 168 raw_context->esp = kX86TestEsp; 169 raw_context->ebp = kX86TestEbp; 170 raw_context->eip = kX86TestEip; 171 172 SetContextFlags(raw_context->context_flags); 173 SetContextX86(raw_context); 174 this->valid_ = true; 175 } else { 176 MDRawContextAMD64* raw_context = new MDRawContextAMD64(); 177 memset(raw_context, 0, sizeof(*raw_context)); 178 179 raw_context->context_flags = MD_CONTEXT_AMD64_FULL; 180 181 raw_context->rax = kAMD64TestRax; 182 raw_context->rbx = kAMD64TestRbx; 183 raw_context->rcx = kAMD64TestRcx; 184 raw_context->rdx = kAMD64TestRdx; 185 raw_context->rsi = kAMD64TestRsi; 186 raw_context->rdi = kAMD64TestRdi; 187 raw_context->rsp = kAMD64TestRsp; 188 raw_context->rbp = kAMD64TestRbp; 189 raw_context->r8 = kAMD64TestR8; 190 raw_context->r9 = kAMD64TestR9; 191 raw_context->r10 = kAMD64TestR10; 192 raw_context->r11 = kAMD64TestR11; 193 raw_context->r12 = kAMD64TestR12; 194 raw_context->r13 = kAMD64TestR13; 195 raw_context->r14 = kAMD64TestR14; 196 raw_context->r15 = kAMD64TestR15; 197 raw_context->rip = kAMD64TestRip; 198 199 SetContextFlags(raw_context->context_flags); 200 SetContextAMD64(raw_context); 201 this->valid_ = true; 202 } 203 } 204 205 TestDumpContext::~TestDumpContext() { 206 FreeContext(); 207 } 208 209 TEST(DisassemblerObjdumpTest, DisassembleInstructionX86) { 210 string instruction; 211 ASSERT_FALSE(DisassemblerObjdumpForTest::DisassembleInstruction( 212 MD_CONTEXT_X86, nullptr, 0, instruction)); 213 std::vector<uint8_t> pop_eax = {0x58}; 214 ASSERT_TRUE(DisassemblerObjdumpForTest::DisassembleInstruction( 215 MD_CONTEXT_X86, pop_eax.data(), pop_eax.size(), instruction)); 216 ASSERT_EQ(instruction, "pop eax"); 217 } 218 219 TEST(DisassemblerObjdumpTest, DisassembleInstructionAMD64) { 220 string instruction; 221 ASSERT_FALSE(DisassemblerObjdumpForTest::DisassembleInstruction( 222 MD_CONTEXT_AMD64, nullptr, 0, instruction)); 223 std::vector<uint8_t> pop_rax = {0x58}; 224 ASSERT_TRUE(DisassemblerObjdumpForTest::DisassembleInstruction( 225 MD_CONTEXT_AMD64, pop_rax.data(), pop_rax.size(), instruction)); 226 ASSERT_EQ(instruction, "pop rax"); 227 } 228 229 TEST(DisassemblerObjdumpTest, TokenizeInstruction) { 230 string operation, dest, src; 231 ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction( 232 "pop eax", operation, dest, src)); 233 ASSERT_EQ(operation, "pop"); 234 ASSERT_EQ(dest, "eax"); 235 236 ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction( 237 "mov eax, ebx", operation, dest, src)); 238 ASSERT_EQ(operation, "mov"); 239 ASSERT_EQ(dest, "eax"); 240 ASSERT_EQ(src, "ebx"); 241 242 ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction( 243 "pop rax", operation, dest, src)); 244 ASSERT_EQ(operation, "pop"); 245 ASSERT_EQ(dest, "rax"); 246 247 ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction( 248 "mov rax, rbx", operation, dest, src)); 249 ASSERT_EQ(operation, "mov"); 250 ASSERT_EQ(dest, "rax"); 251 ASSERT_EQ(src, "rbx"); 252 253 // Test the three parsing failure paths 254 ASSERT_FALSE(DisassemblerObjdumpForTest::TokenizeInstruction( 255 "mov rax,", operation, dest, src)); 256 ASSERT_FALSE(DisassemblerObjdumpForTest::TokenizeInstruction( 257 "mov rax rbx", operation, dest, src)); 258 ASSERT_FALSE(DisassemblerObjdumpForTest::TokenizeInstruction( 259 "mov rax, rbx, rcx", operation, dest, src)); 260 261 // This is of course a nonsense instruction, but test that we do remove 262 // multiple instruction prefixes and can handle multiple memory operands. 263 ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction( 264 "rep lock mov DWORD PTR rax, QWORD PTR rbx", operation, dest, src)); 265 ASSERT_EQ(operation, "mov"); 266 ASSERT_EQ(dest, "rax"); 267 ASSERT_EQ(src, "rbx"); 268 269 // Test that we ignore junk following a valid instruction 270 ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction( 271 "mov rax, rbx ; junk here", operation, dest, src)); 272 ASSERT_EQ(operation, "mov"); 273 ASSERT_EQ(dest, "rax"); 274 ASSERT_EQ(src, "rbx"); 275 } 276 277 namespace x86 { 278 const TestMemoryRegion load_reg(kX86TestEip, {0x8b, 0x06}); // mov eax, [esi]; 279 280 const TestMemoryRegion load_reg_index(kX86TestEip, 281 {0x8b, 0x04, 282 0xbe}); // mov eax, [esi+edi*4]; 283 284 const TestMemoryRegion load_reg_offset(kX86TestEip, 285 {0x8b, 0x46, 286 0x10}); // mov eax, [esi+0x10]; 287 288 const TestMemoryRegion load_reg_index_offset( 289 kX86TestEip, 290 {0x8b, 0x44, 0xbe, 0xf0}); // mov eax, [esi+edi*4-0x10]; 291 292 const TestMemoryRegion rep_stosb(kX86TestEip, {0xf3, 0xaa}); // rep stosb; 293 294 const TestMemoryRegion lock_cmpxchg(kX86TestEip, 295 {0xf0, 0x0f, 0xb1, 0x46, 296 0x10}); // lock cmpxchg [esi + 0x10], eax; 297 298 const TestMemoryRegion call_reg_offset(kX86TestEip, 299 {0xff, 0x96, 0x99, 0x99, 0x99, 300 0x09}); // call [esi+0x9999999]; 301 } // namespace x86 302 303 TEST(DisassemblerObjdumpTest, X86LoadReg) { 304 TestDumpContext context; 305 DisassemblerObjdump dis(context.GetContextCPU(), &x86::load_reg, kX86TestEip); 306 uint64_t src_address = 0, dest_address = 0; 307 ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address)); 308 ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address)); 309 ASSERT_EQ(src_address, kX86TestEsi); 310 } 311 312 TEST(DisassemblerObjdumpTest, X86LoadRegIndex) { 313 TestDumpContext context; 314 DisassemblerObjdump dis(context.GetContextCPU(), &x86::load_reg_index, 315 kX86TestEip); 316 uint64_t src_address = 0, dest_address = 0; 317 ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address)); 318 ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address)); 319 ASSERT_EQ(src_address, kX86TestEsi + (kX86TestEdi * 4)); 320 } 321 322 TEST(DisassemblerObjdumpTest, X86LoadRegOffset) { 323 TestDumpContext context; 324 DisassemblerObjdump dis(context.GetContextCPU(), &x86::load_reg_offset, 325 kX86TestEip); 326 uint64_t src_address = 0, dest_address = 0; 327 ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address)); 328 ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address)); 329 ASSERT_EQ(src_address, kX86TestEsi + 0x10); 330 } 331 332 TEST(DisassemblerObjdumpTest, X86LoadRegIndexOffset) { 333 TestDumpContext context; 334 DisassemblerObjdump dis(context.GetContextCPU(), &x86::load_reg_index_offset, 335 kX86TestEip); 336 uint64_t src_address = 0, dest_address = 0; 337 ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address)); 338 ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address)); 339 ASSERT_EQ(src_address, kX86TestEsi + (kX86TestEdi * 4) - 0x10); 340 } 341 342 TEST(DisassemblerObjdumpTest, X86RepStosb) { 343 TestDumpContext context; 344 DisassemblerObjdump dis(context.GetContextCPU(), &x86::rep_stosb, 345 kX86TestEip); 346 uint64_t src_address = 0, dest_address = 0; 347 ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address)); 348 ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address)); 349 ASSERT_EQ(dest_address, kX86TestEs + kX86TestEdi); 350 } 351 352 TEST(DisassemblerObjdumpTest, X86LockCmpxchg) { 353 TestDumpContext context; 354 DisassemblerObjdump dis(context.GetContextCPU(), &x86::lock_cmpxchg, 355 kX86TestEip); 356 uint64_t src_address = 0, dest_address = 0; 357 ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address)); 358 ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address)); 359 ASSERT_EQ(dest_address, kX86TestEsi + 0x10); 360 } 361 362 TEST(DisassemblerObjdumpTest, X86CallRegOffset) { 363 TestDumpContext context; 364 DisassemblerObjdump dis(context.GetContextCPU(), &x86::call_reg_offset, 365 kX86TestEip); 366 uint64_t src_address = 0, dest_address = 0; 367 ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address)); 368 ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address)); 369 ASSERT_EQ(dest_address, kX86TestEsi + 0x9999999); 370 } 371 372 namespace amd64 { 373 const TestMemoryRegion load_reg(kAMD64TestRip, 374 {0x48, 0x8b, 0x06}); // mov rax, [rsi]; 375 376 const TestMemoryRegion load_reg_index(kAMD64TestRip, 377 {0x48, 0x8b, 0x04, 378 0xbe}); // mov rax, [rsi+rdi*4]; 379 380 const TestMemoryRegion load_rip_relative(kAMD64TestRip, 381 {0x48, 0x8b, 0x05, 0x10, 0x00, 0x00, 382 0x00}); // mov rax, [rip+0x10]; 383 384 const TestMemoryRegion load_reg_index_offset( 385 kAMD64TestRip, 386 {0x48, 0x8b, 0x44, 0xbe, 0xf0}); // mov rax, [rsi+rdi*4-0x10]; 387 388 const TestMemoryRegion rep_stosb(kAMD64TestRip, {0xf3, 0xaa}); // rep stosb; 389 390 const TestMemoryRegion lock_cmpxchg(kAMD64TestRip, 391 {0xf0, 0x48, 0x0f, 0xb1, 0x46, 392 0x10}); // lock cmpxchg [rsi + 0x10], rax; 393 394 const TestMemoryRegion call_reg_offset(kAMD64TestRip, 395 {0xff, 0x96, 0x99, 0x99, 0x99, 396 0x09}); // call [rsi+0x9999999]; 397 } // namespace amd64 398 399 TEST(DisassemblerObjdumpTest, AMD64LoadReg) { 400 TestDumpContext context(true); 401 DisassemblerObjdump dis(context.GetContextCPU(), &amd64::load_reg, 402 kAMD64TestRip); 403 uint64_t src_address = 0, dest_address = 0; 404 ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address)); 405 ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address)); 406 ASSERT_EQ(src_address, kAMD64TestRsi); 407 } 408 409 TEST(DisassemblerObjdumpTest, AMD64LoadRegIndex) { 410 TestDumpContext context(true); 411 DisassemblerObjdump dis(context.GetContextCPU(), &amd64::load_reg_index, 412 kAMD64TestRip); 413 uint64_t src_address = 0, dest_address = 0; 414 ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address)); 415 ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address)); 416 ASSERT_EQ(src_address, kAMD64TestRsi + (kAMD64TestRdi * 4)); 417 } 418 419 TEST(DisassemblerObjdumpTest, AMD64LoadRipRelative) { 420 TestDumpContext context(true); 421 DisassemblerObjdump dis(context.GetContextCPU(), &amd64::load_rip_relative, 422 kAMD64TestRip); 423 uint64_t src_address = 0, dest_address = 0; 424 ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address)); 425 ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address)); 426 ASSERT_EQ(src_address, kAMD64TestRip + 0x10); 427 } 428 429 TEST(DisassemblerObjdumpTest, AMD64LoadRegIndexOffset) { 430 TestDumpContext context(true); 431 DisassemblerObjdump dis(context.GetContextCPU(), 432 &amd64::load_reg_index_offset, kAMD64TestRip); 433 uint64_t src_address = 0, dest_address = 0; 434 ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address)); 435 ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address)); 436 ASSERT_EQ(src_address, kAMD64TestRsi + (kAMD64TestRdi * 4) - 0x10); 437 } 438 439 TEST(DisassemblerObjdumpTest, AMD64RepStosb) { 440 TestDumpContext context(true); 441 DisassemblerObjdump dis(context.GetContextCPU(), &amd64::rep_stosb, 442 kAMD64TestRip); 443 uint64_t src_address = 0, dest_address = 0; 444 ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address)); 445 ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address)); 446 ASSERT_EQ(dest_address, kAMD64TestRdi); 447 } 448 449 TEST(DisassemblerObjdumpTest, AMD64LockCmpxchg) { 450 TestDumpContext context(true); 451 DisassemblerObjdump dis(context.GetContextCPU(), &amd64::lock_cmpxchg, 452 kAMD64TestRip); 453 uint64_t src_address = 0, dest_address = 0; 454 ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address)); 455 ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address)); 456 ASSERT_EQ(dest_address, kAMD64TestRsi + 0x10); 457 } 458 459 TEST(DisassemblerObjdumpTest, AMD64CallRegOffset) { 460 TestDumpContext context(true); 461 DisassemblerObjdump dis(context.GetContextCPU(), &amd64::call_reg_offset, 462 kAMD64TestRip); 463 uint64_t src_address = 0, dest_address = 0; 464 ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address)); 465 ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address)); 466 ASSERT_EQ(dest_address, kAMD64TestRsi + 0x9999999); 467 } 468 } // namespace google_breakpad