source: rtems-tools/tester/covoar/ObjdumpProcessor.cc @ 3e187ba

5
Last change on this file since 3e187ba was 3e187ba, checked in by Cillian O'Donnell <cpodonnell8@…>, on 04/25/18 at 20:33:58

covoar: Add symbol set reader and ELF data parser to covoar.

Add ability to organize symbol sets of libraries in INI file
and then read them with covoar and load the symbols directly from the
libraries.

rtems-tools/../testing: Add configuration files for coverage analysis.

A number of covoar options are not required and are defaulted.

Co-author: Krzysztof Miesowicz <krzysztof.miesowicz@…>
Co-author: Vijay Kumar Banerjee <vijaykumar9597@…>
Co-author: Chris Johns <chrisj@…>

  • 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      fprintf( stderr, METHOD "unable to open %s\n", dlinfoName.c_str() );
155      exit( -1 );
156    }
157
158    // Process the dlinfo file.
159    while ( 1 ) {
160
161      // Get a line.
162      cStatus = fgets( inputBuffer, MAX_LINE_LENGTH, loadAddressFile );
163      if (cStatus == NULL) {
164        fprintf(
165          stderr,
166          METHOD "library %s not found in %s\n",
167          Library.c_str(),
168          dlinfoName.c_str()
169        );
170        fclose( loadAddressFile );
171        exit( -1 );
172      }
173      sscanf( inputBuffer, "%s %x", inLibName, &offset );
174      std::string tmp = inLibName;
175      if ( tmp.find( Library ) != tmp.npos ) {
176        // fprintf( stderr, "%s - 0x%08x\n", inLibName, offset );
177        address = offset;
178        break;
179      }
180    }
181
182    fclose( loadAddressFile );
183    return address;
184
185    #undef METHOD
186  }
187
188  bool ObjdumpProcessor::IsBranch(
189    const char *instruction
190  )
191  {
192    if ( !TargetInfo ) {
193      fprintf(
194        stderr,
195        "ERROR: ObjdumpProcessor::IsBranch - unknown architecture\n"
196      );
197      assert(0);
198      return false;
199    }
200
201    return TargetInfo->isBranch( instruction );
202  }
203
204  bool ObjdumpProcessor::isBranchLine(
205    const char* const line
206  )
207  {
208    if ( !TargetInfo ) {
209      fprintf(
210        stderr,
211        "ERROR: ObjdumpProcessor::isBranchLine - unknown architecture\n"
212      );
213      assert(0);
214      return false;
215    }
216
217    return  TargetInfo->isBranchLine( line );
218  }
219
220  bool ObjdumpProcessor::isNop(
221    const char* const line,
222    int&              size
223  )
224  {
225    if ( !TargetInfo ){
226      fprintf(
227        stderr,
228        "ERROR: ObjdumpProcessor::isNop - unknown architecture\n"
229      );
230      assert(0);
231      return false;
232    }
233
234    return TargetInfo->isNopLine( line, size );
235  }
236
237  void ObjdumpProcessor::getFile(
238    std::string fileName,
239    rld::process::tempfile& objdumpFile,
240    rld::process::tempfile& err
241    )
242  {
243    rld::process::status        status;
244    rld::process::arg_container args = { TargetInfo->getObjdump(),
245                                         "-Cda", "--section=.text", "--source",
246                                         fileName };
247    try
248    {
249      status = rld::process::execute( TargetInfo->getObjdump(),
250                                      args, objdumpFile.name(), err.name() );
251      if ( (status.type != rld::process::status::normal)
252           || (status.code != 0) ) {
253        throw rld::error( "Objdump error", "generating objdump" );
254      }
255    } catch( rld::error& err )
256      {
257        std::cout << "Error while running " << TargetInfo->getObjdump()
258                  << " on " << fileName << std::endl;
259        std::cout << err.what << " in " << err.where << std::endl;
260        return;
261      }
262
263    objdumpFile.open( true );
264  }
265
266  uint32_t ObjdumpProcessor::getAddressAfter( uint32_t address )
267  {
268    objdumpFile_t::iterator itr;
269
270    itr = find ( objdumpList.begin(), objdumpList.end(), address );
271    if (itr == objdumpList.end()) {
272      return 0;
273    }
274
275    itr++;
276    if (itr == objdumpList.end()) {
277      return 0;
278    }
279
280    return (*itr);
281
282  }
283
284  void ObjdumpProcessor::loadAddressTable (
285    ExecutableInfo* const    executableInformation,
286    rld::process::tempfile&  objdumpFile,
287    rld::process::tempfile&  err
288  )
289  {
290    int          items;
291    uint32_t     offset;
292    char         terminator;
293    std::string  line;
294
295    // Obtain the objdump file.
296    if ( !executableInformation->hasDynamicLibrary() )
297      getFile( executableInformation->getFileName(), objdumpFile, err );
298    else
299      getFile( executableInformation->getLibraryName(), objdumpFile, err );
300
301    // Process all lines from the objdump file.
302    while ( true ) {
303
304      // Get the line.
305      objdumpFile.read_line( line );
306      if ( line.empty() ) {
307        break;
308      }
309
310      // See if it is the dump of an instruction.
311      items = sscanf(
312        line.c_str(),
313        "%x%c",
314        &offset, &terminator
315      );
316
317      // If it looks like an instruction ...
318      if ((items == 2) && (terminator == ':')) {
319        objdumpList.push_back(
320          executableInformation->getLoadAddress() + offset
321        );
322      }
323    }
324  }
325
326  void ObjdumpProcessor::load(
327    ExecutableInfo* const    executableInformation,
328    rld::process::tempfile&  objdumpFile,
329    rld::process::tempfile&  err
330  )
331  {
332    std::string     currentSymbol = "";
333    uint32_t        endAddress;
334    uint32_t        instructionOffset;
335    int             items;
336    int             found;
337    objdumpLine_t   lineInfo;
338    uint32_t        offset;
339    bool            processSymbol = false;
340    uint32_t        startAddress = 0;
341    char            symbol[ MAX_LINE_LENGTH ];
342    char            terminator1;
343    char            terminatorOne;
344    char            terminator2;
345    objdumpLines_t  theInstructions;
346    char            instruction[ MAX_LINE_LENGTH ];
347    char            ID[ MAX_LINE_LENGTH ];
348    std::string     call = "";
349    std::string     jumpTableID = "";
350    std::string     line = "";
351
352    // Obtain the objdump file.
353    if ( !executableInformation->hasDynamicLibrary() )
354      getFile( executableInformation->getFileName(), objdumpFile, err );
355    else
356      getFile( executableInformation->getLibraryName(), objdumpFile, err );
357
358    while ( true ) {
359
360      // Get the line.
361      objdumpFile.read_line( line );
362      if ( line.empty() ) {
363
364        // If we are currently processing a symbol, finalize it.
365        if (processSymbol) {
366          finalizeSymbol(
367            executableInformation,
368            currentSymbol,
369            startAddress,
370            executableInformation->getLoadAddress() + offset,
371            theInstructions
372          );
373          fprintf(
374            stderr,
375            "WARNING: ObjdumpProcessor::load - analysis of symbol %s \n"
376            "         may be incorrect.  It was the last symbol in %s\n"
377            "         and the length of its last instruction is assumed "
378            "         to be one.\n",
379            currentSymbol.c_str(),
380            executableInformation->getFileName().c_str()
381          );
382        }
383        objdumpFile.close();
384        break;
385      }
386
387      lineInfo.line          = line;
388      lineInfo.address       = 0xffffffff;
389      lineInfo.isInstruction = false;
390      lineInfo.isNop         = false;
391      lineInfo.nopSize       = 0;
392      lineInfo.isBranch      = false;
393
394      // Look for the start of a symbol's objdump and extract
395      // offset and symbol (i.e. offset <symbolname>:).
396      items = sscanf(
397        line.c_str(),
398        "%x <%[^>]>%c",
399        &offset, symbol, &terminator1
400      );
401
402      // See if it is a jump table.
403      found = sscanf(
404        line.c_str(),
405        "%x%c\t%*[^\t]%c%s %*x %*[^+]%s",
406        &instructionOffset, &terminatorOne, &terminator2, instruction, ID
407      );
408      call = instruction;
409      jumpTableID = ID;
410
411      // If all items found, we are at the beginning of a symbol's objdump.
412      if ((items == 3) && (terminator1 == ':')) {
413
414        endAddress = executableInformation->getLoadAddress() + offset - 1;
415
416        // If we are currently processing a symbol, finalize it.
417        if (processSymbol) {
418          finalizeSymbol(
419            executableInformation,
420            currentSymbol,
421            startAddress,
422            endAddress,
423            theInstructions
424          );
425        }
426
427        // Start processing of a new symbol.
428        startAddress = 0;
429        currentSymbol = "";
430        processSymbol = false;
431        theInstructions.clear();
432
433        // See if the new symbol is one that we care about.
434        if (SymbolsToAnalyze->isDesired( symbol )) {
435          startAddress = executableInformation->getLoadAddress() + offset;
436          currentSymbol = symbol;
437          processSymbol = true;
438          theInstructions.push_back( lineInfo );
439        }
440      }
441      // If it looks like a jump table, finalize the symbol.
442      else if ( (found == 5) && (terminatorOne == ':') && (terminator2 == '\t')
443               && (call.find( "call" ) != std::string::npos)
444               && (jumpTableID.find( "+0x" ) != std::string::npos)
445               && processSymbol )
446      {
447
448          endAddress = executableInformation->getLoadAddress() + offset - 1;
449
450          // If we are currently processing a symbol, finalize it.
451          if ( processSymbol ) {
452            finalizeSymbol(
453              executableInformation,
454              currentSymbol,
455              startAddress,
456              endAddress,
457              theInstructions
458            );
459          }
460          processSymbol = false;
461      }
462      else if (processSymbol) {
463
464        // See if it is the dump of an instruction.
465        items = sscanf(
466          line.c_str(),
467          "%x%c\t%*[^\t]%c",
468          &instructionOffset, &terminator1, &terminator2
469        );
470
471        // If it looks like an instruction ...
472        if ((items == 3) && (terminator1 == ':') && (terminator2 == '\t')) {
473
474          // update the line's information, save it and ...
475          lineInfo.address =
476           executableInformation->getLoadAddress() + instructionOffset;
477          lineInfo.isInstruction = true;
478          lineInfo.isNop         = isNop( line.c_str(), lineInfo.nopSize );
479          lineInfo.isBranch      = isBranchLine( line.c_str() );
480        }
481
482        // Always save the line.
483        theInstructions.push_back( lineInfo );
484      }
485    }
486  }
487}
Note: See TracBrowser for help on using the repository browser.