如何将源代码嵌入到pdb中,并让调试器使用它?

Jam*_*ing 15 c# debugging source-server pdb-files

注意:我的目标关注点是使用常规MSIL定位CLR的C#,以防有些内容适用于此但不是更常见的情况.

一些现有的源代码调试支持示例

最近发布了Sourcepack项目,该项目允许用户重写pdb文件中的源路径以指向不同的位置.当您拥有程序集的源代码时,这非常有用,但不希望尝试将其放入与构建时完全相同的文件系统位置.

http://lowleveldesign.wordpress.com/2011/08/26/sourcepack-released/

对于开源项目,使用http://www.symbolsource.org/作为简化项目用户获取符号和来源的方法是一个很好的主意.

问题

但是,由于法律或方便的原因,使用这种方法的项目通常不太可行.此外,可能正在调试项目的人员组可能相对较小或包含在内.

默认情况下,项目的pdb包含指向磁盘上文件的指针(IIRC),然后源索引可以添加嵌入源位置指针的功能(例如,在版本控制系统中),然后使用源服务器指向实际获取源的指针.

目标

看起来事情可能更简单(对于某些构建,例如调试和/或仅内部),只需将实际源放入pdb(实际上只是取消引用当前在PDB中写入的指针).看起来你可以跳过整个源服务器部分(至少在理论上)并消除对调试时故事的一些依赖.是否将源存储为压缩源在很大程度上是正交的,但是为了使现有调试器的实现更简单,第一遍可能不会这样做.

由于PDB匹配二进制故事已经非常好,因此将源放入PDB甚至比源服务器指针更好,因为指针可能会随着时间的推移而中断(源控制系统移动或更改为不同的系统,或者无论如何,但坐在PDB中的实际来源是"永远".

这与"源服务器"的支持有何不同?

(这是在Tigran评论之后通过编辑添加的,询问有什么好处)

应该与之进行比较的"基线"场景是使用当今"普通"源服务器实例的"正常"调试体验.在那种情况下,(AFAIK)调试引擎从PDB获取指针(通过备用流),然后使用注册的源服务器尝试通过该指针获取源.由于给定的程序集通常包含多个源文件,因此有一个指针包含一个基本位置,或者PDB中有多个指针(或其他内容),但这应与此讨论正交.

对于保持源隐藏/不可访问的项目(大多数Microsoft产品,例如,包括Windows,Office,Visual Studio等),然后让PDB包含指针,FAR优于包括实际源(即使它是加密).没有必要的网络访问和权限,这样的指针毫无意义,所以这种方法意味着您可以将PDB发送给地球上的任何人,而不必担心他们能够访问您的来源(最糟糕的情况是,他们可以看到您的来源如何树的安排,我想).

但是,有两大组项目(特别是构建),其中"隐藏源"的好处不存在.

第一个是仅供有权访问源的人使用的构建.在你自己的机器上完成不会离开那台机器的构建就是一个很好的例子,因为攻击者无论如何都需要从你的文件系统中读取文件以获取源代码,因此从一个文件(.cs)读取另一个文件(. pdb)在攻击难度/向量方面是一个相对较小的差异.类似地,完成并推送到测试/暂存环境的构建,其中访问机器上的pdb的人员等于或者可以正常访问源的人员的子集.

第二个(有点明显)是开源项目,项目的来源无论如何都已经开放给所有人,因此隐藏任何人的来源没有任何好处.

请注意,这可以相对容易地扩展,以包括加密形式的源(因为我们已经在讨论必须存储格式/编码数据),但增加的复杂性将使这种情况可能不太有用而不仅仅是使用'普通'源服务器.

好处?

通过上面的描述,允许这一点的潜在好处列表包括(但不限于:)这些当下突然出现在我的脑海中:

  • 无需处理设置源服务器支持.它只是工作(IJW),至少在调试器知道如何查看pdb时.
    • 与此同时,你仍然可以做一个"固定"的源服务器,它只是一个虚拟的,它提取源并将其反馈给调用者.这样的配置对于每个人来说都是相同的(例如,使用localhost),仍然消除了实际配置源服务器的当前需求
  • 构建不需要包含'源索引'
    • 由于构建读取源文件并且无论如何都要写入pdb文件,我们只是修改pdb中写入的内容,而不是为了进行网络调用或读取内存中没有的数据而进行任何构建时间性操作.
    • 在'native'构建支持源代码之前,它可能是一个简单的构建后步骤,可能首先通过Sourcepack项目的一个小分支实现,因为它已经完成了读取/修改PDB文件的工作:)
  • 不依赖于具有源控制系统的团队/项目
  • 不依赖于检查到源控制系统的每个文件的特定版本(大多数人不会检查他们在IDE中执行的每个构建)
  • 无需访问具有该文件的特定源控制系统
    • 例如,在DVCS情况下,PDB指针可能是git或mercurial或其他任何"随机"实例,不一定是你有权访问的
    • 源服务器工具跟踪该版本回到您有权访问的源控制服务器实例(如果它甚至存在)尚不存在AFAIK)
  • 如果项目死亡(被删除)或移动,没问题
    • 例如,如果项目从一个移动到另一个:自托管,sourceforge,github,bitbucket,codeplex,code.google.com等.
  • 如果您正在调试的计算机没有(或不充分)网络访问,则没问题
    • 例如,如果您正在将"网络KVM"放入一个用于调试问题的框中,但它没有网络,或者它只能与断开连接的网络通信,以致无法访问您的源控制服务器.
  • 在极端情况下,能够从构建中恢复一些项目源.;)

