source: rtems-tools/tester/covoar/ObjdumpProcessor.cc @ 881824f

Last change on this file since 881824f was 881824f, checked in by Chris Johns <chrisj@…>, on May 11, 2018 at 2:24:11 AM

tester/covoar: Remove all exit() calls and throw an rld::error exception.

Add a suitable catch to covoar's main.

  • Property mode set to 100644
File size: 13.5 KB
Line 
1/*! @file ObjdumpProcessor.cc
2 *  @brief ObjdumpProcessor Implementation
3 *
4 *  This file contains the implementation of the functions supporting
5 *  the reading of an objdump output file and adding nops to a
6 *  coverage map.
7 */
8
9#include <assert.h>
10#include <ctype.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <algorithm>
15#include <string>
16
17#include "app_common.h"
18#include "ObjdumpProcessor.h"
19#include "CoverageMap.h"
20#include "ExecutableInfo.h"
21#include "SymbolTable.h"
22#include "TargetFactory.h"
23
24#include "rld.h"
25#include "rld-process.h"
26
27namespace Coverage {
28
29  void finalizeSymbol(
30    ExecutableInfo* const            executableInfo,
31    std::string&                     symbolName,
32    uint32_t                         lowAddress,
33    uint32_t                         highAddress,
34    ObjdumpProcessor::objdumpLines_t instructions
35  ) {
36
37    CoverageMapBase*                                   aCoverageMap = NULL;
38    uint32_t                                           endAddress = highAddress;
39    ObjdumpProcessor::objdumpLines_t::iterator         itr, fnop, lnop;
40    ObjdumpProcessor::objdumpLines_t::reverse_iterator ritr;
41    SymbolInformation*                                 symbolInfo = NULL;
42    SymbolTable*                                       theSymbolTable;
43
44    //
45    // Remove trailing nop instructions.
46    //
47
48    // First find the last instruction.
49    for (ritr = instructions.rbegin();
50         ritr != instructions.rend();
51         ritr++) {
52      if (ritr->isInstruction)
53        break;
54    }
55
56    // If an instruction was found and it is a nop, ...
57    if ((ritr != instructions.rend()) && (ritr->isNop)) {
58
59      // save it as the last nop.  Note that we must account for
60      // the difference between a forward and a reverse iterator.
61      lnop = ritr.base();
62      lnop--;
63      endAddress -= lnop->nopSize;
64
65      // Now look for the first nop in the sequence of trailing nops.
66      fnop = lnop;
67      ritr++;
68      for (; ritr != instructions.rend(); ritr++) {
69        if (ritr->isNop) {
70          fnop = ritr.base();
71          fnop--;
72          endAddress -= fnop->nopSize;
73        }
74        else
75          break;
76      }
77
78      // Erase trailing nops.  The erase operation wants the first
79      // parameter to point to the first item to erase and the second
80      // parameter to point to the item beyond the last item to erase.
81      if ( fnop == lnop )
82        instructions.erase( fnop );
83      else
84        instructions.erase( fnop, ++lnop );
85    }
86
87    // If there are NOT already saved instructions, save them.
88    symbolInfo = SymbolsToAnalyze->find( symbolName );
89    if (symbolInfo->instructions.empty()) {
90      symbolInfo->sourceFile = executableInfo;
91      symbolInfo->baseAddress = lowAddress;
92      symbolInfo->instructions = instructions;
93    }
94
95    // Add the symbol to this executable's symbol table.
96    theSymbolTable = executableInfo->getSymbolTable();
97    theSymbolTable->addSymbol(
98      symbolName, lowAddress, endAddress - lowAddress + 1
99    );
100
101    // Create a coverage map for the symbol.
102    aCoverageMap = executableInfo->createCoverageMap(
103      executableInfo->getFileName().c_str(), symbolName, lowAddress, endAddress
104    );
105
106    if (aCoverageMap) {
107
108      // Mark the start of each instruction in the coverage map.
109      for (itr = instructions.begin();
110           itr != instructions.end();
111           itr++ ) {
112
113        aCoverageMap->setIsStartOfInstruction( itr->address );
114      }
115
116      // Create a unified coverage map for the symbol.
117      SymbolsToAnalyze->createCoverageMap(
118        executableInfo->getFileName().c_str(), symbolName,
119        endAddress - lowAddress + 1
120      );
121    }
122  }
123
124  ObjdumpProcessor::ObjdumpProcessor()
125  {
126  }
127
128  ObjdumpProcessor::~ObjdumpProcessor()
129  {
130  }
131
132  uint32_t ObjdumpProcessor::determineLoadAddress(
133    ExecutableInfo* theExecutable
134  )
135  {
136    #define METHOD "ERROR: ObjdumpProcessor::determineLoadAddress - "
137    FILE*        loadAddressFile = NULL;
138    char*        cStatus;
139    uint32_t     offset;
140
141    // This method should only be call for a dynamic library.
142    if (!theExecutable->hasDynamicLibrary())
143      return 0;
144
145    std::string dlinfoName = theExecutable->getFileName();
146    uint32_t address;
147    char inLibName[128];
148    std::string Library = theExecutable->getLibraryName();
149
150    dlinfoName += ".dlinfo";
151    // Read load address.
152    loadAddressFile = ::fopen( dlinfoName.c_str(), "r" );
153    if (!loadAddressFile) {
154      std::ostringstream what;
155      what << "Unable to open " << dlinfoName;
156      throw rld::error( what, METHOD );
157    }
158
159    // Process the dlinfo file.
160    while ( 1 ) {
161
162      // Get a line.
163      cStatus = ::fgets( inputBuffer, MAX_LINE_LENGTH, loadAddressFile );
164      if (cStatus == NULL) {
165        ::fclose( loadAddressFile );
166        std::ostringstream what;
167        what << "library " << Library << " not found in " << dlinfoName;
168        throw rld::error( what, METHOD );
169      }
170      sscanf( inputBuffer, "%s %x", inLibName, &offset );
171      std::string tmp = inLibName;
172      if ( tmp.find( Library ) != tmp.npos ) {
173        // fprintf( stderr, "%s - 0x%08x\n", inLibName, offset );
174        address = offset;
175        break;
176      }
177    }
178
179    ::fclose( loadAddressFile );
180    return address;
181
182    #undef METHOD
183  }
184
185  bool ObjdumpProcessor::IsBranch(
186    const char *instruction
187  )
188  {
189    if ( !TargetInfo ) {
190      fprintf(
191        stderr,
192        "ERROR: ObjdumpProcessor::IsBranch - unknown architecture\n"
193      );
194      assert(0);
195      return false;
196    }
197
198    return TargetInfo->isBranch( instruction );
199  }
200
201  bool ObjdumpProcessor::isBranchLine(
202    const char* const line
203  )
204  {
205    if ( !TargetInfo ) {
206      fprintf(
207        stderr,
208        "ERROR: ObjdumpProcessor::isBranchLine - unknown architecture\n"
209      );
210      assert(0);
211      return false;
212    }
213
214    return  TargetInfo->isBranchLine( line );
215  }
216
217  bool ObjdumpProcessor::isNop(
218    const char* const line,
219    int&              size
220  )
221  {
222    if ( !TargetInfo ){
223      fprintf(
224        stderr,
225        "ERROR: ObjdumpProcessor::isNop - unknown architecture\n"
226      );
227      assert(0);
228      return false;
229    }
230
231    return TargetInfo->isNopLine( line, size );
232  }
233
234  void ObjdumpProcessor::getFile(
235    std::string fileName,
236    rld::process::tempfile& objdumpFile,
237    rld::process::tempfile& err
238    )
239  {
240    rld::process::status        status;
241    rld::process::arg_container args = { TargetInfo->getObjdump(),
242                                         "-Cda", "--section=.text", "--source",
243                                         fileName };
244    try
245    {
246      status = rld::process::execute( TargetInfo->getObjdump(),
247                                      args, objdumpFile.name(), err.name() );
248      if ( (status.type != rld::process::status::normal)
249           || (status.code != 0) ) {
250        throw rld::error( "Objdump error", "generating objdump" );
251      }
252    } catch( rld::error& err )
253      {
254        std::cout << "Error while running " << TargetInfo->getObjdump()
255                  << " on " << fileName << std::endl;
256        std::cout << err.what << " in " << err.where << std::endl;
257        return;
258      }
259
260    objdumpFile.open( true );
261  }
262
263  uint32_t ObjdumpProcessor::getAddressAfter( uint32_t address )
264  {
265    objdumpFile_t::iterator itr;
266
267    itr = find ( objdumpList.begin(), objdumpList.end(), address );
268    if (itr == objdumpList.end()) {
269      return 0;
270    }
271
272    itr++;
273    if (itr == objdumpList.end()) {
274      return 0;
275    }
276
277    return (*itr);
278
279  }
280
281  void ObjdumpProcessor::loadAddressTable (
282    ExecutableInfo* const    executableInformation,
283    rld::process::tempfile&  objdumpFile,
284    rld::process::tempfile&  err
285  )
286  {
287    int          items;
288    uint32_t     offset;
289    char         terminator;
290    std::string  line;
291
292    // Obtain the objdump file.
293    if ( !executableInformation->hasDynamicLibrary() )
294      getFile( executableInformation->getFileName(), objdumpFile, err );
295    else
296      getFile( executableInformation->getLibraryName(), objdumpFile, err );
297
298    // Process all lines from the objdump file.
299    while ( true ) {
300
301      // Get the line.
302      objdumpFile.read_line( line );
303      if ( line.empty() ) {
304        break;
305      }
306
307      // See if it is the dump of an instruction.
308      items = sscanf(
309        line.c_str(),
310        "%x%c",
311        &offset, &terminator
312      );
313
314      // If it looks like an instruction ...
315      if ((items == 2) && (terminator == ':')) {
316        objdumpList.push_back(
317          executableInformation->getLoadAddress() + offset
318        );
319      }
320    }
321  }
322
323  void ObjdumpProcessor::load(
324    ExecutableInfo* const    executableInformation,
325    rld::process::tempfile&  objdumpFile,
326    rld::process::tempfile&  err
327  )
328  {
329    std::string     currentSymbol = "";
330    uint32_t        endAddress;
331    uint32_t        instructionOffset;
332    int             items;
333    int             found;
334    objdumpLine_t   lineInfo;
335    uint32_t        offset;
336    bool            processSymbol = false;
337    uint32_t        startAddress = 0;
338    char            symbol[ MAX_LINE_LENGTH ];
339    char            terminator1;
340    char            terminatorOne;
341    char            terminator2;
342    objdumpLines_t  theInstructions;
343    char            instruction[ MAX_LINE_LENGTH ];
344    char            ID[ MAX_LINE_LENGTH ];
345    std::string     call = "";
346    std::string     jumpTableID = "";
347    std::string     line = "";
348
349    // Obtain the objdump file.
350    if ( !executableInformation->hasDynamicLibrary() )
351      getFile( executableInformation->getFileName(), objdumpFile, err );
352    else
353      getFile( executableInformation->getLibraryName(), objdumpFile, err );
354
355    while ( true ) {
356
357      // Get the line.
358      objdumpFile.read_line( line );
359      if ( line.empty() ) {
360
361        // If we are currently processing a symbol, finalize it.
362        if (processSymbol) {
363          finalizeSymbol(
364            executableInformation,
365            currentSymbol,
366            startAddress,
367            executableInformation->getLoadAddress() + offset,
368            theInstructions
369          );
370          fprintf(
371            stderr,
372            "WARNING: ObjdumpProcessor::load - analysis of symbol %s \n"
373            "         may be incorrect.  It was the last symbol in %s\n"
374            "         and the length of its last instruction is assumed "
375            "         to be one.\n",
376            currentSymbol.c_str(),
377            executableInformation->getFileName().c_str()
378          );
379        }
380        objdumpFile.close();
381        break;
382      }
383
384      lineInfo.line          = line;
385      lineInfo.address       = 0xffffffff;
386      lineInfo.isInstruction = false;
387      lineInfo.isNop         = false;
388      lineInfo.nopSize       = 0;
389      lineInfo.isBranch      = false;
390
391      // Look for the start of a symbol's objdump and extract
392      // offset and symbol (i.e. offset <symbolname>:).
393      items = sscanf(
394        line.c_str(),
395        "%x <%[^>]>%c",
396        &offset, symbol, &terminator1
397      );
398
399      // See if it is a jump table.
400      found = sscanf(
401        line.c_str(),
402        "%x%c\t%*[^\t]%c%s %*x %*[^+]%s",
403        &instructionOffset, &terminatorOne, &terminator2, instruction, ID
404      );
405      call = instruction;
406      jumpTableID = ID;
407
408      // If all items found, we are at the beginning of a symbol's objdump.
409      if ((items == 3) && (terminator1 == ':')) {
410
411        endAddress = executableInformation->getLoadAddress() + offset - 1;
412
413        // If we are currently processing a symbol, finalize it.
414        if (processSymbol) {
415          finalizeSymbol(
416            executableInformation,
417            currentSymbol,
418            startAddress,
419            endAddress,
420            theInstructions
421          );
422        }
423
424        // Start processing of a new symbol.
425        startAddress = 0;
426        currentSymbol = "";
427        processSymbol = false;
428        theInstructions.clear();
429
430        // See if the new symbol is one that we care about.
431        if (SymbolsToAnalyze->isDesired( symbol )) {
432          startAddress = executableInformation->getLoadAddress() + offset;
433          currentSymbol = symbol;
434          processSymbol = true;
435          theInstructions.push_back( lineInfo );
436        }
437      }
438      // If it looks like a jump table, finalize the symbol.
439      else if ( (found == 5) && (terminatorOne == ':') && (terminator2 == '\t')
440               && (call.find( "call" ) != std::string::npos)
441               && (jumpTableID.find( "+0x" ) != std::string::npos)
442               && processSymbol )
443      {
444
445          endAddress = executableInformation->getLoadAddress() + offset - 1;
446
447          // If we are currently processing a symbol, finalize it.
448          if ( processSymbol ) {
449            finalizeSymbol(
450              executableInformation,
451              currentSymbol,
452              startAddress,
453              endAddress,
454              theInstructions
455            );
456          }
457          processSymbol = false;
458      }
459      else if (processSymbol) {
460
461        // See if it is the dump of an instruction.
462        items = sscanf(
463          line.c_str(),
464          "%x%c\t%*[^\t]%c",
465          &instructionOffset, &terminator1, &terminator2
466        );
467
468        // If it looks like an instruction ...
469        if ((items == 3) && (terminator1 == ':') && (terminator2 == '\t')) {
470
471          // update the line's information, save it and ...
472          lineInfo.address =
473           executableInformation->getLoadAddress() + instructionOffset;
474          lineInfo.isInstruction = true;
475          lineInfo.isNop         = isNop( line.c_str(), lineInfo.nopSize );
476          lineInfo.isBranch      = isBranchLine( line.c_str() );
477        }
478
479        // Always save the line.
480        theInstructions.push_back( lineInfo );
481      }
482    }
483  }
484}
Note: See TracBrowser for help on using the repository browser.