URL
https://opencores.org/ocsvn/forwardcom/forwardcom/trunk
Subversion Repositories forwardcom
[/] [forwardcom/] [bintools/] [disasm2.cpp] - Rev 50
Compare with Previous | Blame | View Log
/**************************** disasm2.cpp ******************************** * Author: Agner Fog * Date created: 2017-04-26 * Last modified: 2021-07-16 * Version: 1.11 * Project: Binary tools for ForwardCom instruction set * Module: disassem.h * Description: * Disassembler for ForwardCom * * Copyright 2007-2021 GNU General Public License http://www.gnu.org/licenses *****************************************************************************/ #include "stdafx.h" static const char * commentSeparator = "//"; // Comment separator in output assembly file /************************** class CDisassembler ***************************** Most member functions of CDisassembler are defined in disasm1.cpp Only the functions that produce output are defined here: ******************************************************************************/ void CDisassembler::writeSymbolName(uint32_t symi) { // Write symbol name. symi = symbol index uint32_t sname = symbols[symi].st_name; if (sname == 0) outFile.put("no_name"); else if (sname >= stringBuffer.dataSize()) outFile.put("(illegal name index)"); else outFile.put((char*)stringBuffer.buf() + sname); } void CDisassembler::writeSectionName(int32_t SegIndex) { // Write name of section, segment or group from section index const char * name = "noname"; if (sectionHeaders[SegIndex].sh_name < stringBuffer.dataSize()) { name = (char*)stringBuffer.buf() + sectionHeaders[SegIndex].sh_name; } outFile.put(name); } // Find any labels at current position and next void CDisassembler::writeLabels() { // check section type uint8_t sectionType = sectionHeaders[section].sh_type; if (!(sectionType & SHT_ALLOCATED)) { return; // section is not allocated } // start at new line if (outFile.getColumn() && !debugMode) outFile.newLine(); if (iInstr == currentFunctionEnd && currentFunction) { // Current function is ending here writeSymbolName(currentFunction); outFile.put(' '); outFile.tabulate(asmTab2); // at least one space outFile.put("end"); outFile.newLine(); currentFunction = 0; currentFunctionEnd = 0; } //bool isFunction = false; // Make dummy symbol to search for ElfFwcSym currentPosition; currentPosition.st_section = section; currentPosition.st_value = iInstr; symbolExeAddress(currentPosition); // Search for any symbol here. Look for any misplaced symbols we might have skipped before last output uint32_t numSymbols = 0; // Check if multiple symbols at same place while (nextSymbol < symbols.numEntries() && symbols[nextSymbol] < currentPosition) { if (symbols[nextSymbol].st_section == currentPosition.st_section && iInstr && symbols[nextSymbol].st_type != STT_CONSTANT) { outFile.put(commentSeparator); outFile.put(" Warning: Misplaced symbol: "); writeSymbolName(nextSymbol); outFile.put(" at offset "); outFile.putHex(symbols[nextSymbol].st_value); outFile.newLine(); symbols[nextSymbol].st_other |= 0x80000000; // Remember symbol has been written } nextSymbol++; } // Write all symbols at current position while (nextSymbol < symbols.numEntries() && symbols[nextSymbol] == currentPosition) { if (symbols[nextSymbol].st_type != STT_CONSTANT) { if (numSymbols++) { // Multiple symbols at same position if (debugMode) { outFile.put("; "); // cannot show multiple lines at same address in debug mode } else { outFile.put("\n"); // put on separate lines } } writeSymbolName(nextSymbol); if (symbols[nextSymbol].st_type == STT_FUNC && symbols[nextSymbol].st_bind != STB_LOCAL) { // This is a function if (debugMode) { outFile.put(": "); } else outFile.put(": function"); //isFunction = true; currentFunction = nextSymbol; // Remember which function we are in if (symbols[nextSymbol].st_unitsize) { // Calculate end of current function if (symbols[nextSymbol].st_unitnum == 0) symbols[nextSymbol].st_unitnum = 1; currentFunctionEnd = iInstr + symbols[nextSymbol].st_unitsize * symbols[nextSymbol].st_unitnum; } else currentFunctionEnd = 0; // Function size is not known } else if (codeMode & 1) { // local label outFile.put(": "); } symbols[nextSymbol].st_other |= 0x80000000; // Remember symbol has been written } nextSymbol++; } if (numSymbols) { if (codeMode == 1) { // if (!isFunction) outFile.put(':'); // has already been written if (!debugMode) outFile.newLine(); // Code. Put label on separate line } else { outFile.put(':'); // Data. Make space after last label } } } void CDisassembler::writeDataItems() { // Write contents of data section to output file uint32_t nextLabel = 0; uint32_t nextRelocation = 0; uint32_t dataSize = 4; uint32_t currentSymbol; uint32_t sequenceEnd; ElfFwcReloc rel; // relocation record for searching uint32_t irel; // index to relocation record uint32_t numRel; // number of relocations found at current position operandType = 2; bool isFloat = false; // translate addresses if executable ElfFwcSym currentPosition; currentPosition.st_section = section; currentPosition.st_value = iInstr; symbolExeAddress(currentPosition); // find first relocation rel.r_offset = iInstr; rel.r_section = section; irel = relocations.findFirst(rel); irel &= 0x7FFFFFFF; if (irel < relocations.numEntries() && relocations[irel].r_section == section) { nextRelocation = (uint32_t)relocations[irel].r_offset; } else nextRelocation = sectionEnd; // Loop through section while (iInstr < sectionEnd) { // Search for symbol labels writeLabels(); if (nextSymbol > 1) { // Get data size from current symbol currentSymbol = nextSymbol - 1; if (symbols[currentSymbol].st_section == currentPosition.st_section) { dataSize = symbols[currentSymbol].st_unitsize; if (dataSize > 8) dataSize = 8; if (dataSize == 0) dataSize = 4; isFloat = (symbols[currentSymbol].st_other & STV_FLOAT) != 0; } } // Get position of next symbol if (nextSymbol < symbols.numEntries()) { nextLabel = (uint32_t)symbols[nextSymbol].st_value; // translate to local offset if (isExecutable) nextLabel -= (uint32_t)sectionHeaders[section].sh_addr; } else nextLabel = sectionEnd; // Search for relocations rel.r_offset = iInstr; numRel = relocations.findAll(&irel, rel); if (numRel) { // Relocation found. Find size // Relocation size overrides any symbol size switch (relocations[irel].r_type & R_FORW_RELSIZEMASK) { case R_FORW_8: dataSize = 1; break; case R_FORW_16: case R_FORW_32LO: case R_FORW_32HI: dataSize = 2; break; case R_FORW_24: dataSize = 4; // 3 bytes. Round up to 4 break; case R_FORW_32: case R_FORW_64LO: case R_FORW_64HI: dataSize = 4; break; case R_FORW_64: dataSize = 8; break; default: writeError("Unknown data size for relocation"); dataSize = 4; break; } isFloat = false; if (numRel > 1) writeError("Overlapping relocations"); // Find position of next relocation if (irel+1 < relocations.numEntries() && relocations[irel+1].r_section == section) { nextRelocation = (uint32_t)relocations[irel+1].r_offset; } else nextRelocation = sectionEnd; } if (numRel) { // There is a relocation here. Write only one data item // Write type outFile.tabulate(asmTab1); switch (dataSize) { case 1: outFile.put("int8 "); break; case 2: outFile.put("int16 "); break; case 4: outFile.put("int32 "); break; case 8: outFile.put("int64 "); break; } outFile.tabulate(asmTab2); writeRelocationTarget(iInstr, dataSize); // Write comment with relocation type outFile.put(' '); outFile.tabulate(asmTab3); outFile.put(commentSeparator); outFile.put(' '); if (sectionEnd + sectionAddress> 0xFFFF) outFile.putHex((uint32_t)(iInstr + sectionAddress), 2); else outFile.putHex((uint16_t)(iInstr + sectionAddress), 2); outFile.put(" _ "); switch(relocations[irel].r_type & R_FORW_RELTYPEMASK) { case R_FORW_ABS: outFile.put("absolute address"); break; case R_FORW_SELFREL: outFile.put("self-relative"); break; case R_FORW_IP_BASE: outFile.put("relative to __ip_base"); break; case R_FORW_DATAP: outFile.put("relative to __datap_base"); break; case R_FORW_THREADP: outFile.put("relative to __threadp_base"); break; case R_FORW_REFP: outFile.put("relative to "); writeSymbolName(relocations[irel].r_refsym & 0x7FFFFFFF); break; case R_FORW_SYSFUNC: outFile.put("system function ID"); break; case R_FORW_SYSMODUL: outFile.put("system module ID"); break; case R_FORW_SYSCALL: outFile.put("system module and function ID"); break; case R_FORW_DATASTACK: outFile.put("data stack size"); break; case R_FORW_CALLSTACK: outFile.put("call stack size"); break; case R_FORW_REGUSE: outFile.put("register use"); break; default: outFile.put("unknown relocation type"); break; } iInstr += dataSize; } else { // Write multiple data items. Find where sequence ends sequenceEnd = sectionEnd; if (nextLabel < sequenceEnd && nextLabel > iInstr) sequenceEnd = nextLabel; if (nextRelocation < sequenceEnd && nextRelocation > iInstr) sequenceEnd = nextRelocation; // Number of data items in this sequence uint32_t num = (sequenceEnd - iInstr) / dataSize; if (num == 0) { dataSize = sequenceEnd - iInstr; // Reduce data size to avoid going past sequenceEnd while (dataSize & (dataSize-1)) dataSize--; // Round down to nearest power of 2 num = 1; } // Number of data items per line uint32_t itemsPerLine = 4; if (dataSize > 4) itemsPerLine = 2; if (dataSize < 2) itemsPerLine = 8; uint32_t lineEnd = iInstr + itemsPerLine * dataSize; if (lineEnd > sequenceEnd) { // Round down to multiple of dataSize itemsPerLine = (sequenceEnd - iInstr) / dataSize; lineEnd = iInstr + itemsPerLine * dataSize; } // Write type outFile.tabulate(asmTab1); switch (dataSize) { case 1: outFile.put("int8 "); break; case 2: outFile.put("int16 "); break; case 4: outFile.put("int32 "); break; case 8: outFile.put("int64 "); break; } outFile.tabulate(asmTab2); // Write items uint32_t lineBegin = iInstr; while (iInstr < lineEnd) { if (sectionHeaders[section].sh_type == SHT_NOBITS) { // BSS section has no data in buffer outFile.put('0'); } else { // Write item switch (dataSize) { case 1: outFile.putHex(*(uint8_t*)(sectionBuffer + iInstr)); break; case 2: outFile.putHex(*(uint16_t*)(sectionBuffer + iInstr)); break; case 4: outFile.putHex(*(uint32_t*)(sectionBuffer + iInstr)); break; case 8: outFile.putHex(*(uint64_t*)(sectionBuffer + iInstr)); break; } } iInstr += dataSize; if (iInstr < lineEnd) outFile.put(", "); // comma if not the last item on line } // Write data comment outFile.put(' '); outFile.tabulate(asmTab3); outFile.put(commentSeparator); outFile.put(' '); // write address uint64_t address = lineBegin + sectionAddress; if (sectionHeaders[section].sh_flags & SHF_IP) { // IP based section. subtract ip_base for continuity with code section address -= fileHeader.e_ip_base; } if (sectionEnd + sectionAddress > 0xFFFF) outFile.putHex(uint32_t(address), 2); else outFile.putHex(uint16_t(address), 2); if (sectionHeaders[section].sh_type != SHT_NOBITS) { // skip data if BSS section outFile.put(" _ "); // Write data in alternative form for (uint32_t i = lineBegin; i < lineEnd; i += dataSize) { switch (dataSize) { case 1: { // bytes. Write as characters char c = *(char*)(sectionBuffer + i); outFile.put((uint8_t)c < ' ' ? '.' : c); break; } case 2: if (isFloat) { // half precision float outFile.putFloat(half2float(*(uint16_t*)(sectionBuffer + i))); } else { // 16 bit integer. Write as signed decimal outFile.putDecimal(*(int16_t*)(sectionBuffer + i), 1); } if (i + dataSize < lineEnd) outFile.put(", "); // Comma except before last item break; case 4: if (isFloat) { // single precision float outFile.putFloat(*(float*)(sectionBuffer + i)); } else { // 16 bit integer. Write as signed decimal outFile.putDecimal(*(int32_t*)(sectionBuffer + i), 1); } if (i + dataSize < lineEnd) outFile.put(", "); // Comma except before last item break; case 8: if (isFloat) { // double precision float outFile.putFloat(*(double*)(sectionBuffer + i)); } else { // 64 bit integer. Write as signed decimal if not huge int64_t x = *(int64_t*)(sectionBuffer + i); if (x == (int32_t)x) outFile.putDecimal((int32_t)x, 1); } if (i + dataSize < lineEnd) outFile.put(", "); // Comma except before last item break; default:; } } } } if (iInstr < sectionEnd) outFile.newLine(); } // write label at end of data section, if any if ((section + 1 == sectionHeaders.numEntries() || ((sectionHeaders[section].sh_flags ^ sectionHeaders[section + 1].sh_flags) & SHF_BASEPOINTER)) && nextSymbol < symbols.numEntries()) { writeLabels(); } } static const uint32_t relocationSizes[16] = {0, 1, 2, 3, 4, 4, 4, 8, 8, 8, 0, 0, 0, 0, 0, 0}; void CDisassembler::writeRelocationTarget(uint32_t src, uint32_t size) { // Write relocation target for this source position // Find relocation ElfFwcReloc rel; rel.r_offset = src; rel.r_section = section; //uint32_t irel; // index to relocation record uint32_t n = relocations.findAll(&relocation, rel); if (n == 0) return; if (n > 1) { writeWarning(n ? "Overlapping relocations" : "No relocation found here"); return; } relocation++; // add 1 to avoid zero relocations[relocation-1].r_refsym |= 0x80000000; // Remember relocation has been used OK // write scale factor if scale factor != 1 and not a jump target bool writeScale = relocations[relocation-1].r_type & R_FORW_RELSCALEMASK; if (writeScale || codeMode > 1) outFile.put('('); uint32_t isym = relocations[relocation-1].r_sym; writeSymbolName(isym); // Find any addend int32_t expectedAddend = 0; int32_t addend = relocations[relocation-1].r_addend; if ((relocations[relocation-1].r_type & R_FORW_RELTYPEMASK) == R_FORW_SELFREL) { if (fInstr) { // Expected addend for self-relative address if (fInstr->addrSize) { // Jump instruction or memory operand expectedAddend = fInstr->addrPos - instrLength * 4; } else { // Relocation of immediate operand expectedAddend = fInstr->immPos - instrLength * 4; } } } addend -= expectedAddend; if ((relocations[relocation-1].r_type & R_FORW_RELTYPEMASK) == R_FORW_REFP) { // has reference point outFile.put('-'); uint32_t isym2 = relocations[relocation-1].r_refsym & 0x7FFFFFFF; // remove 0x80000000 flag writeSymbolName(isym2); } if (writeScale) { // write scale factor outFile.put(")/"); outFile.putDecimal(1 << relocations[relocation-1].r_type & R_FORW_RELSCALEMASK); } // Check size of relocation if (addend > 0) { outFile.put('+'); outFile.putHex((uint32_t)addend); } else if (addend < 0) { outFile.put('-'); outFile.putHex(uint32_t(-addend)); } if (codeMode > 1 && !writeScale) outFile.put(')'); // Check for errors if (n > 1) writeError("Overlapping relocations here"); uint32_t relSize = relocationSizes[relocations[relocation-1].r_type >> 8 & 0x0F]; if (relSize < size) writeWarning("Relocation size less than data field"); if (relSize > size) writeError("Relocation size bigger than data field"); } void CDisassembler::writeJumpTarget(uint32_t src, uint32_t size) { // Write relocation jump target for this source position // Find relocation ElfFwcReloc rel; rel.r_offset = src; rel.r_section = section; //uint32_t irel; // index to relocation record uint32_t n = relocations.findAll(&relocation, rel); if (n == 0) return; if (n > 1) { writeWarning(n ? "Overlapping relocations" : "No relocation found here"); return; } relocation++; // add 1 to avoid zero relocations[relocation-1].r_refsym |= 0x80000000; // Remember relocation has been used OK // write scale factor if scale factor != 1 and not a jump target if (codeMode > 1) outFile.put('('); uint32_t isym = relocations[relocation-1].r_sym; writeSymbolName(isym); // Find any addend int32_t expectedAddend = 0; int32_t addend = relocations[relocation-1].r_addend; if ((relocations[relocation-1].r_type & R_FORW_RELTYPEMASK) == R_FORW_SELFREL && fInstr) { expectedAddend = fInstr->jumpPos - instrLength * 4; } addend -= expectedAddend; // Check size of relocation uint32_t expectedRelSize = size; // Expected size of relocation if (fInstr) { expectedRelSize = fInstr->jumpSize; } if (addend > 0) { outFile.put('+'); outFile.putHex((uint32_t)addend); } else if (addend < 0) { outFile.put('-'); outFile.putHex(uint32_t(-addend)); } if (codeMode > 1) outFile.put(')'); // Check for errors if (n > 1) writeError("Overlapping relocations here"); uint32_t relSize = relocationSizes[relocations[relocation-1].r_type >> 8 & 0x0F]; if (relSize < expectedRelSize) writeWarning("Relocation size less than data field"); if (relSize > expectedRelSize) writeError("Relocation size bigger than data field"); } /* int CDisassembler::writeFillers() { return 1; } void CDisassembler::writeAlign(uint32_t a) { // Write alignment directive outFile.put("ALIGN"); outFile.tabulate(asmTab1); outFile.putDecimal(a); outFile.newLine(); } */ void CDisassembler::writeFileBegin() { outFile.setFileType(FILETYPE_ASM); if (debugMode) return; // Initial comment outFile.put(commentSeparator); if (outputFile == cmd.outputListFile) { outFile.put(" Assembly listing of file: "); } else { outFile.put(" Disassembly of file: "); } outFile.put(cmd.getFilename(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(' '); outFile.put(timestring); outFile.newLine(); } // Write special symbols and addresses if executable file if (isExecutable) { outFile.newLine(); outFile.put(commentSeparator); outFile.put(" __ip_base = "); outFile.putHex(fileHeader.e_ip_base); outFile.newLine(); outFile.put(commentSeparator); outFile.put(" __datap_base = "); outFile.putHex(fileHeader.e_datap_base); outFile.newLine(); outFile.put(commentSeparator); outFile.put(" __threadp_base = "); outFile.putHex(fileHeader.e_threadp_base); outFile.newLine(); outFile.put(commentSeparator); outFile.put(" __entry_point = "); outFile.putHex(fileHeader.e_entry); outFile.newLine(); } // Write imported and exported symbols outFile.newLine(); writePublicsAndExternals(); } void CDisassembler::writePublicsAndExternals() { // Write public and external symbol definitions if (debugMode) return; uint32_t i; // Loop counter uint32_t linesWritten = 0; // Count lines written // Loop through public symbols for (i = 0; i < symbols.numEntries(); i++) { if (symbols[i].st_bind && symbols[i].st_section) { // Symbol is public outFile.put("public "); // Write name writeSymbolName(i); // Code or data if (symbols[i].st_type == STT_FUNC) { outFile.put(": function"); if (symbols[i].st_other & STV_REGUSE) { // Write register use outFile.put(", registeruse = "); outFile.putHex(symbols[i].st_reguse1); outFile.put(", "); outFile.putHex(symbols[i].st_reguse2); } } else if (symbols[i].st_other & STV_EXEC) outFile.put(": function"); else if (symbols[i].st_type == STT_OBJECT || symbols[i].st_type == STT_SECTION) { // data object. get base pointer if (symbols[i].st_other & (STV_IP | STV_EXEC)) outFile.put(": ip"); else if (symbols[i].st_other & STV_DATAP) outFile.put(": datap"); else if (symbols[i].st_other & STV_THREADP) outFile.put(": threadp"); else if (symbols[i].st_other & STV_WRITE) outFile.put(": datap"); } //else if (symbols[i].st_type == STT_FILE) outFile.put(": filename"); //else if (symbols[i].st_type == STT_SECTION) outFile.put(": section"); else if (symbols[i].st_type == STT_CONSTANT) { outFile.put(": constant"); outFile.newLine(); // write value outFile.put("% "); writeSymbolName(i); outFile.put(" = "); outFile.putHex(symbols[i].st_value); } else if (symbols[i].st_type == 0) { outFile.put(": absolute"); outFile.newLine(); } else { outFile.put(": unknown type. type="); outFile.putHex(symbols[i].st_type); outFile.put(", bind="); outFile.putHex(symbols[i].st_bind); outFile.put(", other="); outFile.putHex(symbols[i].st_other); } // Check if weak or communal if (symbols[i].st_bind & STB_WEAK) { outFile.put(" weak"); } if (symbols[i].st_type == STT_COMMON || (symbols[i].st_other & STV_COMMON)) outFile.put(", communal"); outFile.newLine(); linesWritten++; } } // Blank line if anything written if (linesWritten) { outFile.newLine(); linesWritten = 0; } // Loop through external symbols for (i = 0; i < symbols.numEntries(); i++) { if (symbols[i].st_bind && !symbols[i].st_section) { // Symbol is external outFile.put("extern "); // Write name writeSymbolName(i); // Code or data if (symbols[i].st_type == STT_FUNC) { outFile.put(": function"); if (symbols[i].st_other & STV_REGUSE) { // Write register use outFile.put(", registeruse = "); outFile.putHex(symbols[i].st_reguse1); outFile.put(", "); outFile.putHex(symbols[i].st_reguse2); } } else if (symbols[i].st_other & STV_EXEC) outFile.put(": function"); //else if (symbols[i].st_other & (STV_READ | SHF_WRITE)) outFile.put(": data"); else if (symbols[i].st_other & STV_IP) outFile.put(": ip"); else if (symbols[i].st_other & STV_DATAP) outFile.put(": datap"); else if (symbols[i].st_other & STV_THREADP) outFile.put(": threadp"); else if (symbols[i].st_type == STT_OBJECT) outFile.put(": datap"); else if (symbols[i].st_type == STT_CONSTANT) outFile.put(": constant"); else if (symbols[i].st_type == 0) outFile.put(": absolute"); else { outFile.put(": unknown type. type="); outFile.putHex(symbols[i].st_type); outFile.put(", other="); outFile.putHex(symbols[i].st_other); } // Check if weak or communal if (symbols[i].st_bind & STB_WEAK) { if (symbols[i].st_bind == STB_UNRESOLVED) outFile.put(" // unresolved!"); else outFile.put(", weak"); } if (symbols[i].st_type == STT_COMMON) outFile.put(", communal"); // Finished line outFile.newLine(); linesWritten++; } } // Blank line if anything written if (linesWritten) { outFile.newLine(); linesWritten = 0; } } void CDisassembler::writeFileEnd() { // Write end of file } void CDisassembler::writeSectionBegin() { // Write begin of section outFile.newLine(); // Blank line // Check if section is valid if (section == 0 || section >= sectionHeaders.numEntries()) { // Illegal segment entry outFile.put("UNKNOWN SEGMENT"); outFile.newLine(); return; } // Write segment name writeSectionName(section); outFile.put(" "); // tabulate outFile.tabulate(asmTab1); // Write "segment" outFile.put("section"); // Write type if (sectionHeaders[section].sh_flags & SHF_READ) outFile.put(" read"); if (sectionHeaders[section].sh_flags & SHF_WRITE) outFile.put(" write"); if (sectionHeaders[section].sh_flags & SHF_EXEC) outFile.put(" execute"); else if (sectionHeaders[section].sh_flags & SHF_IP) outFile.put(" ip"); if (sectionHeaders[section].sh_flags & SHF_DATAP) outFile.put(" datap"); if (sectionHeaders[section].sh_flags & SHF_THREADP) outFile.put(" threadp"); if (sectionHeaders[section].sh_flags & SHF_EXCEPTION_HND) outFile.put(" exception_hand"); if (sectionHeaders[section].sh_flags & SHF_EVENT_HND) outFile.put(" event_hand"); if (sectionHeaders[section].sh_flags & SHF_DEBUG_INFO) outFile.put(" debug_info"); if (sectionHeaders[section].sh_flags & SHF_COMMENT) outFile.put(" comment_info"); if (sectionHeaders[section].sh_type == SHT_NOBITS) outFile.put(" uninitialized"); if (sectionHeaders[section].sh_type == SHT_COMDAT) outFile.put(" communal"); // Write alignment uint32_t align = 1 << sectionHeaders[section].sh_align; outFile.put(" align="); if (align < 16) outFile.putDecimal(align); else outFile.putHex(align); // tabulate to comment outFile.put(" "); outFile.tabulate(asmTab3); outFile.put(commentSeparator); if (codeMode == 1) { // code section outFile.put(" address/4. "); } else { outFile.put(" address. "); } // Write section number outFile.put("section "); outFile.putDecimal(section); // Write library and module, if available if (sectionHeaders[section].sh_module && sectionHeaders[section].sh_module < secStringTableLen) { outFile.put(". "); if (sectionHeaders[section].sh_library) { outFile.put(secStringTable + sectionHeaders[section].sh_library); outFile.put(':'); } outFile.put(secStringTable + sectionHeaders[section].sh_module); } // New line outFile.newLine(); } void CDisassembler::writeSectionEnd() { // Write end of section outFile.newLine(); // Write segment name writeSectionName(section); outFile.put(" "); outFile.tabulate(asmTab1); // Write "segment" outFile.put("end"); outFile.newLine(); } void CDisassembler::writeInstruction() { // Write instruction and operands // Check if instruction crosses section boundary if (iInstr + instrLength * 4 > sectionEnd) writeError("Instruction crosses section boundary"); // Find instruction in instruction_list SInstruction2 iRecSearch; iRecSearch.format = format; iRecSearch.category = fInstr->category; iRecSearch.op1 = pInstr->a.op1; relocation = 0; if (iRecSearch.category == 4) { // jump instruction // Set op1 = opj for jump instructions in format 2.5.x and 3.1.0 if (fInstr->imm2 & 0x80) { iRecSearch.op1 = pInstr->b[0]; // OPJ is in IM1 if (fInstr->imm2 & 0x40) iRecSearch.op1 = 63; // OPJ has fixed value if (fInstr->imm2 & 0x10) iRecSearch.op1 = pInstr->b[7]; // OPJ is in upper part of IM2 } // Set op1 for template D if (fInstr->tmplate == 0xD) iRecSearch.op1 &= 0xF8; } // Insert op2 only if template E if (instrLength > 1 && fInstr->tmplate == 0xE && !(fInstr->imm2 & 0x100)) { iRecSearch.op2 = pInstr->a.op2; } else iRecSearch.op2 = 0; uint32_t index, n, i; n = instructionlist.findAll(&index, iRecSearch); if (n == 0) { // Instruction not found in list writeWarning("Unknown instruction: "); for (i = 0; i < instrLength; i++) { outFile.putHex(pInstr->i[i]); if (i + 1 < instrLength) outFile.put(" "); } writeCodeComment(); outFile.newLine(); return; } // One or more matches in instruction table. Check if one of these fits the operand type and format uint32_t otMask = 0x101 << operandType; // operand type mask for supported + optional bool otFits = true; // Check if operand type fits bool formatFits = true; // Check if format fits for (i = 0; i < n; i++) { // search through matching instruction table entries if (operandType < 4 && !(fInstr->vect & 1)) { // general purpose register otFits = (instructionlist[index + i].optypesgp & otMask) != 0; } else { // vector register otFits = ((instructionlist[index + i].optypesscalar | instructionlist[index + i].optypesvector) & otMask) != 0; } if (fInstr->category >= 3) { // Multi format or jump instruction. Check if format allowed formatFits = (instructionlist[index+i].format & ((uint64_t)1 << fInstr->formatIndex)) != 0; } if (instructionlist[index+i].opimmediate == OPI_IMPLICIT) { // check if implicit operand fits const uint8_t * bb = pInstr->b; uint32_t x = 0; // get value of immediate operand switch (fInstr->immSize) { case 1: // 8 bits x = *(int8_t*)(bb + fInstr->immPos); break; case 2: // 16 bits x = *(int16_t*)(bb + fInstr->immPos); break; case 4: default: // 32 bits x = *(int32_t*)(bb + fInstr->immPos); break; } if (instructionlist[index+i].implicit_imm != x) formatFits = false; } if (otFits && formatFits) { index += i; // match found break; } } if (!otFits) { writeWarning("No instruction fits the operand type"); } else if (!formatFits) { writeWarning("Error in instruction format"); } // Save pointer to record iRecord = &instructionlist[index]; // Template C or D has no OT field. Get operand type from instruction list if template C or D if (((iRecord->templt) & 0xFE) == 0xC) { uint32_t i, optypeSuppport = iRecord->optypesgp; if (fInstr->vect) optypeSuppport = iRecord->optypesscalar | iRecord->optypesvector; for (i = 0; i < 16; i++) { // Search for supported operand type if (optypeSuppport & (1 << i)) break; } operandType = i & 7; } // Get variant and options variant = iRecord->variant; // Write jump instruction or normal instruction if (fInstr->category == 4 && fInstr->jumpSize) { writeJumpInstruction(); } else { writeNormalInstruction(); } // Write comment writeCodeComment(); // End instruction outFile.newLine(); } // Select a register from template uint8_t getRegister(const STemplate * pInstr, int i) { // i = 5: RT, 6: RS, 7: RU, 8: RD uint8_t r = 0xFF; switch (i) { case 5: r = pInstr->a.rt; break; case 6: r = pInstr->a.rs; break; case 7: r = pInstr->a.ru; break; case 8: r = pInstr->a.rd; break; } return r; } uint8_t findFallback(SFormat const * fInstr, STemplate const * pInstr, int nOperands) { // Find the fallback register for an instruction code. // The return value is the register that is used for fallback // The return value is 0xFF if the fallback is zero or there is no fallback if (fInstr->tmplate != 0xA && fInstr->tmplate != 0xE) { return 0xFF; // cannot have fallback } uint8_t operands[6] = {0,0,0,0,0,0}; // Make list of operands int j = 5; if (fInstr->opAvail & 0x01) operands[j--] = 1; // immediate operand if (fInstr->opAvail & 0x02) operands[j--] = 2; // memory operand if (fInstr->opAvail & 0x10) operands[j--] = 5; // register RT if (fInstr->opAvail & 0x20) operands[j--] = 6; // register RS if (fInstr->opAvail & 0x40) operands[j--] = 7; // register RU //if (fInstr->opAvail & 0x80) operands[j--] = 8; // don't include register RD yet uint8_t fallback; // fallback register bool fallbackSeparate = false; // fallback register is not first source register if (nOperands >= 3 && j < 3) { fallback = operands[3]; // first of three source operands } else if (5-j-nOperands > 1) { // more than one vacant register field fallback = operands[3]; // first of three possible source operands fallbackSeparate = true; } else if (5-j-nOperands == 1) { // one vacant register field used for fallback fallback = operands[j+1]; fallbackSeparate = true; } else if (5-j-nOperands == 0) { // no vacant register field. RD not used for source operand fallback = operands[j+1]; // first source operand } else if (fInstr->opAvail & 0x80) { // RD is first source operand fallback = 8; // fallback is RD } else { fallback = 0xFF; } fallback = getRegister(pInstr, fallback); // find register in specified register field if (fallback == 0x1F && fallbackSeparate) { return 0xFF; // fallback is zero if register 31 is specified and not also a source register } return fallback; } void CDisassembler::writeNormalInstruction() { // Write operand type if (!((variant & VARIANT_D0) /*|| iRecord->sourceoperands == 0*/)) { // skip if no operand type if ((variant & VARIANT_U0) && operandType < 5 && !debugMode) outFile.put('u'); // Unsigned else if (variant & VARIANT_U3 && operandType < 5) { // Unsigned if option bit 5 is set. // Option bit is in IM3 in E formats if (fInstr->tmplate == 0xE && (fInstr->imm2 & 2) && (pInstr->a.im3 & 0x8) && !debugMode) { outFile.put('u'); } } outFile.tabulate(asmTab0); writeOperandType(operandType); outFile.put(' '); } outFile.tabulate(asmTab1); // Write destination operand if (!(variant & (VARIANT_D0 | VARIANT_D1 | VARIANT_D3))) { // skip if no destination operands if (variant & VARIANT_M0) { writeMemoryOperand(); // Memory destination operand } else { if (variant & VARIANT_SPECD) writeSpecialRegister(pInstr->a.rd, variant >> VARIANT_SPECB); else if (fInstr->vect == 0 || (variant & VARIANT_R0)) writeGPRegister(pInstr->a.rd); else writeVectorRegister(pInstr->a.rd); } outFile.put(" = "); } // Write instruction name outFile.put(iRecord->name); /* Source operands are selected according to the following algorithm: 1. Read nOp = number of operands from instruction list. 2. Select nOp operands from the following list, in order of priority: immediate, memory, RT, RS, RU, RD If one in the list is not available, go to the next 3. The selected operands are used as source operands in the reversed order */ int nOperands = (int)iRecord->sourceoperands; // Number of source operands // Make list of operands from available operands. 0=none, 1=immediate, 2=memory, 5=RT, 6=RS, 7=RU, 8=RD uint8_t opAvail = fInstr->opAvail; // Bit index of available operands // opAvail bits: 1 = immediate, 2 = memory, // 0x10 = RT, 0x20 = RS, 0x40 = RU, 0x80 = RD if (fInstr->category != 3) { // Single format instruction. Immediate operand determined by instruction table if (iRecord->opimmediate) opAvail |= 1; else opAvail &= ~1; } if (variant & VARIANT_M0) opAvail &= ~2; // Memory operand already written as destination // (simular to emulator1.cpp:) // Make list of operands from available operands. // The operands[] array must have 6 elements to avoid overflow here, // even if some elements are later overwritten and used for other purposes uint8_t operands[6] = {0,0,0,0,0,0}; // Make list of operands int j = 5; if (opAvail & 0x01) operands[j--] = 1; // immediate operand if (opAvail & 0x02) operands[j--] = 2; // memory operand if (opAvail & 0x10) operands[j--] = 5; // register RT if (opAvail & 0x20) operands[j--] = 6; // register RS if (opAvail & 0x40) operands[j--] = 7; // register RU if (opAvail & 0x80) operands[j--] = 8; // register RD operands[0] = 8; // destination // Write source operands if (nOperands) { // Skip if no source operands outFile.put("("); // Loop through operands int iop = 0; // operand number for (j = 6 - nOperands; j < 6; j++, iop++) { uint8_t reg = getRegister(pInstr, operands[j]);// select register //uint8_t reg = operands[j];// select register switch (operands[j]) { case 1: // Immediate operand writeImmediateOperand(); break; case 2: // Memory operand writeMemoryOperand(); break; case 5: // RT if (variant & VARIANT_SPECS) writeSpecialRegister(reg, variant >> VARIANT_SPECB); else if (fInstr->vect == 0 || (variant & VARIANT_RL) || ((uint32_t)variant & VARIANT_R123 & (1 << (VARIANT_R1B + iop)))) writeGPRegister(reg); else writeVectorRegister(reg); break; case 6: // RS case 7: // RU if (variant & VARIANT_SPECS) writeSpecialRegister(reg, variant >> VARIANT_SPECB); else if (fInstr->vect == 0 || ((uint32_t)variant & VARIANT_R123 & (1 << (VARIANT_R1B + iop)))) writeGPRegister(reg); else writeVectorRegister(reg); break; case 8: // RD if (variant & VARIANT_SPECS) writeSpecialRegister(reg, variant >> VARIANT_SPECB); else if (fInstr->vect == 0 || ((uint32_t)variant & VARIANT_R123 & (1 << (VARIANT_R1B + iop))) || (variant & VARIANT_D3R0) == VARIANT_D3R0) { writeGPRegister(reg); } else writeVectorRegister(reg); break; } if (operands[j] && j < 5 && (iRecord->opimmediate != OPI_IMPLICIT || operands[j+1] != 1)) { outFile.put(", "); // Comma if not the last operand } } // end parameter list outFile.put(")"); // we prefer to end the parenthesis before the mask and options // write mask register if ((fInstr->tmplate == 0xA || fInstr->tmplate == 0xE) && (pInstr->a.mask != 7 || (variant & VARIANT_F1))) { if (pInstr->a.mask != 7) { outFile.put(", mask="); if (fInstr->vect) writeVectorRegister(pInstr->a.mask); else writeGPRegister(pInstr->a.mask); } // write fallback if (!(variant & VARIANT_F0)) { uint8_t fb = findFallback(fInstr, pInstr, nOperands); // find fallback register if (fb == 0xFF) { outFile.put(", fallback=0"); } else if (!(variant & VARIANT_F1) || getRegister(pInstr, operands[6-nOperands]) != fb) { outFile.put(", fallback="); if (fInstr->vect) writeVectorRegister(fb & 0x1F); else writeGPRegister(fb & 0x1F); } } } // write options = IM3, if IM3 is used and not already written by writeImmediateOperand if ((variant & VARIANT_On) && (fInstr->imm2 & 2) && (fInstr->category == 3 || (iRecord->opimmediate != 0 && iRecord->opimmediate != OPI_INT886)) ) { outFile.put(", options="); outFile.putHex(pInstr->a.im3); } } } void CDisassembler::writeJumpInstruction(){ // Write operand type if (!(variant & VARIANT_D0 || iRecord->sourceoperands == 1)) { // skip if no operands other than target outFile.tabulate(asmTab0); if ((variant & VARIANT_U0) && operandType < 5) outFile.put("u"); // unsigned writeOperandType(operandType); } outFile.tabulate(asmTab1); // Split instruction name into arithmetic operation and jump condition char iname[maxINameLen+1]; char * jname; strncpy(iname, iRecord->name, maxINameLen); iname[maxINameLen] = 0; jname = strchr(iname, '/'); if (jname) { *jname = 0; // end first part of name jname++; // point to second part of name } else jname = iname; if (iRecord->sourceoperands > 1) { // Instruction has arithmetic operands if (!(variant & (VARIANT_D0 | VARIANT_D1 | VARIANT_D3))) { // Write destination operand writeRegister(pInstr->a.rd, operandType); outFile.put(" = "); } // Write first part of instruction name outFile.put(iname); outFile.put("("); // Write arithmetic operands if (iRecord->sourceoperands > 2) { if ((fInstr->opAvail & 0x30) == 0x30) { // RS and RT writeRegister(pInstr->a.rs, operandType); outFile.put(", "); writeRegister(pInstr->a.rt, operandType); } else { uint32_t r1 = pInstr->a.rd; // First source operand if ((fInstr->opAvail & 0x21) == 0x21) r1 = pInstr->a.rs; // Two registers and an immediate operand writeRegister(r1, operandType); // Write operand outFile.put(", "); // Second source operand if (fInstr->opAvail & 2) { writeMemoryOperand(); if (fInstr->opAvail & 1) { outFile.put(", "); writeImmediateOperand(); } } else if (fInstr->opAvail & 1) { writeImmediateOperand(); } else { writeRegister(pInstr->a.rs, operandType); } } } else { writeRegister(pInstr->a.rs, operandType); // the only operand is rs } // End operand list if (fInstr->opAvail & 0x80) outFile.put("), "); } // Write second part of instruction name outFile.put(jname); // Write jump target outFile.put(' '); writeJumpTarget(iInstr + fInstr->jumpPos, fInstr->jumpSize); } void CDisassembler::writeCodeComment() { // Write hex listing of instruction as comment after single-format or multi-format instruction // uint32_t i; // Index to current byte // uint32_t fieldSize; // Number of bytes in field // const char * spacer; // Space between fields outFile.tabulate(asmTab3); // tabulate to comment field if (debugMode) return; outFile.put(commentSeparator); outFile.put(' '); // Start comment writeAddress(); // Write address if (cmd.dumpOptions & 2) { // option "-b": binary listing outFile.putHex(pInstr->i[0], 2); if (instrLength > 1) { outFile.put(" "); outFile.putHex(pInstr->i[1], 2); } if (instrLength > 2) { outFile.put(" "); outFile.putHex(pInstr->i[2], 2); } outFile.put(" | "); } if (fInstr->tmplate == 0xE && instrLength > 1) { // format E // Write format_template op1.op2 ot rd.rs.rt.ru mask IM2 IM3 outFile.putHex((format >> 8) & 0xF, 0); outFile.putHex(uint8_t(format), 2); outFile.put('_'); // format outFile.putHex(uint8_t(fInstr->tmplate), 0); outFile.put(' '); outFile.putHex(uint8_t(pInstr->a.op1), 2); outFile.put('.'); // op1.op2 if (!(fInstr->imm2 & 0x100)) { outFile.putHex(uint8_t(pInstr->a.op2), 0); outFile.put(' '); } outFile.putHex(operandType, 0); outFile.put(' '); outFile.putHex(uint8_t(pInstr->a.rd), 2); outFile.put('.'); // registers rd,rs,rt,ru outFile.putHex(uint8_t(pInstr->a.rs), 2); outFile.put('.'); outFile.putHex(uint8_t(pInstr->a.rt), 2); outFile.put('.'); outFile.putHex(uint8_t(pInstr->a.ru), 2); outFile.put(' '); if (pInstr->a.mask != 7) outFile.putHex(pInstr->a.mask, 0); // mask else outFile.put('_'); // no mask outFile.put(' '); outFile.putHex(pInstr->s[2], 2); outFile.put(' '); // IM2 outFile.putHex(uint8_t(pInstr->a.im3), 2); // IM3 if (instrLength == 3) { outFile.put(' '); outFile.putHex(pInstr->i[2], 2); // IM4 } } else if (fInstr->tmplate == 0xD) { // Write format_template op1 data outFile.putHex((format >> 8) & 0xF, 0); outFile.putHex(uint8_t(format), 2); outFile.put('_'); outFile.putHex(uint8_t(fInstr->tmplate), 0); outFile.put(' '); outFile.putHex(uint8_t(pInstr->a.op1), 2); outFile.put(' '); outFile.putHex(uint32_t(pInstr->d.im2 & 0xFFFFFF), 0); } else { // Write format_template op1 ot rd.rs.rt mask outFile.putHex((format >> 8) & 0xF, 0); outFile.putHex(uint8_t(format), 2); outFile.put('_'); outFile.putHex(uint8_t(fInstr->tmplate), 0); outFile.put(' '); outFile.putHex(uint8_t(pInstr->a.op1), 2); outFile.put(' '); if (fInstr->tmplate == 0xC) { // Format C has 16 bit immediate outFile.putHex(uint8_t(pInstr->a.rd), 2); outFile.put(' '); outFile.putHex(pInstr->s[0], 2); } else { // not format C outFile.putHex(operandType, 0); outFile.put(' '); outFile.putHex(uint8_t(pInstr->a.rd), 2); outFile.put('.'); outFile.putHex(uint8_t(pInstr->a.rs), 2); if (fInstr->tmplate == 0xB) { // Format B has 8 bit immediate outFile.put(' '); outFile.putHex(pInstr->b[0], 2); } else { // format A or E outFile.put('.'); outFile.putHex(uint8_t(pInstr->a.rt), 2); outFile.put(' '); if (pInstr->a.mask != 7) outFile.putHex(pInstr->a.mask, 0); else outFile.put('_'); // no mask } } if (instrLength > 1) { outFile.put(' '); if (instrLength == 2) { // format A2, B2, C2 outFile.putHex(pInstr->i[1], 2); } else if (instrLength == 3) { // format A3, B3 uint8_t const * bb = pInstr->b; uint64_t q = *(uint64_t*)(bb + 4); outFile.putHex(q, 2); } else { // unsupported formats longer than 1 for (uint32_t j = 1; j < instrLength; j++) { outFile.putHex(pInstr->i[j], 2); outFile.put(' '); } } } } // Write relocation comment if (relocation && !(relocations[relocation-1].r_type & 0x80000000)) { // 0x80000000 indicates no real relocation uint32_t reltype = relocations[relocation-1].r_type; outFile.put(". Rel: "); const char * rtyp = "", * rsize = ""; switch ((reltype >> 16) & 0xFF) { case R_FORW_ABS >> 16: rtyp = "abs "; break; case R_FORW_SELFREL >> 16: rtyp = "ip "; break; case R_FORW_DATAP >> 16: rtyp = "datap "; break; case R_FORW_THREADP >> 16: rtyp = "threadp "; break; case R_FORW_REFP >> 16: rtyp = "refpt "; break; default: rtyp = "other "; break; } switch ((reltype >> 8) & 0xFF) { case R_FORW_8 >> 8: rsize = "8 bit"; break; case R_FORW_16 >> 8: rsize = "16 bit"; break; case R_FORW_32 >> 8: rsize = "32 bit"; break; case R_FORW_64 >> 8: rsize = "64 bit"; break; case R_FORW_32LO >> 8: rsize = "32 low bits"; break; case R_FORW_32HI >> 8: rsize = "32 high bits"; break; case R_FORW_64LO >> 8: rsize = "64 low bits"; break; case R_FORW_64HI >> 8: rsize = "64 high bits"; break; } int scale = 1 << (reltype & 0xF); outFile.put(rtyp); outFile.put(rsize); if (scale > 1) { outFile.put(" * "); outFile.putDecimal(scale); } } // Write warnings and errors detected after we started writing instruction if (instructionWarning) { if (instructionWarning & 0x100) { outFile.put(". Unsupported format for this instruction"); instructionWarning = 0; // Suppress further warnings } if (instructionWarning & 0x200) { outFile.put(". Unsupported operand type for this instruction"); instructionWarning = 0; // Suppress further warnings } if (instructionWarning & 4) outFile.put(". Warning: float in double size field"); if (instructionWarning & 2) outFile.put(". Warning: unused immediate operand"); if (instructionWarning & 1) outFile.put(". Optional"); } } const char * baseRegisterNames[4] = {"thread", "datap", "ip", "sp"}; void CDisassembler::writeMemoryOperand() { // Check if there is a memory operand if (fInstr->mem == 0) { writeWarning("No memory operand"); return; } int itemsWritten = 0; // items inside [] bool symbolFound = false; // address corresponds to symbol // Check if there is a relocation here relocation = 0; // index to relocation record if (fInstr->addrSize) { ElfFwcReloc rel; rel.r_offset = iInstr + fInstr->addrPos; rel.r_section = section; uint32_t nrel = relocations.findAll(&relocation, rel); if (nrel) relocation++; // add 1 to avoid zero } // Enclose in square bracket outFile.put('['); uint32_t baseP = pInstr->a.rs; // Base pointer is RS if (fInstr->mem & 0x10) { // has relocated symbol if (relocation) { writeRelocationTarget(iInstr + fInstr->addrPos, fInstr->addrSize); itemsWritten++; } else if (isExecutable) { // executable file has no relocation record. Find nearest symbol ElfFwcSym needle; // symbol address to search for needle.st_section = 0; needle.st_value = 0; if (fInstr->addrSize > 1 && baseP >= 28 && baseP <= 30) { needle.st_section = 31 - baseP; // 1: IP, 2: datap, 3: threadp int64_t offset = 0; switch (fInstr->addrSize) { // Read offset of correct size case 2: offset = *(int16_t*)(sectionBuffer + iInstr + fInstr->addrPos); break; case 4: offset = *(int32_t*)(sectionBuffer + iInstr + fInstr->addrPos); break; } switch (baseP) { case 28: // threadp offset += fileHeader.e_threadp_base; break; case 29: // datap offset += fileHeader.e_datap_base; break; case 30: // ip, self-relative offset += sectionAddress + int64_t(iInstr) + instrLength * 4; break; } needle.st_value = offset; // Symbol position int32_t isym = symbols.findFirst(needle); if (isym >= 0) { // symbol found at target address writeSymbolName(isym); symbolFound = true; itemsWritten++; } else { // find nearest preceding symbol isym &= 0x7FFFFFFF; // remove not-found bit if (uint32_t(isym) < symbols.numEntries()) { // near symbol found if (isym > 0 && symbols[isym-1].st_section == needle.st_section) { isym--; // use nearest preceding symbol if in same section } if (symbols[isym].st_section == needle.st_section) { // write nearest symbol writeSymbolName(isym); outFile.put('+'); outFile.putHex((uint32_t)(offset - symbols[isym].st_value), 1); // write offset relative to symbol symbolFound = true; itemsWritten++; } } } } } } if (!symbolFound) { if (fInstr->addrSize > 1 && baseP >= 28 && !(fInstr->mem & 0x20)) { // Special pointers used if at least 16 bit offset if (baseP == 31 || !relocation) { // Do not write base pointer if implicit in relocated symbol if (itemsWritten) outFile.put('+'); outFile.put(baseRegisterNames[baseP - 28]); itemsWritten++; } } else { if (itemsWritten) outFile.put('+'); writeGPRegister(baseP); itemsWritten++; } } if ((fInstr->mem & 4) && pInstr->a.rt != 31) { // Has index in RT if (fInstr->scale & 4) { // Negative index outFile.put('-'); writeGPRegister(pInstr->a.rt); } else { // Positive, scaled index if (itemsWritten) outFile.put('+'); writeGPRegister(pInstr->a.rt); if ((fInstr->scale & 2) && operandType > 0) { // Index is scaled outFile.put('*'); outFile.putDecimal(dataSizeTable[operandType & 7]); } } itemsWritten++; } if (fInstr->mem & 0x10) { // Has offset if (relocation || symbolFound) { // has relocated symbol // has been written above //writeRelocationTarget(iInstr + fInstr->addrPos, fInstr->addrSize); } else { int32_t offset = 0; switch (fInstr->addrSize) { // Read offset of correct size case 1: offset = *(int8_t*)(sectionBuffer + iInstr + fInstr->addrPos); break; case 2: offset = *(int16_t*)(sectionBuffer + iInstr + fInstr->addrPos); break; case 4: offset = *(int32_t*)(sectionBuffer + iInstr + fInstr->addrPos); break; } if (offset > 0) { // Write positive offset outFile.put('+'); outFile.putHex((uint32_t)offset, 1); } else if (offset < 0) { // Write negative offset outFile.put('-'); outFile.putHex((uint32_t)(-offset), 1); } if ((fInstr->scale & 1) && offset != 0) { // Offset is scaled outFile.put('*'); outFile.putDecimal(dataSizeTable[operandType & 7]); } itemsWritten++; } } if (fInstr->mem & 0x20) { // Has limit outFile.put(", limit="); if (fInstr->addrSize == 4) { // 32 bit limit outFile.putHex(*(uint32_t*)(sectionBuffer + iInstr + fInstr->addrPos)); } else { // 16 bit limit outFile.putHex(*(uint16_t*)(sectionBuffer + iInstr + fInstr->addrPos)); } } if ((fInstr->vect & 2) && pInstr->a.rt != 31) { // Has vector length outFile.put(", length="); writeGPRegister(pInstr->a.rt); } else if ((fInstr->vect & 4) && pInstr->a.rt != 31) { // Has broadcast outFile.put(", broadcast="); writeGPRegister(pInstr->a.rt); } else if (fInstr->vect & 7 || ((fInstr->vect & 0x10) && (pInstr->a.ot & 4))) { // Scalar outFile.put(", scalar"); } outFile.put(']'); // End square bracket } void CDisassembler::writeImmediateOperand() { // Write immediate operand depending on type in instruction list // Check if there is a relocation here ElfFwcReloc rel; rel.r_offset = (uint64_t)iInstr + fInstr->immPos; rel.r_section = section; uint32_t irel; // index to relocation record uint32_t numRel = relocations.findAll(&irel, rel); if (numRel) { // Immediate value is relocated writeRelocationTarget(iInstr + fInstr->immPos, fInstr->immSize); return; } // Value is not relocated /*if ((variant & VARIANT_M1) && (fInstr->tmplate) == 0xE && (fInstr->opAvail & 2)) { // VARIANT_M1: immediate operand is in IM3 outFile.putDecimal(pInstr->a.im3); return; } */ const uint8_t * bb = pInstr->b; // use this for avoiding pedantic warnings from Gnu compiler when type casting if (operandType == 1 && (variant & VARIANT_H0)) operandType = 8; // half precision float if (operandType < 5 || iRecord->opimmediate || (variant & VARIANT_I2)) { // integer, or type specified in instruction list // Get value of right size int64_t x = 0; switch (fInstr->immSize) { case 1: // 8 bits x = *(int8_t*)(bb + fInstr->immPos); break; case 2: // 16 bits x = *(int16_t*)(bb + fInstr->immPos); break; case 3: // 24 bits, sign extend to 32 bits x = *(int32_t*)(bb + fInstr->immPos) << 8 >> 8; break; case 4: // 32 bits x = *(int32_t*)(bb + fInstr->immPos); break; case 8: x = *(int64_t*)(bb + fInstr->immPos); break; case 0: if (fInstr->tmplate == 0xE) { x = (pInstr->s[2]); } break; // else continue in default: default: writeError("Unknown immediate size"); } // Write in the form specified in instruction list switch (iRecord->opimmediate) { case 0: // No form specified case OPI_OT: // same as operand type if (fInstr->category == 1 && iRecord->opimmediate == 0 && x != 0) instructionWarning |= 2; // Immediate field not used in this instruction. Write nothing switch (fInstr->immSize) { // Output as hexadecimal case 1: if (operandType > 0) outFile.putDecimal((int32_t)x, 1); // sign extend to larger size else outFile.putHex(uint8_t(x), 1); break; case 2: if ((fInstr->imm2 & 4) && pInstr->a.im3 && !(variant & VARIANT_On)) { // constant is IM2 << IM3 if ((int16_t)x < 0) { outFile.put('-'); x = -x; } outFile.putHex(uint16_t(x), 1); outFile.put(" << "); outFile.putDecimal(pInstr->a.im3); } else if (operandType > 1) { outFile.putDecimal((int32_t)x, 1); // sign extend to larger size } else { outFile.putHex(uint16_t(x), 1); } break; default: case 4: if ((fInstr->imm2 & 8) && pInstr->a.im2) { // constant is IM4 << IM2 if ((int32_t)x < 0) { outFile.put('-'); x = -x; } outFile.putHex(uint32_t(x), 1); outFile.put(" << "); outFile.putDecimal(pInstr->a.im2); } else if (operandType <= 2) outFile.putHex(uint32_t(x), 1); else if (operandType == 5 || operandType == 6) { outFile.putFloat(*(float*)(bb + fInstr->immPos)); } else { outFile.putDecimal((int32_t)x, 1); // sign extend to larger size } break; case 8: if (operandType == 6) outFile.putFloat(*(double*)(bb + fInstr->immPos)); else outFile.putHex(uint64_t(x), 1); break; } break; case OPI_INT8: outFile.putDecimal(int8_t(x), 1); break; case OPI_INT16: outFile.putDecimal(int16_t(x), 1); break; case OPI_INT32: outFile.putDecimal(int32_t(x), 1); break; case OPI_INT8SH: if (int8_t(x >> 8) < 0) { outFile.put('-'); outFile.putHex(uint8_t(-int8_t(x >> 8)), 1); } else outFile.putHex(uint8_t(x >> 8), 1); outFile.put(" << "); outFile.putDecimal(uint8_t(x)); break; case OPI_INT16SH16: if (x < 0) { outFile.put('-'); x = -x; } outFile.putHex(uint16_t(x), 1); outFile.put(" << 16"); break; case OPI_INT32SH32: outFile.putHex(uint32_t(x), 1); outFile.put(" << 32"); break; case OPI_UINT8: outFile.putHex(uint8_t(x), 1); break; case OPI_UINT16: outFile.putHex(uint16_t(x), 1); break; case OPI_UINT32: outFile.putHex(uint32_t(x), 1); break; case OPI_INT64: case OPI_UINT64: outFile.putHex(uint64_t(x), 1); break; case OPI_2INT8: // Two unsigned integers outFile.putHex(uint8_t(x), 1); outFile.put(", "); outFile.putHex(uint8_t(x >> 8), 1); break; case OPI_INT886: // Three unsigned integers, including IM3 outFile.putDecimal(uint8_t(x)); outFile.put(", "); outFile.putDecimal(uint8_t(x >> 8)); outFile.put(", "); outFile.putDecimal(uint8_t(pInstr->a.im3)); break; case OPI_2INT16: // Two 16-bit unsigned integers outFile.putHex(uint16_t(x >> 16), 1); outFile.put(", "); outFile.putHex(uint16_t(x), 1); break; case OPI_INT1632: // One 16-bit and one 32-bit unsigned integer outFile.putHex(uint32_t(pInstr->i[1]), 1); outFile.put(", "); outFile.putHex(uint16_t(x), 1); break; case OPI_2INT32: // Two 32-bit unsigned integer outFile.putHex(uint32_t(x >> 32), 1); outFile.put(", "); outFile.putHex(uint32_t(x), 1); break; case OPI_INT1688: // 16 + 8 + 8 bits outFile.putHex(uint16_t(x), 1); outFile.put(", "); outFile.putHex(uint8_t(x >> 16), 1); outFile.put(", "); outFile.putHex(uint8_t(x >> 24), 1); break; case OPI_FLOAT16: // Half precision float outFile.putFloat(half2float(uint16_t(x))); break; case OPI_IMPLICIT: if (x != iRecord->implicit_imm) { // Does not match implicit value. Make value explicit if (iRecord->sourceoperands > 1) outFile.put(", "); outFile.putHex(uint8_t(x), 1); } break; default: writeWarning("Unknown immediate operand type"); } } else { // floating point uint32_t immSize = fInstr->immSize; // Size of immediate field if (immSize == 8 && operandType == 5) { immSize = 4; instructionWarning |= 4; // float in double size field } switch (immSize) { case 1: // 8 bits. float as integer outFile.putFloat((float)*(int8_t*)(bb + fInstr->immPos)); break; case 2: { // 16 bits uint16_t x = *(uint16_t*)(bb + fInstr->immPos); outFile.putFloat(half2float(x)); break;} case 4: { // float float x = *(float*)(bb + fInstr->immPos); outFile.putFloat(x); break;} case 8: { // double double x = *(double*)(bb + fInstr->immPos); outFile.putFloat(x); break;} default: writeError("unknown size for float operand"); } } } void CDisassembler::writeRegister(uint32_t r, uint32_t ot) { if (r == 31 && !(ot & 4)) outFile.put("sp"); else { outFile.put(ot & 4 ? "v" : "r"); outFile.putDecimal(r); } } void CDisassembler::writeGPRegister(uint32_t r) { // Write name of general purpose register if (r == 31) outFile.put("sp"); else { outFile.put("r"); outFile.putDecimal(r); } } void CDisassembler::writeVectorRegister(uint32_t v) { // Write name of vector register outFile.put("v"); outFile.putDecimal(v); } // Special register types according to Xn and Yn in 'variant' field in instruction list static const char * specialRegNamesPrefix[8] = {"?", "spec", "capab", "perf", "sys", "?", "?", "?"}; static const char * pointerRegNames[4] = {"threadp", "datap", "ip", "sp"}; static const char * specialRegNames[] = {"numcontr", "threadp", "datap", "?", "?", "?"}; void CDisassembler::writeSpecialRegister(uint32_t r, uint32_t type) { // Write name of other type of register if ((type & 0xF) == 0) { // May be special pointer if (r < 28) { writeGPRegister(r); } else { outFile.put(pointerRegNames[(r-28) & 3]); } } else if ((type & 0xF) == 1 && r <= 2) { // special registers with unique names outFile.put(specialRegNames[r]); } else { outFile.put(specialRegNamesPrefix[type & 7]); outFile.putDecimal(r); } } // Write name of operand type static const char * operandTypeNames[8] = { "int8", "int16", "int32", "int64", "int128", "float", "double", "float128 "}; void CDisassembler::writeOperandType(uint32_t ot) { if ((variant & VARIANT_H0) && ot == 1) outFile.put("float16"); else outFile.put(operandTypeNames[ot & 7]); } void CDisassembler::writeWarning(const char * w) { // Write warning to output file outFile.put(commentSeparator); outFile.put(" Warning: "); outFile.put(w); outFile.newLine(); } void CDisassembler::writeError(const char * w) { // Write warning to output file outFile.put(commentSeparator); outFile.put(" Error: "); outFile.put(w); outFile.newLine(); } void CDisassembler::finalErrorCheck() { // Check for illegal entries in symbol table and relocations table // Check for orphaned symbols uint32_t i; // Loop counter uint32_t linesWritten = 0; // Count lines written // Check for orphaned symbols for (i = 0; i < symbols.numEntries(); i++) { if ((symbols[i].st_other & 0x80000000) == 0 && (symbols[i].st_section || symbols[i].st_value) && symbols[i].st_type != STT_CONSTANT && symbols[i].st_type != STT_FILE) { // This symbol has not been written out if (linesWritten == 0) { // First orphaned symbol. Write text outFile.newLine(); outFile.newLine(); outFile.put(commentSeparator); outFile.put(" Warning: Symbols outside address range:"); outFile.newLine(); } outFile.put(commentSeparator); outFile.put(' '); writeSymbolName(i); outFile.put(" = "); outFile.putHex(symbols[i].st_section, 0); outFile.put(':'); outFile.putHex(symbols[i].st_value, 0); outFile.newLine(); linesWritten++; } } // Check for orphaned relocations linesWritten = 0; for (i = 0; i < relocations.numEntries(); i++) { if (relocations[i].r_type == 0) continue; // ignore empty relocation 0 if ((relocations[i].r_refsym & 0x80000000) == 0) { // This relocation has not been used if (linesWritten == 0) { // First orphaned symbol. Write text outFile.newLine(); outFile.newLine(); outFile.put(commentSeparator); outFile.put(" Warning: Unused or misplaced relocations:"); outFile.newLine(); } outFile.put(commentSeparator); outFile.put(" at "); outFile.putHex(uint32_t(relocations[i].r_section)); outFile.put(':'); // Section outFile.putHex(uint32_t(relocations[i].r_offset)); // Offset outFile.put(" to symbol "); writeSymbolName(relocations[i].r_sym & 0x7FFFFFFF); outFile.newLine(); linesWritten++; } } } void CDisassembler::writeAddress() { // write code address >> 2. Subtract ip_base to put code section at address 0 uint64_t address = (iInstr + sectionAddress - fileHeader.e_ip_base) >> 2; if (fileHeader.e_ip_base + sectionEnd + sectionAddress > 0xFFFF * 4) { // Write 32 bit address outFile.putHex(uint32_t(address), 2); } else { // Write 16 bit address outFile.putHex(uint16_t(address), 2); } if (debugMode) outFile.put(" "); else outFile.put(" _ "); // Space after address } void CDisassembler::setTabStops() { // set tab stops for output if (debugMode) { asmTab0 = 18; // Column for operand type asmTab1 = 26; // Column for opcode asmTab2 = 40; // Column for first operand asmTab3 = 64; // Column for destination value } else { asmTab0 = 0; // unused asmTab1 = 8; // Column for opcode asmTab2 = 16; // Column for first operand asmTab3 = 56; // Column for comment } }