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
29 #include <regex>
30
31 #include "examples.h"
32
33 using namespace vixl;
34 using namespace vixl::aarch64;
35
36 #define __ masm->
37
38
39 // We override this method to specify how register names should be disassembled.
AppendRegisterNameToOutput(const Instruction * instr,const CPURegister & reg)40 void CustomDisassembler::AppendRegisterNameToOutput(const Instruction* instr,
41 const CPURegister& reg) {
42 USE(instr);
43 if (reg.IsRegister()) {
44 switch (reg.GetCode()) {
45 case 16:
46 AppendToOutput(reg.Is64Bits() ? "ip0" : "wip0");
47 return;
48 case 17:
49 AppendToOutput(reg.Is64Bits() ? "ip1" : "wip1");
50 return;
51 case 30:
52 AppendToOutput(reg.Is64Bits() ? "lr" : "w30");
53 return;
54 case kSPRegInternalCode:
55 AppendToOutput(reg.Is64Bits() ? "x_stack_pointer" : "w_stack_pointer");
56 return;
57 case 31:
58 AppendToOutput(reg.Is64Bits() ? "x_zero_reg" : "w_zero_reg");
59 return;
60 default:
61 // Fall through.
62 break;
63 }
64 }
65 // Print other register names as usual.
66 Disassembler::AppendRegisterNameToOutput(instr, reg);
67 }
68
69
FakeLookupTargetDescription(const void * address)70 static const char* FakeLookupTargetDescription(const void* address) {
71 USE(address);
72 // We fake looking up the address.
73 static int i = 0;
74 const char* desc = NULL;
75 if (i == 0) {
76 desc = "label: somewhere";
77 } else if (i == 2) {
78 desc = "label: somewhere else";
79 }
80 i++;
81 return desc;
82 }
83
84
85 // We override this method to add a description to addresses that we know about.
86 // In this example we fake looking up a description, but in practice one could
87 // for example use a table mapping addresses to function names.
AppendCodeRelativeCodeAddressToOutput(const Instruction * instr,const void * addr)88 void CustomDisassembler::AppendCodeRelativeCodeAddressToOutput(
89 const Instruction* instr, const void* addr) {
90 USE(instr);
91 // Print the address.
92 int64_t rel_addr = CodeRelativeAddress(addr);
93 if (rel_addr >= 0) {
94 AppendToOutput("(addr 0x%" PRIx64, rel_addr);
95 } else {
96 AppendToOutput("(addr -0x%" PRIx64, -rel_addr);
97 }
98
99 // If available, print a description of the address.
100 const char* address_desc = FakeLookupTargetDescription(addr);
101 if (address_desc != NULL) {
102 Disassembler::AppendToOutput(" ; %s", address_desc);
103 }
104 AppendToOutput(")");
105 }
106
107
108 // We override this method to add a comment to some instructions. Helpers from
109 // the vixl::Instruction class can be used to analyse the instruction being
110 // disassembled.
Visit(Metadata * metadata,const Instruction * instr)111 void CustomDisassembler::Visit(Metadata* metadata, const Instruction* instr) {
112 vixl::aarch64::Disassembler::Visit(metadata, instr);
113 const std::string& form = (*metadata)["form"];
114
115 // Match the forms for 32/64-bit add/subtract with shift, with optional flag
116 // setting.
117 if (std::regex_match(form, // NOLINT: avoid clang-tidy-4.0 errors.
118 std::regex("(?:add|sub)s?_(?:32|64)_addsub_shift"))) {
119 if (instr->GetRd() == 10) {
120 AppendToOutput(" // add/sub to x10");
121 }
122 }
123 ProcessOutput(instr);
124 }
125
126
GenerateCustomDisassemblerTestCode(MacroAssembler * masm)127 void GenerateCustomDisassemblerTestCode(MacroAssembler* masm) {
128 // Generate some code to illustrate how the modified disassembler changes the
129 // disassembly output.
130 Label begin, end;
131 __ Bind(&begin);
132 __ Add(x10, x16, x17);
133 __ Cbz(x10, &end);
134 __ Add(x11, ip0, ip1);
135 __ Add(w5, w6, w30);
136 __ Tbz(x10, 2, &begin);
137 __ Tbnz(x10, 3, &begin);
138 __ Br(x30);
139 __ Br(lr);
140 __ Fadd(d30, d16, d17);
141 __ Push(xzr, xzr);
142 __ Pop(x16, x20);
143 __ Bind(&end);
144 }
145
146
TestCustomDisassembler()147 void TestCustomDisassembler() {
148 MacroAssembler masm;
149
150 // Generate the code.
151 Label code_start, code_end;
152 masm.Bind(&code_start);
153 GenerateCustomDisassemblerTestCode(&masm);
154 masm.Bind(&code_end);
155 masm.FinalizeCode();
156 Instruction* instr_start = masm.GetLabelAddress<Instruction*>(&code_start);
157 Instruction* instr_end = masm.GetLabelAddress<Instruction*>(&code_end);
158
159 // Instantiate a standard disassembler, our custom disassembler, and register
160 // them with a decoder.
161 Decoder decoder;
162 Disassembler disasm;
163 CustomDisassembler custom_disasm;
164 decoder.AppendVisitor(&disasm);
165 decoder.AppendVisitor(&custom_disasm);
166
167 // In our custom disassembler, disassemble as if the base address was -0x8.
168 // Note that this can also be achieved with
169 // custom_disasm.MapCodeAddress(0x0, instr_start + 2 * kInstructionSize);
170 // Users may generally want to map the start address to 0x0. Mapping to a
171 // negative offset can be used to focus on the section of the
172 // disassembly at address 0x0.
173 custom_disasm.MapCodeAddress(-0x8, instr_start);
174
175 // Iterate through the instructions to show the difference in the disassembly.
176 Instruction* instr;
177 for (instr = instr_start; instr < instr_end; instr += kInstructionSize) {
178 decoder.Decode(instr);
179 printf("\n");
180 printf("VIXL disasm\t %p:\t%s\n",
181 reinterpret_cast<void*>(instr),
182 disasm.GetOutput());
183 int64_t rel_addr =
184 custom_disasm.CodeRelativeAddress(reinterpret_cast<void*>(instr));
185 char rel_addr_sign_char = ' ';
186 if (rel_addr < 0) {
187 rel_addr_sign_char = '-';
188 rel_addr = -rel_addr;
189 }
190 printf("custom disasm\t%c0x%" PRIx64 ":\t%s\n",
191 rel_addr_sign_char,
192 rel_addr,
193 custom_disasm.GetOutput());
194 }
195 }
196
197
198 #ifndef TEST_EXAMPLES
main()199 int main() {
200 TestCustomDisassembler();
201 return 0;
202 }
203 #endif
204