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