xref: /aosp_15_r20/external/google-breakpad/src/processor/disassembler_objdump_unittest.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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 
TestMemoryRegion(uint64_t address,std::vector<uint8_t> bytes)73 TestMemoryRegion::TestMemoryRegion(uint64_t address, std::vector<uint8_t> bytes)
74     : base_(address), bytes_(bytes) {}
75 
GetBase() const76 uint64_t TestMemoryRegion::GetBase() const {
77   return base_;
78 }
79 
GetSize() const80 uint32_t TestMemoryRegion::GetSize() const {
81   return static_cast<uint32_t>(bytes_.size());
82 }
83 
GetMemoryAtAddress(uint64_t address,uint8_t * value) const84 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.
GetMemoryAtAddress(uint64_t address,uint16_t * value) const96 bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address,
97                                           uint16_t* value) const {
98   return false;
99 }
100 
GetMemoryAtAddress(uint64_t address,uint32_t * value) const101 bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address,
102                                           uint32_t* value) const {
103   return false;
104 }
105 
GetMemoryAtAddress(uint64_t address,uint64_t * value) const106 bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address,
107                                           uint64_t* value) const {
108   return false;
109 }
110 
Print() const111 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 
TestDumpContext(bool x86_64)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 
~TestDumpContext()205 TestDumpContext::~TestDumpContext() {
206   FreeContext();
207 }
208 
TEST(DisassemblerObjdumpTest,DisassembleInstructionX86)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 
TEST(DisassemblerObjdumpTest,DisassembleInstructionAMD64)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 
TEST(DisassemblerObjdumpTest,TokenizeInstruction)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 
TEST(DisassemblerObjdumpTest,X86LoadReg)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 
TEST(DisassemblerObjdumpTest,X86LoadRegIndex)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 
TEST(DisassemblerObjdumpTest,X86LoadRegOffset)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 
TEST(DisassemblerObjdumpTest,X86LoadRegIndexOffset)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 
TEST(DisassemblerObjdumpTest,X86RepStosb)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 
TEST(DisassemblerObjdumpTest,X86LockCmpxchg)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 
TEST(DisassemblerObjdumpTest,X86CallRegOffset)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 
TEST(DisassemblerObjdumpTest,AMD64LoadReg)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 
TEST(DisassemblerObjdumpTest,AMD64LoadRegIndex)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 
TEST(DisassemblerObjdumpTest,AMD64LoadRipRelative)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 
TEST(DisassemblerObjdumpTest,AMD64LoadRegIndexOffset)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 
TEST(DisassemblerObjdumpTest,AMD64RepStosb)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 
TEST(DisassemblerObjdumpTest,AMD64LockCmpxchg)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 
TEST(DisassemblerObjdumpTest,AMD64CallRegOffset)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
469