From d3adcbbc0c51ab693e7fcbd95569ffd548128d02 Mon Sep 17 00:00:00 2001 From: sagitario Date: Fri, 8 May 2009 15:54:32 +0000 Subject: --- CHANGES | 12 + INSTALL | 49 ++ LICENSE | 201 ++++++ Makefile | 74 ++ README | 106 +++ VERSION | 1 + autoexp.snippet | 100 +++ src/LastError.h | 24 + src/PEImage.cpp | 238 ++++++ src/PEImage.h | 86 +++ src/cv2pdb.cpp | 2000 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/cv2pdb.h | 148 ++++ src/cv2pdb.sln | 20 + src/cv2pdb.vcproj | 232 ++++++ src/demangle.cpp | 493 +++++++++++++ src/demangle.h | 12 + src/main.cpp | 165 +++++ src/mscvpdb.h | 2083 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/mspdb.cpp | 91 +++ src/mspdb.h | 494 +++++++++++++ src/symutil.cpp | 152 ++++ src/symutil.h | 29 + test/cvtest.d | 233 ++++++ 23 files changed, 7043 insertions(+) create mode 100644 CHANGES create mode 100644 INSTALL create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 VERSION create mode 100644 autoexp.snippet create mode 100644 src/LastError.h create mode 100644 src/PEImage.cpp create mode 100644 src/PEImage.h create mode 100644 src/cv2pdb.cpp create mode 100644 src/cv2pdb.h create mode 100644 src/cv2pdb.sln create mode 100644 src/cv2pdb.vcproj create mode 100644 src/demangle.cpp create mode 100644 src/demangle.h create mode 100644 src/main.cpp create mode 100644 src/mscvpdb.h create mode 100644 src/mspdb.cpp create mode 100644 src/mspdb.h create mode 100644 src/symutil.cpp create mode 100644 src/symutil.h create mode 100644 test/cvtest.d diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..8239f0c --- /dev/null +++ b/CHANGES @@ -0,0 +1,12 @@ + +This is the CHANGES file for cv2pdb, a +converter of DMD CodeView debug information to PDB files + +Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved + +Version history +--------------- + +2009-05-08 Version 0.1 + + * initial release diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..e525207 --- /dev/null +++ b/INSTALL @@ -0,0 +1,49 @@ + +This is the INSTALL file for cv2pdb, a +converter of DMD CodeView debug information to PDB files + +Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved + +Prerequisites +------------- + +For this program to be useful, you should have either Microsoft Visual +Studio 2005 or 2008 or one of the Express versions installed. cv2pdb +uses one of the Microsoft DLLs to actually write the PDB file. + +Installation +------------ +Sorry, there is no full featured installer available yet, you'll have +to do some simple manual steps to use cv2pdb. + +1. The binary package of cv2pdb contains an executable cv2pdb.exe, which +should be copied somewhere accessible through your PATH environment +variable. + +2. cv2pdb.exe must be able to locate the DLL mspdb80.dll from the Visual +Studio installation. It reads out the installation path of the latter, but +if this fails, mspdb80.dll should also be accessible through your PATH +environment variable. + +3. For best debugging experience, you should configure Visual Studio +to use C/C++ syntax highlighting for D files. This is done by +navigating to the file extensions option page (found in Tools -> Options +-> Text editor -> File Extensions) and adding extensions "d" and "di" +with editor "Microsoft Visual C++". This will also enable display of +variables in the "Auto" watch window. + +4. You should also add the contents of the file autoexp.snippet to the +[AutoExpand] section of the file autoexp.dat found in +\Common7\Packages\Debugger. +Please note that in a standard installation of Visual Studio, the +section [AutoExpand] extends to the bottom of the file but a few lines +for the section [hresult]. +These lines will enable a convenient display of strings, dynamic arrays, +associative arrays and null references. + +Building from source +-------------------- +The source package comes with a Visual Studio 2008 project and solution +that work with both the Standard and the Express version. These won't +work in VS2005, but rebuilding the project should be easy. + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..32efefc --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + The Artistic License 2.0 + + Copyright (c) 2000-2006, The Perl Foundation. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Preamble + +This license establishes the terms under which a given free software +Package may be copied, modified, distributed, and/or redistributed. +The intent is that the Copyright Holder maintains some artistic +control over the development of that Package while still keeping the +Package available as open source and free software. + +You are always permitted to make arrangements wholly outside of this +license directly with the Copyright Holder of a given Package. If the +terms of this license do not permit the full use that you propose to +make of the Package, you should contact the Copyright Holder and seek +a different licensing arrangement. + +Definitions + + "Copyright Holder" means the individual(s) or organization(s) + named in the copyright notice for the entire Package. + + "Contributor" means any party that has contributed code or other + material to the Package, in accordance with the Copyright Holder's + procedures. + + "You" and "your" means any person who would like to copy, + distribute, or modify the Package. + + "Package" means the collection of files distributed by the + Copyright Holder, and derivatives of that collection and/or of + those files. A given Package may consist of either the Standard + Version, or a Modified Version. + + "Distribute" means providing a copy of the Package or making it + accessible to anyone else, or in the case of a company or + organization, to others outside of your company or organization. + + "Distributor Fee" means any fee that you charge for Distributing + this Package or providing support for this Package to another + party. It does not mean licensing fees. + + "Standard Version" refers to the Package if it has not been + modified, or has been modified only in ways explicitly requested + by the Copyright Holder. + + "Modified Version" means the Package, if it has been changed, and + such changes were not explicitly requested by the Copyright + Holder. + + "Original License" means this Artistic License as Distributed with + the Standard Version of the Package, in its current version or as + it may be modified by The Perl Foundation in the future. + + "Source" form means the source code, documentation source, and + configuration files for the Package. + + "Compiled" form means the compiled bytecode, object code, binary, + or any other form resulting from mechanical transformation or + translation of the Source form. + + +Permission for Use and Modification Without Distribution + +(1) You are permitted to use the Standard Version and create and use +Modified Versions for any purpose without restriction, provided that +you do not Distribute the Modified Version. + + +Permissions for Redistribution of the Standard Version + +(2) You may Distribute verbatim copies of the Source form of the +Standard Version of this Package in any medium without restriction, +either gratis or for a Distributor Fee, provided that you duplicate +all of the original copyright notices and associated disclaimers. At +your discretion, such verbatim copies may or may not include a +Compiled form of the Package. + +(3) You may apply any bug fixes, portability changes, and other +modifications made available from the Copyright Holder. The resulting +Package will still be considered the Standard Version, and as such +will be subject to the Original License. + + +Distribution of Modified Versions of the Package as Source + +(4) You may Distribute your Modified Version as Source (either gratis +or for a Distributor Fee, and with or without a Compiled form of the +Modified Version) provided that you clearly document how it differs +from the Standard Version, including, but not limited to, documenting +any non-standard features, executables, or modules, and provided that +you do at least ONE of the following: + + (a) make the Modified Version available to the Copyright Holder + of the Standard Version, under the Original License, so that the + Copyright Holder may include your modifications in the Standard + Version. + + (b) ensure that installation of your Modified Version does not + prevent the user installing or running the Standard Version. In + addition, the Modified Version must bear a name that is different + from the name of the Standard Version. + + (c) allow anyone who receives a copy of the Modified Version to + make the Source form of the Modified Version available to others + under + + (i) the Original License or + + (ii) a license that permits the licensee to freely copy, + modify and redistribute the Modified Version using the same + licensing terms that apply to the copy that the licensee + received, and requires that the Source form of the Modified + Version, and of any works derived from it, be made freely + available in that license fees are prohibited but Distributor + Fees are allowed. + + +Distribution of Compiled Forms of the Standard Version +or Modified Versions without the Source + +(5) You may Distribute Compiled forms of the Standard Version without +the Source, provided that you include complete instructions on how to +get the Source of the Standard Version. Such instructions must be +valid at the time of your distribution. If these instructions, at any +time while you are carrying out such distribution, become invalid, you +must provide new instructions on demand or cease further distribution. +If you provide valid instructions or cease distribution within thirty +days after you become aware that the instructions are invalid, then +you do not forfeit any of your rights under this license. + +(6) You may Distribute a Modified Version in Compiled form without +the Source, provided that you comply with Section 4 with respect to +the Source of the Modified Version. + + +Aggregating or Linking the Package + +(7) You may aggregate the Package (either the Standard Version or +Modified Version) with other packages and Distribute the resulting +aggregation provided that you do not charge a licensing fee for the +Package. Distributor Fees are permitted, and licensing fees for other +components in the aggregation are permitted. The terms of this license +apply to the use and Distribution of the Standard or Modified Versions +as included in the aggregation. + +(8) You are permitted to link Modified and Standard Versions with +other works, to embed the Package in a larger work of your own, or to +build stand-alone binary or bytecode versions of applications that +include the Package, and Distribute the result without restriction, +provided the result does not expose a direct interface to the Package. + + +Items That are Not Considered Part of a Modified Version + +(9) Works (including, but not limited to, modules and scripts) that +merely extend or make use of the Package, do not, by themselves, cause +the Package to be a Modified Version. In addition, such works are not +considered parts of the Package itself, and are not subject to the +terms of this license. + + +General Provisions + +(10) Any use, modification, and distribution of the Standard or +Modified Versions is governed by this Artistic License. By using, +modifying or distributing the Package, you accept this license. Do not +use, modify, or distribute the Package, if you do not accept this +license. + +(11) If your Modified Version has been derived from a Modified +Version made by someone other than you, you are nevertheless required +to ensure that your Modified Version complies with the requirements of +this license. + +(12) This license does not grant you the right to use any trademark, +service mark, tradename, or logo of the Copyright Holder. + +(13) This license includes the non-exclusive, worldwide, +free-of-charge patent license to make, have made, use, offer to sell, +sell, import and otherwise transfer the Package with respect to any +patent claims licensable by the Copyright Holder that are necessarily +infringed by the Package. If you institute patent litigation +(including a cross-claim or counterclaim) against any party alleging +that the Package constitutes direct or contributory patent +infringement, then this Artistic License to you shall terminate on the +date that such litigation is filed. + +(14) Disclaimer of Warranty: +THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS +IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL +LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fe69a9b --- /dev/null +++ b/Makefile @@ -0,0 +1,74 @@ +# +# makefile to be used with NMAKE for building source and +# installation packages +# +# prerequisites: +# - environment variable VSINSTALLDIR must point to your Visual Studio +# or VCExpress installation folder +# - Info-zip must be found in the PATH +# +# run +# nmake src +# to create a source package with name cv2pdb_src_.zip in +# ..\downloads +# +# run +# nmake bin +# to create a binary package with name cv2pdb_.zip in +# ..\downloads + +SRC = src\cv2pdb.cpp \ + src\cv2pdb.h \ + src\demangle.cpp \ + src\demangle.h \ + src\LastError.h \ + src\main.cpp \ + src\mscvpdb.h \ + src\mspdb.h \ + src\mspdb.cpp \ + src\PEImage.cpp \ + src\PEImage.h \ + src\symutil.cpp \ + src\symutil.h + +ADD = Makefile src\cv2pdb.vcproj src\cv2pdb.sln + +DOC = VERSION README INSTALL LICENSE CHANGES autoexp.snippet + +BIN = bin\Release\cv2pdb.exe + +TEST = test\cvtest.d + +all: bin src + +############################### +include VERSION + +DOWNLOADS = ..\downloads + +$(DOWNLOADS): + if not exist $(DOWNLOADS)\nul mkdir $(DOWNLOADS) + +############################### +SRC_ZIP = $(DOWNLOADS)\cv2pdb_src_$(VERSION).zip + +src: $(DOWNLOADS) $(SRC_ZIP) + +$(SRC_ZIP): $(SRC) $(ADD) $(DOC) $(TEST) + if exist $(SRC_ZIP) del $(SRC_ZIP) + zip $(SRC_ZIP) -X $(SRC) $(ADD) $(DOC) $(TEST) + +############################### +BIN_ZIP = $(DOWNLOADS)\cv2pdb_$(VERSION).zip + +bin: $(DOWNLOADS) $(BIN_ZIP) + +$(BIN_ZIP): $(BIN) $(DOC) Makefile + if exist $(BIN_ZIP) del $(BIN_ZIP) + zip $(BIN_ZIP) -j -X $(BIN) $(DOC) + +IDEDIR = $(VSINSTALLDIR)\Common7\IDE + +$(BIN): $(SRC) $(ADD) VERSION + if exist "$(IDEDIR)\VCExpress.exe" "$(IDEDIR)\VCExpress.exe" /Build Release src\cv2pdb.sln + if not exist "$(IDEDIR)\VCExpress.exe" "$(IDEDIR)\devenv.exe" /Build Release src\cv2pdb.sln diff --git a/README b/README new file mode 100644 index 0000000..8433c99 --- /dev/null +++ b/README @@ -0,0 +1,106 @@ + +This is the README file for cv2pdb, a +converter of DMD CodeView debug information to PDB files + +Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved + +The goal of this project is to make debugging of D applications that +were created with the Digital Mars DMD compiler, as painless as possible +in current versions of Visual Studio (i.e Visual Studio 2008 and +VCExpress). +As a side effect, other applications might also benefit from the +converted debug information, like WinDbg. + +Features +-------- +* conversion of DMD CodeView information to PDB file +* converted line number info allows setting breakpoints +* display of variables, fields and objects in watch, local and auto window and in data tooltips +* generates generic debug info for dynamic arrays, associative arrays and delegates +* autoexp.dat allows convenient display of dynamic and associative arrays in watch windows +* demangles function names for convenient display of callstack + +License information +------------------- + +This code is distributed under the term of the Artistic License 2.0. +For more details, see the full text of the license in the file LICENSE. + +The file demangle.cpp is an adaption of denangle.d to C++ distributed with +the DMD compiler. It is placed into the Public Domain. + +The file mscvpdb.h is taken from the WINE-project (http://www.winehq.org) +and is distributed under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. +See the file header for more details and +http://www.gnu.org/licenses/lgpl.html for the full license. + +Installation +------------ +Sorry, there is no full featured installer available yet, you'll have +to do some simple manual steps to use cv2pdb. + +See the file INSTALL for further details. + +Usage +----- + +cv2pdb.exe is a command line tool which outputs its usage information +if run without arguments: + + usage: cv2pdb [-Dversion] [new-exe-file] [pdb-file] + +With the -D option, you can specify the version of the DMD compiler +you are using. Unfortunately, this information is not embedded into +the debug information. The default is -D2. So far, this information +is only needed to determine whether "char[]" or "const char[]" is +translated to "string". + +The first file name on the command line is expected to be the executable +or dynamic library compiled by the DMD compiler and containing the +CodeView debug information (-g option used when running dmd). + +If no further file name is given, a PDB file will be created with the +same base name as the executable, but with extension "pdb", and the +executable will be modified to redirect debuggers to this pdb-file instead +of the original debug information. + +Example: + cv2pdb debuggee.exe + +In an environment using make-like tools, it is often useful to create +a new file instead of modifying existing files. That way the file +modification time can be used to continue the build process at the +correct step. +If another file name is specified, the new executable is written +to this file and leaves the input executable unmodified.. The naming +of the pdb-file will use the base name of the output file. + +Example: + cv2pdb debuggee.exe debuggee_pdb.exe + +Last but not least, the resulting pdb-file can be renamed by specifying +a third file name. + +Example: + cv2pdb debuggee.exe debuggee_pdb.exe debug.pdb + + + +Changes +------- + +For documentation on the changes between this version and +previous versions, please see the file CHANGES. + +Feedback +-------- +The project home for cv2pdb is here: + + http://www.dsource.org/projects/cv2pdb + +There's also a forum, where you can leave your comments and suggestions. + +Have fun, +Rainer Schuetze diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..b37d68d --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +VERSION = 0.1 diff --git a/autoexp.snippet b/autoexp.snippet new file mode 100644 index 0000000..a430e23 --- /dev/null +++ b/autoexp.snippet @@ -0,0 +1,100 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; This file is part cv2pdb, a +;; converter of DMD CodeView debug information to PDB files +;; +;; written by Rainer Schuetze, placed into the Public Domain +;; +;; D types +;; +;; add the content of this file to the [AutoExpand] section +;; of +;; \Common7\Package\Debugger\autoexp.dat +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; string: dynamic array of char +const char[]|char[]|string { + preview ( + #if ($e.data == 0) ( "null" ) + #else ( + #( + "[", $e.length, "] ", [$e.data, s] + ) + ) + ) + children ( + #( + length: [$e.length, i], + data: [$e.data] + ) + ) +} + +; dynamic array +*[] { + preview ( + #if ($e.data == 0) ( "null" ) + #else ( + #( + "[", $e.length, "](", + #array + ( + expr : ($e.data)[$i], + size : $e.length + ), + ")" + ) + ) + ) + children ( + #( + length: [$e.length, i], + #array ( + expr: $e.data[$i], + size: $e.length + ) + ) + ) +} + +;; display associative array +;; +aa<*> { + preview( + #( + "[", $e.a->nodes, "] ", [(void*) $e.a] + ) + ) + children( + #( + #array ( + expr: &$e.a->b.data[$i], + size: $e.a->b.length + ) : + #tree ( + head : $e, + left : left, + right : right, + ; size : $c.size + ) : $e + + ) + ) +} + +; display tree +internal@aaA<*,*> { + preview( + #( "<", $e.key, ", ", $e.value, ">" ) + ) +} + +;; display null references for class objects +*@* { + preview( + #( + #if (&$e == 0) ( "null" ) + #else ( [$e,!] ) + ) + ) +} diff --git a/src/LastError.h b/src/LastError.h new file mode 100644 index 0000000..b7b6f27 --- /dev/null +++ b/src/LastError.h @@ -0,0 +1,24 @@ +// Convert DMD CodeView debug information to PDB files +// Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved +// +// License for redistribution is given by the Artistic License 2.0 +// see file LICENSE for further details + +#ifndef __LASTERROR_H__ +#define __LASTERROR_H__ + +class LastError +{ +public: + LastError() : lastError("") {} + + bool setError(const char* msg) { lastError = msg; return false; } + const char* getLastError() const { return lastError; } + bool hadError() const { return lastError != 0 && *lastError; } + +private: + const char* lastError; +}; + + +#endif //__LASTERROR_H__ \ No newline at end of file diff --git a/src/PEImage.cpp b/src/PEImage.cpp new file mode 100644 index 0000000..ec826a8 --- /dev/null +++ b/src/PEImage.cpp @@ -0,0 +1,238 @@ +// Convert DMD CodeView debug information to PDB files +// Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved +// +// License for redistribution is given by the Artistic License 2.0 +// see file LICENSE for further details + +#include "PEImage.h" + +extern "C" { +#include "mscvpdb.h" +} + +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////// +PEImage::PEImage(const char* iname) +: dump_base(0) +, dump_total_len(0) +, dirHeader(0) +, fd(-1) +{ + if(iname) + load(iname); +} + +PEImage::~PEImage() +{ + if(fd != -1) + close(fd); + if(dump_base) + free_aligned(dump_base); +} + +/////////////////////////////////////////////////////////////////////// +bool PEImage::load(const char* iname) +{ + if (fd != -1) + return setError("file already open"); + + fd = open(iname, O_RDONLY | O_BINARY); + if (fd == -1) + return setError("Can't open file"); + + struct stat s; + if (fstat(fd, &s) < 0) + return setError("Can't get size"); + dump_total_len = s.st_size; + + dump_base = alloc_aligned(dump_total_len, 0x1000); + if (!dump_base) + return setError("Out of memory"); + if (read(fd, dump_base, dump_total_len) != dump_total_len) + return setError("Cannot read file"); + + + close(fd); + fd = -1; + return initPtr(); +} + +/////////////////////////////////////////////////////////////////////// +bool PEImage::save(const char* oname) +{ + if (fd != -1) + return setError("file already open"); + + if (!dump_base) + return setError("no data to dump"); + + fd = open(oname, O_WRONLY | O_CREAT | O_BINARY | O_TRUNC, S_IREAD | S_IWRITE | S_IEXEC); + if (fd == -1) + return setError("Can't create file"); + + if (write(fd, dump_base, dump_total_len) != dump_total_len) + return setError("Cannot write file"); + + close(fd); + fd = -1; + return true; +} + +/////////////////////////////////////////////////////////////////////// +bool PEImage::replaceDebugSection (const void* data, int datalen) +{ + int align = hdr->OptionalHeader.FileAlignment; + int align_len = datalen; + int fill = 0; + if (align > 0) + { + fill = (align - (dump_total_len % align)) % align; + align_len = ((datalen + align - 1) / align) * align; + } + char* newdata = (char*) alloc_aligned(dump_total_len + fill + datalen, 0x1000); + if(!newdata) + return setError("cannot alloc new image"); + + // assume there is place for another section because of section alignment + int s; + DWORD lastVirtualAddress = 0; + for(s = 0; s < hdr->FileHeader.NumberOfSections; s++) + { + if (strcmp ((char*) sec [s].Name, ".debug") == 0) + strcpy ((char*) sec [s].Name, ".ddebug"); + lastVirtualAddress = sec [s].VirtualAddress + sec[s].Misc.VirtualSize; + } + + int salign_len = datalen; + align = hdr->OptionalHeader.SectionAlignment; + if (align > 0) + { + lastVirtualAddress = ((lastVirtualAddress + align - 1) / align) * align; + salign_len = ((datalen + align - 1) / align) * align; + } + + strcpy((char*) sec[s].Name, ".debug"); + sec[s].Misc.VirtualSize = align_len; // union with PhysicalAddress; + sec[s].VirtualAddress = lastVirtualAddress; + sec[s].SizeOfRawData = datalen; + sec[s].PointerToRawData = dump_total_len + fill; + sec[s].PointerToRelocations = 0; + sec[s].PointerToLinenumbers = 0; + sec[s].NumberOfRelocations = 0; + sec[s].NumberOfLinenumbers = 0; + sec[s].Characteristics = IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_CNT_INITIALIZED_DATA; + + hdr->FileHeader.NumberOfSections++; + hdr->OptionalHeader.SizeOfImage += salign_len; + + dbgDir->PointerToRawData = sec[s].PointerToRawData; + dbgDir->AddressOfRawData = sec[s].PointerToRawData; + dbgDir->SizeOfData = sec[s].SizeOfRawData; + + // append debug data chunk to existing file image + memcpy(newdata, dump_base, dump_total_len); + memset(newdata + dump_total_len, 0, fill); + memcpy(newdata + dump_total_len + fill, data, datalen); + + free_aligned(dump_base); + dump_base = newdata; + dump_total_len += fill + datalen; + + return initPtr(); +} + +/////////////////////////////////////////////////////////////////////// +bool PEImage::initPtr() +{ + dos = DPV (0); + if(!dos) + return setError("file too small for DOS header"); + if(dos->e_magic != IMAGE_DOS_SIGNATURE) + return setError("this is not a DOS executable"); + + hdr = DPV (dos->e_lfanew); + if(!hdr) + return setError("no optional header found"); + + if(hdr->Signature != IMAGE_NT_SIGNATURE) + return setError("optional header does not have PE signature"); + if(hdr->FileHeader.SizeOfOptionalHeader < sizeof(IMAGE_OPTIONAL_HEADER32)) + return setError("optional header too small"); + + sec = IMAGE_FIRST_SECTION(hdr); + + if(hdr->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_DEBUG) + return setError("too few entries in data directory"); + + if(hdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size != 0x1c) + return setError("unexpected size of DEBUG data directory entry"); + + int off = hdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress; + dbgDir = RVA(off, 0x1c); + if (!dbgDir) + return setError("debug directory not placed in image"); + if (dbgDir->Type != IMAGE_DEBUG_TYPE_CODEVIEW) + return setError("debug directory not of type CodeView"); + + cv_base = dbgDir->PointerToRawData; + OMFSignature* sig = DPV(cv_base, dbgDir->SizeOfData); + if (!sig) + return setError("invalid debug data base address and size"); + if (memcmp(sig->Signature, "NB09", 4) != 0 && memcmp(sig->Signature, "NB11", 4) != 0) + { + // return setError("can only handle debug info of type NB09 and NB11"); + dirHeader = 0; + dirEntry = 0; + return true; + } + dirHeader = CVP(sig->filepos); + if (!dirHeader) + return setError("invalid cv dir header data base address"); + dirEntry = CVP(sig->filepos + dirHeader->cbDirHeader); + if (!dirEntry) + return setError("cv debug dir entries invalid"); + + //if (dirHeader->cDir == 0) + // return setError("cv debug dir has no entries"); + + return true; +} + +/////////////////////////////////////////////////////////////////////// +int PEImage::countCVEntries() const +{ + return dirHeader ? dirHeader->cDir : 0; +} + +OMFDirEntry* PEImage::getCVEntry(int i) const +{ + return dirEntry + i; +} + + +/////////////////////////////////////////////////////////////////////// +// utilities +void* PEImage::alloc_aligned(unsigned int size, unsigned int align, unsigned int alignoff) +{ + if (align & (align - 1)) + return 0; + + unsigned int pad = align + sizeof(void*); + char* p = (char*) malloc(size + pad); + unsigned int off = (align + alignoff - sizeof(void*) - (p - (char*) 0)) & (align - 1); + char* q = p + sizeof(void*) + off; + ((void**) q)[-1] = p; + return q; +} + +/////////////////////////////////////////////////////////////////////// +void PEImage::free_aligned(void* p) +{ + void* q = ((void**) p)[-1]; + free(q); +} diff --git a/src/PEImage.h b/src/PEImage.h new file mode 100644 index 0000000..bbc2eb9 --- /dev/null +++ b/src/PEImage.h @@ -0,0 +1,86 @@ +// Convert DMD CodeView debug information to PDB files +// Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved +// +// License for redistribution is given by the Artistic License 2.0 +// see file LICENSE for further details + +#ifndef __PEIMAGE_H__ +#define __PEIMAGE_H__ + +#include "LastError.h" + +#include + +struct OMFDirHeader; +struct OMFDirEntry; + +class PEImage : public LastError +{ +public: + PEImage(const char* iname = 0); + ~PEImage(); + + template P* DP(int off) + { + return (P*) ((char*) dump_base + off); + } + template P* DPV(int off, int size) + { + if(off < 0 || off + size > dump_total_len) + return 0; + return (P*) ((char*) dump_base + off); + } + template P* DPV(int off) + { + return DPV

