/ src / processor / disassembler_objdump_unittest.cc
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