1// Copyright 2014, VIXL authors
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7//   * Redistributions of source code must retain the above copyright notice,
8//     this list of conditions and the following disclaimer.
9//   * Redistributions in binary form must reproduce the above copyright notice,
10//     this list of conditions and the following disclaimer in the documentation
11//     and/or other materials provided with the distribution.
12//   * Neither the name of ARM Limited nor the names of its contributors may be
13//     used to endorse or promote products derived from this software without
14//     specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27#include "custom-disassembler.h"
28#include "examples.h"
29
30
31#define __ masm->
32
33
34// We override this method to specify how register names should be disassembled.
35void CustomDisassembler::AppendRegisterNameToOutput(const Instruction* instr,
36                                                    const CPURegister& reg) {
37  USE(instr);
38  if (reg.IsRegister()) {
39    switch (reg.GetCode()) {
40      case 16:
41        AppendToOutput(reg.Is64Bits() ? "ip0" : "wip0");
42        return;
43      case 17:
44        AppendToOutput(reg.Is64Bits() ? "ip1" : "wip1");
45        return;
46      case 30:
47        AppendToOutput(reg.Is64Bits() ? "lr" : "w30");
48        return;
49      case kSPRegInternalCode:
50        AppendToOutput(reg.Is64Bits() ? "x_stack_pointer" : "w_stack_pointer");
51        return;
52      case 31:
53        AppendToOutput(reg.Is64Bits() ? "x_zero_reg" : "w_zero_reg");
54        return;
55      default:
56        // Fall through.
57        break;
58    }
59  }
60  // Print other register names as usual.
61  Disassembler::AppendRegisterNameToOutput(instr, reg);
62}
63
64
65static const char* FakeLookupTargetDescription(const void* address) {
66  USE(address);
67  // We fake looking up the address.
68  static int i = 0;
69  const char* desc = NULL;
70  if (i == 0) {
71    desc = "label: somewhere";
72  } else if (i == 2) {
73    desc = "label: somewhere else";
74  }
75  i++;
76  return desc;
77}
78
79
80// We override this method to add a description to addresses that we know about.
81// In this example we fake looking up a description, but in practice one could
82// for example use a table mapping addresses to function names.
83void CustomDisassembler::AppendCodeRelativeCodeAddressToOutput(
84    const Instruction* instr, const void* addr) {
85  USE(instr);
86  // Print the address.
87  int64_t rel_addr = CodeRelativeAddress(addr);
88  if (rel_addr >= 0) {
89    AppendToOutput("(addr 0x%" PRIx64, rel_addr);
90  } else {
91    AppendToOutput("(addr -0x%" PRIx64, -rel_addr);
92  }
93
94  // If available, print a description of the address.
95  const char* address_desc = FakeLookupTargetDescription(addr);
96  if (address_desc != NULL) {
97    Disassembler::AppendToOutput(" ; %s", address_desc);
98  }
99  AppendToOutput(")");
100}
101
102
103// We override this method to add a comment to this type of instruction. Helpers
104// from the vixl::Instruction class can be used to analyse the instruction being
105// disasssembled.
106void CustomDisassembler::VisitAddSubShifted(const Instruction* instr) {
107  vixl::aarch64::Disassembler::VisitAddSubShifted(instr);
108  if (instr->GetRd() == 10) {
109    AppendToOutput(" // add/sub to x10");
110  }
111  ProcessOutput(instr);
112}
113
114
115void GenerateCustomDisassemblerTestCode(MacroAssembler* masm) {
116  // Generate some code to illustrate how the modified disassembler changes the
117  // disassembly output.
118  Label begin, end;
119  __ Bind(&begin);
120  __ Add(x10, x16, x17);
121  __ Cbz(x10, &end);
122  __ Add(x11, ip0, ip1);
123  __ Add(w5, w6, w30);
124  __ Tbz(x10, 2, &begin);
125  __ Tbnz(x10, 3, &begin);
126  __ Br(x30);
127  __ Br(lr);
128  __ Fadd(d30, d16, d17);
129  __ Push(xzr, xzr);
130  __ Pop(x16, x20);
131  __ Bind(&end);
132}
133
134
135void TestCustomDisassembler() {
136  MacroAssembler masm;
137
138  // Generate the code.
139  Label code_start, code_end;
140  masm.Bind(&code_start);
141  GenerateCustomDisassemblerTestCode(&masm);
142  masm.Bind(&code_end);
143  masm.FinalizeCode();
144  Instruction* instr_start = masm.GetLabelAddress<Instruction*>(&code_start);
145  Instruction* instr_end = masm.GetLabelAddress<Instruction*>(&code_end);
146
147  // Instantiate a standard disassembler, our custom disassembler, and register
148  // them with a decoder.
149  Decoder decoder;
150  Disassembler disasm;
151  CustomDisassembler custom_disasm;
152  decoder.AppendVisitor(&disasm);
153  decoder.AppendVisitor(&custom_disasm);
154
155  // In our custom disassembler, disassemble as if the base address was -0x8.
156  // Note that this can also be achieved with
157  //   custom_disasm.MapCodeAddress(0x0, instr_start + 2 * kInstructionSize);
158  // Users may generally want to map the start address to 0x0. Mapping to a
159  // negative offset can be used to focus on the section of the
160  // disassembly at address 0x0.
161  custom_disasm.MapCodeAddress(-0x8, instr_start);
162
163  // Iterate through the instructions to show the difference in the disassembly.
164  Instruction* instr;
165  for (instr = instr_start; instr < instr_end; instr += kInstructionSize) {
166    decoder.Decode(instr);
167    printf("\n");
168    printf("VIXL disasm\t %p:\t%s\n",
169           reinterpret_cast<void*>(instr),
170           disasm.GetOutput());
171    int64_t rel_addr =
172        custom_disasm.CodeRelativeAddress(reinterpret_cast<void*>(instr));
173    char rel_addr_sign_char = ' ';
174    if (rel_addr < 0) {
175      rel_addr_sign_char = '-';
176      rel_addr = -rel_addr;
177    }
178    printf("custom disasm\t%c0x%" PRIx64 ":\t%s\n",
179           rel_addr_sign_char,
180           rel_addr,
181           custom_disasm.GetOutput());
182  }
183}
184
185
186#ifndef TEST_EXAMPLES
187int main() {
188  TestCustomDisassembler();
189  return 0;
190}
191#endif
192