spiegel-keyman/common/windows/delphi/ext/tds2dbg/tds2dbg.cpp

2568 lines
87 KiB
C++

// Copyright (c) 2009-2010, Bjarne Juul Pasgaard
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice, this permission notice and the following disclaimer
// appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "tds2dbg.h"
#include "tdscvstructs.h"
#include "log.h"
#include "fileutils.h"
#include <string>
#include <map>
#include <stdexcept>
#include <iomanip>
/// @brief Ensures that the next write operation starts on an aligned
/// address.
///
/// @param dst The target to write to.
/// @param p The current write position of the target.
/// @param m Alignment mask. Must be of the form 2^n-1.
///
/// @tparam T The target (e.g. file wrapper) class to write to. It must
/// provide a <pre>void write(unsigned char*, unsigned int);</pre>
/// method.
///
/// @throw runtime_error In case of errors.
template <class T>
void zeroPad(T& dst, unsigned int p, unsigned int m) throw(std::runtime_error)
{
unsigned int algnmnt = p & m;
if(algnmnt) {
unsigned char filler = 0;
dst.writeRep(filler,1+m-algnmnt);
}
}
/// @brief Ensure that a symbol record to be written does not cross a
/// page boundary in a file.
///
/// S_ALIGN symbols are inserted to ensure proper page alignment.
///
/// @throw runtime_error In case of errors.
void
pageAlignSymbol(FileWrapper& f, unsigned int len, unsigned int mask)
throw(std::runtime_error)
{
unsigned int headPg = (f.tell()) & (~mask);
unsigned int tailPg = (f.tell()+len) & (~mask);
if(headPg != tailPg) {
// Compute the number of bytes to skip
tailPg -= f.tell();
// Enough bytes for a S_ALIGN record with 4 bytes padding?
if(tailPg<8) {
// Otherwise, skip a complete page.
tailPg += 1 + mask;
}
// Report on alignment to be applied
mylog()(4) << " Inserted S_ALIGN at " << f.tell()
<< " with " << tailPg-4
<< " padding bytes to make room for "
<< len << " bytes." << std::endl;
CVsymRecHdr symHdr(tailPg-2,CVskAlign);
f.write(symHdr);
unsigned char pad = 0;
f.writeRep(pad,tailPg-4);
}
}
/// @brief Ensure that a block of bytes to be written does not cross a
/// page boundary in a file.
///
/// 0's are inserted to ensure proper page alignment.
///
/// @throw runtime_error In case of errors.
void
pageAlign(FileWrapper& f, unsigned int len, unsigned int mask)
throw(std::runtime_error)
{
unsigned int headPg = (f.tell()) & mask;
unsigned int tailPg = (f.tell()+len) & mask;
if(headPg != tailPg) {
// Compute the number of bytes to skip
tailPg -= f.tell();
unsigned char pad = 0;
f.write(pad,tailPg);
}
}
/// @brief Handles conversion (from TDS to Code View) of and settings related
/// to various elements.
class Converter
{
public:
/// @brief Constructor.
///
/// @param doConvTypes True if types are to be converted.
/// @param staticsAsPublics True if statics are to be treated as publics.
Converter(bool doConvTypes, bool staticsAsPublics) :
m_doConvTypes(doConvTypes), m_staticsAsPublics(staticsAsPublics) {}
/// @brief Converts from a 4 byte TDS type index to a 2-byte DBG type index.
///
/// @param typeIdx The 4-byte TDS type index.
///
/// @return The 2-byte DBG type index.
WORD typIdx(DWORD typeIdx) const;
/// @brief Loads all names from the sstNames subsection in a
/// TDS file into an internal cache.
///
/// @param tdsFile The TDS file to read from.
/// @param offs Location of the sstNames subsection in the tds file.
/// @param sz Number of bytes in the sstNames subsection.
///
/// @throw runtime_error If reading of sstNames failed.
void loadNameCache(FileWrapper& tdsFile, unsigned int offs, unsigned int sz)
throw(std::runtime_error);
/// @brief Returns a name with a specified 1-based index.
///
/// @param nameIdx The 1-based index of the name.
///
/// @return The name retrieved. Empty if not found.
std::string getName(unsigned int nameIdx) const;
/// @brief Returns true if sstGlobalTypes is to be converted.
///
/// @retval true If sstGlobalTypes is to be converted.
/// @retval false Otherwise.
bool doConvertTypes() const { return m_doConvTypes; }
/// @brief Returns true if statics are to be treated as publics.
///
/// @retval true If statics are to be treated as publics.
/// @retval false Otherwise.
bool staticAsPub() const { return m_staticsAsPublics; }
protected:
/// @brief True if types are to be converted.
bool m_doConvTypes;
/// @brief True if statics are to be treated as publics
bool m_staticsAsPublics;
/// @brief Name cache container type.
typedef std::map<unsigned int,std::string> NameCache;
/// @brief Cache of names.
///
/// The key is the name index
NameCache m_cache;
};
WORD
Converter::typIdx(DWORD typeIdx) const {
// TypeIdx lower than 0x1000 (predefined types) do not need conversion.
if(!m_doConvTypes && typeIdx>=0x1000) {
return 0;
}
static bool warned = false;
if(typeIdx>0xFFFF) {
if(!warned) {
mylog()(1)
<< "Warning: TDS type index too large for DBG file" << std::endl;
warned = true;
}
return 0;
}
return (WORD) typeIdx;
}
void
Converter::
loadNameCache(FileWrapper& tdsFile, unsigned int offs, unsigned int sz)
throw(std::runtime_error)
{
// Remember the current read position
unsigned int oldPos = tdsFile.tell();
// Read the number of names
unsigned int numNames;
tdsFile.seek(offs);
tdsFile.read(numNames);
// Loop to read in all names
unsigned int nameIdx = 1;
while(sz>4 && numNames) {
// Read length of the name
unsigned char len;
tdsFile.read(len);
// Prepare a slot in the name cache
std::pair<NameCache::iterator,bool> insertPoint =
m_cache.insert(NameCache::value_type(nameIdx++,std::string()));
insertPoint.first->second.clear();
insertPoint.first->second.reserve(len);
// Loop to read the contents of the name
while(sz && len) {
unsigned char c;
tdsFile.read(c);
insertPoint.first->second.push_back(c);
--sz;
--len;
}
// An additional 0 is included between each name. Skip it!
if(sz) {
tdsFile.skip(1);
--sz;
}
// Next name
--numNames;
}
// Return to the old read position
tdsFile.seek(oldPos);
}
std::string
Converter::getName(unsigned int nameIdx) const
{
static bool warningReported = false;
NameCache::const_iterator it = m_cache.find(nameIdx);
if(m_cache.end() != it) {
return it->second;
} else if(!warningReported) {
warningReported = true;
mylog()(1) << "Failed to get name with index "
<< std::hex << nameIdx << ". Further reports suppressed."
<< std::endl;
}
return std::string();
}
/// @brief The number of hash buckets in the name hash table.
#define NUM_HASH_BUCKETS 0x82
/// @brief Internal symbol name hash table
class NameHshTbl
{
public:
/// @brief Constructor.
///
/// @param numBuckets The number of buckets to use in the hash table.
NameHshTbl(unsigned short numBuckets);
/// @brief Appends a symbol to the name hash table.
///
/// @param n The name of the symbol to append.
/// @param offset The offset of the symbol reference from the beginning of
/// the symbols.
void append(const std::string& n, DWORD offset);
/// @brief Writes the symbol name hash table to a symbol subsection of a
/// DBG file.
///
/// @param dbgFile The DBG file to write to.
/// @param[out] hshTableSz The size of the generated address table.
///
/// @throw runtime_error In case of errors.
void write(FileWrapper& dbgFile, DWORD& hshTableSz) const
throw(std::runtime_error);
protected:
/// @brief Element in a hash bucket.
struct HshBcktElem
{
/// @brief Default constructor.
HshBcktElem() {}
/// @brief Constructor.
///
/// @param offs The offset of the symbol reference from the beginning of
/// the symbols.
/// @param hsh The name hash of the symbol.
HshBcktElem(DWORD offs, DWORD hsh) : symRefOffs(offs), nameHash(hsh) {}
/// @brief The offset of the symbol reference from the beginning of the
/// symbols.
DWORD symRefOffs;
/// @brief The name hash of the symbol.
DWORD nameHash;
};
/// @brief Container type for the hash buckets that make up the table.
///
/// The index is the hash bucket number and the value is a vector of
/// symbol offsets in that bucket.
typedef std::vector<std::vector<HshBcktElem> > HashBuckets;
protected:
/// @brief The internal name hash table.
HashBuckets hshTable;
};
NameHshTbl::NameHshTbl(unsigned short numBuckets) : hshTable(numBuckets)
{
}
void
NameHshTbl::write(FileWrapper& dbgFile, DWORD& hshTableSz) const
throw(std::runtime_error)
{
// Remember where we start writing to
unsigned int strtPos = dbgFile.tell();
// Write out the number of hash buckets.
unsigned short numBuckets = hshTable.size();
dbgFile.write(numBuckets);
// Filler bytes
numBuckets = 0;
dbgFile.write(numBuckets);
// Write out offsets of each buckets offset table
unsigned int dw = 0;
HashBuckets::const_iterator bcktIt = hshTable.begin();
while(hshTable.end() != bcktIt) {
// Write out current offset
dbgFile.write(dw);
// Compute offset of next buckets offset table
dw += bcktIt->size() * 8;
// Next bucket
++bcktIt;
}
// Write out the number of elements in each buckets offset table
bcktIt = hshTable.begin();
while(hshTable.end() != bcktIt) {
// Write out the number of elements in this buckets offset table
dw = bcktIt->size();
dbgFile.write(dw);
// Next bucket
++bcktIt;
}
// Write out the offset tables for all buckets
bcktIt = hshTable.begin();
while(hshTable.end() != bcktIt) {
// Loop over all symbols within this bucket
std::vector<HshBcktElem>::const_iterator symIt = bcktIt->begin();
while(bcktIt->end() != symIt) {
// Write out offset and name hash of current symbol
dbgFile.write(*symIt);
// Next symbol
++symIt;
}
// Next segment
++bcktIt;
}
// Compute the total size of the address hash table
hshTableSz = dbgFile.tell() - strtPos;
// Log the results
mylog()(2) << " Appended name hash table at " << std::hex
<< strtPos << " with size " << hshTableSz << std::endl;
}
void NameHshTbl::append(const std::string& n, DWORD offset)
{
unsigned int l = n.size();
unsigned long hshTail = 0;
while(l & 3) {
--l;
hshTail |= (n[l] & 0xDF);
hshTail <<= 8;
}
l >>= 2;
unsigned long hsh = 0;
const unsigned long* ln =
reinterpret_cast<const unsigned long*>(n.c_str());
for(unsigned int i=0; i < l; ++i) {
hsh ^= 0xDFDFDFDF & (ln[i]);
_lrotl(hsh,4);
}
hsh ^= hshTail;
hshTable[hsh % hshTable.size()].
push_back(HshBcktElem(offset,hsh));
}
/// @brief Record used internally for temporary storage of symbols.
struct SymbolRecord {
DWORD typeIdx; ///< Type index of the symbol.
DWORD nameIdx; ///< Name index of the symbol.
WORD symKind; ///< Symbol kind.
WORD moduleIdx; ///< Index of module that contains this symbol record.
DWORD offsSymRec; /**< Offset of symbol record in the DBG symbol table
where it is located. */
DWORD symRefOffs; ///< Symbol reference offset to insert in hash table. */
};
/// @brief Container type of the temporary storage of symbols within a segment.
///
/// The key is the offset relative to the segment. This ensures that
/// symbols are ordered according to the offset.
typedef std::map<DWORD,SymbolRecord> SegmSymMap;
/// @brief Container type of the temporary storage of public symbols.
///
/// The key is the segment.
typedef std::map<WORD,SegmSymMap> SymRecMap;
/// @brief Appends an address hash table to a symbol subsection.
///
/// @param dbgFile The DBG file to write to.
/// @param[out] hshTableSz The size of the generated address table.
/// @param syms The symbols to generate the hash table for.
/// The symRefOffs member for each symbol must
/// have been set correctly.
///
/// @throw runtime_error In case of errors.
void createAddrHashTable(FileWrapper& dbgFile,
DWORD& hshTableSz,
const SymRecMap& syms) throw(std::runtime_error)
{
// Remember where we start writing to
unsigned int strtPos = dbgFile.tell();
// Write out the number of segments.
unsigned short numSegs = syms.size();
dbgFile.write(numSegs);
// Filler bytes
numSegs = 0;
dbgFile.write(numSegs);
// Write out offsets of each segments offset table
unsigned int dw = 0;
SymRecMap::const_iterator segIt = syms.begin();
while(syms.end() != segIt) {
// Write out current offset
dbgFile.write(dw);
// Compute offset of next segments offset table
dw += segIt->second.size() * 8;
// Next segment
++segIt;
}
// Write out the number of elements in each segments offset table
segIt = syms.begin();
while(syms.end() != segIt) {
// Write out the number of elements in this segments offset table
dw = segIt->second.size();
dbgFile.write(dw);
// Next segment
++segIt;
}
// Write out the offset tables for all segments
segIt = syms.begin();
while(syms.end() != segIt) {
// Loop over all symbols within this segment
SegmSymMap::const_iterator symIt = segIt->second.begin();
while(segIt->second.end() != symIt) {
// Write out offset of symbol record within the subsection
dbgFile.write(symIt->second.symRefOffs);
// Write out offset of symbol relative to segment
dbgFile.write(symIt->first);
// Next symbol
++symIt;
}
// Next segment
++segIt;
}
// Compute the total size of the address hash table
hshTableSz = dbgFile.tell() - strtPos;
// Log the results
mylog()(2) << " Appended address hash table at " << std::hex
<< strtPos << " with size " << hshTableSz << std::endl;
}
/// @brief Appends a closing S_ALIGN symbol.
///
/// @param dbgFile The file to append to.
/// @param algnmtn The alignment mask to apply.
///
/// @throw runtime_error In case of errors.
void appendClosingAlign(FileWrapper& dbgFile) throw(std::runtime_error)
{
// The symbol record header
CVsymRecHdr recHdr(6,CVskAlign);
// Reserve space for a DBG symbol record header to be
// written out later.
HeaderWithLen<CVsymRecHdr,WORD> dbgRecHdr(recHdr,recHdr.len);
// Create a record writer to keep track of the number of
// payload bytes written.
RecordWriter wrtr;
DWORD pad = 0xFFFFFFFF;
wrtr.write(pad);
// Ensure that the record does not cross a page boundary.
unsigned int bytesToWrite = dbgRecHdr.length(wrtr);
pageAlignSymbol(dbgFile,bytesToWrite,0xFFF);
dbgRecHdr.commit(dbgFile,wrtr);
}
/// @brief Converts a TDS symbol table to a DBG symbol table.
///
/// See "Symbols" in Tools Interface Standard Formats Specification
/// for Windows version 1.0 for the definition of a DBG type record
/// and type string.
///
/// @param conv Converter used for various conversion tasks.
/// @param moduleIdx Index of the current module. 0 if none.
/// @param tdsFile The input tds file.
/// @param tdsTableSz The size of the symbol table in the input.
/// @param dbgFile The output dbg file.
/// @param[out] symTableSz The size of the symbol table in the output.
/// @param[out] syms Addressable symbols already and inserted in
/// the generated symbol table.
/// @param[out] pubSyms Contents for the sstGlobalPub subsection.
/// @param[out] staticSyms Contents for the sstStaticSym subsection.
///
/// @throw runtime_error In case of errors.
void convertSymbolSubsection(const Converter& conv,
unsigned short moduleIdx,
FileWrapper& tdsFile,
DWORD tdsTableSz,
FileWrapper& dbgFile,
DWORD& symTableSz,
SymRecMap& syms,
NameHshTbl& nameHshTable,
SymRecMap& pubSyms,
SymRecMap& staticSyms)
throw(std::runtime_error)
{
// Remember where we start writing to
unsigned int strtPos = dbgFile.tell();
// Loop over all symbol records in the TDS file
RecordReader rdr(tdsFile,tdsTableSz);
while(!rdr.eor()) {
// Remember the file pos that we will start reading the record from
unsigned int tdsHdrPos = rdr.tell();
// Read the symbol record header
CVsymRecHdr recHdr;
rdr.read(recHdr);
// Remember the record length
unsigned int recLen = recHdr.len;
switch(recHdr.symKind) {
case CVskCompile: {
// Handle compile flags (copy almost as is --- seems like the
// tds files have a wrong length field, which we must correct).
mylog()(3) << " Converting S_COMPILE" << std::endl;
// Read the payload header from the tds file.
CVsrCompileFlagPayloadHdr tdsPlHdr;
rdr.read(tdsPlHdr);
BYTE versionLen;
rdr.read(versionLen);
// Reserve space for a DBG symbol record header to be
// written out later.
HeaderWithLen<CVsymRecHdr,WORD> dbgRecHdr(recHdr,recHdr.len);
// Create a record writer to keep track of the number of
// payload bytes written.
RecordWriter wrtr;
// Write the payload header from the TDS file as is.
wrtr.write(tdsPlHdr);
// Write a length-prefixed name (copy from TDS file).
wrtr.write(versionLen);
copyFileContent(wrtr,tdsFile,versionLen);
// Ensure that the next record is properly aligned on a
// dword boundary
zeroPad(wrtr,dbgRecHdr.length(wrtr),sizeof(int)-1);
// Ensure that the record does not cross a page boundary.
pageAlignSymbol(dbgFile,dbgRecHdr.length(wrtr),0xFFF);
// Write the symbol record header with the correct length field
dbgRecHdr.commit(dbgFile,wrtr);
break;
}
case CVskRegister: {
// Handle Register symbol record
mylog()(3) << " Converting S_REGISTER" << std::endl;
// Read the symbol record payload from the TDS file
TDSsrRegisterPayload tdsPl;
rdr.read(tdsPl);
// Reserve space for a DBG symbol record header to be
// written out later.
HeaderWithLen<CVsymRecHdr,WORD> dbgRecHdr(recHdr,recHdr.len);
// Create a record writer to keep track of the number of payload
// bytes written.
RecordWriter wrtr;
// Create the payload header for the DBG file
CVsrRegisterPayloadHdr dbgPlHdr = {
conv.typIdx(tdsPl.typeIdx),
tdsPl.regId
};
// Write the payload header
wrtr.write(dbgPlHdr);
// Append the length-prefixed name of the symbol to the payload
wrtr.write1bLpfName(conv.getName(tdsPl.nameIdx));
// Ensure that the next record is properly aligned on a
// dword boundary
zeroPad(wrtr,dbgRecHdr.length(wrtr),sizeof(int)-1);
// Ensure that the record does not cross a page boundary.
pageAlignSymbol(dbgFile,dbgRecHdr.length(wrtr),0xFFF);
// Write the symbol record header with the correct length field
dbgRecHdr.commit(dbgFile,wrtr);
break;
}
case CVskUDT: {
// Handle a User defined type
if(conv.doConvertTypes()) {
mylog()(3) << " Converting S_UDT" << std::endl;
// Read the entire payload from the TDS file
TDSsrUserDefinedTypePayload tdsPl;
rdr.read(tdsPl);
// If sstGlobalTypes is not converted, we can only handle
// predefined types (typeIdx<0x1000).
// Reserve space for a DBG symbol record header to be
// written out later.
HeaderWithLen<CVsymRecHdr,WORD> dbgRecHdr(recHdr,recHdr.len);
// Create a record writer to keep track of the number of
// payload bytes written.
RecordWriter wrtr;
// Create the payload header for the DBG file
CVsrUserDefinedTypePayloadHdr dbgPlHdr =
{conv.typIdx(tdsPl.typeIdx)};
// Write the payload header
wrtr.write(dbgPlHdr);
// Get the type name
const std::string& tpName = conv.getName(tdsPl.nameIdx);
// Append the length-prefixed type name to the payload
wrtr.write1bLpfName(tpName);
// Ensure that the next record is properly aligned on a
// dword boundary
zeroPad(wrtr,dbgRecHdr.length(wrtr),sizeof(int)-1);
// Ensure that the record does not cross a page boundary.
pageAlignSymbol(dbgFile,dbgRecHdr.length(wrtr),0xFFF);
// The type name should potentially be referenced from a
// name hash table, so register it's location in the name hash.
nameHshTable.append(tpName,dbgFile.tell()-strtPos);
// Write the symbol record header with the correct
// length field
dbgRecHdr.commit(dbgFile,wrtr);
} else {
mylog()(3) << " Skipping S_UDT" << std::endl;
}
break;
}
case CVskSSearch: {
// Handle a Start Search Symbol
mylog()(3) << " Converting S_SSEARCH" << std::endl;
// Read the payload from the TDS file
TDSsrStartSearchPayload tdsPl;
rdr.read(tdsPl);
// Reserve space for a DBG symbol record header to be
// written out later.
HeaderWithLen<CVsymRecHdr,WORD> dbgRecHdr(recHdr,recHdr.len);
// Create a record writer to keep track of the number of
// payload bytes written.
RecordWriter wrtr;
// Create the corresponding DBG payload
CVsrStartSearchPayload dbgPl = {tdsPl.offs, tdsPl.segment};
// Write the payload to the DBG file
wrtr.write(dbgPl);
// Ensure that the next record is properly aligned on a
// dword boundary
zeroPad(wrtr,dbgRecHdr.length(wrtr),sizeof(int)-1);
// Ensure that the record does not cross a page boundary.
pageAlignSymbol(dbgFile,dbgRecHdr.length(wrtr),0xFFF);
// Write the symbol record header with the correct length field
dbgRecHdr.commit(dbgFile,wrtr);
break;
}
case CVskEnd: {
// Handle an End of Block symbol
// No payload, no conversion, just write as is.
mylog()(3) << " Converting S_END" << std::endl;
// Reserve space for a DBG symbol record header to be
// written out later.
HeaderWithLen<CVsymRecHdr,WORD> dbgRecHdr(recHdr,recHdr.len);
// Create a record writer to keep track of the number of
// payload bytes written (none).
RecordWriter wrtr;
// Ensure that the next record is properly aligned on a
// dword boundary
zeroPad(wrtr,dbgRecHdr.length(wrtr),sizeof(int)-1);
// Ensure that the record does not cross a page boundary.
pageAlignSymbol(dbgFile,dbgRecHdr.length(wrtr),0xFFF);
// Write the symbol record header with the correct length field
dbgRecHdr.commit(dbgFile,wrtr);
break;
}
case CVskBPRel: {
// Handle a BP Relative symbol
mylog()(3) << " Converting S_BPREL" << std::endl;
// Read the payload from the TDS file
TDSsrBPRelativePayload tdsPl;
rdr.read(tdsPl);
// Reserve space for a DBG symbol record header to be
// written out later.
HeaderWithLen<CVsymRecHdr,WORD> dbgRecHdr(recHdr,recHdr.len);
// Create a record writer to keep track of the number of
// payload bytes written.
RecordWriter wrtr;
// Create the corresponding payload header for the DBG file
CVsrBPRelativePayloadHdr dbgPlHdr = {
tdsPl.offset,conv.typIdx(tdsPl.typeIdx)
};
// Write the payload header to the DBG file
wrtr.write(dbgPlHdr);
// Append the length-prefixed symbol name to the payload header
wrtr.write1bLpfName(conv.getName(tdsPl.nameIdx));
// Ensure that the next record is properly aligned on a
// dword boundary
zeroPad(wrtr,dbgRecHdr.length(wrtr),sizeof(int)-1);
// Ensure that the record does not cross a page boundary.
pageAlignSymbol(dbgFile,dbgRecHdr.length(wrtr),0xFFF);
// Write the symbol record header with the correct length field
dbgRecHdr.commit(dbgFile,wrtr);
break;
}
case CVskLocalData: // Intentional fall-through
case CVskGlobalData: {
if(CVskLocalData == recHdr.symKind) {
mylog()(3) << " Converting S_LDATA" << std::endl;
} else {
mylog()(3) << " Converting S_GDATA" << std::endl;
}
// Read the payload from the TDS file
TDSsrGlobalDataPayload tdsPl;
rdr.read(tdsPl);
// Reserve space for a DBG symbol record header to be
// written out later.
HeaderWithLen<CVsymRecHdr,WORD> dbgRecHdr(recHdr,recHdr.len);
// Create a record writer to keep track of the number of
// payload bytes written.
RecordWriter wrtr;
// Create the corresponding payload header for the DBG file
CVsrGlobalDataPayloadHdr dbgPlHdr =
{tdsPl.offset,tdsPl.segment,conv.typIdx(tdsPl.typeIdx)};
// Write the payload header to the DBG file
wrtr.write(dbgPlHdr);
// Get the symbol name
const std::string& symName = conv.getName(tdsPl.nameIdx);
// Append the length-prefixed symbol name to the payload header
wrtr.write1bLpfName(symName);
// Ensure that the next record is properly aligned on a
// dword boundary
zeroPad(wrtr,dbgRecHdr.length(wrtr),sizeof(int)-1);
// Ensure that the record does not cross a page boundary.
pageAlignSymbol(dbgFile,dbgRecHdr.length(wrtr),0xFFF);
// Remember where the record is written to
unsigned int dbgHdrPos = dbgFile.tell() - strtPos;
// Write the symbol record header with the correct length field
dbgRecHdr.commit(dbgFile,wrtr);
// Global and Local Data symbol records must have a
// corresponding PUB32 record in the sstGlobalPub
// section or a corresponding DATAREF record in the
// sstStaticSym, respectively, of the DBG file as
// well. We must therefore store the record for later
// use.
SymbolRecord sr;
sr.typeIdx = tdsPl.typeIdx;
sr.nameIdx = tdsPl.nameIdx;
sr.moduleIdx = moduleIdx;
sr.offsSymRec = dbgHdrPos;
sr.symRefOffs = dbgHdrPos;
if(CVskGlobalData == recHdr.symKind || conv.staticAsPub()) {
sr.symKind = CVskPublic;
pubSyms[tdsPl.segment][tdsPl.offset] = sr;
} else {
sr.symKind = CVskDataRef;
staticSyms[tdsPl.segment][tdsPl.offset] = sr;
}
// The address of the symbol should potentially be referenced
// from an address hash table, so we need to add to the syms.
syms[tdsPl.segment][tdsPl.offset] = sr;
// The symbol name should potentially be referenced from a
// name hash table, so we need to register it in the name hash.
nameHshTable.append(symName,dbgHdrPos);
break;
}
case CVskPublic: // Intentional fall-through
case CVskLocalProc:
case CVskGlobalProc: {
// Handle Public, LocalProc, or GlobalProc symbol
if(CVskPublic == recHdr.symKind) {
mylog()(3) << " Converting S_PUB" << std::endl;
} else if(CVskLocalProc == recHdr.symKind) {
mylog()(3) << " Converting S_LPROC" << std::endl;
} else {
mylog()(3) << " Converting S_GPROC" << std::endl;
}
// Read the payload header from the TDS file (ignore the
// length-prefixed linker name of the symbol).
TDSsrLocalProcPayloadHdr tdsPlHdr;
rdr.read(tdsPlHdr);
// Reserve space for a DBG symbol record header to be
// written out later.
HeaderWithLen<CVsymRecHdr,WORD> dbgRecHdr(recHdr,recHdr.len);
// Create a record writer to keep track of the number of
// payload bytes written.
RecordWriter wrtr;
// Prepare the payload header for the DBG file.
CVsrLocalProcPayloadHdr dbgSrHdr =
{tdsPlHdr.parent,tdsPlHdr.end,tdsPlHdr.next,
tdsPlHdr.procLen,tdsPlHdr.dbgStart,tdsPlHdr.dbgEnd,
tdsPlHdr.offset,tdsPlHdr.segment,
conv.typIdx(tdsPlHdr.typeIdx), (BYTE) (tdsPlHdr.flags)};
mylog()(3) << " Writing header" << std::endl;
// Write the payload header to the DBG file
wrtr.write(dbgSrHdr);
// Get the symbol name
const std::string& symName = conv.getName(tdsPlHdr.nameIdx);
// Append the length-prefixed symbol name to the payload header
wrtr.write1bLpfName(symName);
// Ensure that the next record is properly aligned on a
// dword boundary
zeroPad(wrtr,dbgRecHdr.length(wrtr),sizeof(int)-1);
// Ensure that the record does not cross a page boundary.
pageAlignSymbol(dbgFile,dbgRecHdr.length(wrtr),0xFFF);
// Remember where the record is written to
unsigned int dbgHdrPos = dbgFile.tell() - strtPos;
// Write the symbol record header with the correct length field
dbgRecHdr.commit(dbgFile,wrtr);
// Global and Local Procedure symbol records must have
// a corresponding PUB32 record in the sstGlobalPub
// section or a corresponding PROCREF record in the
// sstStaticSym, respectively, of the DBG file as
// well. We must therefore store the record for later
// use.
SymbolRecord sr;
sr.typeIdx = tdsPlHdr.typeIdx;
sr.nameIdx = tdsPlHdr.nameIdx;
sr.moduleIdx = moduleIdx;
sr.offsSymRec = dbgHdrPos;
sr.symRefOffs = dbgHdrPos;
if(CVskLocalProc != recHdr.symKind || conv.staticAsPub()) {
sr.symKind = CVskPublic;
pubSyms[tdsPlHdr.segment][tdsPlHdr.offset] = sr;
} else {
sr.symKind = CVskProcRef;
staticSyms[tdsPlHdr.segment][tdsPlHdr.offset] = sr;
}
// The address of the symbol should potentially be referenced
// from an address hash table, so we need to add to the syms.
syms[tdsPlHdr.segment][tdsPlHdr.offset] = sr;
// The symbol name should potentially be referenced from a
// name hash table, so we need to register it in the name hash.
nameHshTable.append(symName,dbgHdrPos);
break;
}
case CVskBlock32: {
mylog()(3) << " Converting S_BLOCK" << std::endl;
// Read the payload from the TDS file
TDSsrBlock32Payload tdsPl;
rdr.read(tdsPl);
// Reserve space for a DBG symbol record header to be
// written out later.
HeaderWithLen<CVsymRecHdr,WORD> dbgRecHdr(recHdr,recHdr.len);
// Create a record writer to keep track of the number of
// payload bytes written.
RecordWriter wrtr;
// Create the corresponding payload header for the DBG file
CVsrBlock32PayloadHdr dbgPlHdr =
{tdsPl.parent,tdsPl.end,tdsPl.next,tdsPl.len,tdsPl.offset,
tdsPl.segment};
// Write the payload header to the DBG file
wrtr.write(dbgPlHdr);
// Append the length-prefixed symbol name to the payload header
wrtr.write1bLpfName(conv.getName(tdsPl.nameIdx));
// Ensure that the next record is properly aligned on a
// dword boundary
zeroPad(wrtr,dbgRecHdr.length(wrtr),sizeof(int)-1);
// Ensure that the record does not cross a page boundary.
pageAlignSymbol(dbgFile,dbgRecHdr.length(wrtr),0xFFF);
// Write the symbol record header with the correct length field
dbgRecHdr.commit(dbgFile,wrtr);
break;
}
case CVskSkip: {
mylog()(3) << " Skipping S_SKIP" << std::endl;
// Skip this record
break;
}
case CVskGProcRef: {
// The GPROCREF's located in the sstGlobalSym of TDS files
// doesn't contain enough info (reference to the originating
// module is 0) for us to create a Code View PROCREF record
// out of it. We therefore create a PUB32 in the sstGlobalPub
// instead.
mylog()(3) << " Converting S_GPROCREF" << std::endl;
/// @todo Use the segment and offset to look up the data needed
/// to create a Code View PROCREF as well.
// Read the type string payload from the TDS file
TDSsrGlobalProcRefPayload tdsPl;
rdr.read(tdsPl);
SymbolRecord sr;
sr.typeIdx = tdsPl.typeIdx;
sr.nameIdx = tdsPl.nameIdx;
sr.moduleIdx = 0xFFFF;
sr.offsSymRec = sr.symRefOffs = dbgFile.tell()-strtPos;
sr.symKind = CVskPublic;
pubSyms[tdsPl.segment][tdsPl.offset] = sr;
break;
}
case CVskGDataRef: {
// The GDATAREF's located in the sstGlobalSym of TDS files
// doesn't contain enough info (reference to the originating
// module is 0) for us to create a Code View PROCREF record
// out of it. We therefore create a PUB32 in the sstGlobalPub
// instead.
mylog()(3) << " Converting S_GDATAREF" << std::endl;
/// @todo Use the segment and offset to look up the data needed
/// to create a Code View DATAREF as well.
// Read the type string payload from the TDS file
TDSsrGlobalDataRefPayload tdsPl;
rdr.read(tdsPl);
SymbolRecord sr;
sr.typeIdx = tdsPl.typeIdx;
sr.nameIdx = tdsPl.nameIdx;
sr.moduleIdx = 0xFFFF;
sr.offsSymRec = sr.symRefOffs = dbgFile.tell()-strtPos;
sr.symKind = CVskPublic;
pubSyms[tdsPl.segment][tdsPl.offset] = sr;
break;
}
case CVskNamespace: // Intentional fall-through
case CVskUsing:
// We currently don't know how to convert this. There does
// not seem to be a corresponding symbol kind in CodeView.
case CVskEntry32:
// We currently don't know how to convert this. Borlands
// documentation states that this symbol record is optional,
// so we leave it out.
break;
case CVskOptVar32:
// Variable life range support. We don't know how to
// convert this, currently.
break;
case CVskProcRet32:
// Procedure return epilogue.
// Don't know how to convert this.
break;
case CVskSaveRegs32:
// Don't know how to convert this.
break;
default:
mylog()(1)
<< "Warning: Unhandled symbol record with symbol kind "
<< std::hex << recHdr.symKind << " at "
<< tdsHdrPos << " with length " << recLen << std::endl;
break;
}
// Jump to the next symbol record
rdr.seek(tdsHdrPos + sizeof(recHdr.len)+ recLen);
}
// Symbol table must end with a closing S_ALIGN
appendClosingAlign(dbgFile);
// Compute the size of the contructed symbol table.
symTableSz = dbgFile.tell()-strtPos;
}
/// @brief Converts a numeric leaf from a TDS type record to a numeric leaf
/// in a DBG type record.
///
/// @param leafId The id of the numeric leaf.
/// @param tsPos The position within the TDS file from which the leaf Id
/// was read.
/// @param tsRdr Type string reader.
/// @param tsWrtr Type string writer.
///
/// @throw runtime_error In case of errors.
void convertNumericLeaf(WORD leafId,
unsigned int tsPos,
RecordReader& tsRdr,
RecordWriter& tsWrtr) throw(std::runtime_error)
{
switch(leafId) {
case LF_CHAR:
tsWrtr.write(leafId);
copyFileContent(tsWrtr,tsRdr,1);
break;
case LF_SHORT: // Intentional fall-through
case LF_USHORT:
tsWrtr.write(leafId);
copyFileContent(tsWrtr,tsRdr,2);
break;
case LF_LONG: // Intentional fall-through
case LF_ULONG:
case LF_REAL32:
case LF_COMPLEX32:
tsWrtr.write(leafId);
copyFileContent(tsWrtr,tsRdr,4);
break;
case LF_REAL48:
tsWrtr.write(leafId);
copyFileContent(tsWrtr,tsRdr,6);
break;
case LF_REAL64: // Intentional fall-through
case LF_QUADWORD:
case LF_UQUADWORD:
case LF_COMPLEX64:
tsWrtr.write(leafId);
copyFileContent(tsWrtr,tsRdr,8);
break;
case LF_REAL80:
case LF_COMPLEX80:
tsWrtr.write(leafId);
copyFileContent(tsWrtr,tsRdr,10);
break;
case LF_REAL128:
case LF_COMPLEX128:
tsWrtr.write(leafId);
copyFileContent(tsWrtr,tsRdr,16);
break;
case LF_VARSTRING:
unsigned short len;
tsRdr.read(len);
tsWrtr.write(leafId);
tsWrtr.write(len);
copyFileContent(tsWrtr,tsRdr,len);
break;
default:
if(leafId<LF_NUMERIC) {
// The leafId must be interpreted as the value.
tsWrtr.write(leafId);
} else {
mylog()(1)
<< "Error: Unhandled type record with leaf id "
<< std::hex << leafId << " at " << tsPos << std::endl;
}
break;
}
}
/// @brief Converts a numeric leaf from a TDS type record to a numeric leaf
/// in a DBG type record.
///
/// @param tsRdr Type string reader.
/// @param tsWrtr Type string writer.
///
/// @throw runtime_error In case of errors.
void copyNumericLeaf(RecordReader& tsRdr,
RecordWriter& tsWrtr) throw(std::runtime_error)
{
unsigned int tsPos = tsRdr.tell();
WORD leafId;
tsRdr.read(leafId);
convertNumericLeaf(leafId,tsPos,tsRdr,tsWrtr);
}
/// @brief Converts a table of TDS type records to a corresponding table
/// of DBG type records.
///
/// See "Types Definition Segment" in Tools Interface Standard Formats
/// Specification for Windows version 1.0 for the definition of a DBG
/// type record and type string.
///
/// @param conv Converter to use for various conversion tasks.
/// @param tdsFile The input tds file.
/// @param numTdsTypes The number of types in the input tds file.
/// @param tdsTableSz The size of the type table in the input.
/// @param unsigned int The position of the sstGlobalTypes.
/// @param dbgFile The output dbg file.
/// @param[out] numDbgTypes The number of types in the output dbg file.
///
/// @throw runtime_error In case of errors.
///
/// @todo This part of the implementation is incomplete.
void convertTypes(const Converter& conv,
FileWrapper& tdsFile,
DWORD numTdsTypes,
DWORD tdsTableSz,
unsigned int subSecPos,
FileWrapper& dbgFile,
DWORD& numDbgTypes) throw(std::runtime_error)
{
// Establish a reader for the TDS type records.
RecordReader recRdr(tdsFile,tdsTableSz);
// Remember where we start writing type record offsets to.
unsigned int frstDbgTrOffsPos = dbgFile.tell();
unsigned int dbgTrOffsPos = frstDbgTrOffsPos;
// Compute the position of the first type record in the DBG file.
// For TDS files, type record offsets are relative to the start of the
// sstGlobalTypes subsection.
// For DBG NB09 files, type record offsets are relative to the location of
// the first type record.
unsigned int frstDbgTrPos = dbgTrOffsPos + sizeof(DWORD)*numTdsTypes;
// Position of the current type record
unsigned int nextDbgTrPos = frstDbgTrPos;
while(numTdsTypes && !recRdr.eor()) {
// Read the offset of the type record in the TDS file..
DWORD tdsTrOffs;
recRdr.read(tdsTrOffs);
// Remember where to read the next type record offset from.
unsigned int nextTdsTrOffsPos = recRdr.tell();
// Move to the position of the type record in the TDS file.
recRdr.seek(subSecPos+tdsTrOffs);
// Read a type record header (length field) from the TDS file
CVtrHeader tdsRecHdr;
recRdr.read(tdsRecHdr);
// Establish a writer for the header (length field) of the DBG type
// record.
CVtrHeader dbgRecHdr;
HeaderWithLen<CVtrHeader,WORD> recWrtr(dbgRecHdr,dbgRecHdr.len);
// Establish a reader for the TDS type strings
RecordReader tsRdr(tdsFile,tdsRecHdr.len);
// Establish a writer for the DBG type strings
RecordWriter tsWrtr;
// Loop over all type strings in this type record
bool nextTypeString = false;
while(!nextTypeString && !tsRdr.eor()) {
// Remember the position of the type string in the TDS file
unsigned int tsPos = tsRdr.tell();
// Read the leaf identifier of the TDS type string
TDSTypeString tdsTs;
tsRdr.read(tdsTs.leaf);
// Prepare the DBG type string
CVTypeString dbgTs;
dbgTs.leaf = tdsTs.leaf; // Set identical for a start
switch(tdsTs.leaf) {
case LF_MODIFIER: {
tsRdr.read(tdsTs.modifier);
dbgTs.modifier.attribute = tdsTs.modifier.attribute;
dbgTs.modifier.typeIdx = conv.typIdx(tdsTs.modifier.typeIdx);
tsWrtr.write(dbgTs.leaf);
tsWrtr.write(dbgTs.modifier);
break;
}
case LF_POINTER: {
tsRdr.read(tdsTs.pointer);
unsigned char ptrType = (tdsTs.pointer.attribute & 0x1f);
unsigned char ptrMode = (tdsTs.pointer.attribute & 0xE0) >> 5;
if(6 == ptrType) {
mylog()(1)
<< "Unhandled pointer type " << std::hex << ((int) ptrType)
<< " at " << tsPos << std::endl;
nextTypeString = true;
} else {
// Convert the fixed size part
dbgTs.pointer.attribute = tdsTs.pointer.attribute;
dbgTs.pointer.typeIdx = conv.typIdx(tdsTs.pointer.typeIdx);
tsWrtr.write(dbgTs.leaf);
tsWrtr.write(dbgTs.pointer);
// Convert the variant part
if(1 < ptrMode) {
/// @todo for pointer to data/method member, TDS
/// and DBG seem to interchange the position of
/// the class and format fields. Verify this!
WORD format;
DWORD classIdx;
tsRdr.read(format);
tsRdr.read(classIdx);
WORD dbgClassIdx = conv.typIdx(classIdx);
tsWrtr.write(dbgClassIdx);
tsWrtr.write(format);
// Number of bytes to copy depends on format.
unsigned bytesToCopy = 0;
switch(format) {
case 0:
case 1:
case 5:
bytesToCopy = 2;
break;
case 3:
case 6:
case 8:
case 11:
bytesToCopy = 4;
break;
case 2:
case 4:
case 9:
bytesToCopy = 6;
break;
case 7:
case 12:
bytesToCopy = 8;
break;
case 10:
bytesToCopy = 10;
break;
case 13:
bytesToCopy = 16;
break;
default:
mylog()(1)
<< "Unhandled variant format in LF_POINTER at "
<< std::hex << tsPos << std::endl;
break;
}
copyFileContent(tsWrtr,tsRdr,bytesToCopy);
} else if(3 == ptrType || 5 == ptrType) {
/// @todo Test this. Not sure it's correct.
WORD seg;
tsRdr.read(seg);
tsWrtr.write(seg);
} else if(8 == ptrType) {
/// @todo Test this. Not sure it's correct.
mylog()(1)
<< "Warning at " << std::hex << tsPos
<< ": Current conversion of LF_POINTER variant part is"
" dubious when pointer is based on type" << std::endl;
// Copy type index
DWORD typeIdx;
tsRdr.read(typeIdx);
unsigned short dbgTypeIdx = conv.typIdx(typeIdx);
tsWrtr.write(dbgTypeIdx);
// Copy length prefixed name
BYTE l;
tsRdr.read(l);
tsWrtr.write(l);
copyFileContent(tsWrtr,tsRdr,l);
}
}
break;
}
case LF_ARRAY: {
tsRdr.read(tdsTs.array);
dbgTs.array.elemTypeIdx = conv.typIdx(tdsTs.array.elemTypeIdx);
dbgTs.array.idxTypeIdx = conv.typIdx(tdsTs.array.idxTypeIdx);
tsWrtr.write(dbgTs.leaf);
tsWrtr.write(dbgTs.array);
// Convert the numerical leaf representing the length of the array
unsigned int numLeafPos = tsRdr.tell();
WORD numLeafId;
tsRdr.read(numLeafId);
convertNumericLeaf(numLeafId,numLeafPos,tsRdr,tsWrtr);
tsWrtr.write1bLpfName(conv.getName(tdsTs.array.nameIdx));
break;
}
case LF_CLASS: // Intentional fall-through
case LF_STRUCTURE: {
tsRdr.read(tdsTs.cls);
dbgTs.cls.count = tdsTs.cls.count;
dbgTs.cls.fieldTypeIdx = conv.typIdx(tdsTs.cls.fieldTypeIdx);
dbgTs.cls.property = tdsTs.cls.property;
// Set bit 8 of the property field according to whether or
// not the declaration of this class or structure is
// contained within another.
if(tdsTs.cls.cClass) {
dbgTs.cls.property |= 0x80;
} else {
dbgTs.cls.property &= 0xff7f;
}
dbgTs.cls.dList = conv.typIdx(tdsTs.cls.dList);
dbgTs.cls.vShape = conv.typIdx(tdsTs.cls.vShape);
tsWrtr.write(dbgTs.leaf);
tsWrtr.write(dbgTs.procedure);
copyNumericLeaf(tsRdr,tsWrtr);
tsWrtr.write1bLpfName(conv.getName(tdsTs.cls.nameIdx));
break;
}
case LF_PROCEDURE: {
tsRdr.read(tdsTs.procedure);
dbgTs.procedure.rvTypeIdx =
conv.typIdx(tdsTs.procedure.rvTypeIdx);
dbgTs.procedure.callConv = tdsTs.procedure.callConv;
dbgTs.procedure.reserved = 0;
dbgTs.procedure.parms = tdsTs.procedure.parms;
dbgTs.procedure.argList = tdsTs.procedure.argList;
tsWrtr.write(dbgTs.leaf);
tsWrtr.write(dbgTs.procedure);
break;
}
case LF_MFUNCTION: {
tsRdr.read(tdsTs.mFunction);
dbgTs.mFunction.rvTypeIdx =
conv.typIdx(tdsTs.mFunction.rvTypeIdx);
dbgTs.mFunction.classIdx =
conv.typIdx(tdsTs.mFunction.classIdx);
dbgTs.mFunction.thisIdx =
conv.typIdx(tdsTs.mFunction.thisIdx);
dbgTs.mFunction.callConv = tdsTs.mFunction.callConv;
dbgTs.mFunction.reserved = 0;
dbgTs.mFunction.parms = tdsTs.mFunction.parms;
dbgTs.mFunction.argList =
conv.typIdx(tdsTs.mFunction.argList);
dbgTs.mFunction.thisAdjust =
tdsTs.mFunction.thisAdjust;
break;
}
case LF_NOTTRANS: {
tsWrtr.write(dbgTs.leaf);
break;
}
case LF_PAD0: // Intentional fall-throughs
case LF_PAD1:
case LF_PAD2:
case LF_PAD3:
case LF_PAD4:
case LF_PAD5:
case LF_PAD6:
case LF_PAD7:
case LF_PAD8:
case LF_PAD9:
case LF_PAD10:
case LF_PAD11:
case LF_PAD12:
case LF_PAD13:
case LF_PAD14:
case LF_PAD15:
// Skip all these --- we'll do our own alignment.
break;
case LF_ARGLIST: {
// Copy the number of arguments in the list.
WORD argCount;
tsRdr.read(argCount);
tsWrtr.write(argCount);
// In TDS files: argCount 4-byte type indices.
// In DBG files: argCount 2-byte type indices.
while(!tsRdr.eor() && argCount) {
DWORD typeIdx;
tsRdr.read(typeIdx);
WORD dbgTypeIdx = conv.typIdx(typeIdx);
tsWrtr.write(dbgTypeIdx);
--argCount;
}
break;
}
case LF_MLIST: {
tsWrtr.write(dbgTs.leaf);
TDStsMListElem tdsElem;
CVtsMListElem dbgElem;
while(!tsRdr.eor()) {
tsRdr.read(tdsElem);
dbgElem.attribute = tdsElem.attribute;
dbgElem.typeIdx = conv.typIdx(tdsElem.typeIdx);
tsWrtr.write(dbgElem);
unsigned int mProp = dbgElem.attribute & MEMBATTR_PROP_MASK;
// The 4-byte vtaboffset is only present when the method
// property is introducing virtual.
if(MEMBATTR_PROP_INTVIRT == mProp ||
MEMBATTR_PROP_INTPURE == mProp) {
unsigned int vTabOffs;
tsRdr.read(vTabOffs);
tsWrtr.write(vTabOffs);
}
}
break;
}
case LF_CHAR:
case LF_SHORT: // Intentional fall-through
case LF_USHORT:
case LF_LONG: // Intentional fall-through
case LF_ULONG:
case LF_REAL32:
case LF_COMPLEX32:
case LF_REAL48:
case LF_REAL64: // Intentional fall-through
case LF_QUADWORD:
case LF_UQUADWORD:
case LF_COMPLEX64:
case LF_REAL80:
case LF_COMPLEX80:
case LF_REAL128:
case LF_COMPLEX128:
case LF_VARSTRING:
convertNumericLeaf(tdsTs.leaf,tsPos,tsRdr,tsWrtr);
break;
default:
// Set all unhandled types as NOTTRANS
dbgTs.leaf = LF_NOTTRANS;
tsWrtr.write(dbgTs.leaf);
// Skip to the next type record as we cannot safely
// continue with this type record.
nextTypeString = true;
mylog()(1)
<< "Error: Unhandled type record with leaf id "
<< std::hex << tdsTs.leaf << " at " << tsPos << std::endl;
}
// Ensure that the next type string is properly aligned on a
// dword boundary
unsigned int bytesToWrite = recWrtr.length(tsWrtr);
unsigned int malgnmnt = bytesToWrite & 0x3;
if(malgnmnt) {
unsigned char padBytes = 3 - (unsigned char) malgnmnt;
// Determine which LF_PADxx identifier to write out.
unsigned char alignLeaf = 0xEF + padBytes;
tsWrtr.write(alignLeaf);
// Append padding bytes.
alignLeaf = 0;
tsWrtr.write(alignLeaf,padBytes);
}
}
// Move to where the type record is to be written
dbgFile.seek(nextDbgTrPos);
// Ensure that the record does not cross a page boundary.
pageAlign(dbgFile,recWrtr.length(tsWrtr),0xBFFF);
// Remember the write position after alignment relative to the
// first type record.
unsigned int dbgTrPos = (dbgFile.tell() - frstDbgTrPos);
// Update the record header length field and output the header and
// the data registered with the record writer.
recWrtr.commit(dbgFile,tsWrtr);
++numDbgTypes;
// Remember where to write the next type record to.
nextDbgTrPos = dbgFile.tell();
// Move to the position of the type record offset in the DBG file.
dbgFile.seek(dbgTrOffsPos);
// Write the offset of the type record to the DBG file.
dbgFile.write(dbgTrPos);
// Remember where to write the next type recor offset to the DBG file.
dbgTrOffsPos = dbgFile.tell();
// Move to read the next type record offset
recRdr.seek(nextTdsTrOffsPos);
--numTdsTypes;
}
}
/// @brief Creates the sstGlobalPub subsection.
///
/// @param conv Converter to use for various conversion tasks.
/// @param dbgFile The DBG file to write to.
/// @param[out] symTablSz Symbol table size.
/// @param[out] symHshTableSz Symbol hash table size.
/// @param[out] addrHshTableSz Address hash table size.
/// @param syms The public symbols.
///
/// @throw runtime_error In case of errors.
void createGlobalPub(const Converter& conv,
FileWrapper& dbgFile,
DWORD& symTableSz,
DWORD& symHshTableSz,
DWORD& addrHshTableSz,
SymRecMap& syms) throw(std::runtime_error)
{
// Create the internal name hash table
NameHshTbl nameHshTbl(NUM_HASH_BUCKETS);
// Remember the DBG file offset of the first symbol
unsigned int frstSymOffs = dbgFile.tell();
// All symbol records in sstGlobalPub have kind PUB32.
CVsymRecHdr recHdr;
recHdr.symKind = CVskPublic;
SymRecMap::iterator segIt = syms.begin();
// Loop over all segments
while(syms.end() != segIt) {
SegmSymMap::iterator symIt = segIt->second.begin();
// Loop over all symbols within that segment
while(segIt->second.end() != symIt) {
// Reserve space for a DBG symbol record header to be
// written out later.
HeaderWithLen<CVsymRecHdr,WORD> dbgRecHdr(recHdr,recHdr.len);
// Create a record writer to keep track of the number of
// payload bytes written.
RecordWriter wrtr;
// Create the corresponding payload header for the DBG file
CVsrGlobalDataPayloadHdr dbgPlHdr =
{symIt->first,
segIt->first,
conv.typIdx(symIt->second.typeIdx)};
// Write the payload header to the DBG file
wrtr.write(dbgPlHdr);
// Look up the symbol name
const std::string& symName = conv.getName(symIt->second.nameIdx);
// Append the length-prefixed symbol name to the payload header
wrtr.write1bLpfName(symName);
// Ensure that the next record is properly aligned on a
// dword boundary
zeroPad(wrtr,dbgRecHdr.length(wrtr),sizeof(int)-1);
// Ensure that the record does not cross a page boundary.
pageAlignSymbol(dbgFile,dbgRecHdr.length(wrtr),0xFFF);
// Store the relative offset of the current symbol record.
// We will need this later on for the address hash table.
// Note that this field may have been set elsewhere (e.g.
// in convertSymbolSubsection) to point to the position
// where the original symbol was found. We don't need that
// here, however.
symIt->second.symRefOffs = dbgFile.tell() - frstSymOffs;
// Write the symbol record header with the correct length field
dbgRecHdr.commit(dbgFile,wrtr);
// Update the internal name hash table
nameHshTbl.append(symName,symIt->second.symRefOffs);
// Next symbol
++symIt;
}
// Next segment
++segIt;
}
// Symbol table must end with a closing S_ALIGN
appendClosingAlign(dbgFile);
// Compute the size of the symbol table
symTableSz = dbgFile.tell()-frstSymOffs;
// Proceed to write the symbol hash table
nameHshTbl.write(dbgFile,symHshTableSz);
// Proceed to write the address hash table
createAddrHashTable(dbgFile,addrHshTableSz,syms);
}
/// @brief Creates the sstStaticSym or similar subsection.
///
/// @param conv Converter to use for various conversion tasks.
/// @param dbgFile The DBG file to write to.
/// @param[out] symTableSz Symbol table size.
/// @param[out] symHshTableSz Symbol hash table size.
/// @param[out] addrHshTableSz Address hash table size.
/// @param syms The symbols to write to the subsection.
/// @param algnmnt The size of pages. No symbol must cross a page.
/// 0 if none.
///
/// @throw runtime_error In case of errors.
void createSymbolsSubsection(const Converter& conv,
FileWrapper& dbgFile,
DWORD& symTableSz,
DWORD& symHshTableSz,
DWORD& addrHshTableSz,
SymRecMap& syms,
unsigned int algnmnt) throw(std::runtime_error)
{
// Create the internal name hash table
NameHshTbl nameHshTbl(NUM_HASH_BUCKETS);
// Remember where we start writing to
unsigned int strtPos = dbgFile.tell();
CVsymRecHdr recHdr;
SymRecMap::iterator segIt = syms.begin();
// Loop over all segments
while(syms.end() != segIt) {
// Loop over all symbols within this segment
SegmSymMap::iterator symIt = segIt->second.begin();
while(segIt->second.end() != symIt) {
// Set the symbol kind in the record header
recHdr.symKind = symIt->second.symKind;
// Reserve space for a DBG symbol record header to be
// written out later.
HeaderWithLen<CVsymRecHdr,WORD> dbgRecHdr(recHdr,recHdr.len);
// Create a record writer to keep track of the number of
// payload bytes written.
RecordWriter wrtr;
// Create the corresponding payload header for the DBG file
CVsrDataRefPayload dbgPl =
{0,symIt->second.offsSymRec,symIt->second.moduleIdx};
// Write the payload header to the DBG file
wrtr.write(dbgPl);
// Ensure that the next record is properly aligned on a
// dword boundary
zeroPad(wrtr,dbgRecHdr.length(wrtr),sizeof(int)-1);
// Ensure that the record does not cross a page boundary.
pageAlignSymbol(dbgFile,dbgRecHdr.length(wrtr),algnmnt);
// Store the relative offset of the current symbol record.
// We will need this later on for the address hash table.
// Note that this field may have been set elsewhere (e.g.
// in convertSymbolSubsection) to point to the position
// where the original symbol was found. We don't need that
// here, however.
symIt->second.symRefOffs = dbgFile.tell() - strtPos;
// Write the symbol record header with the correct length field
dbgRecHdr.commit(dbgFile,wrtr);
// Look up the symbol name
const std::string& symName = conv.getName(symIt->second.nameIdx);
// Update the internal name hash table
nameHshTbl.append(symName,symIt->second.symRefOffs);
// Next symbol
++symIt;
}
// Next segment
++segIt;
}
// Some symbol tables must end with a closing S_ALIGN - just do it for all.
appendClosingAlign(dbgFile);
// Compute the size of the generated symbol table
symTableSz = dbgFile.tell() - strtPos;
// Proceed to write the symbol hash table
nameHshTbl.write(dbgFile,symHshTableSz);
// Proceed to write the address hash table
createAddrHashTable(dbgFile,addrHshTableSz,syms);
}
/// @brief TDS to Code View conversion algorithm
///
/// @param conv The converter context to use.
/// @param tdsCvHdr The header already read from the TDS file.
/// @param tdsFile The TDS file to read from. The current read position
/// is assumed to be right after the CVHdr already read in.
/// @param segMap Input for the sstSegMap subsection.
/// @param dbgFile The DBG file to read from.
/// @param lfaBase Location of the Code View header in the output file.
///
/// @throw runtime_error In case of errors.
void convertTdsToCodeView(Converter& conv,
CVHdr& tdsCvHdr,
FileWrapper& tdsFile,
const std::vector<CVssSegMapSegDesc>& segMap,
FileWrapper& dbgFile,
unsigned int lfaBase)
throw(std::runtime_error)
{
// Move to the location of the Subsection directory in the tds file.
tdsFile.seek(tdsCvHdr.lfoDir);
// Read the Subsection directory header from the TDS file.
CVDirHdr tdsCvDirHdr;
tdsFile.read(tdsCvDirHdr);
// Read select subsection directory entries from the TDS files
typedef std::vector<CVDirEntry> CVDirEntryList;
CVDirEntryList tdsSubSecDirEntries;
mylog()(2) << "TDS subsection directory header has "
<< tdsCvDirHdr.cDir << " entries." << std::endl;
for(DWORD i=0 ; i<tdsCvDirHdr.cDir ; ++i) {
CVDirEntry dirEntry;
tdsFile.seek(tdsCvHdr.lfoDir+sizeof(CVDirHdr)+i*(sizeof dirEntry));
tdsFile.read(dirEntry);
// TDS files appear to be using module index 0 in places where
// DBG files are apparently using 0xFFFF.
if(0 == dirEntry.iMod) {
dirEntry.iMod = 0xFFFF;
}
switch(dirEntry.subsection) {
case CVsstIdxModule:
tdsSubSecDirEntries.push_back(dirEntry);
break;
case CVsstIdxSrcModule:
tdsSubSecDirEntries.push_back(dirEntry);
break;
case CVsstIdxNames:
conv.loadNameCache(tdsFile,dirEntry.lfo,dirEntry.cb);
break;
case CVsstIdxAlignSym:
tdsSubSecDirEntries.push_back(dirEntry);
break;
case CVsstIdxGlobalSym:
tdsSubSecDirEntries.push_back(dirEntry);
break;
case CVsstIdxGlobalTypes:
tdsSubSecDirEntries.push_back(dirEntry);
break;
default:
mylog()(1)
<< "Warning: Unhandled subsection with tag "
<< std::hex << dirEntry.subsection << std::endl;
break;
}
}
// A place holder for the contents of the sstGlobalPub to be generated
// on the fly.
SymRecMap sstGlobalPubInput;
// A place holder for the contents of the sstStaticSym to be generated
// on the fly.
SymRecMap sstStaticSymInput;
// Module index counter
unsigned short moduleIdx = 0;
// Iterate over the subsections in the TDS file and move them
// to the DBG file.
CVDirEntryList dbgSubSecDirEntries;
CVDirEntryList::const_iterator subSecIt = tdsSubSecDirEntries.begin();
while(tdsSubSecDirEntries.end() != subSecIt) {
CVDirEntry dirEntry = *subSecIt;
// Move to the location of the subsection in the TDS file
tdsFile.seek(dirEntry.lfo);
// Adjust the location of the subsection to fall on a DWORD boundary
unsigned int misalignment = dbgFile.tell() & 0x3;
if(misalignment) {
dbgFile.skip(4-misalignment);
}
// Remember where we start writing to. This is often used as the
// base of all file offsets in the subsection.
long int dbgSubSecBase = dbgFile.tell();
// Set the location of the subsection in the DBG File
dirEntry.lfo = dbgSubSecBase-lfaBase;
switch(dirEntry.subsection) {
case CVsstIdxModule: {
mylog()(2) << "Converting sstModule found at "
<< std::hex << tdsFile.tell()
<< ". Writing at " << dbgFile.tell()
<< " (" << (dbgFile.tell()-lfaBase) << ")" << std::endl;
// Get the TDSsstModuleHdr from the TDS file
TDSssModuleHdr tdsHdr;
tdsFile.read(tdsHdr);
// Write the CVsstModuleHdr
dbgFile.write(*((CVssModuleHdr*)&tdsHdr));
// Copy the segment infos from the TDS file to the DBG file
CVssModuleSegInfo segInfo;
for(WORD i=0 ; i<tdsHdr.cSeg ; ++i) {
tdsFile.read(segInfo);
dbgFile.write(segInfo);
}
// Write the rest of the CVssModule
dbgFile.write1bLpfName(conv.getName(tdsHdr.nameIdx));
// Compute the size of the subsection
dirEntry.cb = dbgFile.tell() - dbgSubSecBase;
// Update the directory
dbgSubSecDirEntries.push_back(dirEntry);
break;
}
case CVsstIdxSrcModule: {
mylog()(2) << "Converting sstSrcModule found at "
<< std::hex << tdsFile.tell()
<< ". Writing at " << dbgFile.tell()
<< " (" << (dbgFile.tell()-lfaBase) << ")" << std::endl;
// Copy the header for the fixed part of TDSssSrcModule::hdr
// from the TDS file to the DBG file.
CVssSrcModuleFixedHdr fixedHdr;
tdsFile.read(fixedHdr);
dbgFile.write(fixedHdr);
// Read in TDSssSrcModule::hdr.baseSrcFile from the TDS file.
std::vector<DWORD> tdsBaseSrcFile(fixedHdr.cFile);
tdsFile.read(tdsBaseSrcFile[0],tdsBaseSrcFile.size());
// Skip writing CVssSrcModule::hdr.baseSrcFile to the DBG
// file for now. We will compute and write it later on.
long int fileOffsOfbaseSrcFile = dbgFile.tell();
dbgFile.skip(sizeof(DWORD)*fixedHdr.cFile);
// Prepare the vector that will contain the data for
// CVssSrcModule::hdr.baseSrcFile
std::vector<DWORD> dbgBaseSrcFile;
// Copy the TDSssSrcModule::hdr.startEnd to
// CVssSrcModule::hdr.startEnd
copyFileContent(dbgFile,tdsFile,2*sizeof(DWORD)*fixedHdr.cSeg);
// Copy the TDSssSrcModule::hdr.seg to CVssSrcModule::hdr.seg
copyFileContent(dbgFile,tdsFile,sizeof(WORD)*fixedHdr.cSeg);
// Copy the TDSssSrcModule::files to CVssSrcModule::files
std::vector<DWORD>::const_iterator srcFileIt = tdsBaseSrcFile.begin();
while(tdsBaseSrcFile.end() != srcFileIt) {
dbgBaseSrcFile.push_back(dbgFile.tell()-dbgSubSecBase);
TDSssSrcModuleFileHdr tdsFileHdr;
tdsFile.seek(subSecIt->lfo+*srcFileIt);
tdsFile.read(tdsFileHdr);
CVssSrcModuleFileHdr dbgFileHdr = { tdsFileHdr.cSeg, 0 };
dbgFile.write(dbgFileHdr);
// Read in TDSssSrcModuleFile::baseSrcLn from the TDS file.
std::vector<DWORD> tdsBaseSrcLn(dbgFileHdr.cSeg);
tdsFile.read(tdsBaseSrcLn[0],tdsBaseSrcLn.size());
// Skip writing CVssSrcModuleFile::baseSrcLn to the DBG
// file as we will write it later on when the offsets are known.
long int fileOffsOfbaseSrcLen = dbgFile.tell();
dbgFile.skip(sizeof(DWORD)*tdsFileHdr.cSeg);
// Prepare a vector for the contents of baseSrcLn
std::vector<DWORD> dbgBaseSrcLen;
// Copy TDSssSrcModuleFile::startEnd to CVssSrcModuleFile::startEnd
copyFileContent(dbgFile,tdsFile,2*sizeof(DWORD)*tdsFileHdr.cSeg);
// Write out CVssSrcModuleFile::nameLen and CVsrcModuleFile::name
dbgFile.write1bLpfName(conv.getName(tdsFileHdr.nameIdx));
// Copy the CVssSrcModuleSeg's
std::vector<DWORD>::const_iterator segIt = tdsBaseSrcLn.begin();
while(tdsBaseSrcLn.end() != segIt) {
dbgBaseSrcLen.push_back(dbgFile.tell()-dbgSubSecBase);
// Copy CVssSrcModuleSeg::hdr
CVssSrcModuleSegHdr segHdr;
tdsFile.seek(subSecIt->lfo+*segIt);
tdsFile.read(segHdr);
dbgFile.write(segHdr);
// Copy CVssSrcModuleSeg::offset and
// CVssSrcModuleSeg::linenumber
copyFileContent(dbgFile,tdsFile,(sizeof(WORD)+sizeof(DWORD))*segHdr.cPair);
++segIt;
}
// Remember where we are
long int moduleRecEnd = dbgFile.tell();
// Write the correct offsets into CVssSrcModuleFile::baseSrcLen
dbgFile.seek(fileOffsOfbaseSrcLen);
dbgFile.write(dbgBaseSrcLen[0],dbgBaseSrcLen.size());
// Move back to where we were
dbgFile.seek(moduleRecEnd);
++srcFileIt;
}
// Remember where we are
long int moduleEnd = dbgFile.tell();
// Write the correct offsets into baseSourceFile[cFile]
dbgFile.seek(fileOffsOfbaseSrcFile);
dbgFile.write(dbgBaseSrcFile[0],dbgBaseSrcFile.size());
// Move back to where we were
dbgFile.seek(moduleEnd);
dirEntry.cb = dbgFile.tell() - dbgSubSecBase;
dbgSubSecDirEntries.push_back(dirEntry);
break;
}
case CVsstIdxGlobalSym: {
mylog()(2) << "Converting sstGlobalSym found at "
<< std::hex << tdsFile.tell()
<< ". Writing at " << dbgFile.tell()
<< " (" << (dbgFile.tell()-lfaBase) << ")" << std::endl;
// Read in the header of the subsection
TDSssGlobalSymHdr tdsSymHdr;
tdsFile.read(tdsSymHdr);
// Prepare the corresponding DBG header
CVssGlobalSymHdr dbgSymHdr;
dbgSymHdr.symhash = 0x0A; // Symbol hash table provided
dbgSymHdr.addrhash = 0x0C; // Address hash table provided
PpRecordWriter<CVssGlobalSymHdr> hdrWrtr(dbgFile,dbgSymHdr);
unsigned int tdsTableSz = dirEntry.cb - (sizeof tdsSymHdr);
// Set up output argument to contain converted symbols
SymRecMap syms;
// Set up the internal name hash table
NameHshTbl nameHshTbl(NUM_HASH_BUCKETS);
// Convert the symbols
convertSymbolSubsection(conv,0,tdsFile,tdsTableSz,
dbgFile,dbgSymHdr.cbSymbol,syms,nameHshTbl,
sstGlobalPubInput,sstStaticSymInput);
// Proceed to write the symbol hash table
nameHshTbl.write(dbgFile,dbgSymHdr.cbSymHash);
// Proceed to write the address hash table
createAddrHashTable(dbgFile,dbgSymHdr.cbAddrHash,syms);
// Write the header with the update fields
hdrWrtr.commit();
dirEntry.cb = dbgSymHdr.cbSymbol + dbgSymHdr.cbSymHash
+ dbgSymHdr.cbAddrHash + hdrWrtr.length();
// Postpone appending the directory entry as we need to
// ensure that the directory entry for the sstGlobalPub
// preceeds the directory entry for the sstGlobalSym.
// Align next subsection on dword boundary
zeroPad(dbgFile,dbgFile.tell(),sizeof(int)-1);
// Proceed to generate the sstGlobalPub
mylog()(2) << "Generating sstGlobalPub at "
<< std::hex << dbgFile.tell()
<< " (" << (dbgFile.tell()-lfaBase) << ")" << std::endl;
CVDirEntry glPubDirEntry;
glPubDirEntry.subsection = CVsstIdxGlobalPub;
glPubDirEntry.iMod = 0xFFFF;
glPubDirEntry.lfo = dbgFile.tell()-lfaBase;
// Prepare the DBG header for this subsection
CVssGlobalSymHdr glPubHdr;
glPubHdr.symhash = 0x0A; // Symbol hash table provided
glPubHdr.addrhash = 0x0C; // Address hash table provided
PpRecordWriter<CVssGlobalSymHdr> hdrWrtr2(dbgFile,glPubHdr);
createGlobalPub(conv,dbgFile,glPubHdr.cbSymbol,
glPubHdr.cbSymHash,glPubHdr.cbAddrHash,
sstGlobalPubInput);
// Write the header with the update fields
hdrWrtr2.commit();
glPubDirEntry.cb = hdrWrtr2.length() +
glPubHdr.cbSymbol + glPubHdr.cbSymHash + glPubHdr.cbAddrHash;
// Update the directory
dbgSubSecDirEntries.push_back(glPubDirEntry);
dbgSubSecDirEntries.push_back(dirEntry);
break;
}
case CVsstIdxGlobalTypes: {
// Skip generation of this section if not requested.
if(!conv.doConvertTypes()) {
mylog()(2) << "Skipping contents of";
} else {
mylog()(2) << "Converting";
}
mylog()(2) << " sstGlobalTypes found at "
<< std::hex << tdsFile.tell()
<< ". Writing at " << dbgFile.tell()
<< " (" << (dbgFile.tell() - lfaBase) << ")"
<< std::endl;
// Remember the location of the subsection
unsigned int ssPos = tdsFile.tell(); (void) ssPos;
CVssGlobalTypesHdr tdsTypHdr;
tdsFile.read(tdsTypHdr);
// Prepare the corresponding DBG header
CVssGlobalTypesHdr dbgTypHdr = { 0x1000000, 0 };
PpRecordWriter<CVssGlobalTypesHdr> hdrWrtr(dbgFile,dbgTypHdr);
unsigned int tdsTableSz = dirEntry.cb - (sizeof tdsTypHdr);
(void) tdsTableSz;
if(conv.doConvertTypes()) {
// Convert type table and increment dirEntry.cb correspondingly
convertTypes(conv,tdsFile,tdsTypHdr.cTypes,tdsTableSz,ssPos,
dbgFile,dbgTypHdr.cTypes);
}
// Write the header with the updated fields
hdrWrtr.commit();
// Compute the size of the subsection
dirEntry.cb = dbgFile.tell() - dbgSubSecBase;
// Update the directory
dbgSubSecDirEntries.push_back(dirEntry);
break;
}
case CVsstIdxAlignSym: {
mylog()(2) << "Converting sstAlignSym found at "
<< std::hex << tdsFile.tell()
<< ". Writing at " << dbgFile.tell()
<< " (" << (dbgFile.tell()-lfaBase) << ")" << std::endl;
CVssAlignSymHdr subSecHdr;
tdsFile.read(subSecHdr);
if(subSecHdr.signature != 1) {
mylog()(1)
<< "Warning: Unidentified sstAlignSym signature!"
<< std::endl;
}
// Write the corresponding DBG header
dbgFile.write(subSecHdr);
// Convert the symbols
DWORD symTableSz = 0;
unsigned int tdsTableSz = dirEntry.cb - (sizeof subSecHdr);
// Set up a dummy internal name hash table
NameHshTbl nameHshTbl(NUM_HASH_BUCKETS);
// Set up output argument to contain converted symbols
SymRecMap syms;
// Convert the symbols
convertSymbolSubsection(conv,++moduleIdx,
tdsFile,tdsTableSz,
dbgFile,
symTableSz,
syms,
nameHshTbl,
sstGlobalPubInput,
sstStaticSymInput);
// Compute the size of the subsection
dirEntry.cb = dbgFile.tell() - dbgSubSecBase;
// Update the directory
dbgSubSecDirEntries.push_back(dirEntry);
break;
}
default:
mylog()(1) << "Unidentified subsection found at "
<< std::hex << tdsFile.tell() << std::endl;
break;
}
++subSecIt;
}
{
// Proceed to generate the sstStaticSym
// Align subsection on dword boundary
zeroPad(dbgFile,dbgFile.tell(),sizeof(int)-1);
mylog()(2) << "Generating sstStaticSym at "
<< std::hex << dbgFile.tell()
<< " (" << (dbgFile.tell()-lfaBase) << ")" << std::endl;
CVDirEntry dirEntry;
dirEntry.subsection = CVsstIdxStaticSym;
dirEntry.iMod = 0xFFFF;
dirEntry.lfo = dbgFile.tell()-lfaBase;
// Prepare the DBG header for this subsection
CVssGlobalSymHdr dbgHdr;
dbgHdr.symhash = 0x0A; // Symbol hash table provided
dbgHdr.addrhash = 0x0C; // Address hash table provided
PpRecordWriter<CVssGlobalSymHdr> hdrWrtr(dbgFile,dbgHdr);
createSymbolsSubsection(conv,dbgFile,dbgHdr.cbSymbol,dbgHdr.cbSymHash,
dbgHdr.cbAddrHash,sstStaticSymInput,0);
// Write the header with the update fields
hdrWrtr.commit();
// Compute the size of the subsection
dirEntry.cb = dbgHdr.cbSymbol + dbgHdr.cbSymHash + dbgHdr.cbAddrHash
+ hdrWrtr.length();
// Push a matching entry to the tail of the directory
dbgSubSecDirEntries.push_back(dirEntry);
}
// TDS files do not contain a sstSegMap. Make sure one is created in the
// DBG file.
{
// Align subsection on dword boundary
zeroPad(dbgFile,dbgFile.tell(),sizeof(int)-1);
mylog()(2) << "Generating sstSegMap at "
<< std::hex << dbgFile.tell()
<< " (" << (dbgFile.tell()-lfaBase) << ")" << std::endl;
CVDirEntry dirEntry;
dirEntry.subsection = CVsstIdxSegMap;
dirEntry.iMod = 0xFFFF;
dirEntry.lfo = dbgFile.tell()-lfaBase;
RecordWriter wrtr;
CVssSegMapHdr segMapHdr = {(WORD) segMap.size(), (WORD) segMap.size()};
wrtr.write(segMapHdr);
std::vector<CVssSegMapSegDesc>::const_iterator segIt = segMap.begin();
while(segMap.end() != segIt) {
wrtr.write(*segIt);
++segIt;
}
// Write the contents
wrtr.commit(dbgFile);
// Compute the size of the subsection
dirEntry.cb = wrtr.length();
// Push a matching entry to the tail of the directory
dbgSubSecDirEntries.push_back(dirEntry);
}
// Prepare the CodeView header.
CVHdr dbgCvHdr = {{'N','B','0','9'},sizeof(CVHdr)}; // Due to bounds checking with zero terminator this must be assigned as array of 4 chars rather than string
// Store the offset (relative to lfaBase) of the subsection
// directory header in the CodeView header.
dbgCvHdr.lfoDir = dbgFile.tell() - lfaBase;
// Create the data for the subsection directory header in the DBG file.
CVDirHdr dbgCvDirHdr = tdsCvDirHdr;
dbgCvDirHdr.cDir = dbgSubSecDirEntries.size();
// Write the subsection directory header to the DBG file.
dbgFile.write(dbgCvDirHdr);
// Write out all subsection directory headers for the DBG file.
for(DWORD i = 0; i<dbgSubSecDirEntries.size() ; ++i) {
dbgFile.write(dbgSubSecDirEntries[i]);
}
// Prepare tail signature
CVHdr dbgCvTail = {{'N','B','0','9'},sizeof(CVHdr)}; // Due to bounds checking with zero terminator this must be assigned as array of 4 chars rather than string
// Last 4 bytes (dbgCvTail.lfoDir) must contain
// lfoBase = length_of_file - lfaBase
dbgCvTail.lfoDir = dbgFile.tell() + sizeof(CVHdr) - lfaBase;
// Write out the trailing NB09 signature
mylog()(2) << "Writing tail signature at " << std::hex
<< dbgFile.tell() << std::endl;
dbgFile.write(dbgCvTail);
// Move to the position of the to-be-written CodeView header
dbgFile.seek(lfaBase);
mylog()(2) << "Writing head signature at " << std::hex
<< dbgFile.tell() << std::endl;
// Write out the CodeView header
dbgFile.write(dbgCvHdr);
}
/// @brief TDS to DBG conversion algorithm
///
/// @param ef EXE file.
/// @param tf TDS file.
/// @param df DBG file.
/// @param doConvTypes Should types be converted?
/// @param staticsAsPublics Treat statics as publics?
/// @param bareCodeView Write bare code view (no headers)?
///
/// @todo doConvTypes == true is incomplete.
///
/// @throw runtime_error In case of errors.
void genDbgFromExeNTds(FILE* ef,
FILE* tf,
FILE* df,
bool doConvTypes,
bool staticsAsPublics,
bool bareCodeView) throw(std::runtime_error)
{
// Create wrappers for the TDS and DBG files.
FileWrapper tdsFile(tf);
FileWrapper dbgFile(df);
// Converter to use for various conversion tasks
Converter conv(doConvTypes,staticsAsPublics);
// Read the DOS header of the executable PE file.
IMAGE_DOS_HEADER dosHdr;
if(1 != fread(&dosHdr,(sizeof dosHdr),1,ef)) {
throw std::runtime_error("Could not read DOS_HEADER from exe file");
}
// Move to the NT headers of the executable
fseek(ef,dosHdr.e_lfanew,SEEK_SET);
// Read the NT headers of the executable
IMAGE_NT_HEADERS ntHdrs;
if(1 != fread(&ntHdrs,(sizeof ntHdrs),1,ef)) {
throw std::runtime_error("Could not read NT_HEADERS from exe file");
}
// Mark the exe file as stripped for debug info
if(0 ==(ntHdrs.FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED)) {
ntHdrs.FileHeader.Characteristics |= IMAGE_FILE_DEBUG_STRIPPED;
fseek(ef,dosHdr.e_lfanew,SEEK_SET);
fwrite(&ntHdrs,(sizeof ntHdrs),1,ef);
fseek(ef,dosHdr.e_lfanew+(sizeof ntHdrs),SEEK_SET);
}
// Bare code view has no PE header
if(!bareCodeView) {
// Prepare the header of the DBG PE file.
IMAGE_SEPARATE_DEBUG_HEADER dbgHdr;
memset(&dbgHdr,0,(sizeof dbgHdr));
// Fill in the remaining parts of the header of the DBG PE file.
dbgHdr.Signature = IMAGE_SEPARATE_DEBUG_SIGNATURE;
dbgHdr.Flags = 0;
dbgHdr.Machine = ntHdrs.FileHeader.Machine;
dbgHdr.Characteristics = ntHdrs.FileHeader.Characteristics;
dbgHdr.TimeDateStamp = ntHdrs.FileHeader.TimeDateStamp;
dbgHdr.CheckSum = ntHdrs.OptionalHeader.CheckSum;
dbgHdr.ImageBase = ntHdrs.OptionalHeader.ImageBase;
dbgHdr.SizeOfImage = ntHdrs.OptionalHeader.SizeOfImage;
dbgHdr.NumberOfSections = ntHdrs.FileHeader.NumberOfSections;
dbgHdr.ExportedNamesSize = 0;
dbgHdr.DebugDirectorySize = sizeof(IMAGE_DEBUG_DIRECTORY);
dbgHdr.SectionAlignment = ntHdrs.OptionalHeader.SectionAlignment;
// Write the header of DBG PE file.
dbgFile.write(dbgHdr);
}
// Copy the Section table (a sequence of IMAGE_SECTION_HEADER), if
// required, and create the contents for the Code View sstSegMap to
// be written later on.
std::vector<CVssSegMapSegDesc> segMap;
for(DWORD i=0 ; i<(DWORD)ntHdrs.FileHeader.NumberOfSections ; ++i) {
IMAGE_SECTION_HEADER secHdr;
if(1 != fread(&secHdr,(sizeof secHdr),1,ef)) {
throw std::runtime_error("Could not read SECTION_HEADER"
" from exe file");
}
if(!bareCodeView) {
dbgFile.write(secHdr);
}
CVssSegMapSegDesc segDesc =
{0,0,0,((WORD) i),0xFFFF,0xFFFF,0,((WORD) secHdr.Misc.VirtualSize)};
segMap.push_back(segDesc);
}
// Note: We do currently not attempt to create the
// IMAGE_DEBUG_TYPE_OMAP_FROM_SRC and IMAGE_DEBUG_TYPE_OMAP_TO_SRC
// subsections.
// Write the IMAGE_DEBUG_DIRECTORY to the DBG PE file.
IMAGE_DEBUG_DIRECTORY dbgDir;
memset(&dbgDir,0,(sizeof dbgDir));
// Skips this if bare Code View output is requested (i.e. not an DBG PE file)
if(!bareCodeView) {
dbgDir.Characteristics = 0;
dbgDir.TimeDateStamp = ntHdrs.FileHeader.TimeDateStamp;
dbgDir.MajorVersion = 0;
dbgDir.MinorVersion = 0;
dbgDir.Type = IMAGE_DEBUG_TYPE_CODEVIEW;
dbgDir.SizeOfData = 0; // Will be filled in later
dbgDir.AddressOfRawData = 0;
dbgDir.PointerToRawData = sizeof(IMAGE_SEPARATE_DEBUG_HEADER)+
(ntHdrs.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER))+
sizeof(IMAGE_DEBUG_DIRECTORY);
dbgFile.write(dbgDir);
}
// Remember where to write the Code View header - this is lfaBase
unsigned int lfaBase = dbgFile.tell();
// Reserve space for the Code View header
dbgFile.skip(sizeof(CVHdr));
// Read the CodeView header of the TDS file - the first record in the file.
CVHdr tdsCvHdr;
tdsFile.seek(0);
tdsFile.read(tdsCvHdr);
mylog()(2) << "TDS header with signature "
<< tdsCvHdr.signature[0]
<< tdsCvHdr.signature[1]
<< tdsCvHdr.signature[2]
<< tdsCvHdr.signature[3] << std::endl;
if('N' == tdsCvHdr.signature[0] &&
'B' == tdsCvHdr.signature[1] &&
'0' == tdsCvHdr.signature[2] &&
'9' == tdsCvHdr.signature[3]) {
// TDS file already contains Code View NB09 - no need for conversion,
// just do a plain copy and update the PE header afterwards.
dbgFile.write(tdsCvHdr);
copyFile(dbgFile,tdsFile);
} else if('F' == tdsCvHdr.signature[0] &&
'B' == tdsCvHdr.signature[1] &&
'0' == tdsCvHdr.signature[2] &&
('9' == tdsCvHdr.signature[3] ||
'A' == tdsCvHdr.signature[3])) {
// Do the conversion and update the PE header afterwards.
convertTdsToCodeView(conv,tdsCvHdr,tdsFile,segMap,dbgFile,lfaBase);
} else {
throw std::runtime_error("Unidentified TDS signature");
}
// Are we generating a PE based DBG file or just a bare Code View file?
if(!bareCodeView) {
// Compute the total amount of Code View data
dbgDir.SizeOfData = dbgFile.tell() - lfaBase;
// Write out the updated dbgDir.SizeOfData
dbgFile.seek(sizeof(IMAGE_SEPARATE_DEBUG_HEADER)+
ntHdrs.FileHeader.NumberOfSections*
sizeof(IMAGE_SECTION_HEADER));
dbgFile.write(dbgDir);
}
}
int tds2dbg(const char* exeFileName,
const char* tdsFileName,
const char* dbgFileName,
unsigned int verbosity,
bool doConvTypes,
bool staticsAsPublics,
bool bareCodeView)
{
// Set the verbosity level.
mylog().setVerbosity(verbosity);
int ret = 1;
FILE *ef = fopen(exeFileName,"rb+");
if(ef) {
FILE *tf = fopen(tdsFileName,"rb");
if(tf) {
FILE* df = fopen(dbgFileName,"wb");
if(df) {
try {
genDbgFromExeNTds(ef,tf,df,doConvTypes,staticsAsPublics,
bareCodeView);
} catch(std::exception& e) {
mylog()(0) << e.what() << std::endl;
}
fclose(df);
ret = 0;
} else {
mylog()(0)
<< "Unable to open " << dbgFileName << " for writing" << std::endl;
}
fclose(tf);
} else {
mylog()(0)
<< "Unable to open " << tdsFileName << " for reading" << std::endl;
}
fclose(ef);
}
return ret;
}
// Local Variables:
// mode:c++
// c-basic-offset: 3
// indent-tabs-mode: nil
// End: