diff --git a/common/windows/delphi/ext/build.sh b/common/windows/delphi/ext/build.sh index 8097e5d1bb..05dffca69d 100755 --- a/common/windows/delphi/ext/build.sh +++ b/common/windows/delphi/ext/build.sh @@ -8,7 +8,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" builder_describe \ "Keyman common third party Delphi components" \ clean configure build test reset \ - :cef4delphi :dcpcrypt :jwa :sentry + :cef4delphi :dcpcrypt :jwa :sentry :tds2dbg builder_parse "$@" builder_run_child_actions clean configure build test diff --git a/common/windows/delphi/ext/sentry/build.sh b/common/windows/delphi/ext/sentry/build.sh index fcbd94b70e..c0b0cc9147 100755 --- a/common/windows/delphi/ext/sentry/build.sh +++ b/common/windows/delphi/ext/sentry/build.sh @@ -24,8 +24,10 @@ function do_test() { delphi_msbuild SentryClientTest.dproj "//p:Platform=Win32" delphi_msbuild SentryClientVclTest.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/SentryClientTest.map" "$WIN32_TARGET" - do_map2pdb "$WIN32_TARGET_PATH/SentryClientVclTest.map" "$WIN32_TARGET" + sentrytool_delphiprep "$WIN32_TARGET_PATH/SentryClientTest.exe" -dpr SentryClientTest.dpr + sentrytool_delphiprep "$WIN32_TARGET_PATH/SentryClientVclTest.exe" -dpr SentryClientVclTest.dpr + tds2dbg "$WIN32_TARGET_PATH/SentryClientTest.exe" + tds2dbg "$WIN32_TARGET_PATH/SentryClientVclTest.exe" sentry-cli upload-dif -p keyman-windows --wait --include-sources . diff --git a/common/windows/delphi/ext/tds2dbg.txt b/common/windows/delphi/ext/tds2dbg.txt new file mode 100644 index 0000000000..fd23f6f915 --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg.txt @@ -0,0 +1,49 @@ +Keyman Third Party Package Readme: Tds2Dbg +=========================================== + +Name: Tds2Dbg +Purpose: Tds2Dbg converts .tds to .dbg files +Source: https://sourceforge.net/projects/tds2dbg/ +Version: 2014-12-22 +License: ISC (MIT compatible) +Updated for: N/A +Documentation: https://sourceforge.net/projects/tds2dbg/ +Download location: https://sourceforge.net/projects/tds2dbg/ +Licensed For: Unlimited developers +Include Paths: N/A + + +Installation notes +------------------ + +Download from site above. + +Build notes +----------- + +tds2dbg is used for building .dbg files from .tds files so that Delphi +projects can be debugged in WinDbg/Visual Studio and with dump files. + +This project builds with Borland C++ 5.5 and not other compilers; +while it may build successfully with other compilers, there appear to +be alignment issues. Hence, the executable is included in the repository +to avoid requiring another build tool. + +License - ISC +------------- + +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. + diff --git a/common/windows/delphi/ext/tds2dbg/.gitignore b/common/windows/delphi/ext/tds2dbg/.gitignore new file mode 100644 index 0000000000..65776c32fc --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/.gitignore @@ -0,0 +1 @@ +/bin/ \ No newline at end of file diff --git a/common/windows/delphi/ext/tds2dbg/Build.txt b/common/windows/delphi/ext/tds2dbg/Build.txt new file mode 100644 index 0000000000..c8bb64961f --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/Build.txt @@ -0,0 +1,8 @@ +Get the free Borland C++ Builder 5.5 from + https://downloads.embarcadero.com/free/c_builder + +Ensure that the location of bcc32.exe is in your path +and type "make.exe" in a command prompt with the +current working directory set to the directory +holder the sources for tds2dbg. + diff --git a/common/windows/delphi/ext/tds2dbg/ChangeLog b/common/windows/delphi/ext/tds2dbg/ChangeLog new file mode 100644 index 0000000000..6f5f67398e --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/ChangeLog @@ -0,0 +1,12 @@ +tds2dbg 0.2.0 + + * Added support for conversion of public and static symbols. + * Corrected padding and alignment. + * Preliminary support for conversion of types. Still incomplete. + * Can now convert a non-PE CodeView NB09 file to a PE CodeView file. + * Can now generate bare CodeView files (i.e. no PE header). + +tds2dbg 0.1.0 + + * Initial version + diff --git a/common/windows/delphi/ext/tds2dbg/LICENSE b/common/windows/delphi/ext/tds2dbg/LICENSE new file mode 100644 index 0000000000..0d7784e0f5 --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/LICENSE @@ -0,0 +1,15 @@ +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. + diff --git a/common/windows/delphi/ext/tds2dbg/Makefile b/common/windows/delphi/ext/tds2dbg/Makefile new file mode 100644 index 0000000000..e01296837e --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/Makefile @@ -0,0 +1,22 @@ +info: + echo The following builds require Borland C++ compiler to be installed and in the path; see Readme.txt for details. + echo So, the tds2dbg.bin is kept in the repo in order to make builds simpler + +BCC55DIR=c:\borland\bcc55 + +tds2dbg.exe: main.cpp tds2dbg.cpp fileutils.cpp log.cpp +# $(CL) /Fe$< /EHsc $** + path $(BCC55DIR)\bin;$(PATH) + $(BCC55DIR)\bin\bcc32 -y -v -L$(BCC55DIR)\Lib -I$(BCC55DIR)\Include -e$< $** + +test.exe: testa.cpp +# $(CL) /Fe$< /EHsc $** + $(BCC55DIR)\bin\bcc32 -y -v -M -L$(BCC55DIR)\Lib -I$(BCC55DIR)\Include -e$< $** + +clean: + -del /Q tds2dbg.obj fileutils.obj log.obj main.obj tds2dbg.tds + -del /Q testa.obj test.exe test.tds test.dbg + -rmdir /S /Q html + +doc: + doxygen tds2dbg.dcf diff --git a/common/windows/delphi/ext/tds2dbg/ReadMe.txt b/common/windows/delphi/ext/tds2dbg/ReadMe.txt new file mode 100644 index 0000000000..6184609438 --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/ReadMe.txt @@ -0,0 +1,64 @@ +NAME + + tds2dbg - Borland TDS debug symbol file to Microsft DBG debug symbol file + converter. + +SYNOPSIS + + tds2dbg [ [-h|--help] | [-t|--types] [-s|--statics] [--] program.exe ] + +DESCRIPTION + + tds2dbg will read the Borland debug symbol file associated with the + Microsoft Windows executable program.exe (program.tds) and create a + corresponding Microsoft DBG debug symbol file (program.dbg). Additionally, + it will modify a flag in program.exe to signal that debug symbols are + available externally to the executable. + + The dbg files can be used with, say, windbg available from the + Microsoft Debugging Tools at the Microsoft Windows Hardware Developer + Central (http://www.microsoft.com/whdc). + +OPTIONS + + -h, --help Prints a help text. Running tds2dbg without arguments + will do the same. + -v, --verbose Each occurence of this option increases verbosity. + -q, --quiet Operate silently. + -t, --types Convert types (incomplete feature). + -s, --statics Treat statics as publics. + -b, --barecv Write bare code view data. No PE header is added. + +KNOWN ISSUES + + tds2dbg is currently only able to insert source code references, public + symbols, and static symbols into the generated DBG file. Debug information + on local variables is partially generated, but no debugger seems to be + able to use this information. Additionally, conversion of types is not + fully supported and is only done on request (the -t option). + +VERSION + + This is the documentation for tds2dbg version 0.2.0 + +AUTHOR + + Copyright (C) 2009-2010, Bjarne Juul Pasgaard + +LICENSE + + 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. + diff --git a/common/windows/delphi/ext/tds2dbg/build.sh b/common/windows/delphi/ext/tds2dbg/build.sh new file mode 100755 index 0000000000..805f957ea1 --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/build.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../../../../../resources/build/builder-full.inc.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +# TODO: move this out of ext and into tools + +builder_describe "tds2dbg component" clean configure build test +builder_parse "$@" + +#------------------------------------------------------------------------------------------------------------------- + +source "$KEYMAN_ROOT/resources/build/win/environment.inc.sh" + +builder_describe_outputs \ + configure:project /resources/build/win/delphi_environment_generated.inc.sh \ + build:project "$WIN32_TARGET_PATH/tds2dbg.exe" + +#------------------------------------------------------------------------------------------------------------------- + +function do_build() { + mkdir -p "$WIN32_TARGET_PATH" + cp tds2dbg.bin "$WIN32_TARGET_PATH/tds2dbg.exe" + + mkdir -p "$KEYMAN_ROOT/common/windows/bin/tools" + cp "$WIN32_TARGET_PATH/tds2dbg.exe" "$KEYMAN_ROOT/common/windows/bin/tools/" +} + +builder_run_action clean:project clean_windows_project_files +builder_run_action configure:project configure_windows_build_environment +builder_run_action build:project do_build +# builder_run_action test:project do_test diff --git a/common/windows/delphi/ext/tds2dbg/fileutils.cpp b/common/windows/delphi/ext/tds2dbg/fileutils.cpp new file mode 100644 index 0000000000..f25013fcb7 --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/fileutils.cpp @@ -0,0 +1,158 @@ +// 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. + +/// @file fileutils.cpp File utilities + +#include "fileutils.h" + +FileWrapper::FileWrapper(FILE* f) : m_f(f) +{ +} + +unsigned int +FileWrapper::tell() const { + return ftell(m_f); +} + +void +FileWrapper::seek(unsigned int pos) throw(std::runtime_error) { + if(fseek(m_f,pos,SEEK_SET)) { + throw std::runtime_error("Failed to set read/write position"); + } +} + +void +FileWrapper::skip(unsigned int amount) throw(std::runtime_error) { + if(fseek(m_f,amount,SEEK_CUR)) { + throw std::runtime_error("Failed to advance read/write position"); + } +} + +void +FileWrapper::write1bLpfName(const std::string& name) throw(std::runtime_error) +{ + unsigned char len = (unsigned char) (name.length()); + + if((sizeof len) != fwrite(&len,1,(sizeof len),m_f)) { + throw std::runtime_error("Failed to write length field of string"); + } + if((unsigned int)(len+1) != fwrite(name.c_str(),1,len+1,m_f)) { + throw std::runtime_error("Failed to write string contents"); + } +} + +RecordReader::RecordReader(FileWrapper& tdsFile, unsigned int recSz) : + m_tdsFile(tdsFile) +{ + m_recPos = tdsFile.tell(); + m_endPos = m_recPos + recSz; +} + +RecordWriter::~RecordWriter() +{ + WriteQCont::iterator it = m_writeQ.begin(); + while(m_writeQ.end() != it) { + delete *it++; + } +} + +void +RecordWriter::commit(FileWrapper& f) const throw(std::runtime_error) +{ + WriteQCont::const_iterator it = m_writeQ.begin(); + while(m_writeQ.end() != it) { + (*it++)->commit(f); + } +} + +unsigned int +RecordWriter::length() const +{ + unsigned int len = 0; + WriteQCont::const_iterator it = m_writeQ.begin(); + while(m_writeQ.end() != it) { + len += (*it++)->length(); + } + return len; +} + +void +RecordWriter::write1bLpfName(const std::string& name) +{ + unsigned char len = (unsigned char) (name.length()); + + this->write(len); + this->write(*(name.c_str()),len+1); +} + +RecordWriter::WriteQueueElement::~WriteQueueElement() +{ +} + +unsigned int +RecordWriter::CopiedData::length() const +{ + return m_data.size(); +} + +void +RecordWriter::CopiedData::commit(FileWrapper& f) const throw(std::runtime_error) +{ + f.write(m_data.front(),m_data.size()); +} + +RecordWriter::WriteQueueElement* +RecordWriter::CopiedData::append(const unsigned char* src, unsigned int len) +{ + m_data.reserve(m_data.size()+len); + while(len--) { + m_data.push_back(*src++); + } + return 0; +} + +RecordWriter::ReferencedData:: +ReferencedData(const unsigned char* src, unsigned int len) + : m_src(src), m_len(len) +{ +} + +unsigned int +RecordWriter::ReferencedData::length() const +{ + return m_len; +} + +void +RecordWriter::ReferencedData::commit(FileWrapper& f) const + throw(std::runtime_error) +{ + f.write(*m_src,m_len); +} + +RecordWriter::WriteQueueElement* +RecordWriter::ReferencedData::append(const unsigned char* src, unsigned int len) +{ + CopiedData* c = new CopiedData(); + c->append(src,len); + return c; +} + + +// Local Variables: +// mode:c++ +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: diff --git a/common/windows/delphi/ext/tds2dbg/fileutils.h b/common/windows/delphi/ext/tds2dbg/fileutils.h new file mode 100644 index 0000000000..0d7d09af95 --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/fileutils.h @@ -0,0 +1,564 @@ +// 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. + +/// @file fileutils.h File utilities + +#ifndef FILEUTILS_H +#define FILEUTILS_H + +#include +#include + +/// @brief Base class for file access wrappers. +class FileWrapper { +public: + + /// @brief Constructor. + /// + /// @param f The file stream being wrapped. Ownership not transferred. + FileWrapper(FILE* f); + + /// @brief Returns the current read/write position. + /// @return The current write position. + unsigned int tell() const; + + /// @brief Sets the current read/write position. + /// + /// @param pos The new (absolute) write position. + /// + /// @throw runtime_error If an error occured. + void seek(unsigned int pos) throw(std::runtime_error); + + /// @brief Skips a number of bytes from the input stream. + /// + /// @param amount The number of bytes to skip. + /// + /// @throw runtime_error If an error occured. + void skip(unsigned int amount) throw(std::runtime_error); + + /// @brief Reads a sequence of structures from the current read + /// position and throws an exception if end of file is + /// reached prematurely. + /// + /// @param dst The 1st structure to hold the data read in. + /// @param num The number of structures to read. + /// + /// @tparam The class of the structures to read. + /// + /// @throw runtime_error If not all bytes could be read. + template + void read(T& dst, unsigned int num = 1) throw(std::runtime_error) { + unsigned int n = fread(&dst,1,num*sizeof(T),m_f); + if(num*sizeof(T) != n) { + throw std::runtime_error("Unexpected end of file"); + } + } + + /// @brief Attempts to reads a sequence of structures from the + /// current read position. + /// + /// @param dst The 1st structure to hold the data read in. + /// @param num The number of structures to read. + /// + /// @return The number of bytes actually read. + /// + /// @tparam The class of the structures to read. + template + unsigned int tryRead(T& dst, unsigned int num = 1) { + return fread(&dst,1,num*sizeof(T),m_f); + } + + /// @brief Writes a sequence of bytes to the current write position. + /// + /// @param src Reference to the 1st structure to be written. + /// @param num The number of instances to write. + /// + /// @return The number of bytes actually written. + /// + /// @tparam T Class of the structure to write. + /// + /// @throw runtime_error If an error occured. + template + unsigned int + write(const T& src, unsigned int num = 1) throw(std::runtime_error) { + unsigned int n = fwrite(&src,1,num*sizeof(T),m_f); + if(n != num*sizeof(T)) { + throw std::runtime_error("Write error in write()"); + } + return n; + } + + /// @brief Writes multiple copies of the same structure starting from the + /// current write position. + /// + /// @param src Reference to the structure to be written. + /// @param num The number of copies to write. + /// + /// @return The number of bytes actually written. + /// + /// @tparam T Class of the structure to write. + /// + /// @throw runtime_error If an error occured. + template + unsigned int + writeRep(const T& src, unsigned int num = 1) throw(std::runtime_error); + + /// @brief Writes a (1-byte) length-prefixed name to the current + /// write position. + /// + /// @param name The name to write. + /// + /// @throw runtime_error If an error occured. + void write1bLpfName(const std::string& name) throw(std::runtime_error); + +protected: + + FILE* m_f; ///< The file stream. + +}; + + +template +unsigned int +FileWrapper::writeRep(const T& src, unsigned int num) + throw(std::runtime_error) +{ + unsigned int n = 0; + while(num--) { + unsigned int m = fwrite(&src,1,sizeof(T),m_f); + if(sizeof(T) != m) { + throw std::runtime_error("Write error in writeRep()"); + } + n += m; + } + return n; +} + +/// @brief Reads a record sequentially from a file while keeping track +/// of the number of bytes available. +class RecordReader { +public: + + /// @brief Constructor. + /// + /// @param tdsFile The file to read from. + /// @param recSz The number of bytes in the record. + RecordReader(FileWrapper& tdsFile, unsigned int recSz); + + /// @brief Returns the current read position. + /// + /// @return The current (absolute) read position. + unsigned int tell() { + return m_tdsFile.tell(); + } + + /// @brief Determines whether an end-of-record condition is met. + /// + /// @retval true if all bytes have been read. + /// @retval false otherwise. + bool eor() const { + return (m_tdsFile.tell() >= m_endPos); + } + + /// @brief Sets the current absolute read position. + /// + /// @param pos The new (absolute) read position. + /// + /// @throw runtime_error In case of errors. + void seek(unsigned int pos) throw(std::runtime_error) { + m_tdsFile.seek(pos); + } + + /// @brief Skips a number of bytes from the input stream. + /// + /// @param amount The number of bytes to skip. + /// + /// @throw runtime_error In case of errors. + void skip(unsigned int amount) throw(std::runtime_error) { + m_tdsFile.skip(amount); + } + + /// @brief Reads a sequence of structures from the current read + /// position in the TDS file. + /// + /// @param dst The 1st structure to hold the data read in. + /// @param num The number of structures to read. + /// + /// @throw runtime_error In case of errors. + template + void read(T& dst, unsigned int num = 1) throw(std::runtime_error) { + m_tdsFile.read(dst,num); + } + +protected: + + FileWrapper& m_tdsFile; ///< The file to read from. + + unsigned int m_recPos; ///< The position of the current record. + + unsigned int m_endPos; ///< The position after the end of the record list. + +}; + +/// @brief Registers data for later writing to a file while counting +/// the number of bytes to be written. +class RecordWriter { +public: + + /// @brief Destructor. + ~RecordWriter(); + + /// @brief Commits the data registered to a file at the current write + /// position of the file. + /// + /// @param file The file to write to. + /// + /// @throw runtime_error In case of errors. + void commit(FileWrapper& f) const throw(std::runtime_error); + + /// @brief Returns the number of bytes registered for later writing. + /// + /// @return The number of bytes registered. + unsigned int length() const; + + /// @brief Appends a sequence of structures to the tail of the + /// write queue. + /// + /// The data provided is copied into the internal write queue. + /// + /// @param dst The 1st structure that holds the data to be written. + /// @param num The number of structures to write. + /// + /// @return The number of bytes written. + template + unsigned int write(const T& src, unsigned int num = 1) { + unsigned int n = sizeof(T)*num; + WriteQueueElement* e; + + if(0 == m_writeQ.size()) { + e = new CopiedData(); + m_writeQ.push_back(e); + } + + e = m_writeQ.back()->append(reinterpret_cast(&src),n); + if(e) { + // New element created, append it to the queue. + m_writeQ.push_back(e); + } + return n; + } + + /// @brief Writes multiple copies of the same structure starting from the + /// current write position. + /// + /// @param src Reference to the structure to be written. + /// @param num The number of copies to write. + /// + /// @return The number of bytes actually written. + /// + /// @tparam T Class of the structure to write. + template + unsigned int writeRep(const T& src, unsigned int num = 1) { + unsigned int n = 0; + while(num--) { + n+= this->write(src); + } + return n; + } + + /// @brief Appends a sequence of structures to the tail of the + /// write queue. + /// + /// This menthod appends a reference to the data to the write + /// queue. The data provided is not copied. + /// + /// @param dst The 1st structure that holds the data to be written. + /// @param num The number of structures to write. + /// + /// @return The number of bytes written. + template + unsigned int delayedWrite(const T& src, unsigned int num = 1) { + unsigned int n = sizeof(T)*num; + m_writeQ. + push_back(new + ReferencedData(reinterpret_cast(&src),n)); + return n; + } + + /// @brief Appends a (1-byte) length-prefixed name to the write queue. + /// + /// @param name The name to write. + void write1bLpfName(const std::string& name); + +protected: + + /// @brief Base class for write queue data. + class WriteQueueElement + { + public: + + /// @brief Destructor. + virtual ~WriteQueueElement(); + + /// @brief Returns the number of bytes registered within this element. + /// + /// @return The number of bytes registered within this element. + virtual unsigned int length() const = 0; + + /// @brief Commits the data in this element to the current write + /// position of a file. + /// + /// @param f The file to write to. + /// + /// @throw runtime_error In case of errors. + virtual void commit(FileWrapper& f) const throw(std::runtime_error) = 0; + + /// @brief Appends a copy of a sequence of bytes to the current + /// element. + /// + /// @param src The first byte to copy. + /// @param len The number of bytes to copy. + /// + /// @return 0 if the bytes were appended to the current element, or + /// a pointer to a new element that received the copied bytes. + virtual + WriteQueueElement* append(const unsigned char* src, unsigned int len) = 0; + + }; + + /// @brief Data copied into the write queue. + class CopiedData : public WriteQueueElement + { + public: + + // Overridden base class method. + unsigned int length() const; + + // Overridden base class method. + void commit(FileWrapper& f) const throw(std::runtime_error); + + // Overridden base class method. + WriteQueueElement* append(const unsigned char* src, unsigned int len); + + protected: + + std::vector m_data; + + }; + + /// @brief Data referenced from the write queue. + class ReferencedData : public WriteQueueElement + { + public: + + // Overridden base class method. + unsigned int length() const; + + // Overridden base class method. + void commit(FileWrapper& f) const throw(std::runtime_error); + + // Overridden base class method. + WriteQueueElement* append(const unsigned char* src, unsigned int len); + + /// @brief Constructor. + /// + /// @param src The first byte to reference. + /// @param len The number of subsequent bytes to reference. + ReferencedData(const unsigned char* src, unsigned int len); + + protected: + + const unsigned char* m_src; ///< The first byte referenced. + unsigned int m_len; ///< The number of bytes referenced. + + }; + +protected: + + /// @brief Container underlying the internal write queue. + typedef std::vector WriteQCont; + + /// @brief The internal write queue. + WriteQCont m_writeQ; + +}; + +/// @brief Postpones writing of a record until committed. +/// +/// @note It is important to create an instance of this class before writing +/// any records that should follow it (in file order). +/// +/// @tparam R The record structure. +template +class PpRecordWriter { +public: + + /// @brief Constructor. + /// + /// @param dbgFile The DBG file to write to. + /// @param rec The record to be written when committed to. + PpRecordWriter(FileWrapper& dbgFile, R& rec) : + m_dbgFile(dbgFile), m_rec(rec), m_len(0) + { + m_recPos = dbgFile.tell(); + dbgFile.skip(sizeof(R)); + } + + /// @brief Commits to the record. + /// + /// This method will write out the record. + /// + /// @throw runtime_error In case of errors. + void commit() throw(std::runtime_error) { + unsigned int oldPos = m_dbgFile.tell(); + m_dbgFile.seek(m_recPos); + m_len = m_dbgFile.write(m_rec); + m_dbgFile.seek(oldPos); + } + + /// @brief Returns the number of bytes written. + /// @return The number of bytes written. + unsigned int length() const { + return m_len; + } + +protected: + + /// @brief The DBG file to write to. + FileWrapper& m_dbgFile; + + /// @breif The record to write. + R& m_rec; + + /// @brief File position of the record. + unsigned int m_recPos; + + /// @brief Number of bytes written. + unsigned int m_len; + +}; + +/// @brief Postpones writing of a header with a length field to be +/// computed until committed. +/// +/// @tparam H The header structure. +/// @tparam L The type of the length field. +template +class HeaderWithLen { +public: + + /// @brief Constructor. + /// + /// @param hdr The header to be written when committed to. + /// Must remain available during the entire lifetime of the + /// object created. + /// @param lenFld The length field to update. Must remaining available + /// during the entire lifetime of the object created. + HeaderWithLen(H& hdr, L& lenFld) : m_hdr(hdr), m_lenFld(lenFld) + { + } + + /// @brief Writes the header and the data registered by a RecordWriter. + /// + /// This method will write out the header with an updated length field + /// as well as the data registered by the RecordWriter. + /// The length field will be computed as the size of the header, plus the + /// size of the data written by the RecordWriter, minus the size of the + /// length field itself. + /// + /// @param f The file to write to. + /// @param w The record writer that was used to write the data committed to. + /// + /// @throw runtime_error In case of errors. + void + commit(FileWrapper& f, const RecordWriter& w) throw(std::runtime_error){ + m_lenFld = sizeof(H) + w.length()-sizeof(L); + f.write(m_hdr); + w.commit(f); + } + + /// @brief Returns the number of bytes (to be) written by the record writer + /// and the header writer itself. + /// + /// @return The number of bytes (to be) written. + unsigned int length(const RecordWriter& w) const { + return sizeof(H) + w.length(); + } + +protected: + + /// @brief The header to write. + H& m_hdr; + + /// @brief The length field to update. + L& m_lenFld; + +}; + +/// @brief Writes a sequence of bytes from one file object to another. +/// +/// @param dst Destination file object. +/// @param src Source file object. +/// @param amount Number of bytes to copy. +/// +/// @tparam D Class of the destination object. +/// @tparam S Class of the source object. +/// +/// @throw runtime_error In case of errors. +template +void +copyFileContent(D& dst, S& src, unsigned int amount = 1) + throw(std::runtime_error) +{ + unsigned char buf[512]; + unsigned int toRead = (sizeof buf); + while(amount) { + if(toRead>amount) { + toRead = amount; + } + src.read(buf[0],toRead); + amount -= toRead; + dst.write(buf[0],toRead); + } +} + +/// @brief Appends the contents of one file to another. +/// +/// @param dst Destination file object. +/// @param src Source file object. +/// +/// @tparam D Class of the destination object. +/// @tparam S Class of the source object. +/// +/// @throw runtime_error In case of errors. +template +void +copyFile(D& dst, S& src) throw(std::runtime_error) +{ + unsigned char buf[512]; + unsigned int bytesRead; + do { + bytesRead = src.tryRead(buf[0],(sizeof buf)); + dst.write(buf[0],bytesRead); + } while((sizeof buf) == bytesRead); +} + +#endif + +// Local Variables: +// mode:c++ +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: diff --git a/common/windows/delphi/ext/tds2dbg/log.cpp b/common/windows/delphi/ext/tds2dbg/log.cpp new file mode 100644 index 0000000000..b6dc31d4c7 --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/log.cpp @@ -0,0 +1,44 @@ +// 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. + +/// @file log.cpp Log utilitites + +#include "log.h" + +std::ostream& Log::operator()(unsigned int verbosity) +{ + if(verbosity + +/// @brief Logger class. +class Log +{ +public: + + /// @brief Constructor. + Log() : m_verbosity(0), m_nullStream(&m_nullStreamBuf) {} + + /// @brief Sets the level of verbosity + /// + /// @param verbosity New level of verbosity. 0 means silence. + void setVerbosity(unsigned int verbosity) { m_verbosity = verbosity; } + + /// @brief Returns a reference to a stream handling the requested level + /// of verbosity. + /// + /// @param verbosity The requested level of verbosity. + std::ostream& operator()(unsigned int verbosity = 0); + +protected: + + /// @brief Helper class implementing a dummy stream buffer that + /// discards everything sent to it. + /// + /// @ingroup HMILoggingFeatures + class NullStreamBuf : public std::streambuf + { + public: + + /// @brief Constructor + NullStreamBuf(); + + }; + +protected: + + /// @brief The current level of verbosity. + unsigned int m_verbosity; + + /// @brief Supporting stream buffer for m_nullStream. + NullStreamBuf m_nullStreamBuf; + + /// @brief Internal stream used as a null-stream that discards + /// everything streamed to it. + std::ostream m_nullStream; + +}; + +/// @brief Accesser for the global Log instance. +/// +/// @return Reference to the global log. +Log& mylog(); + +#endif LOG_H + +// Local Variables: +// mode:c++ +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: diff --git a/common/windows/delphi/ext/tds2dbg/main.cpp b/common/windows/delphi/ext/tds2dbg/main.cpp new file mode 100644 index 0000000000..c0cbd579a7 --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/main.cpp @@ -0,0 +1,115 @@ +// 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 +#include +#include + +void printUsage(const char* argv0) +{ + std::cerr + << "Usage: " << argv0 + << " [-h|--help] [-t|--types] [-s|--statics] [--] exefilenamewithextension\n" + "Options:\n" + " -h, --help Prints this message\n" + " -v, --verbose Increase verbosity\n" + " -q, --quiet Operate silently\n" + " -t, --types Convert types (incomplete feature)\n" + " -s, --statics Treat statics as publics\n" + " -b, --barecv Write bare code view data\n" + << std::endl; +} + +int main(int argc, char* argv[]) +{ + int argNo = 1; + unsigned int verbosity = 1; + bool convertTypes = false; + bool staticsAsPublics = false; + bool bareCodeView = false; + + while(argc > 1) { + char* a = argv[argNo]; + if('-' != *a || 0==strcmp("--",a)) { + break; + } else if(0==strcmp("-h",a) || 0==strcmp("--help",a)) { + printUsage(argv[0]); + return 0; + } else if(0==strcmp("--verbose",a) || 0==strcmp("-v",a)) { + if(verbosity) ++verbosity; + } else if(0==strcmp("--quiet",a) || 0==strcmp("-q",a)) { + verbosity = 0; + } else if(0==strcmp("--types",a) || 0==strcmp("-t",a)) { + convertTypes = true; + } else if(0==strcmp("--statics",a) || 0==strcmp("-s",a)) { + staticsAsPublics = true; + } else if(0==strcmp("--barecv",a) || 0==strcmp("-b",a)) { + bareCodeView = true; + } else { + std::cerr << "Unknown option " << a << std::endl; + printUsage(argv[0]); + return 1; + } + --argc; + ++argNo; + } + + if(2 != argc) { + printUsage(argv[0]); + return 1; + } + + std::string exeFile(argv[argNo]); + std::string::size_type dotP = exeFile.rfind('.'); + if(std::string::npos == dotP) { + std::cerr + << "Argument exe file name must be specified with extension" + << std::endl; + return 2; + } + + std::string tdsFile(exeFile.substr(0,dotP+1)); + std::string dbgFile(tdsFile); + + const std::ctype& ctf = + std::use_facet >(std::locale()); + std::string::size_type ep = dotP; + while(exeFile.length() != ep) { + exeFile[ep] = ctf.tolower(exeFile[ep]); + ++ep; + } + + std::string ext(exeFile.substr(dotP+1)); + + if(!ext.compare("tds") || !ext.compare("dbg")) { + std::cerr + << "Executable file cannot have extension .tds or .dbg" + << std::endl; + return 1; + } + + tdsFile.append("tds"); + dbgFile.append("dbg"); + + return tds2dbg(exeFile.c_str(),tdsFile.c_str(),dbgFile.c_str(), + verbosity,convertTypes,staticsAsPublics,bareCodeView); +} + +// Local Variables: +// mode:c++ +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: diff --git a/common/windows/delphi/ext/tds2dbg/tds2dbg.bin b/common/windows/delphi/ext/tds2dbg/tds2dbg.bin new file mode 100644 index 0000000000..999d60f19d Binary files /dev/null and b/common/windows/delphi/ext/tds2dbg/tds2dbg.bin differ diff --git a/common/windows/delphi/ext/tds2dbg/tds2dbg.cpp b/common/windows/delphi/ext/tds2dbg/tds2dbg.cpp new file mode 100644 index 0000000000..39f20967f7 --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/tds2dbg.cpp @@ -0,0 +1,2568 @@ +// 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 +#include +#include +#include + + +/// @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
void write(unsigned char*, unsigned int);
+/// method. +/// +/// @throw runtime_error In case of errors. +template +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 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 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 > 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::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(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 SegmSymMap; + +/// @brief Container type of the temporary storage of public symbols. +/// +/// The key is the segment. +typedef std::map 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 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 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 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 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 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 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 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 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 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 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 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 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 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& 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 CVDirEntryList; + CVDirEntryList tdsSubSecDirEntries; + + mylog()(2) << "TDS subsection directory header has " + << tdsCvDirHdr.cDir << " entries." << std::endl; + + for(DWORD i=0 ; i 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 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::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 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 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::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 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 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 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 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::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 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: diff --git a/common/windows/delphi/ext/tds2dbg/tds2dbg.dcf b/common/windows/delphi/ext/tds2dbg/tds2dbg.dcf new file mode 100644 index 0000000000..25add6f39a --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/tds2dbg.dcf @@ -0,0 +1,1515 @@ +# Doxyfile 1.6.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = tds2dbg + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.2 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = html + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = main.cpp tdscvstructs.h tds2dbg.h tds2dbg.cpp log.h \ + log.cpp fileutils.h fileutils.cpp + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# When the SEARCHENGINE tag is enable doxygen will generate a search box for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP) or Qt help (GENERATE_QHP) +# there is already a search function so this one should typically +# be disabled. + +SEARCHENGINE = YES + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/common/windows/delphi/ext/tds2dbg/tds2dbg.h b/common/windows/delphi/ext/tds2dbg/tds2dbg.h new file mode 100644 index 0000000000..d77c49a5db --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/tds2dbg.h @@ -0,0 +1,38 @@ +// 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. + +#ifndef TDS2DBGH +#define TDS2DBGH + +/// @brief Converts an exe and a tds file to a dbg file. +/// +/// @param exeFileName Name of EXE file. +/// @param tdsFileName Name of TDS file. +/// @param dbgFileName Name of DBG file. +/// @param verbosity Verbosity level. 0 means silence. +/// @param doConvTypes Should types be converted? +/// @param staticsAsPublics Treat statics as publics? +/// @param bareCodeView Write bare code view (no header). +/// +/// @todo doConvTypes == true is incomplete. +int tds2dbg(const char* exeFileName, + const char* tdsFileName, + const char* dbgFileName, + unsigned int verbosity, + bool doConvTypes, + bool staticsAsPublics, + bool bareCodeView); + +#endif diff --git a/common/windows/delphi/ext/tds2dbg/tds2dbg.sln b/common/windows/delphi/ext/tds2dbg/tds2dbg.sln new file mode 100644 index 0000000000..eed6a4bb0a --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/tds2dbg.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.1000 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tds2dbg", "tds2dbg.vcxproj", "{40BFECE3-7FC0-4649-A3DC-E017FF84C75B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Debug|x64.ActiveCfg = Debug|x64 + {40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Debug|x64.Build.0 = Debug|x64 + {40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Debug|x86.ActiveCfg = Debug|Win32 + {40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Debug|x86.Build.0 = Debug|Win32 + {40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Release|x64.ActiveCfg = Release|x64 + {40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Release|x64.Build.0 = Release|x64 + {40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Release|x86.ActiveCfg = Release|Win32 + {40BFECE3-7FC0-4649-A3DC-E017FF84C75B}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {50FB6E2C-4FEF-4828-A315-1017276BF47A} + EndGlobalSection +EndGlobal diff --git a/common/windows/delphi/ext/tds2dbg/tds2dbg.vcxproj b/common/windows/delphi/ext/tds2dbg/tds2dbg.vcxproj new file mode 100644 index 0000000000..5183081136 --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/tds2dbg.vcxproj @@ -0,0 +1,119 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {40BFECE3-7FC0-4649-A3DC-E017FF84C75B} + Win32Proj + 10.0 + + + + Application + true + v143 + + + Application + false + v143 + + + Application + true + v143 + + + Application + false + v143 + + + + + + + + + + + + + + + + + + + + + true + + + true + + + + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level3 + ProgramDatabase + Disabled + + + MachineX86 + true + Console + + + + + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + + + MachineX86 + true + Console + true + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/windows/delphi/ext/tds2dbg/tds2dbg.vcxproj.filters b/common/windows/delphi/ext/tds2dbg/tds2dbg.vcxproj.filters new file mode 100644 index 0000000000..a74a66814b --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/tds2dbg.vcxproj.filters @@ -0,0 +1,50 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/common/windows/delphi/ext/tds2dbg/tdscvstructs.h b/common/windows/delphi/ext/tds2dbg/tdscvstructs.h new file mode 100644 index 0000000000..ab0a488f09 --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/tdscvstructs.h @@ -0,0 +1,1278 @@ +// 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. + +/// @file tdscvstruct.sh TDS and Code View structures. + +#ifndef TDSCVSTRUCTS_H +#define TDSCVSTRUCTS_H + +#include + +// Use word-alignment +#ifdef __BORLANDC__ +#pragma option push +#pragma option -a2 -w-8059 +#endif + +/// @page DebugSymFileStructures Debug Symbol File Structure +/// +/// @section DBGFileStructure DBG File Structure. +/// +/// Resources +///
    +///
  • Under The Hood, "The Scoop on .DBG Files", Matt Pietrek, +/// Microsoft Systems Journal, March 1999
  • +///
  • Undocumented Windows 2000 Secrets, A Programers Cookbook, +/// Sven B. Schreiber, Addison-Wesley, +/// (http://undocumented.rawol.com/)
  • +///
  • Tool Interface Standard (TIS) - +/// Formats Specification for Windows Version 1.0 +/// (http://www.x86.org/ftp/manuals/tools/winf10.pdf)
  • +///
