Remove check for ABIVERSION, so that I don't have to rebuild binutils when this is...
[nativeclient.git] / ncv / ncdis.c
blob73b494f93db6936ad9537a988f90e996ecb47ec8
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * ncdis.c - disassemble using NaCl decoder.
34 * Mostly for testing.
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #include <string.h>
40 #if !NACL_WINDOWS
41 #include <unistd.h>
42 #endif
43 #include <errno.h>
44 #include <fcntl.h>
45 #include "native_client/include/nacl_elf.h"
46 #include "native_client/ncv/ncfileutil.h"
47 #include "native_client/ncv/ncdecode.h"
48 #include "native_client/ncv/ncdisasmtab.h"
50 static const char *progname;
52 void fatal(const char *fmt, ...)
54 va_list ap;
55 fprintf(stderr, "%s: fatal error: ", progname);
56 va_start(ap, fmt);
57 vfprintf(stderr, fmt, ap);
58 va_end(ap);
59 fputc('\n', stderr);
60 exit(2);
63 /* later this will make decoding x87 instructions a bit more concise. */
64 static const char **kDisasmX87Op[8] = { kDisasm87D8,
65 kDisasm87D9,
66 kDisasm87DA,
67 kDisasm87DB,
68 kDisasm87DC,
69 kDisasm87DD,
70 kDisasm87DE,
71 kDisasm87DF };
73 /* disassembler stuff */
74 static const char *DisFmt(const struct NCDecoderState *mstate) {
75 static const uint8_t kWAITOp = 0x9b;
76 uint8_t *opbyte = &mstate->inst.maddr[mstate->inst.prefixbytes];
77 uint8_t pm = mstate->inst.prefixmask;
79 if (mstate->opinfo->insttype == NACLi_X87) {
80 if (opbyte[0] != kWAITOp) {
81 return kDisasmX87Op[opbyte[0]-0xd8][mstate->inst.mrm];
84 if (mstate->opinfo->insttype == NACLi_FCMOV) {
85 return kDisasmX87Op[opbyte[0]-0xd8][mstate->inst.mrm];
87 if (*opbyte != kTwoByteOpcodeByte1) return kDisasm1ByteOp[opbyte[0]];
88 if (opbyte[1] == 0x0f) return kDisasm0F0FOp[opbyte[mstate->inst.length - 1]];
89 if (opbyte[1] == 0x38) return kDisasm0F38Op[opbyte[2]];
90 if (opbyte[1] == 0x3A) return kDisasm0F3AOp[opbyte[2]];
91 if (! (pm & (kPrefixDATA16 | kPrefixREPNE | kPrefixREP))) {
92 return kDisasm0FXXOp[opbyte[1]];
94 if (pm & kPrefixDATA16) return kDisasm660FXXOp[opbyte[1]];
95 if (pm & kPrefixREPNE) return kDisasmF20FXXOp[opbyte[1]];
96 if (pm & kPrefixREP) return kDisasmF30FXXOp[opbyte[1]];
98 /* no update; should be invalid */
99 return "internal error";
102 static int ByteImmediate(const uint8_t* byte_array) {
103 return (char) byte_array[0];
106 static int WordImmediate(const uint8_t* byte_array) {
107 return (short) (byte_array[0] + (byte_array[1] << 8));
110 static int DwordImmediate(const uint8_t* byte_array) {
111 return (byte_array[0] +
112 (byte_array[1] << 8) +
113 (byte_array[2] << 16) +
114 (byte_array[3] << 24));
117 static const char* gp_regs[] = {
118 "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi"
121 static const char* mmx_regs[] = {
122 "%mm0", "%mm1", "%mm2", "%mm3", "%mm4", "%mm5", "%mm6", "%mm7"
125 static const char* xmm_regs[] = {
126 "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7"
129 static const char* seg_regs[] = {
130 "%es", "%cs", "%ss", "%ds", "%fs", "%gs"
133 static void SibPrint(const struct NCDecoderState *mstate, uint32_t sib_offset) {
134 uint8_t sib = mstate->inst.maddr[sib_offset];
136 if (sib_ss(sib) == 0) {
137 if (sib_base(sib) == 5) {
138 const uint8_t* disp_addr = mstate->inst.maddr + sib_offset + 1;
139 fprintf(stdout, "[0x%x]", DwordImmediate(disp_addr));
140 } else {
141 /* Has a base register */
142 if (sib_index(sib) == 4) {
143 /* No index */
144 fprintf(stdout, "[%s]", gp_regs[sib_base(sib)]);
145 } else {
146 fprintf(stdout, "[%s + %s]",
147 gp_regs[sib_base(sib)],
148 gp_regs[sib_index(sib)]);
151 } else {
152 if (sib_index(sib) == 4) {
153 /* No index */
154 fprintf(stdout, "[%s]", gp_regs[sib_base(sib)]);
155 } else {
156 fprintf(stdout, "[%s + %d * %s]",
157 gp_regs[sib_base(sib)],
158 1 << sib_ss(sib),
159 gp_regs[sib_index(sib)]);
164 static void SegPrefixPrint(const struct NCDecoderState *mstate) {
165 uint8_t pm = mstate->inst.prefixmask;
166 if (pm & kPrefixSEGCS) {
167 fprintf(stdout, "cs:");
168 } else if (pm & kPrefixSEGSS) {
169 fprintf(stdout, "ss:");
170 } else if (pm & kPrefixSEGFS) {
171 fprintf(stdout, "fs:");
172 } else if (pm & kPrefixSEGGS) {
173 fprintf(stdout, "gs:");
177 static void RegMemPrint(const struct NCDecoderState *mstate,
178 const char* reg_names[]) {
179 uint32_t sib_offset =
180 mstate->inst.prefixbytes +
182 mstate->inst.hasopbyte2 +
183 mstate->inst.hasopbyte3 +
185 const uint8_t* disp_addr = mstate->inst.maddr +
186 sib_offset +
187 mstate->inst.hassibbyte;
189 switch (modrm_mod(mstate->inst.mrm)) {
190 case 0:
191 SegPrefixPrint(mstate);
192 if (4 == modrm_rm(mstate->inst.mrm)) {
193 SibPrint(mstate, sib_offset);
194 } else if (5 == modrm_rm(mstate->inst.mrm)) {
195 fprintf(stdout, "[0x%x]", DwordImmediate(disp_addr));
196 } else {
197 fprintf(stdout, "[%s]", gp_regs[modrm_rm(mstate->inst.mrm)]);
199 break;
200 case 1:
202 SegPrefixPrint(mstate);
203 fprintf(stdout, "0x%x", ByteImmediate(disp_addr));
204 if (4 == modrm_rm(mstate->inst.mrm)) {
205 SibPrint(mstate, sib_offset);
206 } else {
207 fprintf(stdout, "[%s]", gp_regs[modrm_rm(mstate->inst.mrm)]);
210 break;
211 case 2:
213 SegPrefixPrint(mstate);
214 fprintf(stdout, "0x%x", DwordImmediate(disp_addr));
215 if (4 == modrm_rm(mstate->inst.mrm)) {
216 SibPrint(mstate, sib_offset);
217 } else {
218 fprintf(stdout, "[%s]", gp_regs[modrm_rm(mstate->inst.mrm)]);
221 break;
222 case 3:
223 fprintf(stdout, "%s", reg_names[modrm_rm(mstate->inst.mrm)]);
224 break;
228 static void InstFormat(const char* format,
229 const struct NCDecoderState *mstate) {
230 char token_buf[128];
231 char* fmt = token_buf;
232 int pos = 0;
234 strncpy(token_buf, format, sizeof(token_buf));
236 while (1) {
237 char* token = strtok(fmt, " ,\n");
238 if (NULL == token) {
239 break;
241 if (pos > 1) {
242 fprintf(stdout, ", ");
243 } else if (pos > 0) {
244 fprintf(stdout, " ");
246 if (('$' == token[0]) && !strncmp(token, "$group", 6)) {
247 int mrm = modrm_reg(mstate->inst.mrm);
248 if (!strcmp(token, "$group1")) {
249 fprintf(stdout, "%s", kDisasmModRMOp[GROUP1][mrm]);
250 } else if (!strcmp(token, "$group2")) {
251 fprintf(stdout, "%s", kDisasmModRMOp[GROUP2][mrm]);
252 } else if (!strcmp(token, "$group3")) {
253 fprintf(stdout, "%s", kDisasmModRMOp[GROUP3][mrm]);
254 } else if (!strcmp(token, "$group4")) {
255 fprintf(stdout, "%s", kDisasmModRMOp[GROUP4][mrm]);
256 } else if (!strcmp(token, "$group5")) {
257 fprintf(stdout, "%s", kDisasmModRMOp[GROUP5][mrm]);
258 } else if (!strcmp(token, "$group6")) {
259 fprintf(stdout, "%s", kDisasmModRMOp[GROUP6][mrm]);
260 } else if (!strcmp(token, "$group7")) {
261 fprintf(stdout, "%s", kDisasmModRMOp[GROUP7][mrm]);
262 } else if (!strcmp(token, "$group8")) {
263 fprintf(stdout, "%s", kDisasmModRMOp[GROUP8][mrm]);
264 } else if (!strcmp(token, "$group9")) {
265 fprintf(stdout, "%s", kDisasmModRMOp[GROUP9][mrm]);
266 } else if (!strcmp(token, "$group10")) {
267 fprintf(stdout, "%s", kDisasmModRMOp[GROUP10][mrm]);
268 } else if (!strcmp(token, "$group11")) {
269 fprintf(stdout, "%s", kDisasmModRMOp[GROUP11][mrm]);
270 } else if (!strcmp(token, "$group12")) {
271 fprintf(stdout, "%s", kDisasmModRMOp[GROUP12][mrm]);
272 } else if (!strcmp(token, "$group13")) {
273 fprintf(stdout, "%s", kDisasmModRMOp[GROUP13][mrm]);
274 } else if (!strcmp(token, "$group14")) {
275 fprintf(stdout, "%s", kDisasmModRMOp[GROUP14][mrm]);
276 } else if (!strcmp(token, "$group15")) {
277 fprintf(stdout, "%s", kDisasmModRMOp[GROUP15][mrm]);
278 } else if (!strcmp(token, "$group16")) {
279 fprintf(stdout, "%s", kDisasmModRMOp[GROUP16][mrm]);
280 } else if (!strcmp(token, "$group17")) {
281 fprintf(stdout, "%s", kDisasmModRMOp[GROUP17][mrm]);
282 } else if (!strcmp(token, "$group1a")) {
283 fprintf(stdout, "%s", kDisasmModRMOp[GROUP1A][mrm]);
284 } else if (!strcmp(token, "$groupp")) {
285 fprintf(stdout, "%s", kDisasmModRMOp[GROUPP][mrm]);
286 } else {
287 fprintf(stdout, "%s", token);
289 } else if ('$' == token[0]) {
290 /* Tokens starting with a $ but not $group need formatting */
291 switch (token[1]) {
292 case 'A':
293 fprintf(stdout, "$A");
294 break;
295 case 'C':
296 fprintf(stdout, "%%cr%d", modrm_reg(mstate->inst.mrm));
297 break;
298 case 'D':
299 fprintf(stdout, "%%dr%d", modrm_reg(mstate->inst.mrm));
300 break;
301 case 'E':
302 case 'M': /* mod should never be 3 for 'M' */
303 /* TODO: byte and word accesses */
304 RegMemPrint(mstate, gp_regs);
305 break;
306 case 'F':
307 fprintf(stdout, "eflags");
308 break;
309 case 'G':
310 fprintf(stdout, "%s", gp_regs[modrm_reg(mstate->inst.mrm)]);
311 break;
312 case 'I':
314 const uint8_t* imm_addr = mstate->inst.maddr +
315 mstate->inst.prefixbytes +
317 mstate->inst.hasopbyte2 +
318 mstate->inst.hasopbyte3 +
319 mstate->opinfo->hasmrmbyte +
320 mstate->inst.hassibbyte +
321 mstate->inst.dispbytes;
322 if ('b' == token[2]) {
323 fprintf(stdout, "0x%x", ByteImmediate(imm_addr));
324 } else if ('w' == token[2]) {
325 fprintf(stdout, "0x%x", WordImmediate(imm_addr));
326 } else {
327 fprintf(stdout, "0x%x", DwordImmediate(imm_addr));
330 break;
331 case 'J':
333 const uint8_t* imm_addr = mstate->inst.maddr +
334 mstate->inst.prefixbytes +
336 mstate->inst.hasopbyte2 +
337 mstate->inst.hasopbyte3 +
338 mstate->opinfo->hasmrmbyte +
339 mstate->inst.hassibbyte +
340 mstate->inst.dispbytes;
341 if ('b' == token[2]) {
342 fprintf(stdout, "0x%x",
343 mstate->inst.vaddr + mstate->inst.length +
344 ByteImmediate(imm_addr));
345 } else {
346 fprintf(stdout, "0x%x",
347 mstate->inst.vaddr + mstate->inst.length +
348 DwordImmediate(imm_addr));
351 break;
352 case 'O':
354 const uint8_t* imm_addr = mstate->inst.maddr +
355 mstate->inst.prefixbytes +
357 mstate->inst.hasopbyte2 +
358 mstate->inst.hasopbyte3;
359 fprintf(stdout, "[0x%x]", DwordImmediate(imm_addr));
361 break;
362 case 'P':
363 if ('R' == token[2]) {
364 fprintf(stdout, "%%mm%d", modrm_rm(mstate->inst.mrm));
365 } else {
366 fprintf(stdout, "%%mm%d", modrm_reg(mstate->inst.mrm));
368 break;
369 case 'Q':
370 RegMemPrint(mstate, mmx_regs);
371 break;
372 case 'R':
373 fprintf(stdout, "%s", gp_regs[modrm_rm(mstate->inst.mrm)]);
374 break;
375 case 'S':
376 fprintf(stdout, "%s", seg_regs[modrm_reg(mstate->inst.mrm)]);
377 break;
378 case 'V':
379 if ('R' == token[2]) {
380 fprintf(stdout, "%%xmm%d", modrm_rm(mstate->inst.mrm));
381 } else {
382 fprintf(stdout, "%%xmm%d", modrm_reg(mstate->inst.mrm));
384 break;
385 case 'W':
386 RegMemPrint(mstate, xmm_regs);
387 break;
388 case 'X':
389 fprintf(stdout, "ds:[esi]");
390 break;
391 case 'Y':
392 fprintf(stdout, "es:[edi]");
393 break;
394 default:
395 fprintf(stdout, "token('%s')", token);
396 break;
398 } else {
399 /* Print the token as is */
400 fprintf(stdout, "%s", token);
402 fmt = NULL;
403 ++pos;
407 static void PrintInst(const struct NCDecoderState *mstate) {
408 int i;
409 fprintf(stdout, " %x:\t%02x", mstate->inst.vaddr,
410 mstate->inst.maddr[0]);
411 for (i = 1; i < mstate->inst.length; i++) {
412 fprintf(stdout, " %02x", mstate->inst.maddr[i]);
414 for (i = mstate->inst.length; i < 7; i++) fprintf(stdout, " ");
415 fprintf(stdout, "\t");
416 InstFormat(DisFmt(mstate), mstate);
417 fprintf(stdout, "\n");
420 int AnalyzeSections(ncfile *ncf) {
421 int badsections = 0;
422 int ii;
423 Elf32_Shdr *shdr = ncf->sheaders;
425 for (ii = 0; ii < ncf->shnum; ii++) {
426 printf("section %d sh_addr %x offset %x flags %x\n",
427 ii, (uint32_t)shdr[ii].sh_addr,
428 (uint32_t)shdr[ii].sh_offset, (uint32_t)shdr[ii].sh_flags);
429 if ((shdr[ii].sh_flags & SHF_EXECINSTR) != SHF_EXECINSTR)
430 continue;
431 printf("parsing section %d\n", ii);
432 NCDecodeSegment(ncf->data + (shdr[ii].sh_addr - ncf->vbase),
433 shdr[ii].sh_addr, shdr[ii].sh_size, NULL);
435 return -badsections;
439 void AnalyzeCodeSegments(ncfile *ncf, const char *fname) {
440 if (AnalyzeSections(ncf) < 0) {
441 fprintf(stderr, "%s: text validate failed\n", fname);
445 static const char* GrockArgv(int argc, const char *argv[]) {
446 progname = argv[0];
448 if (argc == 1) {
449 fprintf(stderr, "Running %s unit tests\n", progname);
450 return NULL;
452 return argv[argc-1];
455 int main(int argc, const char *argv[])
457 const char *loadname = GrockArgv(argc, argv);
458 ncfile *ncf;
460 NCDecodeRegisterCallbacks(PrintInst, NULL, NULL, NULL);
461 if (loadname == NULL) {
462 extern void ncdecode_unittests();
464 ncdecode_unittests();
465 return 0;
468 ncf = nc_loadfile(loadname);
469 if (ncf == NULL)
470 fatal("nc_loadfile(%s): %s\n", strerror(errno));
472 AnalyzeCodeSegments(ncf, loadname);
474 nc_freefile(ncf);
476 return 0;