cctools-port/cctools/otool/i860_disasm.c
Thomas Pöchtrager 32e30a232c cctools 886
2016-06-24 17:47:01 +02:00

539 lines
15 KiB
C

/*
* Copyright © 2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of Apple Inc. ("Apple") nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <stdio.h>
#include <string.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/reloc.h>
#include <mach-o/i860/reloc.h>
#include "stuff/symbol.h"
#include "stuff/bytesex.h"
#include "otool.h"
#include "../as/i860-opcode.h"
static void i860_dump_operands(
uint32_t opcode,
char *format,
uint32_t addr,
uint32_t sect_addr,
struct relocation_info *relocs,
uint32_t nrelocs,
struct nlist *symbols,
uint32_t nsymbols,
struct symbol *sorted_symbols,
uint32_t nsorted_symbols,
char *strings,
uint32_t strings_size,
enum bool verbose);
static void i860_dump_addr(
uint32_t addr_field,
int format,
int32_t addr,
uint32_t sect_addr,
struct relocation_info *relocs,
uint32_t nrelocs,
struct nlist *symbols,
uint32_t nsymbols,
struct symbol *sorted_symbols,
uint32_t nsorted_symbols,
char *strings,
uint32_t strings_size,
enum bool verbose);
static enum bool i860_print_symbol(
uint32_t value,
struct relocation_info *rp,
struct nlist *symbols,
uint32_t nsymbols,
struct symbol *sorted_symbols,
uint32_t nsorted_symbols,
char *strings,
uint32_t strings_size,
enum bool verbose);
/*
* Disassemble 1 instruction and return the length of the disassembled
* piece in bytes.
*/
uint32_t
i860_disassemble(
char *sect,
uint32_t left,
uint32_t addr,
uint32_t sect_addr,
enum byte_sex object_byte_sex,
struct relocation_info *relocs,
uint32_t nrelocs,
struct nlist *symbols,
uint32_t nsymbols,
struct symbol *sorted_symbols,
uint32_t nsorted_symbols,
char *strings,
uint32_t strings_size,
enum bool verbose)
{
enum byte_sex host_byte_sex;
enum bool swapped;
uint32_t opcode;
int isdual;
uint32_t i;
struct i860_opcode *op;
host_byte_sex = get_host_byte_sex();
swapped = host_byte_sex != object_byte_sex;
if(left < sizeof(uint32_t)){
if(left != 0){
memcpy(&opcode, sect, left);
if(swapped)
opcode = SWAP_INT(opcode);
printf(".long\t0x%08x\n", (unsigned int)opcode);
}
printf("(end of section)\n");
return(left);
}
memcpy(&opcode, sect, sizeof(uint32_t));
if(swapped)
opcode = SWAP_INT(opcode);
/*
* The pad opcode, 0, is chosen as an illegal insn to fault if
* executed
*/
if(opcode == 0){
printf("| Padded to i860 section boundary\n");
return(4);
}
isdual = 0;
/*
* See if this is a dual insn mode opcode.
* Turn off the dual mode bit and print a d. if appropriate.
*/
if((opcode & OP_PREFIX_MASK) == PREFIX_FPU ||
opcode == (OP_FNOP|DUAL_INSN_MODE_BIT)){
if(opcode & DUAL_INSN_MODE_BIT){
opcode &= ~DUAL_INSN_MODE_BIT;
isdual = 1;
}
}
/*
* Search the instruction table for a match for this opcode.
* We use a linear search because it's easy, uses the
* assembler insn tables, and I'm so lazy....
* Feel free to recode with whizzy hashes and such.
*/
op = (struct i860_opcode *)i860_opcodes;
for(i = 0; i < NUMOPCODES; i++, op++){
if((opcode & op->mask) == op->match){
if(isdual)
printf("d.%-12s\t", op->name);
else
printf("%-12s\t", op->name);
i860_dump_operands(opcode, (char *)op->args, addr, sect_addr,
relocs, nrelocs, symbols, nsymbols, sorted_symbols,
nsorted_symbols, strings, strings_size, verbose);
return(sizeof(uint32_t));
}
}
/* Didn't find the opcode. Dump it as a .long directive. */
/* Build it as a little-endian insn, in a format to match asm */
printf(".long\t0x%08x\n", (unsigned int)opcode);
return(sizeof(uint32_t));
}
/* 32 possible valuse, of which 6 are actually used. */
static char *i860_controlregs[] = {"fir", "psr", "dirbase", "db", "fsr", "epsr",
"?","?","?","?","?","?",
"?","?","?","?","?","?",
"?","?","?","?","?","?",
"?","?","?","?","?","?"};
static
void
i860_dump_operands(
uint32_t opcode,
char *format,
uint32_t addr,
uint32_t sect_addr,
struct relocation_info *relocs,
uint32_t nrelocs,
struct nlist *symbols,
uint32_t nsymbols,
struct symbol *sorted_symbols,
uint32_t nsorted_symbols,
char *strings,
uint32_t strings_size,
enum bool verbose)
{
uint32_t addr_field;
while(*format != '\0'){
switch(*format){
case '1': /* rs1 register, bits 11-15 of insn */
printf("r%u", GET_RS1(opcode));
break;
case '2': /* rs2 register, bits 21-25 of insn */
printf("r%u", GET_RS2(opcode));
break;
case 'd': /* rd register, bits 16-20 of insn */
printf("r%u", GET_RD(opcode));
break;
case 'E':
case 'e': /* frs1 floating point register, bits 11-15 of insn */
printf("f%u", GET_RS1(opcode));
break;
case 'F':
case 'f': /* frs2 floating point register, bits 21-25 of insn */
printf("f%u", GET_RS2(opcode));
break;
case 'H':
case 'G':
case 'g': /* frsd floating point register, bits 16-20 of insn */
printf("f%u", GET_RD(opcode));
break;
case 'I': /* 16 bit High portion of address, I860_RELOC_HIGH */
case 'J': /* 16 bit High portion of addr requiring adjustment */
addr_field = opcode & 0xFFFF;
i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
nrelocs, symbols, nsymbols, sorted_symbols,
nsorted_symbols, strings, strings_size, verbose);
break;
case 'i': /* 16 bit byte address low half */
addr_field = (opcode & 0xFFFF);
i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
nrelocs, symbols, nsymbols, sorted_symbols,
nsorted_symbols, strings, strings_size, verbose);
break;
case 'j': /* 16 bit short address, I860_RELOC_LOW1 */
addr_field = (opcode & 0xFFFE);
i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
nrelocs, symbols, nsymbols, sorted_symbols,
nsorted_symbols, strings, strings_size, verbose);
break;
case 'k': /* 16 bit word/int address low half, I860_RELOC_LOW2 */
addr_field = (opcode & 0xFFFC);
i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
nrelocs, symbols, nsymbols, sorted_symbols,
nsorted_symbols, strings, strings_size, verbose);
break;
case 'l': /* 16 bit 8-byte address (double) low half */
addr_field = (opcode & 0xFFF8);
i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
nrelocs, symbols, nsymbols, sorted_symbols,
nsorted_symbols, strings, strings_size, verbose);
break;
case 'm': /* 16 bit 16-byte address (quad) low half */
addr_field = (opcode & 0xFFF0);
i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
nrelocs, symbols, nsymbols, sorted_symbols,
nsorted_symbols, strings, strings_size, verbose);
break;
case 'n': /* 16 bit byte aligned low half, split fields */
addr_field = ((opcode >> 5) & 0xF800) | (opcode & 0x7FF);
i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
nrelocs, symbols, nsymbols, sorted_symbols,
nsorted_symbols, strings, strings_size, verbose);
break;
case 'o': /* 16 bit short aligned low half, split fields */
addr_field = ((opcode >> 5) & 0xF800) | (opcode & 0x7FE);
i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
nrelocs, symbols, nsymbols, sorted_symbols,
nsorted_symbols, strings, strings_size, verbose);
break;
case 'p': /* 16 bit int/word aligned low half, split fields */
addr_field = ((opcode >> 5) & 0xF800) | (opcode & 0x7FC);
i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
nrelocs, symbols, nsymbols, sorted_symbols,
nsorted_symbols, strings, strings_size, verbose);
break;
case 'K': /* 26 bit branch displacement */
addr_field = opcode & 0x3FFFFFF;
if(addr_field & 0x02000000) /* MSB set? */
addr_field |= 0xFC000000; /* Sign extend */
addr_field <<= 2; /* Convert to byte addr */
i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
nrelocs, symbols, nsymbols, sorted_symbols,
nsorted_symbols, strings, strings_size, verbose);
break;
case 'L': /* 16 bit split branch displacement */
addr_field = ((opcode >> 5) & 0xF800) | (opcode & 0x7FF);
if(addr_field & 0x8000) /* MSB set? */
addr_field |= 0xFFFF0000; /* Sign extend */
addr_field <<= 2; /* Convert to byte addr */
i860_dump_addr(addr_field, *format, addr, sect_addr, relocs,
nrelocs, symbols, nsymbols, sorted_symbols,
nsorted_symbols, strings, strings_size, verbose);
break;
case 'D': /* constant for shift opcode */
printf("%u", opcode & 0xFFFF);
break;
case 'B': /* 5 bit immediate, for bte and btne insn */
printf("%u", GET_RS1(opcode));
break;
case 'C': /* Control Register */
printf("%s", i860_controlregs[GET_RS2(opcode)]);
break;
default:
printf("%c", *format);
break;
}
++format;
}
printf("\n");
}
static
void
i860_dump_addr(
uint32_t addr_field,
int format,
int32_t addr,
uint32_t sect_addr,
struct relocation_info *relocs,
uint32_t nrelocs,
struct nlist *symbols,
uint32_t nsymbols,
struct symbol *sorted_symbols,
uint32_t nsorted_symbols,
char *strings,
uint32_t strings_size,
enum bool verbose)
{
uint32_t i;
struct relocation_info *rp, *pairp;
struct scattered_relocation_info *sreloc;
char *prefix;
rp = NULL;
pairp = NULL;
if(nrelocs){
for(i = 0; i < nrelocs; i++){
if(((relocs[i].r_address) & R_SCATTERED) != 0){
sreloc = (struct scattered_relocation_info *)(relocs + i);
if(sreloc->r_type == I860_RELOC_PAIR){
fprintf(stderr, "Stray I860_RELOC_PAIR relocation "
"entry %u\n", i);
continue;
}
if(sreloc->r_type == I860_RELOC_HIGH ||
sreloc->r_type == I860_RELOC_HIGHADJ ||
sreloc->r_type == I860_RELOC_SECTDIFF){
if(i+1 >= nrelocs ||
relocs[i+1].r_type != I860_RELOC_PAIR){
fprintf(stderr, "No I860_RELOC_PAIR relocation "
"entry after entry %u\n", i);
}
else{
if(((relocs[i+1].r_address) & R_SCATTERED) != 0){
sreloc = (struct scattered_relocation_info *)
(relocs + i + 1);
if(sreloc->r_type != I860_RELOC_PAIR)
fprintf(stderr, "No I860_RELOC_PAIR "
"relocation entry after entry "
"%u\n", i);
}
else if(relocs[i+1].r_type != I860_RELOC_PAIR){
fprintf(stderr, "No I860_RELOC_PAIR relocation "
"entry after entry %u\n", i);
}
i++;
continue;
}
}
}
if(relocs[i].r_type == I860_RELOC_PAIR){
fprintf(stderr, "Stray I860_RELOC_PAIR relocation entry "
"%u\n", i);
continue;
}
if((uint32_t)relocs[i].r_address == addr - sect_addr){
rp = &relocs[i];
if(rp->r_type == I860_RELOC_HIGH ||
rp->r_type == I860_RELOC_HIGHADJ ||
rp->r_type == I860_RELOC_SECTDIFF){
if(i+1 < nrelocs){
pairp = &rp[1];
if(pairp->r_type != I860_RELOC_PAIR){
fprintf(stderr, "No I860_RELOC_PAIR relocation "
"entry after entry %u\n", i);
rp = NULL;
pairp = NULL;
continue;
}
}
}
break;
}
if(relocs[i].r_type == I860_RELOC_HIGH ||
relocs[i].r_type == I860_RELOC_HIGHADJ ||
relocs[i].r_type == I860_RELOC_SECTDIFF){
if(i+1 >= nrelocs ||
relocs[i+1].r_type != I860_RELOC_PAIR){
fprintf(stderr, "No I860_RELOC_PAIR relocation "
"entry after entry %u\n", i);
}
else
i++;
}
}
}
/* Guess a prefix code for the immediate value */
prefix = NULL;
if((rp != NULL && rp->r_type == I860_RELOC_HIGH) || format == 'I')
prefix = "h%";
else if((rp != NULL && rp->r_type == I860_RELOC_HIGHADJ) ||
format == 'J' )
prefix = "ha%";
else if(rp != NULL && rp->r_type >= I860_RELOC_LOW0 &&
rp->r_type <= I860_RELOC_SPLIT2){
if(rp->r_pcrel == 0) /* Don't use for bte insns */
prefix = "l%";
}
if(rp != NULL && (rp->r_type == I860_RELOC_HIGH ||
rp->r_type == I860_RELOC_HIGHADJ)){
if(pairp->r_type == I860_RELOC_PAIR){
if(rp->r_type == I860_RELOC_HIGHADJ)
if(pairp->r_address & 0x8000)
addr_field = (addr_field << 16) +
(0xffff0000 | (pairp->r_address & 0xffff));
else
addr_field = (addr_field << 16) +
(pairp->r_address & 0xffff);
else
addr_field = (addr_field << 16) |
(pairp->r_address & 0xffff);
}
}
if(prefix != NULL)
printf("%s", prefix);
if(format == 'K' || format == 'L'){ /* branch displacement */
if(i860_print_symbol(addr + 4 + ((int32_t)addr_field), rp,
symbols, nsymbols, sorted_symbols, nsorted_symbols,
strings, strings_size, verbose) == TRUE)
return;
printf(".%+d", (int32_t)(addr_field + 4));
return;
}
if(i860_print_symbol(addr_field, rp, symbols, nsymbols,
sorted_symbols, nsorted_symbols, strings,
strings_size, verbose) == TRUE)
return;
/* we can't find anything else to do with it. */
printf("0x%x", (unsigned int)addr_field);
}
/*
* i860_print_symbol prints a symbol name for the addr and relocation entry
* if a symbol exist with the same address. Nothing else is printed, no
* whitespace, no newline. If it prints something then it returns TRUE, else
* it returns FALSE.
*/
static
enum bool
i860_print_symbol(
uint32_t value,
struct relocation_info *rp,
struct nlist *symbols,
uint32_t nsymbols,
struct symbol *sorted_symbols,
uint32_t nsorted_symbols,
char *strings,
uint32_t strings_size,
enum bool verbose)
{
int32_t high, low, mid;
if(verbose == FALSE)
return(FALSE);
if(rp != NULL){
if(rp->r_extern &&
rp->r_symbolnum < nsymbols){
if(symbols[rp->r_symbolnum].n_un.n_strx > strings_size)
return(FALSE);
if(value != 0)
printf("%s+0x%x", strings +
symbols[rp->r_symbolnum].n_un.n_strx,
(unsigned int)value);
else
printf("%s",strings + symbols[rp->r_symbolnum].n_un.n_strx);
return(TRUE);
}
}
low = 0;
high = nsorted_symbols - 1;
mid = (high - low) / 2;
while(high >= low){
if(sorted_symbols[mid].n_value == value){
printf("%s", sorted_symbols[mid].name);
return(TRUE);
}
if(sorted_symbols[mid].n_value > value){
high = mid - 1;
mid = (high + low) / 2;
}
else{
low = mid + 1;
mid = (high + low) / 2;
}
}
return(FALSE);
}