1 /* Copyright (c) 2008, Google Inc. 2 * All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * --- 31 * Author: David Vitek 32 * 33 * Dump function addresses using Microsoft debug symbols. This works 34 * on PDB files. Note that this program will download symbols to 35 * c:\websymbols without asking. 36 */ 37 38 #define WIN32_LEAN_AND_MEAN 39 #define _CRT_SECURE_NO_WARNINGS 40 #define _CRT_SECURE_NO_DEPRECATE 41 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> // for _strdup 45 46 #include <windows.h> 47 #include <dbghelp.h> 48 49 // Unfortunately, there is no versioning info in dbghelp.h so I can 50 // tell whether it has an old-style (circa VC7.1) IMAGEHLP_MODULE64 51 // struct, with only a few fields, or a new-style (circa VC8) 52 // IMAGEHLP_MODULE64, with lots of fields. These fields are just used 53 // for debugging, so it's fine to just assume the smaller struct, but 54 // for most people, using a modern MSVC, the full struct is available. 55 // If you are one of those people and would like this extra debugging 56 // info, you can uncomment the line below. 57 //#define VC8_OR_ABOVE 58 59 #define SEARCH_CAP (1024*1024) 60 #define WEBSYM "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols" 61 62 typedef struct { 63 char *name; 64 ULONG64 addr; 65 ULONG flags; 66 } SYM; 67 68 typedef struct { 69 ULONG64 module_base; 70 SYM *syms; 71 DWORD syms_len; 72 DWORD syms_cap; 73 } SYM_CONTEXT; 74 75 static int sym_cmp(const void *_s1, const void *_s2) { 76 const SYM *s1 = (const SYM *)_s1; 77 const SYM *s2 = (const SYM *)_s2; 78 79 if (s1->addr < s2->addr) 80 return -1; 81 if (s1->addr > s2->addr) 82 return 1; 83 return 0; 84 } 85 86 static BOOL CALLBACK EnumSymProc(PSYMBOL_INFO symbol_info, 87 ULONG symbol_size, 88 PVOID user_context) { 89 SYM_CONTEXT *ctx = (SYM_CONTEXT*)user_context; 90 if (symbol_info->Address < ctx->module_base || 91 (symbol_info->Flags & SYMFLAG_TLSREL)) { 92 return TRUE; 93 } 94 if (ctx->syms_len == ctx->syms_cap) { 95 if (!ctx->syms_cap) 96 ctx->syms_cap++; 97 ctx->syms_cap *= 2; 98 ctx->syms = realloc(ctx->syms, sizeof(ctx->syms[0]) * ctx->syms_cap); 99 } 100 ctx->syms[ctx->syms_len].name = _strdup(symbol_info->Name); 101 ctx->syms[ctx->syms_len].addr = symbol_info->Address; 102 ctx->syms[ctx->syms_len].flags = symbol_info->Flags; 103 ctx->syms_len++; 104 return TRUE; 105 } 106 107 static void MaybePrint(const char* var, const char* description) { 108 if (var[0]) 109 printf("%s: %s\n", description, var); 110 } 111 112 static void PrintAvailability(BOOL var, const char *description) { 113 printf("s: %s\n", description, (var ? "Available" : "Not available")); 114 } 115 116 static void ShowSymbolInfo(HANDLE process, ULONG64 module_base) { 117 /* Get module information. */ 118 IMAGEHLP_MODULE64 module_info; 119 BOOL getmoduleinfo_rv; 120 printf("Load Address: %I64x\n", module_base); 121 memset(&module_info, 0, sizeof(module_info)); 122 module_info.SizeOfStruct = sizeof(module_info); 123 getmoduleinfo_rv = SymGetModuleInfo64(process, module_base, &module_info); 124 if (!getmoduleinfo_rv) { 125 printf("Error: SymGetModuleInfo64() failed. Error code: %u\n", 126 GetLastError()); 127 return; 128 } 129 /* Display information about symbols, based on kind of symbol. */ 130 switch (module_info.SymType) { 131 case SymNone: 132 printf(("No symbols available for the module.\n")); 133 break; 134 case SymExport: 135 printf(("Loaded symbols: Exports\n")); 136 break; 137 case SymCoff: 138 printf(("Loaded symbols: COFF\n")); 139 break; 140 case SymCv: 141 printf(("Loaded symbols: CodeView\n")); 142 break; 143 case SymSym: 144 printf(("Loaded symbols: SYM\n")); 145 break; 146 case SymVirtual: 147 printf(("Loaded symbols: Virtual\n")); 148 break; 149 case SymPdb: 150 printf(("Loaded symbols: PDB\n")); 151 break; 152 case SymDia: 153 printf(("Loaded symbols: DIA\n")); 154 break; 155 case SymDeferred: 156 printf(("Loaded symbols: Deferred\n")); /* not actually loaded */ 157 break; 158 default: 159 printf(("Loaded symbols: Unknown format.\n")); 160 break; 161 } 162 163 MaybePrint("Image name", module_info.ImageName); 164 MaybePrint("Loaded image name", module_info.LoadedImageName); 165 #ifdef VC8_OR_ABOVE /* TODO(csilvers): figure out how to tell */ 166 MaybePrint("PDB file name", module_info.LoadedPdbName); 167 if (module_info.PdbUnmatched || module_info.DbgUnmatched) { 168 /* This can only happen if the debug information is contained in a 169 * separate file (.DBG or .PDB) 170 */ 171 printf(("Warning: Unmatched symbols.\n")); 172 } 173 #endif 174 175 /* Contents */ 176 #ifdef VC8_OR_ABOVE /* TODO(csilvers): figure out how to tell */ 177 PrintAvailability("Line numbers", module_info.LineNumbers); 178 PrintAvailability("Global symbols", module_info.GlobalSymbols); 179 PrintAvailability("Type information", module_info.TypeInfo); 180 #endif 181 } 182 183 int main(int argc, char *argv[]) { 184 DWORD error; 185 HANDLE process; 186 ULONG64 module_base; 187 SYM_CONTEXT ctx; 188 int i; 189 char* search; 190 char* filename = NULL; 191 int rv = 0; 192 /* We may add SYMOPT_UNDNAME if --demangle is specified: */ 193 DWORD symopts = SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG; 194 195 for (i = 1; i < argc; i++) { 196 if (strcmp(argv[i], "--demangle") == 0 || strcmp(argv[i], "-C") == 0) { 197 symopts |= SYMOPT_UNDNAME; 198 } else { 199 break; 200 } 201 } 202 if (i != argc - 1) { 203 fprintf(stderr, "usage: nm-pdb [-C|--demangle] <module or filename>\n"); 204 exit(1); 205 } 206 filename = argv[i]; 207 208 process = GetCurrentProcess(); 209 210 if (!SymInitialize(process, NULL, FALSE)) { 211 error = GetLastError(); 212 fprintf(stderr, "SymInitialize returned error : %d\n", error); 213 return 1; 214 } 215 216 search = malloc(SEARCH_CAP); 217 if (SymGetSearchPath(process, search, SEARCH_CAP)) { 218 if (strlen(search) + sizeof(";" WEBSYM) > SEARCH_CAP) { 219 fprintf(stderr, "Search path too long\n"); 220 SymCleanup(process); 221 return 1; 222 } 223 strcat(search, ";" WEBSYM); 224 } else { 225 error = GetLastError(); 226 fprintf(stderr, "SymGetSearchPath returned error : %d\n", error); 227 rv = 1; /* An error, but not a fatal one */ 228 strcpy(search, WEBSYM); /* Use a default value */ 229 } 230 if (!SymSetSearchPath(process, search)) { 231 error = GetLastError(); 232 fprintf(stderr, "SymSetSearchPath returned error : %d\n", error); 233 rv = 1; /* An error, but not a fatal one */ 234 } 235 236 SymSetOptions(symopts); 237 module_base = SymLoadModuleEx(process, NULL, filename, NULL, 0, 0, NULL, 0); 238 if (!module_base) { 239 /* SymLoadModuleEx failed */ 240 error = GetLastError(); 241 fprintf(stderr, "SymLoadModuleEx returned error : %d for %s\n", 242 error, filename); 243 SymCleanup(process); 244 return 1; 245 } 246 247 ShowSymbolInfo(process, module_base); 248 249 memset(&ctx, 0, sizeof(ctx)); 250 ctx.module_base = module_base; 251 if (!SymEnumSymbols(process, module_base, NULL, EnumSymProc, &ctx)) { 252 error = GetLastError(); 253 fprintf(stderr, "SymEnumSymbols returned error: %d\n", error); 254 rv = 1; 255 } else { 256 DWORD j; 257 qsort(ctx.syms, ctx.syms_len, sizeof(ctx.syms[0]), sym_cmp); 258 for (j = 0; j < ctx.syms_len; j++) { 259 printf("%016I64x X %s\n", ctx.syms[j].addr, ctx.syms[j].name); 260 } 261 /* In a perfect world, maybe we'd clean up ctx's memory? */ 262 } 263 SymUnloadModule64(process, module_base); 264 SymCleanup(process); 265 return rv; 266 } 267