(off, sizeof(P)); + } + template P* CVP(int off) + { + return DPV

(cv_base + off, sizeof(P)); + } + + template P* RVA(unsigned long rva, int len) + { + IMAGE_DOS_HEADER *dos = DPV (0); + IMAGE_NT_HEADERS32* hdr = DPV (dos->e_lfanew); + IMAGE_SECTION_HEADER* sec = IMAGE_FIRST_SECTION(hdr); + + for (int i = 0; i < hdr->FileHeader.NumberOfSections; i++) + { + if (rva >= sec[i].VirtualAddress && + rva + len <= sec[i].VirtualAddress + sec[i].SizeOfRawData) + return DPV

(sec[i].PointerToRawData + rva - sec[i].VirtualAddress, len); + } + return 0; + } + + bool load(const char* iname); + bool save(const char* oname); + + bool replaceDebugSection (const void* data, int datalen); + bool initPtr(); + + int countCVEntries() const; + OMFDirEntry* getCVEntry(int i) const; + + // utilities + static void* alloc_aligned(unsigned int size, unsigned int align, unsigned int alignoff = 0); + static void free_aligned(void* p); + +private: + int fd; + void* dump_base; + int dump_total_len; + + IMAGE_DOS_HEADER *dos; + IMAGE_NT_HEADERS32* hdr; + IMAGE_SECTION_HEADER* sec; + IMAGE_DEBUG_DIRECTORY* dbgDir; + OMFDirHeader* dirHeader; + OMFDirEntry* dirEntry; + + int cv_base; +}; + + +#endif //__PEIMAGE_H__ \ No newline at end of file diff --git a/src/cv2pdb.cpp b/src/cv2pdb.cpp new file mode 100644 index 0000000..b1a932d --- /dev/null +++ b/src/cv2pdb.cpp @@ -0,0 +1,2000 @@ +// Convert DMD CodeView debug information to PDB files +// Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved +// +// License for redistribution is given by the Artistic License 2.0 +// see file LICENSE for further details + +#include "cv2pdb.h" +#include "PEImage.h" +#include "symutil.h" + +#include +#include + +static const int kIncomplete = 0x80; + +CV2PDB::CV2PDB(PEImage& image) +: img(image), pdb(0), dbi(0), libraries(0), rsds(0), modules(0), globmod(0) +, segMap(0), segMapDesc(0), globalTypeHeader(0) +, globalTypes(0), cbGlobalTypes(0), allocGlobalTypes(0) +, userTypes(0), cbUserTypes(0), allocUserTypes(0) +, globalSymbols(0), cbGlobalSymbols(0), staticSymbols(0), cbStaticSymbols(0) +, udtSymbols(0), cbUdtSymbols(0), allocUdtSymbols(0) +, pointerTypes(0) +, Dversion(2) +{ + thisIsNotRef = true; + v3 = true; + countEntries = img.countCVEntries(); +} + +CV2PDB::~CV2PDB() +{ + cleanup(false); +} + +bool CV2PDB::cleanup(bool commit) +{ + if (modules) + for (int m = 0; m < countEntries; m++) + if (modules[m]) + modules[m]->Close(); + delete [] modules; + if (globmod) + globmod->Close(); + + if (dbi) + dbi->SetMachineType(0x14c); + + if (dbi) + dbi->Close(); + if (tpi) + tpi->Close(); + if (pdb) + pdb->Commit(); + if (pdb) + pdb->Close(); + + if (rsds) + delete [] (char*) rsds; + if (globalTypes) + free(globalTypes); + if (userTypes) + free(userTypes); + if (udtSymbols) + free(udtSymbols); + delete [] pointerTypes; + + globalTypes = 0; + cbGlobalTypes = 0; + allocGlobalTypes = 0; + userTypes = 0; + cbUserTypes = 0; + allocUserTypes = 0; + globalSymbols = 0; + cbGlobalSymbols = 0; + staticSymbols = 0; + cbStaticSymbols = 0; + udtSymbols = 0; + cbUdtSymbols = 0; + allocUdtSymbols = 0; + modules = 0; + globmod = 0; + countEntries = 0; + dbi = 0; + pdb = 0; + rsds = 0; + segMap = 0; + segMapDesc = 0; + globalTypeHeader = 0; + objectType = 0; + pointerTypes = 0; + + return true; +} + +bool CV2PDB::openPDB(const char* pdbname) +{ + wchar_t pdbnameW[260]; // = L"c:\\tmp\\aa\\ddoc4.pdb"; + mbstowcs (pdbnameW, pdbname, 260); + + if (!initMsPdb ()) + return setError("cannot load PDB helper DLL"); + pdb = CreatePDB (pdbnameW); + if (!pdb) + return setError("cannot create PDB file"); + + //printf("PDB::QueryInterfaceVersion() = %d\n", pdb->QueryInterfaceVersion()); + //printf("PDB::QueryImplementationVersion() = %d\n", pdb->QueryImplementationVersion()); + //printf("PDB::QueryPdbImplementationVersion() = %d\n", pdb->QueryPdbImplementationVersion()); + + rsds = (OMFSignatureRSDS *) new char[24 + strlen(pdbname) + 1]; // sizeof(OMFSignatureRSDS) without name + memcpy (rsds->Signature, "RSDS", 4); + pdb->QuerySignature2(&rsds->guid); + rsds->unknown = pdb->QueryAge(); + strcpy(rsds->name, pdbname); + + int rc = pdb->CreateDBI("", &dbi); + if (rc <= 0 || !dbi) + return setError("cannot create DBI"); + + rc = pdb->OpenTpi("", &tpi); + if (rc <= 0 || !tpi) + return setError("cannot create TPI"); + + return true; +} + +bool CV2PDB::setError(const char* msg) +{ + char pdbmsg[256]; + if(pdb) + pdb->QueryLastError (pdbmsg); + return LastError::setError(msg); +} + +bool CV2PDB::createModules() +{ + // assumes libraries and segMap initialized + countEntries = img.countCVEntries(); + modules = new mspdb::Mod* [countEntries]; + memset (modules, 0, countEntries * sizeof(*modules)); + + for (int m = 0; m < countEntries; m++) + { + OMFDirEntry* entry = img.getCVEntry(m); + if (entry->SubSection == sstModule) + { + OMFModule* module = img.CVP(entry->lfo); + OMFSegDesc* segDesc = img.CVP(entry->lfo + sizeof(OMFModule)); + BYTE* pname = img.CVP(entry->lfo + sizeof(OMFModule) + sizeof(OMFSegDesc) * module->cSeg); + char *name = p2c(pname); + const BYTE* plib = getLibrary (module->iLib); + const char* lib = (!plib || !*plib ? name : p2c(plib, 1)); + + if (modules[entry->iMod]) + { + modules[entry->iMod]->Close(); + modules[entry->iMod] = 0; + } + int rc = dbi->OpenMod(name, lib, &modules[entry->iMod]); + if (rc <= 0 || !modules[entry->iMod]) + return setError("cannot create mod"); + + for (int s = 0; s < module->cSeg; s++) + { + int segIndex = segDesc[s].Seg; + int segFlags = 0; + if (segMap && segIndex < segMap->cSeg) + segFlags = segMapDesc[segIndex].flags; + segFlags = 0x60101020; // 0x40401040, 0x60500020; // TODO + rc = modules[entry->iMod]->AddSecContrib(segIndex, segDesc[s].Off, segDesc[s].cbSeg, segFlags); + if (rc <= 0) + return setError("cannot add section contribution to module"); + } + } + } + return true; +} + +bool CV2PDB::initLibraries() +{ + libraries = 0; + for (int m = 0; m < countEntries; m++) + if (img.getCVEntry(m)->SubSection == sstLibraries) + libraries = img.CVP (img.getCVEntry(m)->lfo); + + return true; +} + +const BYTE* CV2PDB::getLibrary(int i) +{ + if (!libraries) + return 0; + const BYTE* p = libraries; + for (int j = 0; j < i; j++) + p += *p + 1; + return p; +} + +bool CV2PDB::initSegMap() +{ + for (int m = 0; m < countEntries; m++) + { + OMFDirEntry* entry = img.getCVEntry(m); + switch(entry->SubSection) + { + case sstSegMap: + segMap = img.CVP(entry->lfo); + segMapDesc = img.CVP(entry->lfo + sizeof(OMFSegMap)); + for (int s = 0; s < segMap->cSeg; s++) + { + int rc = dbi->AddSec(segMapDesc[s].frame, segMapDesc[s].flags, segMapDesc[s].offset, segMapDesc[s].cbSeg); + if (rc <= 0) + return setError("cannot add section"); + } + break; + } + } + return true; +} + +int CV2PDB::numeric_leaf(int* value, const void* leaf) +{ + unsigned short int type = *(const unsigned short int*) leaf; + leaf = (const unsigned short int*) leaf + 2; + int length = 2; + + *value = 0; + switch (type) + { + case LF_CHAR: + length += 1; + *value = *(const char*)leaf; + break; + + case LF_SHORT: + length += 2; + *value = *(const short*)leaf; + break; + + case LF_USHORT: + length += 2; + *value = *(const unsigned short*)leaf; + break; + + case LF_LONG: + case LF_ULONG: + length += 4; + *value = *(const int*)leaf; + break; + + case LF_COMPLEX64: + case LF_QUADWORD: + case LF_UQUADWORD: + case LF_REAL64: + length += 8; + break; + + case LF_COMPLEX32: + case LF_REAL32: + length += 4; + break; + + case LF_REAL48: + length += 6; + break; + + case LF_COMPLEX80: + case LF_REAL80: + length += 10; + break; + + case LF_COMPLEX128: + case LF_REAL128: + length += 16; + break; + + case LF_VARSTRING: + length += 2 + *(const unsigned short*)leaf; + break; + + default: + if (type < LF_NUMERIC) + *value = type; + else + { + setError("unsupported numeric leaf"); + length = 0; + } + break; + } + return length; +} + +int CV2PDB::copy_leaf(unsigned char* dp, int& dpos, const unsigned char* p, int& pos) +{ + int value; + int leaf_len = numeric_leaf(&value, p + pos); + memcpy(dp + dpos, p + pos, leaf_len); + pos += leaf_len; + dpos += leaf_len; + return leaf_len; +} + +static int copy_p2dsym(unsigned char* dp, int& dpos, const unsigned char* p, int& pos, int maxdlen) +{ + int len = dsym2c(p + pos + 1, p[pos], (char*) dp + dpos, maxdlen - dpos) + 1; + dpos += len; + pos += p[pos] + 1; + return len; +} + +int CV2PDB::addFields(codeview_reftype* dfieldlist, const codeview_reftype* fieldlist, int maxdlen) +{ + int len = fieldlist->fieldlist.len - 2; + const unsigned char* p = fieldlist->fieldlist.list; + unsigned char* dp = dfieldlist->fieldlist.list; + int pos = 0, dpos = 0; + int leaf_len, value; + + int prev = pos; + while (pos < len && !hadError()) + { + if (p[pos] >= 0xf1) /* LF_PAD... */ + { + pos += p[pos] & 0x0f; + continue; + } + if(pos & 3) + { + setError("bad field alignment!"); + break; + } + + prev = pos; + const codeview_fieldtype* fieldtype = (const codeview_fieldtype*)(p + pos); + codeview_fieldtype* dfieldtype = (codeview_fieldtype*)(dp + dpos); + int copylen = 0; + + switch (fieldtype->generic.id) + { + case LF_ENUMERATE_V1: + if (v3) + { + dfieldtype->enumerate_v3.id = LF_ENUMERATE_V3; + dfieldtype->enumerate_v3.attribute = fieldtype->enumerate_v1.attribute; + pos += sizeof(fieldtype->enumerate_v1) - sizeof(fieldtype->enumerate_v1.value); + dpos += sizeof(fieldtype->enumerate_v3) - sizeof(fieldtype->enumerate_v3.value); + copy_leaf(dp, dpos, p, pos); + copy_p2dsym(dp, dpos, p, pos, maxdlen); + } + else + { + leaf_len = numeric_leaf(&value, &fieldtype->enumerate_v1.value); + copylen = 2 + 2 + leaf_len + p[pos + 4 + leaf_len] + 1; // id,attr,value,name + } + break; + + case LF_ENUMERATE_V3: + leaf_len = numeric_leaf(&value, &fieldtype->enumerate_v3.value); + copylen = 2 + 2 + leaf_len + strlen((const char*) p + pos + 4 + leaf_len) + 1; // id,attr,value,name + break; + + case LF_MEMBER_V1: + dfieldtype->member_v2.id = v3 ? LF_MEMBER_V3 : LF_MEMBER_V2; + dfieldtype->member_v2.attribute = fieldtype->member_v1.attribute; + dfieldtype->member_v2.type = translateType(fieldtype->member_v1.type); + pos += sizeof(dfieldtype->member_v1.id) + sizeof(dfieldtype->member_v1.attribute) + sizeof(dfieldtype->member_v1.type); + dpos += sizeof(dfieldtype->member_v2.id) + sizeof(dfieldtype->member_v2.attribute) + sizeof(dfieldtype->member_v2.type); + if (v3) + { + copy_leaf(dp, dpos, p, pos); + copy_p2dsym(dp, dpos, p, pos, maxdlen); + } + else + { + leaf_len = numeric_leaf(&value, &fieldtype->member_v1.offset); + copylen = leaf_len + p[pos + leaf_len] + 1; // value,name + } + break; + + case LF_MEMBER_V2: + leaf_len = numeric_leaf(&value, &fieldtype->member_v1.offset); + copylen = sizeof(dfieldtype->member_v2) - sizeof(dfieldtype->member_v2.offset); + copylen += leaf_len + p[pos + copylen + leaf_len] + 1; // value,name + break; + + case LF_MEMBER_V3: + leaf_len = numeric_leaf(&value, &fieldtype->member_v3.offset); + copylen = sizeof(dfieldtype->member_v3) - sizeof(dfieldtype->member_v3.offset); + copylen += leaf_len + strlen((const char*) p + pos + copylen + leaf_len) + 1; // value,name + break; + + case LF_BCLASS_V1: + dfieldtype->bclass_v2.id = LF_BCLASS_V2; + dfieldtype->bclass_v2.attribute = fieldtype->bclass_v1.attribute; + dfieldtype->bclass_v2.type = translateType(fieldtype->bclass_v1.type); + pos += sizeof(fieldtype->bclass_v1) - sizeof(fieldtype->bclass_v1.offset); +#if 1 + dpos += sizeof(dfieldtype->bclass_v2) - sizeof(fieldtype->bclass_v2.offset); + copylen = numeric_leaf(&value, &fieldtype->bclass_v1.offset); + memcpy (dp + dpos, p + pos, copylen); + pos += copylen; + dpos += copylen; + // dp[dpos++] = 0; + copylen = 0; +#else + dfieldtype->member_v2.id = LF_MEMBER_V2; + dfieldtype->member_v2.attribute = 0; + dfieldtype->member_v2.type = fieldtype->bclass_v1.type; + dfieldtype->member_v2.offset = fieldtype->bclass_v1.offset; + //memcpy (&dfieldtype->member_v2 + 1, "\0", 1); + //dpos += sizeof(dfieldtype->member_v2) + 1; + memcpy (&dfieldtype->member_v2 + 1, "\004base", 5); + dpos += sizeof(dfieldtype->member_v2) + 5; + + pos += numeric_leaf(&value, &fieldtype->bclass_v1.offset); +#endif + break; + + case LF_BCLASS_V2: + leaf_len = numeric_leaf(&value, &fieldtype->bclass_v2.offset); + copylen = sizeof(dfieldtype->bclass_v2) - 2 + leaf_len; + break; + + case LF_METHOD_V1: + dfieldtype->method_v2.id = v3 ? LF_METHOD_V3 : LF_METHOD_V2; + dfieldtype->method_v2.count = fieldtype->method_v1.count; + dfieldtype->method_v2.mlist = fieldtype->method_v1.mlist; + pos += sizeof(dfieldtype->method_v1) - sizeof(dfieldtype->method_v1.p_name); + dpos += sizeof(dfieldtype->method_v2) - sizeof(dfieldtype->method_v2.p_name); + if (v3) + copy_p2dsym(dp, dpos, p, pos, maxdlen); + else + copylen = fieldtype->method_v1.p_name.namelen + 1; + break; + + case LF_METHOD_V2: + copylen = sizeof(dfieldtype->method_v2) - sizeof(dfieldtype->method_v2.p_name); + copylen += fieldtype->method_v2.p_name.namelen + 1; + break; + + case LF_METHOD_V3: + copylen = sizeof(dfieldtype->method_v3); + copylen += strlen((const char*) p + pos + copylen) + 1; + break; + + case LF_STMEMBER_V1: + dfieldtype->stmember_v2.id = v3 ? LF_METHOD_V3 : LF_METHOD_V2; + dfieldtype->stmember_v2.attribute = fieldtype->stmember_v1.attribute; + dfieldtype->stmember_v2.type = translateType(fieldtype->stmember_v1.type); + pos += sizeof(dfieldtype->stmember_v1) - sizeof(dfieldtype->stmember_v1.p_name); + dpos += sizeof(dfieldtype->stmember_v2) - sizeof(dfieldtype->stmember_v2.p_name); + if (v3) + copy_p2dsym(dp, dpos, p, pos, maxdlen); + else + copylen = fieldtype->stmember_v1.p_name.namelen + 1; + break; + + case LF_STMEMBER_V2: + copylen = sizeof(dfieldtype->stmember_v2) - sizeof(dfieldtype->stmember_v2.p_name); + copylen += fieldtype->stmember_v2.p_name.namelen + 1; + break; + + case LF_STMEMBER_V3: + copylen = sizeof(dfieldtype->stmember_v3) - sizeof(dfieldtype->stmember_v3.name); + copylen += strlen(fieldtype->stmember_v3.name) + 1; + break; + + case LF_NESTTYPE_V1: + dfieldtype->nesttype_v2.id = v3 ? LF_NESTTYPE_V3 : LF_NESTTYPE_V2; + dfieldtype->nesttype_v2.type = translateType(fieldtype->nesttype_v1.type); + dfieldtype->nesttype_v2._pad0 = 0; + pos += sizeof(dfieldtype->nesttype_v1) - sizeof(dfieldtype->nesttype_v1.p_name); + dpos += sizeof(dfieldtype->nesttype_v2) - sizeof(dfieldtype->nesttype_v2.p_name); + if (v3) + copy_p2dsym(dp, dpos, p, pos, maxdlen); + else + copylen = fieldtype->nesttype_v1.p_name.namelen + 1; + break; + + case LF_NESTTYPE_V2: + copylen = sizeof(dfieldtype->nesttype_v2) - sizeof(dfieldtype->nesttype_v2.p_name); + copylen += fieldtype->nesttype_v2.p_name.namelen + 1; + break; + + case LF_NESTTYPE_V3: + copylen = sizeof(dfieldtype->nesttype_v3) - sizeof(dfieldtype->nesttype_v3.name); + copylen += strlen(fieldtype->nesttype_v3.name) + 1; + break; + + default: + setError("unsupported field entry"); + break; + } + + memcpy (dp + dpos, p + pos, copylen); + pos += copylen; + dpos += copylen; + + for ( ; dpos & 3; dpos++) + dp[dpos] = 0xf4 - (dpos & 3); + } + return dpos; +} + +int CV2PDB::addAggregate(codeview_type* dtype, bool clss, int n_element, int fieldlist, int property, + int derived, int vshape, int structlen, const char*name) +{ + dtype->struct_v2.id = clss ? (v3 ? LF_CLASS_V3 : LF_CLASS_V2) : (v3 ? LF_STRUCTURE_V3 : LF_STRUCTURE_V2); + dtype->struct_v2.n_element = n_element; + dtype->struct_v2.fieldlist = fieldlist; + dtype->struct_v2.property = property; + dtype->struct_v2.derived = derived; + dtype->struct_v2.vshape = vshape; + dtype->struct_v2.structlen = structlen; + int len = cstrcpy_v(v3, (BYTE*)(&dtype->struct_v2 + 1), name); + len += sizeof (dtype->struct_v2); + + unsigned char* p = (unsigned char*) dtype; + for (; len & 3; len++) + p[len] = 0xf4 - (len & 3); + dtype->struct_v2.len = len - 2; + return len; +} + +int CV2PDB::addClass(codeview_type* dtype, int n_element, int fieldlist, int property, + int derived, int vshape, int structlen, const char*name) +{ + return addAggregate(dtype, true, n_element, fieldlist, property, derived, vshape, structlen, name); +} + +int CV2PDB::addStruct(codeview_type* dtype, int n_element, int fieldlist, int property, + int derived, int vshape, int structlen, const char*name) +{ + return addAggregate(dtype, false, n_element, fieldlist, property, derived, vshape, structlen, name); +} + +int CV2PDB::addPointerType(codeview_type* dtype, int type, int attr) +{ + dtype->pointer_v2.id = LF_POINTER_V2; + dtype->pointer_v2.len = 10; + dtype->pointer_v2.datatype = translateType(type); + dtype->pointer_v2.attribute = attr; + return dtype->generic.len + 2; // no alignment data needed, because always 12 bytes +} +int CV2PDB::addPointerType(unsigned char* dtype, int type, int attr) +{ + return addPointerType((codeview_type*) dtype, type, attr); +} + +int CV2PDB::addFieldMember(codeview_fieldtype* dfieldtype, int attr, int offset, int type, const char* name) +{ + dfieldtype->member_v2.id = v3 ? LF_MEMBER_V3 : LF_MEMBER_V2; + dfieldtype->member_v2.attribute = attr; + dfieldtype->member_v2.offset = offset; + dfieldtype->member_v2.type = translateType(type); + int len = cstrcpy_v(v3, (BYTE*)(&dfieldtype->member_v2 + 1), name); + len += sizeof (dfieldtype->member_v2); + + unsigned char* p = (unsigned char*) dfieldtype; + for (; len & 3; len++) + p[len] = 0xf4 - (len & 3); + return len; +} + +void CV2PDB::checkUserTypeAlloc(int size, int add) +{ + if (cbUserTypes + size >= allocUserTypes) + { + allocUserTypes += add; + userTypes = (BYTE*) realloc(userTypes, allocUserTypes); + } +} + +const codeview_type* CV2PDB::getTypeData(int type) +{ + if (!globalTypeHeader) + return 0; + if (type < 0x1000 || type >= (int) (0x1000 + globalTypeHeader->cTypes + nextUserType)) + return 0; + if (type >= (int) (0x1000 + globalTypeHeader->cTypes)) + return getUserTypeData(type); + + DWORD* offset = (DWORD*)(globalTypeHeader + 1); + BYTE* typeData = (BYTE*)(offset + globalTypeHeader->cTypes); + + return (codeview_type*)(typeData + offset[type - 0x1000]); +} + +const codeview_type* CV2PDB::getUserTypeData(int type) +{ + type -= 0x1000 + globalTypeHeader->cTypes; + if (type < 0 || type >= nextUserType - 0x1000) + return 0; + + int pos = 0; + while(type > 0) + { + const codeview_type* ptype = (codeview_type*)(userTypes + pos); + int len = ptype->generic.len + 2; + pos += len; + type--; + } + return (codeview_type*)(userTypes + pos); +} + +const codeview_type* CV2PDB::findCompleteClassType(const codeview_type* cvtype) +{ + if (!globalTypeHeader) + return 0; + + int value; + int cvleaf_len = numeric_leaf(&value, &cvtype->struct_v1.structlen); + + DWORD* offset = (DWORD*)(globalTypeHeader + 1); + BYTE* typeData = (BYTE*)(offset + globalTypeHeader->cTypes); + for (unsigned int t = 0; t < globalTypeHeader->cTypes; t++) + { + const codeview_type* type = (const codeview_type*)(typeData + offset[t]); + if (type->generic.id == LF_CLASS_V1 || type->generic.id == LF_STRUCTURE_V1) + { + if (!(type->struct_v1.property & kIncomplete)) + { + int leaf_len = numeric_leaf(&value, &type->struct_v1.structlen); + if (pstrcmp((const BYTE*) &cvtype->struct_v1.structlen + cvleaf_len, + (const BYTE*) &type->struct_v1.structlen + leaf_len) == 0) + return type; + } + } + } + return cvtype; +} + +int CV2PDB::findMemberFunctionType(codeview_symbol* lastGProcSym, int thisPtrType) +{ + const codeview_type* proctype = getTypeData(lastGProcSym->proc_v2.proctype); + if (!proctype || proctype->generic.id != LF_PROCEDURE_V1) + return lastGProcSym->proc_v2.proctype; + + const codeview_type* thisPtrData = getTypeData(thisPtrType); + if (!thisPtrData || thisPtrData->generic.id != LF_POINTER_V1) + return lastGProcSym->proc_v2.proctype; + + int thistype = thisPtrData->pointer_v1.datatype; + + // search method with same arguments and return type + DWORD* offset = (DWORD*)(globalTypeHeader + 1); + BYTE* typeData = (BYTE*)(offset + globalTypeHeader->cTypes); + for (unsigned int t = 0; t < globalTypeHeader->cTypes; t++) + { + // remember: mfunction_v1.class_type falsely is pointer, not class type + const codeview_type* type = (const codeview_type*)(typeData + offset[t]); + if (type->generic.id == LF_MFUNCTION_V1 && type->mfunction_v1.this_type == thisPtrType) + { + if (type->mfunction_v1.arglist == proctype->procedure_v1.arglist && + type->mfunction_v1.call == proctype->procedure_v1.call && + type->mfunction_v1.rvtype == proctype->procedure_v1.rvtype) + { + return t + 0x1000; + } + } + } + return lastGProcSym->proc_v2.proctype; +} + +int CV2PDB::sizeofClassType(const codeview_type* cvtype) +{ + if (cvtype->struct_v1.property & kIncomplete) + cvtype = findCompleteClassType(cvtype); + + int value; + int leaf_len = numeric_leaf(&value, &cvtype->struct_v1.structlen); + return value; +} + +int CV2PDB::sizeofBasicType(int type) +{ + int size = type & 7; + int typ = (type & 0xf0) >> 4; + int mode = (type & 0x700) >> 8; + + switch (mode) + { + case 1: + case 2: + case 3: + case 4: + case 5: // pointer variations + return 4; + case 6: // 64-bit pointer + return 8; + case 7: // reserved + return 4; + case 0: // not pointer + switch (typ) + { + case 0: // special, cannot determine + return 4; + case 1: + case 2: // integral types + switch (size) + { + case 0: return 1; + case 1: return 2; + case 2: return 4; + case 3: return 8; + // other reserved + } + return 4; + case 3: // boolean + return 1; + case 4: + case 5: // real and complex + switch (size) + { + case 0: return 4; + case 1: return 8; + case 2: return 10; + case 3: return 16; + case 4: return 6; + // other reserved + } + return 4; + case 6: // special2 (bit or pascal char) + return 1; + case 7: // real int + switch (size) + { + case 0: return 1; // char + case 1: return 4; // wide char + case 2: return 2; + case 3: return 2; + case 4: return 4; + case 5: return 4; + case 6: return 8; + case 7: return 8; + } + } + } + return 4; +} + +int CV2PDB::sizeofType(int type) +{ + if (type < 0x1000) + return sizeofBasicType(type); + + const codeview_type* cvtype = getTypeData(type); + if (!cvtype) + return 4; + + if (cvtype->generic.id == LF_CLASS_V1 || cvtype->generic.id == LF_STRUCTURE_V1) + return sizeofClassType(cvtype); + + if (cvtype->generic.id == LF_OEM_V1 || cvtype->generic.id == LF_OEM_V2) + if (((codeview_oem_type*) (&cvtype->generic + 1))->generic.oemid == 0x42) + return 8; // all D oem types + + // everything else must be pointer or function pointer + return 4; +} + +// to be used when writing new type only to avoid double translation +int CV2PDB::translateType(int type) +{ + if (type < 0x1000) + return type; + const codeview_type* cvtype = getTypeData(type); + if (!cvtype) + return type; + + if (cvtype->generic.id != LF_OEM_V1) + return type; + + codeview_oem_type* oem = (codeview_oem_type*) (&cvtype->generic + 1); + if (oem->generic.oemid == 0x42 && oem->generic.id == 3) + { + if (oem->d_delegate.this_type == 0x403 && oem->d_delegate.func_type == 0x74) + return 0x76; // long + } + + return type; +} + +bool CV2PDB::nameOfBasicType(int type, char* name, int maxlen) +{ + int size = type & 7; + int typ = (type & 0xf0) >> 4; + int mode = (type & 0x700) >> 8; + + switch (typ) + { + case 0: // special, cannot determine + if (size == 3) + strcpy(name, "void"); + else + return setError("nameOfBasicType: unsupported basic special type"); + break; + case 1: // signed integral types + switch (size) + { + case 0: strcpy(name, "byte"); break; // cannot distinguish char und byte + case 1: strcpy(name, "short"); break; + case 2: strcpy(name, "int"); break; + case 3: strcpy(name, "long"); break; + default: + return setError("nameOfBasicType: unsupported basic signed integral type"); + // other reserved + } + break; + case 2: // unsigned integral types + switch (size) + { + case 0: strcpy(name, "ubyte"); break; + case 1: strcpy(name, "ushort"); break; + case 2: strcpy(name, "uint"); break; + case 3: strcpy(name, "ulong"); break; + default: + return setError("nameOfBasicType: unsupported basic unsigned integral type"); + } + break; + case 3: // boolean + strcpy(name, "bool"); + break; + case 4: + switch (size) + { + case 0: strcpy(name, "ifloat"); break; + case 1: strcpy(name, "idouble"); break; + case 2: strcpy(name, "ireal"); break; + default: + return setError("nameOfBasicType: unsupported basic complex type"); + } + break; + case 5: // real and complex + switch (size) + { + case 0: strcpy(name, "float"); break; + case 1: strcpy(name, "double"); break; + case 2: strcpy(name, "real"); break; + default: + return setError("nameOfBasicType: unsupported basic real type"); + } + break; + case 6: // special2 (bit or pascal char) + return setError("nameOfBasicType: unsupported basic special2 type"); + return 1; + case 7: // real int + switch (size) + { + case 0: strcpy(name, "char"); break; + case 1: strcpy(name, "wchar"); break; //?? + case 2: strcpy(name, "short"); break; + case 3: strcpy(name, "ushort"); break; + case 4: strcpy(name, "int"); break; + case 5: strcpy(name, "uint"); break; + case 6: strcpy(name, "long"); break; + case 7: strcpy(name, "ulong"); break; + } + } + if (mode != 0 && mode != 7) + strcat(name, "*"); + return true; +} + +bool CV2PDB::nameOfModifierType(int type, int mod, char* name, int maxlen) +{ + *name = 0; + if(mod & 1) + strcat(name, "const "); + if(mod & 2) + strcat(name, "volatile "); + if(mod & 4) + strcat(name, "unaligned "); + int len = strlen(name); + if(!nameOfType(type, name + len, maxlen - len)) + return false; + return true; +} + +bool CV2PDB::nameOfType(int type, char* name, int maxlen) +{ + if(type < 0x1000) + return nameOfBasicType(type, name, maxlen); + + const codeview_type* ptype = getTypeData(type); + if(!ptype) + return setError("nameOfType: invalid type while retreiving name of type"); + + int leaf_len, value, len; + switch(ptype->generic.id) + { + case LF_CLASS_V1: + case LF_STRUCTURE_V1: + leaf_len = numeric_leaf(&value, &ptype->struct_v1.structlen); + p2ccpy(name, (const BYTE*) &ptype->struct_v1.structlen + leaf_len); + break; + case LF_CLASS_V2: + case LF_STRUCTURE_V2: + leaf_len = numeric_leaf(&value, &ptype->struct_v2.structlen); + p2ccpy(name, (const BYTE*) &ptype->struct_v2.structlen + leaf_len); + break; + case LF_CLASS_V3: + case LF_STRUCTURE_V3: + leaf_len = numeric_leaf(&value, &ptype->struct_v3.structlen); + strcpy(name, (const char*) &ptype->struct_v3.structlen + leaf_len); + break; + + case LF_UNION_V1: + leaf_len = numeric_leaf(&value, &ptype->union_v1.un_len); + p2ccpy(name, (const BYTE*) &ptype->union_v1.un_len + leaf_len); + break; + case LF_UNION_V2: + leaf_len = numeric_leaf(&value, &ptype->union_v2.un_len); + p2ccpy(name, (const BYTE*) &ptype->union_v2.un_len + leaf_len); + break; + case LF_UNION_V3: + leaf_len = numeric_leaf(&value, &ptype->union_v3.un_len); + strcpy(name, (const char*) &ptype->union_v3.un_len + leaf_len); + break; + + case LF_POINTER_V1: + if(!nameOfType(ptype->pointer_v1.datatype, name, maxlen)) + return false; + strcat(name,"*"); + break; + case LF_POINTER_V2: + if(!nameOfType(ptype->pointer_v2.datatype, name, maxlen)) + return false; + strcat(name,"*"); + break; + + case LF_ARRAY_V1: + if(!nameOfType(ptype->array_v1.elemtype, name, maxlen)) + return false; + leaf_len = numeric_leaf(&value, &ptype->array_v1.arrlen); + len = strlen(name); + sprintf(name + len, "[%d]", leaf_len); + break; + case LF_ARRAY_V2: + if(!nameOfType(ptype->array_v2.elemtype, name, maxlen)) + return false; + leaf_len = numeric_leaf(&value, &ptype->array_v2.arrlen); + len = strlen(name); + sprintf(name + len, "[%d]", leaf_len); + break; + case LF_ARRAY_V3: + if(!nameOfType(ptype->array_v3.elemtype, name, maxlen)) + return false; + leaf_len = numeric_leaf(&value, &ptype->array_v3.arrlen); + len = strlen(name); + sprintf(name + len, "[%d]", leaf_len); + break; + + case LF_ENUM_V1: + strcpy(name, "enum "); + p2ccpy(name + 5, (const BYTE*) &ptype->enumeration_v1.p_name); + break; + case LF_ENUM_V2: + strcpy(name, "enum "); + p2ccpy(name + 5, (const BYTE*) &ptype->enumeration_v2.p_name); + break; + case LF_ENUM_V3: + strcpy(name, "enum "); + strcpy(name + 5, ptype->enumeration_v3.name); + break; + + case LF_MODIFIER_V1: + if (!nameOfModifierType(ptype->modifier_v1.type, ptype->modifier_v1.attribute, name, maxlen)) + return false; + break; + case LF_MODIFIER_V2: + if (!nameOfModifierType(ptype->modifier_v2.type, ptype->modifier_v2.attribute, name, maxlen)) + return false; + break; + + case LF_PROCEDURE_V1: + if(!nameOfType(ptype->procedure_v1.rvtype, name, maxlen)) + return false; + strcat(name, "()"); + break; + case LF_PROCEDURE_V2: + if(!nameOfType(ptype->procedure_v2.rvtype, name, maxlen)) + return false; + strcat(name, "()"); + break; + + case LF_MFUNCTION_V1: + if(!nameOfType(ptype->mfunction_v1.rvtype, name, maxlen)) + return false; + strcat(name, "()"); + break; + case LF_MFUNCTION_V2: + if(!nameOfType(ptype->mfunction_v2.rvtype, name, maxlen)) + return false; + strcat(name, "()"); + break; + + case LF_OEM_V1: + if (!nameOfOEMType((codeview_oem_type*) (&ptype->generic + 1), name, maxlen)) + return false; + break; + default: + return setError("nameOfType: unsupported type"); + } + return true; +} + +bool CV2PDB::nameOfDynamicArray(int indexType, int elemType, char* name, int maxlen) +{ + if (!nameOfType(elemType, name, maxlen)) + return false; + if (Dversion >= 2 && strcmp(name, "const char") == 0) + strcpy(name, "string"); + else if (Dversion < 2 && strcmp(name, "char") == 0) + strcpy(name, "string"); + else + strcat (name, "[]"); + // sprintf(name, "dyn_array<%X,%X>", indexType, elemType); + return true; +} + +bool CV2PDB::nameOfAssocArray(int indexType, int elemType, char* name, int maxlen) +{ + strcpy(name, "aa<"); + int len = strlen(name); + if (!nameOfType(elemType, name + len, maxlen - len)) + return false; + strcat(name, "["); + len = strlen(name); + if (!nameOfType(indexType, name + len, maxlen - len)) + return false; + strcat(name,"]>"); + + // sprintf(name, "assoc_array<%X,%X>", indexType, elemType); + return true; +} + +bool CV2PDB::nameOfDelegate(int thisType, int funcType, char* name, int maxlen) +{ + strcat(name, "delegate "); + int len = strlen(name); + if (!nameOfType(funcType, name + len, maxlen - len)) + return false; + // sprintf(name, "delegate<%X,%X>", indexType, elemType); + return true; +} + +bool CV2PDB::nameOfOEMType(codeview_oem_type* oem, char* name, int maxlen) +{ + if (oem->generic.oemid == 0x42 && oem->generic.id == 1) + return nameOfDynamicArray(oem->d_dyn_array.index_type, oem->d_dyn_array.elem_type, name, maxlen); + if (oem->generic.oemid == 0x42 && oem->generic.id == 2) + return nameOfAssocArray(oem->d_assoc_array.key_type, oem->d_assoc_array.elem_type, name, maxlen); + if (oem->generic.oemid == 0x42 && oem->generic.id == 3) + return nameOfDelegate(oem->d_delegate.this_type, oem->d_delegate.func_type, name, maxlen); + + return setError("nameOfOEMType: unknown OEM type record"); +} + +const char* CV2PDB::appendDynamicArray(int indexType, int elemType) +{ + codeview_reftype* rdtype; + codeview_type* dtype; + + checkUserTypeAlloc(); + + // nextUserType: pointer to elemType + cbUserTypes += addPointerType(userTypes + cbUserTypes, elemType); + + // nextUserType + 1: field list (size, array) + rdtype = (codeview_reftype*) (userTypes + cbUserTypes); + rdtype->fieldlist.id = LF_FIELDLIST_V2; + + // member indexType length + codeview_fieldtype* dfieldtype = (codeview_fieldtype*)rdtype->fieldlist.list; + int len1 = addFieldMember(dfieldtype, 1, 0, indexType, "length"); + + // member elemType* data[] + dfieldtype = (codeview_fieldtype*)(rdtype->fieldlist.list + len1); + int len2 = addFieldMember(dfieldtype, 1, 4, nextUserType, "data"); + + rdtype->fieldlist.len = len1 + len2 + 2; + cbUserTypes += rdtype->fieldlist.len + 2; + + static char name[256]; + nameOfDynamicArray(indexType, elemType, name, sizeof(name)); + + // nextUserType + 2: struct + dtype = (codeview_type*) (userTypes + cbUserTypes); + cbUserTypes += addClass(dtype, 2, nextUserType + 1, 0, 0, 0, 8, name); + + nextUserType += 3; + addUdtSymbol(nextUserType - 1, name); + return name; +} + +const char* CV2PDB::appendAssocArray(int keyType, int elemType) +{ + // rebuilding types + // struct aaA { + // aaA *left; + // aaA *right; + // hash_t hash; + // keyType key; + // elemType value; + // }; + + codeview_reftype* rdtype; + codeview_type* dtype; + codeview_fieldtype* dfieldtype; + + checkUserTypeAlloc(); + + static char name[256]; +#if 1 + char keyname[256]; + char elemname[256]; + if(!nameOfType(keyType, keyname, sizeof(keyname))) + return false; + if(!nameOfType(elemType, elemname, sizeof(elemname))) + return false; + + sprintf(name, "internal@aaA<%s,%s>", keyname, elemname); + + // undefined struct aaA + dtype = (codeview_type*) (userTypes + cbUserTypes); + cbUserTypes += addClass(dtype, 0, 0, kIncomplete, 0, 0, 0, name); + int aaAType = nextUserType++; + + // pointer to aaA + cbUserTypes += addPointerType(userTypes + cbUserTypes, aaAType); + int aaAPtrType = nextUserType++; + + // field list (left, right, hash, key, value) + rdtype = (codeview_reftype*) (userTypes + cbUserTypes); + rdtype->fieldlist.id = LF_FIELDLIST_V2; + + // member aaA* left + dfieldtype = (codeview_fieldtype*)rdtype->fieldlist.list; + int len1 = addFieldMember(dfieldtype, 1, 0, aaAPtrType, "left"); + + dfieldtype = (codeview_fieldtype*)(rdtype->fieldlist.list + len1); + int len2 = addFieldMember(dfieldtype, 1, 4, aaAPtrType, "right"); + + dfieldtype = (codeview_fieldtype*)(rdtype->fieldlist.list + len1 + len2); + int len3 = addFieldMember(dfieldtype, 1, 8, 0x74, "hash"); + + dfieldtype = (codeview_fieldtype*)(rdtype->fieldlist.list + len1 + len2 + len3); + int len4 = addFieldMember(dfieldtype, 1, 12, keyType, "key"); + + int typeLen = sizeofType(keyType); + typeLen = (typeLen + 3) & ~3; // align to 4 byte + dfieldtype = (codeview_fieldtype*)(rdtype->fieldlist.list + len1 + len2 + len3 + len4); + int len5 = addFieldMember(dfieldtype, 1, 12 + typeLen, elemType, "value"); + + int elemLen = sizeofType(elemType); + elemLen = (elemLen + 3) & ~3; // align to 4 byte + + rdtype->fieldlist.len = len1 + len2 + len3 + len4 + len5 + 2; + cbUserTypes += rdtype->fieldlist.len + 2; + int fieldListType = nextUserType++; + + dtype = (codeview_type*) (userTypes + cbUserTypes); + cbUserTypes += addClass(dtype, 5, fieldListType, 0, 0, 0, 12 + typeLen + elemLen, name); + addUdtSymbol(nextUserType, name); + int completeAAAType = nextUserType++; + + // struct BB { + // aaA*[] b; + // size_t nodes; // total number of aaA nodes + // }; + const char* dynArray = appendDynamicArray(0x74, aaAPtrType); + int dynArrType = nextUserType - 1; + + // field list (aaA*[] b, size_t nodes) + rdtype = (codeview_reftype*) (userTypes + cbUserTypes); + rdtype->fieldlist.id = LF_FIELDLIST_V2; + + // member aaA*[] b + dfieldtype = (codeview_fieldtype*)rdtype->fieldlist.list; + len1 = addFieldMember(dfieldtype, 1, 0, dynArrType, "b"); + + dfieldtype = (codeview_fieldtype*)(rdtype->fieldlist.list + len1); + len2 = addFieldMember(dfieldtype, 1, 8, 0x74, "nodes"); + + rdtype->fieldlist.len = len1 + len2 + 2; + cbUserTypes += rdtype->fieldlist.len + 2; + int bbFieldListType = nextUserType++; + + sprintf(name, "internal@BB<%s,%s>", keyname, elemname); + + // struct BB + dtype = (codeview_type*) (userTypes + cbUserTypes); + cbUserTypes += addClass(dtype, 2, bbFieldListType, 0, 0, 0, 12, name); + addUdtSymbol(nextUserType, name); + int bbType = nextUserType++; + + // struct AA { + // BB* a; + // }; + // pointer to BB + cbUserTypes += addPointerType(userTypes + cbUserTypes, bbType); + int bbPtrType = nextUserType++; +#else + int len1, bbPtrType = elemType; +#endif + + // field list (BB* aa) + rdtype = (codeview_reftype*) (userTypes + cbUserTypes); + rdtype->fieldlist.id = LF_FIELDLIST_V2; + + // member aaA*[] b + dfieldtype = (codeview_fieldtype*)rdtype->fieldlist.list; + len1 = addFieldMember(dfieldtype, 1, 0, bbPtrType, "a"); + + rdtype->fieldlist.len = len1 + 2; + cbUserTypes += rdtype->fieldlist.len + 2; + int aaFieldListType = nextUserType++; + + nameOfAssocArray(keyType, elemType, name, sizeof(name)); + dtype = (codeview_type*) (userTypes + cbUserTypes); + cbUserTypes += addClass(dtype, 1, aaFieldListType, 0, 0, 0, 4, name); + + addUdtSymbol(nextUserType, name); + nextUserType++; + + return name; +} + +const char* CV2PDB::appendDelegate(int thisType, int funcType) +{ + codeview_reftype* rdtype; + codeview_type* dtype; + + checkUserTypeAlloc(); + + // nextUserType + 1: pointer to funcType + cbUserTypes += addPointerType(userTypes + cbUserTypes, funcType); + + bool thisTypeIsVoid = (thisType == 0x403); + if (!thisTypeIsVoid) + { + // nextUserType: pointer to thisType + dtype = (codeview_type*) (userTypes + cbUserTypes); + cbUserTypes += addPointerType(dtype, thisType); + } + + // nextUserType + 2: field list (size, array) + rdtype = (codeview_reftype*) (userTypes + cbUserTypes); + rdtype->fieldlist.id = LF_FIELDLIST_V2; + + // member thisType* thisptr + codeview_fieldtype* dfieldtype = (codeview_fieldtype*)rdtype->fieldlist.list; + int len1 = addFieldMember(dfieldtype, 1, 0, thisTypeIsVoid ? thisType : nextUserType + 1, "thisptr"); + + // member funcType* funcptr + dfieldtype = (codeview_fieldtype*)(rdtype->fieldlist.list + len1); + int len2 = addFieldMember(dfieldtype, 1, 4, nextUserType, "funcptr"); + + rdtype->fieldlist.len = len1 + len2 + 2; + cbUserTypes += rdtype->fieldlist.len + 2; + + static char name[256]; + nameOfDelegate(thisType, funcType, name, sizeof(name)); + + // nextUserType + 3: struct delegate<> + dtype = (codeview_type*) (userTypes + cbUserTypes); + cbUserTypes += addClass(dtype, 2, nextUserType + (thisTypeIsVoid ? 1 : 2), 0, 0, 0, 8, name); + + nextUserType += thisTypeIsVoid ? 3 : 4; + addUdtSymbol(nextUserType - 1, name); + return name; +} + +int CV2PDB::appendObjectType (int object_derived_type) +{ + checkUserTypeAlloc(); + + // append object type info + int typeNo = nextUserType; + codeview_reftype* rdtype = (codeview_reftype*) (userTypes + cbUserTypes); + + // vtable + rdtype->generic.len = 6; + rdtype->generic.id = LF_VTSHAPE_V1; + ((unsigned short*) (&rdtype->generic + 1))[0] = 1; + ((unsigned short*) (&rdtype->generic + 1))[1] = 0xf150; + cbUserTypes += rdtype->generic.len + 2; + + // vtable* + codeview_type* dtype = (codeview_type*) (userTypes + cbUserTypes); + dtype->pointer_v2.id = LF_POINTER_V2; + dtype->pointer_v2.len = 10; + dtype->pointer_v2.datatype = typeNo; + dtype->pointer_v2.attribute = 0x800A; + cbUserTypes += dtype->generic.len + 2; + + // field list + rdtype = (codeview_reftype*) (userTypes + cbUserTypes); + rdtype->fieldlist.id = LF_FIELDLIST_V2; + codeview_fieldtype* dfieldtype = (codeview_fieldtype*)rdtype->fieldlist.list; + dfieldtype->vfunctab_v2.id = LF_VFUNCTAB_V2; // id correct? + dfieldtype->vfunctab_v2._pad0 = 0; + dfieldtype->vfunctab_v2.type = typeNo + 1; // vtable* + rdtype->fieldlist.len = sizeof(dfieldtype->vfunctab_v2) + 2; + cbUserTypes += rdtype->generic.len + 2; + +#define OBJECT_SYMBOL "object@Object" + + dtype = (codeview_type*) (userTypes + cbUserTypes); + dtype->struct_v2.id = v3 ? LF_CLASS_V3 : LF_CLASS_V2; + dtype->struct_v2.n_element = 1; + dtype->struct_v2.fieldlist = typeNo + 2; + dtype->struct_v2.property = 0; + dtype->struct_v2.derived = object_derived_type; + dtype->struct_v2.vshape = typeNo; + dtype->struct_v2.structlen = 4; + int len = cstrcpy_v (v3, (BYTE*) (&dtype->struct_v2 + 1), OBJECT_SYMBOL); + len += sizeof (dtype->struct_v2); + for (; len & 3; len++) + userTypes[cbUserTypes + len] = 0xf4 - (len & 3); + dtype->struct_v2.len = len - 2; + cbUserTypes += dtype->generic.len + 2; + + objectType = typeNo + 3; + nextUserType += 4; + + addUdtSymbol(objectType, OBJECT_SYMBOL); + return objectType; +} + +int CV2PDB::appendPointerType(int pointedType, int attr) +{ + checkUserTypeAlloc(); + + cbUserTypes += addPointerType(userTypes + cbUserTypes, pointedType, attr); + nextUserType++; + + return nextUserType - 1; +} + +bool CV2PDB::initGlobalTypes() +{ + int object_derived_type = 0; + for (int m = 0; m < countEntries; m++) + { + OMFDirEntry* entry = img.getCVEntry(m); + if(entry->SubSection == sstGlobalTypes) + { + globalTypeHeader = img.CVP(entry->lfo); + DWORD* offset = img.CVP(entry->lfo + sizeof(OMFGlobalTypes)); + BYTE* typeData = img.CVP(entry->lfo + sizeof(OMFGlobalTypes) + 4*globalTypeHeader->cTypes); + + if (globalTypes) + return setError("only one global type entry expected"); + + pointerTypes = new int[globalTypeHeader->cTypes]; + memset(pointerTypes, 0, globalTypeHeader->cTypes * sizeof(*pointerTypes)); + + globalTypes = (unsigned char*) malloc(entry->cb + 4); + allocGlobalTypes = entry->cb + 4; + if (!globalTypes) + return setError("Out of memory"); + *(DWORD*) globalTypes = 4; + cbGlobalTypes = 4; + + nextUserType = globalTypeHeader->cTypes + 0x1000; + + for (unsigned int t = 0; t < globalTypeHeader->cTypes && !hadError(); t++) + { + const codeview_type* type = (codeview_type*)(typeData + offset[t]); + const codeview_reftype* rtype = (codeview_reftype*)(typeData + offset[t]); + int leaf_len, value; + + int len = type->generic.len + 2; + if (cbGlobalTypes + len + 1000 > allocGlobalTypes) + { + allocGlobalTypes += len + 1000; + globalTypes = (unsigned char*) realloc(globalTypes, allocGlobalTypes); + } + + unsigned int clsstype; + codeview_type* dtype = (codeview_type*) (globalTypes + cbGlobalTypes); + codeview_reftype* rdtype = (codeview_reftype*) (globalTypes + cbGlobalTypes); + + // for debugging, cancel special processing after the limit + unsigned int typeLimit = 0x7fffffff; + if (t > typeLimit) + { + dtype->pointer_v2.id = LF_POINTER_V2; + dtype->pointer_v2.len = 10; + dtype->pointer_v2.datatype = 0x74; + dtype->pointer_v2.attribute = 0x800a; + cbGlobalTypes += 12; + continue; + } + + switch (type->generic.id) + { + case LF_OEM_V1: + { + codeview_oem_type* oem = (codeview_oem_type*) (&type->generic + 1); + + if (oem->generic.oemid == 0x42 && oem->generic.id == 1) + { + const char* name = appendDynamicArray(oem->d_dyn_array.index_type, oem->d_dyn_array.elem_type); + len = addClass(dtype, 0, 0, kIncomplete, 0, 0, 0, name); + } + else if (oem->generic.oemid == 0x42 && oem->generic.id == 3) + { + const char* name = appendDelegate(oem->d_delegate.this_type, oem->d_delegate.func_type); + len = addClass(dtype, 0, 0, kIncomplete, 0, 0, 0, name); + } + else if (oem->generic.oemid == 0x42 && oem->generic.id == 2) + { + const char* name = appendAssocArray(oem->d_assoc_array.key_type, oem->d_assoc_array.elem_type); + len = addClass(dtype, 0, 0, kIncomplete, 0, 0, 0, name); + } + else + { + dtype->pointer_v2.id = LF_POINTER_V2; + dtype->pointer_v2.len = 10; + dtype->pointer_v2.datatype = oem->d_dyn_array.elem_type; + dtype->pointer_v2.attribute = 0x800a; + len = 12; + } + break; + } + case LF_ARGLIST_V1: + rdtype->arglist_v2.id = LF_ARGLIST_V2; + rdtype->arglist_v2.num = rtype->arglist_v1.num; + for (int i = 0; i < rtype->arglist_v1.num; i++) + rdtype->arglist_v2.args [i] = translateType(rtype->arglist_v1.args [i]); + len = sizeof(rdtype->arglist_v2) + 4 * rdtype->arglist_v2.num - sizeof(rdtype->arglist_v2.args); + break; + + case LF_PROCEDURE_V1: + dtype->procedure_v2.id = LF_PROCEDURE_V2; + dtype->procedure_v2.rvtype = translateType(type->procedure_v1.rvtype); + dtype->procedure_v2.call = type->procedure_v1.call; + dtype->procedure_v2.reserved = type->procedure_v1.reserved; + dtype->procedure_v2.params = type->procedure_v1.params; + dtype->procedure_v2.arglist = type->procedure_v1.arglist; + len = sizeof(dtype->procedure_v2); + break; + + case LF_STRUCTURE_V1: + dtype->struct_v2.id = v3 ? LF_STRUCTURE_V3 : LF_STRUCTURE_V2; + goto LF_CLASS_V1_struct; + case LF_CLASS_V1: + //dtype->struct_v2.id = v3 ? LF_STRUCTURE_V3 : LF_STRUCTURE_V2; + dtype->struct_v2.id = v3 ? LF_CLASS_V3 : LF_CLASS_V2; + LF_CLASS_V1_struct: + dtype->struct_v2.n_element = type->struct_v1.n_element; + dtype->struct_v2.fieldlist = type->struct_v1.fieldlist; + dtype->struct_v2.property = type->struct_v1.property | 0x200; + dtype->struct_v2.derived = type->struct_v1.derived; + dtype->struct_v2.vshape = type->struct_v1.vshape; + leaf_len = numeric_leaf(&value, &type->struct_v1.structlen); + memcpy (&dtype->struct_v2.structlen, &type->struct_v1.structlen, leaf_len); + len = pstrcpy_v(v3, (BYTE*) &dtype->struct_v2.structlen + leaf_len, + (const BYTE*) &type->struct_v1.structlen + leaf_len); + // alternate name can be added here? +#if 0 + if (dtype->struct_v2.id == LF_CLASS_V2) + len += pstrcpy((BYTE*) &dtype->struct_v2.structlen + leaf_len + len, + (const BYTE*) &type->struct_v1.structlen + leaf_len); +#endif + len += leaf_len + sizeof(dtype->struct_v2) - sizeof(type->struct_v2.structlen); + + // remember type index of derived list for object.Object + if (type->struct_v1.derived) + if (memcmp((char*) &type->struct_v1.structlen + leaf_len, "\x0dobject.Object", 14) == 0) + object_derived_type = type->struct_v1.derived; + break; + + case LF_UNION_V1: + dtype->union_v2.id = v3 ? LF_UNION_V3 : LF_UNION_V2; + dtype->union_v2.count = type->union_v1.count; + dtype->union_v2.fieldlist = type->struct_v1.fieldlist; + dtype->union_v2.property = type->struct_v1.property; + leaf_len = numeric_leaf(&value, &type->union_v1.un_len); + memcpy (&dtype->union_v2.un_len, &type->union_v1.un_len, leaf_len); + len = pstrcpy_v(v3, (BYTE*) &dtype->union_v2.un_len + leaf_len, + (const BYTE*) &type->union_v1.un_len + leaf_len); + len += leaf_len + sizeof(dtype->union_v2) - sizeof(type->union_v2.un_len); + break; + + case LF_POINTER_V1: + dtype->pointer_v2.id = LF_POINTER_V2; + dtype->pointer_v2.datatype = translateType(type->pointer_v1.datatype); + if (type->pointer_v1.datatype >= 0x1000 && (type->pointer_v1.attribute & 0xE0) == 0) + { + if (thisIsNotRef) // const pointer for this + pointerTypes[t] = appendPointerType(type->pointer_v1.datatype, + type->pointer_v1.attribute | 0x400); + dtype->pointer_v2.attribute = type->pointer_v1.attribute | 0x20; // convert to reference + } + else + dtype->pointer_v2.attribute = type->pointer_v1.attribute; + len = 12; // ignore p_name field in type->pointer_v1/2 + break; + + case LF_ARRAY_V1: + dtype->array_v2.id = v3 ? LF_ARRAY_V3 : LF_ARRAY_V2; + dtype->array_v2.elemtype = translateType(type->array_v1.elemtype); + dtype->array_v2.idxtype = translateType(type->array_v1.idxtype); + leaf_len = numeric_leaf(&value, &type->array_v1.arrlen); + memcpy (&dtype->array_v2.arrlen, &type->array_v1.arrlen, leaf_len); + len = pstrcpy_v(v3, (BYTE*) &dtype->array_v2.arrlen + leaf_len, + (const BYTE*) &type->array_v1.arrlen + leaf_len); + len += leaf_len + sizeof(dtype->array_v2) - sizeof(dtype->array_v2.arrlen); + // followed by name + break; + + case LF_MFUNCTION_V1: + dtype->mfunction_v2.id = LF_MFUNCTION_V2; + dtype->mfunction_v2.rvtype = translateType(type->mfunction_v1.rvtype); + clsstype = type->mfunction_v1.class_type; + dtype->mfunction_v2.class_type = translateType(clsstype); + if (clsstype >= 0x1000 && clsstype < 0x1000 + globalTypeHeader->cTypes) + { + // fix class_type to point to class, not pointer to class + codeview_type* ctype = (codeview_type*)(typeData + offset[clsstype - 0x1000]); + if (ctype->generic.id == LF_POINTER_V1) + dtype->mfunction_v2.class_type = translateType(ctype->pointer_v1.datatype); + } + dtype->mfunction_v2.this_type = translateType(type->mfunction_v1.this_type); + dtype->mfunction_v2.call = type->mfunction_v1.call; + dtype->mfunction_v2.reserved = type->mfunction_v1.reserved; + dtype->mfunction_v2.params = type->mfunction_v1.params; + dtype->mfunction_v2.arglist = type->mfunction_v1.arglist; + dtype->mfunction_v2.this_adjust = type->mfunction_v1.this_adjust; + len = sizeof(dtype->mfunction_v2); + break; + + case LF_ENUM_V1: + dtype->enumeration_v2.id = v3 ? LF_ENUM_V3 : LF_ENUM_V2; + dtype->enumeration_v2.count = type->enumeration_v1.count; + dtype->enumeration_v2.type = translateType(type->enumeration_v1.type); + dtype->enumeration_v2.fieldlist = type->enumeration_v1.fieldlist; + dtype->enumeration_v2.property = type->enumeration_v1.property; + len = pstrcpy_v (v3, (BYTE*) &dtype->enumeration_v2.p_name, (BYTE*) &type->enumeration_v1.p_name); + len += sizeof(dtype->enumeration_v2) - sizeof(dtype->enumeration_v2.p_name); + break; + + case LF_FIELDLIST_V1: + case LF_FIELDLIST_V2: + rdtype->fieldlist.id = LF_FIELDLIST_V2; + len = addFields(rdtype, rtype, allocGlobalTypes - cbGlobalTypes) + 4; + break; + + case LF_DERIVED_V1: + rdtype->derived_v2.id = LF_DERIVED_V2; + rdtype->derived_v2.num = rtype->derived_v1.num; + for (int i = 0; i < rtype->derived_v1.num; i++) + if (rtype->derived_v1.drvdcls[i] < 0x1000) // + globalTypeHeader->cTypes) + rdtype->derived_v2.drvdcls[i] = translateType(rtype->derived_v1.drvdcls[i] + 0xfff); + else + rdtype->derived_v2.drvdcls[i] = translateType(rtype->derived_v1.drvdcls[i]); + len = sizeof(rdtype->derived_v2) + 4 * rdtype->derived_v2.num - sizeof(rdtype->derived_v2.drvdcls); + break; + + case LF_VTSHAPE_V1: // no alternate version known + len = ((short*)type)[2]; // number of nibbles following + len = 6 + (len + 1) / 2; // cut-off extra bytes + memcpy(dtype, type, len); + //*((char*)dtype + 6) = 0x50; + break; + + case LF_METHODLIST_V1: + { + dtype->generic.id = LF_METHODLIST_V2; + const unsigned short* pattr = (const unsigned short*)((const char*)type + 4); + unsigned* dpattr = (unsigned*)((char*)dtype + 4); + while ((const char*)pattr + 4 <= (const char*)type + type->generic.len + 2) + { + // type translation? + switch ((*pattr >> 2) & 7) + { + case 4: + case 6: + *dpattr++ = *pattr++; + default: + *dpattr++ = *pattr++; + *dpattr++ = *pattr++; + break; + } + } + len = (char*) dpattr - (char*)dtype; + break; + } + case LF_MODIFIER_V1: + dtype->modifier_v2.id = LF_MODIFIER_V2; + dtype->modifier_v2.attribute = type->modifier_v1.attribute; + dtype->modifier_v2.type = translateType(type->modifier_v1.type); + len = sizeof(dtype->modifier_v2); + break; + + default: + memcpy(dtype, type, len); + break; + } + + for (; len & 3; len++) + globalTypes[cbGlobalTypes + len] = 0xf4 - (len & 3); + dtype->generic.len = len - 2; + + cbGlobalTypes += len; + } + +#if 1 + appendObjectType (object_derived_type); +#endif +#if 1 + if (cbGlobalTypes + cbUserTypes > allocGlobalTypes) + { + allocGlobalTypes += cbUserTypes + 1000; + globalTypes = (unsigned char*) realloc(globalTypes, allocGlobalTypes); + } + + memcpy (globalTypes + cbGlobalTypes, userTypes, cbUserTypes); + cbGlobalTypes += cbUserTypes; +#endif + } + } + return !hadError(); +} + +bool CV2PDB::addTypes() +{ + if (!globalTypes) + return true; + + for (int m = 0; m < countEntries; m++) + { + OMFDirEntry* entry = img.getCVEntry(m); + if(entry->SubSection == sstSrcModule) + { + mspdb::Mod* mod = modules[entry->iMod]; + if (!mod) + return setError("sstSrcModule for non-existing module"); + + int cb = cbGlobalTypes; + int rc = mod->AddTypes(globalTypes, cb); + if (rc <= 0) + return setError("cannot add type info to module"); + } + } + return true; +} + +bool CV2PDB::addSrcLines() +{ + for (int m = 0; m < countEntries; m++) + { + OMFDirEntry* entry = img.getCVEntry(m); + if(entry->SubSection == sstSrcModule) + { + mspdb::Mod* mod = modules[entry->iMod]; + if (!mod) + return setError("sstSrcModule for non-existing module"); + + OMFSourceModule* sourceModule = img.CVP(entry->lfo); + int* segStartEnd = img.CVP(entry->lfo + 4 + 4 * sourceModule->cFile); + short* seg = img.CVP(entry->lfo + 4 + 4 * sourceModule->cFile + 8 * sourceModule->cSeg); + + for (int f = 0; f < sourceModule->cFile; f++) + { + int cvoff = entry->lfo + sourceModule->baseSrcFile[f]; + OMFSourceFile* sourceFile = img.CVP (cvoff); + int* lnSegStartEnd = img.CVP(cvoff + 4 + 4 * sourceFile->cSeg); + BYTE* pname = (BYTE*)(lnSegStartEnd + 2 * sourceFile->cSeg); + char* name = p2c (pname); + + for (int s = 0; s < sourceFile->cSeg; s++) + { + int lnoff = entry->lfo + sourceFile->baseSrcLn[s]; + OMFSourceLine* sourceLine = img.CVP (lnoff); + short* lineNo = img.CVP (lnoff + 4 + 4 * sourceLine->cLnOff); + + int segoff = lnSegStartEnd[2*s]; + int seglength = lnSegStartEnd[2*s + 1] - segoff; + + mspdb::LineInfoEntry* lineInfo = new mspdb::LineInfoEntry[sourceLine->cLnOff]; + for (int ln = 0; ln < sourceLine->cLnOff; ln++) + { + lineInfo[ln].offset = sourceLine->offset[ln] - segoff; + lineInfo[ln].line = lineNo[ln] - lineNo[0]; + } + int rc = mod->AddLines(name, sourceLine->Seg, segoff, seglength, segoff, lineNo[0], + (unsigned char*) lineInfo, sourceLine->cLnOff * sizeof(*lineInfo)); + if (rc <= 0) + return setError("cannot add line number info to module"); + } + } + } + } + return true; +} + +bool CV2PDB::addPublics() +{ + for (int m = 0; m < countEntries; m++) + { + OMFDirEntry* entry = img.getCVEntry(m); + if(entry->SubSection == sstGlobalPub) + { + mspdb::Mod* mod = 0; + if (entry->iMod < countEntries) + mod = modules[entry->iMod]; + + OMFSymHash* header = img.CVP(entry->lfo); + BYTE* symbols = img.CVP(entry->lfo + sizeof(OMFSymHash)); + int length; + for (unsigned int i = 0; i < header->cbSymbol; i += length) + { + union codeview_symbol* sym = (union codeview_symbol*)(symbols + i); + length = sym->generic.len + 2; + if (!sym->generic.id || length < 4) + break; + + int rc; + switch (sym->generic.id) + { + case S_GDATA_V1: + case S_LDATA_V1: + case S_PUB_V1: + char symname[2000]; + dsym2c((BYTE*)sym->data_v1.p_name.name, sym->data_v1.p_name.namelen, symname, sizeof(symname)); + int type = translateType(sym->data_v1.symtype); + if (mod) + rc = mod->AddPublic2(symname, sym->data_v1.segment, sym->data_v1.offset, type); + else + rc = dbi->AddPublic2(symname, sym->data_v1.segment, sym->data_v1.offset, type); + if (rc <= 0) + return setError("cannot add public"); + break; + } + } + } + } + return true; +} + +bool CV2PDB::initGlobalSymbols() +{ + for (int m = 0; m < countEntries; m++) + { + OMFDirEntry* entry = img.getCVEntry(m); + if (entry->SubSection == sstGlobalSym) + { + BYTE* symbols = img.CVP(entry->lfo); + OMFSymHash* header = (OMFSymHash*) symbols; + globalSymbols = symbols + sizeof(OMFSymHash); + cbGlobalSymbols = header->cbSymbol; + } + if (entry->SubSection == sstStaticSym) + { + BYTE* symbols = img.CVP(entry->lfo); + OMFSymHash* header = (OMFSymHash*) symbols; + staticSymbols = symbols + sizeof(OMFSymHash); + cbStaticSymbols = header->cbSymbol; + } + } + return true; +} + +// returns new destSize +int CV2PDB::copySymbols(BYTE* srcSymbols, int srcSize, BYTE* destSymbols, int destSize) +{ + codeview_symbol* lastGProcSym = 0; + int type, length, destlength; + for (int i = 0; i < srcSize; i += length) + { + codeview_symbol* sym = (codeview_symbol*)(srcSymbols + i); + length = sym->generic.len + 2; + if (!sym->generic.id || length < 4) + break; + + codeview_symbol* dsym = (codeview_symbol*)(destSymbols + destSize); + memcpy(dsym, sym, length); + destlength = length; + + switch (sym->generic.id) + { + case S_UDT_V1: + dsym->udt_v1.type = translateType(sym->udt_v1.type); + //sym->udt_v1.type = 0x101e; + break; + + case S_LDATA_V1: + dsym->data_v2.id = v3 ? S_LDATA_V3 : S_LDATA_V2; + goto case_DATA_V1; + case S_GDATA_V1: + dsym->data_v2.id = v3 ? S_GDATA_V3 : S_GDATA_V2; + case_DATA_V1: + dsym->data_v2.symtype = translateType(sym->data_v1.symtype); + dsym->data_v2.offset = sym->data_v1.offset; + dsym->data_v2.offset = sym->data_v1.offset; + dsym->data_v2.segment = sym->data_v1.segment; + destlength = pstrcpy_v (v3, (BYTE*) &dsym->data_v2.p_name, (BYTE*) &sym->data_v1.p_name); + destlength += (BYTE*) &dsym->data_v2.p_name - (BYTE*) dsym; + dsym->data_v2.len = destlength - 2; + break; + + case S_LPROC_V1: + dsym->proc_v2.id = v3 ? S_LPROC_V3 : S_LPROC_V2; + goto case_PROC_V1; + case S_GPROC_V1: + dsym->proc_v2.id = v3 ? S_GPROC_V3 : S_GPROC_V2; + case_PROC_V1: + dsym->proc_v2.pparent = sym->proc_v1.pparent; + dsym->proc_v2.pend = sym->proc_v1.pend; + dsym->proc_v2.next = sym->proc_v1.next; + dsym->proc_v2.proc_len = sym->proc_v1.proc_len; + dsym->proc_v2.debug_start = sym->proc_v1.debug_start; + dsym->proc_v2.debug_end = sym->proc_v1.debug_end; + dsym->proc_v2.offset = sym->proc_v1.offset; + dsym->proc_v2.segment = sym->proc_v1.segment; + dsym->proc_v2.proctype = translateType(sym->proc_v1.proctype); + dsym->proc_v2.flags = sym->proc_v1.flags; + + destlength = pstrcpy_v (v3, (BYTE*) &dsym->proc_v2.p_name, (BYTE*) &sym->proc_v1.p_name); + destlength += (BYTE*) &dsym->proc_v2.p_name - (BYTE*) dsym; + dsym->data_v2.len = destlength - 2; + + lastGProcSym = dsym; + break; + + case S_BPREL_V1: + type = dsym->stack_v1.symtype; + if (p2ccmp(dsym->stack_v1.p_name, "this")) + { + if (lastGProcSym) + lastGProcSym->proc_v2.proctype = findMemberFunctionType(lastGProcSym, type); + if (thisIsNotRef && pointerTypes) + { +#if 0 + // insert function info before this + memset (&dsym->funcinfo_32, 0, sizeof (dsym->funcinfo_32)); + dsym->funcinfo_32.id = S_FUNCINFO_32; + dsym->funcinfo_32.len = sizeof (dsym->funcinfo_32) - 2; + dsym->funcinfo_32.sizeLocals = 4; + dsym->funcinfo_32.info = 0x220; + destSize += sizeof (dsym->funcinfo_32); + codeview_symbol* dsym = (codeview_symbol*)(destSymbols + destSize); + memcpy(dsym, sym, length); +#endif +#if 0 + // create another "this" symbol that is a pointer to the object, not a reference + destSize += length; + codeview_symbol* dsym = (codeview_symbol*)(destSymbols + destSize); + memcpy(dsym, sym, length); +#endif + if (type >= 0x1000 && pointerTypes[type - 0x1000]) + type = pointerTypes[type - 0x1000]; + } + } + dsym->stack_v1.symtype = translateType(type); + //sym->stack_v1.symtype = 0x1012; + break; + case S_ENDARG_V1: + case S_RETURN_V1: + case S_SSEARCH_V1: + break; + case S_END_V1: + lastGProcSym = 0; + break; + case S_COMPILAND_V1: + if (((dsym->compiland_v1.unknown >> 8) & 0xFF) == 0) // C? + dsym->compiland_v1.unknown = (dsym->compiland_v1.unknown & ~0xFF00 | 0x100); // C++ + break; + case S_PROCREF_V1: + case S_DATAREF_V1: + case S_LPROCREF_V1: + // dmd does not add a string, but it's not obvious to detect whether it exists or not + if (dsym->procref_v1.len != sizeof(dsym->procref_v1) - 4) + break; + + dsym->procref_v1.p_name.namelen = 0; + memset (dsym->procref_v1.p_name.name, 0, 3); // also 4-byte alignment assumed + destSize += 4; + break; + + case S_ALIGN_V1: + continue; // throw away + break; + default: + sym = sym; + break; + } + + destSize += destlength; + } + return destSize; +} + +bool CV2PDB::addUdtSymbol(int type, const char* name) +{ + if (cbUdtSymbols + 100 > allocUdtSymbols) + { + allocUdtSymbols += 1000; + udtSymbols = (BYTE*) realloc(udtSymbols, allocUdtSymbols); + } + + codeview_symbol* sym = (codeview_symbol*) (udtSymbols + cbUdtSymbols); + sym->udt_v1.id = S_UDT_V1; + sym->udt_v1.type = translateType(type); + strcpy (sym->udt_v1.p_name.name, name); + sym->udt_v1.p_name.namelen = strlen(sym->udt_v1.p_name.name); + sym->udt_v1.len = sizeof(sym->udt_v1) + sym->udt_v1.p_name.namelen - 1 - 2; + cbUdtSymbols += sym->udt_v1.len + 2; + + return true; +} + +bool CV2PDB::addSymbols(int iMod, BYTE* symbols, int cb) +{ + mspdb::Mod* mod = 0; + if (iMod < countEntries) + mod = modules[iMod]; + for (int i = 0; !mod && i < countEntries; i++) + mod = modules[i]; // add global symbols to first module + if (!mod) + { + if (!globmod) + { + int rc = dbi->OpenMod("", "", &globmod); + if (rc <= 0 || !globmod) + return setError("cannot create global module"); + } + mod = globmod; + } + if (!mod) + return setError("no module to set symbols"); + + int prefix = mod == globmod ? 3 : 4; + int words = (cb + cbGlobalSymbols + cbStaticSymbols + cbUdtSymbols + 3) / 4 + prefix; + DWORD* data = new DWORD[2 * words]; + + int databytes = copySymbols(symbols, cb, (BYTE*) (data + prefix), 0); + if (staticSymbols) + databytes = copySymbols(staticSymbols, cbStaticSymbols, (BYTE*) (data + prefix), databytes); + if (globalSymbols) + databytes = copySymbols(globalSymbols, cbGlobalSymbols, (BYTE*) (data + prefix), databytes); + if (udtSymbols) + databytes = copySymbols(udtSymbols, cbUdtSymbols, (BYTE*) (data + prefix), databytes); + + data[0] = 4; + data[1] = 0xf1; + data[2] = databytes + 4 * (prefix - 3); + if (prefix > 3) + data[3] = 1; + int rc = mod->AddSymbols((BYTE*) data, ((databytes + 3) / 4 + prefix) * 4); + if (rc <= 0) + return setError("cannot add symbols to module"); + delete [] data; + return true; +} + +bool CV2PDB::addSymbols() +{ + if (!initGlobalSymbols()) + return false; + + for (int m = 0; m < countEntries; m++) + { + OMFDirEntry* entry = img.getCVEntry(m); + mspdb::Mod* mod = 0; + BYTE* symbols = img.CVP(entry->lfo); + + switch(entry->SubSection) + { + case sstAlignSym: + if (!addSymbols (entry->iMod, symbols + 4, entry->cb - 4)) + return false; + break; + + case sstStaticSym: + case sstGlobalSym: + break; // handled in initGlobalSymbols + } + } + return true; +} + +bool CV2PDB::writeImage(const char* opath) +{ + int len = sizeof(*rsds) + strlen((char*)(rsds + 1)) + 1; + if (!img.replaceDebugSection(rsds, len)) + return setError(img.getLastError()); + + if (!img.save(opath)) + return setError(img.getLastError()); + + return true; +} diff --git a/src/cv2pdb.h b/src/cv2pdb.h new file mode 100644 index 0000000..a900e6c --- /dev/null +++ b/src/cv2pdb.h @@ -0,0 +1,148 @@ +// Convert DMD CodeView debug information to PDB files +// Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved +// +// License for redistribution is given by the Artistic License 2.0 +// see file LICENSE for further details + +#ifndef __CV2PDB_H__ +#define __CV2PDB_H__ + +#include "LastError.h" +#include "mspdb.h" + +#include + +extern "C" { +#include "mscvpdb.h" +} + +class PEImage; + +class CV2PDB : public LastError +{ +public: + CV2PDB(PEImage& image); + ~CV2PDB(); + + bool cleanup(bool commit); + bool openPDB(const char* pdbname); + + bool setError(const char* msg); + bool createModules(); + + bool initLibraries(); + const BYTE* getLibrary(int i); + bool initSegMap(); + + int addFields(codeview_reftype* dfieldlist, const codeview_reftype* fieldlist, int maxdlen); + + int addAggregate(codeview_type* dtype, bool clss, int n_element, int fieldlist, int property, + int derived, int vshape, int structlen, const char*name); + int addClass(codeview_type* dtype, int n_element, int fieldlist, int property, + int derived, int vshape, int structlen, const char*name); + int addStruct(codeview_type* dtype, int n_element, int fieldlist, int property, + int derived, int vshape, int structlen, const char*name); + + int addPointerType(codeview_type* dtype, int type, int attr = 0x800A); + int addPointerType(unsigned char* dtype, int type, int attr = 0x800A); + + int addFieldMember(codeview_fieldtype* dfieldtype, int attr, int offset, int type, const char* name); + + void checkUserTypeAlloc(int size = 1000, int add = 10000); + + const codeview_type* getTypeData(int type); + const codeview_type* getUserTypeData(int type); + const codeview_type* findCompleteClassType(const codeview_type* cvtype); + + int findMemberFunctionType(codeview_symbol* lastGProcSym, int thisPtrType); + + int sizeofClassType(const codeview_type* cvtype); + int sizeofBasicType(int type); + int sizeofType(int type); + + // to be used when writing new type only to avoid double translation + int translateType(int type); + + bool nameOfBasicType(int type, char* name, int maxlen); + bool nameOfType(int type, char* name, int maxlen); + bool nameOfDynamicArray(int indexType, int elemType, char* name, int maxlen); + bool nameOfAssocArray(int indexType, int elemType, char* name, int maxlen); + bool nameOfDelegate(int thisType, int funcType, char* name, int maxlen); + bool nameOfOEMType(codeview_oem_type* oem, char* name, int maxlen); + bool nameOfModifierType(int type, int mod, char* name, int maxlen); + + int numeric_leaf(int* value, const void* leaf); + int copy_leaf(unsigned char* dp, int& dpos, const unsigned char* p, int& pos); + + const char* appendDynamicArray(int indexType, int elemType); + const char* appendAssocArray(int keyType, int elemType); + const char* appendDelegate(int thisType, int funcType); + int appendObjectType (int object_derived_type); + int appendPointerType(int pointedType, int attr); + + bool initGlobalTypes(); + bool initGlobalSymbols(); + + bool addTypes(); + bool addSrcLines(); + bool addPublics(); + + // returns new destSize + int copySymbols(BYTE* srcSymbols, int srcSize, BYTE* destSymbols, int destSize); + + bool addUdtSymbol(int type, const char* name); + bool addSymbols(int iMod, BYTE* symbols, int cb); + bool addSymbols(); + + bool writeImage(const char* opath); + +// private: + BYTE* libraries; + + PEImage& img; + + mspdb::PDB* pdb; + mspdb::DBI *dbi; + mspdb::TPI *tpi; + + mspdb::Mod** modules; + mspdb::Mod* globmod; + int countEntries; + + OMFSignatureRSDS* rsds; + + OMFSegMap* segMap; + OMFSegMapDesc* segMapDesc; + OMFGlobalTypes* globalTypeHeader; + + unsigned char* globalTypes; + int cbGlobalTypes; + int allocGlobalTypes; + + unsigned char* userTypes; + int* pointerTypes; + int cbUserTypes; + int allocUserTypes; + + unsigned char* globalSymbols; + int cbGlobalSymbols; + + unsigned char* staticSymbols; + int cbStaticSymbols; + + unsigned char* udtSymbols; + int cbUdtSymbols; + int allocUdtSymbols; + + int nextUserType; + int objectType; + + bool thisIsNotRef; + bool v3; + const char* lastError; + + double Dversion; +}; + + +#endif //__CV2PDB_H__ \ No newline at end of file diff --git a/src/cv2pdb.sln b/src/cv2pdb.sln new file mode 100644 index 0000000..451ef68 --- /dev/null +++ b/src/cv2pdb.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cv2pdb", "cv2pdb.vcproj", "{5E2BD27D-446A-4C99-9829-135F7C000D90}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5E2BD27D-446A-4C99-9829-135F7C000D90}.Debug|Win32.ActiveCfg = Debug|Win32 + {5E2BD27D-446A-4C99-9829-135F7C000D90}.Debug|Win32.Build.0 = Debug|Win32 + {5E2BD27D-446A-4C99-9829-135F7C000D90}.Release|Win32.ActiveCfg = Release|Win32 + {5E2BD27D-446A-4C99-9829-135F7C000D90}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/cv2pdb.vcproj b/src/cv2pdb.vcproj new file mode 100644 index 0000000..71a7252 --- /dev/null +++ b/src/cv2pdb.vcproj @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/demangle.cpp b/src/demangle.cpp new file mode 100644 index 0000000..883680c --- /dev/null +++ b/src/demangle.cpp @@ -0,0 +1,493 @@ +// adopted from demangle.d distributed with DMD + +/**** + * Demangle D mangled names. + * Macros: + * WIKI = Phobos/StdDemangle + */ + +/* Authors: + * Walter Bright, Digital Mars, www.digitalmars.com + * Thomas Kuehne + * Frits van Bommel + */ + +#include +#include +#include + +using namespace std; + +typedef unsigned char ubyte; +typedef long double real; + +#define length length() + +#define size_t_max 0x7FFFFFFFU + +class MangleException +{ +public: + virtual ~MangleException() {} +}; + +class Demangle +{ +public: + size_t ni; + string name; + string (Demangle::*fparseTemplateInstanceName)(); + + static void error() + { + //writefln("error()"); + throw MangleException(); + } + + static ubyte ascii2hex(char c) + { + if (!isxdigit(c)) + error(); + return (ubyte) + ( (c >= 'a') ? c - 'a' + 10 : + (c >= 'A') ? c - 'A' + 10 : c - '0' ); + } + + size_t parseNumber() + { + //writefln("parseNumber() %d", ni); + size_t result = 0; + + while (ni < name.length && isdigit(name[ni])) + { + int i = name[ni] - '0'; + if (result > (size_t_max - i) / 10) + error(); + result = result * 10 + i; + ni++; + } + return result; + } + + string parseSymbolName() + { + //writefln("parseSymbolName() %d", ni); + size_t i = parseNumber(); + if (ni + i > name.length) + error(); + string result; + if (i >= 5 && + name[ni] == '_' && + name[ni + 1] == '_' && + name[ni + 2] == 'T') + { + size_t nisave = ni; + bool err = false; + ni += 3; + try + { + result = parseTemplateInstanceName(); // (this->*fparseTemplateInstanceName)(); + if (ni != nisave + i) + err = true; + } + catch (MangleException me) + { + err = true; + } + ni = nisave; + if (err) + goto L1; + goto L2; + } + L1: + result = name.substr(ni, i); + L2: + ni += i; + return result; + } + + string parseQualifiedName() + { + //writefln("parseQualifiedName() %d", ni); + string result; + + while (ni < name.length && isdigit(name[ni])) + { + if (result.length) + result += "."; + result += parseSymbolName(); + } + return result; + } + + string parseType(string identifier = string()) + { + //writefln("parseType() %d", ni); + int isdelegate = 0; + bool hasthisptr = false; /// For function/delegate types: expects a 'this' pointer as last argument + Lagain: + if (ni >= name.length) + error(); + string p; + switch (name[ni++]) + { + case 'v': p = "void"; goto L1; + case 'b': p = "bool"; goto L1; + case 'g': p = "byte"; goto L1; + case 'h': p = "ubyte"; goto L1; + case 's': p = "short"; goto L1; + case 't': p = "ushort"; goto L1; + case 'i': p = "int"; goto L1; + case 'k': p = "uint"; goto L1; + case 'l': p = "long"; goto L1; + case 'm': p = "ulong"; goto L1; + case 'f': p = "float"; goto L1; + case 'd': p = "double"; goto L1; + case 'e': p = "real"; goto L1; + case 'o': p = "ifloat"; goto L1; + case 'p': p = "idouble"; goto L1; + case 'j': p = "ireal"; goto L1; + case 'q': p = "cfloat"; goto L1; + case 'r': p = "cdouble"; goto L1; + case 'c': p = "creal"; goto L1; + case 'a': p = "char"; goto L1; + case 'u': p = "wchar"; goto L1; + case 'w': p = "dchar"; goto L1; + + case 'A': // dynamic array + p = parseType() + "[]"; + goto L1; + + case 'P': // pointer + p = parseType() + "*"; + goto L1; + + case 'G': // static array + { size_t ns = ni; + parseNumber(); + size_t ne = ni; + p = parseType() + "[" + name.substr(ns, ne-ns) + "]"; + goto L1; + } + + case 'H': // associative array + p = parseType(); + p = parseType() + "[" + p + "]"; + goto L1; + + case 'D': // delegate + isdelegate = 1; + goto Lagain; + + case 'M': + hasthisptr = true; + goto Lagain; + + case 'F': // D function + case 'U': // C function + case 'W': // Windows function + case 'V': // Pascal function + case 'R': // C++ function + { char mc = name[ni - 1]; + string args; + + while (1) + { + if (ni >= name.length) + error(); + char c = name[ni]; + if (c == 'Z') + break; + if (c == 'X') + { + if (!args.length) error(); + args += " ..."; + break; + } + if (args.length) + args += ", "; + switch (c) + { + case 'J': + args += "out "; + ni++; + goto Ldefault; + + case 'K': + args += "inout "; + ni++; + goto Ldefault; + + case 'L': + args += "lazy "; + ni++; + goto Ldefault; + + default: + Ldefault: + args += parseType(); + continue; + + case 'Y': + args += "..."; + break; + } + break; + } + ni++; + if (!isdelegate && identifier.length) + { + switch (mc) + { + case 'F': p = ""; break; // D function + case 'U': p = "extern (C) "; break; // C function + case 'W': p = "extern (Windows) "; break; // Windows function + case 'V': p = "extern (Pascal) "; break; // Pascal function + default: assert(0); + } + p += parseType() + " " + identifier + "(" + args + ")"; + return p; + } + p = parseType() + + (isdelegate ? " delegate(" : " function(") + args + ")"; + isdelegate = 0; + goto L1; + } + + case 'C': p = "class "; goto L2; + case 'S': p = "struct "; goto L2; + case 'E': p = "enum "; goto L2; + case 'T': p = "typedef "; goto L2; + + L2: p += parseQualifiedName(); + goto L1; + + L1: + if (isdelegate) + error(); // 'D' must be followed by function + if (identifier.length) + p += " " + identifier; + return p; + + default: + size_t i = ni - 1; + ni = name.length; + p = name.substr(i, name.length-i); + goto L1; + } + } + + void getReal(string &result) + { + real r; + ubyte rdata[10]; + ubyte *p = rdata; + + if (ni + 10 * 2 > name.length) + error(); + for (size_t i = 0; i < 10; i++) + { + ubyte b; + + b = (ubyte) ((ascii2hex(name[ni + i * 2]) << 4) + ascii2hex(name[ni + i * 2 + 1])); + p[i] = b; + } + // extract 10-byte double from rdata + __asm { + fld TBYTE PTR rdata; + fstp r; + } + + char num[30]; + sprintf(num, "%g", r); + result += num; // format(r); + ni += 10 * 2; + } + + string parseTemplateInstanceName() + { + string result = parseSymbolName() + "!("; + int nargs = 0; + + while (1) + { + size_t i; + + if (ni >= name.length) + error(); + if (nargs && name[ni] != 'Z') + result += ", "; + nargs++; + switch (name[ni++]) + { + case 'T': + result += parseType(); + continue; + + case 'V': + + result += parseType() + " "; + if (ni >= name.length) + error(); + switch (name[ni++]) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + i = ni - 1; + while (ni < name.length && isdigit(name[ni])) + ni++; + result += name.substr(i, ni - i); + break; + + case 'N': + i = ni; + while (ni < name.length && isdigit(name[ni])) + ni++; + if (i == ni) + error(); + result += "-" + name.substr(i, ni - i); + break; + + case 'n': + result += "null"; + break; + + case 'e': + getReal(result); + break; + + case 'c': + getReal(result); + result += '+'; + getReal(result); + result += 'i'; + break; + + case 'a': + case 'w': + case 'd': + { char m = name[ni - 1]; + if (m == 'a') + m = 'c'; + size_t n = parseNumber(); + if (ni >= name.length || name[ni++] != '_' || + ni + n * 2 > name.length) + error(); + result += '"'; + for (i = 0; i < n; i++) + { char c; + + c = (char)((ascii2hex(name[ni + i * 2]) << 4) + + ascii2hex(name[ni + i * 2 + 1])); + result += c; + } + ni += n * 2; + result += '"'; + result += m; + break; + } + + default: + error(); + break; + } + continue; + + case 'S': + result += parseSymbolName(); + continue; + + case 'Z': + break; + + default: + error(); + } + break; + } + result += ")"; + return result; + } + + string demangle(string _name, bool plainName = false) + { + ni = 2; + name = _name; + + if (name.length < 3 || + name[0] != '_' || + name[1] != 'D' || + !isdigit(name[2])) + { + goto Lnot; + } + + // fparseTemplateInstanceName = &parseTemplateInstanceName; + + try + { + string result = parseQualifiedName(); + if (!plainName) + { + result = parseType(result); + while(ni < name.length){ + result += " . " + parseType(parseQualifiedName()); + } + + if (ni != name.length) + goto Lnot; + } + return result; + } + catch (MangleException e) + { + } + + Lnot: + // Not a recognized D mangled name; so return original + return name; + } + +}; + +void unittest() +{ + // debug(demangle) printf("demangle.demangle.unittest\n"); + + static string table[][2] = + { + { "_D7dparser3dmd8Template21TemplateTypeParameter13overloadMatchMFC7dparser3dmd8Template17TemplateParameterZi", "int dparser.dmd.Template.TemplateTypeParameter.overloadMatch(class dparser.dmd.Template.TemplateParameter)"}, + { "printf", "printf" }, + { "_foo", "_foo" }, + { "_D88", "_D88" }, // causes exception error, return symbol as is + { "_D4test3fooAa", "char[] test.foo"}, + { "_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])" }, + { "_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(class Object)" }, + { "_D4test2dgDFiYd", "double delegate(int, ...) test.dg" }, + { "_D4test58__T9factorialVde67666666666666860140VG5aa5_68656c6c6fVPvnZ9factorialf", "float test.factorial!(double 4.2, char[5] \"hello\"c, void* null).factorial" }, + { "_D4test101__T9factorialVde67666666666666860140Vrc9a999999999999d9014000000000000000c00040VG5aa5_68656c6c6fVPvnZ9factorialf", "float test.factorial!(double 4.2, cdouble 6.8+3i, char[5] \"hello\"c, void* null).factorial" }, + { "_D4test34__T3barVG3uw3_616263VG3wd3_646566Z1xi", "int test.bar!(wchar[3] \"abc\"w, dchar[3] \"def\"d).x" }, + { "_D8demangle4testFLC6ObjectLDFLiZiZi", "int demangle.test(lazy class Object, lazy int delegate(lazy int))"}, + { "_D8demangle4testFAiXi", "int demangle.test(int[] ...)"}, + { "_D8demangle4testFLAiXi", "int demangle.test(lazy int[] ...)"} , + }; + + Demangle d; + for(int i = 0; i < sizeof(table)/sizeof(table[0]); i++) + { + string r = d.demangle(table[i][0]); + assert(r == table[i][1]); + // "table entry #" + toString(i) + ": '" + name[0] + "' demangles as '" + r + "' but is expected to be '" + name[1] + "'"); + + } +} + +bool d_demangle(const char* name, char* demangled, int maxlen, bool plain) +{ + Demangle d; + string r = d.demangle(name, plain); + if (r.length == 0) + return false; + strncpy(demangled, r.c_str(), maxlen); + return true; +} diff --git a/src/demangle.h b/src/demangle.h new file mode 100644 index 0000000..0a63c60 --- /dev/null +++ b/src/demangle.h @@ -0,0 +1,12 @@ +// Convert DMD CodeView debug information to PDB files +// Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved +// +// License for redistribution is given by the Artistic License 2.0 +// see file LICENSE for further details + +#ifndef __DEMANGLE_H__ +#define __DEMANGLE_H__ + +bool d_demangle(const char* name, char* demangled, int maxlen, bool plain); + +#endif //__DEMANGLE_H__ \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..16dbb34 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,165 @@ +// Convert DMD CodeView debug information to PDB files +// Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved +// +// License for redistribution is given by the Artistic License 2.0 +// see file LICENSE for further details + +#include "PEImage.h" +#include "cv2pdb.h" + +#include + +double +#include "../VERSION" +; + +void fatal(const char *message, ...) +{ + va_list argptr; + va_start(argptr, message); + vprintf(message, argptr); + va_end(argptr); + printf("\n"); + exit(1); +} + +void makefullpath(char* pdbname) +{ + char* pdbstart = pdbname; + char fullname[260]; + char* pfullname = fullname; + + int drive = 0; + if (pdbname[0] && pdbname[1] == ':') + { + if (pdbname[2] == '\\' || pdbname[2] == '/') + return; + drive = toupper (pdbname[0]); + pdbname += 2; + } + else + { + drive = _getdrive(); + } + + if (*pdbname != '\\' && *pdbname != '/') + { + _getdcwd(drive, pfullname, sizeof(fullname) - 2); + pfullname += strlen(pfullname); + if (pfullname[-1] != '\\') + *pfullname++ = '\\'; + } + else + { + *pfullname++ = 'a' - 1 + drive; + *pfullname++ = ':'; + } + strcpy(pfullname, pdbname); + strcpy(pdbstart, fullname); + + for(char*p = pdbstart; *p; p++) + if (*p == '/') + *p = '\\'; + + // remove relative parts "./" and "../" + while (char* p = strstr (pdbstart, "\\.\\")) + strcpy(p, p + 2); + + while (char* p = strstr (pdbstart, "\\..\\")) + { + for (char* q = p - 1; q >= pdbstart; q--) + if (*q == '\\') + { + strcpy(q, p + 3); + break; + } + } +} + +int main(int argc, char** argv) +{ + if (argc < 2) + { + printf("Convert DMD CodeView debug information to PDB files, Version %g\n", VERSION); + printf("Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved\n"); + printf("\n"); + printf("License for redistribution is given by the Artistic License 2.0\n"); + printf("see file LICENSE for further details\n"); + printf("\n"); + printf("usage: %s [-Dversion] [new-exe-file] [pdb-file]\n", argv[0]); + return -1; + } + + PEImage img; + double Dversion = 2; + + while (argc > 1 && argv[1][0] == '-') + { + argv++; + argc--; + if (argv[0][1] == '-') + break; + if (argv[0][1] == 'D') + Dversion = strtod (argv[0] + 2, 0); + else + fatal("unknwon option: %s", argv[0]); + } + + if (!img.load(argv[1])) + fatal("%s: %s", argv[1], img.getLastError()); + if (img.countCVEntries() == 0) + fatal("%s: no codeview debug entries found", argv[1]); + + CV2PDB cv2pdb(img); + cv2pdb.initLibraries(); + cv2pdb.Dversion = Dversion; + + char* outname = argv[1]; + if (argc > 2 && argv[2][0]) + outname = argv[2]; + + char pdbname[260]; + if (argc > 3) + strcpy (pdbname, argv[3]); + else + { + strcpy (pdbname, outname); + char *pDot = strrchr (pdbname, '.'); + if (!pDot || pDot <= strrchr (pdbname, '/') || pDot <= strrchr (pdbname, '\\')) + strcat (pdbname, ".pdb"); + else + strcpy (pDot, ".pdb"); + } + makefullpath(pdbname); + + unlink(pdbname); + + if(!cv2pdb.openPDB(pdbname)) + fatal("%s: %s", pdbname, cv2pdb.getLastError()); + + if (!cv2pdb.initSegMap()) + fatal("%s: %s", argv[1], cv2pdb.getLastError()); + + if (!cv2pdb.initGlobalTypes()) + fatal("%s: %s", argv[1], cv2pdb.getLastError()); + + if (!cv2pdb.createModules()) + fatal("%s: %s", pdbname, cv2pdb.getLastError()); + + if (!cv2pdb.addTypes()) + fatal("%s: %s", pdbname, cv2pdb.getLastError()); + + if (!cv2pdb.addSymbols()) + fatal("%s: %s", pdbname, cv2pdb.getLastError()); + + if (!cv2pdb.addSrcLines()) + fatal("%s: %s", pdbname, cv2pdb.getLastError()); + + if (!cv2pdb.addPublics()) + fatal("%s: %s", pdbname, cv2pdb.getLastError()); + + if (!cv2pdb.writeImage(outname)) + fatal("%s: %s", outname, cv2pdb.getLastError()); + + return 0; +} diff --git a/src/mscvpdb.h b/src/mscvpdb.h new file mode 100644 index 0000000..12b4177 --- /dev/null +++ b/src/mscvpdb.h @@ -0,0 +1,2083 @@ +/* + * MS debug information definitions. + * + * Copyright (C) 1996 Eric Youngdale + * Copyright (C) 1999-2000 Ulrich Weigand + * Copyright (C) 2004 Eric Pouech + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* MS has stored all its debug information in a set of structures + * which has been rather consistent across the years (ie you can grasp + * some continuity, and not so many drastic changes). + * + * A bit of history on the various formats + * MSVC 1.0 PDB v1 (new format for debug info) + * MSVC 2.0 Inclusion in link of debug info (PDB v2) + * MSVC 5.0 Types are 24 bits (instead of 16 for <= 4.x) + * MSVC x.0 PDB (change in internal streams layout) + * + * .DBG Contains COFF, FPO and Codeview info + * .PDB New format for debug info (information is + * derived from Codeview information) + * VCx0.PDB x major MSVC number, stores types, while + * .PDB stores symbols. + * + * Debug information can either be found in the debug section of a PE + * module (in something close to a .DBG file), or the debug section + * can actually refer to an external file, which can be in turn, + * either a .DBG or .PDB file. + * + * Regarding PDB files: + * ------------------- + * They are implemented as a set of internal files (as a small file + * system). The file is split into blocks, an internal file is made + * of a set of blocks. Internal files are accessed through + * numbers. For example, + * 1/ is the ROOT (basic information on the file) + * 2/ is the Symbol information (global symbols, local variables...) + * 3/ is the Type internal file (each the symbols can have type + * information associated with it). + * + * Over the years, three formats existed for the PDB: + * - ?? was rather linked to 16 bit code (our support shall be rather + * bad) + * - JG: it's the signature embedded in the file header. This format + * has been used in MSVC 2.0 => 5.0. + * - DS: it's the signature embedded in the file header. It's the + * current format supported my MS. + * + * Types internal stream + * --------------------- + * Types (from the Type internal file) have existed in three flavors + * (note that those flavors came as historical evolution, but there + * isn't a one to one link between types evolution and PDB formats' + * evolutions: + * - the first flavor (suffixed by V1 in this file), where the types + * and subtypes are 16 bit entities; and where strings are in Pascal + * format (first char is their length and are not 0 terminated) + * - the second flavor (suffixed by V2) differs from first flavor with + * types and subtypes as 32 bit entities. This forced some + * reordering of fields in some types + * - the third flavor (suffixed by V3) differs from second flavor with + * strings stored as C strings (ie are 0 terminated, instead of + * length prefixed) + * The different flavors can coexist in the same file (is this really + * true ??) + * + * For the evolution of types, the need of the second flavor was the + * number of types to be defined (limited to 0xFFFF, including the C + * basic types); the need of the third flavor is the increase of + * symbol size (to be greater than 256), which was likely needed for + * complex C++ types (nested + templates). + * + * It's somehow difficult to represent the layout of those types on + * disk because: + * - some integral values are stored as numeric leaf, which size is + * variable depending on its value + * + * Symbols internal stream + * ----------------------- + * Here also we find three flavors (that we've suffixed with _V1, _V2 + * and _V3) even if their evolution is closer to the evolution of + * types, they are not completely linked together. + */ + +/* + * some minor modifications have been done by Rainer Schuetze, mainly + * adding structs for the OEM types used by the DMD compiler + */ + +#include "pshpack1.h" + +/* ======================================== * + * Type information + * ======================================== */ + +struct p_string +{ + unsigned char namelen; + char name[1]; +}; + +union codeview_type +{ + struct + { + unsigned short int len; + short int id; + } generic; + + struct + { + unsigned short int len; + short int id; + short int attribute; + short int type; + } modifier_v1; + + struct + { + unsigned short int len; + short int id; + int type; + short int attribute; + } modifier_v2; + + struct + { + unsigned short int len; + short int id; + short int attribute; + short int datatype; + struct p_string p_name; + } pointer_v1; + + struct + { + unsigned short int len; + short int id; + unsigned int datatype; + unsigned int attribute; + struct p_string p_name; + } pointer_v2; + + struct + { + unsigned short int len; + short int id; + short int elemtype; + short int idxtype; + unsigned short int arrlen; /* numeric leaf */ +#if 0 + struct p_string p_name; +#endif + } array_v1; + + struct + { + unsigned short int len; + short int id; + unsigned int elemtype; + unsigned int idxtype; + unsigned short int arrlen; /* numeric leaf */ +#if 0 + struct p_string p_name; +#endif + } array_v2; + + struct + { + unsigned short int len; + short int id; + unsigned int elemtype; + unsigned int idxtype; + unsigned short int arrlen; /* numeric leaf */ +#if 0 + char name[1]; +#endif + } array_v3; + + struct + { + unsigned short int len; + short int id; + short int n_element; + short int fieldlist; + short int property; + short int derived; + short int vshape; + unsigned short int structlen; /* numeric leaf */ +#if 0 + struct p_string p_name; +#endif + } struct_v1; + + struct + { + unsigned short int len; + short int id; + short int n_element; + short int property; + unsigned int fieldlist; + unsigned int derived; + unsigned int vshape; + unsigned short int structlen; /* numeric leaf */ +#if 0 + struct p_string p_name; +#endif + } struct_v2; + + struct + { + unsigned short int len; + short int id; + short int n_element; + short int property; + unsigned int fieldlist; + unsigned int derived; + unsigned int vshape; + unsigned short int structlen; /* numeric leaf */ +#if 0 + char name[1]; +#endif + } struct_v3; + + struct + { + unsigned short int len; + short int id; + short int count; + short int fieldlist; + short int property; + unsigned short int un_len; /* numeric leaf */ +#if 0 + struct p_string p_name; +#endif + } union_v1; + + struct + { + unsigned short int len; + short int id; + short int count; + short int property; + unsigned int fieldlist; + unsigned short int un_len; /* numeric leaf */ +#if 0 + struct p_string p_name; +#endif + } union_v2; + + struct + { + unsigned short int len; + short int id; + short int count; + short int property; + unsigned int fieldlist; + unsigned short int un_len; /* numeric leaf */ +#if 0 + char name[1]; +#endif + } union_v3; + + struct + { + unsigned short int len; + short int id; + short int count; + short int type; + short int fieldlist; + short int property; + struct p_string p_name; + } enumeration_v1; + + struct + { + unsigned short int len; + short int id; + short int count; + short int property; + unsigned int type; + unsigned int fieldlist; + struct p_string p_name; + } enumeration_v2; + + struct + { + unsigned short int len; + short int id; + short int count; + short int property; + unsigned int type; + unsigned int fieldlist; + char name[1]; + } enumeration_v3; + + struct + { + unsigned short int len; + short int id; + unsigned short int rvtype; + unsigned char call; + unsigned char reserved; + unsigned short int params; + unsigned short int arglist; + } procedure_v1; + + struct + { + unsigned short int len; + short int id; + unsigned int rvtype; + unsigned char call; + unsigned char reserved; + unsigned short int params; + unsigned int arglist; + } procedure_v2; + + struct + { + unsigned short int len; + short int id; + unsigned short int rvtype; + unsigned short int class_type; + unsigned short int this_type; + unsigned char call; + unsigned char reserved; + unsigned short int params; + unsigned short int arglist; + unsigned int this_adjust; + } mfunction_v1; + + struct + { + unsigned short int len; + short int id; + unsigned int rvtype; + unsigned int class_type; + unsigned this_type; + unsigned char call; + unsigned char reserved; + unsigned short params; + unsigned int arglist; + unsigned int this_adjust; + } mfunction_v2; +}; + +union codeview_reftype +{ + struct + { + unsigned short int len; + short int id; + } generic; + + struct + { + unsigned short int len; + short int id; + unsigned char list[1]; + } fieldlist; + + struct + { + unsigned short int len; + short int id; + unsigned char nbits; + unsigned char bitoff; + unsigned short type; + } bitfield_v1; + + struct + { + unsigned short int len; + short int id; + unsigned int type; + unsigned char nbits; + unsigned char bitoff; + } bitfield_v2; + + struct + { + unsigned short int len; + short int id; + unsigned short num; + unsigned short args[1]; + } arglist_v1; + + struct + { + unsigned short int len; + short int id; + unsigned num; + unsigned args[1]; + } arglist_v2; + + struct + { + unsigned short int len; + short int id; + unsigned short num; + unsigned short drvdcls[1]; + } derived_v1; + + struct + { + unsigned short int len; + short int id; + unsigned num; + unsigned drvdcls[1]; + } derived_v2; +}; + +union codeview_fieldtype +{ + struct + { + short int id; + } generic; + + struct + { + short int id; + short int type; + short int attribute; + unsigned short int offset; /* numeric leaf */ + } bclass_v1; + + struct + { + short int id; + short int attribute; + unsigned int type; + unsigned short int offset; /* numeric leaf */ + } bclass_v2; + + struct + { + short int id; + short int btype; + short int vbtype; + short int attribute; + unsigned short int vbpoff; /* numeric leaf */ +#if 0 + unsigned short int vboff; /* numeric leaf */ +#endif + } vbclass_v1; + + struct + { + short int id; + short int attribute; + unsigned int btype; + unsigned int vbtype; + unsigned short int vbpoff; /* numeric leaf */ +#if 0 + unsigned short int vboff; /* numeric leaf */ +#endif + } vbclass_v2; + + struct + { + short int id; + short int attribute; + unsigned short int value; /* numeric leaf */ +#if 0 + struct p_string p_name; +#endif + } enumerate_v1; + + struct + { + short int id; + short int attribute; + unsigned short int value; /* numeric leaf */ +#if 0 + char name[1]; +#endif + } enumerate_v3; + + struct + { + short int id; + short int type; + struct p_string p_name; + } friendfcn_v1; + + struct + { + short int id; + short int _pad0; + unsigned int type; + struct p_string p_name; + } friendfcn_v2; + + struct + { + short int id; + short int type; + short int attribute; + unsigned short int offset; /* numeric leaf */ +#if 0 + struct p_string p_name; +#endif + } member_v1; + + struct + { + short int id; + short int attribute; + unsigned int type; + unsigned short int offset; /* numeric leaf */ +#if 0 + struct p_string p_name; +#endif + } member_v2; + + struct + { + short int id; + short int attribute; + unsigned int type; + unsigned short int offset; /* numeric leaf */ +#if 0 + unsigned char name[1]; +#endif + } + member_v3; + + struct + { + short int id; + short int type; + short int attribute; + struct p_string p_name; + } stmember_v1; + + struct + { + short int id; + short int attribute; + unsigned int type; + struct p_string p_name; + } stmember_v2; + + struct + { + short int id; + short int attribute; + unsigned int type; + char name[1]; + } stmember_v3; + + struct + { + short int id; + short int count; + short int mlist; + struct p_string p_name; + } method_v1; + + struct + { + short int id; + short int count; + unsigned int mlist; + struct p_string p_name; + } method_v2; + + struct + { + short int id; + short int count; + unsigned int mlist; + char name[1]; + } method_v3; + + struct + { + short int id; + short int type; + struct p_string p_name; + } nesttype_v1; + + struct + { + short int id; + short int _pad0; + unsigned int type; + struct p_string p_name; + } nesttype_v2; + + struct + { + short int id; + short int _pad0; + unsigned int type; + char name[1]; + } nesttype_v3; + + struct + { + short int id; + short int type; + } vfunctab_v1; + + struct + { + short int id; + short int _pad0; + unsigned int type; + } vfunctab_v2; + + struct + { + short int id; + short int type; + } friendcls_v1; + + struct + { + short int id; + short int _pad0; + unsigned int type; + } friendcls_v2; + + struct + { + short int id; + short int attribute; + short int type; + struct p_string p_name; + } onemethod_v1; + + struct + { + short int id; + short int attribute; + unsigned int type; + struct p_string p_name; + } onemethod_v2; + + struct + { + short int id; + short int attribute; + unsigned int type; + char name[1]; + } onemethod_v3; + + struct + { + short int id; + short int attribute; + short int type; + unsigned int vtab_offset; + struct p_string p_name; + } onemethod_virt_v1; + + struct + { + short int id; + short int attribute; + unsigned int type; + unsigned int vtab_offset; + struct p_string p_name; + } onemethod_virt_v2; + + struct + { + short int id; + short int attribute; + unsigned int type; + unsigned int vtab_offset; + char name[1]; + } onemethod_virt_v3; + + struct + { + short int id; + short int type; + unsigned int offset; + } vfuncoff_v1; + + struct + { + short int id; + short int _pad0; + unsigned int type; + unsigned int offset; + } vfuncoff_v2; + + struct + { + short int id; + short int attribute; + short int type; + struct p_string p_name; + } nesttypeex_v1; + + struct + { + short int id; + short int attribute; + unsigned int type; + struct p_string p_name; + } nesttypeex_v2; + + struct + { + short int id; + short int attribute; + unsigned int type; + struct p_string p_name; + } membermodify_v2; + +}; + +union codeview_oem_type +{ + struct + { + short int oemid; + short int id; + short int count; + } generic; + + struct + { + short int oemid; // 0x42 for D + short int id; // 1 + short int count; // 2 + short int index_type; + short int elem_type; + } d_dyn_array; + + struct + { + short int oemid; // 0x42 for D + short int id; // 2 + short int count; // 2 + short int key_type; + short int elem_type; + } d_assoc_array; + + struct + { + short int oemid; // 0x42 for D + short int id; // 3 + short int count; // 2 + short int this_type; + short int func_type; + } d_delegate; +}; + +/* + * This covers the basic datatypes that VC++ seems to be using these days. + * 32 bit mode only. There are additional numbers for the pointers in 16 + * bit mode. There are many other types listed in the documents, but these + * are apparently not used by the compiler, or represent pointer types + * that are not used. + * + * Official MS documentation says that type (< 0x4000, so 12 bits) is made of: + * +----------+------+------+----------+------+ + * | 11 | 10-8 | 7-4 | 3 | 2-0 | + * +----------+------+------+----------+------+ + * | reserved | mode | type | reserved | size | + * +----------+------+------+----------+------+ + * In recent PDB files, type 8 exists, and is seen as an HRESULT... So we've + * added this basic type... as if bit 3 had been integrated into the size field + */ + +/* the type number of a built-in type is a 16-bit value specified in the following format: + bit # | 11 | 10-8 | 7-4 | 3 | 2-0 | + field | reserved | mode | type | reserved | size | + + where + is one of the following types: + 0x00 Special + 0x01 Signed integral value + 0x02 Unsigned integral value + 0x03 Boolean + 0x04 Real + 0x05 Complex + 0x06 Special2 + 0x07 Real int value + 0x08 Reserved + 0x09 Reserved + 0x0a Reserved + 0x0b Reserved + 0x0c Reserved + 0x0d Reserved + 0x0e Reserved + 0x0f Reserved for debugger expression evaluator + + is an enumerated value for each of the types. + Type = special + 0x00 No type + 0x01 Absolute symbol + 0x02 Segment + 0x03 Void + 0x04 Basic 8-byte currency value + 0x05 Near Basic string + 0x06 Far Basic string + 0x07 Untranslated type from previous Microsoft symbol formats + Type = signed/unsigned integral and Boolean values + 0x00 1 byte + 0x01 2 byte + 0x02 4 byte + 0x03 8 byte + 0x04 Reserved + 0x05 Reserved + 0x06 Reserved + 0x07 Reserved + Type = real and complex + 0x00 32 bit + 0x01 64 bit + 0x02 80 bit + 0x03 128 bit + 0x04 48 bit + 0x05 Reserved + 0x06 Reserved + 0x07 Reserved + Type = special2 + 0x00 Bit + 0x01 Pascal CHAR + Type = Real int + 0x00 Char + 0x01 Wide character + 0x02 2-byte signed integer + 0x03 2-byte unsigned integer + 0x04 4-byte signed integer + 0x05 4-byte unsigned integer + 0x06 8-byte signed integer + 0x07 8-byte unsigned integer + + is the pointer mode: + 0x00 Direct; not a pointer + 0x01 Near pointer + 0x02 Far pointer + 0x03 Huge pointer + 0x04 32-bit near pointer + 0x05 32-bit far pointer + 0x06 64-bit near pointer + 0x07 Reserved +*/ + +/* basic types */ +#define T_NOTYPE 0x0000 /* Notype */ +#define T_ABS 0x0001 /* Abs */ +#define T_SEGMENT 0x0002 /* segment type */ +#define T_VOID 0x0003 /* Void */ +#define T_CURRENCY 0x0004 /* basic 8-byte currency value */ +#define T_NBASICSTR 0x0005 /* near basic string */ +#define T_FBASICSTR 0x0006 /* far basic string */ +#define T_NOTTRANS 0x0007 /* untranslated type record from MS symbol format */ +#define T_HRESULT 0x0008 /* HRESULT - or error code ??? */ +#define T_CHAR 0x0010 /* signed char */ +#define T_SHORT 0x0011 /* short */ +#define T_LONG 0x0012 /* long */ +#define T_QUAD 0x0013 /* long long */ +#define T_UCHAR 0x0020 /* unsigned char */ +#define T_USHORT 0x0021 /* unsigned short */ +#define T_ULONG 0x0022 /* unsigned long */ +#define T_UQUAD 0x0023 /* unsigned long long */ +#define T_BOOL08 0x0030 /* 8-bit boolean */ +#define T_BOOL16 0x0031 /* 16-bit boolean */ +#define T_BOOL32 0x0032 /* 32-bit boolean */ +#define T_BOOL64 0x0033 /* 64-bit boolean */ +#define T_REAL32 0x0040 /* float */ +#define T_REAL64 0x0041 /* double */ +#define T_REAL80 0x0042 /* 80-bit real */ +#define T_REAL128 0x0043 /* 128-bit real */ +#define T_REAL48 0x0044 /* 48-bit real */ +#define T_CPLX32 0x0050 /* 32-bit complex number */ +#define T_CPLX64 0x0051 /* 64-bit complex number */ +#define T_CPLX80 0x0052 /* 80-bit complex number */ +#define T_CPLX128 0x0053 /* 128-bit complex number */ +#define T_BIT 0x0060 /* bit */ +#define T_PASCHAR 0x0061 /* pascal CHAR */ +#define T_RCHAR 0x0070 /* real char */ +#define T_WCHAR 0x0071 /* wide char */ +#define T_INT2 0x0072 /* real 16-bit signed int */ +#define T_UINT2 0x0073 /* real 16-bit unsigned int */ +#define T_INT4 0x0074 /* int */ +#define T_UINT4 0x0075 /* unsigned int */ +#define T_INT8 0x0076 /* 64-bit signed int */ +#define T_UINT8 0x0077 /* 64-bit unsigned int */ + + +/* near pointers to basic types */ +#define T_PVOID 0x0103 /* near pointer to void */ +#define T_PCHAR 0x0110 /* Near pointer to 8-bit signed */ +#define T_PSHORT 0x0111 /* Near pointer to 16-bit signed */ +#define T_PLONG 0x0112 /* Near pointer to 32-bit signed */ +#define T_PQUAD 0x0113 /* Near pointer to 64-bit signed */ +#define T_PUCHAR 0x0120 /* Near pointer to 8-bit unsigned */ +#define T_PUSHORT 0x0121 /* Near pointer to 16-bit unsigned */ +#define T_PULONG 0x0122 /* Near pointer to 32-bit unsigned */ +#define T_PUQUAD 0x0123 /* Near pointer to 64-bit unsigned */ +#define T_PBOOL08 0x0130 /* Near pointer to 8-bit Boolean */ +#define T_PBOOL16 0x0131 /* Near pointer to 16-bit Boolean */ +#define T_PBOOL32 0x0132 /* Near pointer to 32-bit Boolean */ +#define T_PBOOL64 0x0133 /* Near pointer to 64-bit Boolean */ +#define T_PREAL32 0x0140 /* Near pointer to 32-bit real */ +#define T_PREAL64 0x0141 /* Near pointer to 64-bit real */ +#define T_PREAL80 0x0142 /* Near pointer to 80-bit real */ +#define T_PREAL128 0x0143 /* Near pointer to 128-bit real */ +#define T_PREAL48 0x0144 /* Near pointer to 48-bit real */ +#define T_PCPLX32 0x0150 /* Near pointer to 32-bit complex */ +#define T_PCPLX64 0x0151 /* Near pointer to 64-bit complex */ +#define T_PCPLX80 0x0152 /* Near pointer to 80-bit complex */ +#define T_PCPLX128 0x0153 /* Near pointer to 128-bit complex */ +#define T_PRCHAR 0x0170 /* Near pointer to a real char */ +#define T_PWCHAR 0x0171 /* Near pointer to a wide char */ +#define T_PINT2 0x0172 /* Near pointer to 16-bit signed int */ +#define T_PUINT2 0x0173 /* Near pointer to 16-bit unsigned int */ +#define T_PINT4 0x0174 /* Near pointer to 32-bit signed int */ +#define T_PUINT4 0x0175 /* Near pointer to 32-bit unsigned int */ +#define T_PINT8 0x0176 /* Near pointer to 64-bit signed int */ +#define T_PUINT8 0x0177 /* Near pointer to 64-bit unsigned int */ + + +/* far pointers to basic types */ +#define T_PFVOID 0x0203 /* Far pointer to void */ +#define T_PFCHAR 0x0210 /* Far pointer to 8-bit signed */ +#define T_PFSHORT 0x0211 /* Far pointer to 16-bit signed */ +#define T_PFLONG 0x0212 /* Far pointer to 32-bit signed */ +#define T_PFQUAD 0x0213 /* Far pointer to 64-bit signed */ +#define T_PFUCHAR 0x0220 /* Far pointer to 8-bit unsigned */ +#define T_PFUSHORT 0x0221 /* Far pointer to 16-bit unsigned */ +#define T_PFULONG 0x0222 /* Far pointer to 32-bit unsigned */ +#define T_PFUQUAD 0x0223 /* Far pointer to 64-bit unsigned */ +#define T_PFBOOL08 0x0230 /* Far pointer to 8-bit Boolean */ +#define T_PFBOOL16 0x0231 /* Far pointer to 16-bit Boolean */ +#define T_PFBOOL32 0x0232 /* Far pointer to 32-bit Boolean */ +#define T_PFBOOL64 0x0233 /* Far pointer to 64-bit Boolean */ +#define T_PFREAL32 0x0240 /* Far pointer to 32-bit real */ +#define T_PFREAL64 0x0241 /* Far pointer to 64-bit real */ +#define T_PFREAL80 0x0242 /* Far pointer to 80-bit real */ +#define T_PFREAL128 0x0243 /* Far pointer to 128-bit real */ +#define T_PFREAL48 0x0244 /* Far pointer to 48-bit real */ +#define T_PFCPLX32 0x0250 /* Far pointer to 32-bit complex */ +#define T_PFCPLX64 0x0251 /* Far pointer to 64-bit complex */ +#define T_PFCPLX80 0x0252 /* Far pointer to 80-bit complex */ +#define T_PFCPLX128 0x0253 /* Far pointer to 128-bit complex */ +#define T_PFRCHAR 0x0270 /* Far pointer to a real char */ +#define T_PFWCHAR 0x0271 /* Far pointer to a wide char */ +#define T_PFINT2 0x0272 /* Far pointer to 16-bit signed int */ +#define T_PFUINT2 0x0273 /* Far pointer to 16-bit unsigned int */ +#define T_PFINT4 0x0274 /* Far pointer to 32-bit signed int */ +#define T_PFUINT4 0x0275 /* Far pointer to 32-bit unsigned int */ +#define T_PFINT8 0x0276 /* Far pointer to 64-bit signed int */ +#define T_PFUINT8 0x0277 /* Far pointer to 64-bit unsigned int */ + + +/* huge pointers to basic types */ +#define T_PHVOID 0x0303 /* Huge pointer to void */ +#define T_PHCHAR 0x0310 /* Huge pointer to 8-bit signed */ +#define T_PHSHORT 0x0311 /* Huge pointer to 16-bit signed */ +#define T_PHLONG 0x0312 /* Huge pointer to 32-bit signed */ +#define T_PHQUAD 0x0313 /* Huge pointer to 64-bit signed */ +#define T_PHUCHAR 0x0320 /* Huge pointer to 8-bit unsigned */ +#define T_PHUSHORT 0x0321 /* Huge pointer to 16-bit unsigned */ +#define T_PHULONG 0x0322 /* Huge pointer to 32-bit unsigned */ +#define T_PHUQUAD 0x0323 /* Huge pointer to 64-bit unsigned */ +#define T_PHBOOL08 0x0330 /* Huge pointer to 8-bit Boolean */ +#define T_PHBOOL16 0x0331 /* Huge pointer to 16-bit Boolean */ +#define T_PHBOOL32 0x0332 /* Huge pointer to 32-bit Boolean */ +#define T_PHBOOL64 0x0333 /* Huge pointer to 64-bit Boolean */ +#define T_PHREAL32 0x0340 /* Huge pointer to 32-bit real */ +#define T_PHREAL64 0x0341 /* Huge pointer to 64-bit real */ +#define T_PHREAL80 0x0342 /* Huge pointer to 80-bit real */ +#define T_PHREAL128 0x0343 /* Huge pointer to 128-bit real */ +#define T_PHREAL48 0x0344 /* Huge pointer to 48-bit real */ +#define T_PHCPLX32 0x0350 /* Huge pointer to 32-bit complex */ +#define T_PHCPLX64 0x0351 /* Huge pointer to 64-bit complex */ +#define T_PHCPLX80 0x0352 /* Huge pointer to 80-bit complex */ +#define T_PHCPLX128 0x0353 /* Huge pointer to 128-bit real */ +#define T_PHRCHAR 0x0370 /* Huge pointer to a real char */ +#define T_PHWCHAR 0x0371 /* Huge pointer to a wide char */ +#define T_PHINT2 0x0372 /* Huge pointer to 16-bit signed int */ +#define T_PHUINT2 0x0373 /* Huge pointer to 16-bit unsigned int */ +#define T_PHINT4 0x0374 /* Huge pointer to 32-bit signed int */ +#define T_PHUINT4 0x0375 /* Huge pointer to 32-bit unsigned int */ +#define T_PHINT8 0x0376 /* Huge pointer to 64-bit signed int */ +#define T_PHUINT8 0x0377 /* Huge pointer to 64-bit unsigned int */ + + +/* 32-bit near pointers to basic types */ +#define T_32PVOID 0x0403 /* 32-bit near pointer to void */ +#define T_32PHRESULT 0x0408 /* 16:32 near pointer to HRESULT - or error code ??? */ +#define T_32PCHAR 0x0410 /* 16:32 near pointer to 8-bit signed */ +#define T_32PSHORT 0x0411 /* 16:32 near pointer to 16-bit signed */ +#define T_32PLONG 0x0412 /* 16:32 near pointer to 32-bit signed */ +#define T_32PQUAD 0x0413 /* 16:32 near pointer to 64-bit signed */ +#define T_32PUCHAR 0x0420 /* 16:32 near pointer to 8-bit unsigned */ +#define T_32PUSHORT 0x0421 /* 16:32 near pointer to 16-bit unsigned */ +#define T_32PULONG 0x0422 /* 16:32 near pointer to 32-bit unsigned */ +#define T_32PUQUAD 0x0423 /* 16:32 near pointer to 64-bit unsigned */ +#define T_32PBOOL08 0x0430 /* 16:32 near pointer to 8-bit Boolean */ +#define T_32PBOOL16 0x0431 /* 16:32 near pointer to 16-bit Boolean */ +#define T_32PBOOL32 0x0432 /* 16:32 near pointer to 32-bit Boolean */ +#define T_32PBOOL64 0x0433 /* 16:32 near pointer to 64-bit Boolean */ +#define T_32PREAL32 0x0440 /* 16:32 near pointer to 32-bit real */ +#define T_32PREAL64 0x0441 /* 16:32 near pointer to 64-bit real */ +#define T_32PREAL80 0x0442 /* 16:32 near pointer to 80-bit real */ +#define T_32PREAL128 0x0443 /* 16:32 near pointer to 128-bit real */ +#define T_32PREAL48 0x0444 /* 16:32 near pointer to 48-bit real */ +#define T_32PCPLX32 0x0450 /* 16:32 near pointer to 32-bit complex */ +#define T_32PCPLX64 0x0451 /* 16:32 near pointer to 64-bit complex */ +#define T_32PCPLX80 0x0452 /* 16:32 near pointer to 80-bit complex */ +#define T_32PCPLX128 0x0453 /* 16:32 near pointer to 128-bit complex */ +#define T_32PRCHAR 0x0470 /* 16:32 near pointer to a real char */ +#define T_32PWCHAR 0x0471 /* 16:32 near pointer to a wide char */ +#define T_32PINT2 0x0472 /* 16:32 near pointer to 16-bit signed int */ +#define T_32PUINT2 0x0473 /* 16:32 near pointer to 16-bit unsigned int */ +#define T_32PINT4 0x0474 /* 16:32 near pointer to 32-bit signed int */ +#define T_32PUINT4 0x0475 /* 16:32 near pointer to 32-bit unsigned int */ +#define T_32PINT8 0x0476 /* 16:32 near pointer to 64-bit signed int */ +#define T_32PUINT8 0x0477 /* 16:32 near pointer to 64-bit unsigned int */ + + +/* 32-bit far pointers to basic types */ +#define T_32PFVOID 0x0503 /* 32-bit far pointer to void */ +#define T_32PFCHAR 0x0510 /* 16:32 far pointer to 8-bit signed */ +#define T_32PFSHORT 0x0511 /* 16:32 far pointer to 16-bit signed */ +#define T_32PFLONG 0x0512 /* 16:32 far pointer to 32-bit signed */ +#define T_32PFQUAD 0x0513 /* 16:32 far pointer to 64-bit signed */ +#define T_32PFUCHAR 0x0520 /* 16:32 far pointer to 8-bit unsigned */ +#define T_32PFUSHORT 0x0521 /* 16:32 far pointer to 16-bit unsigned */ +#define T_32PFULONG 0x0522 /* 16:32 far pointer to 32-bit unsigned */ +#define T_32PFUQUAD 0x0523 /* 16:32 far pointer to 64-bit unsigned */ +#define T_32PFBOOL08 0x0530 /* 16:32 far pointer to 8-bit Boolean */ +#define T_32PFBOOL16 0x0531 /* 16:32 far pointer to 16-bit Boolean */ +#define T_32PFBOOL32 0x0532 /* 16:32 far pointer to 32-bit Boolean */ +#define T_32PFBOOL64 0x0533 /* 16:32 far pointer to 64-bit Boolean */ +#define T_32PFREAL32 0x0540 /* 16:32 far pointer to 32-bit real */ +#define T_32PFREAL64 0x0541 /* 16:32 far pointer to 64-bit real */ +#define T_32PFREAL80 0x0542 /* 16:32 far pointer to 80-bit real */ +#define T_32PFREAL128 0x0543 /* 16:32 far pointer to 128-bit real */ +#define T_32PFREAL48 0x0544 /* 16:32 far pointer to 48-bit real */ +#define T_32PFCPLX32 0x0550 /* 16:32 far pointer to 32-bit complex */ +#define T_32PFCPLX64 0x0551 /* 16:32 far pointer to 64-bit complex */ +#define T_32PFCPLX80 0x0552 /* 16:32 far pointer to 80-bit complex */ +#define T_32PFCPLX128 0x0553 /* 16:32 far pointer to 128-bit complex */ +#define T_32PFRCHAR 0x0570 /* 16:32 far pointer to a real char */ +#define T_32PFWCHAR 0x0571 /* 16:32 far pointer to a wide char */ +#define T_32PFINT2 0x0572 /* 16:32 far pointer to 16-bit signed int */ +#define T_32PFUINT2 0x0573 /* 16:32 far pointer to 16-bit unsigned int */ +#define T_32PFINT4 0x0574 /* 16:32 far pointer to 32-bit signed int */ +#define T_32PFUINT4 0x0575 /* 16:32 far pointer to 32-bit unsigned int */ +#define T_32PFINT8 0x0576 /* 16:32 far pointer to 64-bit signed int */ +#define T_32PFUINT8 0x0577 /* 16:32 far pointer to 64-bit unsigned int */ + + +/* counts, bit masks, and shift values needed to access various parts of the built-in type numbers */ +#define T_MAXPREDEFINEDTYPE 0x0580 /* maximum type index for all built-in types */ +#define T_MAXBASICTYPE 0x0080 /* maximum type index all non-pointer built-in types */ +#define T_BASICTYPE_MASK 0x00ff /* mask of bits that can potentially identify a non-pointer basic type */ +#define T_BASICTYPE_SHIFT 8 /* shift count to push out the basic type bits from a type number */ +#define T_MODE_MASK 0x0700 /* type mode mask (ptr/non-ptr) */ +#define T_SIZE_MASK 0x0007 /* type size mask (depends on 'type' value) */ +#define T_TYPE_MASK 0x00f0 /* type type mask (data treatment mode) */ + +/* bit patterns for the portion of a built-in type number */ +#define T_NEARPTR_BITS 0x0100 +#define T_FARPTR_BITS 0x0200 +#define T_HUGEPTR_BITS 0x0300 +#define T_NEAR32PTR_BITS 0x0400 +#define T_FAR32PTR_BITS 0x0500 +#define T_NEAR64PTR_BITS 0x0600 + +#define LF_MODIFIER_V1 0x0001 +#define LF_POINTER_V1 0x0002 +#define LF_ARRAY_V1 0x0003 +#define LF_CLASS_V1 0x0004 +#define LF_STRUCTURE_V1 0x0005 +#define LF_UNION_V1 0x0006 +#define LF_ENUM_V1 0x0007 +#define LF_PROCEDURE_V1 0x0008 +#define LF_MFUNCTION_V1 0x0009 +#define LF_VTSHAPE_V1 0x000a +#define LF_COBOL0_V1 0x000b +#define LF_COBOL1_V1 0x000c +#define LF_BARRAY_V1 0x000d +#define LF_LABEL_V1 0x000e +#define LF_NULL_V1 0x000f +#define LF_NOTTRAN_V1 0x0010 +#define LF_DIMARRAY_V1 0x0011 +#define LF_VFTPATH_V1 0x0012 +#define LF_PRECOMP_V1 0x0013 +#define LF_ENDPRECOMP_V1 0x0014 +#define LF_OEM_V1 0x0015 +#define LF_TYPESERVER_V1 0x0016 + +#define LF_MODIFIER_V2 0x1001 /* variants with new 32-bit type indices (V2) */ +#define LF_POINTER_V2 0x1002 +#define LF_ARRAY_V2 0x1003 +#define LF_CLASS_V2 0x1004 +#define LF_STRUCTURE_V2 0x1005 +#define LF_UNION_V2 0x1006 +#define LF_ENUM_V2 0x1007 +#define LF_PROCEDURE_V2 0x1008 +#define LF_MFUNCTION_V2 0x1009 +#define LF_COBOL0_V2 0x100a +#define LF_BARRAY_V2 0x100b +#define LF_DIMARRAY_V2 0x100c +#define LF_VFTPATH_V2 0x100d +#define LF_PRECOMP_V2 0x100e +#define LF_OEM_V2 0x100f + +#define LF_SKIP_V1 0x0200 +#define LF_ARGLIST_V1 0x0201 +#define LF_DEFARG_V1 0x0202 +#define LF_LIST_V1 0x0203 +#define LF_FIELDLIST_V1 0x0204 +#define LF_DERIVED_V1 0x0205 +#define LF_BITFIELD_V1 0x0206 +#define LF_METHODLIST_V1 0x0207 +#define LF_DIMCONU_V1 0x0208 +#define LF_DIMCONLU_V1 0x0209 +#define LF_DIMVARU_V1 0x020a +#define LF_DIMVARLU_V1 0x020b +#define LF_REFSYM_V1 0x020c + +#define LF_SKIP_V2 0x1200 /* variants with new 32-bit type indices (V2) */ +#define LF_ARGLIST_V2 0x1201 +#define LF_DEFARG_V2 0x1202 +#define LF_FIELDLIST_V2 0x1203 +#define LF_DERIVED_V2 0x1204 +#define LF_BITFIELD_V2 0x1205 +#define LF_METHODLIST_V2 0x1206 +#define LF_DIMCONU_V2 0x1207 +#define LF_DIMCONLU_V2 0x1208 +#define LF_DIMVARU_V2 0x1209 +#define LF_DIMVARLU_V2 0x120a + +/* Field lists */ +#define LF_BCLASS_V1 0x0400 +#define LF_VBCLASS_V1 0x0401 +#define LF_IVBCLASS_V1 0x0402 +#define LF_ENUMERATE_V1 0x0403 +#define LF_FRIENDFCN_V1 0x0404 +#define LF_INDEX_V1 0x0405 +#define LF_MEMBER_V1 0x0406 +#define LF_STMEMBER_V1 0x0407 +#define LF_METHOD_V1 0x0408 +#define LF_NESTTYPE_V1 0x0409 +#define LF_VFUNCTAB_V1 0x040a +#define LF_FRIENDCLS_V1 0x040b +#define LF_ONEMETHOD_V1 0x040c +#define LF_VFUNCOFF_V1 0x040d +#define LF_NESTTYPEEX_V1 0x040e +#define LF_MEMBERMODIFY_V1 0x040f + +#define LF_BCLASS_V2 0x1400 /* variants with new 32-bit type indices (V2) */ +#define LF_VBCLASS_V2 0x1401 +#define LF_IVBCLASS_V2 0x1402 +#define LF_FRIENDFCN_V2 0x1403 +#define LF_INDEX_V2 0x1404 +#define LF_MEMBER_V2 0x1405 +#define LF_STMEMBER_V2 0x1406 +#define LF_METHOD_V2 0x1407 +#define LF_NESTTYPE_V2 0x1408 +#define LF_VFUNCTAB_V2 0x1409 +#define LF_FRIENDCLS_V2 0x140a +#define LF_ONEMETHOD_V2 0x140b +#define LF_VFUNCOFF_V2 0x140c +#define LF_NESTTYPEEX_V2 0x140d + +#define LF_ENUMERATE_V3 0x1502 +#define LF_ARRAY_V3 0x1503 +#define LF_CLASS_V3 0x1504 +#define LF_STRUCTURE_V3 0x1505 +#define LF_UNION_V3 0x1506 +#define LF_ENUM_V3 0x1507 +#define LF_MEMBER_V3 0x150d +#define LF_STMEMBER_V3 0x150e +#define LF_METHOD_V3 0x150f +#define LF_NESTTYPE_V3 0x1510 +#define LF_ONEMETHOD_V3 0x1511 + +#define LF_NUMERIC 0x8000 /* numeric leaf types */ +#define LF_CHAR 0x8000 +#define LF_SHORT 0x8001 +#define LF_USHORT 0x8002 +#define LF_LONG 0x8003 +#define LF_ULONG 0x8004 +#define LF_REAL32 0x8005 +#define LF_REAL64 0x8006 +#define LF_REAL80 0x8007 +#define LF_REAL128 0x8008 +#define LF_QUADWORD 0x8009 +#define LF_UQUADWORD 0x800a +#define LF_REAL48 0x800b +#define LF_COMPLEX32 0x800c +#define LF_COMPLEX64 0x800d +#define LF_COMPLEX80 0x800e +#define LF_COMPLEX128 0x800f +#define LF_VARSTRING 0x8010 + +/* ======================================== * + * Symbol information + * ======================================== */ + +union codeview_symbol +{ + struct + { + short int len; + short int id; + } generic; + + struct + { + short int len; + short int id; + unsigned int offset; + unsigned short segment; + unsigned short symtype; + struct p_string p_name; + } data_v1; + + struct + { + short int len; + short int id; + unsigned int symtype; + unsigned int offset; + unsigned short segment; + struct p_string p_name; + } data_v2; + + struct + { + short int len; + short int id; + unsigned int symtype; + unsigned int offset; + unsigned short segment; + char name[1]; + } data_v3; + + struct + { + short int len; + short int id; + unsigned int pparent; + unsigned int pend; + unsigned int next; + unsigned int offset; + unsigned short segment; + unsigned short thunk_len; + unsigned char thtype; + struct p_string p_name; + } thunk_v1; + + struct + { + short int len; + short int id; + unsigned int pparent; + unsigned int pend; + unsigned int next; + unsigned int offset; + unsigned short segment; + unsigned short thunk_len; + unsigned char thtype; + char name[1]; + } thunk_v3; + + struct + { + short int len; + short int id; + unsigned int pparent; + unsigned int pend; + unsigned int next; + unsigned int proc_len; + unsigned int debug_start; + unsigned int debug_end; + unsigned int offset; + unsigned short segment; + unsigned short proctype; + unsigned char flags; + struct p_string p_name; + } proc_v1; + + struct + { + short int len; + short int id; + unsigned int pparent; + unsigned int pend; + unsigned int next; + unsigned int proc_len; + unsigned int debug_start; + unsigned int debug_end; + unsigned int proctype; + unsigned int offset; + unsigned short segment; + unsigned char flags; + struct p_string p_name; + } proc_v2; + + struct + { + short int len; + short int id; + unsigned int pparent; + unsigned int pend; + unsigned int next; + unsigned int proc_len; + unsigned int debug_start; + unsigned int debug_end; + unsigned int proctype; + unsigned int offset; + unsigned short segment; + unsigned char flags; + char name[1]; + } proc_v3; + + struct + { + short int len; + short int id; + unsigned int symtype; + unsigned int offset; + unsigned short segment; + struct p_string p_name; + } public_v2; + + struct + { + short int len; + short int id; + unsigned int symtype; + unsigned int offset; + unsigned short segment; + char name[1]; + } public_v3; + + struct + { + short int len; /* Total length of this entry */ + short int id; /* Always S_BPREL_V1 */ + unsigned int offset; /* Stack offset relative to BP */ + unsigned short symtype; + struct p_string p_name; + } stack_v1; + + struct + { + short int len; /* Total length of this entry */ + short int id; /* Always S_BPREL_V2 */ + unsigned int offset; /* Stack offset relative to EBP */ + unsigned int symtype; + struct p_string p_name; + } stack_v2; + + struct + { + short int len; /* Total length of this entry */ + short int id; /* Always S_BPREL_V3 */ + int offset; /* Stack offset relative to BP */ + unsigned int symtype; + char name[1]; + } stack_v3; + + struct + { + short int len; /* Total length of this entry */ + short int id; /* Always S_BPREL_V3 */ + int offset; /* Stack offset relative to BP */ + unsigned int symtype; + unsigned short unknown; + char name[1]; + } stack_xxxx_v3; + + struct + { + short int len; /* Total length of this entry */ + short int id; /* Always S_REGISTER */ + unsigned short type; + unsigned short reg; + struct p_string p_name; + /* don't handle register tracking */ + } register_v1; + + struct + { + short int len; /* Total length of this entry */ + short int id; /* Always S_REGISTER_V2 */ + unsigned int type; /* check whether type & reg are correct */ + unsigned short reg; + struct p_string p_name; + /* don't handle register tracking */ + } register_v2; + + struct + { + short int len; /* Total length of this entry */ + short int id; /* Always S_REGISTER_V3 */ + unsigned int type; /* check whether type & reg are correct */ + unsigned short reg; + char name[1]; + /* don't handle register tracking */ + } register_v3; + + struct + { + short int len; + short int id; + unsigned int parent; + unsigned int end; + unsigned int length; + unsigned int offset; + unsigned short segment; + struct p_string p_name; + } block_v1; + + struct + { + short int len; + short int id; + unsigned int parent; + unsigned int end; + unsigned int length; + unsigned int offset; + unsigned short segment; + char name[1]; + } block_v3; + + struct + { + short int len; + short int id; + unsigned int offset; + unsigned short segment; + unsigned char flags; + struct p_string p_name; + } label_v1; + + struct + { + short int len; + short int id; + unsigned int offset; + unsigned short segment; + unsigned char flags; + char name[1]; + } label_v3; + + struct + { + short int len; + short int id; + unsigned short type; + unsigned short cvalue; /* numeric leaf */ +#if 0 + struct p_string p_name; +#endif + } constant_v1; + + struct + { + short int len; + short int id; + unsigned type; + unsigned short cvalue; /* numeric leaf */ +#if 0 + struct p_string p_name; +#endif + } constant_v2; + + struct + { + short int len; + short int id; + unsigned type; + unsigned short cvalue; +#if 0 + char name[1]; +#endif + } constant_v3; + + struct + { + short int len; + short int id; + unsigned short type; + struct p_string p_name; + } udt_v1; + + struct + { + short int len; + short int id; + unsigned type; + struct p_string p_name; + } udt_v2; + + struct + { + short int len; + short int id; + unsigned int type; + char name[1]; + } udt_v3; + + struct + { + short int len; + short int id; + char signature[4]; + struct p_string p_name; + } objname_v1; + + struct + { + short int len; + short int id; + unsigned int unknown; + struct p_string p_name; + } compiland_v1; + + struct + { + short int len; + short int id; + unsigned unknown1[4]; + unsigned short unknown2; + struct p_string p_name; + } compiland_v2; + + struct + { + short int len; + short int id; + unsigned int unknown; + char name[1]; + } compiland_v3; + + struct + { + short int len; + short int id; + unsigned int offset; + unsigned short segment; + } ssearch_v1; + + struct + { + short int len; + short int id; + short int flags; + char style; + char count; /* optional */ + char reglist[1]; /* count elements */ + + } functionret_v1; + + struct + { + short int len; + short int id; + unsigned int checksum; + unsigned int offset; + unsigned int module; + struct p_string p_name; // not included in len + } procref_v1; + + struct + { + short int len; + short int id; + unsigned short int sizeLocals; // sum of size of locals and arguments + unsigned short int unknown[10]; + unsigned short int info; // hasAlloca,hasSetjmp,hasLongjmp,hasInlAsm,hasEH,inl_specified,hasSEH,naked,hasGsChecks,hasEHa,noStackOrdering,wasInlined,strictGsCheck + // return UDT,instance constructor,instance constructor with virtual base + unsigned int unknown2; + } funcinfo_32; +}; + +#define S_COMPILAND_V1 0x0001 +#define S_REGISTER_V1 0x0002 +#define S_CONSTANT_V1 0x0003 +#define S_UDT_V1 0x0004 +#define S_SSEARCH_V1 0x0005 +#define S_END_V1 0x0006 +#define S_SKIP_V1 0x0007 +#define S_CVRESERVE_V1 0x0008 +#define S_OBJNAME_V1 0x0009 +#define S_ENDARG_V1 0x000a +#define S_COBOLUDT_V1 0x000b +#define S_MANYREG_V1 0x000c +#define S_RETURN_V1 0x000d +#define S_ENTRYTHIS_V1 0x000e + +#define S_BPREL_V1 0x0200 +#define S_LDATA_V1 0x0201 +#define S_GDATA_V1 0x0202 +#define S_PUB_V1 0x0203 +#define S_LPROC_V1 0x0204 +#define S_GPROC_V1 0x0205 +#define S_THUNK_V1 0x0206 +#define S_BLOCK_V1 0x0207 +#define S_WITH_V1 0x0208 +#define S_LABEL_V1 0x0209 +#define S_CEXMODEL_V1 0x020a +#define S_VFTPATH_V1 0x020b +#define S_REGREL_V1 0x020c +#define S_LTHREAD_V1 0x020d +#define S_GTHREAD_V1 0x020e + +#define S_PROCREF_V1 0x0400 +#define S_DATAREF_V1 0x0401 +#define S_ALIGN_V1 0x0402 +#define S_LPROCREF_V1 0x0403 + +#define S_REGISTER_V2 0x1001 /* Variants with new 32-bit type indices */ +#define S_CONSTANT_V2 0x1002 +#define S_UDT_V2 0x1003 +#define S_COBOLUDT_V2 0x1004 +#define S_MANYREG_V2 0x1005 +#define S_BPREL_V2 0x1006 +#define S_LDATA_V2 0x1007 +#define S_GDATA_V2 0x1008 +#define S_PUB_V2 0x1009 +#define S_LPROC_V2 0x100a +#define S_GPROC_V2 0x100b +#define S_VFTTABLE_V2 0x100c +#define S_REGREL_V2 0x100d +#define S_LTHREAD_V2 0x100e +#define S_GTHREAD_V2 0x100f +#if 0 +#define S_XXXXXXXXX_32 0x1012 /* seems linked to a function, content unknown */ +#endif +#define S_FUNCINFO_32 0x1012 +#define S_COMPILAND_V2 0x1013 + +#define S_COMPILAND_V3 0x1101 +#define S_THUNK_V3 0x1102 +#define S_BLOCK_V3 0x1103 +#define S_LABEL_V3 0x1105 +#define S_REGISTER_V3 0x1106 +#define S_CONSTANT_V3 0x1107 +#define S_UDT_V3 0x1108 +#define S_BPREL_V3 0x110B +#define S_LDATA_V3 0x110C +#define S_GDATA_V3 0x110D +#define S_PUB_V3 0x110E +#define S_LPROC_V3 0x110F +#define S_GPROC_V3 0x1110 +#define S_BPREL_XXXX_V3 0x1111 /* not really understood, but looks like bprel... */ +#define S_MSTOOL_V3 0x1116 /* compiler command line options and build information */ +#define S_PUB_FUNC1_V3 0x1125 /* didn't get the difference between the two */ +#define S_PUB_FUNC2_V3 0x1127 + +#define S_COMPILER_V4 0x113c /* compiler version info? */ +#define S_MSTOOL_V4 0x113d /* compiler command line options and build information */ + +#define S_LINKER_1 0x112c /* symbol info emitted from linker */ +#define S_SEGINFO_1 0x1136 /* emitted by linker */ +#define S_SEGINFO_2 0x1137 /* emitted by linker */ + +/* ======================================== * + * Line number information + * ======================================== */ + +union any_size +{ + const char* c; + const unsigned char* uc; + const short* s; + const int* i; + const unsigned int* ui; +}; + +struct startend +{ + unsigned int start; + unsigned int end; +}; + +struct codeview_linetab +{ + unsigned int nline; + unsigned int segno; + unsigned int start; + unsigned int end; + unsigned int source; + const unsigned short* linetab; + const unsigned int* offtab; +}; + + +/* ======================================== * + * PDB file information + * ======================================== */ + + +struct PDB_FILE +{ + DWORD size; + DWORD unknown; +}; + +struct PDB_JG_HEADER +{ + CHAR ident[40]; + DWORD signature; + DWORD block_size; + WORD free_list; + WORD total_alloc; + struct PDB_FILE toc; + WORD toc_block[1]; +}; + +struct PDB_DS_HEADER +{ + char signature[32]; + DWORD block_size; + DWORD unknown1; + DWORD num_pages; + DWORD toc_size; + DWORD unknown2; + DWORD toc_page; +}; + +struct PDB_JG_TOC +{ + DWORD num_files; + struct PDB_FILE file[1]; +}; + +struct PDB_DS_TOC +{ + DWORD num_files; + DWORD file_size[1]; +}; + +struct PDB_JG_ROOT +{ + DWORD Version; + DWORD TimeDateStamp; + DWORD Age; + DWORD cbNames; + CHAR names[1]; +}; + +struct PDB_DS_ROOT +{ + DWORD Version; + DWORD TimeDateStamp; + DWORD Age; + GUID guid; + DWORD cbNames; + CHAR names[1]; +}; + +typedef struct _PDB_TYPES_OLD +{ + DWORD version; + WORD first_index; + WORD last_index; + DWORD type_size; + WORD file; + WORD pad; +} PDB_TYPES_OLD, *PPDB_TYPES_OLD; + +typedef struct _PDB_TYPES +{ + DWORD version; + DWORD type_offset; + DWORD first_index; + DWORD last_index; + DWORD type_size; + WORD file; + WORD pad; + DWORD hash_size; + DWORD hash_base; + DWORD hash_offset; + DWORD hash_len; + DWORD search_offset; + DWORD search_len; + DWORD unknown_offset; + DWORD unknown_len; +} PDB_TYPES, *PPDB_TYPES; + +typedef struct _PDB_SYMBOL_RANGE +{ + WORD segment; + WORD pad1; + DWORD offset; + DWORD size; + DWORD characteristics; + WORD index; + WORD pad2; +} PDB_SYMBOL_RANGE, *PPDB_SYMBOL_RANGE; + +typedef struct _PDB_SYMBOL_RANGE_EX +{ + WORD segment; + WORD pad1; + DWORD offset; + DWORD size; + DWORD characteristics; + WORD index; + WORD pad2; + DWORD timestamp; + DWORD unknown; +} PDB_SYMBOL_RANGE_EX, *PPDB_SYMBOL_RANGE_EX; + +typedef struct _PDB_SYMBOL_FILE +{ + DWORD unknown1; + PDB_SYMBOL_RANGE range; + WORD flag; + WORD file; + DWORD symbol_size; + DWORD lineno_size; + DWORD unknown2; + DWORD nSrcFiles; + DWORD attribute; + CHAR filename[1]; +} PDB_SYMBOL_FILE, *PPDB_SYMBOL_FILE; + +typedef struct _PDB_SYMBOL_FILE_EX +{ + DWORD unknown1; + PDB_SYMBOL_RANGE_EX range; + WORD flag; + WORD file; + DWORD symbol_size; + DWORD lineno_size; + DWORD unknown2; + DWORD nSrcFiles; + DWORD attribute; + DWORD reserved[2]; + CHAR filename[1]; +} PDB_SYMBOL_FILE_EX, *PPDB_SYMBOL_FILE_EX; + +typedef struct _PDB_SYMBOL_SOURCE +{ + WORD nModules; + WORD nSrcFiles; + WORD table[1]; +} PDB_SYMBOL_SOURCE, *PPDB_SYMBOL_SOURCE; + +typedef struct _PDB_SYMBOL_IMPORT +{ + DWORD unknown1; + DWORD unknown2; + DWORD TimeDateStamp; + DWORD Age; + CHAR filename[1]; +} PDB_SYMBOL_IMPORT, *PPDB_SYMBOL_IMPORT; + +typedef struct _PDB_SYMBOLS_OLD +{ + WORD hash1_file; + WORD hash2_file; + WORD gsym_file; + WORD pad; + DWORD module_size; + DWORD offset_size; + DWORD hash_size; + DWORD srcmodule_size; +} PDB_SYMBOLS_OLD, *PPDB_SYMBOLS_OLD; + +typedef struct _PDB_SYMBOLS +{ + DWORD signature; + DWORD version; + DWORD unknown; + DWORD hash1_file; + DWORD hash2_file; + WORD gsym_file; + WORD unknown1; + DWORD module_size; + DWORD offset_size; + DWORD hash_size; + DWORD srcmodule_size; + DWORD pdbimport_size; + DWORD resvd[5]; +} PDB_SYMBOLS, *PPDB_SYMBOLS; + +#include "poppack.h" + +/* ---------------------------------------------- + * Information used for parsing + * ---------------------------------------------- */ + +typedef struct +{ + DWORD from; + DWORD to; +} OMAP_DATA; + +struct msc_debug_info +{ + struct module* module; + int nsect; + const IMAGE_SECTION_HEADER* sectp; + int nomap; + const OMAP_DATA* omapp; + const BYTE* root; +}; + +/* coff.c */ +extern BOOL coff_process_info(const struct msc_debug_info* msc_dbg); + +/* =================================================== + * The old CodeView stuff (for NB09 and NB11) + * =================================================== */ + +#define sstModule 0x120 +#define sstTypes 0x121 +#define sstPublic 0x122 +#define sstPublicSym 0x123 +#define sstSymbols 0x124 +#define sstAlignSym 0x125 +#define sstSrcLnSeg 0x126 +#define sstSrcModule 0x127 +#define sstLibraries 0x128 +#define sstGlobalSym 0x129 +#define sstGlobalPub 0x12a +#define sstGlobalTypes 0x12b +#define sstMPC 0x12c +#define sstSegMap 0x12d +#define sstSegName 0x12e +#define sstPreComp 0x12f +#define sstFileIndex 0x133 +#define sstStaticSym 0x134 + +/* overall structure information */ +typedef struct OMFSignature +{ + char Signature[4]; + long filepos; +} OMFSignature; + +typedef struct OMFSignatureRSDS +{ + char Signature[4]; + GUID guid; + DWORD unknown; + CHAR name[1]; +} OMFSignatureRSDS; + +typedef struct _CODEVIEW_PDB_DATA +{ + char Signature[4]; + long filepos; + DWORD timestamp; + DWORD unknown; + CHAR name[1]; +} CODEVIEW_PDB_DATA, *PCODEVIEW_PDB_DATA; + +typedef struct OMFDirHeader +{ + WORD cbDirHeader; + WORD cbDirEntry; + DWORD cDir; + DWORD lfoNextDir; + DWORD flags; +} OMFDirHeader; + +typedef struct OMFDirEntry +{ + WORD SubSection; + WORD iMod; + DWORD lfo; + DWORD cb; +} OMFDirEntry; + +/* sstModule subsection */ + +typedef struct OMFSegDesc +{ + WORD Seg; + WORD pad; + DWORD Off; + DWORD cbSeg; +} OMFSegDesc; + +typedef struct OMFModule +{ + WORD ovlNumber; + WORD iLib; + WORD cSeg; + char Style[2]; +/* + OMFSegDesc SegInfo[cSeg]; + p_string Name; +*/ +} OMFModule; + +typedef struct OMFGlobalTypes +{ + DWORD flags; + DWORD cTypes; +/* + DWORD offset[cTypes]; + types_record[]; +*/ +} OMFGlobalTypes; + +/* sstGlobalPub section */ + +/* Header for symbol table */ +typedef struct OMFSymHash +{ + unsigned short symhash; + unsigned short addrhash; + unsigned long cbSymbol; + unsigned long cbHSym; + unsigned long cbHAddr; +} OMFSymHash; + +/* sstSegMap section */ + +typedef struct OMFSegMapDesc +{ + unsigned short flags; + unsigned short ovl; + unsigned short group; + unsigned short frame; + unsigned short iSegName; + unsigned short iClassName; + unsigned long offset; + unsigned long cbSeg; +} OMFSegMapDesc; + +typedef struct OMFSegMap +{ + unsigned short cSeg; + unsigned short cSegLog; +/* OMFSegMapDesc rgDesc[0];*/ +} OMFSegMap; + + +/* sstSrcModule section */ + +typedef struct OMFSourceLine +{ + unsigned short Seg; + unsigned short cLnOff; + unsigned long offset[1]; + unsigned short lineNbr[1]; +} OMFSourceLine; + +typedef struct OMFSourceFile +{ + unsigned short cSeg; + unsigned short reserved; + unsigned long baseSrcLn[1]; + unsigned short cFName; + char Name; +} OMFSourceFile; + +typedef struct OMFSourceModule +{ + unsigned short cFile; + unsigned short cSeg; + unsigned long baseSrcFile[1]; +} OMFSourceModule; diff --git a/src/mspdb.cpp b/src/mspdb.cpp new file mode 100644 index 0000000..b7a92a4 --- /dev/null +++ b/src/mspdb.cpp @@ -0,0 +1,91 @@ +// Convert DMD CodeView debug information to PDB files +// Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved +// +// License for redistribution is given by the Artistic License 2.0 +// see file LICENSE for further details + +#include "mspdb.h" + +#include + +#pragma comment(lib, "rpcrt4.lib") + +HMODULE modMsPdb; +mspdb::fnPDBOpen2W *pPDBOpen2W; + +char* mspdb_dll = "mspdb80.dll"; + +bool getInstallDir(const char* version, char* installDir, DWORD size) +{ + char key[260] = "SOFTWARE\\Microsoft\\"; + strcat(key, version); + + HKEY hkey; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) + return false; + + bool rc = RegQueryValueExA(hkey, "InstallDir", 0, 0, (LPBYTE)installDir, &size) == ERROR_SUCCESS; + RegCloseKey(hkey); + return rc; +} + +bool tryLoadMsPdb(const char* version, const char* mspdb) +{ + char installDir[260]; + if (!getInstallDir(version, installDir, sizeof(installDir))) + return false; + char* p = installDir + strlen(installDir); + if (p[-1] != '\\' && p[-1] != '/') + *p++ = '\\'; + strcpy(p, mspdb); + + modMsPdb = LoadLibraryA(installDir); + return modMsPdb != 0; +} + +bool initMsPdb() +{ + if (!modMsPdb) + modMsPdb = LoadLibraryA(mspdb_dll); + if (!modMsPdb) + tryLoadMsPdb("VisualStudio\\9.0", mspdb_dll); + if (!modMsPdb) + tryLoadMsPdb("VisualStudio\\8.0", mspdb_dll); + if (!modMsPdb) + tryLoadMsPdb("VCExpress\\9.0", mspdb_dll); + if (!modMsPdb) + tryLoadMsPdb("VCExpress\\8.0", mspdb_dll); + + if (!modMsPdb) + return false; + + if (!pPDBOpen2W) + pPDBOpen2W = (mspdb::fnPDBOpen2W*) GetProcAddress(modMsPdb, "PDBOpen2W"); + if (!pPDBOpen2W) + return false; + + return true; +} + +bool exitMsPdb() +{ + pPDBOpen2W = 0; + if (modMsPdb) + FreeLibrary(modMsPdb); + modMsPdb = 0; + return true; +} + +mspdb::PDB* CreatePDB(wchar_t* pdbname) +{ + if (!initMsPdb ()) + return 0; + + mspdb::PDB* pdb = 0; + long data[194] = { 193, 0 }; + wchar_t ext[256] = L".exe"; + if (!(*pPDBOpen2W) (pdbname, "wf", data, ext, 0x400, &pdb)) + return 0; + + return pdb; +} diff --git a/src/mspdb.h b/src/mspdb.h new file mode 100644 index 0000000..c3b92ec --- /dev/null +++ b/src/mspdb.h @@ -0,0 +1,494 @@ +// Convert DMD CodeView debug information to PDB files +// Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved +// +// License for redistribution is given by the Artistic License 2.0 +// see file LICENSE for further details + +#ifndef __MSPDB_H__ +#define __MSPDB_H__ + +#include + +namespace mspdb +{ + +struct MREUtil; +struct MREFile; +struct MREBag; +struct BufferDefaultAllocator; +struct EnumSC; +struct Stream; +struct EnumThunk; +struct EnumSyms; +struct EnumLines; +struct Dbg; +struct EnumSrc; +struct MREDrv; +struct MREngine; +struct EnumNameMap_Special; +struct MRECmp2; +struct PDB; +struct Src; +struct Mod; +struct DBI; +struct StreamCached; +struct GSI; +struct TPI; +struct NameMap; +struct EnumNameMap; + +#define MRECmp MRECmp2 +#define PDBCommon PDB +#define SrcCommon Src +#define ModCommon Mod +#define DBICommon DBI + +#define MREUtil2 MREUtil +#define MREFile2 MREFile +#define MREBag2 MREBag +#define Mod2 Mod +#define DBI2 DBI +#define GSI2 GSI +#define TPI2 TPI +#define NameMap2 NameMap +#define EnumNameMap2 EnumNameMap + +struct MREUtil { +public: virtual int MREUtil::FRelease(void); +public: virtual void MREUtil::EnumSrcFiles(int (__stdcall*)(struct MREUtil *,struct EnumFile &,enum EnumType),unsigned short const *,void *); +public: virtual void MREUtil::EnumDepFiles(struct EnumFile &,int (__stdcall*)(struct MREUtil *,struct EnumFile &,enum EnumType)); +public: virtual void MREUtil::EnumAllFiles(int (__stdcall*)(struct MREUtil *,struct EnumFile &),unsigned short const *,void *); +public: virtual void MREUtil::Enumstructes(int (__stdcall*)(struct MREUtil *,struct Enumstruct &),unsigned short const *,void *); +public: virtual void MREUtil::SummaryStats(struct MreStats &); +}; + +struct MREFile { +public: virtual int MREFile::FOpenBag(struct MREBag * *,unsigned long); +public: virtual int MREFile::FnoteEndInclude(unsigned long); +public: virtual int MREFile::FnotestructMod(unsigned long,unsigned long); +public: virtual int MREFile::FnoteInlineMethodMod(unsigned long,char const *,unsigned long); +public: virtual int MREFile::FnoteLineDelta(unsigned long,int); +public: virtual void MREFile::EnumerateChangedstructes(int (__cdecl*)(unsigned long,struct MREFile *,int (MREFile::*)(unsigned long,unsigned long))); +public: virtual int MREFile::FnotestructTI(unsigned long,unsigned long); +public: virtual int MREFile::FIsBoring(void); +public: virtual int MREFile::FnotePchCreateUse(unsigned short const *,unsigned short const *); +}; + +struct MREBag { +public: virtual int MREBag::FAddDep(unsigned long,unsigned long,char const *,enum DEPON,unsigned long); +public: virtual int MREBag::FClose(void); +}; + +struct BufferDefaultAllocator { +public: virtual unsigned char * BufferDefaultAllocator::Alloc(long); +public: virtual unsigned char * BufferDefaultAllocator::AllocZeroed(long); +public: virtual void BufferDefaultAllocator::DeAlloc(unsigned char *); +}; + + +struct EnumSC { +public: virtual int EnumSC::next(void); +public: virtual void EnumSC::get(unsigned short *,unsigned short *,long *,long *,unsigned long *); +public: virtual void EnumSC::getCrcs(unsigned long *,unsigned long *); +public: virtual bool EnumSC::fUpdate(long,long); +public: virtual int EnumSC::prev(void); +public: virtual int EnumSC::clone(struct EnumContrib * *); +public: virtual int EnumSC::locate(long,long); +}; + +struct Stream { +public: virtual long Stream::QueryCb(void); +public: virtual int Stream::Read(long,void *,long *); +public: virtual int Stream::Write(long,void *,long); +public: virtual int Stream::Replace(void *,long); +public: virtual int Stream::Append(void *,long); +public: virtual int Stream::Delete(void); +public: virtual int Stream::Release(void); +public: virtual int Stream::Read2(long,void *,long); +public: virtual int Stream::Truncate(long); +}; + +struct EnumThunk { +public: virtual void EnumThunk::release(void); +public: virtual void EnumThunk::reset(void); +public: virtual int EnumThunk::next(void); +public: virtual void EnumThunk::get(unsigned short *,long *,long *); +}; + +struct EnumSyms { +public: virtual void EnumSyms::release(void); +public: virtual void EnumSyms::reset(void); +public: virtual int EnumSyms::next(void); +public: virtual void EnumSyms::get(unsigned char * *); +public: virtual int EnumSyms::prev(void); +public: virtual int EnumSyms::clone(struct EnumSyms * *); +public: virtual int EnumSyms::locate(long,long); +}; + +struct EnumLines { +public: virtual void EnumLines::release(void); +public: virtual void EnumLines::reset(void); +public: virtual int EnumLines::next(void); +public: virtual bool EnumLines::getLines(unsigned long *,unsigned long *,unsigned short *,unsigned long *,unsigned long *,struct CV_Line_t *); +public: virtual bool EnumLines::getLinesColumns(unsigned long *,unsigned long *,unsigned short *,unsigned long *,unsigned long *,struct CV_Line_t *,struct CV_Column_t *); +public: virtual bool EnumLines::clone(struct EnumLines * *); +}; + +struct Dbg { +public: virtual int Dbg::Close(void); +public: virtual long Dbg::QuerySize(void); +public: virtual void Dbg::Reset(void); +public: virtual int Dbg::Skip(unsigned long); +public: virtual int Dbg::QueryNext(unsigned long,void *); +public: virtual int Dbg::Find(void *); +public: virtual int Dbg::Clear(void); +public: virtual int Dbg::Append(unsigned long,void const *); +public: virtual int Dbg::ReplaceNext(unsigned long,void const *); +public: virtual int Dbg::Clone(struct Dbg * *); +public: virtual long Dbg::QueryElementSize(void); +}; + +struct EnumSrc { +public: virtual void EnumSrc::release(void); +public: virtual void EnumSrc::reset(void); +public: virtual int EnumSrc::next(void); +public: virtual void EnumSrc::get(struct SrcHeaderOut const * *); +}; + +struct MREDrv { +public: virtual int MREDrv::FRelease(void); +public: virtual int MREDrv::FRefreshFileSysInfo(void); +public: virtual int MREDrv::FSuccessfulCompile(int,unsigned short const *,unsigned short const *); +public: virtual enum YNM MREDrv::YnmFileOutOfDate(struct SRCTARG &); +public: virtual int MREDrv::FFilesOutOfDate(struct CAList *); +public: virtual int MREDrv::FUpdateTargetFile(unsigned short const *,enum TrgType); +public: virtual void MREDrv::OneTimeInit(void); +}; + +struct MREngine { +public: virtual int MREngine::FDelete(void); +public: virtual int MREngine::FClose(int); +public: virtual void MREngine::QueryPdbApi(struct PDB * &,struct NameMap * &); +public: virtual void MREngine::_Reserved_was_QueryMreLog(void); +public: virtual void MREngine::QueryMreDrv(struct MREDrv * &); +public: virtual void MREngine::QueryMreCmp(struct MRECmp * &,struct TPI *); +public: virtual void MREngine::QueryMreUtil(struct MREUtil * &); +public: virtual int MREngine::FCommit(void); +}; + +struct MRECmp2 { +public: virtual int MRECmp2::FRelease(void); +public: virtual int MRECmp2::FOpenCompiland(struct MREFile * *,unsigned short const *,unsigned short const *); +public: virtual int MRECmp2::FCloseCompiland(struct MREFile *,int); +public: virtual int MRECmp2::FPushFile(struct MREFile * *,unsigned short const *,void *); +public: virtual struct MREFile * MRECmp2::PmrefilePopFile(void); +public: virtual int MRECmp::FStoreDepData(struct DepData *); +public: virtual int MRECmp::FRestoreDepData(struct DepData *); +public: virtual void MRECmp::structIsBoring(unsigned long); +}; + +//public: virtual void * Pool<16384>::AllocBytes(unsigned int); +//public: virtual void EnumSyms::get(unsigned char * *); +//public: virtual void * Pool<65536>::AllocBytes(unsigned int); +//public: virtual void EnumSyms::get(unsigned char * *); + +typedef int __cdecl fnPDBOpen2W(unsigned short const *path,char const *mode,long *p, + unsigned short *ext,unsigned int flags,struct PDB **pPDB); + +struct PDB { +public: static int __cdecl PDBCommon::Open2W(unsigned short const *path,char const *mode,long *p,unsigned short *ext,unsigned int flags,struct PDB **pPDB); + +public: virtual unsigned long PDB::QueryInterfaceVersion(void); +public: virtual unsigned long PDB::QueryImplementationVersion(void); +public: virtual long PDBCommon::QueryLastError(char * const); +public: virtual char * PDB::QueryPDBName(char * const); +public: virtual unsigned long PDB::QuerySignature(void); +public: virtual unsigned long PDB::QueryAge(void); +public: virtual int PDB::CreateDBI(char const *,struct DBI * *); +public: virtual int PDB::OpenDBI(char const *,char const *,struct DBI * *); +public: virtual int PDB::OpenTpi(char const *,struct TPI * *); +public: virtual int PDB::Commit(void); +public: virtual int PDB::Close(void); +public: virtual int PDBCommon::OpenStreamW(unsigned short const *,struct Stream * *); +public: virtual int PDB::GetEnumStreamNameMap(struct Enum * *); +public: virtual int PDB::GetRawBytes(int (__cdecl*)(void const *,long)); +public: virtual unsigned long PDB::QueryPdbImplementationVersion(void); +public: virtual int PDB::OpenDBIEx(char const *,char const *,struct DBI * *,int (__stdcall*)(struct _tagSEARCHDEBUGINFO *)); +public: virtual int PDBCommon::CopyTo(char const *,unsigned long,unsigned long); +public: virtual int PDB::OpenSrc(struct Src * *); +public: virtual long PDB::QueryLastErrorExW(unsigned short *,unsigned int); +public: virtual unsigned short * PDB::QueryPDBNameExW(unsigned short *,unsigned int); +public: virtual int PDB::QuerySignature2(struct _GUID *); +public: virtual int PDBCommon::CopyToW(unsigned short const *,unsigned long,unsigned long); +public: virtual int PDB::fIsSZPDB(void)const ; +public: virtual int PDB::containsW(unsigned short const *,unsigned long *); +public: virtual int PDB::CopyToW2(unsigned short const *,unsigned long,int (__cdecl*(__cdecl*)(void *,enum PCC))(void),void *); +public: virtual int PDB::OpenStreamEx(char const *,char const *,struct Stream * *); +}; + +struct Src { +public: virtual bool Src::Close(void); +public: virtual bool SrcCommon::Add(struct SrcHeader const *,void const *); +public: virtual bool Src::Remove(char const *); +public: virtual bool SrcCommon::QueryByName(char const *,struct SrcHeaderOut *)const ; +public: virtual bool Src::GetData(struct SrcHeaderOut const *,void *)const ; +public: virtual bool Src::GetEnum(struct EnumSrc * *)const ; +public: virtual bool Src::GetHeaderBlock(struct SrcHeaderBlock &)const ; +public: virtual bool Src::RemoveW(unsigned short *); +public: virtual bool Src::QueryByNameW(unsigned short *,struct SrcHeaderOut *)const ; +public: virtual bool Src::AddW(struct SrcHeaderW const *,void const *); +}; + +#include "pshpack1.h" + +struct LineInfoEntry +{ + unsigned int offset; + unsigned short line; +}; + +struct LineInfo +{ + unsigned int cntEntries; + unsigned short unknown; + LineInfoEntry entries[1]; // first entry { 0, 0x7fff } +}; + +struct SymbolChunk +{ + unsigned int chunkType; // seen 0xf1 (symbols), f2(??) f3 (FPO), f4 (MD5?), f5 (NEWFPO) + unsigned int chunkSize; // 0x18a: size of compiler symbols + + // symbol entries + // S_COMPILER_V4 + // S_MSTOOL_V4 +}; + +struct SymbolData +{ + unsigned int magic; // 4: version? sizeof header? + // followed by SymbolChunks +}; + +struct TypeChunk +{ + // see also codeview_type + + unsigned short len; + unsigned short type; + + union + { + struct _refpdb // type 0x1515 + { + unsigned int md5[4]; + unsigned int unknown; + unsigned pdbname[1]; + } refpdb; + }; +}; + +struct TypeData +{ + unsigned int magic; // 4: version? sizeof header? + // followed by TypeChunks +}; + +#include "poppack.h" + +struct Mod { +public: virtual unsigned long Mod::QueryInterfaceVersion(void); +public: virtual unsigned long Mod::QueryImplementationVersion(void); +public: virtual int Mod::AddTypes(unsigned char *pTypeData,long cbTypeData); +public: virtual int Mod::AddSymbols(unsigned char *pSymbolData,long cbSymbolData); +public: virtual int Mod2::AddPublic(char const *,unsigned short,long); // forwards to AddPublic2(...,0) +public: virtual int ModCommon::AddLines(char const *fname,unsigned short sec,long off,long size,long off2,unsigned short firstline,unsigned char *pLineInfo,long cbLineInfo); // forwards to AddLinesW +public: virtual int Mod2::AddSecContrib(unsigned short sec,long off,long size,unsigned long secflags); // forwards to Mod2::AddSecContribEx(..., 0, 0) +public: virtual int ModCommon::QueryCBName(long *); +public: virtual int ModCommon::QueryName(char * const,long *); +public: virtual int Mod::QuerySymbols(unsigned char *,long *); +public: virtual int Mod::QueryLines(unsigned char *,long *); +public: virtual int Mod2::SetPvClient(void *); +public: virtual int Mod2::GetPvClient(void * *); +public: virtual int Mod2::QueryFirstCodeSecContrib(unsigned short *,long *,long *,unsigned long *); +public: virtual int Mod2::QueryImod(unsigned short *); +public: virtual int Mod2::QueryDBI(struct DBI * *); +public: virtual int Mod2::Close(void); +public: virtual int ModCommon::QueryCBFile(long *); +public: virtual int ModCommon::QueryFile(char * const,long *); +public: virtual int Mod::QueryTpi(struct TPI * *); +public: virtual int Mod2::AddSecContribEx(unsigned short sec,long off,long size,unsigned long secflags,unsigned long crc/*???*/,unsigned long); +public: virtual int Mod::QueryItsm(unsigned short *); +public: virtual int ModCommon::QuerySrcFile(char * const,long *); +public: virtual int Mod::QuerySupportsEC(void); +public: virtual int ModCommon::QueryPdbFile(char * const,long *); +public: virtual int Mod::ReplaceLines(unsigned char *,long); +public: virtual bool Mod::GetEnumLines(struct EnumLines * *); +public: virtual bool Mod::QueryLineFlags(unsigned long *); +public: virtual bool Mod::QueryFileNameInfo(unsigned long,unsigned short *,unsigned long *,unsigned long *,unsigned char *,unsigned long *); +public: virtual int Mod::AddPublicW(unsigned short const *,unsigned short,long,unsigned long); +public: virtual int Mod::AddLinesW(unsigned short const *fname,unsigned short sec,long off,long size,long off2,unsigned long firstline,unsigned char *plineInfo,long cbLineInfo); +public: virtual int Mod::QueryNameW(unsigned short * const,long *); +public: virtual int Mod::QueryFileW(unsigned short * const,long *); +public: virtual int Mod::QuerySrcFileW(unsigned short * const,long *); +public: virtual int Mod::QueryPdbFileW(unsigned short * const,long *); +public: virtual int Mod2::AddPublic2(char const *name,unsigned short sec,long off,unsigned long type); +public: virtual int Mod::InsertLines(unsigned char *,long); +public: virtual int Mod::QueryLines2(long,unsigned char *,long *); +}; + + +struct DBI { +public: virtual unsigned long DBI::QueryImplementationVersion(void); +public: virtual unsigned long DBI::QueryInterfaceVersion(void); +public: virtual int DBICommon::OpenMod(char const *objName,char const *libName,struct Mod * *); +public: virtual int DBI::DeleteMod(char const *); +public: virtual int DBI2::QueryNextMod(struct Mod *,struct Mod * *); +public: virtual int DBI::OpenGlobals(struct GSI * *); +public: virtual int DBI::OpenPublics(struct GSI * *); +public: virtual int DBI::AddSec(unsigned short sec,unsigned short flags,long offset,long cbseg); +public: virtual int DBI2::QueryModFromAddr(unsigned short,long,struct Mod * *,unsigned short *,long *,long *); +public: virtual int DBI::QuerySecMap(unsigned char *,long *); +public: virtual int DBI::QueryFileInfo(unsigned char *,long *); +public: virtual void DBI::DumpMods(void); +public: virtual void DBI::DumpSecContribs(void); +public: virtual void DBI::DumpSecMap(void); +public: virtual int DBI2::Close(void); +public: virtual int DBI::AddThunkMap(long *,unsigned int,long,struct SO *,unsigned int,unsigned short,long); +public: virtual int DBI::AddPublic(char const *,unsigned short,long); +public: virtual int DBI2::getEnumContrib(struct Enum * *); +public: virtual int DBI::QueryTypeServer(unsigned char,struct TPI * *); +public: virtual int DBI::QueryItsmForTi(unsigned long,unsigned char *); +public: virtual int DBI::QueryNextItsm(unsigned char,unsigned char *); +public: virtual int DBI::reinitialize(void); // returns 0 +public: virtual int DBI::SetLazyTypes(int); +public: virtual int DBI::FindTypeServers(long *,char *); +public: virtual void DBI::noop(void); // noop +public: virtual int DBI::OpenDbg(enum DBGTYPE,struct Dbg * *); +public: virtual int DBI::QueryDbgTypes(enum DBGTYPE *,long *); +public: virtual int DBI::QueryAddrForSec(unsigned short *,long *,unsigned short,long,unsigned long,unsigned long); +public: virtual int DBI::QuerySupportsEC(void); +public: virtual int DBI2::QueryPdb(struct PDB * *); +public: virtual int DBI::AddLinkInfo(struct LinkInfo *); +public: virtual int DBI::QueryLinkInfo(struct LinkInfo *,long *); +public: virtual unsigned long DBI::QueryAge(void)const ; +public: virtual int DBI2::reinitialize2(void); // returns 0 +public: virtual void DBI::FlushTypeServers(void); +public: virtual int DBICommon::QueryTypeServerByPdb(char const *,unsigned char *); +public: virtual int DBI2::OpenModW(unsigned short const *objName,unsigned short const *libName,struct Mod * *); +public: virtual int DBI::DeleteModW(unsigned short const *); +public: virtual int DBI::AddPublicW(unsigned short const *name,unsigned short sec,long off,unsigned long type); +public: virtual int DBI::QueryTypeServerByPdbW(unsigned short const *,unsigned char *); +public: virtual int DBI::AddLinkInfoW(struct LinkInfoW *); +public: virtual int DBI::AddPublic2(char const *name,unsigned short sec,long off,unsigned long type); +public: virtual unsigned short DBI::QueryMachineType(void)const ; +public: virtual void DBI::SetMachineType(unsigned short); +public: virtual void DBI::RemoveDataForRva(unsigned long,unsigned long); +public: virtual int DBI::FStripped(void); +public: virtual int DBI2::QueryModFromAddr2(unsigned short,long,struct Mod * *,unsigned short *,long *,long *,unsigned long *); +public: virtual int DBI::QueryNoOfMods(long *); +public: virtual int DBI2::QueryMods(struct Mod * *,long); +public: virtual int DBI2::QueryImodFromAddr(unsigned short,long,unsigned short *,unsigned short *,long *,long *,unsigned long *); +public: virtual int DBI2::OpenModFromImod(unsigned short,struct Mod * *); +public: virtual int DBI::QueryHeader2(long,unsigned char *,long *); +public: virtual int DBI::FAddSourceMappingItem(unsigned short const *,unsigned short const *,unsigned long); +public: virtual int DBI::FSetPfnNotePdbUsed(void *,void (__cdecl*)(void *,unsigned short const *,int,int)); +public: virtual int DBI::FCTypes(void); +public: virtual int DBI::QueryFileInfo2(unsigned char *,long *); +public: virtual int DBI::FSetPfnQueryCallback(void *,int (__cdecl*(__cdecl*)(void *,enum DOVC))(void)); +}; + +struct StreamCached { +public: virtual long StreamCached::QueryCb(void); +public: virtual int StreamCached::Read(long,void *,long *); +public: virtual int StreamCached::Write(long,void *,long); +public: virtual int StreamCached::Replace(void *,long); +public: virtual int StreamCached::Append(void *,long); +public: virtual int StreamCached::Delete(void); +public: virtual int StreamCached::Release(void); +public: virtual int StreamCached::Read2(long,void *,long); +public: virtual int StreamCached::Truncate(long); +}; + +struct GSI { +public: virtual unsigned long GSI::QueryInterfaceVersion(void); +public: virtual unsigned long GSI::QueryImplementationVersion(void); +public: virtual unsigned char * GSI::NextSym(unsigned char *); +public: virtual unsigned char * GSI::HashSymW(unsigned short const *,unsigned char *); +public: virtual unsigned char * GSI2::NearestSym(unsigned short,long,long *); +public: virtual int GSI::Close(void); +public: virtual int GSI::getEnumThunk(unsigned short,long,struct EnumThunk * *); +public: virtual int GSI::QueryTpi(struct TPI * *); // returns 0 +public: virtual int GSI::QueryTpi2(struct TPI * *); // returns 0 +public: virtual unsigned char * GSI2::HashSymW2(unsigned short const *,unsigned char *); // same as GSI2::HashSymW +public: virtual int GSI::getEnumByAddr(struct EnumSyms * *); +}; + +struct TPI { +public: virtual unsigned long TPI::QueryInterfaceVersion(void); +public: virtual unsigned long TPI::QueryImplementationVersion(void); +public: virtual int TPI::QueryTi16ForCVRecord(unsigned char *,unsigned short *); +public: virtual int TPI::QueryCVRecordForTi16(unsigned short,unsigned char *,long *); +public: virtual int TPI::QueryPbCVRecordForTi16(unsigned short,unsigned char * *); +public: virtual unsigned short TPI::QueryTi16Min(void); +public: virtual unsigned short TPI::QueryTi16Mac(void); +public: virtual long TPI::QueryCb(void); +public: virtual int TPI::Close(void); +public: virtual int TPI::Commit(void); +public: virtual int TPI::QueryTi16ForUDT(char const *,int,unsigned short *); +public: virtual int TPI::SupportQueryTiForUDT(void); +public: virtual int TPI::fIs16bitTypePool(void); +public: virtual int TPI::QueryTiForUDT(char const *,int,unsigned long *); +public: virtual int TPI2::QueryTiForCVRecord(unsigned char *,unsigned long *); +public: virtual int TPI2::QueryCVRecordForTi(unsigned long,unsigned char *,long *); +public: virtual int TPI2::QueryPbCVRecordForTi(unsigned long,unsigned char * *); +public: virtual unsigned long TPI::QueryTiMin(void); +public: virtual unsigned long TPI::QueryTiMac(void); +public: virtual int TPI::AreTypesEqual(unsigned long,unsigned long); +public: virtual int TPI2::IsTypeServed(unsigned long); +public: virtual int TPI::QueryTiForUDTW(unsigned short const *,int,unsigned long *); +}; + + +struct NameMap { +public: virtual int NameMap::close(void); +public: virtual int NameMap2::reinitialize(void); +public: virtual int NameMap2::getNi(char const *,unsigned long *); +public: virtual int NameMap2::getName(unsigned long,char const * *); +public: virtual int NameMap2::getEnumNameMap(struct Enum * *); +public: virtual int NameMap2::contains(char const *,unsigned long *); +public: virtual int NameMap::commit(void); +public: virtual int NameMap2::isValidNi(unsigned long); +public: virtual int NameMap2::getNiW(unsigned short const *,unsigned long *); +public: virtual int NameMap2::getNameW(unsigned long,unsigned short *,unsigned int *); +public: virtual int NameMap2::containsW(unsigned short const *,unsigned long *); +public: virtual int NameMap2::containsUTF8(char const *,unsigned long *); +public: virtual int NameMap2::getNiUTF8(char const *,unsigned long *); +public: virtual int NameMap2::getNameA(unsigned long,char const * *); +public: virtual int NameMap2::getNameW2(unsigned long,unsigned short const * *); +}; + +struct EnumNameMap { +public: virtual void EnumNameMap::release(void); +public: virtual void EnumNameMap::reset(void); +public: virtual int EnumNameMap::next(void); +public: virtual void EnumNameMap2::get(char const * *,unsigned long *); +}; + +struct EnumNameMap_Special { +public: virtual void EnumNameMap_Special::release(void); +public: virtual void EnumNameMap_Special::reset(void); +public: virtual int EnumNameMap_Special::next(void); +public: virtual void EnumNameMap_Special::get(char const * *,unsigned long *); +}; + +} // namespace mspdb + +bool initMsPdb(); +bool exitMsPdb(); + +mspdb::PDB* CreatePDB(wchar_t* pdbname); + +extern char* mspdb_dll; + +#endif // __MSPDB_H__ diff --git a/src/symutil.cpp b/src/symutil.cpp new file mode 100644 index 0000000..6f5e42f --- /dev/null +++ b/src/symutil.cpp @@ -0,0 +1,152 @@ +// Convert DMD CodeView debug information to PDB files +// Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved +// +// License for redistribution is given by the Artistic License 2.0 +// see file LICENSE for further details + +#include "symutil.h" +#include "demangle.h" + +extern "C" { +#include "mscvpdb.h" +} + +#include + +char dotReplacementChar = '@'; + +int dsym2c(const BYTE* p, BYTE len, char* cname, int maxclen) +{ + int zlen, zpos, cpos = 0; + + // decompress symbol + while (len-- > 0) + { + int ch = *p++; + if (ch == 0x80) + { + if (len-- <= 0) + break; + zlen = *p++ & 0x7f; + if (len-- <= 0) + break; + zpos = *p++ & 0x7f; + if (zpos > cpos) + break; + for(int z = 0; z < zlen; z++) + cname[cpos + z] = cname[cpos - zpos + z]; + cpos += zlen; + break; + } + else if (ch > 0x80) + { + zlen = (ch & 0x7) + 1; + zpos = ((ch >> 3) & 0xf) - 7; // + zlen; + for(int z = 0; z < zlen; z++) + cname[cpos + z] = cname[cpos - zpos + z]; + cpos += zlen; + } + else + cname[cpos++] = ch; + } + + cname[cpos] = 0; + if (cname[0] == '_' && cname[1] == 'D' && isdigit(cname[2])) + d_demangle(cname, cname, maxclen, true); + +#if 1 + for(int i = 0; i < cpos; i++) + if (cname[i] == '.') + cname[i] = dotReplacementChar; +#endif + + return cpos; +} + +char* p2c(const BYTE* p, int idx) +{ + static char cname[4][2560]; + int len = *p++; + +#if 1 + memcpy(cname[idx], p, len); + cname[idx][len] = 0; +#else + dsym2c(p, len, cname[idx], 2560); +#endif + return cname[idx]; +} + +char* p2c(const p_string& p, int idx) +{ + return p2c(&p.namelen, idx); +} + +int p2ccpy(char* p, const BYTE* s) +{ + memcpy(p, s + 1, *s); + p[*s] = 0; + return *s + 1; +} + +int pstrcpy(BYTE* p, const BYTE* s) +{ + int len = *p = *s; + for(int i = 1; i <= len; i++) + if (s[i] == '.') + { + //p[i++] = ':'; + p[i] = dotReplacementChar; + } + else + p[i] = s[i]; + return len + 1; // *(BYTE*) memcpy (p, s, *s + 1) + 1; +} + +int pstrcpy(p_string& p, const p_string& s) +{ + return *(BYTE*) memcpy (&p, &s, s.namelen + 1) + 1; +} + +int pstrcmp(const BYTE* p1, const BYTE* p2) +{ + if (*p1 != *p2) + return *p2 - *p1; + return memcmp(p1 + 1, p2 + 1, *p1); +} + +bool p2ccmp(const BYTE* pp, const char* cp) +{ + int len = strlen(cp); + if (len != *pp) + return false; + return memcmp(pp + 1, cp, len) == 0; +} +bool p2ccmp(const p_string& pp, const char* cp) +{ + return p2ccmp(&pp.namelen, cp); +} + +int pstrcpy_v(bool v3, BYTE* d, const BYTE* s) +{ + if (!v3) + return pstrcpy(d, s); + + int len = *s++; + int clen = dsym2c(s, len, (char*) d, 1000) + 1; + + return clen; +} + +int cstrcpy_v(bool v3, BYTE* d, const char* s) +{ + int len = strlen(s); + if(!v3) + *d++ = len; + else + assert(len < 256); + + memcpy(d, s, len + 1); + return len + 1; +} + diff --git a/src/symutil.h b/src/symutil.h new file mode 100644 index 0000000..a055a7a --- /dev/null +++ b/src/symutil.h @@ -0,0 +1,29 @@ +// Convert DMD CodeView debug information to PDB files +// Copyright (c) 2009 by Rainer Schuetze, All Rights Reserved +// +// License for redistribution is given by the Artistic License 2.0 +// see file LICENSE for further details + +#ifndef __SYMUTIL_H__ +#define __SYMUTIL_H__ + +#include + +struct p_string; + +int dsym2c(const BYTE* p, BYTE len, char* cname, int maxclen); + +char* p2c(const BYTE* p, int idx = 0); +char* p2c(const p_string& p, int idx = 0); +int p2ccpy(char* p, const BYTE* s); +int pstrcpy(BYTE* p, const BYTE* s); +int pstrcpy(p_string& p, const p_string& s); +int pstrcmp(const BYTE* p1, const BYTE* p2); +bool p2ccmp(const BYTE* pp, const char* cp); +bool p2ccmp(const p_string& pp, const char* cp); +int pstrcpy_v(bool v3, BYTE* d, const BYTE* s); +int cstrcpy_v(bool v3, BYTE* d, const char* s); + +extern char dotReplacementChar; + +#endif //__SYMUTIL_H__ \ No newline at end of file diff --git a/test/cvtest.d b/test/cvtest.d new file mode 100644 index 0000000..c978f03 --- /dev/null +++ b/test/cvtest.d @@ -0,0 +1,233 @@ + +module cvtest; + + +///////////////// field types //////////////////////// +// field type LF_ENUMERATE_V1 +enum enum_name +{ + kEnum1, + kEnum2, + kEnum3, + kEnum500 = 500, +}; + +// field type LF_MEMBER_V1 +class class_member +{ + int member1; +}; + +// field type LF_BCLASS_V1 +class base_class +{ + int base_member; +}; + +class derived_class : public base_class +{ + int derived_member; + int a1_1234; + int a2; + int a3; + int a4; + int a5; + int a6; + int a7; + int a8; + int a9; + int a10; + int a11; + int a12; + int a13; + int a14; + int a15; + int a16; + int a17; +}; + +// field type LF_METHOD_V1 +class class_method +{ + int member = 3; + static int stmember = 2; + int method() + { + return member + stmember + this.member; + } +}; + +// function with same arguments as class_method.method. does it have different type? +int sim_method(class_method mthd) +{ + return 2; +} + +// field type LF_STMEMBER_V1 +class class_staticmember +{ + static int static_member = 2; +}; + +// field type LF_NESTTYPE_V1 +class class_outer +{ + class class_inner + { + int a, b; + }; + class_inner inner; + int c, d; +}; + +// LF_ARRAY_V1 +long[4] global_fixed_array; + +// OEM types; +long[] global_oem_long_dynarray; +int[int] global_oem_int_assoc_array; + +alias int[] alias_int_array; + +void procedure(int arg1) +{ +} + +struct struct_name +{ + int member; + static int static_member; +} + +union union_name +{ + int int_member; + long long_member; + float float_member; + struct_name struct_member; +} + +class this_is_a_rather_long_classname_to_test_what_happens_if_the_classname_gets_longer_than_the_limit_imposed_by_the_old_codeview_format_which_limits_the_length_of_names_to_tw0_hundred_and_fifty_five_characters_because_it_uses_pascal_strings_with_a_length_byte_and_chars_appended +{ + int member; +}; + +int longfoo(this_is_a_rather_long_classname_to_test_what_happens_if_the_classname_gets_longer_than_the_limit_imposed_by_the_old_codeview_format_which_limits_the_length_of_names_to_tw0_hundred_and_fifty_five_characters_because_it_uses_pascal_strings_with_a_length_byte_and_chars_appended x) +{ + return 1; +} + +class Templ(T) +{ + T foo(T x) + { + return x+x; + } +} + +enum stringMixin = "int a = 0; +return x + a; +"; + +int mixinTest(int x) +{ + mixin(stringMixin); +} + +int main2(char[][]argv) +{ + enum_name inst_enum = enum_name.kEnum2; + class_member inst_member = new class_member; + base_class inst_base = new base_class; + derived_class inst_derived = new derived_class; + class_method inst_method = new class_method; + class_staticmember inst_staticmember = new class_staticmember; + class_outer inst_outer = new class_outer; + class_outer.class_inner inst_inner = inst_outer.inner; // = new class_outer.class_inner(inst_outer); + struct_name inst_struct; + inst_struct.member = 1; + struct_name.static_member = 3; + this_is_a_rather_long_classname_to_test_what_happens_if_the_classname_gets_longer_than_the_limit_imposed_by_the_old_codeview_format_which_limits_the_length_of_names_to_tw0_hundred_and_fifty_five_characters_because_it_uses_pascal_strings_with_a_length_byte_and_chars_appended long_class_name; + + inst_member.member1 = 2; + + union_name inst_union; + inst_union.float_member = 1; + + int* pointer_int = null; + struct_name* pointer_struct_name = &inst_struct; + class_member* pointer_class_member = &inst_member; + + alias_int_array int_array; + + int[] int_oem_long_dynarray; int_oem_long_dynarray ~= 12; + int[int] local_oem_int_assoc_array; local_oem_int_assoc_array[7] = 17; + + Object null_obj; + derived_class null_derived; + + // delegate + int delegate() dg = &inst_method.method; + int res = dg(); + + return 0; +} + + +int main(char[][]argv) +{ + long lng = 3; + + main2(argv); + + int[int] int_arr; + int_arr[1] = 100; + int_arr[98] = 101; + int_arr[8] = 7; + int_arr[12] = 11; + int_arr[17] = 28; + int_arr[45] = 91; + + struct ab { + int a; + int b; + } + ab ab1 = { 1, 2 }; + ab ab2 = { 3, 4 }; + ab ab3 = { 1, 3 }; + int[ab] struc_arr; + struc_arr[ab1] = 5; + struc_arr[ab2] = 6; + struc_arr[ab3] = 7; + + Templ!(int) templ = new Templ!(int); + int y = templ.foo(3); + int z = mixinTest(7); + + (new Test).test(); + + int[] dynint_arr; + dynint_arr ~= 12; + return dynint_arr.length; +} + +alias invariant(char)[] string; + +class Test +{ + int test() + { + class_outer clss = new class_outer; + + string[] dyn_arr; + dyn_arr ~= "foo"; + dyn_arr ~= "bar"; + + int[string] assoc_arr; + assoc_arr["foo"] = 3; + assoc_arr["bar"] = 1; + assoc_arr["abc"] = 7; + + return dyn_arr.length + assoc_arr.length + clss.c; + } +} -- cgit v0.12