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 62typedef struct { 63 char *name; 64 ULONG64 addr; 65 ULONG flags; 66} SYM; 67 68typedef struct { 69 ULONG64 module_base; 70 SYM *syms; 71 DWORD syms_len; 72 DWORD syms_cap; 73} SYM_CONTEXT; 74 75static 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 86static 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 107static void MaybePrint(const char* var, const char* description) { 108 if (var[0]) 109 printf("%s: %s\n", description, var); 110} 111 112static void PrintAvailability(BOOL var, const char *description) { 113 printf("s: %s\n", description, (var ? "Available" : "Not available")); 114} 115 116static 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 183void usage() { 184 fprintf(stderr, "usage: nm-pdb [-C|--demangle] <module or filename>\n"); 185} 186 187int main(int argc, char *argv[]) { 188 DWORD error; 189 HANDLE process; 190 ULONG64 module_base; 191 SYM_CONTEXT ctx; 192 int i; 193 char* search; 194 char* filename = NULL; 195 int rv = 0; 196 /* We may add SYMOPT_UNDNAME if --demangle is specified: */ 197 DWORD symopts = SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG; 198 199 for (i = 1; i < argc; i++) { 200 if (strcmp(argv[i], "--demangle") == 0 || strcmp(argv[i], "-C") == 0) { 201 symopts |= SYMOPT_UNDNAME; 202 } else if (strcmp(argv[i], "--help") == 0) { 203 usage(); 204 exit(0); 205 } else { 206 break; 207 } 208 } 209 if (i != argc - 1) { 210 usage(); 211 exit(1); 212 } 213 filename = argv[i]; 214 215 process = GetCurrentProcess(); 216 217 if (!SymInitialize(process, NULL, FALSE)) { 218 error = GetLastError(); 219 fprintf(stderr, "SymInitialize returned error : %d\n", error); 220 return 1; 221 } 222 223 search = malloc(SEARCH_CAP); 224 if (SymGetSearchPath(process, search, SEARCH_CAP)) { 225 if (strlen(search) + sizeof(";" WEBSYM) > SEARCH_CAP) { 226 fprintf(stderr, "Search path too long\n"); 227 SymCleanup(process); 228 return 1; 229 } 230 strcat(search, ";" WEBSYM); 231 } else { 232 error = GetLastError(); 233 fprintf(stderr, "SymGetSearchPath returned error : %d\n", error); 234 rv = 1; /* An error, but not a fatal one */ 235 strcpy(search, WEBSYM); /* Use a default value */ 236 } 237 if (!SymSetSearchPath(process, search)) { 238 error = GetLastError(); 239 fprintf(stderr, "SymSetSearchPath returned error : %d\n", error); 240 rv = 1; /* An error, but not a fatal one */ 241 } 242 243 SymSetOptions(symopts); 244 module_base = SymLoadModuleEx(process, NULL, filename, NULL, 0, 0, NULL, 0); 245 if (!module_base) { 246 /* SymLoadModuleEx failed */ 247 error = GetLastError(); 248 fprintf(stderr, "SymLoadModuleEx returned error : %d for %s\n", 249 error, filename); 250 SymCleanup(process); 251 return 1; 252 } 253 254 ShowSymbolInfo(process, module_base); 255 256 memset(&ctx, 0, sizeof(ctx)); 257 ctx.module_base = module_base; 258 if (!SymEnumSymbols(process, module_base, NULL, EnumSymProc, &ctx)) { 259 error = GetLastError(); 260 fprintf(stderr, "SymEnumSymbols returned error: %d\n", error); 261 rv = 1; 262 } else { 263 DWORD j; 264 qsort(ctx.syms, ctx.syms_len, sizeof(ctx.syms[0]), sym_cmp); 265 for (j = 0; j < ctx.syms_len; j++) { 266 printf("%016I64x X %s\n", ctx.syms[j].addr, ctx.syms[j].name); 267 } 268 /* In a perfect world, maybe we'd clean up ctx's memory? */ 269 } 270 SymUnloadModule64(process, module_base); 271 SymCleanup(process); 272 return rv; 273} 274