+/// +/// See the documentation for IMAGE_SEPARATE_DEBUG_HEADER in winnt.h: +/// @code +/// IMAGE_SEPARATE_DEBUG_HEADER +/// Signature = IMAGE_SEPARATE_DEBUG_SIGNATURE +/// IMAGE_SECTION_HEADER* (Copied from the executable) +/// IMAGE_DEBUG_DIRECTORY* (COFF, MISC, FPO Data, CodeView) +/// COFF Information - not generated +/// Misc Information - not generated +/// FPO Data - not generated +/// CodeView Information - The only information handled by MS Debuggers +/// lfaBase: NB09 (ascii) +/// DWORD offsetOfSubSectionDir; +/// SubSectionTable +/// SubSection* +/// SubSectionDir +/// SubSectionDirEntry* +/// @endcode +/// +/// +/// @section PDBFileStructure PDB File Structure +/// +/// NOTE: The current code is not able to generate PDB files. +/// +/// A PDB file is a PE file (see +/// "http://msdn.microsoft.com/en-us/magazine/ms809762.aspx" +/// MSDN Magazine +/// Debugging and Error Handling Technical Articles +/// Peering Inside the PE: A Tour of the Win32 Portable Executable File Format, +/// By Matt Pietrek, March 1994) +/// +/// PE file structure (see winnt.h). +/// @code +/// IMAGE_DOS_HEADER +/// e_lfanew = Releative location of IMAGE_NT_HEADERS +/// IMAGE_NT_HEADERS +/// DWORD Signature = IMAGE_NT_SIGNATURE +/// IMAGE_FILE_HEADER +/// Machine +/// NumberOfSections +/// IMAGE_OPTIONAL_HEADER +/// IMAGE_DATA_DIRECTORY[16] +/// DWORD VirtualAddress +/// DWORD Size +/// Section Table +/// IMAGE_SECTION_HEADER +/// +/// IMAGE_DATA_DIRECTORY[IMAGE_DIRECTORY_ENTRY_DEBUG] points to: +/// IMAGE_DEBUG_DIRECTORY+ (number of dirs computed from DataDirectory.Size) +/// Type=IMAGE_DEBUG_TYPE_CODEVIEW +/// @endcode + +/// @page TdsFileStructure Structure of TDS file +/// +/// CodeView header signature is "FB09" or "FB0A". Tail of tds file is +/// "FB0A" or "FB09" followed by length of file. +/// +/// The layout of a TDS file seems to be (derived from a dump made by +/// Borland's tdump.exe): +/// +/// @code +/// Header +/// sstModule* +/// sstAlignSym |* (sstAlignSym and sstSrcModule occur in pairs) +/// sstSrcModule| +/// sstGlobalSym +/// sstGlobalTypes +/// sstNames +/// @endcode +/// +/// +/// @section TDSsstNamesSubSec Borlands sstNames subsection +/// +/// A Borland specific susbection with index 0x130. +/// +/// @code +/// struct TDSsstNames { +/// DWORD numberOfNames +/// Name []; +/// }; +/// +/// struct Name { +/// unsigned char len; // Length of name excluding zero-terminator +/// char name; // Zero-terminated name +/// }; +/// @endcode +/// +/// +/// @section TDSsstSrcModuleSubSec Borlands sstSrcModule subsection +/// +/// The only deviation is the use of name index in the file info record +/// instead of an inlined name: +/// +/// @code +/// struct TDSssSrcModule { +/// CVssSrcModuleHdr hdr; +/// TDSssSrcModuleRec files[hdr.cFile]; +/// }; +/// +/// struct CVssSrcModule { +/// CVssSrcModuleHdr hdr; +/// CVssSrcModuleRec files[hdr.cFile]; +/// }; +/// +/// struct CVssSrcModuleHdr : public CVssSrcModuleFixedHdr { +/// DWORD baseSrcFile[cFile]; +/// DWORD startEnd[2*cSeg]; +/// WORD seg[cSeg]; +/// }; +/// +/// struct CVssSrcModuleFixedHdr { +/// WORD cFile; +/// WORD cSeg; +/// }; +/// +/// struct TDSssSrcModuleRec { +/// TDSsrcModuleFile file; +/// CVsrcModuleSeg segs[file.cSeg]; +/// }; +/// +/// struct CVssSrcModuleRec { +/// CVsrcModuleFile file; +/// CVsrcModuleSeg segs[file.cSeg]; +/// }; +/// +/// struct TDSssSrcModuleFile : public TDSssSrcModuleFileHdr { +/// DWORD baseSrcLn[cSeg]; +/// DWORD startEnd[2*cSeg]; +/// }; +/// +/// struct TDSssSrcModuleFileHdr { +/// WORD cSeg; +/// DWORD NameId; // This is "WORD pad" in MS STI +/// }; +/// +/// struct CVssSrcModuleFile : public CVssSrcModuleFileHdr { +/// DWORD baseSrcLen[cSeg]; +/// DWORD startEnd[2*cSeg]; +/// WORD nameLen; +/// char name[nameLen]; +/// }; +/// +/// struct CVssSrcModuleFileHdr { +/// WORD cSeg; +/// WORD pad; +/// }; +/// +/// struct CVssSrcModuleSeg { +/// CVssSrcModuleSegHdr hdr; +/// DWORD offset[cPair]; +/// WORD linenumber[cPair]; +/// }; +/// +/// struct CVssSrcModuleSegHdr { +/// WORD seg; +/// WORD cPair; +/// }; +/// @endcode +/// +/// +/// @section TDSsstSegMapSubSec Code View sstSegMap subsection +/// +/// Not present in TDS files. But apparently required (and ignored?) by +/// Microsofts debuggers. +/// +// +/// @section TDSsstGlobalSymSubSec TDS sstGlobalSym subsection +/// +/// The header (TDSssGlobalSymHdr) is an extension of the corresponding +/// Code View header (CVssGlobalSymHdr). +/// +/// +/// @section TDSSymbolRecords TDS Symbol Records +/// +/// +/// @subsection TDSSymbolRecordEntryPoint The Entry Point Symbol Record +/// +/// Identified by means of kind index S_ENTRY32=0x210. There is no +/// corresponding record in Code View. Payload: +/// +/// @code +/// struct TDSsrEntry32Payload { +/// DWORD offset; // Offset +/// WORD seg; // Segment +/// }; +/// @endcode +/// +/// +/// @subsection TDSSymbolRecordNameSpace The Namespace Symbol Record +/// +/// Identified by means of kind index S_NAMESPACE=0x25. There is no +/// corresponding record in Code View. Payload: +/// +/// @code +/// struct TDSsrNameSpacePayload { +/// DWORD nameIdx; // Index of namespace name in sstNames. +/// DWORD pad; // Padding? +/// WORD cUsing; // Number of name indices for using namespaces. +/// DWORD [cUsing]; // Indices of using namespace names in sstNames. +/// }; +/// @endcode +/// +/// +/// @subsection TDSSymbolRecordUDF The User Defined Type Symbol Record +/// +/// Identified by means of kind index s_UDT=0x4. The TDS form of the +/// record payload (TDSsrUserDefinedTypePayload) deviates from the Code View +/// form of the record payload (CVsrUserDefinedTypePayload): +/// +/// @code +/// struct CVsrUserDefinedTypePayloadHdr { +/// WORD typeIdx; // Type index +/// }; +/// +/// struct CVsrUserDefinedTypePayload { +/// CVsrUserDefinedTypePayloadHdr hdr; +/// unsigned char nameLen; +/// char name[nameLen]; +/// }; +/// +/// struct TDSsrUserDefinedTypePayload { +/// DWORD typeIdx; // Type index +/// WORD flags; // 0: on=tag,off=typedef 1: on=nested +/// DWORD nameIdx; // Index of the types name in sstNames. +/// DWORD brwsInfoOffs; // Browser info offset +/// }; +/// @endcode +/// +/// +/// @subsection TDSSymbolRecordGlobalProcRef The Global Procedure Reference Symbol Record +/// +/// Identified by means of kind index S_GPROCREF=0x20. There is no +/// corresponding record in Code View --- except, perhaps, for +/// S_PROCREF. The S_GPROCREF symbol can be used to identify public +/// functions/procedures that should be registered in +/// sstGlobalPub of the DBG file. Payload: +/// +/// @code +/// struct TDSsrGlobalProcRefPayload { +/// DWORD offSymRef; // Seems always to be 0. +/// DWORD typeIdx; // Type index. 0 if none. +/// DWORD nameIdx; // Index of name in sstNames +/// DWORD brwsInfoOffset; // Browser info offset? +/// DWORD offset; // Address offset of proc or name index of +/// // DLL if segment==0xFFFF. +/// WORD segment; // Segment. +/// DWORD nameIdx; // Index of linker name, if segment=0xFFFF? +/// }; +/// @endcode +/// +/// +/// @subsection TDSSymbolRecordGlobalDataRef The Global Data Reference Symbol Record +/// +/// Identified by means of kind index S_GDATAREF=0x21. There is no +/// corresponding record in Code View --- except, perhaps, for +/// S_DATAREF. The S_GDATAREF symbol can be used to identify public +/// data that should be registered in sstGlobalPub of the DBG +/// file. Payload: +/// +/// @code +/// struct TDSsrGlobalDataRefPayload { +/// DWORD offSymRef; // Seems always to be 0. +/// DWORD typeIdx; // Type index. 0 if none. +/// DWORD nameIdx; // Index of name in sstNames +/// DWORD brwsInfoOffset; // Browser info offset? +/// DWORD offset; // Address offset of proc or name index of +/// // DLL if segment==0xFFFF. +/// WORD segment; // Segment. +/// }; +/// @endcode +/// +/// +/// @subsection TDSSymbolRecordGlobalData The Global Data Symbol Record +/// +/// Identified by means of kind index S_GLOBALDATA=0x202. The TDS form +/// of the payload (TDSsrGlobalDataPayload) deviates from the Code +/// View form of the payload (CVsrGlobalDataPayload): +/// +/// @code +/// struct CVsrGlobalDataPayloadHdr { +/// DWORD offset; +/// WORD segment; +/// WORD typeIdx; +/// }; +/// +/// struct CVsrGlobalDataPayload { +/// CVsrGlobalDataPayloadHdr hdr; +/// unsigned char nameLen; +/// char name[nameLen]; +/// }; +/// +/// struct TDSsrGlobalDataPayload { +/// DWORD offset; // Address offset +/// WORD segment; // Segment +/// WORD flags; // 1=TLS var, 0=Not a TLS var.? +/// DWORD typeIdx; // Index of type +/// DWORD nameIdx; // Index of name in sstNames. +/// DWORD brwsInfoOffs; // Browser info offset? +/// }; +/// @endcode +/// +/// +/// @subsection TDSSymbolRecordLocalData The Local Data Symbol Record +/// +/// Identified by means of kind index S_LOCALDATA=0x201. The TDS form +/// of the payload is identical to TDSsrGlobalDataPayload. The Code View +/// form of the payload is identical to CVsrGlobalDataPayload. +/// +/// +/// @section TDSsstGlobalTypesSubSec TDS sstGlobalTypes subsection +/// +/// TDS relies on a signature of 0x1 in the first 4 bytes, whereas +/// Code View uses 0x1000000. In TDS files, offsets in type directory +/// are computed relative to the start of the subsection, whereas for +/// Code View (NB09), offsets are relative to the first type record. +/// +/// +/// @section TDSTypeRecords The TDS Type Records +/// +/// TDS and Code View rely on the same type record header (CVtrHeader). +/// The header is followed by a sequence of one or more type strings as +/// defined in the following subsections. +/// +/// +/// @subsection TDSTypeStringModifier The Modifier Type String +/// +/// Identified by LF_MODIFIER=0x1. Code View uses the format specified +/// by CVtsModifier, whereas TDS files use the format specified by +/// TDStsModifier. +/// +/// +/// @subsection TDSTypeStringPointer The Pointer Type String +/// +/// Identified by LF_POINTER=0x2. Code view relies on the format +/// specified by CVtsPointer, whereas TDS files rely on the format +/// specified by TDStsPointer. In both cases, there is a variant part +/// that follows immediately after CVtsPointer and TDStsPointer, respectively. +/// +/// @todo Investigate the differences in the variant part. +/// +/// +/// @subsection TDSTypeStringArray The Array Type String +/// +/// Identified by LF_ARRAY=0x3. Code view use the format specified by +/// CVtsArray followed by a length-prefixed name of the array. TDS +/// files rely on a format specified by TDStsArray. +/// +/// +/// @subsection TDSTypeStringClass The Class Type String +/// +/// Identified by LF_CLASS=0x4. The same comments as for the structure +/// type string (LF_STRUCTURE) applies here as well. +/// +/// +/// @subsection TDSTypeStringStructure The Structure Type String +/// +/// Identified by LF_STRUCTURE=0x5. It deviates from the corresponding Code +/// View structure: +/// +/// @code +/// struct CVtsClasses { +/// WORD count; // Number of elements in the class or structure. +/// WORD fieldIdx; // Type index of the field list for this class. +/// WORD property; +/// WORD dList; // Type index of the derivation list. +/// WORD vshape; // Type index of the virtual function table shape desc. +/// unsigned char len[]; // Numeric leaf describing the size of the structure. +/// unsigned char nameLen; +/// char name[nameLen]; +/// }; +/// +/// struct TDStsClasses { +/// WORD count; // Number of elements in the class or structure +/// DWORD fieldIdx; // Type index of the field list for this class. +/// WORD property; +/// DWORD contCls; // Type index of containing class. +/// DWORD dList; // Type index of the derivation list. +/// DWORD vshape; // Type index of the virtual function table shape desc. +/// DWORD nameIdx; // Index of name in sstNames +/// WORD len; // Length of the structure in bytes +/// }; +/// @endcode +/// +/// Apart from this, bit 8 of TDStsClasses::property is on if the class has a +/// destructor, whereas bit 8 of CVtsClasses::property indicates whether the +/// definition is scoped. +/// +/// +/// @subsection TDSTypeStringEnum The Enum Type String +/// +/// Identified by LF_ENUM=0x7. It deviates from the corresponding Code View +/// structure as follows: +/// +/// @code +/// struct CVtsEnum { +/// WORD count; // Number of enumerations. +/// WORD typeIdx; // Type index of the container underlying the enum. +/// WORD fieldIdx; // Type index of the fields of the enum. +/// WORD property; // Property bit field +/// unsigned char nameLen; +/// char name[nameLen]; +/// }; +/// +/// struct TDStsEnum { +/// WORD count; // Number of enumeration. +/// DWORD typeIdx; // Type index of the container underlying the enum. +/// DWORD fieldIdx; // Type index of the fields of the enum +/// DWORD contCls; // Type index of the containing class +/// DWORD nameIdx; // Index of name in sstNames +/// }; +/// @endcode +/// +/// +/// @subsection TDSTypeStringProcedure The Procedure Type String +/// +/// Identified by LF_PROCEDURE=0x8. The Code View and TDS structures +/// (CVtsProcedure and TDStsProcedure) dffer in the size of the +/// argList member. +/// +/// +/// @subsection TDSTypeStringMembFunc The Member Function Type String +/// +/// Identified by LF_MFUNCTION=0x9. It deviates from the corresponding +/// Code View structure as follows: +/// +/// @code +/// struct CVtsMFunction { +/// WORD rvTypeIdx; // Index of the return value type. +/// WORD classIdx; // Type index of the containg class. +/// WORD thisIdx; // Type index of the this pointer. +/// BYTE callConv; // Calling convention. +/// BYTE reserved; +/// WORD parms; // Number of params. +/// WORD argList; // Type index of the argument list. +/// DWORD thisAdjust; // This pointer adjustment. +/// }; +/// +/// struct TDStsMFunction { +/// DWORD rvTypeIdx; // Index of the return value type. +/// DWORD classIdx; // Type index of the containing class. +/// DWORD thisIdx; // Type index of the this pointer. +/// BYTE callConv; // Calling convention. +/// BYTE reserved; +/// WORD params; // Number of params. +/// DWORD argList; // Type index of the argument list. +/// DWORD thisAdjust; // This pointer adjustment. +/// }; +/// @endcode +/// +/// +/// @subsection TDSTypeStringVTShape The Virtual Function Table Shape Type String +/// +/// Identified by LF_VTSHAPE=0xA. It is identical to the corresponding Code +/// View structure. +/// +/// +/// @subsection TDSTypeStringLabel The Label Type String +/// +/// Identified by LF_LABEL=0xE. It is identical to the corresponding Code +/// View structure. +/// +/// +/// @subsection TDSTypeStringPArray The PArray Type String +/// +/// Identified by LF_PARRAY=0x32. There is no corresponding Code View structure. +/// +/// @code +/// struct TDStsPArray { +/// DWORD elemTypeIdx; // Type index of type of array elements +/// DWORD idxTypeIdx; // Type index of index variable +/// DWORD nameIdx; // Name index in sstNames +/// WORD size; +/// WORD numElements; +/// }; +/// @endcode +/// +/// +/// @subsection TDSTypeStringArgList The ArgList Type String +/// +/// Identified by LF_ARGLIST=0x0201. It is identical to the +/// corresponding Code View structure. +/// +/// +/// @subsection TDSTypeStringFieldList The Field List Type String +/// +/// Identified by LF_FIELDLIST=0x204. It is identical to the corresponding +/// Code View structure. The padding used between the individual fields are +/// as defined for Code View, too. +/// +/// +/// @subsection TDSTypeStringMethodList The Method List Type String +/// +/// Identified by LF_MLIST=0x207. It is deviates from the corresponding +/// Code View structure. +/// +/// @code +/// struct CVtsMList { +/// CVtsMListElem records[]; +/// }; +/// +/// struct CVtsMListElem { +/// WORD attribute; +/// WORD typeIdx; // Type index of the procedure record +/// DWORD vtabOffset[]; // 0 or 1 element. +/// }; +/// +/// struct TDStsMList { +/// TDStsMListElem records[]; +/// }; +/// +/// struct TDStsMListElem { +/// WORD attribute; +/// DWORD typeIdx; +/// DWORD brwsInfoOffst; // Browser info offset +/// DWORD vtabOffset[];; // 0 or 1 element. +/// }; +/// @endcode +/// +/// +/// @subsection TDSTypeStringEnumerate The Enumerate Type String +/// +/// Identified by LF_ENUMERATE=0x403. It deviates from the corresponding +/// Code View structure: +/// +/// @code +/// struct CVtsEnumerate { +/// WORD attribute; +/// unsigned char value[]; // Numeric leaf specifying the value of the enum +/// unsigned char nameLen; +/// char name[nameLen]; +/// }; +/// +/// struct TDStsEnumerator { +/// WORD attribute; +/// DWORD nameIdx; // Index of the enum name in sstNames +/// DWORD brwsInfoOffs; // Browser info offset? +/// unsigned char value[]; // Numeric leaf specifying the value of the enum +/// }; +/// +/// +/// @subsection TDSTypeStringMethod The Method Type String +/// +/// Identified by LF_METHOD=0x408. It deviates from the corresponding Code View +/// structure: +/// +/// @code +/// struct CVtsMethod { +/// WORD count; // Number of methods in method list +/// WORD listIdx; // Type index of the method list (size>1 when overloaded). +/// BYTE nameLen; +/// char name[nameLen]; +/// }; +/// +/// struct TDStsMethod { +/// WORD count; // Number of methods in method list +/// DWORD listIdx; // Type index of the method list (size_1 when overloaded). +/// DWORD nameIdx; // Index of name in sstNames +/// }; +/// @endcode + + +/// @brief Code View header +struct CVHdr { + char signature[4]; // File offset of this field is lfaBase + DWORD lfoDir; +}; + +/// @brief Code View directory header +struct CVDirHdr { + WORD cbDirHdr; // Length of directory header + WORD cbDirEntry; // Length of each directory entry + DWORD cDir; // Number of directory entries + LONG lfoNextDir; // Offset of lfaBase of next dir. Unused in TDS files. + DWORD flags; // Unused. +}; + +/// @brief Code View directory entry +struct CVDirEntry { + WORD subsection; // Subdirectory index. + WORD iMod; // Module index. + DWORD lfo; // Offset from the base address lfaBase + DWORD cb; // Number of bytes in subsection +}; + +/// @brief Extended Code View subsection types for TDS files +enum CVSst +{ + CVsstIdxModule = 0x120, + CVsstIdxAlignSym = 0x125, + CVsstIdxSrcModule = 0x127, + CVsstIdxGlobalSym = 0x129, + CVsstIdxGlobalPub = 0x12a, + CVsstIdxGlobalTypes = 0x12b, + CVsstIdxSegMap = 0x12d, + CVsstIdxNames = 0x130, + CVsstIdxStaticSym = 0x134 +}; + +/// @brief Header of Code View sstModule subsection +/// +/// The header is followed by a length-prefixed name of the module +/// and cSeg CVssModuleSegInfo records (in that order). +struct CVssModuleHdr { + WORD ovlNumber; + WORD iLib; + WORD cSeg; + WORD style; +}; + +/// @brief Header of TDS sstModule subsection +/// +/// The header is followed by cSeg CVssModuleSegInfo records. +struct TDSssModuleHdr : public CVssModuleHdr { + DWORD nameIdx; ///< 1-based name index in sstNames + char pad[16]; ///< Unused? +}; + +/// @brief Segment info record from Code View sstModule subsection +struct CVssModuleSegInfo { + WORD seg; + WORD flags; + DWORD offs; + DWORD cbSeg; +}; + +/// @brief Fixed part of the header of the Code View and TDS +/// sstSrcModule subsection. +/// +/// The header is followed by a structure of the form +/// @code +/// DWORD baseSrcFile[cFile]; +/// DWORD startEnd[2*cSeg]; +/// WORD seg[cSeg]; +/// @endcode +/// For DBG Files, the above is followed by cFile instances of the +/// following structure +/// @code +/// WORD cSeg2; // See CVssSrcModuleFileHdr +/// WORD pad; +/// DWORD baseSrcLen[cSeg2]; +/// DWORD startEnd[2*cSeg2]; +/// WORD nameLen; +/// char name[nameLen]; +/// { +/// WORD seg; // See CVssSrcModuleSegHdr +/// WORD cPair; +/// DWORD offset[cPair]; +/// WORD linenumber[cPair]; +/// } [cSeg2]; +/// @endcode +/// whereas for TDS files, it is followed by cFile instances of the +/// following structure +/// @code +/// WORD cSeg2; // See TDSssSrcModuleFileHdr +/// DWORD nameIdx; +/// DWORD baseSrcLen[cSeg2]; +/// DWORD startEnd[2*cSeg2]; +/// { +/// WORD seg; // Se CVssSrcModuleSegHdr +/// WORD cPair; +/// DWORD offset[cPair]; +/// WORD linenumber[cPair]; +/// } [cSeg2]; +/// @endcode +struct CVssSrcModuleFixedHdr { + WORD cFile; + WORD cSeg; +}; + +/// @brief Header of the Segment info record from Code View sstSrcModule subsec +struct CVssSrcModuleSegHdr { + WORD seg; + WORD cPair; +}; + +/// @brief Header of the File info record from TDS sstSrcModule subsec +struct TDSssSrcModuleFileHdr { + WORD cSeg; + DWORD nameIdx; +}; + +/// @brief Header of the File info record from Code View sstSrcModule subsec +struct CVssSrcModuleFileHdr { + WORD cSeg; + WORD pad; +}; + +/// @brief Header of the Code View sstSegMap subsection +struct CVssSegMapHdr { + WORD cSeg; ///< Number of segment descriptors + WORD cSegLog; ///< Number of logical segment descriptors +}; + +/// @brief A segment descriptor in the Code View sstSegMap subection +struct CVssSegMapSegDesc { + WORD flags; ///< Descriptor flags. + WORD ovl; ///< Logical overlay number. + WORD group; ///< Group index. + WORD frame; ///< Frame. + WORD iSegName; ///< Segment name. 0xFFFF means no name. + WORD iClassName; ///< Class name. 0xFFFF means no name. + WORD offset; /** Offset of logical segment within physical segment. + Always 0. */ + WORD cbSeg; ///< Size of segment. +}; + +/// @brief Header of the Code View sstGlobalTypes subsection +struct CVssGlobalTypesHdr { + DWORD flags; ///< Flags and signature. + DWORD cTypes; ///< Number of types in the subsection +}; + +/// @brief Header of the Code View sstGlobalSym subsection +struct CVssGlobalSymHdr { + WORD symhash; + WORD addrhash; + DWORD cbSymbol; + DWORD cbSymHash; + DWORD cbAddrHash; +}; + +/// @brief Header for the TDS sstGlobalSym subsection +struct TDSssGlobalSymHdr : public CVssGlobalSymHdr { + DWORD numUserDefTypes; ///< Number of user defined types. + DWORD numOtherSyms; ///< Number of other symbols. + DWORD numSyms; ///< Number of symbols. + DWORD numNameSpcs; ///< Number of namespaces. +}; + +/// @brief Header for the CodeView and TDS sstAlignSym subsection +struct CVssAlignSymHdr { + DWORD signature; ///< Always 1. +}; + +/// @brief Code View leaf identifiers +enum CVLeafId { + LF_MODIFIER = 0x0001, + LF_POINTER = 0x0002, + LF_ARRAY = 0x0003, + LF_CLASS = 0x0004, + LF_STRUCTURE = 0x0005, + LF_PROCEDURE = 0x0008, + LF_MFUNCTION = 0x0009, + LF_NOTTRANS = 0x0010, + LF_PAD0 = 0x00f0, + LF_PAD1 = 0x00f1, + LF_PAD2 = 0x00f2, + LF_PAD3 = 0x00f3, + LF_PAD4 = 0x00f4, + LF_PAD5 = 0x00f5, + LF_PAD6 = 0x00f6, + LF_PAD7 = 0x00f7, + LF_PAD8 = 0x00f8, + LF_PAD9 = 0x00f9, + LF_PAD10 = 0x00fa, + LF_PAD11 = 0x00fb, + LF_PAD12 = 0x00fc, + LF_PAD13 = 0x00fd, + LF_PAD14 = 0x00fe, + LF_PAD15 = 0x00ff, + LF_ARGLIST = 0x0201, + LF_MLIST = 0x0207, + LF_NUMERIC = 0x8000, + LF_CHAR = 0x8000, + LF_SHORT = 0x8001, + LF_USHORT = 0x8002, + LF_LONG = 0x8003, + LF_ULONG = 0x8004, + LF_REAL32 = 0x8005, + LF_REAL64 = 0x8006, + LF_REAL80 = 0x8007, + LF_REAL128 = 0x8008, + LF_QUADWORD = 0x8009, + LF_UQUADWORD = 0x800a, + LF_REAL48 = 0x800b, + LF_COMPLEX32 = 0x800c, + LF_COMPLEX64 = 0x800d, + LF_COMPLEX80 = 0x800e, + LF_COMPLEX128 = 0x800f, + LF_VARSTRING = 0x8010 +}; + +/// @brief Header of the Code View type record. +struct CVtrHeader { + WORD len; ///< Length excluding the length field itself. +}; + +/// @brief Type string for Code View LF_MODIFIER +struct CVtsModifier { + WORD attribute; ///< Modifier attributes + WORD typeIdx; ///< Index of modified type +}; + +/// @brief Type string for TDS LF_MODIFIER +struct TDStsModifier { + WORD attribute; ///< Modifier attributes + DWORD typeIdx; ///< Index of modified type +}; + +/// @brief Type string for Code View LF_POINTER +struct CVtsPointer { + WORD attribute; ///< Pointer attributes. + WORD typeIdx; ///< Type index of the object pointed to. +}; + +/// @brief Type string for TDS LF_POINTER +struct TDStsPointer { + WORD attribute; ///< Pointer attributes. + DWORD typeIdx; ///< Type index of the object pointed to. +}; + +/// @brief Type string for Code View LF_ARRAY. +/// +/// The complete type string consists of the CVtsArray structure followed +/// by a length-prefixed name. +struct CVtsArray { + WORD elemTypeIdx; ///< Type index of the array elements + WORD idxTypeIdx; ///< Type index of the index variable +}; + +/// @brief Type string for TDS LF_ARRAY +struct TDStsArray { + DWORD elemTypeIdx; ///< Type index of the array elements + DWORD idxTypeIdx; ///< Type index of the index variable + DWORD nameIdx; + WORD size; + WORD numElements; +}; + +/// @brief Type string for Code View LF_CLASS (and LF_STRUCTURE) +/// +/// The complete type string consists of the CVtsClass structure followed +/// by a numeric leaf specifying the size of the class in bytes and the +/// length-prefixed name of the type (in that order). +struct CVtsClass { + WORD count; ///< Number of elements in the class or structure. + WORD fieldTypeIdx; ///< Type index of the field list of this class. + WORD property; ///< Property bit field. + WORD dList; ///< Type index of derivation list. + WORD vShape; ///< Type index of the virtual function table shape desc. +}; + +/// @brief Type string for TDS LF_CLASS (and LF_STRUCTURE) +/// +/// The complete type string consists of the CVtsClass structure followed +/// by a numeric leaf specifying the size of the class in bytes. +struct TDStsClass { + WORD count; ///< Number of elements in the class or structure. + DWORD fieldTypeIdx;///< Type index of the field list of this class. + WORD property; ///< Property bit field. + DWORD cClass; ///< Index of containing class. + DWORD dList; ///< Type index of derivation list. + DWORD vShape; ///< Type index of the virtual function table shape desc. + DWORD nameIdx; ///< Index of class name. +}; + +/// @brief Type string for Code View LF_PROCEDURE +struct CVtsProcedure { + WORD rvTypeIdx; ///< Type index of the return value. + BYTE callConv; ///< Calling convention. + BYTE reserved; ///< Reserved. + WORD parms; ///< Number of parameters. + WORD argList; ///< Type index of argument list. +}; + +/// @brief Type string for TDS LF_PROCEDURE +struct TDStsProcedure { + DWORD rvTypeIdx; ///< Type index of the return value. + BYTE callConv; ///< Calling convention. + BYTE reserved; ///< Reserved. + WORD parms; ///< Number of parameters. + DWORD argList; ///< Type index of argument list. +}; + +/// @brief Type string for Code View LF_MFUNCTION +struct CVtsMFunction { + WORD rvTypeIdx; ///< Type index of the return value. + WORD classIdx; ///< Type index of the containing class. + WORD thisIdx; ///< Type index of the this parameter. + BYTE callConv; ///< Calling convention. + BYTE reserved; ///< Reserved. + WORD parms; ///< Number of parameters. + WORD argList; ///< Type index of argument list. + DWORD thisAdjust; ///< Logical this adjuster. +}; + +/// @brief Type string for TDS LF_MFUNCTION +struct TDStsMFunction { + DWORD rvTypeIdx; ///< Type index of the return value. + DWORD classIdx; ///< Type index of the containing class. + DWORD thisIdx; ///< Type index of the this parameter. + BYTE callConv; ///< Calling convention. + BYTE reserved; ///< Reserved. + WORD parms; ///< Number of parameters. + DWORD argList; ///< Type index of argument list. + DWORD thisAdjust; ///< Logical this adjuster. +}; + +/// @defgroup MemberAttrBitField Member Attribute Bit Field +/// @{ + +/// @brief Mask for the access attribute +#define MEMBATTR_ACCESS_MASK 0x0003 + +/// @brief Value of the access attribute if the method has no access protection +#define MEMBATTR_NOACCPROT 0x0000 + +/// @brief Value of the access attribute if the method offers private access +#define MEMBATTR_PRIVATE 0x0001 + +/// @brief Value of the access attribute if the method offers protected access +#define MEMBATTR_PROTECTED 0x0002 + +/// @brief Value of the access attribute if the method offers public access +#define MEMBATTR_PUBLIC 0x0003 + +/// @brief Mask for the method properties +#define MEMBATTR_PROP_MASK 0x001C + +/// @brief Value of the method properties for a vanilla method +#define MEMBATTR_PROP_VANILLA 0x0000 + +/// @brief Value of the method properties for a virtual method +#define MEMBATTR_PROP_VIRTUAL 0x0004 + +/// @brief Value of the method properties for a static method +#define MEMBATTR_PROP_STATIC 0x0008 + +/// @brief Value of the method properties for a friend method +#define MEMBATTR_PROP_FRIEND 0x000C + +/// @brief Value of the method properties for an "introducing" virtual method +#define MEMBATTR_PROP_INTVIRT 0x0010 + +/// @brief Value of the method properties for a pure virtual method +#define MEMBATTR_PROP_PUREV 0x0014 + +/// @brief Value of the method properties for an "introducing" pure virtual +/// method. +#define MEMBATTR_PROP_INTPURE 0x0018 + +/// @brief Set if method is never instantiated by the compiler. +#define MEMBATTR_PSEUDO 0x0020 + +/// @brief Set if class cannot be inherited +#define MEMBATTR_NONINHERIT 0x0040 + +/// @brief Set if class cannot be constructed +#define MEMBATTR_NOCONSTRUCT 0x0080 + +/// @brief Set if this is an overloaded operator +#define MEMBATTR_OVLOP 0x0100 + +/// @brief Set if this is a type conversion operator +#define MEMBATTR_CNVOP 0x0200 + +/// @brief Set if this is a constructor +#define MEMBATTR_ISCTOR 0x0400 + +/// @brief Set if this is a destructor +#define MEMBATTR_ISDTOR 0x0800 + +/// @brief Set if this is a compiler-created method +#define MEMBATTR_ISBLTIN 0x1000 + +/// @brief Set if this is a bitfield member +#define MEMBATTR_ISBITFIELD 0x2000 + +/// @} + +/// @brief Type string element for Code View LF_MLIST +/// +/// The full type string is a sequence of CVtsMListElem structures, +/// each optionally followed by a 4-byte vtaboffset. +struct CVtsMListElem { + WORD attribute; ///< Member function attributes. + WORD typeIdx; ///< Type index of member function signature. +}; + +/// @brief Type string element for TDS LF_MLIST +/// +/// The full type string is a sequence of TDStsMListElem structures, +/// each optionally followed by a 4-byte vtaboffset. +struct TDStsMListElem { + WORD attribute; ///< Member function attributes. + DWORD typeIdx; ///< Type index of member function signature. + DWORD brwsOffs; ///< Browser offset. +}; + +/// @brief Type string for TDS files. +struct TDSTypeString { + WORD leaf; ///< Leaf identifier (see CVLeafId). + union { + TDStsModifier modifier; ///< Type string for a modifier + TDStsPointer pointer; ///< Type string for a pointer. + TDStsArray array; ///< Type string for an array. + TDStsClass cls; ///< Type string for a class. + TDStsProcedure procedure; ///< Type string for a procedure. + TDStsMFunction mFunction; ///< Type string for a member function. + }; +}; + +/// @brief Type string for Code View files +struct CVTypeString { + WORD leaf; ///< Leaf indentifier (see CVLeafId). + union { + CVtsModifier modifier; ///< Type string for a modifier. + CVtsPointer pointer; ///< Type string for a pointer. + CVtsArray array; ///< Type string for an array. + CVtsClass cls; ///< Type string for a class. + CVtsProcedure procedure; ///< Type string for a procedure. + CVtsMFunction mFunction; ///< Type string for a member function. + }; +}; + +/// @brief The kind of symbol described in the payload of a symbol record. +enum CVSymKind { + CVskCompile = 0x0001, + CVskRegister = 0x0002, + CVskUDT = 0x0004, + CVskSSearch = 0x0005, + CVskEnd = 0x0006, + CVskSkip = 0x0007, + CVskGProcRef = 0x0020, + CVskGDataRef = 0x0021, + CVskNamespace = 0x0025, + CVskUsing = 0x0026, + CVskBPRel = 0x0200, + CVskLocalData = 0x0201, + CVskGlobalData = 0x0202, + CVskPublic = 0x0203, + CVskLocalProc = 0x0204, + CVskGlobalProc = 0x0205, + CVskBlock32 = 0x0207, + CVskEntry32 = 0x0210, + CVskOptVar32 = 0x0211, // Variable life range support. + CVskProcRet32 = 0x0212, // Procedure return epilogue. + CVskSaveRegs32 = 0x0213, // ??? + CVskProcRef = 0x0400, + CVskDataRef = 0x0401, + CVskAlign = 0x0402 +}; + +/// @brief Header of the Code View symbol record +struct CVsymRecHdr { + + /// @brief Default constructor + CVsymRecHdr() : len(1), symKind(CVskSkip) {} + + /// @brief Constructor + CVsymRecHdr(WORD len_, WORD symKind_) : len(len_), symKind(symKind_) {} + + WORD len; ///< Length excluding length field itself + WORD symKind; ///< Kind of symbol (see CVSymKind) +}; + +/// @brief Header of the payload of the Code View and TDS Compile Flag +/// symbol record. +struct CVsrCompileFlagPayloadHdr { + BYTE machine; ///< Target processor enum. + BYTE language; ///< Language enum. + WORD flags; ///< Misc flags. +}; + +/// @brief Header of the payload of the Code View Register symbol record +/// +/// @note Register tracking information is assumed to be absent. +struct CVsrRegisterPayloadHdr { + WORD typeIdx; ///< Type index. + WORD regId; ///< Register identifier. +}; + +/// @brief Payload of the TDS Register symbol record { +struct TDSsrRegisterPayload { + DWORD typeIdx; ///< Type index. + WORD regId; ///< Register indentifier. + DWORD nameIdx; ///< Index of symbol name. + WORD brwsOffs; ///< Browser info offset. +}; + +/// @brief Header of the payload of the Code View User Defined Type +/// symbol record +struct CVsrUserDefinedTypePayloadHdr { + WORD typeIdx; ///< Type index +}; + +/// @brief The payload of the TDS User Defined Type symbol record +struct TDSsrUserDefinedTypePayload { + DWORD typeIdx; ///< Type index + WORD flags; ///< 0: on=tag,off=typedef 1: on=nested + DWORD nameIdx; ///< Index of the types name in sstNames + DWORD brwsInfoOffs; ///< Browser info offset +}; + +/// @brief The payload of the Code View Start Search Symbol Record +struct CVsrStartSearchPayload { + DWORD offs; ///< Offset of first code within segment + WORD segment; ///< Segment (PE section) +}; + +/// @brief The payload of the TDS Start Search Symbol Record +struct TDSsrStartSearchPayload : public CVsrStartSearchPayload { + WORD codeSyms; ///< Number of code symbols. + WORD dataSyms; ///< Number of data symbols. + DWORD firstData; ///< Offset of first data within segment + WORD dummy; ///< Currently not understood what this is used for. +}; + +/// @brief The payload of the Code View Procedure Reference Record +struct CVsrProcRefPayload { + DWORD checksum; ///< Checksum. + DWORD offSymRef; /**< Offset of symbol record refered to relative to the + symbol table for the module (sstAlignSym). */ + WORD module; /**< Index of module that contains the symbol record for + the procedure. */ +}; + +/// @brief The payload of the TDS Global Procedure Reference Record +struct TDSsrGlobalProcRefPayload { + DWORD offSymRef; /**< Offset of symbol record referred to relative to + the FB0A signature. Most often 0. */ + DWORD typeIdx; ///< Type index. 0 if none. + DWORD nameIdx; ///< Index of name in sstNames + DWORD brwsInfoOffset; ///< Browser info offset? + DWORD offset; /**< Address offset of proc or name index of + DLL if segment==0xFFFF. */ + WORD segment; ///< Segment. + DWORD linkNameIdx; ///< Index of linker name, if segment=0xFFFF? +}; + +/// @brief The payload of the Code View Data Reference Record +typedef CVsrProcRefPayload CVsrDataRefPayload; + +struct TDSsrGlobalDataRefPayload { + DWORD offSymRef; /**< Always 0. */ + DWORD typeIdx; ///< Type index. 0 if none. + DWORD nameIdx; ///< Index of name in sstNames + DWORD brwsInfoOffset; ///< Browser info offset? + DWORD offset; /**< Address offset of proc or name index of + DLL if segment==0xFFFF. */ + WORD segment; ///< Segment. +}; + +/// @brief The header of the payload of the Code View Global Data Symbol Record +struct CVsrGlobalDataPayloadHdr { + DWORD offset; ///< Address offset + WORD segment; ///< Segment + WORD typeIdx; ///< Index of type +}; + +/// @brief The payload of the TDS Global Data symbol record +struct TDSsrGlobalDataPayload { + DWORD offset; ///< Address offset + WORD segment; ///< Segment + WORD flags; ///< 1=TLS var, 0=Not a TLS var.? + DWORD typeIdx; ///< Index of type + DWORD nameIdx; ///< Index of name in sstNames. + DWORD brwsInfoOffs; ///< Browser info offset? +}; + +/// @brief The header of the payload of the Code View BP Relative symbol record +struct CVsrBPRelativePayloadHdr { + DWORD offset; ///< Signed offset relative to BP. + WORD typeIdx; ///< Index of the type. +}; + +/// @brief The payload of the TDS BP Relative symbol record +struct TDSsrBPRelativePayload { + DWORD offset; ///< Signed offset relative to BP. + DWORD typeIdx; ///< Index of the type. + DWORD nameIdx; ///< Index of the symbol name. + DWORD brwsOffs; ///< Browser info offset. +}; + +/// @brief The header of the payload of the Code View Local Proc symbol record +struct CVsrLocalProcPayloadHdr { + DWORD parent; ///< Parent of procedure relative to subsection. + DWORD end; ///< End of procedure relative to subsection. + DWORD next; ///< Next procedure relative to subsection. + DWORD procLen; ///< Length of procedure in bytes. + DWORD dbgStart; ///< Offset of code where stack frame is ok. + DWORD dbgEnd; ///< Offset of code where return value is ok. + DWORD offset; ///< Offset of 1st procedure code. + WORD segment; ///< Segment of procedure code. + WORD typeIdx; ///< Index of type (procedure signature) + BYTE flags; ///< Procedure flags +}; + +/// @brief The payload of the TDS Local Proc symbol record. +/// +/// The structure is appended with a length-prefixed linker name of the +/// procedure. +struct TDSsrLocalProcPayloadHdr { + DWORD parent; ///< Parent of procedure relative to subsection. + DWORD end; ///< End of procedure relative to subsection. + DWORD next; ///< Next procedure relative to subsection. + DWORD procLen; ///< Length of procedure in bytes. + DWORD dbgStart; ///< Offset of code where stack frame is ok. + DWORD dbgEnd; ///< Offset of code where return value is ok. + DWORD offset; ///< Offset of 1st procedure code. + WORD segment; ///< Segment of procedure code. ??? + WORD flags; ///< Not sure whether this is correct! + DWORD typeIdx; ///< Index of type (procedure signature) ??? + DWORD nameIdx; ///< Index of procedure name. + DWORD brwsOffs; ///< Browser info offset (not sure). +}; + +/// @brief The payload header of the DBG Block Start symbol record. +/// +/// The structure is appended with a length-prefixed expression string. +struct CVsrBlock32PayloadHdr { + DWORD parent; ///< Parent of block relative to subsection. + DWORD end; ///< End of block relative to subsection. + DWORD next; ///< Next block relative to subsection. + DWORD len; ///< Length of block in bytes. + DWORD offset; ///< Offset of block. + WORD segment; ///< Segment of block. +}; + +/// @brief The payload of the TDS Block Start symbol record. +struct TDSsrBlock32Payload : public CVsrBlock32PayloadHdr { + DWORD nameIdx; ///< Name index of expression string. +}; + +#ifdef __BORLANDC__ +#pragma option pop +#endif + +#endif + +// Local Variables: +// mode:c++ +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: diff --git a/common/windows/delphi/ext/tds2dbg/testa.cpp b/common/windows/delphi/ext/tds2dbg/testa.cpp new file mode 100644 index 0000000000..d3f55dadcc --- /dev/null +++ b/common/windows/delphi/ext/tds2dbg/testa.cpp @@ -0,0 +1,27 @@ +#include +#include + +static int a = 1; + +int main(int argc, char* argv[]) +{ + (void) argc; (void) argv; + + int b = a+1; + + printf("%i %i\n",a,b); + + while(a<0xf0000000) { + ++a; + printf("%i\n",a++); + Sleep(1000); + } + + return 0; +} + +// Local Variables: +// mode:c++ +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: diff --git a/developer/src/kmconvert/build.sh b/developer/src/kmconvert/build.sh index 75c9210546..e18977f05b 100755 --- a/developer/src/kmconvert/build.sh +++ b/developer/src/kmconvert/build.sh @@ -29,10 +29,11 @@ function do_build() { build_manifest.res run_in_vs_env rc icons.rc delphi_msbuild kmconvert.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/kmconvert.map" "$WIN32_TARGET" + sentrytool_delphiprep "$WIN32_TARGET" kmconvert.dpr + tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM" - cp_if_exists "$WIN32_TARGET_PATH/kmconvert.pdb" "$DEVELOPER_DEBUGPATH" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmconvert.dbg" "$DEVELOPER_DEBUGPATH" rm -rf "$DEVELOPER_PROGRAM/projects/templates" mkdir -p "$DEVELOPER_PROGRAM/projects/templates" @@ -52,12 +53,7 @@ function do_publish() { wrap-signcode //d "Keyman Developer" "$DEVELOPER_PROGRAM/kmconvert.exe" wrap-symstore "$DEVELOPER_PROGRAM/kmconvert.exe" //t keyman-developer - wrap-symstore "$DEVELOPER_DEBUGPATH/kmconvert.pdb" //t keyman-developer -} - -function do_install() { - cp "$DEVELOPER_PROGRAM/kmconvert.exe" "$INSTALLPATH_KEYMANDEVELOPER/kmconvert.exe" - cp_if_exists "$DEVELOPER_DEBUGPATH/kmconvert.pdb" "$INSTALLPATH_KEYMANDEVELOPER/kmconvert.pdb" + wrap-symstore "$DEVELOPER_DEBUGPATH/kmconvert.dbg" //t keyman-developer } builder_run_action clean:project clean_windows_project_files @@ -65,5 +61,5 @@ builder_run_action configure:project configure_windows_build_environment builder_run_action build:project do_build builder_run_action test:project do_test builder_run_action publish:project do_publish -builder_run_action install:project do_install +builder_run_action install:project cp "$DEVELOPER_PROGRAM/kmconvert.exe" "$INSTALLPATH_KEYMANDEVELOPER/kmconvert.exe" builder_run_action edit:project start kmconvert.dproj diff --git a/developer/src/kmdbrowserhost/build.sh b/developer/src/kmdbrowserhost/build.sh index a1a6ecc58d..8996b17bc4 100755 --- a/developer/src/kmdbrowserhost/build.sh +++ b/developer/src/kmdbrowserhost/build.sh @@ -28,10 +28,11 @@ function do_build() { build_version.res build_manifest.res delphi_msbuild kmdbrowserhost.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/kmdbrowserhost.map" "$WIN32_TARGET" + sentrytool_delphiprep "$WIN32_TARGET" kmdbrowserhost.dpr + tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM" - cp_if_exists "$WIN32_TARGET_PATH/kmdbrowserhost.pdb" "$DEVELOPER_DEBUGPATH" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmdbrowserhost.dbg" "$DEVELOPER_DEBUGPATH" } function do_publish() { @@ -40,12 +41,7 @@ function do_publish() { wrap-signcode //d "Keyman Developer" "$DEVELOPER_PROGRAM/kmdbrowserhost.exe" wrap-symstore "$DEVELOPER_PROGRAM/kmdbrowserhost.exe" //t keyman-developer - wrap-symstore "$DEVELOPER_DEBUGPATH/kmdbrowserhost.pdb" //t keyman-developer -} - -function do_install() { - cp "$DEVELOPER_PROGRAM/kmdbrowserhost.exe" "$INSTALLPATH_KEYMANDEVELOPER/kmdbrowserhost.exe" - cp_if_exists "$DEVELOPER_DEBUGPATH/kmdbrowserhost.pdb" "$INSTALLPATH_KEYMANDEVELOPER/kmdbrowserhost.pdb" + wrap-symstore "$DEVELOPER_DEBUGPATH/kmdbrowserhost.dbg" //t keyman-developer } builder_run_action clean:project clean_windows_project_files @@ -53,6 +49,6 @@ builder_run_action configure:project configure_windows_build_environment builder_run_action build:project do_build # builder_run_action test:project do_test builder_run_action publish:project do_publish -builder_run_action install:project do_install +builder_run_action install:project cp "$DEVELOPER_PROGRAM/kmdbrowserhost.exe" "$INSTALLPATH_KEYMANDEVELOPER/kmdbrowserhost.exe" builder_run_action edit:project start kmdbrowserhost.dproj diff --git a/developer/src/setup/build.sh b/developer/src/setup/build.sh index da16c4e574..1e4ab94a68 100755 --- a/developer/src/setup/build.sh +++ b/developer/src/setup/build.sh @@ -30,32 +30,11 @@ function do_build() { build_version.res build_manifest.res delphi_msbuild setup.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/setup.map" "$WIN32_TARGET" - - # - # The installer needs to be a multiple of 512 bytes in order for the SFX code - # to find the ZIP header. After map2pdb appends its header, the file may not - # be the right size. So take the file size of setup.exe and generate a nul - # file that would expand it to nearest 512 bytes, and append it to the - # executable. This will make the file 512 bytes larger if its size is already - # on a 512 byte boundary. - # - - local file_size expansion_size expansion_file - - file_size=$(stat -c %s "$WIN32_TARGET") - expansion_size=$(( 512 - ( $file_size % 512 ) )) - expansion_file="$(mktemp)" - dd if=/dev/zero of="${expansion_file}" bs=1 count="${expansion_size}" - cat "$WIN32_TARGET" "$expansion_file" > "$WIN32_TARGET.out" - mv -f "$WIN32_TARGET.out" "$WIN32_TARGET" - rm -f "${expansion_file}" - - + sentrytool_delphiprep "$WIN32_TARGET" setup.dpr + tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM" - cp_if_exists "$WIN32_TARGET_PATH/setup.pdb" "$DEVELOPER_DEBUGPATH/devsetup.pdb" - # builder_if_release_build_level cp "$WIN32_TARGET_PATH/setup.pdb" "$DEVELOPER_DEBUGPATH/devsetup.pdb" - # builder_if_release_build_level mv "$WIN32_TARGET_PATH/setup.pdb" "$WIN32_TARGET_PATH/devsetup.pdb" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/setup.dbg" "$DEVELOPER_DEBUGPATH/devsetup.dbg" + builder_if_release_build_level mv "$WIN32_TARGET_PATH/setup.dbg" "$WIN32_TARGET_PATH/devsetup.dbg" } function do_publish() { @@ -63,7 +42,7 @@ function do_publish() { # Not signing here because we need to sign the bundled installer instead # wrap-signcode /d "Keyman Developer Setup" $(DEVELOPER_PROGRAM)\setup.exe wrap-symstore "$DEVELOPER_PROGRAM/setup.exe" //t keyman-developer - wrap-symstore "$DEVELOPER_DEBUGPATH/setup.pdb" //t keyman-developer + wrap-symstore "$DEVELOPER_DEBUGPATH/setup.dbg" //t keyman-developer } builder_run_action clean:project clean_windows_project_files diff --git a/developer/src/tike/build.sh b/developer/src/tike/build.sh index ceec4b4428..15e2add3c4 100755 --- a/developer/src/tike/build.sh +++ b/developer/src/tike/build.sh @@ -68,15 +68,16 @@ function do_build() { cp -R "$DEVELOPER_ROOT/src/tike/xml/"* "$DEVELOPER_PROGRAM/xml/" delphi_msbuild tike.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/tike.map" "$WIN32_TARGET" + sentrytool_delphiprep "$WIN32_TARGET" tike.dpr + tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM" - cp_if_exists "$WIN32_TARGET_PATH/tike.pdb" "$DEVELOPER_DEBUGPATH" cp kmc.cmd "$DEVELOPER_PROGRAM" cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_DLL" "$DEVELOPER_PROGRAM" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/tike.dbg" "$DEVELOPER_DEBUGPATH" cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_DLL" "$WIN32_TARGET_PATH" - builder_if_release_build_level cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_PDB" "$DEVELOPER_DEBUGPATH" + builder_if_release_build_level cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_PDB" "$WIN32_TARGET_PATH" cp "$KEYMAN_ROOT/common/windows/delphi/ext/sentry/sentry.dll" "$DEVELOPER_PROGRAM/" cp "$KEYMAN_ROOT/common/windows/delphi/ext/sentry/sentry.x64.dll" "$DEVELOPER_PROGRAM/" @@ -95,12 +96,11 @@ function do_publish() { wrap-signcode //d "Keyman Developer" "$DEVELOPER_PROGRAM/sentry.x64.dll" wrap-signcode //d "Keyman Developer" "$DEVELOPER_PROGRAM/crashpad_handler.exe" wrap-symstore "$DEVELOPER_PROGRAM/tike.exe" //t keyman-developer - wrap-symstore "$DEVELOPER_DEBUGPATH/tike.pdb" //t keyman-developer + wrap-symstore "$DEVELOPER_DEBUGPATH/tike.dbg" //t keyman-developer } function do_install() { cp "$DEVELOPER_PROGRAM/tike.exe" "$INSTALLPATH_KEYMANDEVELOPER/tike.exe" - cp_if_exists "$DEVELOPER_DEBUGPATH/tike.pdb" "$INSTALLPATH_KEYMANDEVELOPER/tike.pdb" cp "$DEVELOPER_PROGRAM/$KEYMANCORE_DLL" "$INSTALLPATH_KEYMANDEVELOPER/$KEYMANCORE_DLL" } diff --git a/resources/build/win/delphi_flags.inc.sh b/resources/build/win/delphi_flags.inc.sh index 441aa0f964..087e88c60f 100644 --- a/resources/build/win/delphi_flags.inc.sh +++ b/resources/build/win/delphi_flags.inc.sh @@ -30,4 +30,5 @@ DEVELOPER_DEBUGPATH="$DEVELOPER_ROOT/debug" # Delphi build tool macros SENTRYTOOL="$COMMON_ROOT/tools/sentrytool/$WIN32_TARGET_PATH/sentrytool.exe" +TDS2DBG="$KEYMAN_ROOT/common/windows/bin/tools/tds2dbg" DEVTOOLS="$COMMON_ROOT/tools/devtools/$WIN32_TARGET_PATH/devtools.exe" diff --git a/resources/build/win/environment.inc.sh b/resources/build/win/environment.inc.sh index 22ecf419fa..37a055d001 100644 --- a/resources/build/win/environment.inc.sh +++ b/resources/build/win/environment.inc.sh @@ -80,25 +80,23 @@ run_in_delphi_env() { ) } -# -# If map2pdb is on the path, then run it. Note: we do not include it in the repo -# because it can have false positives in malware scans. map2pdb is used to -# generate .pdb files for Delphi-generated executables, which can then be used -# in debuggers such as WinDbg, or profilers such as VTune. -# -do_map2pdb() { - if hash map2pdb 2>/dev/null; then - map2pdb "$1" -bind:"$2" +sentrytool_delphiprep() { + if builder_is_ci_build && builder_is_ci_build_level_build; then + builder_echo "Skipping sentrytool_delphiprep - buildLevel=build: $@" + return 0 fi + + local EXE_PATH="$1" + local DPR_PATH="$2" + ( + builder_echo heading "### delphi: sentrytool_delphiprep $1 $2" + source "$KEYMAN_ROOT/resources/build/win/delphi_environment.inc.sh" + "$SENTRYTOOL" delphiprep -r "$KEYMAN_ROOT" -i "$DELPHIINCLUDES" "$(cygpath -w "$EXE_PATH")" -dpr "$DPR_PATH" + ) } -# -# If the first parameter is a file that exists, do the copy, otherwise, no-op. -# -cp_if_exists() { - if [[ -f "$1" ]]; then - cp "$@" - fi +tds2dbg() { + builder_if_release_build_level "$TDS2DBG" "$@" } delphi_msbuild() { diff --git a/windows/src/desktop/inst/build.sh b/windows/src/desktop/inst/build.sh index 14da0b7427..8e64342a22 100755 --- a/windows/src/desktop/inst/build.sh +++ b/windows/src/desktop/inst/build.sh @@ -90,6 +90,10 @@ function do_publish() { rm -f setup.inf cat "$WINDOWS_PROGRAM_APP/setup-redist.exe" keymandesktop.zip > keymandesktop.exe rm -f keymandesktop.zip + + # + # Sign the installer + # wrap-signcode //d "Keyman for Windows" keymandesktop.exe builder_if_release_build_level copy-installer diff --git a/windows/src/desktop/insthelp/build.sh b/windows/src/desktop/insthelp/build.sh index 0a84eeefe5..d57a4b935f 100755 --- a/windows/src/desktop/insthelp/build.sh +++ b/windows/src/desktop/insthelp/build.sh @@ -29,10 +29,11 @@ function do_build() { # TODO: why no manifest? # build_manifest.res delphi_msbuild insthelp.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/insthelp.map" "$WIN32_TARGET" + sentrytool_delphiprep "$WIN32_TARGET" insthelp.dpr + tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP" - cp_if_exists "$WIN32_TARGET_PATH/insthelp.pdb" "$WINDOWS_DEBUGPATH_APP" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/insthelp.dbg" "$WINDOWS_DEBUGPATH_APP/insthelp.dbg" } function do_publish() { @@ -42,7 +43,7 @@ function do_publish() { wrap-signcode //d "Keyman for Windows Install Helper" "$WINDOWS_PROGRAM_APP/insthelp.exe" wrap-symstore "$WINDOWS_PROGRAM_APP/insthelp.exe" //t keyman-windows - wrap-symstore "$WINDOWS_DEBUGPATH_APP/insthelp.pdb" //t keyman-windows + wrap-symstore "$WINDOWS_DEBUGPATH_APP/insthelp.dbg" //t keyman-windows } builder_run_action clean:project clean_windows_project_files diff --git a/windows/src/desktop/kmbrowserhost/build.sh b/windows/src/desktop/kmbrowserhost/build.sh index 4c405f1c31..ab309a09b0 100755 --- a/windows/src/desktop/kmbrowserhost/build.sh +++ b/windows/src/desktop/kmbrowserhost/build.sh @@ -28,10 +28,11 @@ function do_build() { build_version.res build_manifest.res delphi_msbuild kmbrowserhost.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/kmbrowserhost.map" "$WIN32_TARGET" + sentrytool_delphiprep "$WIN32_TARGET" kmbrowserhost.dpr + tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP" - cp_if_exists "$WIN32_TARGET_PATH/kmbrowserhost.pdb" "$WINDOWS_DEBUGPATH_APP" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmbrowserhost.dbg" "$WINDOWS_DEBUGPATH_APP/kmbrowserhost.dbg" } function do_publish() { @@ -40,12 +41,7 @@ function do_publish() { wrap-signcode //d "Keyman for Windows" "$WINDOWS_PROGRAM_APP/kmbrowserhost.exe" wrap-symstore "$WINDOWS_PROGRAM_APP/kmbrowserhost.exe" //t keyman-windows - wrap-symstore "$WINDOWS_DEBUGPATH_APP/kmbrowserhost.pdb" //t keyman-windows -} - -function do_install() { - cp "$WINDOWS_PROGRAM_APP/kmbrowserhost.exe" "$INSTALLPATH_KEYMANAPP/kmbrowserhost.exe" - cp_if_exists "$WINDOWS_DEBUGPATH_APP/kmbrowserhost.pdb" "$INSTALLPATH_KEYMANAPP/kmbrowserhost.pdb" + wrap-symstore "$WINDOWS_DEBUGPATH_APP/kmbrowserhost.dbg" //t keyman-windows } builder_run_action clean:project clean_windows_project_files @@ -53,5 +49,5 @@ builder_run_action configure:project configure_windows_build_environment builder_run_action build:project do_build # builder_run_action test:project do_test builder_run_action publish:project do_publish -builder_run_action install:project do_install +builder_run_action install:project cp "$WINDOWS_PROGRAM_APP/kmbrowserhost.exe" "$INSTALLPATH_KEYMANAPP/kmbrowserhost.exe" builder_run_action edit:project start kmbrowserhost.dproj diff --git a/windows/src/desktop/kmconfig/build.sh b/windows/src/desktop/kmconfig/build.sh index 2ba87c1082..bfe07e5868 100755 --- a/windows/src/desktop/kmconfig/build.sh +++ b/windows/src/desktop/kmconfig/build.sh @@ -28,10 +28,11 @@ function do_build() { build_version.res build_manifest.res delphi_msbuild kmconfig.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/kmconfig.map" "$WIN32_TARGET" + sentrytool_delphiprep "$WIN32_TARGET" kmconfig.dpr + tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP" - cp_if_exists "$WIN32_TARGET_PATH/kmconfig.pdb" "$WINDOWS_DEBUGPATH_APP" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmconfig.dbg" "$WINDOWS_DEBUGPATH_APP/kmconfig.dbg" } function do_publish() { @@ -40,12 +41,7 @@ function do_publish() { wrap-signcode //d "Keyman for Windows" "$WINDOWS_PROGRAM_APP/kmconfig.exe" wrap-symstore "$WINDOWS_PROGRAM_APP/kmconfig.exe" //t keyman-windows - wrap-symstore "$WINDOWS_DEBUGPATH_APP/kmconfig.pdb" //t keyman-windows -} - -function do_install() { - cp "$WINDOWS_PROGRAM_APP/kmconfig.exe" "$INSTALLPATH_KEYMANAPP/kmconfig.exe" - cp_if_exists "$WINDOWS_DEBUGPATH_APP/kmconfig.pdb" "$INSTALLPATH_KEYMANAPP/kmconfig.pdb" + wrap-symstore "$WINDOWS_DEBUGPATH_APP/kmconfig.dbg" //t keyman-windows } builder_run_action clean:project clean_windows_project_files @@ -53,5 +49,5 @@ builder_run_action configure:project configure_windows_build_environment builder_run_action build:project do_build # builder_run_action test:project do_test builder_run_action publish:project do_publish -builder_run_action install:project do_install +builder_run_action install:project cp "$WINDOWS_PROGRAM_APP/kmconfig.exe" "$INSTALLPATH_KEYMANAPP/kmconfig.exe" builder_run_action edit:project start kmconfig.dproj diff --git a/windows/src/desktop/kmshell/build.sh b/windows/src/desktop/kmshell/build.sh index b2147962eb..03d4b786fa 100755 --- a/windows/src/desktop/kmshell/build.sh +++ b/windows/src/desktop/kmshell/build.sh @@ -35,10 +35,11 @@ function do_build() { build_manifest.res run_in_vs_env rc icons.rc delphi_msbuild kmshell.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/kmshell.map" "$WIN32_TARGET" + sentrytool_delphiprep "$WIN32_TARGET" kmshell.dpr + tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP" - cp_if_exists "$WIN32_TARGET_PATH/kmshell.pdb" "$WINDOWS_DEBUGPATH_APP" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmshell.dbg" "$WINDOWS_DEBUGPATH_APP/kmshell.dbg" do_build_data } @@ -72,13 +73,11 @@ function do_publish() { wrap-signcode //d "Keyman for Windows" "$WINDOWS_PROGRAM_APP/kmshell.exe" wrap-symstore "$WINDOWS_PROGRAM_APP/kmshell.exe" //t keyman-windows - wrap-symstore "$WINDOWS_DEBUGPATH_APP/kmshell.pdb" //t keyman-windows + wrap-symstore "$WINDOWS_DEBUGPATH_APP/kmshell.dbg" //t keyman-windows } function do_install() { cp "$WINDOWS_PROGRAM_APP/kmshell.exe" "$INSTALLPATH_KEYMANAPP/kmshell.exe" - cp_if_exists "$WINDOWS_DEBUGPATH_APP/kmshell.pdb" "$INSTALLPATH_KEYMANAPP/kmshell.pdb" - rm -rf "$INSTALLPATH_KEYMANAPP/xml" mkdir -p "$INSTALLPATH_KEYMANAPP/xml" cp -r "$WINDOWS_PROGRAM_APP/xml"/* "$INSTALLPATH_KEYMANAPP/xml/" diff --git a/windows/src/desktop/setup/build.sh b/windows/src/desktop/setup/build.sh index 57e03de7ac..5b97dda8ba 100755 --- a/windows/src/desktop/setup/build.sh +++ b/windows/src/desktop/setup/build.sh @@ -37,32 +37,13 @@ function do_build() { "$DEVTOOLS" -buildsetupstrings locale\\ .\\ delphi_msbuild setup.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/setup.map" "$WIN32_TARGET" - - # - # The installer needs to be a multiple of 512 bytes in order for the SFX code - # to find the ZIP header. After map2pdb appends its header, the file may not - # be the right size. So take the file size of setup.exe and generate a nul - # file that would expand it to nearest 512 bytes, and append it to the - # executable. This will make the file 512 bytes larger if its size is already - # on a 512 byte boundary. - # - - local file_size expansion_size expansion_file - - file_size=$(stat -c %s "$WIN32_TARGET") - expansion_size=$(( 512 - ( $file_size % 512 ) )) - expansion_file="$(mktemp)" - dd if=/dev/zero of="${expansion_file}" bs=1 count="${expansion_size}" - cat "$WIN32_TARGET" "$expansion_file" > "$WIN32_TARGET.out" - mv -f "$WIN32_TARGET.out" "$WIN32_TARGET" - rm -f "${expansion_file}" + sentrytool_delphiprep "$WIN32_TARGET" setup.dpr + tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP" - cp_if_exists "$WIN32_TARGET_PATH/setup.pdb" "$WINDOWS_DEBUGPATH_APP" - # setup-redist.exe does not get signed as it is intended for a bundled installer cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP/setup-redist.exe" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/setup.dbg" "$WINDOWS_DEBUGPATH_APP/setup.dbg" } function do_build_debug_manifest() { @@ -80,7 +61,7 @@ function do_publish() { wrap-signcode //d "Keyman for Windows Setup" "$WINDOWS_PROGRAM_APP/setup.exe" wrap-symstore "$WINDOWS_PROGRAM_APP/setup.exe" //t keyman-windows - wrap-symstore "$WINDOWS_DEBUGPATH_APP/setup.pdb" //t keyman-windows + wrap-symstore "$WINDOWS_DEBUGPATH_APP/setup.dbg" //t keyman-windows } builder_run_action clean:project do_clean diff --git a/windows/src/engine/insthelper/build.sh b/windows/src/engine/insthelper/build.sh index 5f8a4b2eb8..62aec62586 100755 --- a/windows/src/engine/insthelper/build.sh +++ b/windows/src/engine/insthelper/build.sh @@ -27,17 +27,18 @@ function do_build() { create-windows-output-folders build_version.res delphi_msbuild insthelper.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/insthelper.map" "$WIN32_TARGET" + sentrytool_delphiprep "$WIN32_TARGET" insthelper.dpr + tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE" - cp_if_exists "$WIN32_TARGET_PATH/insthelper.pdb" "$WINDOWS_DEBUGPATH_ENGINE" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/insthelper.dbg" "$WINDOWS_DEBUGPATH_ENGINE/insthelper.dbg" } function do_publish() { wrap-signcode //d "Keyman Engine for Windows" "$WINDOWS_PROGRAM_ENGINE/insthelper.dll" wrap-symstore "$WINDOWS_PROGRAM_ENGINE/insthelper.dll" //t keyman-engine-windows - wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/insthelper.pdb" //t keyman-engine-windows + wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/insthelper.dbg" //t keyman-engine-windows } builder_run_action clean:project clean_windows_project_files diff --git a/windows/src/engine/keyman/build.sh b/windows/src/engine/keyman/build.sh index c13effa338..efdaf0698c 100755 --- a/windows/src/engine/keyman/build.sh +++ b/windows/src/engine/keyman/build.sh @@ -38,10 +38,11 @@ function do_build() { run_in_vs_env rc osktoolbar.rc run_in_vs_env rc langswitch/langswitchmanager.rc delphi_msbuild keyman.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/keyman.map" "$WIN32_TARGET" + sentrytool_delphiprep "$WIN32_TARGET" keyman.dpr + tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE" - cp_if_exists "$WIN32_TARGET_PATH/keyman.pdb" "$WINDOWS_DEBUGPATH_ENGINE" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/keyman.dbg" "$WINDOWS_DEBUGPATH_ENGINE/keyman.dbg" # Also copy sentry files here cp "$KEYMAN_ROOT/common/windows/delphi/ext/sentry/sentry.dll" \ @@ -64,7 +65,7 @@ function do_publish() { wrap-signcode //d "Keyman Engine for Windows" "$WINDOWS_PROGRAM_ENGINE/crashpad_handler.exe" wrap-symstore "$WINDOWS_PROGRAM_ENGINE/keyman.exe" //t keyman-engine-windows - wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/keyman.pdb" //t keyman-engine-windows + wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/keyman.dbg" //t keyman-engine-windows } function do_build_debug_manifest() { @@ -76,17 +77,12 @@ function do_build_debug_manifest() { mv -f std-manifest.tmp manifest.in } -function do_install() { - cp "$WINDOWS_PROGRAM_ENGINE/keyman.exe" "$INSTALLPATH_KEYMANENGINE/keyman.exe" - cp_if_exists "$WINDOWS_DEBUGPATH_ENGINE/keyman.pdb" "$INSTALLPATH_KEYMANENGINE/keyman.pdb" -} - builder_run_action clean:project do_clean builder_run_action configure:project configure_windows_build_environment builder_run_action build:project do_build # builder_run_action test:project do_test builder_run_action publish:project do_publish -builder_run_action install:project do_install +builder_run_action install:project cp "$WINDOWS_PROGRAM_ENGINE/keyman.exe" "$INSTALLPATH_KEYMANENGINE/keyman.exe" builder_run_action debug-manifest:project do_build_debug_manifest builder_run_action edit:project start keyman.dproj diff --git a/windows/src/engine/keymanmc/build.sh b/windows/src/engine/keymanmc/build.sh index 389cc9ecb1..8ad933abb7 100755 --- a/windows/src/engine/keymanmc/build.sh +++ b/windows/src/engine/keymanmc/build.sh @@ -30,10 +30,10 @@ function do_build() { function do_install() { cp "$WINDOWS_PROGRAM_ENGINE/keymanmc.dll" "$INSTALLPATH_KEYMANENGINE/keymanmc.dll" - reg add 'HKLM\SYSTEM\CurrentControlSet\services\eventlog\Application\Keyman' //v CategoryCount //t REG_DWORD //d 1 //f - reg add 'HKLM\SYSTEM\CurrentControlSet\services\eventlog\Application\Keyman' //v CategoryMessageFile //t REG_SZ //d "$INSTALLPATH_KEYMANENGINE\\keymanmc.dll" //f - reg add 'HKLM\SYSTEM\CurrentControlSet\services\eventlog\Application\Keyman' //v EventMessageFile //t REG_SZ //d "$INSTALLPATH_KEYMANENGINE\\keymanmc.dll" //f - reg add 'HKLM\SYSTEM\CurrentControlSet\services\eventlog\Application\Keyman' //v TypesSupported //t REG_DWORD //d 7 //f + reg add 'HKLM\\SYSTEM\\CurrentControlSet\\services\\eventlog\\Application\\Keyman' //v CategoryCount //t REG_DWORD //d 1 //f + reg add 'HKLM\\SYSTEM\\CurrentControlSet\\services\\eventlog\\Application\\Keyman' //v CategoryMessageFile //t REG_SZ //d "$INSTALLPATH_KEYMANENGINE\\keymanmc.dll" //f + reg add 'HKLM\\SYSTEM\\CurrentControlSet\\services\\eventlog\\Application\\Keyman' //v EventMessageFile //t REG_SZ //d "$INSTALLPATH_KEYMANENGINE\\keymanmc.dll" //f + reg add 'HKLM\\SYSTEM\\CurrentControlSet\\services\\eventlog\\Application\\Keyman' //v TypesSupported //t REG_DWORD //d 7 //f } builder_run_action clean:project rm -f keymanmc.dll keymanmc.h keymanmc.rc keymanmc.res MSG00409.bin diff --git a/windows/src/engine/kmcomapi/build.sh b/windows/src/engine/kmcomapi/build.sh index 9776de77af..78b57e792b 100755 --- a/windows/src/engine/kmcomapi/build.sh +++ b/windows/src/engine/kmcomapi/build.sh @@ -36,16 +36,15 @@ function do_build() { run_in_vs_env gentlb -Tkmcomapi.tlb kmcomapi.ridl delphi_msbuild kmcomapi.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/kmcomapi.map" "$WIN32_TARGET" - # TODO: map2pdb for .x64 + sentrytool_delphiprep "$WIN32_TARGET" kmcomapi.dpr + tds2dbg "$WIN32_TARGET" delphi_msbuild kmcomapi.dproj "//p:Platform=Win64" # Delphi does not allow us to build to a different target filename so we rename after build mv -f "$WIN64_TARGET_PATH/kmcomapi.dll" "$WIN64_TARGET_PATH/kmcomapi.x64.dll" cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE" - cp_if_exists "$WIN32_TARGET_PATH/kmcomapi.pdb" "$WINDOWS_DEBUGPATH_ENGINE" - + builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmcomapi.dbg" "$WINDOWS_DEBUGPATH_ENGINE/kmcomapi.dbg" cp "$WIN64_TARGET_PATH/kmcomapi.x64.dll" "$WINDOWS_PROGRAM_ENGINE/kmcomapi.x64.dll" # x64 Delphi symbols not supported: cp "$WIN64_TARGET_PATH/kmcomapi.dbg" "$WINDOWS_PROGRAM_ENGINE/kmcomapi.x64.dbg" @@ -57,7 +56,7 @@ function do_publish() { wrap-symstore "$WINDOWS_PROGRAM_ENGINE/kmcomapi.dll" //t keyman-engine-windows wrap-symstore "$WINDOWS_PROGRAM_ENGINE/kmcomapi.x64.dll" //t keyman-engine-windows - wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/kmcomapi.pdb" //t keyman-engine-windows + wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/kmcomapi.dbg" //t keyman-engine-windows # No support for 64-bit delphi symbols } @@ -66,8 +65,6 @@ function do_install() { cp "$WINDOWS_PROGRAM_ENGINE/kmcomapi.dll" "$INSTALLPATH_KEYMANENGINE/kmcomapi.dll" regsvr32 //s "$INSTALLPATH_KEYMANENGINE/kmcomapi.dll" - cp_if_exists "$WINDOWS_DEBUGPATH_ENGINE/kmcomapi.pdb" "$INSTALLPATH_KEYMANENGINE/kmcomapi.pdb" - regsvr32 //s //u "$INSTALLPATH_KEYMANENGINE/kmcomapi.x64.dll" cp "$WINDOWS_PROGRAM_ENGINE/kmcomapi.x64.dll" "$INSTALLPATH_KEYMANENGINE/kmcomapi.x64.dll" regsvr32 //s "$INSTALLPATH_KEYMANENGINE/kmcomapi.x64.dll" diff --git a/windows/src/engine/tsysinfo/build.sh b/windows/src/engine/tsysinfo/build.sh index 3e94f381ba..e3390b899b 100755 --- a/windows/src/engine/tsysinfo/build.sh +++ b/windows/src/engine/tsysinfo/build.sh @@ -42,11 +42,12 @@ function do_build() { rm -f tsysinfox64.bin delphi_msbuild tsysinfo.dproj "//p:Platform=Win32" - do_map2pdb "$WIN32_TARGET_PATH/tsysinfo.map" "$WIN32_TARGET" + sentrytool_delphiprep "$WIN32_TARGET" tsysinfo.dpr + tds2dbg "$WIN32_TARGET" cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE" cp *.xslt "$WINDOWS_PROGRAM_ENGINE/" - cp_if_exists "$WIN32_TARGET_PATH/tsysinfo.pdb" "$WINDOWS_DEBUGPATH_ENGINE" + builder_if_release_build_level cp "$WIN32_TARGET_PATH/tsysinfo.dbg" "$WINDOWS_DEBUGPATH_ENGINE/tsysinfo.dbg" } function do_publish() { @@ -56,12 +57,11 @@ function do_publish() { wrap-signcode //d "Keyman Engine for Windows" "$WINDOWS_PROGRAM_ENGINE/tsysinfo.exe" wrap-symstore "$WINDOWS_PROGRAM_ENGINE/tsysinfo.exe" //t keyman-engine-windows - wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/tsysinfo.pdb" //t keyman-engine-windows + wrap-symstore "$WINDOWS_DEBUGPATH_ENGINE/tsysinfo.dbg" //t keyman-engine-windows } function do_install() { cp "$WINDOWS_PROGRAM_ENGINE/tsysinfo.exe" "$INSTALLPATH_KEYMANENGINE/tsysinfo.exe" - cp_if_exists "$WINDOWS_DEBUGPATH_ENGINE/tsysinfo.pdb" "$INSTALLPATH_KEYMANENGINE/tsysinfo.pdb" cp *.xslt "$INSTALLPATH_KEYMANENGINE/" } diff --git a/windows/src/engine/tsysinfox64/build.sh b/windows/src/engine/tsysinfox64/build.sh index d7b3e9a355..0a97e01c22 100755 --- a/windows/src/engine/tsysinfox64/build.sh +++ b/windows/src/engine/tsysinfox64/build.sh @@ -39,10 +39,15 @@ function do_publish() { wrap-signcode //d "Keyman Engine for Windows" "$WINDOWS_PROGRAM_ENGINE/tsysinfox64.exe" } +function do_install() { + cp "$WINDOWS_PROGRAM_ENGINE/tsysinfox64.exe" "$INSTALLPATH_KEYMANENGINE/tsysinfox64.exe" + cp *.xslt "$INSTALLPATH_KEYMANENGINE/" +} + builder_run_action clean:project clean_windows_project_files builder_run_action configure:project configure_windows_build_environment builder_run_action build:project do_build -# builder_run_action test:project (no current tests) +# builder_run_action test:project do_test builder_run_action publish:project do_publish -# builder_run_action install:project (installed as part of tsysinfo) +builder_run_action install:project do_install builder_run_action edit:project start tsysinfox64.dproj