注意:另一种方法是在实际程序集中包含源代码(例如,作为资源),但pdb是一个更好的选择(很容易发布没有pdb的构建,如果源代码在pdb中,则没有正常的运行时性命中因为程序集是相同的代码和相同的大小等)

如何实施?

从表面上看,这种支持看起来似乎不太难添加,但我觉得这是因为我对所涉及的机制并不是很了解,而不是实际上是一件简单的事情.实施.:)

我的猜测将是:

  1. 添加一个类似于Sourcepack的构建后步骤,但不是更改指针,而是用实际源代替它.
    • 根据源服务器需要执行的操作,可能需要获取前缀,或者实际源将位于不同的备用数据流中,并且"指针"会更新为'source-in-pdb:ads-foo.cs ' 管他呢.前缀或指针可以包括源文件的存储方式(未压缩,gzip,bzip2等,以及文件的编码)
  2. 实现一个"源服务器",它实际上从有问题的pdb中提取源并将其返回.
    • 不知道源服务器'API'是否有足够的信息来获取PDB的位置,更不用说它是否有权实际读取内容.

完整性检查?

随着上面的喋喋不休,问题实际上是:

  • 这种事情已经存在吗?(如果有,请提供指示!)
  • 假设它还不存在,上述内容是否有意义作为首次实现?以上是否存在陷阱或复杂性?
  • 假设上述情况为"否"和"是",是否有一个现有的项目在接受它(它已接近或在其现有范围内)方面有意义?

Jar*_*Par 4

我读过这篇文章并想总结一下我的理解以便清楚起见

如今,调试器使用 PDB 来获取文件的磁盘路径以及编译后创建可执行文件的给定部分的校验和。然后,调试器尝试使用本地磁盘和可用符号服务器加载文件。根据此提议,我们只需将文件本身嵌入到 PDB 中即可跳过中间人。尤里卡,不再寻找来源!

作为一个以这种方式挖掘源代码的人,我喜欢用一个包来满足所有调试需求的想法。不过,这个提议有几个方面需要考虑。

第一个是将源代码实际嵌入到 PDB 中。这是非常可行的。PDB本质上是一个轻量级文件数据库。它编码的内容有结构,但据我所知,您可以将任何您想要的内容放入某些插槽中(例如局部变量值/类型)。某些插槽可能有大小限制,但我确信您可以发明一种编码方案来将大文件分成块。

第二个方面是让调试器实际从 PDB 加载文件,而不是在磁盘上搜索文件。我对调试器的那部分不太熟悉,但据我了解,它只使用两条信息来定位文件

  1. 文件在磁盘上的路径
  2. 所述文件的校验和(用于消除同名文件的歧义)

我相当确定这是它传递到符号服务器的唯一信息。这使得实现符号服务器变得不可行,因为它无法访问 PDB(当然假设我是对的)。

我四处寻找,希望有一个可以重写的 VS COM 组件,它允许您拦截给定路径的文件加载,但我找不到。

我认为可行的一种方法是

  1. 将源代码嵌入到 PDB 中
  2. 拥有一个工具,既可以将源代码提取到已知位置,又可以重写 PDB 以指向该位置。

但这并不完全是你想要的。