| // Copyright (c) 2006, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "common/windows/pdb_source_line_writer.h" |
| |
| #include <windows.h> |
| #include <winnt.h> |
| #include <atlbase.h> |
| #include <dia2.h> |
| #include <diacreate.h> |
| #include <ImageHlp.h> |
| #include <stdio.h> |
| |
| #include <algorithm> |
| #include <limits> |
| #include <map> |
| #include <set> |
| #include <utility> |
| |
| #include "common/windows/dia_util.h" |
| #include "common/windows/guid_string.h" |
| #include "common/windows/string_utils-inl.h" |
| |
| // This constant may be missing from DbgHelp.h. See the documentation for |
| // IDiaSymbol::get_undecoratedNameEx. |
| #ifndef UNDNAME_NO_ECSU |
| #define UNDNAME_NO_ECSU 0x8000 // Suppresses enum/class/struct/union. |
| #endif // UNDNAME_NO_ECSU |
| |
| /* |
| * Not defined in WinNT.h for some reason. Definitions taken from: |
| * http://uninformed.org/index.cgi?v=4&a=1&p=13 |
| * |
| */ |
| typedef unsigned char UBYTE; |
| |
| #if !defined(_WIN64) |
| #define UNW_FLAG_EHANDLER 0x01 |
| #define UNW_FLAG_UHANDLER 0x02 |
| #define UNW_FLAG_CHAININFO 0x04 |
| #endif |
| |
| union UnwindCode { |
| struct { |
| UBYTE offset_in_prolog; |
| UBYTE unwind_operation_code : 4; |
| UBYTE operation_info : 4; |
| }; |
| USHORT frame_offset; |
| }; |
| |
| enum UnwindOperationCodes { |
| UWOP_PUSH_NONVOL = 0, /* info == register number */ |
| UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */ |
| UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */ |
| UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */ |
| UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */ |
| UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */ |
| // XXX: these are missing from MSDN! |
| // See: http://www.osronline.com/ddkx/kmarch/64bitamd_4rs7.htm |
| UWOP_SAVE_XMM, |
| UWOP_SAVE_XMM_FAR, |
| UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */ |
| UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */ |
| UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */ |
| }; |
| |
| // See: http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx |
| // Note: some fields removed as we don't use them. |
| struct UnwindInfo { |
| UBYTE version : 3; |
| UBYTE flags : 5; |
| UBYTE size_of_prolog; |
| UBYTE count_of_codes; |
| UBYTE frame_register : 4; |
| UBYTE frame_offset : 4; |
| UnwindCode unwind_code[1]; |
| }; |
| |
| namespace google_breakpad { |
| |
| namespace { |
| |
| using std::vector; |
| |
| // The symbol (among possibly many) selected to represent an rva. |
| struct SelectedSymbol { |
| SelectedSymbol(const CComPtr<IDiaSymbol>& symbol, bool is_public) |
| : symbol(symbol), is_public(is_public), is_multiple(false) {} |
| |
| // The symbol to use for an rva. |
| CComPtr<IDiaSymbol> symbol; |
| // Whether this is a public or function symbol. |
| bool is_public; |
| // Whether the rva has multiple associated symbols. An rva will correspond to |
| // multiple symbols in the case of linker identical symbol folding. |
| bool is_multiple; |
| }; |
| |
| // Maps rva to the symbol to use for that address. |
| typedef std::map<DWORD, SelectedSymbol> SymbolMap; |
| |
| // Record this in the map as the selected symbol for the rva if it satisfies the |
| // necessary conditions. |
| void MaybeRecordSymbol(DWORD rva, |
| const CComPtr<IDiaSymbol> symbol, |
| bool is_public, |
| SymbolMap* map) { |
| SymbolMap::iterator loc = map->find(rva); |
| if (loc == map->end()) { |
| map->insert(std::make_pair(rva, SelectedSymbol(symbol, is_public))); |
| return; |
| } |
| |
| // Prefer function symbols to public symbols. |
| if (is_public && !loc->second.is_public) { |
| return; |
| } |
| |
| loc->second.is_multiple = true; |
| |
| // Take the 'least' symbol by lexicographical order of the decorated name. We |
| // use the decorated rather than undecorated name because computing the latter |
| // is expensive. |
| BSTR current_name, new_name; |
| loc->second.symbol->get_name(¤t_name); |
| symbol->get_name(&new_name); |
| if (wcscmp(new_name, current_name) < 0) { |
| loc->second.symbol = symbol; |
| loc->second.is_public = is_public; |
| } |
| } |
| |
| // A helper class to scope a PLOADED_IMAGE. |
| class AutoImage { |
| public: |
| explicit AutoImage(PLOADED_IMAGE img) : img_(img) {} |
| ~AutoImage() { |
| if (img_) |
| ImageUnload(img_); |
| } |
| |
| operator PLOADED_IMAGE() { return img_; } |
| PLOADED_IMAGE operator->() { return img_; } |
| |
| private: |
| PLOADED_IMAGE img_; |
| }; |
| |
| bool SymbolsMatch(IDiaSymbol* a, IDiaSymbol* b) { |
| DWORD a_section, a_offset, b_section, b_offset; |
| if (FAILED(a->get_addressSection(&a_section)) || |
| FAILED(a->get_addressOffset(&a_offset)) || |
| FAILED(b->get_addressSection(&b_section)) || |
| FAILED(b->get_addressOffset(&b_offset))) |
| return false; |
| return a_section == b_section && a_offset == b_offset; |
| } |
| |
| bool CreateDiaDataSourceInstance(CComPtr<IDiaDataSource> &data_source) { |
| if (SUCCEEDED(data_source.CoCreateInstance(CLSID_DiaSource))) { |
| return true; |
| } |
| |
| class DECLSPEC_UUID("B86AE24D-BF2F-4ac9-B5A2-34B14E4CE11D") DiaSource100; |
| class DECLSPEC_UUID("761D3BCD-1304-41D5-94E8-EAC54E4AC172") DiaSource110; |
| class DECLSPEC_UUID("3BFCEA48-620F-4B6B-81F7-B9AF75454C7D") DiaSource120; |
| class DECLSPEC_UUID("E6756135-1E65-4D17-8576-610761398C3C") DiaSource140; |
| |
| // If the CoCreateInstance call above failed, msdia*.dll is not registered. |
| // We can try loading the DLL corresponding to the #included DIA SDK, but |
| // the DIA headers don't provide a version. Lets try to figure out which DIA |
| // version we're compiling against by comparing CLSIDs. |
| const wchar_t *msdia_dll = nullptr; |
| if (CLSID_DiaSource == _uuidof(DiaSource100)) { |
| msdia_dll = L"msdia100.dll"; |
| } else if (CLSID_DiaSource == _uuidof(DiaSource110)) { |
| msdia_dll = L"msdia110.dll"; |
| } else if (CLSID_DiaSource == _uuidof(DiaSource120)) { |
| msdia_dll = L"msdia120.dll"; |
| } else if (CLSID_DiaSource == _uuidof(DiaSource140)) { |
| msdia_dll = L"msdia140.dll"; |
| } |
| |
| if (msdia_dll && |
| SUCCEEDED(NoRegCoCreate(msdia_dll, CLSID_DiaSource, IID_IDiaDataSource, |
| reinterpret_cast<void **>(&data_source)))) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| } // namespace |
| |
| PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) { |
| } |
| |
| PDBSourceLineWriter::~PDBSourceLineWriter() { |
| } |
| |
| bool PDBSourceLineWriter::SetCodeFile(const wstring &exe_file) { |
| if (code_file_.empty()) { |
| code_file_ = exe_file; |
| return true; |
| } |
| // Setting a different code file path is an error. It is success only if the |
| // file paths are the same. |
| return exe_file == code_file_; |
| } |
| |
| bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) { |
| Close(); |
| code_file_.clear(); |
| |
| if (FAILED(CoInitialize(NULL))) { |
| fprintf(stderr, "CoInitialize failed\n"); |
| return false; |
| } |
| |
| CComPtr<IDiaDataSource> data_source; |
| if (!CreateDiaDataSourceInstance(data_source)) { |
| const int kGuidSize = 64; |
| wchar_t classid[kGuidSize] = {0}; |
| StringFromGUID2(CLSID_DiaSource, classid, kGuidSize); |
| fprintf(stderr, "CoCreateInstance CLSID_DiaSource %S failed " |
| "(msdia*.dll unregistered?)\n", classid); |
| return false; |
| } |
| |
| switch (format) { |
| case PDB_FILE: |
| if (FAILED(data_source->loadDataFromPdb(file.c_str()))) { |
| fprintf(stderr, "loadDataFromPdb failed for %ws\n", file.c_str()); |
| return false; |
| } |
| break; |
| case EXE_FILE: |
| if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) { |
| fprintf(stderr, "loadDataForExe failed for %ws\n", file.c_str()); |
| return false; |
| } |
| code_file_ = file; |
| break; |
| case ANY_FILE: |
| if (FAILED(data_source->loadDataFromPdb(file.c_str()))) { |
| if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) { |
| fprintf(stderr, "loadDataForPdb and loadDataFromExe failed for %ws\n", |
| file.c_str()); |
| return false; |
| } |
| code_file_ = file; |
| } |
| break; |
| default: |
| fprintf(stderr, "Unknown file format\n"); |
| return false; |
| } |
| |
| if (FAILED(data_source->openSession(&session_))) { |
| fprintf(stderr, "openSession failed\n"); |
| } |
| |
| return true; |
| } |
| |
| bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) { |
| // The line number format is: |
| // <rva> <line number> <source file id> |
| CComPtr<IDiaLineNumber> line; |
| ULONG count; |
| |
| while (SUCCEEDED(lines->Next(1, &line, &count)) && count == 1) { |
| DWORD rva; |
| if (FAILED(line->get_relativeVirtualAddress(&rva))) { |
| fprintf(stderr, "failed to get line rva\n"); |
| return false; |
| } |
| |
| DWORD length; |
| if (FAILED(line->get_length(&length))) { |
| fprintf(stderr, "failed to get line code length\n"); |
| return false; |
| } |
| |
| DWORD dia_source_id; |
| if (FAILED(line->get_sourceFileId(&dia_source_id))) { |
| fprintf(stderr, "failed to get line source file id\n"); |
| return false; |
| } |
| // duplicate file names are coalesced to share one ID |
| DWORD source_id = GetRealFileID(dia_source_id); |
| |
| DWORD line_num; |
| if (FAILED(line->get_lineNumber(&line_num))) { |
| fprintf(stderr, "failed to get line number\n"); |
| return false; |
| } |
| |
| AddressRangeVector ranges; |
| MapAddressRange(image_map_, AddressRange(rva, length), &ranges); |
| for (size_t i = 0; i < ranges.size(); ++i) { |
| fprintf(output_, "%lx %lx %lu %lu\n", ranges[i].rva, ranges[i].length, |
| line_num, source_id); |
| } |
| line.Release(); |
| } |
| return true; |
| } |
| |
| bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function, |
| IDiaSymbol *block, |
| bool has_multiple_symbols) { |
| // The function format is: |
| // FUNC <address> <length> <param_stack_size> <function> |
| DWORD rva; |
| if (FAILED(block->get_relativeVirtualAddress(&rva))) { |
| fprintf(stderr, "couldn't get rva\n"); |
| return false; |
| } |
| |
| ULONGLONG length; |
| if (FAILED(block->get_length(&length))) { |
| fprintf(stderr, "failed to get function length\n"); |
| return false; |
| } |
| |
| if (length == 0) { |
| // Silently ignore zero-length functions, which can infrequently pop up. |
| return true; |
| } |
| |
| CComBSTR name; |
| int stack_param_size; |
| if (!GetSymbolFunctionName(function, &name, &stack_param_size)) { |
| return false; |
| } |
| |
| // If the decorated name didn't give the parameter size, try to |
| // calculate it. |
| if (stack_param_size < 0) { |
| stack_param_size = GetFunctionStackParamSize(function); |
| } |
| |
| AddressRangeVector ranges; |
| MapAddressRange(image_map_, AddressRange(rva, static_cast<DWORD>(length)), |
| &ranges); |
| for (size_t i = 0; i < ranges.size(); ++i) { |
| const char* optional_multiple_field = has_multiple_symbols ? "m " : ""; |
| fprintf(output_, "FUNC %s%lx %lx %x %ws\n", optional_multiple_field, |
| ranges[i].rva, ranges[i].length, stack_param_size, name.m_str); |
| } |
| |
| CComPtr<IDiaEnumLineNumbers> lines; |
| if (FAILED(session_->findLinesByRVA(rva, DWORD(length), &lines))) { |
| return false; |
| } |
| |
| if (!PrintLines(lines)) { |
| return false; |
| } |
| return true; |
| } |
| |
| bool PDBSourceLineWriter::PrintSourceFiles() { |
| CComPtr<IDiaSymbol> global; |
| if (FAILED(session_->get_globalScope(&global))) { |
| fprintf(stderr, "get_globalScope failed\n"); |
| return false; |
| } |
| |
| CComPtr<IDiaEnumSymbols> compilands; |
| if (FAILED(global->findChildren(SymTagCompiland, NULL, |
| nsNone, &compilands))) { |
| fprintf(stderr, "findChildren failed\n"); |
| return false; |
| } |
| |
| CComPtr<IDiaSymbol> compiland; |
| ULONG count; |
| while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) { |
| CComPtr<IDiaEnumSourceFiles> source_files; |
| if (FAILED(session_->findFile(compiland, NULL, nsNone, &source_files))) { |
| return false; |
| } |
| CComPtr<IDiaSourceFile> file; |
| while (SUCCEEDED(source_files->Next(1, &file, &count)) && count == 1) { |
| DWORD file_id; |
| if (FAILED(file->get_uniqueId(&file_id))) { |
| return false; |
| } |
| |
| CComBSTR file_name; |
| if (FAILED(file->get_fileName(&file_name))) { |
| return false; |
| } |
| |
| wstring file_name_string(file_name); |
| if (!FileIDIsCached(file_name_string)) { |
| // this is a new file name, cache it and output a FILE line. |
| CacheFileID(file_name_string, file_id); |
| fwprintf(output_, L"FILE %d %ws\n", file_id, file_name_string.c_str()); |
| } else { |
| // this file name has already been seen, just save this |
| // ID for later lookup. |
| StoreDuplicateFileID(file_name_string, file_id); |
| } |
| file.Release(); |
| } |
| compiland.Release(); |
| } |
| return true; |
| } |
| |
| bool PDBSourceLineWriter::PrintFunctions() { |
| ULONG count = 0; |
| DWORD rva = 0; |
| CComPtr<IDiaSymbol> global; |
| HRESULT hr; |
| |
| if (FAILED(session_->get_globalScope(&global))) { |
| fprintf(stderr, "get_globalScope failed\n"); |
| return false; |
| } |
| |
| CComPtr<IDiaEnumSymbols> symbols = NULL; |
| |
| // Find all function symbols first. |
| SymbolMap rva_symbol; |
| hr = global->findChildren(SymTagFunction, NULL, nsNone, &symbols); |
| |
| if (SUCCEEDED(hr)) { |
| CComPtr<IDiaSymbol> symbol = NULL; |
| |
| while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) { |
| if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) { |
| // Potentially record this as the canonical symbol for this rva. |
| MaybeRecordSymbol(rva, symbol, false, &rva_symbol); |
| } else { |
| fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n"); |
| return false; |
| } |
| |
| symbol.Release(); |
| } |
| |
| symbols.Release(); |
| } |
| |
| // Find all public symbols and record public symbols that are not also private |
| // symbols. |
| hr = global->findChildren(SymTagPublicSymbol, NULL, nsNone, &symbols); |
| |
| if (SUCCEEDED(hr)) { |
| CComPtr<IDiaSymbol> symbol = NULL; |
| |
| while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) { |
| if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) { |
| // Potentially record this as the canonical symbol for this rva. |
| MaybeRecordSymbol(rva, symbol, true, &rva_symbol); |
| } else { |
| fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n"); |
| return false; |
| } |
| |
| symbol.Release(); |
| } |
| |
| symbols.Release(); |
| } |
| |
| // For each rva, dump the selected symbol at the address. |
| SymbolMap::iterator it; |
| for (it = rva_symbol.begin(); it != rva_symbol.end(); ++it) { |
| CComPtr<IDiaSymbol> symbol = it->second.symbol; |
| // Only print public symbols if there is no function symbol for the address. |
| if (!it->second.is_public) { |
| if (!PrintFunction(symbol, symbol, it->second.is_multiple)) |
| return false; |
| } else { |
| if (!PrintCodePublicSymbol(symbol, it->second.is_multiple)) |
| return false; |
| } |
| } |
| |
| // When building with PGO, the compiler can split functions into |
| // "hot" and "cold" blocks, and move the "cold" blocks out to separate |
| // pages, so the function can be noncontiguous. To find these blocks, |
| // we have to iterate over all the compilands, and then find blocks |
| // that are children of them. We can then find the lexical parents |
| // of those blocks and print out an extra FUNC line for blocks |
| // that are not contained in their parent functions. |
| CComPtr<IDiaEnumSymbols> compilands; |
| if (FAILED(global->findChildren(SymTagCompiland, NULL, |
| nsNone, &compilands))) { |
| fprintf(stderr, "findChildren failed on the global\n"); |
| return false; |
| } |
| |
| CComPtr<IDiaSymbol> compiland; |
| while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) { |
| CComPtr<IDiaEnumSymbols> blocks; |
| if (FAILED(compiland->findChildren(SymTagBlock, NULL, |
| nsNone, &blocks))) { |
| fprintf(stderr, "findChildren failed on a compiland\n"); |
| return false; |
| } |
| |
| CComPtr<IDiaSymbol> block; |
| while (SUCCEEDED(blocks->Next(1, &block, &count)) && count == 1) { |
| // find this block's lexical parent function |
| CComPtr<IDiaSymbol> parent; |
| DWORD tag; |
| if (SUCCEEDED(block->get_lexicalParent(&parent)) && |
| SUCCEEDED(parent->get_symTag(&tag)) && |
| tag == SymTagFunction) { |
| // now get the block's offset and the function's offset and size, |
| // and determine if the block is outside of the function |
| DWORD func_rva, block_rva; |
| ULONGLONG func_length; |
| if (SUCCEEDED(block->get_relativeVirtualAddress(&block_rva)) && |
| SUCCEEDED(parent->get_relativeVirtualAddress(&func_rva)) && |
| SUCCEEDED(parent->get_length(&func_length))) { |
| if (block_rva < func_rva || block_rva > (func_rva + func_length)) { |
| if (!PrintFunction(parent, block, false)) { |
| return false; |
| } |
| } |
| } |
| } |
| parent.Release(); |
| block.Release(); |
| } |
| blocks.Release(); |
| compiland.Release(); |
| } |
| |
| global.Release(); |
| return true; |
| } |
| |
| #undef max |
| |
| bool PDBSourceLineWriter::PrintFrameDataUsingPDB() { |
| // It would be nice if it were possible to output frame data alongside the |
| // associated function, as is done with line numbers, but the DIA API |
| // doesn't make it possible to get the frame data in that way. |
| |
| CComPtr<IDiaEnumFrameData> frame_data_enum; |
| if (!FindTable(session_, &frame_data_enum)) |
| return false; |
| |
| DWORD last_type = std::numeric_limits<DWORD>::max(); |
| DWORD last_rva = std::numeric_limits<DWORD>::max(); |
| DWORD last_code_size = 0; |
| DWORD last_prolog_size = std::numeric_limits<DWORD>::max(); |
| |
| CComPtr<IDiaFrameData> frame_data; |
| ULONG count = 0; |
| while (SUCCEEDED(frame_data_enum->Next(1, &frame_data, &count)) && |
| count == 1) { |
| DWORD type; |
| if (FAILED(frame_data->get_type(&type))) |
| return false; |
| |
| DWORD rva; |
| if (FAILED(frame_data->get_relativeVirtualAddress(&rva))) |
| return false; |
| |
| DWORD code_size; |
| if (FAILED(frame_data->get_lengthBlock(&code_size))) |
| return false; |
| |
| DWORD prolog_size; |
| if (FAILED(frame_data->get_lengthProlog(&prolog_size))) |
| return false; |
| |
| // parameter_size is the size of parameters passed on the stack. If any |
| // parameters are not passed on the stack (such as in registers), their |
| // sizes will not be included in parameter_size. |
| DWORD parameter_size; |
| if (FAILED(frame_data->get_lengthParams(¶meter_size))) |
| return false; |
| |
| DWORD saved_register_size; |
| if (FAILED(frame_data->get_lengthSavedRegisters(&saved_register_size))) |
| return false; |
| |
| DWORD local_size; |
| if (FAILED(frame_data->get_lengthLocals(&local_size))) |
| return false; |
| |
| // get_maxStack can return S_FALSE, just use 0 in that case. |
| DWORD max_stack_size = 0; |
| if (FAILED(frame_data->get_maxStack(&max_stack_size))) |
| return false; |
| |
| // get_programString can return S_FALSE, indicating that there is no |
| // program string. In that case, check whether %ebp is used. |
| HRESULT program_string_result; |
| CComBSTR program_string; |
| if (FAILED(program_string_result = frame_data->get_program( |
| &program_string))) { |
| return false; |
| } |
| |
| // get_allocatesBasePointer can return S_FALSE, treat that as though |
| // %ebp is not used. |
| BOOL allocates_base_pointer = FALSE; |
| if (program_string_result != S_OK) { |
| if (FAILED(frame_data->get_allocatesBasePointer( |
| &allocates_base_pointer))) { |
| return false; |
| } |
| } |
| |
| // Only print out a line if type, rva, code_size, or prolog_size have |
| // changed from the last line. It is surprisingly common (especially in |
| // system library PDBs) for DIA to return a series of identical |
| // IDiaFrameData objects. For kernel32.pdb from Windows XP SP2 on x86, |
| // this check reduces the size of the dumped symbol file by a third. |
| if (type != last_type || rva != last_rva || code_size != last_code_size || |
| prolog_size != last_prolog_size) { |
| // The prolog and the code portions of the frame have to be treated |
| // independently as they may have independently changed in size, or may |
| // even have been split. |
| // NOTE: If epilog size is ever non-zero, we have to do something |
| // similar with it. |
| |
| // Figure out where the prolog bytes have landed. |
| AddressRangeVector prolog_ranges; |
| if (prolog_size > 0) { |
| MapAddressRange(image_map_, AddressRange(rva, prolog_size), |
| &prolog_ranges); |
| } |
| |
| // And figure out where the code bytes have landed. |
| AddressRangeVector code_ranges; |
| MapAddressRange(image_map_, |
| AddressRange(rva + prolog_size, |
| code_size - prolog_size), |
| &code_ranges); |
| |
| struct FrameInfo { |
| DWORD rva; |
| DWORD code_size; |
| DWORD prolog_size; |
| }; |
| std::vector<FrameInfo> frame_infos; |
| |
| // Special case: The prolog and the code bytes remain contiguous. This is |
| // only done for compactness of the symbol file, and we could actually |
| // be outputting independent frame info for the prolog and code portions. |
| if (prolog_ranges.size() == 1 && code_ranges.size() == 1 && |
| prolog_ranges[0].end() == code_ranges[0].rva) { |
| FrameInfo fi = { prolog_ranges[0].rva, |
| prolog_ranges[0].length + code_ranges[0].length, |
| prolog_ranges[0].length }; |
| frame_infos.push_back(fi); |
| } else { |
| // Otherwise we output the prolog and code frame info independently. |
| for (size_t i = 0; i < prolog_ranges.size(); ++i) { |
| FrameInfo fi = { prolog_ranges[i].rva, |
| prolog_ranges[i].length, |
| prolog_ranges[i].length }; |
| frame_infos.push_back(fi); |
| } |
| for (size_t i = 0; i < code_ranges.size(); ++i) { |
| FrameInfo fi = { code_ranges[i].rva, code_ranges[i].length, 0 }; |
| frame_infos.push_back(fi); |
| } |
| } |
| |
| for (size_t i = 0; i < frame_infos.size(); ++i) { |
| const FrameInfo& fi(frame_infos[i]); |
| fprintf(output_, "STACK WIN %lx %lx %lx %lx %x %lx %lx %lx %lx %d ", |
| type, fi.rva, fi.code_size, fi.prolog_size, |
| 0 /* epilog_size */, parameter_size, saved_register_size, |
| local_size, max_stack_size, program_string_result == S_OK); |
| if (program_string_result == S_OK) { |
| fprintf(output_, "%ws\n", program_string.m_str); |
| } else { |
| fprintf(output_, "%d\n", allocates_base_pointer); |
| } |
| } |
| |
| last_type = type; |
| last_rva = rva; |
| last_code_size = code_size; |
| last_prolog_size = prolog_size; |
| } |
| |
| frame_data.Release(); |
| } |
| |
| return true; |
| } |
| |
| bool PDBSourceLineWriter::PrintFrameDataUsingEXE() { |
| if (code_file_.empty() && !FindPEFile()) { |
| fprintf(stderr, "Couldn't locate EXE or DLL file.\n"); |
| return false; |
| } |
| |
| // Convert wchar to native charset because ImageLoad only takes |
| // a PSTR as input. |
| string code_file; |
| if (!WindowsStringUtils::safe_wcstombs(code_file_, &code_file)) { |
| return false; |
| } |
| |
| AutoImage img(ImageLoad((PSTR)code_file.c_str(), NULL)); |
| if (!img) { |
| fprintf(stderr, "Failed to load %s\n", code_file.c_str()); |
| return false; |
| } |
| PIMAGE_OPTIONAL_HEADER64 optional_header = |
| &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader; |
| if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { |
| fprintf(stderr, "Not a PE32+ image\n"); |
| return false; |
| } |
| |
| // Read Exception Directory |
| DWORD exception_rva = optional_header-> |
| DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress; |
| DWORD exception_size = optional_header-> |
| DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size; |
| PIMAGE_RUNTIME_FUNCTION_ENTRY funcs = |
| static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>( |
| ImageRvaToVa(img->FileHeader, |
| img->MappedAddress, |
| exception_rva, |
| &img->LastRvaSection)); |
| for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) { |
| DWORD unwind_rva = funcs[i].UnwindInfoAddress; |
| // handle chaining |
| while (unwind_rva & 0x1) { |
| unwind_rva ^= 0x1; |
| PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func = |
| static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>( |
| ImageRvaToVa(img->FileHeader, |
| img->MappedAddress, |
| unwind_rva, |
| &img->LastRvaSection)); |
| unwind_rva = chained_func->UnwindInfoAddress; |
| } |
| |
| UnwindInfo *unwind_info = static_cast<UnwindInfo *>( |
| ImageRvaToVa(img->FileHeader, |
| img->MappedAddress, |
| unwind_rva, |
| &img->LastRvaSection)); |
| |
| DWORD stack_size = 8; // minimal stack size is 8 for RIP |
| DWORD rip_offset = 8; |
| do { |
| for (UBYTE c = 0; c < unwind_info->count_of_codes; c++) { |
| UnwindCode *unwind_code = &unwind_info->unwind_code[c]; |
| switch (unwind_code->unwind_operation_code) { |
| case UWOP_PUSH_NONVOL: { |
| stack_size += 8; |
| break; |
| } |
| case UWOP_ALLOC_LARGE: { |
| if (unwind_code->operation_info == 0) { |
| c++; |
| if (c < unwind_info->count_of_codes) |
| stack_size += (unwind_code + 1)->frame_offset * 8; |
| } else { |
| c += 2; |
| if (c < unwind_info->count_of_codes) |
| stack_size += (unwind_code + 1)->frame_offset | |
| ((unwind_code + 2)->frame_offset << 16); |
| } |
| break; |
| } |
| case UWOP_ALLOC_SMALL: { |
| stack_size += unwind_code->operation_info * 8 + 8; |
| break; |
| } |
| case UWOP_SET_FPREG: |
| case UWOP_SAVE_XMM: |
| case UWOP_SAVE_XMM_FAR: |
| break; |
| case UWOP_SAVE_NONVOL: |
| case UWOP_SAVE_XMM128: { |
| c++; // skip slot with offset |
| break; |
| } |
| case UWOP_SAVE_NONVOL_FAR: |
| case UWOP_SAVE_XMM128_FAR: { |
| c += 2; // skip 2 slots with offset |
| break; |
| } |
| case UWOP_PUSH_MACHFRAME: { |
| if (unwind_code->operation_info) { |
| stack_size += 88; |
| } else { |
| stack_size += 80; |
| } |
| rip_offset += 80; |
| break; |
| } |
| } |
| } |
| if (unwind_info->flags & UNW_FLAG_CHAININFO) { |
| PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func = |
| reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>( |
| (unwind_info->unwind_code + |
| ((unwind_info->count_of_codes + 1) & ~1))); |
| |
| unwind_info = static_cast<UnwindInfo *>( |
| ImageRvaToVa(img->FileHeader, |
| img->MappedAddress, |
| chained_func->UnwindInfoAddress, |
| &img->LastRvaSection)); |
| } else { |
| unwind_info = NULL; |
| } |
| } while (unwind_info); |
| fprintf(output_, "STACK CFI INIT %lx %lx .cfa: $rsp .ra: .cfa %lu - ^\n", |
| funcs[i].BeginAddress, |
| funcs[i].EndAddress - funcs[i].BeginAddress, rip_offset); |
| fprintf(output_, "STACK CFI %lx .cfa: $rsp %lu +\n", |
| funcs[i].BeginAddress, stack_size); |
| } |
| |
| return true; |
| } |
| |
| bool PDBSourceLineWriter::PrintFrameData() { |
| PDBModuleInfo info; |
| if (GetModuleInfo(&info) && info.cpu == L"x86_64") { |
| return PrintFrameDataUsingEXE(); |
| } else { |
| return PrintFrameDataUsingPDB(); |
| } |
| return false; |
| } |
| |
| bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol, |
| bool has_multiple_symbols) { |
| BOOL is_code; |
| if (FAILED(symbol->get_code(&is_code))) { |
| return false; |
| } |
| if (!is_code) { |
| return true; |
| } |
| |
| DWORD rva; |
| if (FAILED(symbol->get_relativeVirtualAddress(&rva))) { |
| return false; |
| } |
| |
| CComBSTR name; |
| int stack_param_size; |
| if (!GetSymbolFunctionName(symbol, &name, &stack_param_size)) { |
| return false; |
| } |
| |
| AddressRangeVector ranges; |
| MapAddressRange(image_map_, AddressRange(rva, 1), &ranges); |
| for (size_t i = 0; i < ranges.size(); ++i) { |
| const char* optional_multiple_field = has_multiple_symbols ? "m " : ""; |
| fprintf(output_, "PUBLIC %s%lx %x %ws\n", optional_multiple_field, |
| ranges[i].rva, stack_param_size > 0 ? stack_param_size : 0, |
| name.m_str); |
| } |
| |
| // Now walk the function in the original untranslated space, asking DIA |
| // what function is at that location, stepping through OMAP blocks. If |
| // we're still in the same function, emit another entry, because the |
| // symbol could have been split into multiple pieces. If we've gotten to |
| // another symbol in the original address space, then we're done for |
| // this symbol. See https://crbug.com/678874. |
| for (;;) { |
| // This steps to the next block in the original image. Simply doing |
| // rva++ would also be correct, but would emit tons of unnecessary |
| // entries. |
| rva = image_map_.subsequent_rva_block[rva]; |
| if (rva == 0) |
| break; |
| |
| CComPtr<IDiaSymbol> next_sym = NULL; |
| LONG displacement; |
| if (FAILED(session_->findSymbolByRVAEx(rva, SymTagPublicSymbol, &next_sym, |
| &displacement))) { |
| break; |
| } |
| |
| if (!SymbolsMatch(symbol, next_sym)) |
| break; |
| |
| AddressRangeVector next_ranges; |
| MapAddressRange(image_map_, AddressRange(rva, 1), &next_ranges); |
| for (size_t i = 0; i < next_ranges.size(); ++i) { |
| fprintf(output_, "PUBLIC %lx %x %ws\n", next_ranges[i].rva, |
| stack_param_size > 0 ? stack_param_size : 0, name.m_str); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool PDBSourceLineWriter::PrintPDBInfo() { |
| PDBModuleInfo info; |
| if (!GetModuleInfo(&info)) { |
| return false; |
| } |
| |
| // Hard-code "windows" for the OS because that's the only thing that makes |
| // sense for PDB files. (This might not be strictly correct for Windows CE |
| // support, but we don't care about that at the moment.) |
| fprintf(output_, "MODULE windows %ws %ws %ws\n", |
| info.cpu.c_str(), info.debug_identifier.c_str(), |
| info.debug_file.c_str()); |
| |
| return true; |
| } |
| |
| bool PDBSourceLineWriter::PrintPEInfo() { |
| PEModuleInfo info; |
| if (!GetPEInfo(&info)) { |
| return false; |
| } |
| |
| fprintf(output_, "INFO CODE_ID %ws %ws\n", |
| info.code_identifier.c_str(), |
| info.code_file.c_str()); |
| return true; |
| } |
| |
| // wcstol_positive_strict is sort of like wcstol, but much stricter. string |
| // should be a buffer pointing to a null-terminated string containing only |
| // decimal digits. If the entire string can be converted to an integer |
| // without overflowing, and there are no non-digit characters before the |
| // result is set to the value and this function returns true. Otherwise, |
| // this function returns false. This is an alternative to the strtol, atoi, |
| // and scanf families, which are not as strict about input and in some cases |
| // don't provide a good way for the caller to determine if a conversion was |
| // successful. |
| static bool wcstol_positive_strict(wchar_t *string, int *result) { |
| int value = 0; |
| for (wchar_t *c = string; *c != '\0'; ++c) { |
| int last_value = value; |
| value *= 10; |
| // Detect overflow. |
| if (value / 10 != last_value || value < 0) { |
| return false; |
| } |
| if (*c < '0' || *c > '9') { |
| return false; |
| } |
| unsigned int c_value = *c - '0'; |
| last_value = value; |
| value += c_value; |
| // Detect overflow. |
| if (value < last_value) { |
| return false; |
| } |
| // Forbid leading zeroes unless the string is just "0". |
| if (value == 0 && *(c+1) != '\0') { |
| return false; |
| } |
| } |
| *result = value; |
| return true; |
| } |
| |
| bool PDBSourceLineWriter::FindPEFile() { |
| CComPtr<IDiaSymbol> global; |
| if (FAILED(session_->get_globalScope(&global))) { |
| fprintf(stderr, "get_globalScope failed\n"); |
| return false; |
| } |
| |
| CComBSTR symbols_file; |
| if (SUCCEEDED(global->get_symbolsFileName(&symbols_file))) { |
| wstring file(symbols_file); |
| |
| // Look for an EXE or DLL file. |
| const wchar_t *extensions[] = { L"exe", L"dll" }; |
| for (size_t i = 0; i < sizeof(extensions) / sizeof(extensions[0]); i++) { |
| size_t dot_pos = file.find_last_of(L"."); |
| if (dot_pos != wstring::npos) { |
| file.replace(dot_pos + 1, wstring::npos, extensions[i]); |
| // Check if this file exists. |
| if (GetFileAttributesW(file.c_str()) != INVALID_FILE_ATTRIBUTES) { |
| code_file_ = file; |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| // static |
| bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function, |
| BSTR *name, |
| int *stack_param_size) { |
| *stack_param_size = -1; |
| const DWORD undecorate_options = UNDNAME_NO_MS_KEYWORDS | |
| UNDNAME_NO_FUNCTION_RETURNS | |
| UNDNAME_NO_ALLOCATION_MODEL | |
| UNDNAME_NO_ALLOCATION_LANGUAGE | |
| UNDNAME_NO_THISTYPE | |
| UNDNAME_NO_ACCESS_SPECIFIERS | |
| UNDNAME_NO_THROW_SIGNATURES | |
| UNDNAME_NO_MEMBER_TYPE | |
| UNDNAME_NO_RETURN_UDT_MODEL | |
| UNDNAME_NO_ECSU; |
| |
| // Use get_undecoratedNameEx to get readable C++ names with arguments. |
| if (function->get_undecoratedNameEx(undecorate_options, name) != S_OK) { |
| if (function->get_name(name) != S_OK) { |
| fprintf(stderr, "failed to get function name\n"); |
| return false; |
| } |
| |
| // It's possible for get_name to return an empty string, so |
| // special-case that. |
| if (wcscmp(*name, L"") == 0) { |
| SysFreeString(*name); |
| // dwarf_cu_to_module.cc uses "<name omitted>", so match that. |
| *name = SysAllocString(L"<name omitted>"); |
| return true; |
| } |
| |
| // If a name comes from get_name because no undecorated form existed, |
| // it's already formatted properly to be used as output. Don't do any |
| // additional processing. |
| // |
| // MSVC7's DIA seems to not undecorate names in as many cases as MSVC8's. |
| // This will result in calling get_name for some C++ symbols, so |
| // all of the parameter and return type information may not be included in |
| // the name string. |
| } else { |
| // C++ uses a bogus "void" argument for functions and methods that don't |
| // take any parameters. Take it out of the undecorated name because it's |
| // ugly and unnecessary. |
| const wchar_t *replace_string = L"(void)"; |
| const size_t replace_length = wcslen(replace_string); |
| const wchar_t *replacement_string = L"()"; |
| size_t length = wcslen(*name); |
| if (length >= replace_length) { |
| wchar_t *name_end = *name + length - replace_length; |
| if (wcscmp(name_end, replace_string) == 0) { |
| WindowsStringUtils::safe_wcscpy(name_end, replace_length, |
| replacement_string); |
| length = wcslen(*name); |
| } |
| } |
| |
| // Undecorate names used for stdcall and fastcall. These names prefix |
| // the identifier with '_' (stdcall) or '@' (fastcall) and suffix it |
| // with '@' followed by the number of bytes of parameters, in decimal. |
| // If such a name is found, take note of the size and undecorate it. |
| // Only do this for names that aren't C++, which is determined based on |
| // whether the undecorated name contains any ':' or '(' characters. |
| if (!wcschr(*name, ':') && !wcschr(*name, '(') && |
| (*name[0] == '_' || *name[0] == '@')) { |
| wchar_t *last_at = wcsrchr(*name + 1, '@'); |
| if (last_at && wcstol_positive_strict(last_at + 1, stack_param_size)) { |
| // If this function adheres to the fastcall convention, it accepts up |
| // to the first 8 bytes of parameters in registers (%ecx and %edx). |
| // We're only interested in the stack space used for parameters, so |
| // so subtract 8 and don't let the size go below 0. |
| if (*name[0] == '@') { |
| if (*stack_param_size > 8) { |
| *stack_param_size -= 8; |
| } else { |
| *stack_param_size = 0; |
| } |
| } |
| |
| // Undecorate the name by moving it one character to the left in its |
| // buffer, and terminating it where the last '@' had been. |
| WindowsStringUtils::safe_wcsncpy(*name, length, |
| *name + 1, last_at - *name - 1); |
| } else if (*name[0] == '_') { |
| // This symbol's name is encoded according to the cdecl rules. The |
| // name doesn't end in a '@' character followed by a decimal positive |
| // integer, so it's not a stdcall name. Strip off the leading |
| // underscore. |
| WindowsStringUtils::safe_wcsncpy(*name, length, *name + 1, length); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| // static |
| int PDBSourceLineWriter::GetFunctionStackParamSize(IDiaSymbol *function) { |
| // This implementation is highly x86-specific. |
| |
| // Gather the symbols corresponding to data. |
| CComPtr<IDiaEnumSymbols> data_children; |
| if (FAILED(function->findChildren(SymTagData, NULL, nsNone, |
| &data_children))) { |
| return 0; |
| } |
| |
| // lowest_base is the lowest %ebp-relative byte offset used for a parameter. |
| // highest_end is one greater than the highest offset (i.e. base + length). |
| // Stack parameters are assumed to be contiguous, because in reality, they |
| // are. |
| int lowest_base = INT_MAX; |
| int highest_end = INT_MIN; |
| |
| CComPtr<IDiaSymbol> child; |
| DWORD count; |
| while (SUCCEEDED(data_children->Next(1, &child, &count)) && count == 1) { |
| // If any operation fails at this point, just proceed to the next child. |
| // Use the next_child label instead of continue because child needs to |
| // be released before it's reused. Declare constructable/destructable |
| // types early to avoid gotos that cross initializations. |
| CComPtr<IDiaSymbol> child_type; |
| |
| // DataIsObjectPtr is only used for |this|. Because |this| can be passed |
| // as a stack parameter, look for it in addition to traditional |
| // parameters. |
| DWORD child_kind; |
| if (FAILED(child->get_dataKind(&child_kind)) || |
| (child_kind != DataIsParam && child_kind != DataIsObjectPtr)) { |
| goto next_child; |
| } |
| |
| // Only concentrate on register-relative parameters. Parameters may also |
| // be enregistered (passed directly in a register), but those don't |
| // consume any stack space, so they're not of interest. |
| DWORD child_location_type; |
| if (FAILED(child->get_locationType(&child_location_type)) || |
| child_location_type != LocIsRegRel) { |
| goto next_child; |
| } |
| |
| // Of register-relative parameters, the only ones that make any sense are |
| // %ebp- or %esp-relative. Note that MSVC's debugging information always |
| // gives parameters as %ebp-relative even when a function doesn't use a |
| // traditional frame pointer and stack parameters are accessed relative to |
| // %esp, so just look for %ebp-relative parameters. If you wanted to |
| // access parameters, you'd probably want to treat these %ebp-relative |
| // offsets as if they were relative to %esp before a function's prolog |
| // executed. |
| DWORD child_register; |
| if (FAILED(child->get_registerId(&child_register)) || |
| child_register != CV_REG_EBP) { |
| goto next_child; |
| } |
| |
| LONG child_register_offset; |
| if (FAILED(child->get_offset(&child_register_offset))) { |
| goto next_child; |
| } |
| |
| // IDiaSymbol::get_type can succeed but still pass back a NULL value. |
| if (FAILED(child->get_type(&child_type)) || !child_type) { |
| goto next_child; |
| } |
| |
| ULONGLONG child_length; |
| if (FAILED(child_type->get_length(&child_length))) { |
| goto next_child; |
| } |
| |
| // Extra scope to avoid goto jumping over variable initialization |
| { |
| int child_end = child_register_offset + static_cast<ULONG>(child_length); |
| if (child_register_offset < lowest_base) { |
| lowest_base = child_register_offset; |
| } |
| if (child_end > highest_end) { |
| highest_end = child_end; |
| } |
| } |
| |
| next_child: |
| child.Release(); |
| } |
| |
| int param_size = 0; |
| // Make sure lowest_base isn't less than 4, because [%esp+4] is the lowest |
| // possible address to find a stack parameter before executing a function's |
| // prolog (see above). Some optimizations cause parameter offsets to be |
| // lower than 4, but we're not concerned with those because we're only |
| // looking for parameters contained in addresses higher than where the |
| // return address is stored. |
| if (lowest_base < 4) { |
| lowest_base = 4; |
| } |
| if (highest_end > lowest_base) { |
| // All stack parameters are pushed as at least 4-byte quantities. If the |
| // last type was narrower than 4 bytes, promote it. This assumes that all |
| // parameters' offsets are 4-byte-aligned, which is always the case. Only |
| // worry about the last type, because we're not summing the type sizes, |
| // just looking at the lowest and highest offsets. |
| int remainder = highest_end % 4; |
| if (remainder) { |
| highest_end += 4 - remainder; |
| } |
| |
| param_size = highest_end - lowest_base; |
| } |
| |
| return param_size; |
| } |
| |
| bool PDBSourceLineWriter::WriteMap(FILE *map_file) { |
| output_ = map_file; |
| |
| // Load the OMAP information, and disable auto-translation of addresses in |
| // preference of doing it ourselves. |
| OmapData omap_data; |
| if (!GetOmapDataAndDisableTranslation(session_, &omap_data)) |
| return false; |
| BuildImageMap(omap_data, &image_map_); |
| |
| bool ret = PrintPDBInfo(); |
| // This is not a critical piece of the symbol file. |
| PrintPEInfo(); |
| ret = ret && |
| PrintSourceFiles() && |
| PrintFunctions() && |
| PrintFrameData(); |
| |
| output_ = NULL; |
| return ret; |
| } |
| |
| void PDBSourceLineWriter::Close() { |
| session_.Release(); |
| } |
| |
| bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) { |
| if (!info) { |
| return false; |
| } |
| |
| info->debug_file.clear(); |
| info->debug_identifier.clear(); |
| info->cpu.clear(); |
| |
| CComPtr<IDiaSymbol> global; |
| if (FAILED(session_->get_globalScope(&global))) { |
| return false; |
| } |
| |
| DWORD machine_type; |
| // get_machineType can return S_FALSE. |
| if (global->get_machineType(&machine_type) == S_OK) { |
| // The documentation claims that get_machineType returns a value from |
| // the CV_CPU_TYPE_e enumeration, but that's not the case. |
| // Instead, it returns one of the IMAGE_FILE_MACHINE values as |
| // defined here: |
| // http://msdn.microsoft.com/en-us/library/ms680313%28VS.85%29.aspx |
| switch (machine_type) { |
| case IMAGE_FILE_MACHINE_I386: |
| info->cpu = L"x86"; |
| break; |
| case IMAGE_FILE_MACHINE_AMD64: |
| info->cpu = L"x86_64"; |
| break; |
| default: |
| info->cpu = L"unknown"; |
| break; |
| } |
| } else { |
| // Unexpected, but handle gracefully. |
| info->cpu = L"unknown"; |
| } |
| |
| // DWORD* and int* are not compatible. This is clean and avoids a cast. |
| DWORD age; |
| if (FAILED(global->get_age(&age))) { |
| return false; |
| } |
| |
| bool uses_guid; |
| if (!UsesGUID(&uses_guid)) { |
| return false; |
| } |
| |
| if (uses_guid) { |
| GUID guid; |
| if (FAILED(global->get_guid(&guid))) { |
| return false; |
| } |
| |
| // Use the same format that the MS symbol server uses in filesystem |
| // hierarchies. |
| wchar_t age_string[9]; |
| swprintf(age_string, sizeof(age_string) / sizeof(age_string[0]), |
| L"%x", age); |
| |
| // remove when VC++7.1 is no longer supported |
| age_string[sizeof(age_string) / sizeof(age_string[0]) - 1] = L'\0'; |
| |
| info->debug_identifier = GUIDString::GUIDToSymbolServerWString(&guid); |
| info->debug_identifier.append(age_string); |
| } else { |
| DWORD signature; |
| if (FAILED(global->get_signature(&signature))) { |
| return false; |
| } |
| |
| // Use the same format that the MS symbol server uses in filesystem |
| // hierarchies. |
| wchar_t identifier_string[17]; |
| swprintf(identifier_string, |
| sizeof(identifier_string) / sizeof(identifier_string[0]), |
| L"%08X%x", signature, age); |
| |
| // remove when VC++7.1 is no longer supported |
| identifier_string[sizeof(identifier_string) / |
| sizeof(identifier_string[0]) - 1] = L'\0'; |
| |
| info->debug_identifier = identifier_string; |
| } |
| |
| CComBSTR debug_file_string; |
| if (FAILED(global->get_symbolsFileName(&debug_file_string))) { |
| return false; |
| } |
| info->debug_file = |
| WindowsStringUtils::GetBaseName(wstring(debug_file_string)); |
| |
| return true; |
| } |
| |
| bool PDBSourceLineWriter::GetPEInfo(PEModuleInfo *info) { |
| if (!info) { |
| return false; |
| } |
| |
| if (code_file_.empty() && !FindPEFile()) { |
| fprintf(stderr, "Couldn't locate EXE or DLL file.\n"); |
| return false; |
| } |
| |
| // Convert wchar to native charset because ImageLoad only takes |
| // a PSTR as input. |
| string code_file; |
| if (!WindowsStringUtils::safe_wcstombs(code_file_, &code_file)) { |
| return false; |
| } |
| |
| AutoImage img(ImageLoad((PSTR)code_file.c_str(), NULL)); |
| if (!img) { |
| fprintf(stderr, "Failed to open PE file: %s\n", code_file.c_str()); |
| return false; |
| } |
| |
| info->code_file = WindowsStringUtils::GetBaseName(code_file_); |
| |
| // The date and time that the file was created by the linker. |
| DWORD TimeDateStamp = img->FileHeader->FileHeader.TimeDateStamp; |
| // The size of the file in bytes, including all headers. |
| DWORD SizeOfImage = 0; |
| PIMAGE_OPTIONAL_HEADER64 opt = |
| &((PIMAGE_NT_HEADERS64)img->FileHeader)->OptionalHeader; |
| if (opt->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { |
| // 64-bit PE file. |
| SizeOfImage = opt->SizeOfImage; |
| } else { |
| // 32-bit PE file. |
| SizeOfImage = img->FileHeader->OptionalHeader.SizeOfImage; |
| } |
| wchar_t code_identifier[32]; |
| swprintf(code_identifier, |
| sizeof(code_identifier) / sizeof(code_identifier[0]), |
| L"%08X%X", TimeDateStamp, SizeOfImage); |
| info->code_identifier = code_identifier; |
| |
| return true; |
| } |
| |
| bool PDBSourceLineWriter::UsesGUID(bool *uses_guid) { |
| if (!uses_guid) |
| return false; |
| |
| CComPtr<IDiaSymbol> global; |
| if (FAILED(session_->get_globalScope(&global))) |
| return false; |
| |
| GUID guid; |
| if (FAILED(global->get_guid(&guid))) |
| return false; |
| |
| DWORD signature; |
| if (FAILED(global->get_signature(&signature))) |
| return false; |
| |
| // There are two possibilities for guid: either it's a real 128-bit GUID |
| // as identified in a code module by a new-style CodeView record, or it's |
| // a 32-bit signature (timestamp) as identified by an old-style record. |
| // See MDCVInfoPDB70 and MDCVInfoPDB20 in minidump_format.h. |
| // |
| // Because DIA doesn't provide a way to directly determine whether a module |
| // uses a GUID or a 32-bit signature, this code checks whether the first 32 |
| // bits of guid are the same as the signature, and if the rest of guid is |
| // zero. If so, then with a pretty high degree of certainty, there's an |
| // old-style CodeView record in use. This method will only falsely find an |
| // an old-style CodeView record if a real 128-bit GUID has its first 32 |
| // bits set the same as the module's signature (timestamp) and the rest of |
| // the GUID is set to 0. This is highly unlikely. |
| |
| GUID signature_guid = {signature}; // 0-initializes other members |
| *uses_guid = !IsEqualGUID(guid, signature_guid); |
| return true; |
| } |
| |
| } // namespace google_breakpad |