/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <assert.h>
#include <limits.h>
#include "enc_base.h"
#include "enc_wrapper.h"
#include "dec_base.h"
#include "utils/Log.h"
//#define PRINT_ENCODER_STREAM
bool dump_x86_inst = false;
//map_reg
const RegName map_of_regno_2_regname[] = {
RegName_EAX, RegName_EBX, RegName_ECX, RegName_EDX,
RegName_EDI, RegName_ESI, RegName_ESP, RegName_EBP,
RegName_XMM0, RegName_XMM1, RegName_XMM2, RegName_XMM3,
RegName_XMM4, RegName_XMM5, RegName_XMM6, RegName_XMM7,
RegName_Null, RegName_Null, RegName_Null, RegName_Null,
RegName_Null, RegName_Null, RegName_Null, RegName_Null,
RegName_Null,
RegName_Null, RegName_Null, RegName_Null, RegName_Null, RegName_Null,
RegName_Null, RegName_Null, RegName_Null, RegName_Null,
RegName_Null, RegName_Null, RegName_Null, RegName_Null,
RegName_Null, RegName_Null, //SCRATCH
RegName_Null, RegName_Null, RegName_Null, RegName_Null
};
//getRegSize, getAliasReg:
//OpndSize, RegName, OpndExt: enum enc_defs.h
inline void add_r(EncoderBase::Operands & args, int physicalReg, OpndSize sz, OpndExt ext = OpndExt_None) {
RegName reg = map_of_regno_2_regname[physicalReg];
if (sz != getRegSize(reg)) {
reg = getAliasReg(reg, sz);
}
args.add(EncoderBase::Operand(reg, ext));
}
inline void add_m(EncoderBase::Operands & args, int baseReg, int disp, OpndSize sz, OpndExt ext = OpndExt_None) {
args.add(EncoderBase::Operand(sz,
map_of_regno_2_regname[baseReg],
RegName_Null, 0,
disp, ext));
}
inline void add_m_scale(EncoderBase::Operands & args, int baseReg, int indexReg, int scale,
OpndSize sz, OpndExt ext = OpndExt_None) {
args.add(EncoderBase::Operand(sz,
map_of_regno_2_regname[baseReg],
map_of_regno_2_regname[indexReg], scale,
0, ext));
}
inline void add_m_disp_scale(EncoderBase::Operands & args, int baseReg, int disp, int indexReg, int scale,
OpndSize sz, OpndExt ext = OpndExt_None) {
args.add(EncoderBase::Operand(sz,
map_of_regno_2_regname[baseReg],
map_of_regno_2_regname[indexReg], scale,
disp, ext));
}
inline void add_fp(EncoderBase::Operands & args, unsigned i, bool dbl) {
return args.add((RegName)( (dbl ? RegName_FP0D : RegName_FP0S) + i));
}
inline void add_imm(EncoderBase::Operands & args, OpndSize sz, int value, bool is_signed) {
//assert(n_size != imm.get_size());
args.add(EncoderBase::Operand(sz, value,
is_signed ? OpndExt_Signed : OpndExt_Zero));
}
#define MAX_DECODED_STRING_LEN 1024
char tmpBuffer[MAX_DECODED_STRING_LEN];
void printOperand(const EncoderBase::Operand & opnd) {
unsigned int sz;
if(!dump_x86_inst) return;
sz = strlen(tmpBuffer);
if(opnd.size() != OpndSize_32) {
sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%s ",
getOpndSizeString(opnd.size()));
}
if(opnd.is_mem()) {
if(opnd.scale() != 0) {
sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz,
"%d(%s,%s,%d)", opnd.disp(),
getRegNameString(opnd.base()),
getRegNameString(opnd.index()), opnd.scale());
} else {
sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%d(%s)",
opnd.disp(), getRegNameString(opnd.base()));
}
}
if(opnd.is_imm()) {
sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "#%x",
(int)opnd.imm());
}
if(opnd.is_reg()) {
sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%s",
getRegNameString(opnd.reg()));
}
}
//TODO: the order of operands
//to make the printout have the same order as assembly in .S
//I reverse the order here
void printDecoderInst(Inst & decInst) {
unsigned int sz;
if(!dump_x86_inst) return;
sz = strlen(tmpBuffer);
sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%s ",
EncoderBase::toStr(decInst.mn));
for(unsigned int k = 0; k < decInst.argc; k++) {
if(k > 0) {
sz = strlen(tmpBuffer);
sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, ", ");
}
printOperand(decInst.operands[decInst.argc-1-k]);
}
ALOGE("%s", tmpBuffer);
}
void printOperands(EncoderBase::Operands& opnds) {
unsigned int sz;
if(!dump_x86_inst) return;
for(unsigned int k = 0; k < opnds.count(); k++) {
if(k > 0) {
sz = strlen(tmpBuffer);
sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, ", ");
}
printOperand(opnds[opnds.count()-1-k]);
}
}
void printEncoderInst(Mnemonic m, EncoderBase::Operands& opnds) {
if(!dump_x86_inst) return;
snprintf(tmpBuffer, MAX_DECODED_STRING_LEN, "--- ENC %s ",
EncoderBase::toStr(m));
printOperands(opnds);
ALOGE("%s", tmpBuffer);
}
int decodeThenPrint(char* stream_start) {
if(!dump_x86_inst) return 0;
snprintf(tmpBuffer, MAX_DECODED_STRING_LEN, "--- INST @ %p: ",
stream_start);
Inst decInst;
unsigned numBytes = DecoderBase::decode(stream_start, &decInst);
printDecoderInst(decInst);
return numBytes;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm(Mnemonic m, OpndSize size, int imm, char * stream) {
EncoderBase::Operands args;
//assert(imm.get_size() == size_32);
add_imm(args, size, imm, true/*is_signed*/);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT unsigned encoder_get_inst_size(char * stream) {
Inst decInst;
unsigned numBytes = DecoderBase::decode(stream, &decInst);
return numBytes;
}
extern "C" ENCODER_DECLARE_EXPORT unsigned encoder_get_cur_operand_offset(int opnd_id)
{
return (unsigned)EncoderBase::getOpndLocation(opnd_id);
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_update_imm(int imm, char * stream) {
Inst decInst;
unsigned numBytes = DecoderBase::decode(stream, &decInst);
EncoderBase::Operands args;
//assert(imm.get_size() == size_32);
add_imm(args, decInst.operands[0].size(), imm, true/*is_signed*/);
char* stream_next = (char *)EncoderBase::encode(stream, decInst.mn, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(decInst.mn, args);
decodeThenPrint(stream);
#endif
return stream_next;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem(Mnemonic m, OpndSize size,
int disp, int base_reg, bool isBasePhysical, char * stream) {
EncoderBase::Operands args;
add_m(args, base_reg, disp, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg(Mnemonic m, OpndSize size,
int reg, bool isPhysical, LowOpndRegType type, char * stream) {
EncoderBase::Operands args;
if(m == Mnemonic_IDIV || m == Mnemonic_MUL || m == Mnemonic_IMUL) {
add_r(args, 0/*eax*/, size);
add_r(args, 3/*edx*/, size);
}
add_r(args, reg, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
//both operands have same size
extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg_reg(Mnemonic m, OpndSize size,
int reg, bool isPhysical,
int reg2, bool isPhysical2, LowOpndRegType type, char * stream) {
if((m == Mnemonic_MOV || m == Mnemonic_MOVQ) && reg == reg2) return stream;
EncoderBase::Operands args;
add_r(args, reg2, size); //destination
if(m == Mnemonic_SAL || m == Mnemonic_SHR || m == Mnemonic_SHL || m == Mnemonic_SAR)
add_r(args, reg, OpndSize_8);
else
add_r(args, reg, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_reg(Mnemonic m, OpndSize size,
int disp, int base_reg, bool isBasePhysical,
int reg, bool isPhysical, LowOpndRegType type, char * stream) {
EncoderBase::Operands args;
add_r(args, reg, size);
add_m(args, base_reg, disp, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_scale_reg(Mnemonic m, OpndSize size,
int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
int reg, bool isPhysical, LowOpndRegType type, char * stream) {
EncoderBase::Operands args;
add_r(args, reg, size);
add_m_scale(args, base_reg, index_reg, scale, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg_mem_scale(Mnemonic m, OpndSize size,
int reg, bool isPhysical,
int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
LowOpndRegType type, char * stream) {
EncoderBase::Operands args;
add_m_scale(args, base_reg, index_reg, scale, size);
add_r(args, reg, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_disp_scale_reg(Mnemonic m, OpndSize size,
int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
int reg, bool isPhysical, LowOpndRegType type, char * stream) {
EncoderBase::Operands args;
add_r(args, reg, size);
add_m_disp_scale(args, base_reg, disp, index_reg, scale, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_movzs_mem_disp_scale_reg(Mnemonic m, OpndSize size,
int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
int reg, bool isPhysical, LowOpndRegType type, char * stream) {
EncoderBase::Operands args;
add_r(args, reg, OpndSize_32);
add_m_disp_scale(args, base_reg, disp, index_reg, scale, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char* encoder_reg_mem_disp_scale(Mnemonic m, OpndSize size,
int reg, bool isPhysical,
int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
LowOpndRegType type, char* stream) {
EncoderBase::Operands args;
add_m_disp_scale(args, base_reg, disp, index_reg, scale, size);
add_r(args, reg, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg_mem(Mnemonic m, OpndSize size,
int reg, bool isPhysical,
int disp, int base_reg, bool isBasePhysical, LowOpndRegType type, char * stream) {
EncoderBase::Operands args;
add_m(args, base_reg, disp, size);
add_r(args, reg, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm_reg(Mnemonic m, OpndSize size,
int imm, int reg, bool isPhysical, LowOpndRegType type, char * stream) {
EncoderBase::Operands args;
add_r(args, reg, size); //dst
if(m == Mnemonic_IMUL) add_r(args, reg, size); //src CHECK
if(m == Mnemonic_SAL || m == Mnemonic_SHR || m == Mnemonic_SHL
|| m == Mnemonic_SAR || m == Mnemonic_ROR) //fix for shift opcodes
add_imm(args, OpndSize_8, imm, true/*is_signed*/);
else
add_imm(args, size, imm, true/*is_signed*/);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_update_imm_rm(int imm, char * stream) {
Inst decInst;
unsigned numBytes = DecoderBase::decode(stream, &decInst);
EncoderBase::Operands args;
args.add(decInst.operands[0]);
add_imm(args, decInst.operands[1].size(), imm, true/*is_signed*/);
char* stream_next = (char *)EncoderBase::encode(stream, decInst.mn, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(decInst.mn, args);
decodeThenPrint(stream);
#endif
return stream_next;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm_mem(Mnemonic m, OpndSize size,
int imm,
int disp, int base_reg, bool isBasePhysical, char * stream) {
EncoderBase::Operands args;
add_m(args, base_reg, disp, size);
if (m == Mnemonic_SAL || m == Mnemonic_SHR || m == Mnemonic_SHL
|| m == Mnemonic_SAR || m == Mnemonic_ROR)
size = OpndSize_8;
add_imm(args, size, imm, true);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_fp_mem(Mnemonic m, OpndSize size, int reg,
int disp, int base_reg, bool isBasePhysical, char * stream) {
EncoderBase::Operands args;
add_m(args, base_reg, disp, size);
// a fake FP register as operand
add_fp(args, reg, size == OpndSize_64/*is_double*/);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_fp(Mnemonic m, OpndSize size,
int disp, int base_reg, bool isBasePhysical,
int reg, char * stream) {
EncoderBase::Operands args;
// a fake FP register as operand
add_fp(args, reg, size == OpndSize_64/*is_double*/);
add_m(args, base_reg, disp, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_return(char * stream) {
EncoderBase::Operands args;
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, Mnemonic_RET, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(Mnemonic_RET, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_compare_fp_stack(bool pop, int reg, bool isDouble, char * stream) {
//Mnemonic m = pop ? Mnemonic_FUCOMP : Mnemonic_FUCOM;
Mnemonic m = pop ? Mnemonic_FUCOMIP : Mnemonic_FUCOMI;
//a single operand or 2 operands?
//FST ST(i) has a single operand in encoder.inl?
EncoderBase::Operands args;
add_fp(args, reg, isDouble);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, m, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(m, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_movez_mem_to_reg(OpndSize size,
int disp, int base_reg, bool isBasePhysical,
int reg, bool isPhysical, char * stream) {
EncoderBase::Operands args;
add_r(args, reg, OpndSize_32);
add_m(args, base_reg, disp, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVZX, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(Mnemonic_MOVZX, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_moves_mem_to_reg(OpndSize size,
int disp, int base_reg, bool isBasePhysical,
int reg, bool isPhysical, char * stream) {
EncoderBase::Operands args;
add_r(args, reg, OpndSize_32);
add_m(args, base_reg, disp, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVSX, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(Mnemonic_MOVSX, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_movez_reg_to_reg(OpndSize size,
int reg, bool isPhysical, int reg2,
bool isPhysical2, LowOpndRegType type, char * stream) {
EncoderBase::Operands args;
add_r(args, reg2, OpndSize_32); //destination
add_r(args, reg, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVZX, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(Mnemonic_MOVZX, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
extern "C" ENCODER_DECLARE_EXPORT char * encoder_moves_reg_to_reg(OpndSize size,
int reg, bool isPhysical,int reg2,
bool isPhysical2, LowOpndRegType type, char * stream) {
EncoderBase::Operands args;
add_r(args, reg2, OpndSize_32); //destination
add_r(args, reg, size);
char* stream_start = stream;
stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVSX, args);
#ifdef PRINT_ENCODER_STREAM
printEncoderInst(Mnemonic_MOVSX, args);
decodeThenPrint(stream_start);
#endif
return stream;
}
// Disassemble the operand "opnd" and put the readable format in "strbuf"
// up to a string length of "len".
unsigned int DisassembleOperandToBuf(const EncoderBase::Operand& opnd, char* strbuf, unsigned int len)
{
unsigned int sz = 0;
if(opnd.size() != OpndSize_32) {
sz += snprintf(&strbuf[sz], len-sz, "%s ",
getOpndSizeString(opnd.size()));
}
if(opnd.is_mem()) {
if(opnd.scale() != 0) {
sz += snprintf(&strbuf[sz], len-sz, "%d(%s,%s,%d)", opnd.disp(),
getRegNameString(opnd.base()),
getRegNameString(opnd.index()), opnd.scale());
} else {
sz += snprintf(&strbuf[sz], len-sz, "%d(%s)",
opnd.disp(), getRegNameString(opnd.base()));
}
} else if(opnd.is_imm()) {
sz += snprintf(&strbuf[sz], len-sz, "#%x", (int)opnd.imm());
} else if(opnd.is_reg()) {
sz += snprintf(&strbuf[sz], len-sz, "%s",
getRegNameString(opnd.reg()));
}
return sz;
}
// Disassemble the instruction "decInst" and put the readable format
// in "strbuf" up to a string length of "len".
void DisassembleInstToBuf(Inst& decInst, char* strbuf, unsigned int len)
{
unsigned int sz = 0;
int k;
sz += snprintf(&strbuf[sz], len-sz, "%s ", EncoderBase::toStr(decInst.mn));
if (decInst.argc > 0) {
sz += DisassembleOperandToBuf(decInst.operands[decInst.argc-1],
&strbuf[sz], len-sz);
for(k = decInst.argc-2; k >= 0; k--) {
sz += snprintf(&strbuf[sz], len-sz, ", ");
sz += DisassembleOperandToBuf(decInst.operands[k], &strbuf[sz], len-sz);
}
}
}
// Disassmble the x86 instruction pointed to by code pointer "stream."
// Put the disassemble text in the "strbuf" up to string length "len".
// Return the code pointer after the disassemble x86 instruction.
extern "C" ENCODER_DECLARE_EXPORT
char* decoder_disassemble_instr(char* stream, char* strbuf, unsigned int len)
{
Inst decInst;
unsigned numBytes = DecoderBase::decode(stream, &decInst);
DisassembleInstToBuf(decInst, strbuf, len);
return (stream + numBytes);
}