/*! @file ObjdumpProcessor.cc * @brief ObjdumpProcessor Implementation * * This file contains the implementation of the functions supporting * the reading of an objdump output file and adding nops to a * coverage map. */ #include #include #include #include #include #include #include #include "app_common.h" #include "ObjdumpProcessor.h" #include "CoverageMap.h" #include "ExecutableInfo.h" #include "SymbolTable.h" #include "TargetFactory.h" namespace Coverage { void finalizeSymbol( ExecutableInfo* const executableInfo, std::string& symbolName, uint32_t lowAddress, uint32_t highAddress, ObjdumpProcessor::objdumpLines_t instructions ) { CoverageMapBase* aCoverageMap = NULL; uint32_t endAddress = highAddress; ObjdumpProcessor::objdumpLines_t::iterator itr, fnop, lnop; ObjdumpProcessor::objdumpLines_t::reverse_iterator ritr; SymbolInformation* symbolInfo = NULL; SymbolTable* theSymbolTable; // // Remove trailing nop instructions. // // First find the last instruction. for (ritr = instructions.rbegin(); ritr != instructions.rend(); ritr++) { if (ritr->isInstruction) break; } // If an instruction was found and it is a nop, ... if ((ritr != instructions.rend()) && (ritr->isNop)) { // save it as the last nop. Note that we must account for // the difference between a forward and a reverse iterator. lnop = ritr.base(); lnop--; endAddress -= lnop->nopSize; // Now look for the first nop in the sequence of trailing nops. fnop = lnop; ritr++; for (; ritr != instructions.rend(); ritr++) { if (ritr->isNop) { fnop = ritr.base(); fnop--; endAddress -= fnop->nopSize; } else break; } // Erase trailing nops. The erase operation wants the first // parameter to point to the first item to erase and the second // parameter to point to the item beyond the last item to erase. if ( fnop == lnop ) instructions.erase( fnop ); else instructions.erase( fnop, ++lnop ); } // If there are NOT already saved instructions, save them. symbolInfo = SymbolsToAnalyze->find( symbolName ); if (symbolInfo->instructions.empty()) { symbolInfo->sourceFile = executableInfo; symbolInfo->baseAddress = lowAddress; symbolInfo->instructions = instructions; } // Add the symbol to this executable's symbol table. theSymbolTable = executableInfo->getSymbolTable(); theSymbolTable->addSymbol( symbolName, lowAddress, endAddress - lowAddress + 1 ); // Create a coverage map for the symbol. aCoverageMap = executableInfo->createCoverageMap( symbolName, lowAddress, endAddress ); if (aCoverageMap) { // Mark the start of each instruction in the coverage map. for (itr = instructions.begin(); itr != instructions.end(); itr++ ) { aCoverageMap->setIsStartOfInstruction( itr->address ); } // Create a unified coverage map for the symbol. SymbolsToAnalyze->createCoverageMap( symbolName, endAddress - lowAddress + 1 ); } } ObjdumpProcessor::ObjdumpProcessor() { } ObjdumpProcessor::~ObjdumpProcessor() { } uint32_t ObjdumpProcessor::determineLoadAddress( ExecutableInfo* theExecutable ) { #define METHOD "ERROR: ObjdumpProcessor::determineLoadAddress - " FILE* loadAddressFile = NULL; char* cStatus; uint32_t offset; // This method should only be call for a dynamic library. if (!theExecutable->hasDynamicLibrary()) return 0; std::string dlinfoName = theExecutable->getFileName(); uint32_t address; char inLibName[128]; std::string Library = theExecutable->getLibraryName(); dlinfoName += ".dlinfo"; // Read load address. loadAddressFile = fopen( dlinfoName.c_str(), "r" ); if (!loadAddressFile) { fprintf( stderr, METHOD "unable to open %s\n", dlinfoName.c_str() ); exit( -1 ); } // Process the dlinfo file. while ( 1 ) { // Get a line. cStatus = fgets( inputBuffer, MAX_LINE_LENGTH, loadAddressFile ); if (cStatus == NULL) { fprintf( stderr, METHOD "library %s not found in %s\n", Library.c_str(), dlinfoName.c_str() ); fclose( loadAddressFile ); exit( -1 ); } sscanf( inputBuffer, "%s %x", inLibName, &offset ); std::string tmp = inLibName; if ( tmp.find( Library ) != tmp.npos ) { // fprintf( stderr, "%s - 0x%08x\n", inLibName, offset ); address = offset; break; } } fclose( loadAddressFile ); return address; #undef METHOD } bool ObjdumpProcessor::IsBranch( const char *instruction ) { if ( !TargetInfo ) { fprintf( stderr, "ERROR: ObjdumpProcessor::IsBranch - unknown architecture\n" ); assert(0); return false; } return TargetInfo->isBranch( instruction ); } bool ObjdumpProcessor::isBranchLine( const char* const line ) { if ( !TargetInfo ) { fprintf( stderr, "ERROR: ObjdumpProcessor::isBranchLine - unknown architecture\n" ); assert(0); return false; } return TargetInfo->isBranchLine( line ); } bool ObjdumpProcessor::isNop( const char* const line, int& size ) { if ( !TargetInfo ){ fprintf( stderr, "ERROR: ObjdumpProcessor::isNop - unknown architecture\n" ); assert(0); return false; } return TargetInfo->isNopLine( line, size ); } FILE* ObjdumpProcessor::getFile( std::string fileName ) { char dumpFile[128]; FILE* objdumpFile; char buffer[ 512 ]; int status; sprintf( dumpFile, "%s.dmp", fileName.c_str() ); // Generate the objdump. if (FileIsNewer( fileName.c_str(), dumpFile )) { sprintf( buffer, "%s -Cda --section=.text --source %s | sed -e \'s/ *$//\' >%s", TargetInfo->getObjdump(), fileName.c_str(), dumpFile ); status = system( buffer ); if (status) { fprintf( stderr, "ERROR: ObjdumpProcessor::getFile - command (%s) failed with %d\n", buffer, status ); exit( -1 ); } } // Open the objdump file. objdumpFile = fopen( dumpFile, "r" ); if (!objdumpFile) { fprintf( stderr, "ERROR: ObjdumpProcessor::getFile - unable to open %s\n", dumpFile ); exit(-1); } return objdumpFile; } uint32_t ObjdumpProcessor::getAddressAfter( uint32_t address ) { objdumpFile_t::iterator itr; itr = find ( objdumpList.begin(), objdumpList.end(), address ); if (itr == objdumpList.end()) { return 0; } itr++; if (itr == objdumpList.end()) { return 0; } return (*itr); } void ObjdumpProcessor::loadAddressTable ( ExecutableInfo* const executableInformation ) { char* cStatus; int items; FILE* objdumpFile; uint32_t offset; char terminator; // Obtain the objdump file. if (!executableInformation->hasDynamicLibrary()) objdumpFile = getFile( executableInformation->getFileName() ); else objdumpFile = getFile( executableInformation->getLibraryName() ); // Process all lines from the objdump file. while ( 1 ) { // Get the line. cStatus = fgets( inputBuffer, MAX_LINE_LENGTH, objdumpFile ); if (cStatus == NULL) { break; } inputBuffer[ strlen(inputBuffer) - 1] = '\0'; // See if it is the dump of an instruction. items = sscanf( inputBuffer, "%x%c", &offset, &terminator ); // If it looks like an instruction ... if ((items == 2) && (terminator == ':')){ objdumpList.push_back( executableInformation->getLoadAddress() + offset ); } } } void ObjdumpProcessor::load( ExecutableInfo* const executableInformation ) { char* cStatus; std::string currentSymbol = ""; uint32_t endAddress; uint32_t instructionOffset; int items; objdumpLine_t lineInfo; FILE* objdumpFile; uint32_t offset; bool processSymbol = false; uint32_t startAddress = 0; char symbol[ MAX_LINE_LENGTH ]; char terminator1; char terminator2; objdumpLines_t theInstructions; // Obtain the objdump file. if (!executableInformation->hasDynamicLibrary()) objdumpFile = getFile( executableInformation->getFileName() ); else objdumpFile = getFile( executableInformation->getLibraryName() ); // Process all lines from the objdump file. while ( 1 ) { // Get the line. cStatus = fgets( inputBuffer, MAX_LINE_LENGTH, objdumpFile ); if (cStatus == NULL) { // If we are currently processing a symbol, finalize it. if (processSymbol) { finalizeSymbol( executableInformation, currentSymbol, startAddress, executableInformation->getLoadAddress() + offset, theInstructions ); fprintf( stderr, "WARNING: ObjdumpProcessor::load - analysis of symbol %s \n" " may be incorrect. It was the last symbol in %s\n" " and the length of its last instruction is assumed " " to be one.\n", currentSymbol.c_str(), executableInformation->getFileName().c_str() ); } break; } inputBuffer[ strlen(inputBuffer) - 1] = '\0'; lineInfo.line = inputBuffer; lineInfo.address = 0xffffffff; lineInfo.isInstruction = false; lineInfo.isNop = false; lineInfo.nopSize = 0; lineInfo.isBranch = false; // Look for the start of a symbol's objdump and extract // offset and symbol (i.e. offset :). items = sscanf( inputBuffer, "%x <%[^>]>%c", &offset, symbol, &terminator1 ); // If all items found, we are at the beginning of a symbol's objdump. if ((items == 3) && (terminator1 == ':')) { endAddress = executableInformation->getLoadAddress() + offset - 1; // If we are currently processing a symbol, finalize it. if (processSymbol) { finalizeSymbol( executableInformation, currentSymbol, startAddress, endAddress, theInstructions ); } // Start processing of a new symbol. startAddress = 0; currentSymbol = ""; processSymbol = false; theInstructions.clear(); // See if the new symbol is one that we care about. if (SymbolsToAnalyze->isDesired( symbol )) { startAddress = executableInformation->getLoadAddress() + offset; currentSymbol = symbol; processSymbol = true; theInstructions.push_back( lineInfo ); } } else if (processSymbol) { // See if it is the dump of an instruction. items = sscanf( inputBuffer, "%x%c\t%*[^\t]%c", &instructionOffset, &terminator1, &terminator2 ); // If it looks like an instruction ... if ((items == 3) && (terminator1 == ':') && (terminator2 == '\t')) { // update the line's information, save it and ... lineInfo.address = executableInformation->getLoadAddress() + instructionOffset; lineInfo.isInstruction = true; lineInfo.isNop = isNop( inputBuffer, lineInfo.nopSize ); lineInfo.isBranch = isBranchLine( inputBuffer ); } // Always save the line. theInstructions.push_back( lineInfo ); } } } }