pdb_source_line_writer.cc
来自「SumatraPDF是一款小型开源的pdf阅读工具。虽然玲珑小巧(只有800多K」· CC 代码 · 共 801 行 · 第 1/2 页
CC
801 行
} CComBSTR name; int stack_param_size; if (!GetSymbolFunctionName(symbol, &name, &stack_param_size)) { return false; } fprintf(output_, "PUBLIC %x %x %ws\n", rva, stack_param_size > 0 ? stack_param_size : 0, name); 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;}// 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;}// staticbool 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; } // 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;}// staticint 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; } 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; bool ret = PrintPDBInfo() && 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; } // All CPUs in CV_CPU_TYPE_e (cvconst.h) below 0x10 are x86. There's no // single specific constant to use. DWORD platform; if (SUCCEEDED(global->get_platform(&platform)) && platform < 0x10) { info->cpu = L"x86"; } 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::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
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?