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 + -
显示快捷键?