kolibrios/programs/develop/objconv/disasm2.cpp
turbocat 60a4b1c9ef Added objconv port.
git-svn-id: svn://kolibrios.org@9683 a494cfbc-eb01-0410-851d-a64ba20cac60
2022-02-06 11:09:00 +00:00

4336 lines
155 KiB
C++

/**************************** disasm2.cpp ********************************
* Author: Agner Fog
* Date created: 2007-02-25
* Last modified: 2016-11-27
* Project: objconv
* Module: disasm2.cpp
* Description:
* Module for disassembler containing file output functions
*
* Changes that relate to assembly language syntax should be done in this file only.
*
* Copyright 2007-2016 GNU General Public License http://www.gnu.org/licenses
*****************************************************************************/
#include "stdafx.h"
/********************** Warning and error texts ***************************
These texts are inserted in disassembled code in case of warnings or errors.
The occurrence of an error makes the disassembler mark the code block between
the nearest known code labels as dubious. This means that the byte sequence
might be data in the code segment or the disassembler might be out of phase
with instruction boundaries. Dubious code will be shown both as code and as
data.
A warning will be shown as 'Note:' before the instruction it applies to.
This might indicate suboptimal coding or a possible cause for concern.
The criteria for distinguishing between warnings and errors is not the
severity of consequences, but whether the condition is likely to be caused
by common programming errors or by data in the code segment.
Some of the warning messages are quite benign, e.g. an unnecessary prefix.
Other warning messages can have severe consequences, e.g. a function missing
a return statement.
Still other warnings are no case for concern, but a condition requiring
attention. For example the message: "Multi-byte NOP. Replace with ALIGN",
might actually indicate a well optimized code. But it requires attention
because the assembler cannot re-create the multi-byte NOP if the code
is assembled again. The programmer needs to decide what level of alignment
is optimal and replace the NOP with an align statement.
*****************************************************************************/
// Define error texts.
SIntTxt AsmErrorTexts[] = {
{1, "Instruction longer than 15 bytes"},
{2, "Lock prefix not allowed for this opcode"},
{4, "Illegal opcode"},
{8, "Illegal operands for this opcode"},
{0x10, "Instruction extends beyond end of code block"},
{0x20, "Prefix after REX prefix not allowed"},
{0x40, "This instruction is not allowed in 64 bit mode"},
{0x80, "Instruction out of phase with next label"},
{0x100, "Attempt to use R13 as base register without displacement"},
{0x200, "Register 8 - 15 only allowed in 64 bit mode (Ignored)."},
{0x400, "REX prefix not allowed on instruction with DREX byte"},
{0x800, "VEX has X bit but no SIB byte (Probably ignored)"},
{0x1000, "Relocation source does not match address or operand field"},
{0x2000, "Overlapping relocations"},
{0x4000, "This is unlikely to be code"}, // Consecutive bytes of 0 found
{0x8000, "VEX.L bit not allowed here"},
{0x10000, "VEX.mmmm bits out of range"},
{0x80000, "Internal error in opcode table in opcodes.cpp"}
};
// Warning texts 1: Warnings about conditions that could be intentional and suboptimal code
SIntTxt AsmWarningTexts1[] = {
{1, "Immediate operand could be made smaller by sign extension"},
{2, "Immediate operand could be made smaller by zero extension"},
{4, "Zero displacement could be omitted"},
{8, "Displacement could be made smaller by sign extension"},
{0x10, "SIB byte unnecessary here"},
{0x20, "A shorter instruction exists for register operand"},
{0x40, "Length-changing prefix causes delay on Intel processors"},
{0x80, "Address size prefix should be avoided"},
{0x100, "Same prefix occurs more than once"},
{0x200, "Prefix valid but unnecessary"},
{0x400, "Prefix bit or byte has no meaning in this context"},
{0x800, "Contradicting prefixes"},
{0x1000, "Required prefix missing"},
{0x2000, "Address has scale factor but no index register"},
{0x4000, "Address is not rip-relative"},
{0x8000, "Absolute memory address without relocation"},
{0x10000, "Unusual relocation type for this operand"},
{0x20000, "Instruction pointer truncated by operand size prefix"},
{0x40000, "Stack pointer truncated by address size prefix"},
{0x80000, "Jump or call to data segment not allowed"},
{0x100000, "Undocumented opcode"},
{0x200000, "Unknown opcode reserved for future extensions"},
{0x400000, "Memory operand is misaligned. Performance penalty"},
{0x800000, "Alignment fault. Memory operand must be aligned"},
{0x1000000, "Multi-byte NOP. Replace with ALIGN"},
{0x2000000, "Bogus length-changing prefix causes delay on Intel processors here"},
{0x4000000, "Non-default size for stack operation"},
{0x8000000, "Function does not end with ret or jmp"},
{0x10000000, "No jump seems to point here"},
{0x20000000, "Full 64-bit address"},
{0x40000000, "VEX prefix bits not allowed here"}
};
// Warning texts 2: Warnings about possible misinterpretation; serious warnings
SIntTxt AsmWarningTexts2[] = {
{1, "Label out of phase with instruction. Possibly spurious"},
{2, "Planned future instruction, according to preliminary specification"},
{4, "This instruction has been planned but never implemented because plans were changed. Will not work"},
{0x10, "EVEX prefix not allowed for this instruction"},
{0x20, "MVEX prefix not allowed for this instruction"},
{0x40, "EVEX prefix option bits not allowed here"},
{0x80, "MVEX prefix option bits not allowed here"},
{0x100, "Mask register must be nonzero"},
{0x200, "Broadcasting to scalar not allowd"},
};
// Indication of relocation types in comments:
SIntTxt RelocationTypeNames[] = {
{0x001, "(d)" }, // Direct address in flat address space
{0x002, "(rel)" }, // Self-relative
{0x004, "(imgrel)" }, // Image-relative
{0x008, "(segrel)" }, // Segment-relative
{0x010, "(refpoint)" }, // Relative to arbitrary point (position-independent code in Mach-O)
{0x021, "(d)" }, // Direct (adjust by image base)
{0x041, "(d)" }, // Direct (make procecure linkage table entry)
{0x081, "(indirect)" }, // Gnu indirect function dispatcher (make procecure linkage table entry?)
{0x100, "(seg)" }, // Segment address or descriptor
{0x200, "(sseg)" }, // Segment of symbol
{0x400, "(far)" }, // Far segment:offset address
{0x1001, "(GOT)" }, // GOT entry
{0x1002, "(GOT r)" }, // self-relative to GOT entry
{0x2002, "(PLT r)" } // self-relative to PLT entry
};
// Instruction set names
const char * InstructionSetNames[] = {
"8086", "80186", "80286", "80386", // 0 - 3
"80486", "Pentium", "Pentium Pro", "MMX", // 4 - 7
"Pentium II", "", "", "", // 8 - B
"", "", "", "", // C - F
"", "SSE", "SSE2", "SSE3", // 10 - 13
"Supplementary SSE3", "SSE4.1", "SSE4.2", "AES", // 14 - 17
"CLMUL", "AVX", "FMA3", "?", // 18 - 1B
"AVX2", "BMI etc.", "?", "?", // 1C - 1F
"AVX-512", "AVX512PF/ER/CD", "MPX,SHA,TBD", "AVX512IFMA/VBMI", // 20 - 23
"AVX512_4FMAPS", "?", "?", "?", // 24 - 27
"?", "?", "?", "?", // 28 - 2B
"?", "?", "?", "?", // 2C - 2F
"?", "?", "?", "?", // 30 - 33
"?", "?", "?", "?", // 34 - 37
"?", "?", "?", "?", // 38 - 3B
"?", "?", "?", "?", // 3C - 3F
"?", "?", "?", "?", // 40 - 43
"?", "?", "?", "?", // 44 - 47
"?", "?", "?", "?", // 48 - 4B
"?", "?", "?", "?", // 4C - 4F
"?", "?", "?", "?", // 50 - 53
"?", "?", "?", "?", // 54 - 57
"?", "?", "?", "?", // 58 - 5B
"?", "?", "?", "?", // 5C - 5F
"?", "?", "?", "?", // 60 - 63
"?", "?", "?", "?", // 64 - 67
"?", "?", "?", "?", // 68 - 6B
"?", "?", "?", "?", // 6C - 6F
"?", "?", "?", "?", // 70 - 73
"?", "?", "?", "?", // 74 - 77
"?", "?", "?", "?", // 78 - 7B
"?", "?", "?", "?", // 7C - 7F
"Knights Corner", "?", "?", "?", // 80 - 83
"?", "?", "?", "?" // 84 - 87
};
const int InstructionSetNamesLen = TableSize(InstructionSetNames);
/************************** class CDisassembler *****************************
Most member functions of CDisassembler are defined in disasm1.cpp
Only the functions that produce output are defined here:
******************************************************************************/
void CDisassembler::WriteShortRegOperand(uint32 Type) {
// Write register operand from lower 3 bits of opcode byte to OutFile
uint32 rnum = Get<uint8>(s.OpcodeStart2) & 7;
// Check REX.B prefix
if (s.Prefixes[7] & 1) rnum |= 8; // Add 8 if REX.B prefix
// Write register name
WriteRegisterName(rnum, Type);
}
void CDisassembler::WriteRegOperand(uint32 Type) {
// Write register operand from reg bits
uint32 Num = s.Reg; // Register number
// Write register name
WriteRegisterName(Num, Type);
}
void CDisassembler::WriteRMOperand(uint32 Type) {
// Write memory or register operand from mod/rm bits of mod/reg/rm byte
// and possibly SIB byte or direct memory operand to OutFile.
// Also used for writing direct memory operand
if ((Type & 0xFF) == 0) {
// No explicit operand
return;
}
uint32 Components = 0; // Count number of addends inside []
int64 Addend = 0; // Inline displacement or addend
int AddressingMode = 0; // 0: 16- or 32 bit addressing mode
// 1: 64-bit pointer
// 2: 32-bit absolute in 64-bit mode
// 4: 64-bit rip-relative
// 8: 64-bit absolute
// Check if register or memory
if (s.Mod == 3) {
// Register operand
WriteRegisterName(s.RM, Type);
return;
}
// Find addend, if any
switch (s.AddressFieldSize) {
case 1: // 1 byte displacement
Addend = Get<int8>(s.AddressField);
break;
case 2: // 2 bytes displacement
Addend = Get<int16>(s.AddressField);
break;
case 4: // 4 bytes displacement
Addend = Get<int32>(s.AddressField);
if ((s.MFlags & 0x100) && !s.AddressRelocation) {
// rip-relative
Addend += ImageBase + uint64(SectionAddress + IEnd);
}
break;
case 8: // 8 bytes address
Addend = Get<int64>(s.AddressField);
break;
}
// Get AddressingMode
if (s.AddressSize > 32) {
if (s.MFlags & 0x100) {
AddressingMode = 4; // 64-bit rip-relative
}
else if (s.AddressFieldSize == 8) {
AddressingMode = 8; // 64-bit absolute
}
else if (s.AddressRelocation || (s.BaseReg==0 && s.IndexReg==0)) {
AddressingMode = 2; // 32-bit absolute in 64-bit mode
}
else {
AddressingMode = 1; // 64-bit pointer
}
}
// Make exception for LEA with no type
if (Opcodei == 0x8D) {
Type = 0;
}
// Write type override
if ((s.OpcodeDef->InstructionFormat & 0x1F) == 0x1E) {
WriteOperandType(Type & 0xFF); // has vsib address: write element type rather than vector type
}
else if (!(s.OpcodeDef->Options & 0x800)) {
WriteOperandType(Type); // write operand type
}
if (Syntax != SUBTYPE_MASM) {
// Write "[" around memory operands, before segment
OutFile.Put("[");
}
// Write segment prefix, if any
if (s.Prefixes[0]) {
OutFile.Put(RegisterNamesSeg[GetSegmentRegisterFromPrefix()]);
OutFile.Put(":");
}
else if (!s.BaseReg && !s.IndexReg && (!s.AddressRelocation || (s.Warnings1 & 0x10000)) && Syntax != SUBTYPE_YASM) {
// No pointer register and no memory reference or wrong type of memory reference.
// Write segment register to indicate that we have a memory operand
OutFile.Put("DS:");
}
if (Syntax == SUBTYPE_MASM) {
// Write "[" around memory operands, after segment
OutFile.Put("[");
}
if (Syntax == SUBTYPE_YASM && (AddressingMode & 0x0E)) {
// Specify absolute or relative addressing mode
switch (AddressingMode) {
case 2: OutFile.Put("abs "); break;
case 4: OutFile.Put("rel "); break;
case 8: OutFile.Put("abs qword "); break;
}
}
// Write relocation target, if any
if (s.AddressRelocation) {
// Write cross reference
WriteRelocationTarget(s.AddressRelocation, 4 | (s.MFlags & 0x100), Addend);
// Addend has been written, don't write it again
Addend = 0;
// Remember that something has been written
Components++;
}
// Check address size for pointer registers
//const char * * PointerRegisterNames;
uint32 RegisterType = 0;
switch (s.AddressSize) {
case 16:
RegisterType = 2; break;
case 32:
RegisterType = 3; break;
case 64:
RegisterType = 4; break;
}
// Write base register, if any
if (s.BaseReg) {
if (Components++) OutFile.Put("+"); // Put "+" if anything before
WriteRegisterName(s.BaseReg - 1, RegisterType);
}
// Write index register, if any
if (s.IndexReg) {
if (Components++) OutFile.Put("+"); // Put "+" if anything before
if ((s.OpcodeDef->InstructionFormat & 0x1F) != 0x1E) {
// normal index register
WriteRegisterName(s.IndexReg - 1, RegisterType);
}
else {
// VSIB byte specifies vector index register
WriteRegisterName(s.IndexReg - 1, Type & 0xF00);
}
// Write scale factor, if any
if (s.Scale) {
OutFile.Put("*");
OutFile.PutDecimal(1 << s.Scale);
}
}
// Write +/- before addend
if (Components && Addend) {
// Displacement comes after base/index registers
if (Addend >= 0 || s.AddressFieldSize == 8) {
// Positive. Write +
OutFile.Put("+");
}
else {
// Negative. Write -
OutFile.Put("-");
Addend = -Addend;
}
}
if (Addend || Components == 0) {
// Find minimum number of digits needed
uint32 AddendSize = s.AddressFieldSize;
if ((uint64)Addend < 0x100 && AddendSize > 1) AddendSize = 1;
else if ((uint64)Addend < 0x10000 && AddendSize > 2) AddendSize = 2;
// Write address or addend as hexadecimal
OutFile.PutHex((uint64)Addend, 2);
// Check if offset multiplier needed
if (s.OffsetMultiplier && s.AddressFieldSize == 1 && Addend) {
OutFile.Put("*");
OutFile.PutHex(s.OffsetMultiplier, 2);
}
}
if (Syntax == SUBTYPE_GASM && (AddressingMode == 4)) {
// Need to specify rip-relative address
OutFile.Put("+rip");
}
// End with "]"
OutFile.Put("]");
}
void CDisassembler::WriteOperandType(uint32 type) {
switch (Syntax) {
case SUBTYPE_MASM:
WriteOperandTypeMASM(type); break;
case SUBTYPE_YASM:
WriteOperandTypeYASM(type); break;
case SUBTYPE_GASM:
WriteOperandTypeGASM(type); break;
}
}
void CDisassembler::WriteOperandTypeMASM(uint32 type) {
// Write type override before operand, e.g. "dword ", MASM syntax
if (type & 0xF00) {
type &= 0xF00; // Ignore element type for vectors
}
else {
type &= 0xFF; // Use operand type only
}
switch (type) {
case 1: // 8 bits
OutFile.Put("byte "); break;
case 2: // 16 bits
OutFile.Put("word "); break;
case 3: // 32 bits
OutFile.Put("dword "); break;
case 4: // 64 bits
OutFile.Put("qword "); break;
case 5: // 80 bits
if ((s.OpcodeDef->Destination & 0xFF) == 0xD) {
// 64+16 bit far pointer. Not supported by MASM
OutFile.Put("fword ");
s.OpComment = "64+16 bit. Need REX.W prefix";
}
else {
OutFile.Put("tbyte ");}
break;
case 6: case 0x40: case 0x48: case 0:
// Other size. Write nothing
break;
case 7: case 0x0D: // 48 bits or far
OutFile.Put("fword ");
if ((s.OpcodeDef->Destination & 0xFF) == 0xD && WordSize == 64) {
// All assemblers I have tried forget the REX.W prefix here. Make a notice
s.OpComment = "32+16 bit. Possibly forgot REX.W prefix";
}
break;
case 0x4A: // 16 bits float
OutFile.Put("word "); break;
case 0x43: // 32 bits float (x87)
case 0x4B: // 32 bits float (SSE2)
OutFile.Put("dword "); break;
case 0x44: // 64 bits float
case 0x4C: // 64 bits float (SSE2)
OutFile.Put("qword "); break;
case 0x45: // 80 bits float
OutFile.Put("tbyte "); break;
case 0x84: case 0x85: // far call
OutFile.Put("far "); break;
case 0x95: // 16 bits mask register
OutFile.Put("word "); break;
case 0x300: // MMX
OutFile.Put("qword "); break;
case 0x400: // XMM
OutFile.Put("xmmword "); break;
case 0x500: // YMM
OutFile.Put("ymmword "); break;
case 0x600: // ZMM
OutFile.Put("zmmword "); break;
case 0x700: // future 1024 bit
OutFile.Put("?mmword "); break;
}
OutFile.Put("ptr ");
}
void CDisassembler::WriteOperandTypeYASM(uint32 type) {
// Write type override before operand, e.g. "dword", NASM/YASM syntax
if (type & 0xF00) {
type &= 0xF00; // Ignore element type for vectors
}
else {
type &= 0xFF; // Use operand type only
}
uint32 Dest = s.OpcodeDef->Destination & 0xFF;// Destination operand
if (Dest >= 0xB && Dest < 0x10) {
// This is a pointer
if (Dest < 0x0D) {
OutFile.Put("near "); // Near indirect jump/call
}
else {
// Far pointer
if ((WordSize == 16 && type == 3) || (WordSize == 32 && type == 7)) {
OutFile.Put("far ");
}
else {
// Size currently not supported by YASM
switch (type) {
case 3: OutFile.Put("far ");
s.OpComment = "16+16 bit. Needs 66H prefix";
break;
case 7: OutFile.Put("far ");
s.OpComment = "32+16 bit. Possibly forgot REX.W prefix";
break;
case 5: OutFile.Put("far ");
s.OpComment = "64+16 bit. Needs REX.W prefix";
break;
}
}
}
return;
}
switch (type) {
case 1: // 8 bits
OutFile.Put("byte "); break;
case 2: // 16 bits
OutFile.Put("word "); break;
case 3: // 32 bits
OutFile.Put("dword "); break;
case 4: // 64 bits
OutFile.Put("qword "); break;
case 5: // 80 bits
OutFile.Put("tbyte "); break;
case 7: // 48 bits
OutFile.Put("fword "); break;
case 0x4A: // 16 bits float
OutFile.Put("word "); break;
case 0x43: // 32 bits float (x87)
case 0x4B: // 32 bits float (SSE2)
OutFile.Put("dword "); break;
case 0x44: // 64 bits float
case 0x4C: // 64 bits float (SSE2)
OutFile.Put("qword "); break;
case 0x45: // 80 bits float
OutFile.Put("tbyte "); break;
case 0x84: case 0x85: // far call
OutFile.Put("far "); break;
case 0x95: // 16 bits mask register
OutFile.Put("word "); break;
case 0x300: // MMX
OutFile.Put("qword "); break;
case 0x400: // XMM
OutFile.Put("oword "); break;
case 0x500: // YMM
OutFile.Put("yword "); break;
case 0x600: // ZMM
OutFile.Put("zword "); break;
case 0x700: // Future 128 bytes
OutFile.Put("?word "); break;
default:; // Anything else: write nothing
}
}
void CDisassembler::WriteOperandTypeGASM(uint32 type) {
// Write type override before operand, e.g. "dword ", GAS syntax
if (type & 0xF00) {
type &= 0xF00; // Ignore element type for vectors
}
else {
type &= 0xFF; // Use operand type only
}
switch (type) {
case 1: // 8 bits
OutFile.Put("byte "); break;
case 2: // 16 bits
OutFile.Put("word "); break;
case 3: // 32 bits
OutFile.Put("dword "); break;
case 4: // 64 bits
OutFile.Put("qword "); break;
case 5: // 80 bits
if ((s.OpcodeDef->Destination & 0xFF) == 0xD) {
// 64+16 bit far pointer. Not supported by Gas
OutFile.Put("fword ");
s.OpComment = "64+16 bit. Needs REX.W prefix";
}
else {
OutFile.Put("tbyte ");}
break;
case 6: case 0x40: case 0x48: case 0:
// Other size. Write nothing
break;
case 7: // 48 bits
OutFile.Put("fword ");
if ((s.OpcodeDef->Destination & 0xFF) == 0xD && WordSize == 64) {
// All assemblers I have tried forget the REX.W prefix here. Make a notice
s.OpComment = "32+16 bit. Possibly forgot REX.W prefix";
}
break;
case 0x4A: // 16 bits float
OutFile.Put("word "); break;
case 0x43: // 32 bits float (x87)
case 0x4B: // 32 bits float (SSE2)
OutFile.Put("dword "); break;
case 0x44: // 64 bits float
case 0x4C: // 64 bits float (SSE2)
OutFile.Put("qword "); break;
case 0x45: // 80 bits float
OutFile.Put("tbyte "); break;
case 0x84: case 0x85: // far call
OutFile.Put("far "); break;
case 0x95: // 16 bits mask register
OutFile.Put("word "); break;
case 0x300: // MMX
OutFile.Put("qword "); break;
case 0x400: // XMM
OutFile.Put("xmmword "); break;
case 0x500: // YMM
OutFile.Put("ymmword "); break;
case 0x600: // ZMM
OutFile.Put("zmmword "); break;
case 0x700: // future 1024 bit
OutFile.Put("?mmword "); break;
}
}
void CDisassembler::WriteDREXOperand(uint32 Type) {
// Write register operand from dest bits of DREX byte (AMD only)
uint32 Num = s.Vreg >> 4; // Register number
// Write register name
WriteRegisterName(Num, Type);
}
void CDisassembler::WriteVEXOperand(uint32 Type, int i) {
// Write register operand from VEX.vvvv bits or immediate bits
uint32 Num; // Register number
switch (i) {
case 0: // Use VEX.vvvv bits
Num = s.Vreg & 0x1F; break;
case 1: // Use immediate bits 4-7
Num = Get<uint8>(s.ImmediateField) >> 4; break;
case 2: // Use immediate bits 0-3 (Unused. For possible future use)
Num = Get<uint8>(s.ImmediateField) & 0x0F; break;
default:
Num = 0;
}
// Write register name
WriteRegisterName(Num, Type);
}
void CDisassembler::WriteOperandAttributeEVEX(int i, int isMem) {
// Write operand attributes and instruction attributes from EVEX z, LL, b and aaa bits
// i = operand number (0 = destination, 1 = first source, 2 = second source,
// 98 = after last SIMD operand, 99 = after last operand)
// isMem: true if memory operand, false if register operand
uint32 swiz = s.OpcodeDef->EVEX; // indicates meaning of EVEX attribute bits
if ((swiz & 0x30) && (i == 0 || (s.OpcodeDef->Destination == 0 && i == 1))) { // first operand
// write mask
if (s.Kreg || (swiz & 0xC0)) {
OutFile.Put(" {k");
OutFile.PutDecimal(s.Kreg);
OutFile.Put("}");
if ((swiz & 0x20) && (s.Esss & 8)) {
// zeroing
OutFile.Put("{z}");
}
}
}
if (swiz & 0x07) {
// broadcast, rounding or sae allowed
if (isMem && i < 8) {
// memory operand
if ((swiz & 0x01) && (s.Esss & 1)) {
// write memory broadcast
// calculate broadcast factor
uint32 op = s.Operands[i]; // operand
uint32 elementsize = GetDataElementSize(op); // element size
uint32 opv = s.Operands[0]; // any vector operand
if (!(opv & 0xF00)) opv = s.Operands[1]; // first operand is not a vector, use next
uint32 vectorsize = GetDataItemSize(opv); // vector size
if (vectorsize > elementsize) { // avoid broadcasting to scalar
if (elementsize) { // avoid division by zero
OutFile.Put(" {1to");
OutFile.PutDecimal(vectorsize/elementsize);
OutFile.Put("}");
}
else {
OutFile.Put("{unknown broadcast}");
}
}
}
}
if (i == 98 && s.Mod == 3) { // after last SIMD operand. no memory operand
// NASM has rounding mode and sae decoration after last SIMD operand with a comma.
// No spec. for other assemblers available yet (2014).
// use i == 99 if it should be placed after last operand.
// Perhaps the comma should be removed for other assemblers?
if ((swiz & 0x4) && (s.Esss & 1)) {
// write rounding mode
uint32 rounding = (s.Esss >> 1) & 3;
OutFile.Put(", {");
OutFile.Put(EVEXRoundingNames[rounding]);
OutFile.Put("}");
}
else if ((swiz & 0x2) && (s.Esss & 1)) {
// no rounding mode. write sae
OutFile.Put(", {");
OutFile.Put(EVEXRoundingNames[4]);
OutFile.Put("}");
}
}
}
}
void CDisassembler::WriteOperandAttributeMVEX(int i, int isMem) {
// Write operand attributes and instruction attributes from MVEX sss, e and kkk bits.
// i = operand number (0 = destination, 1 = first source, 2 = second source, 99 = after last operand)
// isMem: true if memory operand, false if register operand
uint32 swiz = s.OpcodeDef->MVEX; // indicates meaning of MVEX attribute bits
const int R_sae_syntax = 0; // syntax alternatives for rounding mode + sae
// 0: {rn-sae}, 1: {rn}{sae}
const char * text = 0; // temporary text pointer
if ((swiz & 0x1000) && (i == 0 || (s.OpcodeDef->Destination == 0 && i == 1))) { // first operand
// write mask
if (s.Kreg || (swiz & 0x2000)) {
OutFile.Put(" {k");
OutFile.PutDecimal(s.Kreg);
OutFile.Put("}");
}
}
if (swiz & 0x1F) {
// swizzle allowed
if (isMem && i < 90) {
// write memory broadcast/up/down conversion
text = s.SwizRecord->name;
if (text && *text) {
OutFile.Put(" {"); OutFile.Put(text); OutFile.Put("}");
}
}
//if (i == 2 || ((s.OpcodeDef->Source2 & 0xF0F00) == 0 && i == 1)) {
if (i == 98) { // after last SIMD operand
// last register or memory operand
if (s.Mod == 3 && !((swiz & 0x700) && (s.Esss & 8))) { // skip alternative meaning of sss field for register operand when E=1
// write register swizzle
text = s.SwizRecord->name;
if (text && *text) {
OutFile.Put(" {"); OutFile.Put(text); OutFile.Put("}");
}
}
}
if (i == 99) { // after last operand
if (s.Mod == 3 && (swiz & 0x300) && (s.Esss & 8)) {
// alternative meaning of sss field for register operand when E=1
switch (swiz & 0x300) {
case 0x100: // rounding mode and not sae
text = SwizRoundTables[0][0][s.Esss & 3].name;
break;
case 0x200: // suppress all exceptions
if ((s.Esss & 4) && !(swiz & 0x800)) text = "sae";
break;
case 0x300: // rounding mode and sae
text = SwizRoundTables[0][R_sae_syntax][s.Esss & 7].name;
break;
}
}
if (text && *text) {
OutFile.Put(", {"); OutFile.Put(text); OutFile.Put("}");
}
}
}
if (isMem && (s.Esss & 8) && !(swiz & 0x800)) {
// cache eviction hint after memory operand
OutFile.Put(" {eh}");
}
}
void CDisassembler::WriteRegisterName(uint32 Value, uint32 Type) {
// Write name of register to OutFile
if (Type & 0xF00) {
// vector register
Type &= 0xF00;
}
else {
// Other register
Type &= 0xFF; // Remove irrelevant bits
}
// Check fixed registers (do not depend on Value)
switch (Type) {
case 0xA1: // al
Type = 1; Value = 0;
break;
case 0xA2: // ax
Type = 2; Value = 0;
break;
case 0xA3: // eax
Type = 3; Value = 0;
break;
case 0xA4: // rax
Type = 4; Value = 0;
break;
case 0xAE: // xmm0
Type = 0x400; Value = 0;
break;
case 0xAF: // st(0)
Type = 0x40; Value = 0;
break;
case 0xB2: // dx
Type = 2; Value = 2;
break;
case 0xB3: // cl
Type = 1; Value = 1;
break;
}
// Get register number limit
uint32 RegNumLimit = 7; // largest register number
if (WordSize >= 64) {
RegNumLimit = 15;
if ((s.Prefixes[6] & 0x40) && (Type & 0xF40)) {
// EVEX or MVEX prefix and vector
RegNumLimit = 31;
}
}
switch (Type) {
case 0x91: // segment register
RegNumLimit = 5;
break;
case 0x300: // mmx
case 0x40: // st register
case 0x95: // k mask register
RegNumLimit = 7;
break;
case 0x98: // bounds register
RegNumLimit = 3;
break;
}
if (Value > RegNumLimit) {
// register number out of range
OutFile.Put("unknown register ");
switch (Type) {
case 1:
OutFile.Put("(8 bit) "); break;
case 2:
OutFile.Put("(16 bit) "); break;
case 3:
OutFile.Put("(32 bit) "); break;
case 4:
OutFile.Put("(64 bit) "); break;
case 0x40: // st register
OutFile.Put("st"); break;
case 0x91: // Segment register
OutFile.Put("seg"); break;
case 0x92: // Control register
OutFile.Put("cr"); break;
case 0x95: // k mask register
OutFile.Put("k"); break;
case 0x300: // mmx register
OutFile.Put("mm"); break;
case 0x400: // xmm register
OutFile.Put("xmm"); break;
case 0x500: // ymm register
OutFile.Put("ymm"); break;
case 0x600: // zmm register
OutFile.Put("zmm"); break;
case 0x700: // future 1024 bit register
OutFile.Put("?mm"); break;
}
OutFile.PutDecimal(Value);
}
else {
// Write register name depending on type
switch (Type) {
case 1: // 8 bit register. Depends on any REX prefix
OutFile.Put(s.Prefixes[7] ? RegisterNames8x[Value] : RegisterNames8[Value & 7]);
break;
case 2: // 16 bit register
OutFile.Put(RegisterNames16[Value]);
break;
case 3: // 32 bit register
OutFile.Put(RegisterNames32[Value]);
break;
case 4: // 64 bit register
OutFile.Put(RegisterNames64[Value]);
break;
case 0x300: // mmx register
OutFile.Put("mm");
OutFile.PutDecimal(Value);
break;
case 0x400: // xmm register (packed integer or float)
case 0x48: case 0x4B: case 0x4C: // xmm register (scalar float)
OutFile.Put("xmm");
OutFile.PutDecimal(Value);
break;
case 0x500: // ymm register (packed)
OutFile.Put("ymm");
OutFile.PutDecimal(Value);
break;
case 0x600: // zmm register (packed)
OutFile.Put("zmm");
OutFile.PutDecimal(Value);
break;
case 0x700: // future 1024 bit register
OutFile.Put("?mm");
OutFile.PutDecimal(Value);
break;
case 0x40: // st register
if (Syntax == SUBTYPE_YASM) {
// NASM, YASM and GAS-AT&T use st0
OutFile.Put("st");
OutFile.PutDecimal(Value);
}
else {
// MASM and GAS-Intel use st(0),
OutFile.Put("st(");
OutFile.PutDecimal(Value);
OutFile.Put(")");
}
break;
case 0x91: // Segment register
OutFile.Put(RegisterNamesSeg[Value & 7]);
break;
case 0x92: // Control register
OutFile.Put(RegisterNamesCR[Value]);
break;
case 0x93: // Debug register
OutFile.Put("dr");
OutFile.PutDecimal(Value);
break;
case 0x94: // Test register (obsolete)
OutFile.Put("tr");
OutFile.PutDecimal(Value);
break;
case 0x95: // k mask register
OutFile.Put("k");
OutFile.PutDecimal(Value);
break;
case 0x98: // bounds register
OutFile.Put("bnd");
OutFile.PutDecimal(Value);
break;
case 0xB1: // 1
OutFile.Put("1");
break;
default: // Unexpected
OutFile.Put("UNKNOWN REGISTER TYPE ");
OutFile.PutDecimal(Value);
break;
}
}
}
void CDisassembler::WriteImmediateOperand(uint32 Type) {
// Write immediate operand or direct jump/call address
int WriteFormat; // 0: unsigned, 1: signed, 2: hexadecimal
int Components = 0; // Number of components in immediate operand
uint32 OSize; // Operand size
uint32 FieldPointer; // Pointer to field containing value
uint32 FieldSize; // Size of field containing value
int64 Value = 0; // Value of immediate operand
// Check if far
if ((Type & 0xFE) == 0x84) {
// Write far
WriteOperandType(Type);
}
// Check if type override needed
if ((s.OpcodeDef->AllowedPrefixes & 2) && s.Prefixes[4] == 0x66
&& (Opcodei == 0x68 || Opcodei == 0x6A)) {
// Push immediate with non-default operand size needs type override
WriteOperandType(s.OperandSize == 16 ? 2 : 3);
}
FieldPointer = s.ImmediateField;
FieldSize = s.ImmediateFieldSize;
if (Syntax == SUBTYPE_YASM && (Type & 0x0F) == 4 && FieldSize == 8) {
// Write type override to make sure we get 8 bytes address in case there is a relocation here
WriteOperandType(4);
}
if (Type & 0x200000) {
if (FieldSize > 1) {
// Uses second part of field. Single byte only
FieldPointer += FieldSize-1;
FieldSize = 1;
}
else {
// Uses half a byte
FieldSize = 0;
}
}
// Get inline value
switch (FieldSize) {
case 0: // 4 bits
Value = Get<uint8>(FieldPointer) & 0x0F;
break;
case 1: // 8 bits
Value = Get<int8>(FieldPointer);
break;
case 2: // 16 bits
Value = Get<int16>(FieldPointer); break;
case 6: // 48 bits
Value = Get<int32>(FieldPointer);
Value += (uint64)Get<uint16>(FieldPointer + 4) << 32;
break;
case 4: // 32 bits
Value = Get<int32>(FieldPointer); break;
case 8: // 64 bits
Value = Get<int64>(FieldPointer); break;
case 3: // 16+8 bits ("Enter" instruction)
if ((Type & 0xFF) == 0x12) {
// First 16 bits
FieldSize = 2; Value = Get<int16>(FieldPointer); break;
}
// else continue in default case to get error message
default: // Other sizes should not occur
err.submit(3000); Value = -1;
}
// Check if relocation
if (s.ImmediateRelocation) {
// Write relocation target name
uint32 Context = 2;
if ((Type & 0xFC) == 0x80) Context = 8; // Near jump/call destination
if ((Type & 0xFC) == 0x84) Context = 0x10; // Far jump/call destination
// Write cross reference
WriteRelocationTarget(s.ImmediateRelocation, Context, Value);
// Remember that Value has been written
Value = 0;
Components++;
}
// Check if AAM or AAD
if (Value == 10 && (Opcodei & 0xFE) == 0xD4) {
// Don't write operand for AAM or AAD if = 10
return;
}
// Write as unsigned, signed or hexadecimal:
if ((Type & 0xF0) == 0x30 || (Type & 0xF0) == 0x80) {
// Hexadecimal
WriteFormat = 2;
}
else if (s.ImmediateFieldSize == 8) {
// 64 bit constant
if (Value == (int32)Value) {
// Signed
WriteFormat = 1;
}
else {
// Hexadecimal
WriteFormat = 2;
}
}
else if ((Type & 0xF0) == 0x20) {
// Signed
WriteFormat = 1;
}
else {
// Unsigned
WriteFormat = 0;
}
if ((Type & 0xFC) == 0x80 && !s.ImmediateRelocation) {
// Self-relative jump or call without relocation. Adjust immediate value
Value += IEnd; // Get absolute address of target
// Look for symbol at target address
uint32 ISymbol = Symbols.FindByAddress(Section, (uint32)Value);
if (ISymbol && (Symbols[ISymbol].Name || CodeMode == 1)) {
// Symbol found. Write its name
OutFile.Put(Symbols.GetName(ISymbol));
// No offset to write
return;
}
// Target address has no name
Type |= 0x4000; // Write target as hexadecimal
}
// Operand size
if ((s.Operands[0] & 0xFFF) <= 0xA || (s.Operands[0] & 0xF0) == 0xA0) {
// Destination is general purpose register
OSize = s.OperandSize;
}
else {
// Constant probably unrelated to destination size
OSize = 8;
}
// Check if destination is 8 bit operand
//if ((s.Operands[0] & 0xFF) == 1 || (s.Operands[0] & 0xFF) == 0xA1) OSize = 8;
// Check if sign extended
if (OSize > s.ImmediateFieldSize * 8) {
if (WriteFormat == 2 && Value >= 0) {
// Hexadecimal sign extended, not negative:
// Does not need full length
OSize = s.ImmediateFieldSize * 8;
}
else if (WriteFormat == 0) {
// Unsigned and sign extended, change to signed
WriteFormat = 1;
}
}
if (Components) {
// There was a relocated name
if (Value) {
// Addend to relocation is not zero
if (Value > 0 || WriteFormat != 1) {
OutFile.Put("+"); // Put "+" between name and addend
}
else {
OutFile.Put("-"); // Put "-" between name and addend
Value = - Value; // Change sign to avoid another "-"
}
}
else {
// No addend to relocated name
return;
}
}
// Write value
if (WriteFormat == 2) {
// Write with hexadecimal number appropriate size
switch (OSize) {
case 8: // 8 bits
OutFile.PutHex((uint8)Value, 1); break;
case 16: // 16 bits
if ((Type & 0xFC) == 0x84) {
// Segment of far call
OutFile.PutHex((uint16)(Value >> 16), 1);
OutFile.Put(':');
}
OutFile.PutHex((uint16)Value, 2); break;
case 32: // 32 bits
default: // Should not occur
if ((Type & 0xFC) == 0x84) {
// Segment of far call
OutFile.PutHex((uint16)(Value >> 32), 1);
OutFile.Put(':');
}
OutFile.PutHex((uint32)Value, 2); break;
case 64: // 64 bits
OutFile.PutHex((uint64)Value, 2); break;
}
}
else {
// Write as signed or unsigned decimal
if (WriteFormat == 0) { // unsigned
switch (OSize) {
case 8: // 8 bits
Value &= 0x00FF; break;
case 16: // 16 bits
Value &= 0xFFFF; break;
}
}
OutFile.PutDecimal((int32)Value, WriteFormat); // Write value. Signed or usigned decimal
}
}
void CDisassembler::WriteOtherOperand(uint32 Type) {
// Write other type of operand
const char * * OpRegisterNames; // Pointer to list of register names
uint32 RegI = 0; // Index into list of register names
switch (Type & 0x8FF) {
case 0xA1: // AL
OpRegisterNames = RegisterNames8;
break;
case 0xA2: // AX
OpRegisterNames = RegisterNames16;
break;
case 0xA3: // EAX
OpRegisterNames = RegisterNames32;
break;
case 0xA4: // RAX
OpRegisterNames = RegisterNames64;
break;
case 0xAE: // xmm0
OutFile.Put("xmm0");
return;
case 0xAF: // ST(0)
OutFile.Put("st(0)");
return;
case 0xB1: // 1
OutFile.Put("1");
return;
case 0xB2: // DX
OpRegisterNames = RegisterNames16;
RegI = 2;
break;
case 0xB3: // CL
OpRegisterNames = RegisterNames8;
RegI = 1;
break;
default:
OutFile.Put("unknown operand");
err.submit(3000);
return;
}
// Write register name
OutFile.Put(OpRegisterNames[RegI]);
}
void CDisassembler::WriteErrorsAndWarnings() {
// Write errors, warnings and comments, if any
uint32 n; // Error bit
if (s.Errors) {
// There are errors
// Loop through all bits in s.Errors
for (n = 1; n; n <<= 1) {
if (s.Errors & n) {
if (OutFile.GetColumn()) OutFile.NewLine();
OutFile.Put(CommentSeparator); // Write "\n; "
OutFile.Put("Error: "); // Write "Error: "
OutFile.Put(Lookup(AsmErrorTexts,n));// Write error text
OutFile.NewLine();
}
}
}
if (s.Warnings1) {
// There are warnings 1
// Loop through all bits in s.Warnings1
for (n = 1; n; n <<= 1) {
if (s.Warnings1 & n) {
if (OutFile.GetColumn()) OutFile.NewLine();
OutFile.Put(CommentSeparator); // Write "; "
OutFile.Put("Note: "); // Write "Note: "
OutFile.Put(Lookup(AsmWarningTexts1, n));// Write warning text
OutFile.NewLine();
}
}
}
if (s.Warnings2) {
// There are warnings 2
// Loop through all bits in s.Warnings2
for (n = 1; n; n <<= 1) {
if (s.Warnings2 & n) {
if (OutFile.GetColumn()) OutFile.NewLine();
OutFile.Put(CommentSeparator); // Write "; "
OutFile.Put("Warning: "); // Write "Warning: "
OutFile.Put(Lookup(AsmWarningTexts2, n)); // Write warning text
OutFile.NewLine();
}
}
if (s.Warnings2 & 1) {
// Write spurious label
uint32 sym1 = Symbols.FindByAddress(Section, LabelEnd);
if (sym1) {
const char * name = Symbols.GetName(sym1);
OutFile.Put(CommentSeparator);
OutFile.Put(name);
OutFile.Put("; Misplaced symbol at address ");
OutFile.PutHex(Symbols[sym1].Offset);
OutFile.NewLine();
}
}
}
if (s.OpcodeDef && (s.OpcodeDef->AllowedPrefixes & 8) && !s.Warnings1) {
if (s.Prefixes[0]) {
// Branch hint prefix. Write comment
OutFile.Put(CommentSeparator); // Write "; "
switch (s.Prefixes[0]) {
case 0x2E:
OutFile.Put("Branch hint prefix for Pentium 4: Predict no jump");
break;
case 0x3E:
OutFile.Put("Branch hint prefix for Pentium 4: Predict jump");
break;
case 0x64:
OutFile.Put("Branch hint prefix for Pentium 4: Predict alternate");
break;
default:
OutFile.Put("Note: Unrecognized branch hint prefix");
}
OutFile.NewLine();
}
}
}
void CDisassembler::WriteSymbolName(uint32 symi) {
// Write symbol name. symi = new symbol index
OutFile.Put(Symbols.GetName(symi));
}
void CDisassembler::WriteSectionName(int32 SegIndex) {
// Write name of section, segment or group from section index
const char * Name = 0;
// Check for special index values
switch (SegIndex) {
case ASM_SEGMENT_UNKNOWN: // Unknown segment. Typical for external symbols
Name = "Unknown"; break;
case ASM_SEGMENT_ABSOLUTE: // No segment. Used for absolute symbols
Name = "Absolute"; break;
case ASM_SEGMENT_FLAT: // Flat segment group
Name = "flat"; break;
case ASM_SEGMENT_NOTHING: // No segment
Name = "Nothing"; break;
case ASM_SEGMENT_ERROR: // Segment register assumed to error
Name = "Error"; break;
case ASM_SEGMENT_IMGREL: // Segment unknown. Offset relative to image base or file base
Name = "ImageBased"; break;
default: // > 0 means normal segment index
if ((uint32)SegIndex >= Sections.GetNumEntries()) {
// Out of range
Name = "IndexOutOfRange";
}
else {
// Get index into NameBuffer
uint32 NameIndex = Sections[SegIndex].Name;
// Check if valid
if (NameIndex == 0 || NameIndex >= NameBuffer.GetDataSize()) {
Name = "ErrorNameMissing";
}
else {
// Normal valid name of segment, section or group
Name = NameBuffer.Buf() + NameIndex;
}
}
break;
}
if (Syntax == SUBTYPE_YASM && Name[0] == '_') {
// Change leading underscore to dot
OutFile.Put('.');
OutFile.Put(Name+1); // Write rest of name
}
else {
// Write name
OutFile.Put(Name);
}
}
void CDisassembler::WriteDataItems() {
// Write data items to output file
int LineState; // 0: Start of new line, write label
// 1: Label written if any, write data directive
// 2: Data directive written, write data
// 3: First data item written, write comma and more data
// 4: Last data item written, write comment
// 5: Comment written if any, start new line
uint32 Pos = IBegin; // Current position
uint32 LinePos = IBegin; // Position for beginning of output line
uint32 BytesPerLine; // Number of bytes to write per line
uint32 LineEnd; // Data position for end of line
uint32 DataEnd; // End of data
uint32 ElementSize, OldElementSize; // Size of each data element
uint32 RelOffset; // Offset of relocation
uint32 irel, Oldirel; // Relocation index
int64 Value; // Inline value or addend
const char * Symname; // Symbol name
int SeparateLine; // Label is on separate line
SARelocation Rel; // Dummy relocation record
// Check if size is valid
if (DataSize == 0) DataSize = 1;
if (DataSize > 32) DataSize = 32;
// Expected end position
if (CodeMode & 3) {
// Writing data for dubious code. Make same length as code instruction
DataEnd = IEnd;
}
else {
// Regular data. End at next label
DataEnd = LabelEnd;
if (DataEnd > FunctionEnd) DataEnd = FunctionEnd;
if (DataEnd <= Pos) DataEnd = Pos + DataSize;
if (DataEnd > Sections[Section].InitSize && Pos < Sections[Section].InitSize) {
DataEnd = Sections[Section].InitSize;
}
}
// Size of each data element
ElementSize = DataSize;
// Check if packed type
if (DataType & 0xF00) {
// This is a packed vector type. Get element size
ElementSize = GetDataElementSize(DataType);
}
// Avoid sizes that are not powers of 2
if (ElementSize == 6 || ElementSize == 10) ElementSize = 2;
// Set maximum element size to 8
if (ElementSize > 8) ElementSize = 8;
// Set minimum element size to 1
if (ElementSize < 1) ElementSize = 1;
if (Pos + ElementSize > DataEnd) {
// Make sure we end at DataEnd
ElementSize = 1; BytesPerLine = 8;
LineEnd = DataEnd;
}
// Set number of bytes per line
BytesPerLine = (DataSize == 10) ? 10 : 8;
if (!(CodeMode & 3)) {
// Begin new line for each data item (except in code segment)
OutFile.NewLine();
}
LineState = 0; irel = 0;
// Check if alignment required
if (DataSize >= 16 && (DataType & 0xC00) && (DataType & 0xFF) != 0x51
&& (FlagPrevious & 0x100) < (DataSize << 4) && !(IBegin & (DataSize-1))) {
// Write align directive
WriteAlign(DataSize);
// Remember that data is aligned
FlagPrevious |= (DataSize << 4);
}
// Get symbol name for label
uint32 sym; // Current symbol index
uint32 sym1, sym2 = 0; // First and last symbol at current address
sym1 = Symbols.FindByAddress(Section, Pos, &sym2);
// Loop for one or more symbols at this address
for (sym = sym1; sym <= sym2; sym++) {
if (sym && Symbols[sym].Scope && !(Symbols[sym].Scope & 0x100) && !(Symbols[sym].Type & 0x80000000)) {
// Prepare for writing symbol label
Symname = Symbols.GetName(sym); // Symbol name
// Check if label needs a separate line
SeparateLine = (ElementSize != DataSize
|| Symbols[sym].Size != DataSize
|| strlen(Symname) > AsmTab1
|| sym < sym2
// || (Sections[Section].Type & 0xFF) == 3
|| ((Symbols[sym].Type+1) & 0xFE) == 0x0C);
// Write symbol label
switch (Syntax) {
case SUBTYPE_MASM:
WriteDataLabelMASM(Symname, sym, SeparateLine); break;
case SUBTYPE_YASM:
WriteDataLabelYASM(Symname, sym, SeparateLine); break;
case SUBTYPE_GASM:
WriteDataLabelGASM(Symname, sym, SeparateLine); break;
}
LineState = 1; // Label written
if (SeparateLine) {
LineState = 0;
}
}
}
if ((Sections[Section].Type & 0xFF) == 3 || Pos >= Sections[Section].InitSize) {
// This is an unitialized data (BSS) section
// Data repeat count
uint32 DataCount = (DataEnd - Pos) / ElementSize;
if (DataCount) {
OutFile.Tabulate(AsmTab1);
// Write data directives
switch (Syntax) {
case SUBTYPE_MASM:
WriteUninitDataItemsMASM(ElementSize, DataCount); break;
case SUBTYPE_YASM:
WriteUninitDataItemsYASM(ElementSize, DataCount); break;
case SUBTYPE_GASM:
WriteUninitDataItemsGASM(ElementSize, DataCount); break;
}
// Write comment
WriteDataComment(ElementSize, Pos, Pos, 0);
OutFile.NewLine();
LineState = 0;
}
// Update data position
Pos += DataCount * ElementSize;
if (Pos < DataEnd) {
// Some odd data remain. Write as bytes
DataCount = DataEnd - Pos;
ElementSize = 1;
OutFile.Tabulate(AsmTab1);
switch (Syntax) {
case SUBTYPE_MASM:
WriteUninitDataItemsMASM(ElementSize, DataCount); break;
case SUBTYPE_YASM:
WriteUninitDataItemsYASM(ElementSize, DataCount); break;
case SUBTYPE_GASM:
WriteUninitDataItemsGASM(ElementSize, DataCount); break;
}
// Write comment
WriteDataComment(ElementSize, Pos, Pos, 0);
OutFile.NewLine();
Pos = DataEnd;
LineState = 0;
}
}
else {
// Not a BSS section
// Label has been written, write data
// Loop for one or more elements
LinePos = Pos;
while (Pos < DataEnd) {
// Find end of line position
LineEnd = LinePos + BytesPerLine;
// Remember element size and relocation
OldElementSize = ElementSize;
Oldirel = irel;
// Check if relocation
Rel.Section = Section;
Rel.Offset = Pos;
uint32 irel = Relocations.FindFirst(Rel);
if (irel >= Relocations.GetNumEntries() || Relocations[irel].Section != (int32)Section) {
// No relevant relocation
irel = 0;
}
if (irel) {
// A relocation is found
// Check relocation source
RelOffset = Relocations[irel].Offset;
if (RelOffset == Pos) {
// Relocation source is here
// Make sure the size fits and begin new line
ElementSize = Relocations[irel].Size; BytesPerLine = 8;
if (ElementSize < 1) ElementSize = WordSize / 8;
if (ElementSize < 1) ElementSize = 4;
LineEnd = Pos + ElementSize;
if (LineState > 2) LineState = 4; // Make sure we begin at new line
}
else if (RelOffset < Pos + ElementSize) {
// Relocation source begins before end of element with current ElementSize
// Change ElementSize to make sure a new element begins at relocation source
ElementSize = 1; BytesPerLine = 8;
LineEnd = RelOffset;
if (LineState > 2) LineState = 4; // Make sure we begin at new line
irel = 0;
}
else {
// Relocation is after this element
irel = 0;
}
// Check for overlapping relocations
if (irel && irel+1 < Relocations.GetNumEntries()
&& Relocations[irel+1].Section == (int32)Section
&& Relocations[irel+1].Offset < RelOffset + ElementSize) {
// Overlapping relocations
s.Errors |= 0x2000;
WriteErrorsAndWarnings();
LineEnd = Relocations[irel+1].Offset;
if (LineState > 2) LineState = 4; // Make sure we begin at new line
}
// Drop alignment
FlagPrevious &= ~0xF00;
}
if (irel == 0) {
// No relocation here
// Check if DataEnd would be exceeded
if (Pos + ElementSize > DataEnd) {
// Make sure we end at DataEnd unless there is a relocation source here
ElementSize = 1; BytesPerLine = 8;
LineEnd = DataEnd;
if (LineState > 2) LineState = 4; // Make sure we begin at new line
FlagPrevious &= ~0xF00; // Drop alignment
}
}
// Check if new line needed
if (LineState == 4) {
// Finish this line
if (!(CodeMode & 3)) {
WriteDataComment(OldElementSize, LinePos, Pos, Oldirel);
}
// Start new line
OutFile.NewLine();
LineState = 0;
LinePos = Pos;
continue;
}
// Tabulate
OutFile.Tabulate(AsmTab1);
if (LineState < 2) {
// Write data definition directive for appropriate size
switch (Syntax) {
case SUBTYPE_MASM:
WriteDataDirectiveMASM(ElementSize); break;
case SUBTYPE_YASM:
WriteDataDirectiveYASM(ElementSize); break;
case SUBTYPE_GASM:
WriteDataDirectiveGASM(ElementSize); break;
}
LineState = 2;
}
else if (LineState == 3) {
// Not the first element, write comma
OutFile.Put(", ");
}
// Get inline value
switch (ElementSize) {
case 1: Value = Get<int8>(Pos); break;
case 2: Value = Get<int16>(Pos); break;
case 4: Value = Get<int32>(Pos); break;
case 6: Value = Get<uint32>(Pos) + ((uint64)Get<uint16>(Pos+4) << 32); break;
case 8: Value = Get<int64>(Pos); break;
case 10: Value = Get<int64>(Pos); break;
default: Value = 0; // should not occur
}
if (irel) {
// There is a relocation here. Write the name etc.
WriteRelocationTarget(irel, 1, Value);
}
else {
// Write value
switch (ElementSize) {
case 1:
OutFile.PutHex((uint8)Value, 1);
break;
case 2:
OutFile.PutHex((uint16)Value, 1);
break;
case 4:
OutFile.PutHex((uint32)Value, 1);
break;
case 6:
OutFile.PutHex((uint16)(Value >> 32), 1);
OutFile.Put(":");
OutFile.PutHex((uint32)Value, 1);
break;
case 8:
OutFile.PutHex((uint64)Value, 1);
break;
case 10:
OutFile.Put("??");
break;
}
}
LineState = 3;
// Increment position
Pos += ElementSize;
// Check if end of line
if (Pos >= LineEnd || Pos >= DataEnd) LineState = 4;
if (LineState == 4) {
// End of line
if (!(CodeMode & 3)) {
// Write comment
WriteDataComment(ElementSize, LinePos, Pos, irel);
}
OutFile.NewLine();
LinePos = Pos;
LineState = 0;
}
}
}
// Indicate end
if (IEnd < Pos) IEnd = Pos;
if (IEnd > LabelEnd) IEnd = LabelEnd;
if (IEnd > FunctionEnd && FunctionEnd) IEnd = FunctionEnd;
// Reset FlagPrevious if not aligned
if (DataSize < 16 || (DataType & 0xFF) == 0x28) FlagPrevious = 0;
}
void CDisassembler::WriteDataLabelMASM(const char * name, uint32 sym, int line) {
// Write label before data item, MASM syntax
// name = name of data item(s)
// sym = symbol index
// line = 1 if label is on separate line, 0 if data follows on same line
// Write name
OutFile.Put(name);
// At least one space
OutFile.Put(" ");
// Tabulate
OutFile.Tabulate(AsmTab1);
if (line) {
// Write label and type on seperate line
// Get size
uint32 Symsize = Symbols[sym].Size;
if (Symsize == 0) Symsize = DataSize;
OutFile.Put("label ");
// Write type
switch(Symsize) {
case 1: default:
OutFile.Put("byte"); break;
case 2:
OutFile.Put("word"); break;
case 4:
OutFile.Put("dword"); break;
case 6:
OutFile.Put("fword"); break;
case 8:
OutFile.Put("qword"); break;
case 10:
OutFile.Put("tbyte"); break;
case 16:
OutFile.Put("xmmword"); break;
case 32:
OutFile.Put("ymmword"); break;
}
// Check if jump table or call table
if (((Symbols[sym].Type+1) & 0xFE) == 0x0C) {
OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
if (Symbols[sym].DLLName) {
// DLL import
OutFile.Put("import from ");
OutFile.Put(Symbols.GetDLLName(sym));
}
else if (Symbols[sym].Type & 1) {
OutFile.Put("switch/case jump table");
}
else {
OutFile.Put("virtual table or function pointer");
}
}
// New line
OutFile.NewLine();
}
}
void CDisassembler::WriteDataLabelYASM(const char * name, uint32 sym, int line) {
// Write label before data item, YASM syntax
// name = name of data item(s)
// sym = symbol index
// line = 1 if label is on separate line, 0 if data follows on same line
// Write name and colon
OutFile.Put(name);
OutFile.Put(": ");
// Tabulate
OutFile.Tabulate(AsmTab1);
if (line) {
// Write label on seperate line
// Write comment
OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
// Check if jump table or call table
if (((Symbols[sym].Type+1) & 0xFE) == 0x0C) {
if (Symbols[sym].DLLName) {
// DLL import
OutFile.Put("import from ");
OutFile.Put(Symbols.GetDLLName(sym));
}
else if (Symbols[sym].Type & 1) {
OutFile.Put("switch/case jump table");
}
else {
OutFile.Put("virtual table or function pointer");
}
}
else {
// Write size
uint32 Symsize = Symbols[sym].Size;
if (Symsize == 0) Symsize = DataSize;
switch(Symsize) {
case 1: default:
OutFile.Put("byte"); break;
case 2:
OutFile.Put("word"); break;
case 4:
OutFile.Put("dword"); break;
case 6:
OutFile.Put("fword"); break;
case 8:
OutFile.Put("qword"); break;
case 10:
OutFile.Put("tbyte"); break;
case 16:
OutFile.Put("oword"); break;
case 32:
OutFile.Put("yword"); break;
case 64:
OutFile.Put("zword"); break;
}
}
// New line
OutFile.NewLine();
}
}
void CDisassembler::WriteDataLabelGASM(const char * name, uint32 sym, int line) {
// Write label before data item, GAS syntax
// name = name of data item(s)
// sym = symbol index
// line = 1 if label is on separate line, 0 if data follows on same line
// Write name and colon
OutFile.Put(name);
OutFile.Put(": ");
// Tabulate
OutFile.Tabulate(AsmTab1);
if (line) {
// Write label on seperate line
// Write comment
OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
// Check if jump table or call table
if (((Symbols[sym].Type+1) & 0xFE) == 0x0C) {
if (Symbols[sym].DLLName) {
// DLL import
OutFile.Put("import from ");
OutFile.Put(Symbols.GetDLLName(sym));
}
else if (Symbols[sym].Type & 1) {
OutFile.Put("switch/case jump table");
}
else {
OutFile.Put("virtual table or function pointer");
}
}
else {
// Write size
uint32 Symsize = Symbols[sym].Size;
if (Symsize == 0) Symsize = DataSize;
switch(Symsize) {
case 1: default:
OutFile.Put("byte"); break;
case 2:
OutFile.Put("word"); break;
case 4:
OutFile.Put("int"); break;
case 6:
OutFile.Put("farword"); break;
case 8:
OutFile.Put("qword"); break;
case 10:
OutFile.Put("tfloat"); break;
case 16:
OutFile.Put("xmmword"); break;
case 32:
OutFile.Put("ymmword"); break;
}
}
// New line
OutFile.NewLine();
}
}
void CDisassembler::WriteUninitDataItemsMASM(uint32 size, uint32 count) {
// Write uninitialized (BSS) data, MASM syntax
// size = size of each data element
// count = number of data elements on each line
// Write data definition directive for appropriate size
switch (size) {
case 1:
OutFile.Put("db "); break;
case 2:
OutFile.Put("dw "); break;
case 4:
OutFile.Put("dd "); break;
case 6:
OutFile.Put("df "); break;
case 8:
OutFile.Put("dq "); break;
case 10:
OutFile.Put("dt "); break;
}
OutFile.Tabulate(AsmTab2);
if (count > 1) {
// Write duplication operator
OutFile.PutDecimal(count);
OutFile.Put(" dup (?)");
}
else {
// DataCount == 1
OutFile.Put("?");
}
}
void CDisassembler::WriteUninitDataItemsYASM(uint32 size, uint32 count) {
// Write uninitialized (BSS) data, YASM syntax
// Write data definition directive for appropriate size
switch (size) {
case 1:
OutFile.Put("resb "); break;
case 2:
OutFile.Put("resw "); break;
case 4:
OutFile.Put("resd "); break;
case 6:
OutFile.Put("resw "); count *= 3; break;
case 8:
OutFile.Put("resq "); break;
case 10:
OutFile.Put("rest "); break;
}
OutFile.Tabulate(AsmTab2);
OutFile.PutDecimal(count);
}
void CDisassembler::WriteUninitDataItemsGASM(uint32 size, uint32 count) {
// Write uninitialized (BSS) data, GAS syntax
OutFile.Put(".zero");
OutFile.Tabulate(AsmTab2);
if (count != 1) {
OutFile.PutDecimal(count); OutFile.Put(" * ");
}
OutFile.PutDecimal(size);
}
void CDisassembler::WriteDataDirectiveMASM(uint32 size) {
// Write DB, etc., MASM syntax
// Write data definition directive for appropriate size
switch (size) {
case 1: OutFile.Put("db "); break;
case 2: OutFile.Put("dw "); break;
case 4: OutFile.Put("dd "); break;
case 6: OutFile.Put("df "); break;
case 8: OutFile.Put("dq "); break;
case 10: OutFile.Put("dt "); break;
case 16: OutFile.Put("xmmword "); break;
case 32: OutFile.Put("ymmword "); break;
default: OutFile.Put("Error "); break;
}
}
void CDisassembler::WriteDataDirectiveYASM(uint32 size) {
// Write DB, etc., YASM syntax
// Write data definition directive for appropriate size
switch (size) {
case 1: OutFile.Put("db "); break;
case 2: OutFile.Put("dw "); break;
case 4: OutFile.Put("dd "); break;
case 6: OutFile.Put("df "); break;
case 8: OutFile.Put("dq "); break;
case 10: OutFile.Put("dt "); break;
case 16: OutFile.Put("ddq "); break;
default: OutFile.Put("Error "); break;
}
}
void CDisassembler::WriteDataDirectiveGASM(uint32 size) {
// Write DB, etc., GAS syntax
// Write data definition directive for appropriate size
switch (size) {
case 1: OutFile.Put(".byte "); break;
case 2: OutFile.Put(".short "); break;
case 4: OutFile.Put(".int "); break;
case 8: OutFile.Put(".quad "); break;
case 10: OutFile.Put(".tfloat "); break;
default: OutFile.Put("Error "); break;
}
}
void CDisassembler::WriteDataComment(uint32 ElementSize, uint32 LinePos, uint32 Pos, uint32 irel) {
// Write comment after data item
uint32 pos1; // Position of data for comment
uint32 RelType = 0; // Relocation type
char TextBuffer[64]; // Buffer for writing floating point number
OutFile.Tabulate(AsmTab3); // Tabulate to comment field
OutFile.Put(CommentSeparator); // Start comment
// Write address
if (SectionEnd + SectionAddress + (uint32)ImageBase > 0xFFFF) {
// Write 32 bit address
OutFile.PutHex(LinePos + SectionAddress + (uint32)ImageBase);
}
else {
// Write 16 bit address
OutFile.PutHex((uint16)(LinePos + SectionAddress));
}
if ((Sections[Section].Type & 0xFF) == 3 || Pos > Sections[Section].InitSize) {
// Unitialized data. Write no data
return;
}
if (irel && irel < Relocations.GetNumEntries() && Relocations[irel].Offset == LinePos) {
// Value is relocated, get relocation type
RelType = Relocations[irel].Type;
}
// Space after address
OutFile.Put(" _ ");
// Comment type depends on ElementSize and DataType
switch (ElementSize) {
case 1:
// Bytes. Write ASCII characters
for (pos1 = LinePos; pos1 < Pos; pos1++) {
// Get character
int8 c = Get<int8>(pos1);
// Avoid non-printable characters
if (c < ' ' || c == 0x7F) c = '.';
// Print ASCII character
OutFile.Put(c);
}
break;
case 2:
// Words. Write as decimal
for (pos1 = LinePos; pos1 < Pos; pos1 += 2) {
if (RelType) {
OutFile.PutHex(Get<uint16>(pos1), 1); // Write as hexadecimal
}
else {
OutFile.PutDecimal(Get<int16>(pos1), 1);// Write as signed decimal
}
OutFile.Put(' ');
}
break;
case 4:
// Dwords
for (pos1 = LinePos; pos1 < Pos; pos1 += 4) {
if ((DataType & 0x47) == 0x43) {
// Write as float
sprintf(TextBuffer, "%.8G", Get<float>(pos1));
OutFile.Put(TextBuffer);
// Make sure the number has a . or E to indicate a floating point number
if (!strchr(TextBuffer,'.') && !strchr(TextBuffer,'E')) OutFile.Put(".0");
}
else if (((DataType + 1) & 0xFF) == 0x0C || RelType) {
// jump/call address or offset. Write as hexadecimal
OutFile.PutHex(Get<uint32>(pos1));
}
else {
// Other. Write as decimal
OutFile.PutDecimal(Get<int32>(pos1), 1);
}
OutFile.Put(' ');
}
break;
case 8:
// Qwords
for (pos1 = LinePos; pos1 < Pos; pos1 += 8) {
if ((DataType & 0x47) == 0x44) {
// Write as double
sprintf(TextBuffer, "%.16G", Get<double>(pos1));
OutFile.Put(TextBuffer);
// Make sure the number has a . or E to indicate a floating point number
if (!strchr(TextBuffer,'.') && !strchr(TextBuffer,'E')) OutFile.Put(".0");
}
else {
// Write as hexadecimal
OutFile.PutHex(Get<uint64>(pos1));
}
OutFile.Put(' ');
}
break;
case 10:
// tbyte. Many compilers do not support long doubles in sprintf. Write as bytes
for (pos1 = LinePos; pos1 < Pos; pos1++) {
OutFile.PutHex(Get<uint8>(pos1), 1);
}
break;
}
if (RelType) {
// Indicate relocation type
OutFile.Put(Lookup(RelocationTypeNames, RelType));
}
}
void CDisassembler::WriteRelocationTarget(uint32 irel, uint32 Context, int64 Addend) {
// Write cross reference, including addend, but not including segment override and []
// irel = index into Relocations
// Context:
// 1 = Data definition
// 2 = Immediate data field in instruction
// 4 = Data address in instruction
// 8 = Near jump/call destination
// 0x10 = Far jump/call destination
// 0x100 = Self-relative address expected
// Addend: inline addend
// Implicit parameters:
// IBegin: value of '$' operator
// IEnd: reference point for self-relative addressing
// BaseReg, IndexReg
uint32 RefFrame; // Target segment
int32 Addend2 = 0; // Difference between '$' and reference point
// Get relocation type
uint32 RelType = Relocations[irel].Type;
if (RelType & 0x60) {
// Inline addend is already relocated.
// Ignore addend and treat as direct relocation
RelType = 1;
Addend = 0;
}
// Get relocation size
uint32 RelSize = Relocations[irel].Size;
// Get relocation addend
Addend += Relocations[irel].Addend;
// Get relocation target
uint32 Target = Relocations[irel].TargetOldIndex;
// Is offset operand needed?
if (Syntax != SUBTYPE_YASM && (
((RelType & 0xB) && (Context & 2))
|| ((RelType & 8) && (Context & 0x108)))) {
// offset operator needed to convert memory operand to immediate address
OutFile.Put("offset ");
}
// Is seg operand needed?
if (RelType & 0x200) {
// seg operator needed to convert memory operand to its segment
OutFile.Put("seg ");
}
// Is explicit segment or frame needed?
if ((RelType & 0x408) && (Context & 0x11B)) {
// Write name of segment/group frame
RefFrame = Relocations[irel].RefOldIndex;
if (!RefFrame) {
// No frame. Use segment of symbol
RefFrame = Symbols[Symbols.Old2NewIndex(Target)].Section;
}
if (RefFrame && RefFrame < Sections.GetNumEntries()) {
// Write segment or group name
const char * SecName = NameBuffer.Buf()+Sections[RefFrame].Name;
OutFile.Put(SecName);
OutFile.Put(":");
}
}
// Is imagerel operator needed?
if (RelType & 4) {
// imagerel operator needed to get image-relative address
OutFile.Put("imagerel(");
}
// Adjust addend
// Adjust offset if self-relative relocation expected and found
if ((RelType & 2) && (Context & 0x108)) {
// Self-relative relocation expected and found
// Adjust by size of address field and immediate field
Addend += IEnd - Relocations[irel].Offset;
}
// Subtract self-reference if unexpected self-relative relocation
if ((RelType & 2) && !(Context & 0x108)) {
// Self-relative relocation found but not expected
// Fix difference between '$' and reference point
Addend2 = Relocations[irel].Offset - IBegin;
Addend -= Addend2;
}
// Add self-reference if self-relative relocation expected but not found
if (!(RelType & 2) && (Context & 0x108)) {
// Self-relative relocation expected but not found
// Fix difference between '$' and reference point
Addend += IEnd - IBegin;
}
if (RelType & 0x100) {
// Target is a segment
RefFrame = Symbols[Symbols.Old2NewIndex(Target)].Section;
if (RefFrame && RefFrame < Sections.GetNumEntries()) {
const char * SecName = NameBuffer.Buf()+Sections[RefFrame].Name;
OutFile.Put(SecName);
}
else {
OutFile.Put("undefined segment");
}
}
else {
// Target is a symbol
// Find target symbol
uint32 TargetSym = Symbols.Old2NewIndex(Target);
// Check if Target is appropriate
if (((Symbols[TargetSym].Type & 0x80000000) || (int32)Addend)
&& !(CodeMode == 1 && s.BaseReg)) {
// Symbol is a start-of-section entry in symbol table, or has an addend
// Look for a more appropriate symbol, except if code with base register
uint32 sym, sym1, sym2 = 0;
sym1 = Symbols.FindByAddress(Symbols[TargetSym].Section, Symbols[TargetSym].Offset + (int32)Addend, &sym2);
for (sym = sym1; sym && sym <= sym2; sym++) {
if (Symbols[sym].Scope && !(Symbols[sym].Type & 0x80000000)) {
// Found a better symbol name for target address
TargetSym = sym;
Addend = Addend2;
}
}
}
// Write name of target symbol
OutFile.Put(Symbols.GetName(TargetSym));
if (Syntax == SUBTYPE_GASM && (
RelType == 0x41 || RelType == 0x81 || RelType == 0x2002)) {
// make PLT entry
OutFile.Put("@PLT");
}
}
// End parenthesis if we started one
if (RelType & 4) {
OutFile.Put(")");
}
// Subtract reference point, if any
if (RelType & 0x10) {
OutFile.Put("-");
// Write name of segment/group frame
uint32 RefPoint = Relocations[irel].RefOldIndex;
if (RefPoint) {
// Reference point name valid
OutFile.Put(Symbols.GetNameO(RefPoint));
}
else {
OutFile.Put("Reference_Point_Missing");
}
}
// Subtract self-reference if unexpected self-relative relocation
if ((RelType & 2) && !(Context & 0x108)) {
// Self-relative relocation found but not expected
OutFile.Put("-"); OutFile.Put(HereOperator);
}
// Add self-reference if self-relative relocation expected but not found
if (!(RelType & 2) && (Context & 0x108)) {
// Self-relative relocation expected but not found
OutFile.Put("+"); OutFile.Put(HereOperator);
}
// Write addend, if not zero
if (Addend) {
if (Addend < 0) {
// Negative, write "-"
OutFile.Put("-");
Addend = -Addend;
}
else {
// Positive, write "+"
OutFile.Put("+");
}
// Write value as hexadecimal
switch (RelSize) {
case 1:
OutFile.PutHex((uint8)Addend, 1);
break;
case 2:
OutFile.PutHex((uint16)Addend, 2);
break;
case 4:
OutFile.PutHex((uint32)Addend, 2);
break;
case 6:
OutFile.PutHex((uint16)(Addend >> 32), 1);
OutFile.Put(":");
OutFile.PutHex((uint32)Addend, 1);
break;
case 8:
OutFile.PutHex((uint64)Addend, 2);
break;
default:
OutFile.Put("??"); // Should not occur
break;
}
}
}
int CDisassembler::WriteFillers() {
// Check if code is a series of NOPs or other fillers.
// If so then write it as filler and return 1.
// If not, then return 0.
// Check if code is filler
if (!(OpcodeOptions & 0x40)) {
// This instruction can not be used as filler
return 0;
}
uint32 FillerType; // Type of filler
const char * FillerName = s.OpcodeDef->Name; // Name of filler
uint32 IFillerBegin = IBegin; // Start of filling space
uint32 IFillerEnd; // End of filling space
// check for CC = int 3 breakpoint, 3C00 = 90 NOP, 11F = multibyte NOP
if (Opcodei == 0xCC || (Opcodei & 0xFFFE) == 0x3C00 || Opcodei == 0x11F) {
// Instruction is a NOP or int 3 breakpoint
FillerType = Opcodei;
}
else if (s.Warnings1 & 0x1000000) {
// Instruction is a LEA, MOV, etc. with same source and destination
// used as a multi-byte NOP
FillerType = 0xFFFFFFFF;
}
else {
// This instruction does something. Not a filler
return 0;
}
// Save beginning position
IFillerEnd = IEnd = IBegin;
// Loop through instructions to find all consecutive fillers
while (NextInstruction2()) {
// Parse instruction
ParseInstruction();
// Check if code is filler
if (!(OpcodeOptions & 0x40)) {
// This instruction can not be a filler
// Save position of this instruction
IFillerEnd = IBegin;
break;
}
if (Opcodei != 0xCC && (Opcodei & 0xFFFE) != 0x3C00 && Opcodei != 0x11F
&& !(s.Warnings1 & 0x1000000)) {
// Not a filler
// Save position of this instruction
IFillerEnd = IBegin;
break;
}
// If loop exits here then fillers end at end of this instruction
IFillerEnd = IEnd;
}
// Safety check
if (IFillerEnd <= IFillerBegin) return 0;
// Size of fillers
uint32 FillerSize = IFillerEnd - IFillerBegin;
// Write size of filling space
OutFile.Put(CommentSeparator);
OutFile.Put("Filling space: ");
OutFile.PutHex(FillerSize, 2);
OutFile.NewLine();
// Write filler type
OutFile.Put(CommentSeparator);
OutFile.Put("Filler type: ");
switch (FillerType) {
case 0xCC:
FillerName = "INT 3 Debug breakpoint"; break;
case 0x3C00:
FillerName = "NOP"; break;
case 0x3C01:
FillerName = "NOP with prefixes"; break;
case 0x011F:
FillerName = "Multi-byte NOP";break;
}
OutFile.Put(FillerName);
if (FillerType == 0xFFFFFFFF) {
OutFile.Put(" with same source and destination");
}
// Write as bytes
uint32 Pos;
for (Pos = IFillerBegin; Pos < IFillerEnd; Pos++) {
if (((Pos - IFillerBegin) & 7) == 0) {
// Start new line
OutFile.NewLine();
OutFile.Put(CommentSeparator);
OutFile.Tabulate(AsmTab1);
OutFile.Put(Syntax == SUBTYPE_GASM ? ".byte " : "db ");
}
else {
// Continue on same line
OutFile.Put(", ");
}
// Write byte value
OutFile.PutHex(Get<uint8>(Pos), 1);
}
// Blank line
OutFile.NewLine(); OutFile.NewLine();
// Find alignment
uint32 Alignment = 4; // Limit to 2^4 = 16
// Check if first non-filler is aligned by this value
while (Alignment && (IFillerEnd & ((1 << Alignment) - 1))) {
// Not aligned by 2^Alignment
Alignment--;
}
if (Alignment) {
// Check if smaller alignment would do
if (Alignment > 3 && FillerSize < 1u << (Alignment-1)) {
// End is aligned by 16, but there are less than 8 filler bytes.
// Change to align 8
Alignment--;
}
// Write align directive
WriteAlign(1 << Alignment);
// Prevent writing ALIGN again
FlagPrevious &= ~1;
}
// Restore IBegin and IEnd to beginning of first non-filler instruction
IBegin = IEnd = IFillerEnd;
if (LabelInaccessible == IFillerBegin && IFillerEnd < LabelEnd) {
// Mark first instruction after filler as inaccessible
LabelInaccessible = IFillerEnd;
}
// Return success. Fillers have been written. Don't write as normal instructions
return 1;
}
void CDisassembler::WriteAlign(uint32 a) {
// Write alignment directive
OutFile.Put(Syntax == SUBTYPE_GASM ? ".ALIGN" : "ALIGN");
OutFile.Tabulate(AsmTab1);
OutFile.PutDecimal(a);
OutFile.NewLine();
}
void CDisassembler::WriteFileBegin() {
// Write begin of file
OutFile.SetFileType(FILETYPE_ASM);
// Initial comment
OutFile.Put(CommentSeparator);
OutFile.Put("Disassembly of file: ");
OutFile.Put(cmd.InputFile);
OutFile.NewLine();
// Date and time.
// Note: will fail after year 2038 on computers that use 32-bit time_t
time_t time1 = time(0);
char * timestring = ctime(&time1);
if (timestring) {
// Remove terminating '\n' in timestring
for (char *c = timestring; *c; c++) {
if (*c < ' ') *c = 0;
}
// Write date and time as comment
OutFile.Put(CommentSeparator);
OutFile.Put(timestring);
OutFile.NewLine();
}
// Write mode
OutFile.Put(CommentSeparator);
OutFile.Put("Mode: ");
OutFile.PutDecimal(WordSize);
OutFile.Put(" bits");
OutFile.NewLine();
// Write syntax dialect
OutFile.Put(CommentSeparator);
OutFile.Put("Syntax: ");
switch (Syntax) {
case SUBTYPE_MASM:
OutFile.Put(WordSize < 64 ? "MASM/ML" : "MASM/ML64"); break;
case SUBTYPE_YASM:
OutFile.Put("YASM/NASM"); break;
case SUBTYPE_GASM:
OutFile.Put("GAS(Intel)"); break;
}
OutFile.NewLine();
// Write instruction set as comment
// Instruction set is at least .386 if 32 bit mode
if (InstructionSetMax < 3 && (MasmOptions & 0x200)) InstructionSetMax = 3;
// Get name of basic instruction set
const char * set0 = "";
if (InstructionSetMax < InstructionSetNamesLen) {
set0 = InstructionSetNames[InstructionSetMax];
}
// Write as comment
OutFile.Put(CommentSeparator);
OutFile.Put("Instruction set: ");
OutFile.Put(set0);
if (InstructionSetAMDMAX) {
// Get name of any AMD-specific instruction set
const char * setA = "";
switch (InstructionSetAMDMAX) {
case 1: setA = "AMD 3DNow"; break;
case 2: setA = "AMD 3DNowE"; break;
case 4: setA = "AMD SSE4a"; break;
case 5: setA = "AMD XOP"; break;
case 6: setA = "AMD FMA4"; break;
case 7: setA = "AMD TBM"; break;
}
if (*setA) {
OutFile.Put(", ");
OutFile.Put(setA);
}
}
// VIA instruction set:
if (InstructionSetOR & 0x2000) OutFile.Put(", VIA");
// Additional instruction sets:
if (WordSize > 32) OutFile.Put(", x64");
if (InstructionSetOR & 0x100) OutFile.Put(", 80x87");
if (InstructionSetOR & 0x800) OutFile.Put(", privileged instructions");
OutFile.NewLine();
if (NamesChanged) {
// Tell that symbol names have been changed
OutFile.NewLine();
OutFile.Put(CommentSeparator);
OutFile.Put("Error: symbol names contain illegal characters,");
OutFile.NewLine(); OutFile.Put(CommentSeparator);
OutFile.PutDecimal(NamesChanged);
#if ReplaceIllegalChars
OutFile.Put(" Symbol names changed");
#else
OutFile.Put(" Symbol names not changed");
#endif
OutFile.NewLine();
}
// Write syntax-specific initializations
switch (Syntax) {
case SUBTYPE_MASM:
WriteFileBeginMASM();
WritePublicsAndExternalsMASM();
break;
case SUBTYPE_YASM:
WriteFileBeginYASM();
WritePublicsAndExternalsYASMGASM();
break;
case SUBTYPE_GASM:
WriteFileBeginGASM();
WritePublicsAndExternalsYASMGASM();
break;
}
}
void CDisassembler::WriteFileBeginMASM() {
// Write MASM-specific file init
if (WordSize < 64) {
// Write instruction set directive, except for 64 bit assembler
const char * set1 = "";
switch (InstructionSetMax) {
case 0: set1 = ".8086"; break;
case 1: set1 = ".186"; break;
case 2: set1 = ".286"; break;
case 3: set1 = ".386"; break;
case 4: set1 = ".486"; break;
case 5: set1 = ".586"; break;
case 6: default:
set1 = ".686"; break;
}
// Write basic instruction set
OutFile.NewLine();
OutFile.Put(set1);
if (InstructionSetOR & 0x800) {
// Privileged. Add "p"
OutFile.Put("p");
}
OutFile.NewLine();
// Write extended instruction set
if (InstructionSetOR & 0x100) {
// Floating point
if (InstructionSetMax < 3) {
OutFile.Put(".8087"); OutFile.NewLine();
}
else if (InstructionSetMax < 5) {
OutFile.Put(".387"); OutFile.NewLine();
}
}
if (InstructionSetMax >= 0x11) {
// .xmm directive. Not differentiated between SSE, SSE2, etc.
OutFile.Put(".xmm"); OutFile.NewLine();
}
else if (InstructionSetMax >= 7) {
// .mmx directive
OutFile.Put(".mmx"); OutFile.NewLine();
}
}
if (MasmOptions & 1) {
// Need dotname option
OutFile.Put("option dotname"); OutFile.NewLine();
}
if (WordSize == 32) {
// Write .model flat if 32 bit mode
OutFile.Put(".model flat"); OutFile.NewLine();
}
// Initialize Assumes for segment registers
if (!(MasmOptions & 0x100)) {
// No 16-bit segments. Assume CS=DS=ES=SS=flat
Assumes[0]=Assumes[1]=Assumes[2]=Assumes[3] = ASM_SEGMENT_FLAT;
}
else {
// 16-bit segmented model. Segment register values unknown
Assumes[0]=Assumes[1]=Assumes[2]=Assumes[3] = ASM_SEGMENT_UNKNOWN;
}
// FS and GS assumed to ERROR
Assumes[4] = Assumes[5] = ASM_SEGMENT_ERROR;
// Write assume if FS or GS used
// This is superfluous because an assume directive will be written at first use of FS/GS
if (MasmOptions & 2) {
OutFile.Put("assume fs:nothing"); OutFile.NewLine();
}
if (MasmOptions & 4) {
OutFile.Put("assume gs:nothing"); OutFile.NewLine();
}
OutFile.NewLine(); // Blank line
}
void CDisassembler::WriteFileBeginYASM() {
// Write YASM-specific file init
OutFile.NewLine();
if (WordSize == 64) {
OutFile.Put("default rel"); OutFile.NewLine();
}
//if (InstructionSetMax >= 0x11) {OutFile.Put("%define xmmword oword"); OutFile.NewLine();}
//if (InstructionSetMax >= 0x19) {OutFile.Put("%define ymmword"); OutFile.NewLine();}
OutFile.NewLine();
}
void CDisassembler::WriteFileBeginGASM() {
// Write GAS-specific file init
OutFile.NewLine();
OutFile.Put(CommentSeparator);
OutFile.Put("Note: Uses Intel syntax with destination operand first. Remember to");
OutFile.NewLine();
OutFile.Put(CommentSeparator);
OutFile.Put("put syntax directives in the beginning and end of inline assembly:");
OutFile.NewLine();
OutFile.Put(".intel_syntax noprefix ");
OutFile.NewLine(); OutFile.NewLine();
}
void CDisassembler::WritePublicsAndExternalsMASM() {
// Write public and external symbol definitions
uint32 i; // Loop counter
uint32 LinesWritten = 0; // Count lines written
const char * XName; // Name of external symbols
// Loop through public symbols
for (i = 0; i < Symbols.GetNumEntries(); i++) {
if (Symbols[i].Scope & 0x1C) {
// Symbol is public
OutFile.Put("public ");
// Write name
OutFile.Put(Symbols.GetName(i));
// Check if weak or communal
if (Symbols[i].Scope & 0x18) {
// Scope is weak or communal
OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
if (Symbols[i].Scope & 8) OutFile.Put("Note: Weak. Not supported by MASM ");
if (Symbols[i].Scope & 0x10) OutFile.Put("Note: Communal. Not supported by MASM");
}
OutFile.NewLine(); LinesWritten++;
}
}
// Blank line if anything written
if (LinesWritten) {
OutFile.NewLine();
LinesWritten = 0;
}
// Loop through external symbols
for (i = 0; i < Symbols.GetNumEntries(); i++) {
if (Symbols[i].Scope & 0x20) {
// Symbol is external
OutFile.Put("extern ");
// Get name
XName = Symbols.GetName(i);
// Check for dynamic import
if (Symbols[i].DLLName && strncmp(XName, Symbols.ImportTablePrefix, (uint32)strlen(Symbols.ImportTablePrefix)) == 0) {
// Remove "_imp" prefix from name
XName += (uint32)strlen(Symbols.ImportTablePrefix);
}
// Write name
OutFile.Put(XName);
OutFile.Put(": ");
// Write type
if ((Symbols[i].Type & 0xFE) == 0x84) {
// Far
OutFile.Put("far");
}
else if ((Symbols[i].Type & 0xF0) == 0x80 || Symbols[i].DLLName) {
// Near
OutFile.Put("near");
}
else {
// Data. Write size
switch (GetDataItemSize(Symbols[i].Type)) {
case 1: default: OutFile.Put("byte"); break;
case 2: OutFile.Put("word"); break;
case 4: OutFile.Put("dword"); break;
case 6: OutFile.Put("fword"); break;
case 8: OutFile.Put("qword"); break;
case 10: OutFile.Put("tbyte"); break;
case 16: OutFile.Put("xmmword"); break;
case 32: OutFile.Put("ymmword"); break;
}
}
// Add comment if DLL import
if (Symbols[i].DLLName) {
OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
OutFile.Put(Symbols.GetDLLName(i));
}
// Finished line
OutFile.NewLine(); LinesWritten++;
}
}
// Blank line if anything written
if (LinesWritten) {
OutFile.NewLine();
LinesWritten = 0;
}
// Write the value of any constants
// Loop through symbols
for (i = 0; i < Symbols.GetNumEntries(); i++) {
// Local symbols included because there might be a rip-relative address to a named constant = 0
if (Symbols[i].Section == ASM_SEGMENT_ABSOLUTE /*&& (Symbols[i].Scope & 0x1C)*/) {
// Symbol is constant
// Write name
OutFile.Put(Symbols.GetName(i));
OutFile.Put(" equ ");
// Write value as hexadecimal
OutFile.PutHex(Symbols[i].Offset, 1);
// Write decimal value as comment
OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
OutFile.PutDecimal(Symbols[i].Offset, 1);
OutFile.NewLine(); LinesWritten++;
}
}
// Blank line if anything written
if (LinesWritten) {
OutFile.NewLine();
LinesWritten = 0;
}
// Write any group definitions
int32 GroupId, SegmentId;
// Loop through sections to search for group definitions
for (GroupId = 1; GroupId < (int32)Sections.GetNumEntries(); GroupId++) {
// Get section type
uint32 SectionType = Sections[GroupId].Type;
if (SectionType & 0x800) {
// This is a segment group definition
// Count number of members
uint32 NumMembers = 0;
// Write group name
WriteSectionName(GroupId);
// Write "group"
OutFile.Put(" "); OutFile.Tabulate(AsmTab1); OutFile.Put("GROUP ");
// Search for group members
for (SegmentId = 1; SegmentId < (int32)Sections.GetNumEntries(); SegmentId++) {
if (Sections[SegmentId].Group == GroupId && !(Sections[SegmentId].Type & 0x800)) {
// is this first member?
if (NumMembers++) {
// Not first member. Write comma
OutFile.Put(", ");
}
// Write group member
WriteSectionName(SegmentId);
}
}
// End line
OutFile.NewLine(); LinesWritten++;
}
}
// Blank line if anything written
if (LinesWritten) {
OutFile.NewLine();
LinesWritten = 0;
}
}
void CDisassembler::WritePublicsAndExternalsYASMGASM() {
// Write public and external symbol definitions, YASM and GAS syntax
uint32 i; // Loop counter
uint32 LinesWritten = 0; // Count lines written
const char * XName; // Name of external symbols
// Loop through public symbols
for (i = 0; i < Symbols.GetNumEntries(); i++) {
if (Symbols[i].Scope & 0x1C) {
// Symbol is public
if (Syntax == SUBTYPE_GASM) OutFile.Put(".");
OutFile.Put("global ");
// Write name
OutFile.Put(Symbols.GetName(i));
// Write type
if ((Symbols[i].Type & 0xF0) == 0x80) {
// Symbol is a function
if (Syntax == SUBTYPE_YASM) {
OutFile.Put(": function");
}
else if (Syntax == SUBTYPE_GASM) {
OutFile.NewLine();
OutFile.Put(".type ");
OutFile.Put(Symbols.GetName(i));
OutFile.Put(", @function");
}
}
// Check if weak or communal
if (Symbols[i].Scope & 0x18) {
// Scope is weak or communal
OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
if (Symbols[i].Scope & 8) OutFile.Put("Note: Weak.");
if (Symbols[i].Scope & 0x10) OutFile.Put("Note: Communal.");
}
OutFile.NewLine(); LinesWritten++;
}
}
// Blank line if anything written
if (LinesWritten) {
OutFile.NewLine();
LinesWritten = 0;
}
// Loop through external symbols
for (i = 0; i < Symbols.GetNumEntries(); i++) {
if (Symbols[i].Scope & 0x20) {
// Symbol is external
if (Syntax == SUBTYPE_GASM) OutFile.Put(".");
OutFile.Put("extern ");
// Get name
XName = Symbols.GetName(i);
// Check for dynamic import
if (Symbols[i].DLLName && strncmp(XName, Symbols.ImportTablePrefix, (uint32)strlen(Symbols.ImportTablePrefix)) == 0) {
// Remove "_imp" prefix from name
XName += (uint32)strlen(Symbols.ImportTablePrefix);
}
// Write name
OutFile.Put(XName);
OutFile.Put(" ");
OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
// Write type
if ((Symbols[i].Type & 0xFE) == 0x84) {
// Far
OutFile.Put("far");
}
else if ((Symbols[i].Type & 0xF0) == 0x80 || Symbols[i].DLLName) {
// Near
OutFile.Put("near");
}
else {
// Data. Write size
switch (GetDataItemSize(Symbols[i].Type)) {
case 1: default: OutFile.Put("byte"); break;
case 2: OutFile.Put("word"); break;
case 4: OutFile.Put("dword"); break;
case 6: OutFile.Put("fword"); break;
case 8: OutFile.Put("qword"); break;
case 10: OutFile.Put("tbyte"); break;
case 16: OutFile.Put("xmmword"); break;
case 32: OutFile.Put("ymmword"); break;
}
}
// Add comment if DLL import
if (Symbols[i].DLLName) {
OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
OutFile.Put(Symbols.GetDLLName(i));
}
// Finished line
OutFile.NewLine(); LinesWritten++;
}
}
// Blank line if anything written
if (LinesWritten) {
OutFile.NewLine(); LinesWritten = 0;
}
// Write the value of any constants
// Loop through symbols
for (i = 0; i < Symbols.GetNumEntries(); i++) {
if (Symbols[i].Section == ASM_SEGMENT_ABSOLUTE /*&& (Symbols[i].Scope & 0x1C)*/) {
// Symbol is constant
if (Syntax == SUBTYPE_YASM) {
// Write name equ value
OutFile.Put(Symbols.GetName(i));
OutFile.Put(" equ ");
}
else {
// Gas: write .equ name, value
OutFile.Put(".equ ");
OutFile.Tabulate(AsmTab1);
OutFile.Put(Symbols.GetName(i));
OutFile.Put(", ");
}
// Write value as hexadecimal
OutFile.PutHex(Symbols[i].Offset, 1);
// Write decimal value as comment
OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
OutFile.PutDecimal(Symbols[i].Offset, 1);
OutFile.NewLine(); LinesWritten++;
}
}
// Blank line if anything written
if (LinesWritten) {
OutFile.NewLine();
LinesWritten = 0;
}
// Write any group definitions
int32 GroupId, SegmentId;
// Loop through sections to search for group definitions
for (GroupId = 1; GroupId < (int32)Sections.GetNumEntries(); GroupId++) {
// Get section type
uint32 SectionType = Sections[GroupId].Type;
if (SectionType & 0x800) {
// This is a segment group definition
// Count number of members
uint32 NumMembers = 0;
// Write group name
WriteSectionName(GroupId);
// Write "group"
OutFile.Put(" "); OutFile.Tabulate(AsmTab1); OutFile.Put("GROUP ");
// Search for group members
for (SegmentId = 1; SegmentId < (int32)Sections.GetNumEntries(); SegmentId++) {
if (Sections[SegmentId].Group == GroupId && !(Sections[SegmentId].Type & 0x800)) {
// is this first member?
if (NumMembers++) {
// Not first member. Write comma
OutFile.Put(", ");
}
// Write group member
WriteSectionName(SegmentId);
}
}
// End line
OutFile.NewLine(); LinesWritten++;
}
}
// Blank line if anything written
if (LinesWritten) {
OutFile.NewLine();
LinesWritten = 0;
}
}
void CDisassembler::WriteFileEnd() {
// Write end of file
OutFile.NewLine();
switch(Syntax) {
case SUBTYPE_MASM:
OutFile.Put("END"); break;
case SUBTYPE_GASM:
OutFile.Put(CommentSeparator);
OutFile.Put("Return to AT&T syntax with destination operand last:");
OutFile.NewLine();
OutFile.Put(".att_syntax prefix ");
OutFile.NewLine();
break;
case SUBTYPE_YASM:
break;
}
}
void CDisassembler::WriteSegmentBegin() {
// Write begin of segment
// Choose dialect
switch (Syntax) {
case SUBTYPE_MASM:
WriteSegmentBeginMASM(); break;
case SUBTYPE_YASM:
WriteSegmentBeginYASM(); break;
case SUBTYPE_GASM:
WriteSegmentBeginGASM(); break;
}
}
void CDisassembler::WriteSegmentBeginMASM() {
// Write begin of segment
OutFile.NewLine(); // Blank line
// Check if Section is valid
if (Section == 0 || Section >= Sections.GetNumEntries()) {
// Illegal segment entry
OutFile.Put("UNKNOWN SEGMENT"); OutFile.NewLine();
return;
}
// Write segment name
WriteSectionName(Section);
// Tabulate
OutFile.Put(" "); OutFile.Tabulate(AsmTab1);
// Write "segment"
OutFile.Put("SEGMENT ");
// Write alignment
switch (Sections[Section].Align) {
case 0: // 1
OutFile.Put("BYTE "); break;
case 1: // 2
OutFile.Put("WORD "); break;
case 2: // 4
OutFile.Put("DWORD "); break;
case 4: // 16
OutFile.Put("PARA "); break;
//case 8: // 256 or 4096. Definition is ambiguous!
// OutFile.Put("PAGE "); break;
default:
// Non-standard alignment
OutFile.Put("ALIGN(");
OutFile.PutDecimal(1 << Sections[Section].Align);
OutFile.Put(") ");
break;
}
if (WordSize != 64) {
// "PUBLIC" not supported by ml64 assembler
OutFile.Put("PUBLIC ");
// Write segment word size if necessary
if (MasmOptions & 0x100) {
// There is at least one 16-bit segment. Write segment word size
OutFile.Put("USE");
OutFile.PutDecimal(Sections[Section].WordSize);
OutFile.Put(" ");
}
}
// Write segment class
switch (Sections[Section].Type & 0xFF) {
case 1:
OutFile.Put("'CODE'"); break;
case 2:
OutFile.Put("'DATA'"); break;
case 3:
OutFile.Put("'BSS'"); break;
case 4:
OutFile.Put("'CONST'"); break;
default:;
// Unknown class. Write nothing
}
// Tabulate to comment
OutFile.Put(" "); OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
// Write section number
OutFile.Put("section number ");
OutFile.PutDecimal(Section);
// New line
OutFile.NewLine();
if (Sections[Section].Type & 0x1000) {
// Communal
OutFile.Put(CommentSeparator);
OutFile.Put(" Communal section not supported by MASM");
OutFile.NewLine();
}
if (WordSize == 16 && Sections[Section].Type == 1) {
// 16 bit code segment. Write ASSUME CS: SEGMENTNAME
OutFile.Put("ASSUME ");
OutFile.Tabulate(AsmTab1);
OutFile.Put("CS:");
if (Sections[Section].Group) {
// Group name takes precedence over segment name
WriteSectionName(Sections[Section].Group);
}
else {
WriteSectionName(Section);
}
OutFile.NewLine();
Assumes[1] = Section;
}
}
void CDisassembler::WriteSegmentBeginYASM() {
// Write begin of segment
OutFile.NewLine(); // Blank line
// Check if Section is valid
if (Section == 0 || Section >= Sections.GetNumEntries()) {
// Illegal segment entry
OutFile.Put("UNKNOWN SEGMENT"); OutFile.NewLine();
return;
}
// Write SECTION directive
OutFile.Put("SECTION ");
// Write segment name
WriteSectionName(Section);
// Tabulate
OutFile.Put(" "); OutFile.Tabulate(AsmTab2);
OutFile.Put("align=");
OutFile.PutDecimal(1 << Sections[Section].Align);
if (Sections[Section].WordSize != WordSize) {
OutFile.Put(" use");
OutFile.PutDecimal(Sections[Section].WordSize);
}
if ((Sections[Section].Type & 0xFF) == 1) {
OutFile.Put(" execute");
}
else {
OutFile.Put(" noexecute");
}
// Tabulate to comment
OutFile.Put(" "); OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
// Write section number
OutFile.Put("section number ");
OutFile.PutDecimal(Section);
// Write type
OutFile.Put(", ");
switch (Sections[Section].Type & 0xFF) {
case 1: OutFile.Put("code"); break;
case 2: OutFile.Put("data"); break;
case 3: OutFile.Put("bss"); break;
case 4: OutFile.Put("const"); break;
default: OutFile.Put("unknown type: ");
OutFile.PutHex(Sections[Section].Type & 0xFF);
break;
}
// New line
OutFile.NewLine();
if (Sections[Section].Type & 0x1000) {
// Communal
OutFile.Put(CommentSeparator);
OutFile.Put(" Communal section not supported by YASM");
OutFile.NewLine();
}
}
void CDisassembler::WriteSegmentBeginGASM() {
// Write begin of segment
uint32 Type; // Section type
OutFile.NewLine(); // Blank line
// Check if Section is valid
if (Section == 0 || Section >= Sections.GetNumEntries()) {
// Illegal segment entry
OutFile.Put("UNKNOWN SEGMENT"); OutFile.NewLine();
return;
}
// Write SECTION directive
OutFile.Put(".SECTION ");
OutFile.Tabulate(AsmTab1);
// Write segment name
WriteSectionName(Section);
// Tabulate
OutFile.Put(" "); OutFile.Tabulate(AsmTab2);
// Flags not supported by all versions of Gas. Put as comment:
OutFile.Put(CommentSeparator);
// Write flags
OutFile.Put('"');
Type = Sections[Section].Type & 0xFF;
if (Type) OutFile.Put('a'); // Allocatable
if (Type != 1 && Type != 4) OutFile.Put('w'); // Writeable
if (Type == 1) OutFile.Put('x'); // Executable
OutFile.Put('"');
if (Type) OutFile.Put(", @progbits"); // Allocatable
// Tabulate to comment
OutFile.Put(" "); OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
// Write section number
OutFile.Put("section number ");
OutFile.PutDecimal(Section);
// Write type
OutFile.Put(", ");
switch (Sections[Section].Type & 0xFF) {
case 1: OutFile.Put("code"); break;
case 2: OutFile.Put("data"); break;
case 3: OutFile.Put("bss"); break;
case 4: OutFile.Put("const"); break;
default: OutFile.Put("unknown"); break;
}
OutFile.NewLine(); // Blank line
if (Sections[Section].Type & 0x1000) {
// Communal
OutFile.Put(CommentSeparator);
OutFile.Put(" Communal section ");
OutFile.NewLine();
}
// Write alignment
OutFile.Tabulate(AsmTab1);
OutFile.Put(".ALIGN");
OutFile.Tabulate(AsmTab2);
OutFile.PutDecimal(1 << Sections[Section].Align);
// New line
OutFile.NewLine();
}
void CDisassembler::WriteSegmentEnd() {
// Write end of segment
OutFile.NewLine();
if (Syntax != SUBTYPE_MASM) {
// Not MASM syntax, write only blank line
return;
}
// Check if Section is valid
if (Section == 0 || Section >= Sections.GetNumEntries()) {
// Illegal segment entry
OutFile.Put("UNKNOWN ENDS"); OutFile.NewLine();
return;
}
// Write segment name
const char * segname = NameBuffer.Buf() + Sections[Section].Name;
OutFile.Put(segname);
// Tabulate
OutFile.Put(" "); OutFile.Tabulate(AsmTab1);
// Write "segment"
OutFile.Put("ENDS");
// New line
OutFile.NewLine();
}
void CDisassembler::WriteFunctionBegin() {
// Write begin of function IFunction
// Check if IFunction is valid
if (IFunction == 0 || IFunction >= FunctionList.GetNumEntries()) {
// Should not occur
OutFile.Put(CommentSeparator);
OutFile.Put("Internal error: undefined function begin");
return;
}
// Get symbol old index
uint32 symi = FunctionList[IFunction].OldSymbolIndex;
// Get symbol record
uint32 SymI = Symbols.Old2NewIndex(symi);
OutFile.NewLine(); // Blank line
// Remember that symbol has been written
Symbols[SymI].Scope |= 0x100;
// Check alignment if preceded by NOP
if ((FlagPrevious & 1) && (IBegin & 0x0F) == 0 && Sections[Section].Align >= 4) {
WriteAlign(16);
}
if (Symbols[SymI].Name == 0) {
// Has no name. Probably only NOP fillers
return;
}
// Write function name etc.
switch (Syntax) {
case SUBTYPE_MASM:
WriteFunctionBeginMASM(SymI, Symbols[SymI].Scope); break;
case SUBTYPE_YASM:
WriteFunctionBeginYASM(SymI, Symbols[SymI].Scope); break;
case SUBTYPE_GASM:
WriteFunctionBeginGASM(SymI, Symbols[SymI].Scope); break;
}
}
void CDisassembler::WriteFunctionBeginMASM(uint32 symi, uint32 scope) {
// Write begin of function, MASM syntax
// Write name
WriteSymbolName(symi);
// Space
OutFile.Put(" "); OutFile.Tabulate(AsmTab1);
if (scope & 0x1C) {
// Scope is public
// Write "PROC"
OutFile.Put("PROC");
// Write "NEAR" unless 64 bit mode
if (WordSize < 64) OutFile.Put(" NEAR");
// Check if weak
if (scope & 8) {
OutFile.NewLine();
OutFile.Put(CommentSeparator);
OutFile.Put(" WEAK ");
WriteSymbolName(symi);
}
// Check if communal
if (scope & 0x10) {
OutFile.NewLine();
OutFile.Put(CommentSeparator);
OutFile.Put(" COMDEF ");
WriteSymbolName(symi);
}
}
else {
// Scope is local
OutFile.Put("LABEL NEAR");
}
// Check if Gnu indirect
if (Symbols[symi].Type & 0x40000000) {
OutFile.Put(CommentSeparator);
OutFile.Put("Gnu indirect function"); // Cannot be represented in Masm syntax
}
// End line
OutFile.NewLine();
}
void CDisassembler::WriteFunctionBeginYASM(uint32 symi, uint32 scope) {
// Write begin of function, YASM syntax
// Write name
WriteSymbolName(symi);
// Colon
OutFile.Put(":"); OutFile.Tabulate(AsmTab1);
if (scope & 0x1C) {
// Scope is public
// Write comment
OutFile.Put(CommentSeparator);
OutFile.Put("Function begin");
// Check if weak
if (scope & 8) {
OutFile.Put(", weak");
}
// Check if communal
if (scope & 0x10) {
OutFile.Put(", communal");
}
}
else {
// Scope is local. Write comment
OutFile.Put(CommentSeparator);
OutFile.Put("Local function");
}
// Check if Gnu indirect
if (Symbols[symi].Type & 0x40000000) {
OutFile.Put(CommentSeparator);
OutFile.Put("Gnu indirect function"); // Cannot be represented in NASM/YASM syntax
}
// End line
OutFile.NewLine();
}
void CDisassembler::WriteFunctionBeginGASM(uint32 symi, uint32 scope) {
// Write begin of function, GAS syntax
WriteSymbolName(symi); // Write name
OutFile.Put(":");
OutFile.Tabulate(AsmTab3); OutFile.Put(CommentSeparator);
if (scope & 3) OutFile.Put("Local ");
if (scope & 8) OutFile.Put("weak ");
if (scope & 0x10) OutFile.Put("communal ");
OutFile.Put("Function");
OutFile.NewLine();
OutFile.Tabulate(AsmTab1);
OutFile.Put(".type ");
OutFile.Tabulate(AsmTab2);
WriteSymbolName(symi); // Write name
if (Symbols[symi].Type & 0x40000000) {
OutFile.Put(", @gnu_indirect_function");
}
else {
OutFile.Put(", @function");
}
OutFile.NewLine();
}
void CDisassembler::WriteFunctionEnd() {
// Write end of function
// Check if IFunction is valid
if (IFunction == 0 || IFunction >= FunctionList.GetNumEntries()) {
// Should not occur
OutFile.Put(CommentSeparator);
OutFile.Put("Internal error: undefined function end");
return;
}
// Get symbol index
uint32 SymOldI = FunctionList[IFunction].OldSymbolIndex;
uint32 SymNewI = Symbols.Old2NewIndex(SymOldI);
// check scope
if (Symbols[SymNewI].Scope & 0x1C) {
// Has public scope. Write end of function
switch (Syntax) {
case SUBTYPE_MASM:
WriteFunctionEndMASM(SymNewI); break;
case SUBTYPE_YASM:
WriteFunctionEndYASM(SymNewI); break;
case SUBTYPE_GASM:
WriteFunctionEndGASM(SymNewI); break;
}
}
}
void CDisassembler::WriteFunctionEndMASM(uint32 symi) {
// Write end of function, MASM syntax
// Write name
WriteSymbolName(symi);
// Space
OutFile.Put(" "); OutFile.Tabulate(AsmTab1);
// Write "ENDP"
OutFile.Put("ENDP");
OutFile.NewLine();
}
void CDisassembler::WriteFunctionEndYASM(uint32 symi) {
// Write end of function, YASM syntax
// Write comment
OutFile.Put(CommentSeparator);
// Write name
WriteSymbolName(symi);
OutFile.Put(" End of function");
OutFile.NewLine();
}
void CDisassembler::WriteFunctionEndGASM(uint32 symi){
// Write end of function, GAS syntax
// Write .size directive
OutFile.Tabulate(AsmTab1);
OutFile.Put(".size ");
OutFile.Tabulate(AsmTab2);
WriteSymbolName(symi); // Name of function
OutFile.Put(", . - ");
WriteSymbolName(symi); // Name of function
OutFile.Tabulate(AsmTab3);
OutFile.Put(CommentSeparator);
OutFile.Put("End of function is probably here");
OutFile.NewLine();
}
void CDisassembler::WriteCodeLabel(uint32 symi) {
// Write private or public code label. symi is new symbol index
// Get scope
uint32 Scope = Symbols[symi].Scope;
// Check scope
if (Scope & 0x100) return; // Has been written as function begin
if (Scope == 0) {
// Inaccessible. No name. Make blank line
OutFile.NewLine();
// Remember position for warning check
LabelInaccessible = IBegin;
return;
}
// Begin on new line if preceded by another symbol
if (OutFile.GetColumn()) OutFile.NewLine();
// Check alignment if preceded by NOP
if ((Scope & 0xFF) > 1 && (FlagPrevious & 1) && (IBegin & 0x0F) == 0 && Sections[Section].Align >= 4) {
WriteAlign(16);
}
switch (Syntax) {
case SUBTYPE_MASM:
WriteCodeLabelMASM(symi, Symbols[symi].Scope); break;
case SUBTYPE_YASM:
WriteCodeLabelYASM(symi, Symbols[symi].Scope); break;
case SUBTYPE_GASM:
WriteCodeLabelGASM(symi, Symbols[symi].Scope); break;
}
// Remember this has been written
Symbols[symi].Scope |= 0x100;
}
void CDisassembler::WriteCodeLabelMASM(uint32 symi, uint32 scope) {
// Write private or public code label, MASM syntax
if ((scope & 0xFF) > 1) {
// Scope > function local. Write as label near
// Check if extra linefeed needed
// if (!(IFunction && FunctionList[IFunction].Start == IBegin))
// New line
OutFile.NewLine();
// Write name
WriteSymbolName(symi);
// Space
OutFile.Put(" "); OutFile.Tabulate(AsmTab1);
// Write "LABEL"
OutFile.Put("LABEL");
// Write "NEAR" even 64 bit mode
OutFile.Put(" NEAR");
// New line
OutFile.NewLine();
// Check if weak
if (scope & 8) {
OutFile.Put(CommentSeparator);
OutFile.Put(" WEAK ");
WriteSymbolName(symi);
OutFile.NewLine();
}
// Check if communal
if (scope & 0x10) {
OutFile.Put(CommentSeparator);
OutFile.Put(" COMDEF ");
WriteSymbolName(symi);
OutFile.NewLine();
}
}
else {
// Symbol is local to current function. Write name with colon
if (FlagPrevious & 2) {
// Insert blank line if previous instruction was unconditional jump or return
OutFile.NewLine();
}
// Write name
WriteSymbolName(symi);
// Write ":"
OutFile.Put(":");
if (OutFile.GetColumn() > AsmTab1) {
// Past tabstop. Go to next line
OutFile.NewLine(); // New line
}
}
}
void CDisassembler::WriteCodeLabelYASM(uint32 symi, uint32 scope) {
// Write private or public code label, YASM syntax
if ((scope & 0xFF) > 2) {
// Scope is public
OutFile.NewLine();
// Write name
WriteSymbolName(symi);
OutFile.Put(":");
// Check if weak
if (scope & 8) {
OutFile.Put(CommentSeparator);
OutFile.Put(" weak ");
WriteSymbolName(symi);
}
// Check if communal
if (scope & 0x10) {
OutFile.Put(CommentSeparator);
OutFile.Put(" communal ");
WriteSymbolName(symi);
}
OutFile.NewLine();
}
else {
// Symbol is local to current function. Write name with colon
if (FlagPrevious & 2) {
// Insert blank line if previous instruction was unconditional jump or return
OutFile.NewLine();
}
// Write name
WriteSymbolName(symi);
// Write ":"
OutFile.Put(":");
if (OutFile.GetColumn() > AsmTab1) {
// Past tabstop. Go to next line
OutFile.NewLine(); // New line
}
}
}
void CDisassembler::WriteCodeLabelGASM(uint32 symi, uint32 scope) {
// Write private or public code label, GAS syntax same as YASM syntax
WriteCodeLabelYASM(symi, scope);
}
void CDisassembler::WriteAssume() {
// Write assume directive for segment register if MASM syntax
if (Syntax != SUBTYPE_MASM) return;
if (!s.AddressField) return;
int32 SegReg, PrefixSeg; // Segment register used
uint32 symo; // Target symbol old index
uint32 symi; // Target symbol new index
int32 TargetSegment; // Target segment/section
int32 TargetGroup; // Group containing target segment
// Find which segment register is used for addressing memory operand
SegReg = 3; // DS is default
if (s.BaseReg == 4+1 || s.BaseReg == 5+1) {
// Base register is (E)BP or ESP
SegReg = 2; // SS register used unless there is a prefix
}
if (s.Prefixes[0]) {
// There is a segment prefix
PrefixSeg = GetSegmentRegisterFromPrefix();
if (PrefixSeg >= 0 && PrefixSeg <= 5) {
// Segment prefix is valid. Segment determined by segment prefix
SegReg = PrefixSeg;
}
}
// Default target segment is none
TargetSegment = TargetGroup = 0;
// Find symbol referenced by next instruction
if (s.AddressRelocation && s.AddressRelocation < Relocations.GetNumEntries()) {
symo = Relocations[s.AddressRelocation].TargetOldIndex; // Target symbol old index
if (symo) {
symi = Symbols.Old2NewIndex(symo); // Target symbol new index
if (symi) {
TargetSegment = Symbols[symi].Section; // Target segment
if (TargetSegment < 0 || TargetSegment >= (int32)Sections.GetNumEntries()) {
TargetSegment = 0;
}
else {
TargetGroup = Sections[TargetSegment].Group; // Group containing target segment
if (TargetGroup <= ASM_SEGMENT_ERROR || TargetGroup >= (int32)Sections.GetNumEntries()) {
TargetGroup = 0;
}
}
}
}
}
if (TargetSegment) {
// Target has a segment. Check if it is different from currently assumed segment
if (TargetSegment != Assumes[SegReg] && TargetGroup != Assumes[SegReg]) {
// Assume directive needed
// If segment belongs to a group then the group takes precedence
if (TargetGroup) TargetSegment = TargetGroup;
// Write assume directive
OutFile.Put("ASSUME ");
OutFile.Tabulate(AsmTab1);
OutFile.Put(RegisterNamesSeg[SegReg]); // Name of segment register used
OutFile.Put(":");
WriteSectionName(TargetSegment); // Name of segment or group referenced
OutFile.NewLine();
Assumes[SegReg] = TargetSegment;
}
}
else {
// Target segment not specified. Assumed value may be anyting but 'error'
if (Assumes[SegReg] <= ASM_SEGMENT_ERROR) {
// Segment register is assumed to 'error'. Change assume to 'nothing'
OutFile.Put("ASSUME ");
OutFile.Tabulate(AsmTab1);
OutFile.Put(RegisterNamesSeg[SegReg]); // Name of segment register used
OutFile.Put(":NOTHING");
OutFile.NewLine();
Assumes[SegReg] = ASM_SEGMENT_NOTHING;
}
}
}
void CDisassembler::WriteInstruction() {
// Write instruction and operands
uint32 NumOperands = 0; // Number of operands written
uint32 i; // Loop index
const char * OpName; // Opcode name
if (s.AddressFieldSize && Syntax == SUBTYPE_MASM) {
// There is a memory operand. Check if ASSUME directive needed
WriteAssume();
}
if (CodeMode & 6) {
// Code is dubious. Show as comment only
OutFile.Put(CommentSeparator); // Start comment
}
else if ((s.OpcodeDef->Options & 0x20) && s.OpcodeStart1 > IBegin) {
// Write prefixes explicitly.
// This is used for rare cases where the assembler cannot generate the prefix
OutFile.Tabulate(AsmTab1); // Tabulate
OutFile.Put(Syntax == SUBTYPE_GASM ? ".byte " : "DB ");
OutFile.Tabulate(AsmTab2); // Tabulate
for (i = IBegin; i < s.OpcodeStart1; i++) {
if (i > IBegin) OutFile.Put(", ");
OutFile.PutHex(Get<uint8>(i), 1);
}
OutFile.Tabulate(AsmTab3); // Tabulate
OutFile.Put(CommentSeparator);
if ((s.OpcodeDef->AllowedPrefixes & 8) && Get<uint8>(IBegin) == 0xF2) {
OutFile.Put("BND prefix coded explicitly"); // Comment
}
else {
OutFile.Put("Prefix coded explicitly"); // Comment
}
OutFile.NewLine();
}
if ((s.Operands[0] & 0xF0) == 0xC0 || (s.Operands[1] & 0xF0) == 0xC0) {
// String instruction or xlat instruction
WriteStringInstruction();
return;
}
OutFile.Tabulate(AsmTab1); // Tabulate
if ((s.OpcodeDef->AllowedPrefixes & 0xC40) == 0xC40) {
switch (s.Prefixes[5]) {
case 0xF2:
OutFile.Put("xacquire "); break; // xacquire prefix
case 0xF3:
OutFile.Put("xrelease "); break; // xrelease prefix
}
}
if (s.Prefixes[2]) {
OutFile.Put("lock "); // Lock prefix
}
// Get opcode name
if (s.OpcodeDef->Name) {
// Opcode name
OpName = s.OpcodeDef->Name;
// Search for opcode comment
s.OpComment = strchr(OpName, ';');
if (s.OpComment) s.OpComment++; // Point to after ';'
}
else {
OpName = "UNDEFINED"; // Undefined code with no name
s.OpComment = 0;
}
// Check prefix option
if ((s.OpcodeDef->Options & 2) && (s.Prefixes[7] & 0x30)) {
// Put prefix 'v' for VEX-prefixed instruction
OutFile.Put('v');
}
// Write opcode name
if (s.OpComment) {
// OpName string contains opcode name and comment, separated by ';'
while (*OpName != ';' && *OpName != 0) { // Write opcode name until comment
OutFile.Put(*(OpName++));
}
}
else {
OutFile.Put(OpName); // Write normal opcode name
}
// Check suffix option
if (s.OpcodeDef->Options & 1) {
// Append suffix for operand size or type to name
if ((s.OpcodeDef->AllowedPrefixes & 0x7000) == 0x1000) {
// F.P. operand size defined by W prefix bit
i = s.Prefixes[7] & 8; // W prefix bit
OutFile.Put(i ? 'd' : 's');
}
else if ((s.OpcodeDef->AllowedPrefixes & 0x7000) == 0x3000) {
// Integer or f.p. operand size defined by W prefix bit
bool f = false;
// Find out if operands are integer or f.p.
for (i = 0; i < s.MaxNumOperands; i++) {
if ((s.Operands[i] & 0xF0) == 0x40) {
f = true; break;
}
}
i = s.Prefixes[7] & 8; // W prefix bit
if (f) {
OutFile.Put(i ? 'd' : 's'); // float precision suffix
}
else {
OutFile.Put(i ? 'q' : 'd'); // integer size suffix
}
}
else if ((s.OpcodeDef->AllowedPrefixes & 0x7000) == 0x4000) {
// Integer operand size defined by W prefix bit
i = s.Prefixes[7] & 8; // W prefix bit
OutFile.Put(i ? 'w' : 'b');
}
else if ((s.OpcodeDef->AllowedPrefixes & 0x7000) == 0x5000) {
// mask register operand size defined by W prefix bit and 66 prefix
i = (s.Prefixes[7] & 8) >> 2; // W prefix bit
i |= s.Prefixes[5] != 0x66; // 66 prefix bit
OutFile.Put("bwdq"[i]);
}
else if (s.OpcodeDef->AllowedPrefixes & 0xE00) {
// F.P. operand type and size defined by prefixes
switch (s.Prefixes[5]) {
case 0: // No prefix = ps
OutFile.Put("ps"); break;
case 0x66: // 66 prefix = pd
OutFile.Put("pd"); break;
case 0xF3: // F3 prefix = ss
OutFile.Put("ss"); break;
case 0xF2: // F2 prefix = sd
OutFile.Put("sd"); break;
default:
err.submit(9000); // Should not occur
}
}
else if (s.OpcodeDef->AllowedPrefixes & 0x100){
// Integer operand size defined by prefixes
// Suffix for operand size
i = s.OperandSize / 8;
if (i <= 8) {
static const char SizeSuffixes[] = " bw d f q"; // Table of suffixes
OutFile.Put(SizeSuffixes[i]);
}
}
}
// Alternative suffix option
if (s.OpcodeDef->Options & 0x1000) {
// Append alternative suffix for vector element size to name
if ((s.OpcodeDef->AllowedPrefixes & 0x7000) == 0x3000) {
// Integer operand size defined by W prefix bit
i = ((s.Prefixes[7] & 8) + 8) * 4; // W prefix bit -> 8 / 16
OutFile.PutDecimal(i);
}
if ((s.OpcodeDef->AllowedPrefixes & 0x7000) == 0x4000) { // 32 / 64
i = (s.Prefixes[7] & 8) + 8; // W prefix bit -> 8 / 16
OutFile.PutDecimal(i);
}
}
// More suffix option
if ((s.OpcodeDef->Options & 0x400) && s.ImmediateFieldSize == 8) {
// 64 bit immediate mov
if (Syntax == SUBTYPE_GASM) OutFile.Put("abs");
}
// Space between opcode name and operands
OutFile.Put(" "); OutFile.Tabulate(AsmTab2); // Tabulate. At least one space
// Loop for all operands to write
for (i = 0; i < s.MaxNumOperands; i++) {
if (s.Operands[i] & 0xFFFF) {
// Write operand i
if (NumOperands++) {
// At least one operand before this one. Separate by ", "
OutFile.Put(", ");
}
// Write constant and jump operands
switch (s.Operands[i] & 0xF0) {
case 0x10: case 0x20: case 0x30: case 0x80:
WriteImmediateOperand(s.Operands[i]);
continue;
}
// Write register and memory operands
uint32 optype = (s.Operands[i] >> 16) & 0x0F;
switch (optype) {
case 0: // Other type of operand
WriteOtherOperand(s.Operands[i]); break;
case 0x1: // Direct memory operand
WriteRMOperand(s.Operands[i]); break;
case 0x2: // Register operand indicated by last bits of opcode
WriteShortRegOperand(s.Operands[i]); break;
case 0x3: // Register or memory operand indicated by mod/rm bits
WriteRMOperand(s.Operands[i]); break;
case 0x4: // Register operand indicated by reg bits
WriteRegOperand(s.Operands[i]); break;
case 0x5: // Register operand indicated by dest bits of DREX byte
WriteDREXOperand(s.Operands[i]); break;
case 0x6: // Register operand indicated by VEX.vvvv bits
WriteVEXOperand(s.Operands[i], 0); break;
case 0x7: // Register operand indicated by bits 4-7 of immediate operand
WriteVEXOperand(s.Operands[i], 1); break;
case 0x8: // Register operand indicated by bits 0-3 of immediate operand
WriteVEXOperand(s.Operands[i], 2); break; // Unused. For future use
}
int isMem = optype == 3 && s.Mod != 3;
if (s.Prefixes[3] == 0x62) { // EVEX and MVEX prefix can have extra operand attributes
if (s.Prefixes[6] & 0x20) {
WriteOperandAttributeEVEX(i, isMem);
}
else {
WriteOperandAttributeMVEX(i, isMem);
}
}
if (s.Prefixes[3] == 0x62 && (i == s.MaxNumOperands - 1 || (s.Operands[i+1] & 0xFFF) < 0x40)) {
// This is the last SIMD operand
if (!(s.Operands[4] & 0x80000000)) {
s.Operands[4] |= 0x80000000; // Make sure we don't write this twice
if (s.Prefixes[6] & 0x20) {
WriteOperandAttributeEVEX(98, isMem);
}
else {
WriteOperandAttributeMVEX(98, isMem);
}
}
}
}
}
if (s.Prefixes[3] == 0x62) { // EVEX and MVEX prefix can have extra attributes after operands
if (s.Prefixes[6] & 0x20) {
WriteOperandAttributeEVEX(99, 0);
}
else {
WriteOperandAttributeMVEX(99, 0);
}
}
if (s.OpComment) {
// Write opcode comment
OutFile.Put(' ');
OutFile.Put(CommentSeparator);
OutFile.Put(s.OpComment);
}
}
void CDisassembler::WriteStringInstruction() {
// Write string instruction or xlat instruction
uint32 NumOperands = 0; // Number of operands written
uint32 i; // Loop index
uint32 Segment; // Possible segment prefix
if (!(s.OpcodeDef->AllowedPrefixes & 0x1100)) {
// Operand size is 8 if operand size prefixes not allowed
s.OperandSize = 8;
}
OutFile.Tabulate(AsmTab1); // Tabulate
if (Syntax != SUBTYPE_MASM && s.Prefixes[0] && (s.OpcodeDef->AllowedPrefixes & 4)) {
// Get segment prefix
Segment = GetSegmentRegisterFromPrefix(); // Interpret segment prefix
// Write segment override
OutFile.Put(RegisterNamesSeg[Segment]);
OutFile.Put(" ");
}
// Check repeat prefix
if (s.OpcodeDef->AllowedPrefixes & 0x20) {
if (s.Prefixes[3]) {
// Repeat prefix
OutFile.Put("rep ");
}
}
else if (s.OpcodeDef->AllowedPrefixes & 0x40) {
if (s.Prefixes[3] == 0xF2) {
// repne prefix
OutFile.Put("repne ");
}
else if (s.Prefixes[3] == 0xF3) {
// repe prefix
OutFile.Put("repe ");
}
}
// Write opcode name
OutFile.Put(s.OpcodeDef->Name); // Opcode name
if (Syntax == SUBTYPE_MASM
&& (((s.OpcodeDef->AllowedPrefixes & 4) && s.Prefixes[0])
|| ((s.OpcodeDef->AllowedPrefixes & 1) && s.Prefixes[1]))) {
// Has segment or address size prefix. Must write operands explicitly
OutFile.Put(" "); // Space before operands
// Check address size for pointer registers
const char * * PointerRegisterNames;
switch (s.AddressSize) {
case 16:
PointerRegisterNames = RegisterNames16; break;
case 32:
PointerRegisterNames = RegisterNames32; break;
case 64:
PointerRegisterNames = RegisterNames64; break;
default:
PointerRegisterNames = 0; // should not occur
}
// Loop for possibly two operands
for (i = 0; i < 2; i++) {
if (s.Operands[i]) {
// Operand i defined
if (NumOperands++) {
// An operand before this one. Separate by ", "
OutFile.Put(", ");
}
if (NumOperands == 1) {
// Write operand size for first operand
switch (s.OperandSize) {
case 8:
OutFile.Put("byte "); break;
case 16:
OutFile.Put("word "); break;
case 32:
OutFile.Put("dword "); break;
case 64:
OutFile.Put("qword "); break;
}
}
// Get segment
Segment = 1; // Default segment is DS
if (s.Prefixes[0]) {
Segment = GetSegmentRegisterFromPrefix(); // Interpret segment prefix
}
if ((s.Operands[i] & 0xCF) == 0xC2) {
Segment = 0; // Segment is ES regardless of prefix for [edi] operand
}
// Write segment override
OutFile.Put(RegisterNamesSeg[Segment]);
OutFile.Put(":");
// Opening "["
OutFile.Put("[");
// Write pointer register
switch (s.Operands[i] & 0xCF) {
case 0xC0: // [bx], [ebx] or [rbx]
OutFile.Put(PointerRegisterNames[3]);
break;
case 0xC1: // [si], [esi] or [rsi]
OutFile.Put(PointerRegisterNames[6]);
break;
case 0xC2: // [di], [edi] or [rdi]
OutFile.Put(PointerRegisterNames[7]);
break;
}
// Closing "]"
OutFile.Put("]");
}
}
}
else {
// We don't have to write the operands
// Append suffix for operand size, except for xlat
if ((s.Operands[1] & 0xCF) != 0xC0) {
// Suffix for operand size
uint32 i = s.OperandSize / 8;
if (i <= 8) {
static const char SizeSuffixes[] = " bw d q"; // Table of suffixes
OutFile.Put(SizeSuffixes[i]);
}
}
}
}
void CDisassembler::WriteCodeComment() {
// Write hex listing of instruction as comment after instruction
uint32 i; // Index to current byte
uint32 FieldSize; // Number of bytes in field
const char * Spacer; // Space between fields
OutFile.Tabulate(AsmTab3); // Tabulate to comment field
OutFile.Put(CommentSeparator); // Start comment
// Write address
if (SectionEnd + SectionAddress + (uint32)ImageBase > 0xFFFF) {
// Write 32 bit address
OutFile.PutHex(IBegin + SectionAddress + (uint32)ImageBase);
}
else {
// Write 16 bit address
OutFile.PutHex((uint16)(IBegin + SectionAddress));
}
// Space after address
OutFile.Put(" _");
// Start of instruction
i = IBegin;
// Write bytes
while (i < IEnd) {
FieldSize = 1; // Size of field to write
Spacer = " "; // Space between fields
// Spacer and FieldSize depends on fields
if (i == s.OpcodeStart1 && i > IBegin) {
Spacer = ": "; // Space between prefixes and opcode
}
if (i == s.OpcodeStart2 + 1) {
Spacer = ". "; // Space between opcode and mod/reg/rm bytes
}
if (i == s.AddressField && s.AddressFieldSize) {
Spacer = ", "; // Space before address field
FieldSize = s.AddressFieldSize;
}
if (i == s.ImmediateField && s.ImmediateFieldSize) {
Spacer = ", "; // Space before immediate operand field
FieldSize = s.ImmediateFieldSize;
}
// Write space
OutFile.Put(Spacer);
// Write byte or bytes
switch (FieldSize) {
case 1: // Write single byte
OutFile.PutHex(Get<uint8>(i));
break;
case 2: // Write two bytes
OutFile.PutHex(Get<uint16>(i));
break;
case 3: // Write three bytes (operands for "enter" instruction)
OutFile.PutHex(Get<uint16>(i));
OutFile.Put(", ");
OutFile.PutHex(Get<uint8>(i+2));
break;
case 4: // Write four bytes
if ((s.Operands[0] & 0xFE) == 0x84) {
// Far jump/call address
OutFile.PutHex(Get<uint16>(i));
OutFile.Put(" ");
OutFile.PutHex(Get<uint16>(i+2));
}
else {
// Any other 32 bit operand
OutFile.PutHex(Get<uint32>(i));
}
break;
case 6: // Write six bytes (far jump address)
OutFile.PutHex(Get<uint32>(i));
OutFile.Put(" ");
OutFile.PutHex(Get<uint16>(i+4));
break;
case 8: // Write eight bytes
OutFile.PutHex(Get<uint64>(i));
break;
}
// Search for relocation
SARelocation rel1; // Make relocation records for searching
rel1.Section = Section;
rel1.Offset = i; // rel1 marks current field in instruction
// Is there a relocation source exactly here?
int32 irel = Relocations.Exists(rel1); // Finds relocation with source = i
if (irel > 0) {
// This field has a relocation. Indicate relocation type
// 0 = unknown, 1 = direct, 2 = self-relative, 3 = image-relative,
// 4 = segment relative, 5 = relative to arbitrary ref. point, 8 = segment address/descriptor
uint32 RelType = Relocations[irel].Type;
if (RelType) {
OutFile.Put(Lookup(RelocationTypeNames, RelType));
}
if (Relocations[irel].Size > FieldSize) {
// Relocation has wrong size
OutFile.Put(" Misplaced relocation.");
}
}
// Point to next byte
i += FieldSize;
}
// New line
OutFile.NewLine();
}
void CDisassembler::CountInstructions() {
// Count total number of instructions defined in opcodes.cpp
// Two instructions are regarded as the same and counted as one if they
// have the same name and differ only in the bits that define register
// name, operand size, etc.
uint32 map; // Map number
uint32 index; // Index into map
uint32 n; // Number of instructions with same code
uint32 iset; // Instruction set
uint32 instructions = 0; // Total number of instructions
uint32 mmxinstr = 0; // Number of MMX instructions
uint32 sseinstr = 0; // Number of SSE instructions
uint32 sse2instr = 0; // Number of SSE2 instructions
uint32 sse3instr = 0; // Number of SSE3 instructions
uint32 ssse3instr = 0; // Number of SSSE3 instructions
uint32 sse41instr = 0; // Number of SSE4.1 instructions
uint32 sse42instr = 0; // Number of SSE4.2 instructions
uint32 AVXinstr = 0; // Number of AVX instructions
uint32 FMAinstr = 0; // Number of FMA3 and later instructions
uint32 AVX2instr = 0; // Number of AVX2 instructions
uint32 BMIinstr = 0; // Number of BMI instructions and other small instruction sets
uint32 AVX512instr = 0; // Number of AVX-512 instructions
uint32 MICinstr = 0; // Number of MIC instructions
uint32 AMDinstr = 0; // Number of AMD instructions
uint32 VIAinstr = 0; // Number of AMD instructions
uint32 privilinstr = 0; // Number of privileged instructions
uint32 undocinstr = 0; // Number of undocumented instructions
uint32 droppedinstr = 0; // Number of opcodes planned but never implemented
uint32 VEXdouble = 0; // Number of instructions that have both VEX and non-VEX version
SOpcodeDef const * opcode; // Pointer to map entry
// Loop through all maps
for (map = 0; map < NumOpcodeTables1; map++) {
// Loop through each map
for (index = 0; index < OpcodeTableLength[map]; index++) {
opcode = OpcodeTables[map] + index;
if (opcode->InstructionFormat && opcode->Name
&& !opcode->TableLink && !(opcode->InstructionFormat & 0x8000)) {
// instruction is defined
if ((opcode->InstructionFormat & 0xFFF) == 3
&& index > 0 && (opcode-1)->Name
&& strcmp(opcode->Name, (opcode-1)->Name) == 0) {
// Same as previous instruction, just with another register
continue; // Don't count this
}
n = 1; // Default = one instruction per map entry
// Check if we have multiple instructions with different prefixes
if (opcode->Options & 1) {
if (opcode->AllowedPrefixes & 0x3000) {
n++; // Extra instruction with W prefix bit
}
else if (opcode->AllowedPrefixes & 0xE00) {
if (opcode->AllowedPrefixes & 0x200) n++; // Extra instruction with 66 prefix
if (opcode->AllowedPrefixes & 0x400) n++; // Extra instruction with F3 prefix
if (opcode->AllowedPrefixes & 0x800) n++; // Extra instruction with F2 prefix
}
else if (opcode->AllowedPrefixes & 0x100) {
n++; // Extra instruction with 66 prefix
if (opcode->AllowedPrefixes & 0x1000) n++;// Extra instruction with L prefix bit
}
}
if (opcode->Options & 2) VEXdouble += n; // Instructions that have both VEX and non-VEX version
instructions += n; // Count total instructions
iset = opcode->InstructionSet; // Instruction set
if (iset & 0x20000) {
droppedinstr += n; iset = 0; // Opcodes planned but never implemented
}
if (iset & 0x800) privilinstr += n; // Privileged instruction
if (opcode->InstructionFormat & 0x4000) undocinstr += n; // Undocumented instruction
switch (iset & 0x37FF) {
case 7: // MMX
mmxinstr += n; break;
case 0x11: // SSE
sseinstr += n; break;
case 0x12: // SSE2
sse2instr += n; break;
case 0x13: // SSE3
sse3instr += n; break;
case 0x14: // SSSE3
ssse3instr += n; break;
case 0x15: // SSE4.1
sse41instr += n; break;
case 0x16: // SSE4.2
sse42instr += n; break;
case 0x17: case 0x18: case 0x19: // VEX etc.
AVXinstr += n; break;
case 0x1A: case 0x1B: // FMA and later instructions
FMAinstr += n; break;
case 0x1C: // AVX2 instructions
AVX2instr += n; break;
case 0x1D: case 0x1E: // BMI and other small instruction sets
BMIinstr += n; break;
case 0x20: // AVX-512 instructions
AVX512instr += n; break;
case 0x80: // MIC instructions
MICinstr += n; break;
case 0x1001: case 0x1002: case 0x1004: case 0x1005: case 0x1006: // AMD
AMDinstr += n; break;
case 0x2001: // VIA
VIAinstr += n; break;
}
}
}
}
// output result
printf("\n\nNumber of instruction opcodes supported by disassembler:\n%5i Total, including:",
instructions);
printf("\n%5i Privileged instructions", privilinstr);
printf("\n%5i MMX instructions", mmxinstr);
printf("\n%5i SSE instructions", sseinstr);
printf("\n%5i SSE2 instructions", sse2instr);
printf("\n%5i SSE3 instructions", sse3instr);
printf("\n%5i SSSE3 instructions", ssse3instr);
printf("\n%5i SSE4.1 instructions", sse41instr);
printf("\n%5i SSE4.2 instructions", sse42instr);
printf("\n%5i AVX instructions etc.", AVXinstr);
printf("\n%5i AVX2 instructions", AVX2instr);
printf("\n%5i FMA3 instructions", FMAinstr);
printf("\n%5i BMI/micsellaneous instr.", BMIinstr);
printf("\n%5i AVX-512 instructions", AVX512instr);
printf("\n%5i MIC/Xeon Phi instructions", MICinstr);
printf("\n%5i AMD instructions", AMDinstr);
printf("\n%5i VIA instructions", VIAinstr);
printf("\n%5i instructions planned but never implemented in any CPU", droppedinstr);
printf("\n%5i undocumented or illegal instructions", undocinstr);
printf("\n%5i instructions have both VEX and non-VEX versions", VEXdouble);
printf("\n");
#if 0 // temporary test code
// find entries with 0x2000 prefix code
printf("\n\nInstructions with operand swap flag:\n");
// Loop through all maps
for (map = 0; map < NumOpcodeTables1; map++) {
// Loop through each map
for (index = 0; index < OpcodeTableLength[map]; index++) {
opcode = OpcodeTables[map] + index;
if ((opcode->AllowedPrefixes & 0x2000) == 0x2000) {
printf("\n%04X %02X %s", map, index, opcode->Name);
}
}
}
/*
printf("\n\nTables linked by type 0x0E:\n");
// Loop through all maps
for (map = 0; map < NumOpcodeTables1; map++) {
// Loop through each map
for (index = 0; index < OpcodeTableLength[map]; index++) {
opcode = OpcodeTables[map] + index;
if (opcode->TableLink == 0x0E) {
printf(" 0x%02X", opcode->InstructionSet);
}
}
}*/
printf("\n");
#endif
}