Austin Schuh | 745610d | 2015-09-06 18:19:50 -0700 | [diff] [blame] | 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 | void usage() { |
| 184 | fprintf(stderr, "usage: nm-pdb [-C|--demangle] <module or filename>\n"); |
| 185 | } |
| 186 | |
| 187 | int 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 | } |