1/*
2 * Copyright © 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1.  Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * 2.  Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
15 * contributors may be used to endorse or promote products derived from this
16 * software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * @APPLE_LICENSE_HEADER_END@
30 */
31#include <stdio.h>
32#include <string.h>
33#include <mach-o/loader.h>
34#include <mach-o/nlist.h>
35#include <mach-o/reloc.h>
36#include <mach-o/i860/reloc.h>
37#include "stuff/symbol.h"
38#include "stuff/bytesex.h"
39#include "otool.h"
40#include "../as/i860-opcode.h"
41
42static void i860_dump_operands(
43    uint32_t opcode,
44    char *format,
45    uint32_t addr,
46    uint32_t sect_addr,
47    struct relocation_info *relocs,
48    uint32_t nrelocs,
49    struct nlist *symbols,
50    uint32_t nsymbols,
51    struct symbol *sorted_symbols,
52    uint32_t nsorted_symbols,
53    char *strings,
54    uint32_t strings_size,
55    enum bool verbose);
56
57static void i860_dump_addr(
58    uint32_t addr_field,
59    int format,
60    int32_t addr,
61    uint32_t sect_addr,
62    struct relocation_info *relocs,
63    uint32_t nrelocs,
64    struct nlist *symbols,
65    uint32_t nsymbols,
66    struct symbol *sorted_symbols,
67    uint32_t nsorted_symbols,
68    char *strings,
69    uint32_t strings_size,
70    enum bool verbose);
71
72static enum bool i860_print_symbol(
73    uint32_t value,
74    struct relocation_info *rp,
75    struct nlist *symbols,
76    uint32_t nsymbols,
77    struct symbol *sorted_symbols,
78    uint32_t nsorted_symbols,
79    char *strings,
80    uint32_t strings_size,
81    enum bool verbose);
82
83/*
84 * Disassemble 1 instruction and return the length of the disassembled
85 * piece in bytes.
86 */
87uint32_t
88i860_disassemble(
89char *sect,
90uint32_t left,
91uint32_t addr,
92uint32_t sect_addr,
93enum byte_sex object_byte_sex,
94struct relocation_info *relocs,
95uint32_t nrelocs,
96struct nlist *symbols,
97uint32_t nsymbols,
98struct symbol *sorted_symbols,
99uint32_t nsorted_symbols,
100char *strings,
101uint32_t strings_size,
102enum bool verbose)
103{
104    enum byte_sex host_byte_sex;
105    enum bool swapped;
106    uint32_t opcode;
107    int isdual;
108    uint32_t i;
109    struct i860_opcode *op;
110
111	host_byte_sex = get_host_byte_sex();
112	swapped = host_byte_sex != object_byte_sex;
113
114	if(left < sizeof(uint32_t)){
115	   if(left != 0){
116		memcpy(&opcode, sect, left);
117		if(swapped)
118		    opcode = SWAP_INT(opcode);
119		printf(".long\t0x%08x\n", (unsigned int)opcode);
120	   }
121	   printf("(end of section)\n");
122	   return(left);
123	}
124
125	memcpy(&opcode, sect, sizeof(uint32_t));
126	if(swapped)
127	    opcode = SWAP_INT(opcode);
128
129	/*
130	 * The pad opcode, 0, is chosen as an illegal insn to fault if
131	 * executed
132	 */
133	if(opcode == 0){
134	    printf("| Padded to i860 section boundary\n");
135	    return(4);
136	}
137
138	isdual = 0;
139	/*
140	 * See if this is a dual insn mode opcode.
141	 * Turn off the dual mode bit and print a d. if appropriate.
142	 */
143	if((opcode & OP_PREFIX_MASK) == PREFIX_FPU ||
144	   opcode == (OP_FNOP|DUAL_INSN_MODE_BIT)){
145	    if(opcode & DUAL_INSN_MODE_BIT){
146		opcode &= ~DUAL_INSN_MODE_BIT;
147		isdual = 1;
148	    }
149	}
150	/*
151	 * Search the instruction table for a match for this opcode.
152	 * We use a linear search because it's easy, uses the
153	 * assembler insn tables, and I'm so lazy....
154	 * Feel free to recode with whizzy hashes and such.
155	 */
156	op = (struct i860_opcode *)i860_opcodes;
157	for(i = 0; i < NUMOPCODES; i++, op++){
158	    if((opcode & op->mask) == op->match){
159		if(isdual)
160		    printf("d.%-12s\t", op->name);
161		else
162		    printf("%-12s\t", op->name);
163		i860_dump_operands(opcode, (char *)op->args, addr, sect_addr,
164			relocs, nrelocs, symbols, nsymbols, sorted_symbols,
165			nsorted_symbols, strings, strings_size, verbose);
166		return(sizeof(uint32_t));
167	    }
168	}
169
170	/* Didn't find the opcode.  Dump it as a .long directive. */
171	/* Build it as a little-endian insn, in a format to match asm */
172	printf(".long\t0x%08x\n", (unsigned int)opcode);
173	return(sizeof(uint32_t));
174}
175
176/* 32 possible valuse, of which 6 are actually used. */
177static char *i860_controlregs[] = {"fir", "psr", "dirbase", "db", "fsr", "epsr",
178				   "?","?","?","?","?","?",
179				   "?","?","?","?","?","?",
180				   "?","?","?","?","?","?",
181				   "?","?","?","?","?","?"};
182
183static
184void
185i860_dump_operands(
186uint32_t opcode,
187char *format,
188uint32_t addr,
189uint32_t sect_addr,
190struct relocation_info *relocs,
191uint32_t nrelocs,
192struct nlist *symbols,
193uint32_t nsymbols,
194struct symbol *sorted_symbols,
195uint32_t nsorted_symbols,
196char *strings,
197uint32_t strings_size,
198enum bool verbose)
199{
200    uint32_t addr_field;
201
202	while(*format != '\0'){
203	    switch(*format){
204	    case '1': /* rs1 register, bits 11-15 of insn */
205		printf("r%u", GET_RS1(opcode));
206		break;
207
208	    case '2': /* rs2 register, bits 21-25 of insn */
209		printf("r%u", GET_RS2(opcode));
210		break;
211
212	    case 'd': /* rd register, bits 16-20 of insn */
213		printf("r%u", GET_RD(opcode));
214		break;
215
216	    case 'E':
217	    case 'e': /* frs1 floating point register, bits 11-15 of insn */
218		printf("f%u", GET_RS1(opcode));
219		break;
220
221	    case 'F':
222	    case 'f': /* frs2 floating point register, bits 21-25 of insn */
223		printf("f%u", GET_RS2(opcode));
224		break;
225
226	    case 'H':
227	    case 'G':
228	    case 'g': /* frsd floating point register, bits 16-20 of insn */
229		printf("f%u", GET_RD(opcode));
230		break;
231
232	    case 'I': /* 16 bit High portion of address, I860_RELOC_HIGH */
233	    case 'J': /* 16 bit High portion of addr requiring adjustment */
234		addr_field = opcode & 0xFFFF;
235		i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
236			nrelocs, symbols, nsymbols, sorted_symbols,
237			nsorted_symbols, strings, strings_size, verbose);
238		break;
239
240	    case 'i': /* 16 bit byte address low half */
241		addr_field = (opcode & 0xFFFF);
242		i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
243			nrelocs, symbols, nsymbols, sorted_symbols,
244			nsorted_symbols, strings, strings_size, verbose);
245		break;
246
247	    case 'j': /* 16 bit short address, I860_RELOC_LOW1 */
248		addr_field = (opcode & 0xFFFE);
249		i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
250			nrelocs, symbols, nsymbols, sorted_symbols,
251			nsorted_symbols, strings, strings_size, verbose);
252		break;
253
254	    case 'k': /* 16 bit word/int address low half, I860_RELOC_LOW2 */
255		addr_field = (opcode & 0xFFFC);
256		i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
257			nrelocs, symbols, nsymbols, sorted_symbols,
258			nsorted_symbols, strings, strings_size, verbose);
259		break;
260
261	    case 'l': /* 16 bit 8-byte address (double) low half */
262		addr_field = (opcode & 0xFFF8);
263		i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
264			nrelocs, symbols, nsymbols, sorted_symbols,
265			nsorted_symbols, strings, strings_size, verbose);
266		break;
267
268	    case 'm': /* 16 bit 16-byte address (quad) low half */
269		addr_field = (opcode & 0xFFF0);
270		i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
271			nrelocs, symbols, nsymbols, sorted_symbols,
272			nsorted_symbols, strings, strings_size, verbose);
273		break;
274
275	    case 'n': /* 16 bit byte aligned low half, split fields */
276		addr_field = ((opcode >> 5) & 0xF800) | (opcode & 0x7FF);
277		i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
278			nrelocs, symbols, nsymbols, sorted_symbols,
279			nsorted_symbols, strings, strings_size, verbose);
280		break;
281
282	    case 'o': /* 16 bit short aligned low half, split fields */
283		addr_field = ((opcode >> 5) & 0xF800) | (opcode & 0x7FE);
284		i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
285			nrelocs, symbols, nsymbols, sorted_symbols,
286			nsorted_symbols, strings, strings_size, verbose);
287		break;
288
289	    case 'p': /* 16 bit int/word aligned low half, split fields */
290		addr_field = ((opcode >> 5) & 0xF800) | (opcode & 0x7FC);
291		i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
292			nrelocs, symbols, nsymbols, sorted_symbols,
293			nsorted_symbols, strings, strings_size, verbose);
294		break;
295
296	    case 'K': /* 26 bit branch displacement */
297		addr_field = opcode & 0x3FFFFFF;
298		if(addr_field & 0x02000000)	/* MSB set? */
299		    addr_field |= 0xFC000000; 	/* Sign extend */
300		addr_field <<= 2;	/* Convert to byte addr */
301		i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
302			nrelocs, symbols, nsymbols, sorted_symbols,
303			nsorted_symbols, strings, strings_size, verbose);
304		break;
305
306	    case 'L': /* 16 bit split branch displacement */
307		addr_field = ((opcode >> 5) & 0xF800) | (opcode & 0x7FF);
308		if(addr_field & 0x8000)		/* MSB set? */
309		    addr_field |= 0xFFFF0000; 	/* Sign extend */
310		addr_field <<= 2;	/* Convert to byte addr */
311		i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
312			nrelocs, symbols, nsymbols, sorted_symbols,
313			nsorted_symbols, strings, strings_size, verbose);
314		break;
315
316	    case 'D': /* constant for shift opcode */
317		printf("%u", opcode & 0xFFFF);
318		break;
319
320	    case 'B': /* 5 bit immediate, for bte and btne insn */
321		printf("%u", GET_RS1(opcode));
322		break;
323
324	    case 'C': /* Control Register */
325		printf("%s", i860_controlregs[GET_RS2(opcode)]);
326		break;
327
328	    default:
329		printf("%c", *format);
330		break;
331	    }
332	    ++format;
333	}
334	printf("\n");
335}
336
337static
338void
339i860_dump_addr(
340uint32_t addr_field,
341int format,
342int32_t addr,
343uint32_t sect_addr,
344struct relocation_info *relocs,
345uint32_t nrelocs,
346struct nlist *symbols,
347uint32_t nsymbols,
348struct symbol *sorted_symbols,
349uint32_t nsorted_symbols,
350char *strings,
351uint32_t strings_size,
352enum bool verbose)
353{
354    uint32_t i;
355    struct relocation_info *rp, *pairp;
356    struct scattered_relocation_info *sreloc;
357    char *prefix;
358
359	rp = NULL;
360	pairp = NULL;
361	if(nrelocs){
362	    for(i = 0; i < nrelocs; i++){
363		if(((relocs[i].r_address) & R_SCATTERED) != 0){
364		    sreloc = (struct scattered_relocation_info *)(relocs + i);
365		    if(sreloc->r_type == I860_RELOC_PAIR){
366			fprintf(stderr, "Stray I860_RELOC_PAIR relocation "
367				"entry %u\n", i);
368			continue;
369		    }
370		    if(sreloc->r_type == I860_RELOC_HIGH ||
371		       sreloc->r_type == I860_RELOC_HIGHADJ ||
372		       sreloc->r_type == I860_RELOC_SECTDIFF){
373			if(i+1 >= nrelocs ||
374			   relocs[i+1].r_type != I860_RELOC_PAIR){
375				fprintf(stderr, "No I860_RELOC_PAIR relocation "
376					"entry after entry %u\n", i);
377			}
378			else{
379			    if(((relocs[i+1].r_address) & R_SCATTERED) != 0){
380				sreloc = (struct scattered_relocation_info *)
381					 (relocs + i + 1);
382				if(sreloc->r_type != I860_RELOC_PAIR)
383				    fprintf(stderr, "No I860_RELOC_PAIR "
384					    "relocation entry after entry "
385					    "%u\n", i);
386			    }
387			    else if(relocs[i+1].r_type != I860_RELOC_PAIR){
388				fprintf(stderr, "No I860_RELOC_PAIR relocation "
389					"entry after entry %u\n", i);
390			    }
391			    i++;
392			    continue;
393			}
394		    }
395		}
396		if(relocs[i].r_type == I860_RELOC_PAIR){
397		    fprintf(stderr, "Stray I860_RELOC_PAIR relocation entry "
398			    "%u\n", i);
399		    continue;
400		}
401		if((uint32_t)relocs[i].r_address == addr - sect_addr){
402		    rp = &relocs[i];
403		    if(rp->r_type == I860_RELOC_HIGH ||
404		       rp->r_type == I860_RELOC_HIGHADJ ||
405		       rp->r_type == I860_RELOC_SECTDIFF){
406			if(i+1 < nrelocs){
407			    pairp = &rp[1];
408			    if(pairp->r_type != I860_RELOC_PAIR){
409				fprintf(stderr, "No I860_RELOC_PAIR relocation "
410					"entry after entry %u\n", i);
411				rp = NULL;
412				pairp = NULL;
413				continue;
414			    }
415			}
416		    }
417		    break;
418		}
419		if(relocs[i].r_type == I860_RELOC_HIGH ||
420		   relocs[i].r_type == I860_RELOC_HIGHADJ ||
421		   relocs[i].r_type == I860_RELOC_SECTDIFF){
422		    if(i+1 >= nrelocs ||
423		       relocs[i+1].r_type != I860_RELOC_PAIR){
424			    fprintf(stderr, "No I860_RELOC_PAIR relocation "
425				    "entry after entry %u\n", i);
426		    }
427		    else
428			i++;
429		}
430	    }
431	}
432
433	/* Guess a prefix code for the immediate value */
434	prefix = NULL;
435	if((rp != NULL && rp->r_type == I860_RELOC_HIGH) || format == 'I')
436	    prefix = "h%";
437	else if((rp != NULL && rp->r_type == I860_RELOC_HIGHADJ) ||
438		format == 'J' )
439	    prefix = "ha%";
440	else if(rp != NULL && rp->r_type >= I860_RELOC_LOW0 &&
441			      rp->r_type <= I860_RELOC_SPLIT2){
442	    if(rp->r_pcrel == 0)	/* Don't use for bte insns */
443		prefix = "l%";
444	}
445	if(rp != NULL && (rp->r_type == I860_RELOC_HIGH ||
446			  rp->r_type == I860_RELOC_HIGHADJ)){
447	    if(pairp->r_type == I860_RELOC_PAIR){
448		if(rp->r_type == I860_RELOC_HIGHADJ)
449
450		    if(pairp->r_address & 0x8000)
451			addr_field = (addr_field << 16) +
452				     (0xffff0000 | (pairp->r_address & 0xffff));
453		    else
454			addr_field = (addr_field << 16) +
455				     (pairp->r_address & 0xffff);
456		else
457		    addr_field = (addr_field << 16) |
458				 (pairp->r_address & 0xffff);
459	    }
460	}
461	if(prefix != NULL)
462	    printf("%s", prefix);
463
464	if(format == 'K' || format == 'L'){ /* branch displacement */
465	    if(i860_print_symbol(addr + 4 + ((int32_t)addr_field), rp,
466		     symbols, nsymbols, sorted_symbols, nsorted_symbols,
467		     strings, strings_size, verbose) == TRUE)
468		return;
469	    printf(".%+d", (int32_t)(addr_field + 4));
470	    return;
471	}
472	if(i860_print_symbol(addr_field, rp, symbols, nsymbols,
473			     sorted_symbols, nsorted_symbols, strings,
474			     strings_size, verbose) == TRUE)
475	    return;
476
477	/* we can't find anything else to do with it. */
478	printf("0x%x", (unsigned int)addr_field);
479}
480
481/*
482 * i860_print_symbol prints a symbol name for the addr and relocation entry
483 * if a symbol exist with the same address.  Nothing else is printed, no
484 * whitespace, no newline.  If it prints something then it returns TRUE, else
485 * it returns FALSE.
486 */
487static
488enum bool
489i860_print_symbol(
490uint32_t value,
491struct relocation_info *rp,
492struct nlist *symbols,
493uint32_t nsymbols,
494struct symbol *sorted_symbols,
495uint32_t nsorted_symbols,
496char *strings,
497uint32_t strings_size,
498enum bool verbose)
499{
500    int32_t high, low, mid;
501
502	if(verbose == FALSE)
503	    return(FALSE);
504
505	if(rp != NULL){
506	    if(rp->r_extern &&
507	       rp->r_symbolnum < nsymbols){
508		if(value != 0)
509		    printf("%s+0x%x", strings +
510			   symbols[rp->r_symbolnum].n_un.n_strx,
511			   (unsigned int)value);
512		else
513		    printf("%s",strings + symbols[rp->r_symbolnum].n_un.n_strx);
514		return(TRUE);
515	    }
516	}
517
518	low = 0;
519	high = nsorted_symbols - 1;
520	mid = (high - low) / 2;
521	while(high >= low){
522	    if(sorted_symbols[mid].n_value == value){
523		printf("%s", sorted_symbols[mid].name);
524		return(TRUE);
525	    }
526	    if(sorted_symbols[mid].n_value > value){
527		high = mid - 1;
528		mid = (high + low) / 2;
529	    }
530	    else{
531		low = mid + 1;
532		mid = (high + low) / 2;
533	    }
534	}
535	return(FALSE);
536}
537