zoukankan      html  css  js  c++  java
  • 符号杂谈

    符号服务器允许Windows上的开发人员工具自动查找符号。他们做得很好,以至于大多数开发人员都不必担心内部机制。然而,当事情出了问题时,了解它们是如何工作的是有帮助的,事实证明,这一切都非常简单。

    我对Windows符号服务器的讨论利用了我笔记本电脑上的符号服务器,用于我自己的个人项目。每当我发布一个新版本的分形极限(64位优化,多核,快速和流动的分形探索,这里的演示版本)我把符号和二进制文件放在我的符号服务器上,这样我就可以轻松地调查我收到的任何崩溃报告。这对于一个家庭项目来说似乎有点过头了,但实际上本地符号服务器只是文件的副本,以特定的方式排列以便于检索,而且设置起来很简单。对于我公开发布的可执行文件,比如UIforETW,我将PE和PDB文件发布到Google存储上的一个公共符号服务器上——详细信息在这里。
     

    查找PE文件

    但不只是存储符号和服务器(EXELS)。如果这些还不可用,例如在查看小型转储或xperf配置文件时,通常会先检索它们,然后再检索符号。对于来自64位进程的崩溃转储,这些PE文件可能是执行堆栈遍历所必需的,因为它们包含必需的元数据。从符号服务器检索PE文件需要三条信息:文件名、链接时间戳和图像大小。为了手动检查分形.exe在我的符号服务器中,我会从可执行文件中提取链接时间戳和图像大小,如下所示:

    > dumpbin FractalX.exe /headers | find “date stamp”
    4FFD0109 time date stamp Tue Jul 10 21:28:57 2012
    > dumpbin FractalX.exe /headers | find “size of image”
    147000 size of image

    符号服务器共享中PE文件的路径格式为:

    “%s\%s\%08X%x\%s” % (serverName, peName, timeStamp, imageSize, peName)

    请注意,时间戳始终打印为8位数字(根据需要使用前导零),使用大写字母表示“A”到“F”(如果符号服务器区分大小写,则很重要),而图像大小将根据需要使用较少的数字以小写形式打印。


    旁白:如果两个构建之间的差异很小,那么大小可能不会改变,而peName肯定不会改变,所以唯一阻止两个构建具有相同符号服务器地址的就是时间戳。这意味着降低时间戳的精度(比如从1秒缩短到24小时)可能会导致一些相当严重的文件名冲突。最后可能会用第二个条目覆盖符号服务器中的第一个条目。你知道,假设一下。

    我的符号服务器在c:MySyms中(通常在共享服务器上,但这是我的个人笔记本电脑),因此上面检查的文件的完整路径是:

    c:MySymsfractalx.exe4FFD0109147000FractalX.exe

    很简单。在我的情况下,我使用符号库.exe当我添加文件时,'s/compress选项(它节省了很多空间)。压缩文件通过用下划线替换最后一个字符来表示,因此实际路径如下:

    c:MySymsfractalx.exe4FFD0109147000FractalX.ex_

    这是一个很好的测试,可以确保PE文件已正确添加到符号服务器,但这不是一个非常现实的用例,因为我们使用PE文件来获取检索PE文件所需的值。更常见的情况是,您将拥有一个小型转储或xperf ETL文件,该文件将包含一系列模块名称、链接时间戳、图像大小三元组,这些将在分析时用于检索PE文件。对于小型转储文件,有一个包含相关数据的minidump_模块结构数组。请注意,符号服务器共享的布局可能要复杂得多。您应该使用api(稍后讨论)来检索PE文件–上面的技术纯粹是为了排除故障。


    如果您有一个链接时间戳并希望将其转换为日期,则可以使用以下Python一行程序批处理文件:

    python -c "import time; print time.ctime(int('%1',16))"

    另外一个怪癖是,在微软的链接器/调试器工具链的某个地方,这些工具链用于小写PE文件的名称。这意味着,如果您为符号服务器使用区分大小写的文件系统(就像Chrome那样),那么您必须使用小写文件名来上载符号。Chrome团队很难发现这一点。PDB名称是从PE文件中提取的,大小写保持不变,因此它们只需匹配。我再也无法重现这种行为-UIforETW.exe以混合大小写方式上载到区分大小写的Google存储中,这样就可以了。

     

    查找PDB文件

    在分析客户崩溃转储以获得组装说明时,查找PE文件非常方便,但实际上比这更重要。小型转储文件不一定记录足够的信息来检索PDB文件。在这些情况下,工具检索PE文件,然后查找PE文件以获取检索PDB文件所需的信息。我们可以再次使用dumpbin从PE文件中提取此信息:

    > dumpbin FractalX.exe /headers | find “Format:”
    4FFD0109 cv           56 000B9308    B7B08    Format: RSDS, {6143E0D1-9975-4456-AC8E-F24C8777336D}, 1, FractalX.pdb

    RSDS后面的长十六进制数是一个GUID,后面的数字(一个32位十进制数,但在本例中只有“1”)称为“age”。PDB文件名也列在这里。它们一起唯一地标识了PDB文件的特定版本。符号服务器共享中PDB文件的路径格式为:

    “%s\%s\%s%x\%s” % (serverPath, pdbName, guid, age, pdbName)

    有趣的是,请注意,我使用%x来打印年龄,但在上一段中,我将年龄描述为十进制数。好吧,PDB age(只是衡量同一PDB被重用了多少次)只是一个32位数字,但是dumpbin以十进制格式打印它,而符号服务器希望它是十六进制的。祝大家一致!这意味着,如果解析dumpbin输出,则需要将age转换为整数并将其打印为十六进制。如果你弄错了,那么这个bug不会出现,直到你遇到一个10岁以上的PDB。精彩的。
    与PE文件一样,最后一个下划线表示文件被压缩的symstore.exe. 上面列出的PDB在符号服务器上的路径如下所示:

    c:MySymsFractalX.pdb6143E0D199754456AC8EF24C8777336D1FractalX.pd_

    很简单。生成GUID和age的算法是,每当您进行重建时(每当生成新的PDB时),就会创建一个新的GUID并将age设置为1。无论何时进行部分构建,PDB都会使用新的调试信息进行更新,并且age也会增加。就这样–使用PE名称、链接时间戳和图像大小来查找PE(如果尚未加载),然后使用GUID、age和PDB文件名来查找PDB文件。请注意,符号服务器共享的布局可能要复杂得多。您应该使用api(稍后讨论)来检索PDB文件–上面的技术纯粹是为了排除故障。

    添加到符号服务器


    如果你在Windows上发布软件,你应该有一个符号服务器。该符号服务器应该包含每种产品的PE文件和PDB文件。如果你不这样做,那么你对你自己或你的客户都是一种伤害。你还应该有一个符号服务器,用于公司里任何人都可能最终运行的所有内部构建。如果程序可能崩溃,并且您希望能够调查崩溃,那么将符号放在符号服务器上。如果您担心内部版本会占用太多空间,请将它们放在单独的符号服务器上,并偶尔清除旧文件。您还应该确保您的生成计算机正在运行源索引,这样当您在旧版本的软件中调试崩溃时,您将自动获得正确的源文件。幸运的是我已经写过了。向符号服务器添加文件是最简单的。将sourcedir设置为指向包含要添加的文件的目录,并将dest设置为符号服务器目录,所有需要符号的人都可以访问该目录。然后运行以下命令:

    symstore add /f %sourcedir%*.dll /s %dest% /t prodname /compress

    symstore add /f %sourcedir%*.exe /s %dest% /t prodname /compress

    symstore add /f %sourcedir%*.pdb /s %dest% /t prodname /compress

    如果您运行的是区分大小写的文件系统,那么之后您需要将PE文件名的大小写改为小写—抱歉。


    就这样。如果希望递归地添加文件,请使用/r,有关详细信息,请参阅帮助。如果现有的符号服务器未被压缩,或者希望在添加到符号服务器时单独执行压缩步骤,则makecab命令(随Windows一起提供)就可以实现此目的。这是Chrome过去使用的命令行类型:

    makecab /D CompressionType=LZX /D CompressionMemory=21 chrome.dll.pdb

    这将生成一个压缩的chrome.dll.pd_下载到本地符号缓存时将自动解压缩的文件。


    另一个选择是使用压缩.exe从Windows Server 2003资源工具包工具。但是要小心。compress/help说LZX是默认值,但不是这样。所以一定要使用compress-ZX,否则将得到SymSrv无法解压缩的压缩文件。


    显然是makecab,压缩.exe,和symstore都有其输入文件必须小于2 GiB的限制。这是Chrome统一版的一个问题chrome.dll.pdb目前(2019年10月)约为2.5GB,因此无法压缩。哎呀。pigz可以处理4gib的输入文件,但是生成CAB文件的版本还没有开源。我们最终修复了这个问题,通过前面的微软压缩,而不是使用HTTP压缩——我们在gsutil命令行中添加了-Z,这样pdb在上传时将被gzip压缩。

    如果您通过HTTP使符号服务器可用,请确保使用https,以确保下载的完整性。


    或者,如果您试图创建一个可公开访问的符号服务器,只需遵循以下简单的说明。

     

    使用符号服务器

    如何让开发工具使用symbol server的具体细节各不相同,但一种几乎通用的方法是将_NT_symbol_PATH环境变量(此处和此处的高级用法)设置为如下所示:

    _NT_SYMBOL_PATH=SRV*c:symbols*c:MySyms;SRV*c:symbols*https://msdl.microsoft.com/download/symbols;SRV*c:symbols*https://chromium-browser-symsrv.commondatastorage.googleapis.com

    这告诉工具首先在本地缓存(c:symbols)中查找,然后在符号服务器c:MySyms中查找。如果在c:MySyms中找到符号,则将它们复制(并解压缩)到c:symbols。如果这些都不起作用,那么微软基于web的符号缓存和Chrome的符号缓存将遵循相同的过程(包括相同的缓存目录)。请注意,在处理压缩符号时需要本地符号缓存。请注意,一些符号服务器,如Chrome和Microsofts,可以通过https和http访问。当https作为选项可用时,您应该始终使用它,否则中间人攻击可能会在下载和使用这些pdb时使用格式错误的pdb或源索引命令来执行任意代码。

    微软在某些地方仍然使用http列出了他们的符号服务器,但是https是有效的,应该是首选。

    _NT_SYMBOL_PATH的SRV*部分很重要,文档记录不清楚,而且很难理解。我的理解是,通过对stackoverflow的讨论,SRV*告诉我们symsrv.dll将以下路径或url视为符号服务器,而不仅仅是松散文件的集合。因此,如果_NT_SYMBOL_PATH是c:symbols,那么dbghelp或symsrv可能会递归地搜索目录结构中的符号,但是如果_NT_SYMBOL_PATH是SRV*c:symbols,那么它将以非常结构化和高效的方式进行搜索。如果符号路径是


    SRV*c:symbols*https://msdl.microsoft.com/download/symbols


    然后symsrv将首先使用快速高效的符号服务器算法在c:symbols中查找,然后(如果没有找到符号)在微软的symbol server中进行同样有效的搜索。您应该更喜欢使用SRV*和符号服务器布局,而不是非结构化符号。

    以编程方式检索符号

    通常,您使用的调试器和探查器将知道如何使用符号服务器,但有时您可能需要编写代码来下载符号—可能您正在编写调试器或探查器。在我的例子中,我有一个网页,列出了几十个微软dll的guid、age和PDB名称,这些dll来自于我们需要符号的几十个Windows版本。编写代码来下载所有这些符号是很简单的—比为其他版本的Linux获取符号要容易几个数量级。我需要做的简短解释是“调用symfindfeinpath”。为了证明这是多么容易,我决定给出一个稍微长一点的解释。示例代码作为UIforETW的一部分在github上提供,它接受GUID、age和pdb名称,或者日期戳、大小和pename,并从符号服务器下载PE或pdb文件。最大的代码块是用来解析GUID的——实际下载很简单。


    测试定义使用已知良好的GUID、age、name和symbol server。注释掉定义使用此命令从指定的符号服务器下载任意符号。如果遇到任何困难,请在调试器中运行此程序–dbghelp将在调试器输出窗口中打印诊断结果。唯一的问题是dbghelp.dll以及symsrv.dll必须和你的工具在同一个目录中-把它们放在你的路径中是不可靠的。


    在前面提到的最新版本的源代码可以在这里找到。symchk工具(在Windows调试器工具包中提供)在传递PE文件时也会下载PDB文件–使用/v选项获取有关PDB文件下载位置的信息以及其他信息。如果有需要符号的.dmp文件或PE文件,symchk更方便;而如果有GUID、age和PDB文件名,则RetrieveSymbols更方便。

    用windbg诊断符号问题

    如果您有一个小型转储,但它的符号没有加载,那么我建议将该小型转储加载到windbg并使用其诊断:


    !sym noise–打印有关尝试获取符号的详细信息

    lmv m MyModule–从崩溃转储的模块列表中打印一条记录,包括其名称、时间戳、图像大小以及PDB所在的位置(如果找到)

    !lmi MyModule–打印模块的头信息–仅当PE文件已加载时才有效,这是加载符号的先决条件


    dumpbin摘要



    “%VS120COMNTOOLS%....VCvcvarsall.bat”–这会将dumpbin的目录添加到路径中

    dumpbin FX.exe /headers | find “date stamp”–查找PE文件的链接时间戳

    dumpbin FX.exe /headers | find “size of image”–查找PE文件的图像大小

    dumpbin FractalX.exe /headers | find “Format:”–查找PE文件的PDB文件的GUID、age和文件名

  • 相关阅读:
    201871010132-张潇潇-作业四 软件研发团队组建
    201871010132-张潇潇 实验三 结对项目—《D{0-1}KP 实例数据集算法实验平台》项目报告
    201871010132-张潇潇 实验二 个人项目—《西北师范大学学生疫情上报系统》项目报告
    201871010132-张潇潇 实验一软件工程准备-软件工程初识
    张潇潇--学期获奖总结
    201871010132--张潇潇--《面向对象程序设计(java)》课程总结
    201871010132--张潇潇--《面向对象程序设计(java)》第十七周学习总结
    201871010132--张潇潇--《面向对象程序设计(java)》第十六周学习总结
    201871010132--张潇潇--《面向对象程序设计(java)》第十五周学习总结
    201871010132--张潇潇--《面向对象程序设计(java)》第十四周学习总结
  • 原文地址:https://www.cnblogs.com/yilang/p/13359871.html
Copyright © 2011-2022 走看看