用于异构调试的DWARF扩展

警告

本文档描述了 DWARF 版本 5 [DWARF] 的临时扩展,以支持异构调试。它目前尚未完全实现,可能会发生更改。

1. 简介

AMD [AMD] 一直致力于支持异构计算。异构计算程序可以使用高级语言(如 C++ 或 Fortran)以及 OpenMP pragma、OpenCL 或 HIP(一种用于异构计算的可移植 C++ 编程环境 [HIP])编写。异构编译器和运行时允许程序在同一原生进程中的多个设备上执行。设备可能包括 CPU、GPU、DSP、FPGA 或其他专用加速器。目前,HIP 程序在具有 CPU 和 GPU 的系统上执行。

AMD [AMD] ROCm 平台 [AMD-ROCm] 是由异构系统架构 (HSA) 基金会 [HSA] 定义的异构计算设备行业标准的实现。它是开源的,包括对 LLVM [LLVM](用于编译)和 GDB(用于调试 [GDB])等开源项目的贡献。

LLVM 编译器具有对商用 AMD GPU 硬件 (AMDGPU) [AMDGPU-LLVM] 的上游支持。开源 ROCgdb [AMD-ROCgdb] 基于 GDB 的调试器也具有对 AMDGPU 的支持,并且正在向上游推送。第三方也在为 GCC [GCC] 编译器和 Perforce TotalView HPC 调试器 [Perforce-TotalView] 添加 AMDGPU 支持。

为了支持调试异构程序,已经确定了当前 DWARF 版本 5 [DWARF] 未提供的几个功能。2. 扩展 节概述了为解决缺失功能而设计的扩展。这些扩展力求具有通用性,并向后兼容 DWARF 版本 5。 它们的目标是适用于满足任何异构系统的需求,而不是特定于供应商或架构。紧随其后的是附录 A. 相对于 DWARF 版本 5 的更改,其中包含相对于 DWARF 版本 5 标准的扩展的文本更改。其中包含许多注释,提出了悬而未决的问题,或提供了可能值得考虑的替代方法。然后,附录 C. 更多示例 链接到 AMD GPU 特定扩展用法,其中包括一个示例。最后,附录 D. 参考文献 提供了更多信息的参考。

2. 扩展

这些扩展通过与许多个人的协作以及在 GDB 调试器和 LLVM 编译器中的积极原型设计而不断发展。来自 Perforce TotalView HPC 调试器和 GCC 编译器的开发人员的意见也得到了高度赞赏。

到目前为止提供的输入和获得的见解已纳入当前版本。计划是参与上游工作并解决任何反馈。如果大家普遍感兴趣,那么这些扩展中的部分或全部可以作为未来的 DWARF 标准提案提交。

设计扩展的一般原则是

  1. 向后兼容 DWARF 版本 5 [DWARF] 标准。

  2. 保持供应商和架构中立。它们旨在适用于其他异构硬件设备,包括 GPU、DSP、FPGA 和其他专用硬件。这些设备共同包含与 AMDGPU 设备类似的特性和要求。

  3. 为非 GPU 代码提供改进的优化支持。例如,一些扩展适用于支持大型向量寄存器的传统 CPU 硬件。编译器可以将描述大规模并行执行的源语言和源语言扩展映射到向量寄存器的通道上。这在 ML 和 HPC 中使用的编程语言中很常见。

  4. 基于 DWARF 版本 5 规范,以一致的风格完全定义格式良好的 DWARF。

一些推广也可能有利于已提出的其他 DWARF 问题。

本节的其余部分列举了扩展,并针对异构调试提供了每个扩展的动机。

2.1 允许在 DWARF 表达式栈上使用位置描述

DWARF 版本 5 不允许位置描述作为 DWARF 表达式栈上的条目。它们只能是 DWARF 表达式求值的最终结果。但是,通过允许位置描述成为 DWARF 表达式栈上的第一类条目,可以自然地组合包含值和位置描述的表达式。它允许对象位于任何类型的内存地址空间、寄存器中,成为隐式值、未定义值或这些的组合。

通过仔细扩展 DWARF,所有现有的 DWARF 表达式都可以保留其当前的语义含义。DWARF 具有隐式转换,可以将表示默认地址空间中地址的值转换为内存位置描述。这可以扩展为允许将默认地址空间内存位置描述隐式转换回其地址值。这允许所有 DWARF 版本 5 表达式保留其相同的含义,同时能够显式创建非默认地址空间中的内存位置描述,并推广复合位置描述的能力以支持任何类型的位置描述。

对于那些熟悉 DWARF 版本 5 中位置描述定义的人来说,这些扩展中的定义呈现方式不同,但实际上定义了相同的概念和相同的基本语义。但是,它的实现方式允许该概念扩展以支持地址空间、位寻址、复合位置描述由任何类型的位置描述组成的能力,以及支持位于多个位置的对象的能力。这些更改共同扩展了可以支持的架构集,并改进了对优化代码的支持。

考虑了几种方法,所提出的方法及其启用的扩展,似乎是提供 DWARF 支持调试优化 GPU 和非 GPU 代码能力的最大改进的最简单和最干净的方法。检查 GDB 调试器和 LLVM 编译器,似乎只需要进行适度的更改,因为它们都已经必须支持位置描述的通用使用。预计其他调试器和编译器也会如此。

GDB 已被修改为评估将位置描述作为栈条目和隐式转换的 DWARF 版本 5 表达式。所有 GDB 测试都已通过,只有一个测试被证明是违反 DWARF 版本 5 规则的无效测试用例。GDB 中的代码实际上变得更简单了,因为所有评估都在单个栈上完成,不再需要为位置描述结果维护单独的结构。这为向后兼容性提供了信心。

请参阅 A.2.5 DWARF 表达式 和嵌套部分。

此扩展在允许在 DWARF 表达式栈上使用位置描述 [AMDGPU-DWARF-LOC] 中单独描述。

2.2 将 CFI 推广到允许任何位置描述类型

CFI 描述了恢复被溢出的被调用者保存的寄存器。目前,CFI 仅允许作为寄存器、内存地址或隐式位置描述的位置描述。AMDGPU 优化代码可能会将标量寄存器溢出到向量寄存器的某些部分。这需要扩展 CFI 以允许支持任何位置描述类型。

请参阅 A.6.4 调用帧信息

2.3 推广 DWARF 操作表达式以支持多个位置

在 DWARF 版本 5 中,位置描述被定义为单个位置描述或位置列表。位置列表被定义为有效的未定义位置描述,或者定义为一个或多个单个位置描述,以描述具有多个位置的对象。

使用 2.1 允许在 DWARF 表达式栈上使用位置描述DW_OP_push_object_addressDW_OP_call* 操作可以将位置描述放到栈上。此外,调试信息条目属性(如 DW_AT_data_member_locationDW_AT_use_locationDW_AT_vtable_elem_location)被定义为在评估表达式之前将位置描述推送到表达式栈上。

DWARF 版本 5 仅允许栈包含值,因此栈上只能有一个内存地址。这使得这些操作和属性无法处理具有多个位置或内存以外位置的位置描述。

由于 2.1 允许在 DWARF 表达式栈上使用位置描述 允许栈包含位置描述,因此操作被推广以支持可以具有多个位置的位置描述。这向后兼容 DWARF 版本 5,并允许支持具有多个位置的对象。例如,描述如何访问对象字段的表达式可以使用具有多个位置的位置描述进行评估,并将产生具有多个位置的位置描述。

通过此更改,描述 DWARF 表达式和位置列表的单独 DWARF 版本 5 节被统一为一个描述通用 DWARF 表达式的节。这种统一是允许位置描述成为评估栈一部分的自然结果和必然结果。

请参阅 A.2.5.3 DWARF 位置描述

2.4 推广位置描述的偏移

DW_OP_plusDW_OP_minus 操作可以定义为在默认目标架构特定地址空间中的内存位置描述和通用类型值上操作,以生成更新的内存位置描述。这允许它们继续用于偏移地址。

为了将偏移推广到任何位置描述,包括描述字节在寄存器中、隐式或这些组合的位置描述,添加了 DW_OP_LLVM_offsetDW_OP_LLVM_offset_uconstDW_OP_LLVM_bit_offset 偏移操作。

偏移操作可以对任何大小的位置存储进行操作。例如,隐式位置存储可以是任意位大小。将超过位置存储大小的偏移定义为评估错误比迫使实现支持潜在的无限精度偏移以使其能够正确跟踪一系列可能瞬时溢出或下溢但最终在范围内的正负偏移更简单。这对于算术运算来说很简单,因为它们是根据固定大小的基本类型上的二进制补码算术定义的。因此,偏移操作定义整数溢出是不良形式的。这与 DW_OP_plusDW_OP_plus_uconstDW_OP_minus 算术运算形成对比,后者定义为导致回绕。

具有偏移操作允许 DW_OP_push_object_address 推送可能在寄存器中或为隐式值的位置描述。DW_TAG_ptr_to_member_type 的 DWARF 表达式可以使用偏移操作,而无需考虑推送的位置描述类型。

由于 2.1 允许在 DWARF 表达式栈上使用位置描述 已将位置存储推广为可按位索引,因此 DW_OP_LLVM_bit_offset 推广了 DWARF 以支持位域。这在 DWARF 版本 5 中通常是不可能的。

DW_OP_*piece 操作仅允许字面值索引。需要一种使用任意位置描述(如向量寄存器)的计算偏移量的方法。偏移操作提供了这种能力,因为它们可以用于计算栈上的位置描述。

可以定义 DW_OP_plusDW_OP_plus_uconstDW_OP_minus 以对位置描述进行操作,从而避免需要 DW_OP_LLVM_offsetDW_OP_LLVM_offset_uconst。但是,由于当前算术运算被定义为需要相同基本类型的值并产生具有相同基本类型的结果,因此不建议这样做。允许这些操作对位置描述起作用将允许第一个操作数是位置描述,第二个操作数是整数值类型,反之亦然,并返回位置描述。这使得默认地址空间内存位置描述和通用基本类型值之间的隐式转换规则复杂化。当前,这些规则会将此类位置描述转换为内存地址值,然后执行二进制补码回绕算术。如果结果用作位置描述,它将被隐式转换回默认地址空间内存位置描述。这与位置描述的溢出规则不同。为了允许控制,将需要一种将内存位置描述转换为地址整数类型值的操作。保持位置描述操作和算术操作的分离避免了这种语义复杂性。

请参阅 A.2.5.4.4.1 通用位置描述操作 中的 DW_OP_LLVM_offsetDW_OP_LLVM_offset_uconstDW_OP_LLVM_bit_offset

2.5 推广未定义位置描述的创建

当前 DWARF 使用空表达式来指示未定义的位置描述。由于 2.1 允许在 DWARF 表达式栈上使用位置描述 允许在栈上创建位置描述,因此必须有一种显式方法来指定未定义的位置描述。

例如,DW_OP_LLVM_select_bit_piece(请参阅 2.13 支持 SIMT 硬件发散控制流)操作在栈上接受多个位置描述。如果没有此功能,则无法指定输入的某个位置描述是未定义的。

请参阅 A.2.5.4.4.2 未定义位置描述操作 中的 DW_OP_LLVM_undefined 操作。

2.6 推广复合位置描述的创建

为了允许复合位置描述的组合,需要一个显式操作来指示复合位置描述定义的结束。如果到达 DWARF 表达式的末尾,则可以暗示这一点,从而允许当前的 DWARF 表达式保持合法。

请参阅 A.2.5.4.4.6 复合位置描述操作 中的 DW_OP_LLVM_piece_end

2.7 推广 DWARF 基础对象以允许任何位置描述类型

对于 AMDGPU,寄存器的数量和内存操作的成本远高于典型的 CPU。编译器尝试将整个变量和数组优化到寄存器中。

当前 DWARF 仅允许 DW_OP_push_object_address 和相关操作与全局内存位置一起使用。为了支持 AMDGPU 优化代码,需要推广 DWARF 以允许使用任何位置描述。这允许寄存器或可能是内存、寄存器甚至隐式值的混合的复合位置描述。

请参阅 A.2.5.4.4.1 通用位置描述操作 中的 DW_OP_push_object_address

2.8 地址空间的一般支持

AMDGPU 需要能够描述不同类型内存中的地址。优化代码可能需要描述驻留在不同类型存储中的片段中的变量,这些存储可能包括寄存器的部分、混合内存类型的内存、隐式值或未定义值。

DWARF 具有段地址的概念。但是,无法在 DWARF 表达式中指定段,DWARF 表达式只能指定段地址的偏移部分。段索引仅由指定 DWARF 表达式的实体提供。因此,段索引是只能放在完整对象(例如变量)上的属性。这使其仅适用于描述位于单种内存中的实体(例如变量或子程序代码)。

AMDGPU 使用多个地址空间。例如,变量可以分配在寄存器中,该寄存器部分溢出到私有地址空间中的调用栈,并部分溢出到本地地址空间。DWARF 提到了地址空间,例如作为 DW_OP_xderef* 操作的参数。添加了一个定义地址空间的新节(请参阅 A.2.13 地址空间)。

为指针和引用类型添加了一个新属性 DW_AT_LLVM_address_space(请参阅 A.5.3 类型修饰符条目)。这允许编译器指定用于表示指针或引用类型的地址空间。

DWARF 在许多表达式操作中使用了地址的概念,但没有定义它与地址空间的关系。例如,DW_OP_push_object_address 推送对象的地址。其他上下文在评估表达式之前隐式地将地址推送到栈上。例如,DW_TAG_ptr_to_member_typeDW_AT_use_location 属性。表达式属于源语言类型,该类型可能适用于分配在不同类型存储中的对象。因此,希望使用地址的表达式可以这样做,而无需考虑它指定哪种类型的存储,包括内存位置描述的地址空间。例如,指向成员的值可能希望应用于可能驻留在任何地址空间中的对象。

DWARF DW_OP_xderef* 操作允许将值转换为指定地址空间的地址,然后读取该地址。但是,它没有提供为非默认地址空间中的地址创建内存位置描述的方法。例如,AMDGPU 变量可以以固定地址分配在本地地址空间中。

DW_OP_LLVM_form_aspace_address(请参阅 A.2.5.4.4.3 内存位置描述操作)操作被定义为从地址和地址空间创建内存位置描述。它可用于指定分配在特定地址空间中的变量的位置。这允许地址空间中地址的大小大于通用类型。它还允许消费者具有很大的实现自由度。它允许将隐式转换回值的操作仅限于默认地址空间,以保持与 DWARF 版本 5 的兼容性。对于其他地址空间,生产者可以使用显式指定地址空间的新操作。

相比之下,如果 DW_OP_LLVM_form_aspace_address 操作被定义为产生值,并且定义了到内存位置描述的隐式转换,那么它将被限制为通用类型的大小(这与默认地址空间的大小匹配)。实现可能必须使用值的保留范围来表示不同的地址空间。这样的值可能与实际硬件中的任何地址值都不匹配。这将要求消费者对此类值进行特殊处理。

DW_OP_breg* 将寄存器视为包含默认地址空间中的地址。添加了 DW_OP_LLVM_aspace_bregx(请参阅 A.2.5.4.4.3 内存位置描述操作)操作,以允许指定寄存器中保存的地址的地址空间。

类似地,DW_OP_implicit_pointer 将其隐式指针值视为在默认地址空间中。DW_OP_LLVM_aspace_implicit_pointer (A.2.5.4.4.5 隐式位置描述操作) 操作被添加以允许指定地址空间。

DWARF 中几乎所有地址的使用都限于定义位置描述,或取消引用以读取内存。例外是 DW_CFA_val_offset,它使用地址来设置寄存器的值。为了支持地址空间,CFA DWARF 表达式被定义为内存位置描述。这允许它指定用于将偏移地址转换回该地址空间中地址的地址空间。请参阅 A.6.4 调用帧信息

这种扩展内存位置描述以支持地址空间的方法,允许所有现有的 DWARF 版本 5 表达式具有相同的语义。它允许编译器显式指定它正在使用的地址空间。例如,当以 SIMT 方式将源语言线程映射到波阵面的通道时,编译器可以选择以混洗方式访问私有内存。或者,如果以波阵面作为线程映射相同的语言,编译器可以选择以非混洗方式访问它。

它还允许编译器混合用于访问私有内存的地址空间。例如,对于 SIMT,它仍然可以以非混洗方式溢出整个向量寄存器,同时使用混洗的私有内存进行 SIMT 变量访问。

此方法也允许使用常规的 DW_OP_*piece 操作来组合不同地址空间的内存位置描述。

位置描述是存储的抽象概念。它们让消费者可以自由地决定如何实现它们。它们允许地址空间编码通道信息,以便仅使用内存位置描述而无需额外信息即可读取内存。同一组操作可以独立于其存储类型对位置进行操作。DW_OP_deref* 因此可以用于任何存储类型,包括不同地址空间的内存位置描述。因此,DW_OP_xderef* 操作是不必要的,除非是为了更紧凑地编码非默认地址空间地址,然后对其进行解引用。请参阅 A.2.5.4.3.4 特殊值操作

2.9 向量基本类型支持

AMDGPU 的向量寄存器以其完整的 wavefront 大小表示,这意味着 wavefront 大小乘以 dword 大小。这反映了实际硬件,并允许编译器为将线程映射到完整 wavefront 的语言生成 DWARF。它还允许生成更高效的 DWARF 来描述 CFI,因为整个向量寄存器只需要一个表达式,而不是向量寄存器中每个通道的 dword 都需要单独的表达式。它还允许编译器生成 DWARF,如果它将标量寄存器溢出到向量寄存器的部分中,则可以索引向量寄存器。

由于 DWARF 堆栈值条目具有基本类型,而 AMDGPU 寄存器是 dword 的向量,因此需要能够指定基本类型是向量。

请参阅 DW_AT_LLVM_vector_size,位于 A.5.1 基本类型条目

2.10 用于创建向量复合位置描述的 DWARF 操作

AMDGPU 优化的代码可能会将向量寄存器溢出到非全局地址空间内存中,并且这种溢出可能仅针对在子程序入口处处于活动状态的 SIMT 通道执行。为了支持这一点,部分溢出寄存器的 CFI 规则需要使用一个表达式,该表达式使用 EXEC 寄存器作为位掩码,以在寄存器(对于非活动通道)和堆栈溢出位置(对于溢出的活动通道)之间进行选择。这需要评估为一个位置描述,而不是一个值,因为调试器需要在用户分配给变量时更改该值。

另一种用法是创建一个表达式,该表达式评估为 SIMT 执行模型中活动和非活动通道提供逻辑 PC 向量。同样,EXEC 寄存器用于在活动和非活动 PC 值之间进行选择。为了表示 PC 值向量,需要一种创建复合位置描述的方法,该复合位置描述是单个位置的向量。

可能可以使用现有的 DWARF 逐步构建复合位置描述,可能使用 DWARF 操作进行控制流以创建循环。但是,对于 AMDGPU,这将需要 64 次循环迭代。一个担忧是,生成的 DWARF 将具有很大的大小,并且会非常常见,因为每个函数中溢出的每个向量寄存器都需要它。AMDGPU 最多可以有 512 个向量寄存器。另一个担忧是重复评估如此复杂的表达式所需的时间。

为了避免这些问题,提出了一种可以创建为掩码选择的复合位置描述。此外,还需要一种操作,该操作创建的复合位置描述是另一个位置描述上的向量。这些操作使用单个 DWARF 操作生成复合位置描述,该操作在一个步骤中组合向量的所有通道。DWARF 表达式更紧凑,并且可以被消费者更有效地评估。

使用这些操作的示例在 C. 更多示例 附录中引用。

请参阅 DW_OP_LLVM_select_bit_pieceDW_OP_LLVM_extend,位于 A.2.5.4.4.6 复合位置描述操作

2.11 用于访问调用帧入口寄存器的 DWARF 操作

2.10 用于创建向量复合位置描述的 DWARF 操作 中所述,需要一个 DWARF 表达式,其中包含在子程序入口处处于活动状态的 SIMT 通道集。SIMT 活动通道掩码可能保存在一个寄存器中,该寄存器在子程序执行时被修改。但是,其值可能会在子程序入口处保存。

调用帧信息 (CFI) 已经编码了这种寄存器保存,因此提供一种操作来返回已保存寄存器的位置比生成 loclist 来描述相同信息更有效。现在这是可能的,因为 2.1 允许 DWARF 表达式堆栈上的位置描述 允许堆栈上的位置描述。

请参阅 DW_OP_LLVM_call_frame_entry_reg,位于 A.2.5.4.4.1 通用位置描述操作A.6.4 调用帧信息

2.12 对映射到 SIMT 硬件的源语言的支持

如果源语言以 SIMT 方式映射到 AMDGPU wavefront,则变量 DWARF 位置表达式必须计算 wavefront 单个通道的位置。因此,需要一个 DWARF 操作来表示当前通道,很像 DW_OP_push_object_address 表示当前对象。请参阅 DW_OP_LLVM_push_lane,位于 A.2.5.4.3.1 字面值操作

此外,编译器需要一种方法来传达有多少源语言执行线程映射到目标架构线程的 SIMT 通道。请参阅 DW_AT_LLVM_lanes,位于 A.3.3.5 底层信息

2.13 对 SIMT 硬件发散控制流的支持

如果源语言以 SIMT 方式映射到 AMDGPU wavefront,则编译器可以使用 AMDGPU 执行掩码寄存器来控制哪些通道处于活动状态。为了描述非活动通道的概念位置,需要一个属性,该属性具有一个表达式,该表达式计算每个通道的源位置 PC。

为了提高效率,该表达式计算整个 wavefront 的源位置。这可以使用 DW_OP_LLVM_select_bit_piece 操作(请参阅 2.10 用于创建向量复合位置描述的 DWARF 操作)完成。

AMDGPU 可能会更新执行掩码以执行整个 wavefront 操作。因此,需要一个属性来计算当前活动通道掩码。这可以有一个表达式,该表达式可以评估为 SIMT 活动通道掩码寄存器,或者在整个 wavefront 执行模式下评估为保存的掩码。

使用这些属性的示例在 C. 更多示例 附录中引用。

请参阅 DW_AT_LLVM_lane_pcDW_AT_LLVM_active_lane,位于 A.2.5.4.4.6 复合位置描述操作

2.14 定义源语言内存类

AMDGPU 支持定义源语言内存类的语言,例如 OpenCL [OpenCL]。添加了对定义特定于语言的内存空间的支持,以便消费者可以以一致的方式使用它们。

还添加了在定义源语言类型和数据对象分配中使用内存空间的支持。

请参阅 A.2.14 内存空间

2.15 定义增强字符串以支持多个扩展

向编译单元调试器信息条目添加了 DW_AT_LLVM_augmentation 属性,以指示该编译单元的调试信息条目中存在额外的特定于目标架构的信息。这允许消费者知道调试信息条目中存在哪些扩展,就像其他部分的增强字符串一样。请参阅 。

还建议使用增强字符串的格式。这允许消费者在字符串包含来自多个供应商的信息时解析该字符串。增强字符串出现在 DW_AT_LLVM_augmentation 属性中、按名称查找表中以及 CFI 公共信息条目 (CIE) 中。

请参阅 A.3.1.1 完整和部分编译单元条目A.6.1.1.4.1 节头A.6.4.1 调用帧信息的结构

2.16 支持嵌入源代码文本以进行在线编译

AMDGPU 支持包含在线编译的编程语言,其中源代码文本可以在运行时创建。例如,OpenCL 和 HIP 语言运行时支持在线编译。为了支持这一点,提供了一种将源代码文本嵌入到调试信息中的方法。

请参阅 A.6.2 行号信息

2.17 允许 MD5 校验和可选存在

在 DWARF 版本 5 中,文件时间戳和文件大小可以是可选的,但是如果存在 MD5 校验和,则它对于所有文件都必须有效。如果使用链接时优化来组合某些具有 MD5 校验和而某些没有的编译单元,则这是一个问题。因此,添加了允许 MD5 校验和在行表中可选存在的支持。

请参阅 A.6.2 行号信息

2.18 添加 HIP 编程语言

添加了 AMDGPU 支持的 HIP 编程语言 [HIP]。

请参阅 语言名称

2.19 支持导致并发迭代执行的源语言优化

编译器可以执行循环优化,从而导致生成的代码并发执行多个迭代。例如,软件流水以交错方式调度多个迭代,以允许一个迭代的指令隐藏另一个迭代的指令的延迟。另一个示例是向量化,它可以利用 SIMD 硬件来允许单个指令使用向量寄存器执行多个迭代。

请注意,尽管这与 SIMT 执行类似,但客户端调试器使用信息的方式从根本上是不同的。在 SIMT 执行中,调试器需要将并发执行呈现为不同的源语言线程,用户可以列出这些线程并在它们之间切换焦点。对于迭代并发优化(例如软件流水和向量化 SIMD),调试器不得将并发呈现为不同的源语言线程。相反,它必须告知用户多个循环迭代正在并行执行,并允许用户在它们之间进行选择。

一般来说,SIMT 执行固定了每个目标架构线程的并发执行次数。但是,软件流水和 SIMD 向量化都可能改变单个源语言线程执行的不同循环的并发迭代次数。

编译器可以在单个源语言线程的代码中同时使用 SIMT 并发和迭代并发技术。

因此,需要一个 DWARF 操作来表示当前的并发迭代实例,很像 DW_OP_push_object_address 表示当前对象。请参阅 DW_OP_LLVM_push_iteration,位于 A.2.5.4.3.1 字面值操作

此外,编译器需要一种方法来传达有多少源语言循环迭代正在并发执行。请参阅 DW_AT_LLVM_iterations,位于 A.3.3.5 底层信息

2.20 用于创建运行时覆盖复合位置描述的 DWARF 操作

在 SIMD 向量化中,编译器通常会生成将数组的一部分提升到向量寄存器中的代码。例如,如果硬件具有包含 8 个元素的向量寄存器和 8 宽 SIMD 指令,则编译器可以向量化循环,以便对于每个向量化循环迭代并发执行 8 个迭代。

在生成的向量化循环的第一次迭代中,源语言循环的迭代 0 到 7 将使用 SIMD 指令执行。然后在生成的向量化循环的下一次迭代中,将执行迭代 8 到 15,依此类推。

如果源语言循环根据循环迭代索引访问数组元素,则编译器可能会在迭代期间将该元素读取到寄存器中。下一次迭代它会将下一个元素读取到寄存器中,依此类推。使用 SIMD,这推广到编译器在第一个向量化循环迭代中将数组元素 0 到 7 读取到向量寄存器中,然后在下一次迭代中读取数组元素 8 到 15,依此类推。

数组的 DWARF 位置描述需要表达所有元素都在内存中,除了已提升到向量寄存器的切片。切片的起始位置是一个运行时值,该值基于迭代索引模向量化大小。这无法通过 DW_OP_pieceDW_OP_bit_piece 表示,它们仅允许表示常量偏移量。

因此,定义了一个新的运算符,该运算符接受两个位置描述、一个偏移量和一个大小,并创建一个复合,该复合有效地将第二个位置描述用作第一个位置描述的覆盖,并根据偏移量和大小进行定位。请参阅 DW_OP_LLVM_overlayDW_OP_LLVM_bit_overlay,位于 A.2.5.4.4.6 复合位置描述操作

考虑一个已部分寄存器化的数组,使得当前处理的元素保存在寄存器中,而数组的其余部分保留在内存中。例如,考虑以下 C 函数中的循环

1extern void foo(uint32_t dst[], uint32_t src[], int len) {
2  for (int i = 0; i < len; ++i)
3    dst[i] += src[i];
4}

在循环体内部,机器代码将 src[i]dst[i] 加载到寄存器中,将它们相加,并将结果存储回 dst[i] 中。

考虑到循环体中 dstsrc 的位置,元素 dst[i]src[i] 将位于寄存器中,所有其他元素都位于内存中。让寄存器 R0 包含 dst 的基地址,寄存器 R1 包含 i,寄存器 R2 包含寄存器化的 dst[i] 元素。我们可以将 dst 的位置描述为一个内存位置,其中寄存器位置覆盖在涉及 i 的运行时偏移处

 1// 1. Memory location description of dst elements located in memory:
 2DW_OP_breg0 0
 3
 4// 2. Register location description of element dst[i] is located in R2:
 5DW_OP_reg2
 6
 7// 3. Offset of the register within the memory of dst:
 8DW_OP_breg1 0
 9DW_OP_lit4
10DW_OP_mul
11
12// 4. The size of the register element:
13DW_OP_lit4
14
15// 5. Make a composite location description for dst that is the memory #1 with
16//    the register #2 positioned as an overlay at offset #3 of size #4:
17DW_OP_LLVM_overlay

2.21 源语言内存空间支持

AMDGPU 支持定义源语言内存空间的语言,例如 OpenCL。添加了对定义特定于语言的内存空间的支持,以便消费者可以以一致的方式使用它们。请参阅 A.2.14 内存空间

添加了一个新的属性 DW_AT_LLVM_memory_space,以支持在定义源语言指针和引用类型(请参阅 A.5.3 类型修饰符条目)和数据对象分配(请参阅 A.4.1 数据对象条目)中使用内存空间。

2.22 表达式操作供应商可扩展性操作码

DWARF 表达式操作的供应商扩展编码空间仅容纳 32 个唯一操作。实际上,由于缺少中央注册表以及对向后兼容性的渴望,即使标准版本被 DWARF 本身接受,供应商扩展也永远不会退役。这导致了这样一种情况:今天可用于新供应商扩展的有效编码空间微乎其微。

为了扩展此编码空间,添加了一个新的 DWARF 操作 DW_OP_LLVM_user,它充当供应商扩展的“前缀”。它后面跟一个 ULEB128 编码的供应商扩展操作码,然后是相应供应商扩展操作的操作数。

这种方法允许对这些扩展中定义的所有剩余操作进行编码,而不会与现有的供应商扩展冲突。

请参阅 DW_OP_LLVM_user,位于 A.2.5.4.0 供应商扩展操作

A. 相对于 DWARF 版本 5 的更改

注意

本附录提供了相对于 DWARF 版本 5 的更改。它被定义为向后兼容 DWARF 版本 5。非规范性文本以斜体显示。除非另有说明,否则节号通常与 DWARF 版本 5 标准中的节号相对应。给出了附加操作的定义,并阐明了现有表达式操作、CFI 操作和属性在支持地址空间和多个位置的广义位置描述方面的行为方式。

新操作、属性和常量的名称包括 “LLVM”,并使用供应商特定的代码进行编码,以便这些扩展可以作为 DWARF 版本 5 的 LLVM 供应商扩展来实现。除了 DW_OP_LLVM_user 之外的新操作都以 DW_OP_LLVM_user 为“前缀”,以便为它们的实现提供足够的编码空间。

注意

包含注释是为了描述如何将更改应用于 DWARF 版本 5 标准。它们还描述了可能需要进一步考虑的原理和问题。

A.2 概述

A.2.2 属性类型

注意

这扩充了 DWARF 版本 5 的第 2.2 节和表 2.2。

下表提供了附加属性。

表 5 属性名称

属性

用法

DW_AT_LLVM_active_lane

SIMT 活动通道(请参阅 A.3.3.5 底层信息

DW_AT_LLVM_augmentation

编译单元增强字符串(请参阅 A.3.1.1 完整和部分编译单元条目

DW_AT_LLVM_lane_pc

SIMT 通道程序位置(请参阅 A.3.3.5 底层信息

DW_AT_LLVM_lanes

SIMT 通道计数(请参阅 A.3.3.5 底层信息

DW_AT_LLVM_iterations

并发迭代计数(请参阅 A.3.3.5 底层信息

DW_AT_LLVM_vector_size

基本类型向量大小(请参阅 A.5.1 基本类型条目

DW_AT_LLVM_address_space

特定于架构的地址空间(请参阅 A.2.13 地址空间

DW_AT_LLVM_memory_space

指针或引用类型(请参阅 5.3 “类型修饰符条目”)数据对象(请参阅 4.1 “数据对象条目”)

A.2.5 DWARF 表达式

注意

本节及其嵌套节取代了 DWARF 版本 5 的第 2.5 节和第 2.6 节。定义了新的 DWARF 表达式操作扩展,并阐明了对现有 DWARF 版本 5 操作的扩展。它基于现有 DWARF 版本 5 标准的文本。

DWARF 表达式描述了如何计算值或指定位置。

DWARF 表达式的评估可以提供对象的位置、数组边界的值、动态字符串的长度、所需的值本身等等。

如果 DWARF 表达式的评估没有遇到错误,则它可能产生一个值(请参阅 A.2.5.2 DWARF 表达式值)或一个位置描述(请参阅 A.2.5.3 DWARF 位置描述)。当评估 DWARF 表达式时,可以指定需要值还是位置描述作为结果类型。

如果指定了结果类型,并且评估结果与指定的结果类型不匹配,则如果有效,则执行 A.2.5.4.4.3 内存位置描述操作 中描述的隐式转换。否则,DWARF 表达式格式不正确。

如果 DWARF 表达式的评估遇到评估错误,则结果为评估错误。

注意

决定定义评估错误的概念。另一种方法是以类似于位置描述具有未定义的位置描述的方式引入未定义的值基本类型。然后,遇到评估错误的操作可以返回未定义的位置描述或具有未定义基本类型的值。

如果给定未定义的值,则所有对值进行操作的操作都将返回未定义的实体。然后,表达式将始终评估完成,并且可以进行测试以确定它是否是未定义的实体。

但是,这将增加相当大的额外复杂性,并且与 GDB 在发生这些评估错误时抛出异常的情况不符。

如果 DWARF 表达式格式不正确,则结果是未定义的。

以下各节详细说明了 DWARF 表达式何时格式不正确或导致评估错误的规则。

DWARF 表达式可以编码为操作表达式(请参阅 A.2.5.4 DWARF 操作表达式),也可以编码为位置列表表达式(请参阅 A.2.5.5 DWARF 位置列表表达式)。

A.2.5.1 DWARF 表达式评估上下文

DWARF 表达式在上下文中评估,该上下文可以包含多个上下文元素。如果指定了多个上下文元素,则它们必须是自洽的,否则评估结果是未定义的。可以指定的上下文元素包括

当前结果类型

DWARF 表达式评估所需的结果类型。如果指定,则可以是位置描述或值。

当前线程

目标架构线程标识符。对于未使用 SIMT 执行模型实现的源语言,这对应于用户提供的表达式当前正在评估的源程序执行线程。对于使用 SIMT 执行模型实现的源语言,这与当前通道一起对应于用户提供的表达式当前正在评估的源程序执行线程。

对于与目标架构线程相关的操作是必需的。

例如, DW_OP_regval_type 操作,或者 DW_OP_form_tls_address DW_OP_LLVM_form_aspace_address 操作,当给定一个特定于目标架构线程的地址空间时。

当前通道

用于评估用户提供的表达式的基于 0 的 SIMT 通道标识符。这适用于为使用 SIMT 执行模型的目标架构实现的源语言。这些实现将源语言执行线程映射到目标架构线程的通道。

对于与 SIMT 通道相关的操作是必需的。

例如, DW_OP_LLVM_push_lane 操作和 DW_OP_LLVM_form_aspace_address 操作,当给定一个特定于 SIMT 通道的地址空间时。

如果指定,则它必须与上下文的帧和程序位置对应的子程序的 DW_AT_LLVM_lanes 属性的值一致。如果该值大于或等于 0 且小于 DW_AT_LLVM_lanes 属性的可能默认值,则它是一致的。否则,结果是未定义的。

当前迭代

用于评估用户提供的表达式的基于 0 的源语言迭代实例。这适用于支持导致并发执行多个源语言循环迭代的优化的目标架构。

例如,软件流水和 SIMD 向量化。

对于与源语言循环迭代相关的操作是必需的。

例如, DW_OP_LLVM_push_iteration 操作。

如果指定,则它必须与上下文帧和程序位置对应的子程序的 DW_AT_LLVM_iterations 属性值一致。如果该值大于或等于 0 且小于(可能为默认值)DW_AT_LLVM_iterations 属性的值,则它是一致的。否则,结果是未定义的。

当前调用帧

目标架构调用帧标识符。它标识一个调用帧,该调用帧对应于当前线程中子程序的活动调用。它由其在调用堆栈上的地址标识。该地址被称为规范帧地址 (CFA)。调用帧信息用于确定当前线程调用堆栈的调用帧的 CFA(参见 A.6.4 调用帧信息)。

对于指定目标架构寄存器的操作,需要它来支持调用堆栈的虚拟展开。

例如, DW_OP_*reg* 操作。

如果指定,则它必须是当前线程中的活动调用帧。如果指定了当前通道,则该通道必须在进入调用帧时处于活动状态(参见 DW_AT_LLVM_lane_pc 属性)。否则,结果是未定义的。

如果是当前正在执行的调用帧,则称其为顶部调用帧。

当前程序位置

与当前线程的当前调用帧对应的目标架构程序位置。

顶部调用帧的程序位置是当前线程的目标架构程序计数器。调用帧信息用于获取返回地址寄存器的值,以确定其他调用帧的程序位置(参见 A.6.4 调用帧信息)。

对于位置列表表达式的评估以在多个程序位置范围之间进行选择,这是必需的。对于指定目标架构寄存器的操作,需要它来支持调用堆栈的虚拟展开(参见 A.6.4 调用帧信息)。

如果指定

  • 如果未指定当前通道

    • 如果当前调用帧是顶部调用帧,则它必须是当前目标架构程序位置。

    • 如果当前调用帧 F 不是顶部调用帧,则它必须是与当前调用者帧 F 中调用被调用者帧的调用站点关联的程序位置。

  • 如果指定了当前通道,并且由当前通道的 DW_AT_LLVM_lane_pc 属性计算的架构程序位置 LPC 不是未定义的位置描述(表示该通道在进入调用帧时未处于活动状态),则它必须是 LPC。

  • 否则,结果是未定义的。

当前编译单元

包含正在评估的 DWARF 表达式的编译单元调试信息条目。

对于引用与同一编译单元关联的调试信息的操作,包括指示此类引用是否使用 32 位或 64 位 DWARF 格式,这是必需的。如果未指定当前目标架构,它还可以提供默认地址空间地址大小。

例如, DW_OP_constx DW_OP_addrx 操作。

请注意,此编译单元可能与从对应于当前程序位置的已加载代码对象确定的编译单元不同。例如,与 DW_OP_call* 操作的操作数调试信息条目的 DW_AT_location 属性关联的表达式 E 的评估是使用包含 E 的编译单元而不是包含 DW_OP_call* 操作表达式的编译单元来评估的。

当前目标架构

目标架构。

对于指定目标架构特定实体的操作,这是必需的。

例如,目标架构特定实体包括 DWARF 寄存器标识符、DWARF 通道标识符、DWARF 地址空间标识符、默认地址空间和地址空间地址大小。

如果指定

  • 如果指定了当前帧,则当前目标架构必须与当前帧的目标架构相同。

  • 如果指定了当前帧并且是顶部帧,并且如果指定了当前线程,则当前目标架构必须与当前线程的目标架构相同。

  • 如果指定了当前编译单元,则当前目标架构默认地址空间地址大小必须与当前编译单元头中的 address_size 字段以及 .debug_aranges 部分中任何关联的条目相同。

  • 如果指定了当前程序位置,则当前目标架构必须与任何对应于当前程序位置的行号信息条目(参见 A.6.2 行号信息)的目标架构相同。

  • 如果指定了当前程序位置,则当前目标架构默认地址空间地址大小必须与 .debug_addr.debug_line.debug_rnglists.debug_rnglists.dwo.debug_loclists.debug_loclists.dwo 部分中任何对应于当前程序位置的条目的头中的 address_size 字段相同。

  • 否则,结果是未定义的。

当前对象

程序对象的位置描述。

对于 DW_OP_push_object_address 操作,这是必需的。

例如,类型调试信息条目上的 DW_AT_data_location 属性在评估其关联表达式时,将对应于运行时描述符的程序对象指定为当前对象。

如果位置描述无效(参见 A.2.5.3 DWARF 位置描述),则结果是未定义的。

初始堆栈

这是一个值或位置描述的列表,在操作表达式评估开始之前,将按提供的顺序将其推送到操作表达式评估堆栈上。

某些调试器信息条目具有使用初始堆栈条目评估其 DWARF 表达式值的属性。在所有其他情况下,初始堆栈为空。

如果任何位置描述无效(参见 A.2.5.3 DWARF 位置描述),则结果是未定义的。

如果评估需要未指定的上下文元素,则评估结果为错误。

位置描述的 DWARF 表达式可能能够在没有线程、通道、调用帧、程序位置或架构上下文的情况下进行评估。例如,全局变量的位置可能能够在没有此类上下文的情况下进行评估。如果表达式评估出现错误,则可能表明该变量已被优化,因此需要更多上下文。

调用帧信息(参见 A.6.4 调用帧信息)操作的 DWARF 表达式被限制为那些不需要指定编译单元上下文的操作。

如果与任何给定程序位置对应的 .debug_info.debug_addr.debug_line.debug_rnglists.debug_rnglists.dwo.debug_loclists.debug_loclists.dwo 部分中所有条目的头中的所有 address_size 字段不匹配,则 DWARF 格式不正确。

A.2.5.2 DWARF 表达式值

值具有类型和字面值。它可以表示目标架构任何受支持基本类型的字面值。基本类型指定字面值的大小、编码和字节序。

注意

可能需要添加隐式指针基本类型编码。它将用于当 DW_OP_deref* 操作检索由 DW_OP_implicit_pointerDW_OP_LLVM_aspace_implicit_pointer 操作创建的隐式指针位置存储的完整内容时生成的值的类型。字面值将记录由关联的 DW_OP_implicit_pointerDW_OP_LLVM_aspace_implicit_pointer 操作指定的调试信息条目和字节位移。

有一个称为通用类型的特殊基本类型,它是一种整数类型,其大小是目标架构默认地址空间中地址的大小,目标架构定义的字节序和未指定的符号。

通用类型与 DWARF 版本 4 及更早版本中定义的堆栈操作使用的未指定类型相同。

整数类型是一种基本类型,其编码为 DW_ATE_signedDW_ATE_signed_charDW_ATE_unsignedDW_ATE_unsigned_charDW_ATE_boolean 或任何目标架构定义的整数编码,范围在 DW_ATE_lo_userDW_ATE_hi_user(含)之间。

注意

目前尚不清楚 DW_ATE_address 是否为整数类型。GDB 似乎不认为它是整数类型。

A.2.5.3 DWARF 位置描述

调试信息必须为使用者提供一种查找程序变量位置、确定动态数组和字符串边界的方法,并可能找到子程序调用帧的基地址或子程序的返回地址。此外,为了满足最新计算机架构和优化技术的需求,调试信息必须能够描述对象的生命周期内位置发生变化的对象的位置,并且可能在对象生命周期的一部分内同时驻留在多个位置。

有关程序对象位置的信息由位置描述提供。

位置描述可以由一个或多个单个位置描述组成。

单个位置描述指定保存程序对象的位置存储,以及程序对象开始的位置存储内的位置。位置存储内的位置表示为相对于位置存储开始处的位偏移。

位置存储是可以保存值的线性位流。每个位置存储都有一个位大小,并且可以使用从零开始的位偏移进行访问。位置存储中位的排序使用适合目标架构上当前语言的位编号和方向约定。

位置存储共有五种

内存位置存储

对应于目标架构内存地址空间。

寄存器位置存储

对应于目标架构寄存器。

隐式位置存储

对应于只能读取的固定值。

未定义位置存储

指示没有可用值,因此无法读取或写入。

复合位置存储

允许这些位置存储的混合,其中某些位来自一个位置存储,而某些位来自另一个位置存储,或者来自同一位置存储的不相交部分。

注意

最好添加一个隐式指针位置存储类型,供 DW_OP_implicit_pointerDW_OP_LLVM_aspace_implicit_pointer 操作使用。它将指定操作提供的调试器信息条目和字节偏移。

位置描述是地址规则的语言无关表示。

  • 它们可以是评估调试器信息条目属性的结果,该属性指定任意复杂度的操作表达式。在这种用法中,只要对象的生命周期是静态的或与其所属的词法块(参见 :ref:`amdgpu-dwarf-lexical-block-entries`)相同,并且在其生命周期内不会移动,它们就可以描述对象的位置。

  • 它们可以是评估调试器信息条目属性的结果,该属性指定位置列表表达式。在这种用法中,它们可以描述生命周期有限、在其生命周期内更改其位置或在其部分或全部生命周期内具有多个位置的对象的位置。

如果位置描述具有多个单个位置描述,则如果每个单个位置描述的位置存储内位置中保存的对象值不是相同的值,则 DWARF 表达式格式不正确,但未初始化的值部分除外。

具有多个单个位置描述的位置描述只能由具有重叠程序位置范围的位置列表表达式创建,或者由对具有多个单个位置描述的位置描述执行操作的某些表达式操作创建。没有可以直接创建具有多个单个位置描述的位置描述的操作表达式操作。

具有多个单个位置描述的位置描述可用于描述同时驻留在多个存储块中的对象。由于优化,对象可能具有多个位置。例如,仅读取的值可能会从内存提升到寄存器以用于某些代码区域,但稍后的代码可能会恢复为从内存中读取该值,因为该寄存器可能用于其他目的。对于值在寄存器中的代码区域,对对象值的任何更改都必须在寄存器和内存中进行,以便两个代码区域都将读取更新后的值。

具有多个单个位置描述的位置描述的使用者可以从任何单个位置描述中读取对象的值(因为它们都引用具有相同值的位置存储),但必须将任何更改的值写入所有单个位置描述。

表达式的评估可能需要上下文元素来创建位置描述。如果访问此类位置描述,则其表示的存储是与创建位置描述时指定的上下文元素值关联的存储,这可能与访问时的上下文不同。

例如,创建寄存器位置描述需要线程上下文:位置存储是该线程的指定寄存器的位置存储。为地址空间创建内存位置描述可能需要线程和通道上下文:位置存储是与该线程和通道关联的内存。

如果创建位置描述所需的任何上下文元素发生更改,则位置描述将变为无效,并且访问它是未定义的。

可能使位置描述无效的上下文示例包括

  • 需要线程上下文,并且执行导致线程终止。

  • 需要调用帧上下文,并且进一步执行导致调用帧返回到调用帧。

  • 需要程序位置,并且发生线程的进一步执行。这可能会更改适用的位置列表条目或调用帧信息条目。

  • 操作使用调用帧信息

    • 虚拟调用帧展开中使用的任何帧都返回。

    • 使用顶部调用帧,程序位置用于选择调用帧信息条目,并且发生线程的进一步执行。

DWARF 表达式可用于计算对象的位置描述。后续的 DWARF 表达式评估可以将对象位置描述作为对象上下文或初始堆栈上下文,以计算对象的组件。如果对象位置描述在两次表达式评估之间变为无效,则最终结果是未定义的。

线程程序位置的更改可能不会使位置描述无效,但仍可能使其不再有意义。访问此类位置描述,或将其用作表达式评估的对象上下文或初始堆栈上下文,可能会产生未定义的结果。

例如,位置描述可能指定一个寄存器,该寄存器在程序位置更改后不再保存预期的程序对象。避免此类问题的一种方法是在线程的程序位置更改时重新计算与线程关联的位置描述。

A.2.5.4 DWARF 操作表达式

操作表达式由操作流组成,每个操作都由一个操作码后跟零个或多个操作数组成。操作数的数量由操作码隐含。

操作表示简单堆栈机器上的后缀操作。每个堆栈条目都可以保存值或位置描述。操作可以作用于堆栈上的条目,包括添加条目和删除条目。如果堆栈条目的类型与操作所需的类型不匹配,并且不能隐式转换为所需的类型(参见 A.2.5.4.4.3 内存位置描述操作),则 DWARF 操作表达式格式不正确。

操作表达式的评估从空堆栈开始,上下文提供的初始堆栈中的条目按提供的顺序推送到该空堆栈上。然后评估操作,从操作流的第一个操作开始。评估持续进行,直到操作发生评估错误,或者直到到达操作流的最后一个操作之后的位置。

评估结果是

  • 如果操作发生评估错误,或者操作评估的表达式发生评估错误,则结果为评估错误。

  • 如果当前结果类型指定位置描述,则

    • 如果堆栈为空,则结果是具有一个未定义位置描述的位置描述。

      此规则是为了向后兼容 DWARF 版本 5,该版本没有显式操作来创建未定义的位置描述,并且为此目的使用空操作表达式。

    • 如果顶部堆栈条目是位置描述,或者可以转换为位置描述(参见 A.2.5.4.4.3 内存位置描述操作),则结果是该(可能已转换的)位置描述。堆栈上的任何其他条目都将被丢弃。

    • 否则,DWARF 表达式格式不正确。

      注意

      可以将这种情况定义为返回隐式位置描述,就像执行 DW_OP_implicit 操作一样。

  • 如果当前结果类型指定值,则

    • 如果顶部堆栈条目是值,或者可以转换为值(参见 A.2.5.4.4.3 内存位置描述操作),则结果是该(可能已转换的)值。堆栈上的任何其他条目都将被丢弃。

    • 否则,DWARF 表达式格式不正确。

  • 如果未指定当前结果类型,则

    • 如果堆栈为空,则结果是具有一个未定义位置描述的位置描述。

      此规则是为了向后兼容 DWARF 版本 5,该版本没有显式操作来创建未定义的位置描述,并且为此目的使用空操作表达式。

      注意

      此规则与上面请求位置描述时的规则一致。但是,GDB 似乎将此报告为错误,并且没有 GDB 测试似乎导致此情况下的堆栈为空。

    • 否则,返回顶部堆栈条目。堆栈上的任何其他条目都将被丢弃。

操作表达式编码为字节块,带有一些形式的前缀,用于指定字节计数。它可以用于

A.2.5.4.0 供应商扩展操作
  1. DW_OP_LLVM_user

DW_OP_LLVM_user 编码供应商扩展操作。它至少有一个操作数:一个 ULEB128 常量,用于标识供应商扩展操作。其余操作数由供应商扩展定义。供应商扩展操作码 0 是保留的,任何供应商扩展都不能使用。

DW_OP_user 编码空间可以理解为补充由 DW_OP_lo_user 和 DW_OP_hi_user 定义的空间,这些空间由标准分配用于相同的目的。

A.2.5.4.1 堆栈操作

注意

本节替换 DWARF 版本 5 第 2.5.1.3 节。

以下操作操纵 DWARF 堆栈。索引堆栈的操作假定堆栈顶部(最近添加的条目)的索引为 0。它们允许堆栈条目是值或位置描述。

如果堆栈操作访问的任何堆栈条目是不完整的复合位置描述(参见 A.2.5.4.4.6 复合位置描述操作),则 DWARF 表达式格式不正确。

注意

这些操作现在支持作为值和位置描述的堆栈条目。

注意

如果还需要使它们与不完整的复合位置描述一起使用,则需要定义在推送副本时,不完整的复合位置描述指定的复合位置存储也会被复制。这确保了不完整的复合位置描述的每个副本都可以独立更新它们指定的复合位置存储。

  1. DW_OP_dup

    DW_OP_dup 复制堆栈顶部的堆栈条目。

  2. DW_OP_drop

    DW_OP_drop 弹出堆栈顶部的堆栈条目并将其丢弃。

  3. DW_OP_pick

    DW_OP_pick 具有单个无符号 1 字节操作数,该操作数表示索引 I。索引为 I 的堆栈条目的副本被推送到堆栈上。

  4. DW_OP_over

    DW_OP_over 推送索引为 1 的条目的副本。

    这等效于 DW_OP_pick 1 操作。

  5. DW_OP_swap

    DW_OP_swap 交换顶部两个堆栈条目。堆栈顶部的条目变为第二个堆栈条目,第二个堆栈条目变为堆栈顶部。

  6. DW_OP_rot

    DW_OP_rot 旋转前三个堆栈条目。堆栈顶部的条目变为第三个堆栈条目,第二个条目变为堆栈顶部,第三个条目变为第二个条目。

Appendix D.1.2 on page 289 中提供了许多这些堆栈操作的示例。

A.2.5.4.2 控制流操作

注意

本节替换 DWARF 版本 5 第 2.5.1.5 节。

以下操作提供对 DWARF 操作表达式流的简单控制。

  1. DW_OP_nop

    DW_OP_nop 是一个占位符。它对 DWARF 堆栈条目没有影响。

  2. DW_OP_leDW_OP_geDW_OP_eqDW_OP_ltDW_OP_gtDW_OP_ne

    注意

    与 DWARF 版本 5 第 2.5.1.5 节中的相同。

  3. DW_OP_skip

    DW_OP_skip 是一个无条件分支。它的单个操作数是一个 2 字节有符号整数常量。2 字节常量是要从当前操作向前或向后跳过的 DWARF 表达式的字节数,从 2 字节常量之后开始。

    如果更新后的位置位于最后一个操作之后的位置,则操作表达式评估完成。

    否则,如果更新后的操作位置不在第一个到最后一个操作(含)的范围内,或者不在操作的开头,则 DWARF 表达式格式不正确。

  4. DW_OP_bra

    DW_OP_bra 是一个条件分支。它的单个操作数是一个 2 字节有符号整数常量。此操作弹出堆栈顶部。如果弹出的值不是常量 0,则 2 字节常量操作数是要从当前操作向前或向后跳过的 DWARF 操作表达式的字节数,从 2 字节常量之后开始。

    如果更新后的位置位于最后一个操作之后的位置,则操作表达式评估完成。

    否则,如果更新后的操作位置不在第一个到最后一个操作(含)的范围内,或者不在操作的开头,则 DWARF 表达式格式不正确。

  5. DW_OP_call2、 DW_OP_call4、 DW_OP_call_ref

    DW_OP_call2DW_OP_call4DW_OP_call_ref 在评估 DWARF 操作表达式期间执行 DWARF 过程调用。

    DW_OP_call2DW_OP_call4 各有一个操作数,分别是 2 字节或 4 字节无符号偏移量 DR,表示调试信息条目 D 相对于当前编译单元开头的字节偏移量。

    DW_OP_call_ref 有一个操作数,该操作数在 32 位 DWARF 格式中为 4 字节无符号值,在 64 位 DWARF 格式中为 8 字节无符号值,表示调试信息条目 D 相对于包含当前编译单元的 .debug_info 部分开头的字节偏移量 DR。D 可能不在当前编译单元中。

    注意

    DWARF 版本 5 声明 DR 可以是 .debug_info 部分(而不是包含当前编译单元的部分)中的偏移量。它声明从一个可执行文件或共享对象文件到另一个可执行文件或共享对象文件的引用的重定位必须由使用者执行。但是,鉴于 DR 被定义为 .debug_info 部分中的偏移量,这似乎是不可能的。如果 DR 被定义为实现定义的值,则使用者可以选择以实现定义的方式解释该值,以引用另一个可执行文件或共享对象中的调试信息。

    在 ELF 中,.debug_info 部分位于非 PT_LOAD 段中,因此不能使用标准动态重定位。但是,即使它们是加载段并使用了动态重定位,DR 也需要是 D 的地址,而不是 .debug_info 部分中的偏移量。这也需要 DR 为全局地址的大小。因此,在 64 位全局地址空间中不可能使用 32 位 DWARF 格式。此外,使用者需要确定重定位地址位于哪个可执行文件或共享对象中,以便它可以确定包含的编译单元。

    GDB 仅将 DR 解释为 .debug_info 部分(包含当前编译单元的部分)中的偏移量。

    此注释也适用于 DW_OP_implicit_pointerDW_OP_LLVM_aspace_implicit_pointer

    DW_OP_call2 DW_OP_call4 DW_OP_call_ref 的操作数解释与 DW_FORM_ref2 、``DW_FORM_ref4``* 和 DW_FORM_ref_addr 的操作数解释完全相同。

    调用操作通过以下方式评估

    • 如果 D 具有编码为 exprlocDW_AT_location 属性,该属性指定操作表达式 E,则当前操作表达式的执行从 E 的第一个操作继续。执行持续到到达 E 的最后一个操作之后的位置,此时执行继续执行调用操作之后的下一个操作。E 的操作使用相同的当前上下文进行评估,但当前编译单元是包含 D 的编译单元,并且堆栈与调用操作使用的堆栈相同。因此,在评估调用操作后,堆栈将保持 E 的操作评估后留下的状态。由于 E 在与调用操作相同的堆栈上进行评估,因此 E 可以使用和/或删除已在堆栈上的条目,并且可以将新条目添加到堆栈。

      调用时堆栈上的值可以用作被调用表达式的参数,而被调用表达式留在堆栈上的值可以用作调用和被调用表达式之间事先约定的返回值。

    • 如果 D 具有编码为 loclistloclistsptrDW_AT_location 属性,则评估指定的位置列表表达式 E。E 的评估使用当前上下文,但结果类型是位置描述,编译单元是包含 D 的编译单元,并且初始堆栈为空。位置描述结果被推送到堆栈上。

      注意

      此规则避免了在有多个匹配项时必须定义如何在与调用相同的堆栈上执行匹配的位置列表条目操作表达式。但是,它允许调用获取变量或形式参数的位置描述,这可能会使用位置列表表达式。

      另一种替代方案是处理当 D 具有一个 DW_AT_location 属性,该属性被编码为 loclistloclistsptr 的情况,并且指定的 location list 表达式 E’ 匹配一个带有操作表达式 E 的 location list 条目,这与 exprloc 情况相同,并在相同的堆栈上求值。

      但这并不吸引人,因为如果属性是针对碰巧以非单例堆栈结束的变量,它将不会简单地将位置描述放在堆栈上。据推测,在变量或形式参数调试信息条目上使用 DW_OP_call* 的意图是仅在堆栈上推送一个位置描述。该位置描述可能具有多个单一位置描述。

      先前关于 exprloc 的规则也存在同样的问题,因为通常变量或形式参数位置表达式可能会在堆栈上留下多个条目,并且仅返回顶部条目。

      GDB 通过始终在相同的堆栈上执行 E 来实现 DW_OP_call*。如果 location list 具有多个匹配的条目,它只会选择第一个并忽略其余的。这似乎从根本上与支持变量的多个位置的愿望相悖。

      因此,感觉 DW_OP_call* 应该既支持为变量或形式参数在堆栈上推送位置描述,又支持能够在相同的堆栈上执行操作表达式。能够为不同的程序位置指定不同的操作表达式似乎是一个值得保留的特性。

      对此的一种解决方案是为 DW_TAG_dwarf_procedure 调试信息条目设置一个不同的 DW_AT_LLVM_proc 属性。然后 DW_AT_location 属性表达式始终单独执行并推送一个位置描述(可能具有多个单一位置描述),而 DW_AT_LLVM_proc 属性表达式始终在相同的堆栈上执行,并且可以在堆栈上留下任何内容。

      DW_AT_LLVM_proc 属性可以具有新的类 exprprocloclistprocloclistsptrproc,以指示表达式在相同的堆栈上执行。exprprocexprloc 的编码相同。loclistprocloclistsptrproc 与它们的非 proc counterparts 的编码相同,除非 location list 没有完全匹配一个 location list 条目,并且需要一个默认条目,否则 DWARF 格式不正确。这些形式明确指示匹配的单个操作表达式必须在相同的堆栈上执行。这比 loclistprocloclistsptrproc 的临时特殊规则更好,这些规则目前被明确定义为始终返回位置描述。生产者然后通过属性类显式地指示意图。

      这样的更改将是对 GDB 实现 DW_OP_call* 方式的破坏性更改。然而,破坏性情况实际上在实践中发生吗?GDB 可以为 DWARF 版本 5 实现当前方法,并为 DWARF 版本 6 实现新的语义,这已为某些其他功能完成。

      另一种选择是将执行限制为仅在相同的堆栈上进行,以用于求值作为 DW_TAG_dwarf_procedure 调试信息条目的 DW_AT_location 属性值的表达式 E。如果 E 是一个 location list 表达式,但它没有完全匹配一个 location list 条目,则 DWARF 格式将不正确。在所有其他情况下,作为 DW_AT_location 属性值的表达式 E 的求值将使用当前上下文求值 E,除非结果类型是位置描述,编译单元是包含 D 的单元,并且初始堆栈为空。位置描述结果被推送到堆栈上。

    • 如果 D 具有带有值 V 的 DW_AT_const_value 属性,那么就好像执行了 DW_OP_implicit_value V 操作一样。

      这允许使用 call 操作来计算任何变量或形式参数的位置描述,无论生产者是否已将其优化为常量。这与 DW_OP_implicit_pointer 操作一致。

      注意

      或者,可以弃用对作为常量的 DW_TAG_variableDW_TAG_formal_parameter 调试信息条目使用 DW_AT_const_value,而是使用 DW_AT_location 和一个操作表达式,该表达式产生一个带有一个隐式位置描述的位置描述。那么就不需要此规则。

    • 否则,不会产生任何影响,堆栈也不会进行任何更改。

      注意

      在 DWARF 版本 5 中,如果 D 没有 DW_AT_location,则 DW_OP_call* 被定义为没有效果。尚不清楚这是否是正确的定义,因为生产者应该能够依赖使用 DW_OP_call* 来获取任何非 DW_TAG_dwarf_procedure 调试信息条目的位置描述。此外,生产者不应该创建带有 DW_OP_call* 到不具有 DW_AT_location 属性的 DW_TAG_dwarf_procedure 的 DWARF。那么,这种情况应该被定义为格式不正确的 DWARF 表达式吗?

    DW_TAG_dwarf_procedure 调试信息条目可以用于定义可以调用的 DWARF 过程。

A.2.5.4.3 值操作

本节描述将值推送到堆栈上的操作。

每个值堆栈条目都具有类型和字面值。它可以表示目标架构的任何受支持基本类型的字面值。基本类型指定字面值的大小、编码和字节序。

值堆栈条目的基本类型可以是区分的通用类型。

A.2.5.4.3.1 字面值操作

注意

本节替换 DWARF 版本 5 第 2.5.1.1 节。

以下操作都将字面值推送到 DWARF 堆栈上。

DW_OP_const_type 之外的操作都推送一个具有通用类型的值 V。如果 V 大于通用类型,则 V 将被截断为通用类型大小,并使用低位。

  1. DW_OP_lit0, DW_OP_lit1, …, DW_OP_lit31

    DW_OP_lit<N> 操作编码一个从 0 到 31(包括 31)的无符号字面值 N。它们推送具有通用类型的值 N。

  2. DW_OP_const1u, DW_OP_const2u, DW_OP_const4u, DW_OP_const8u

    DW_OP_const<N>u 操作具有单个操作数,该操作数分别是 1、2、4 或 8 字节的无符号整数常量 U。它们推送具有通用类型的值 U。

  3. DW_OP_const1s, DW_OP_const2s, DW_OP_const4s, DW_OP_const8s

    DW_OP_const<N>s 操作具有单个操作数,该操作数分别是 1、2、4 或 8 字节的有符号整数常量 S。它们推送具有通用类型的值 S。

  4. DW_OP_constu

    DW_OP_constu 具有单个无符号 LEB128 整数操作数 N。它推送具有通用类型的值 N。

  5. DW_OP_consts

    DW_OP_consts 具有单个有符号 LEB128 整数操作数 N。它推送具有通用类型的值 N。

  6. DW_OP_constx

    DW_OP_constx 具有单个无符号 LEB128 整数操作数,该操作数表示相对于关联编译单元的 DW_AT_addr_base 属性值,在 .debug_addr 节中的从零开始的索引。 .debug_addr 节中的值 N 具有通用类型的大小。它推送具有通用类型的值 N。

    DW_OP_constx 操作是为需要链接时重定位的常量提供的,但不应被消费者解释为可重定位地址(例如,线程本地存储的偏移量)。

  7. DW_OP_const_type

    DW_OP_const_type 具有三个操作数。第一个是无符号 LEB128 整数 DR,它表示相对于当前编译单元的开始处的调试信息条目 D 的字节偏移量,该条目 D 提供了常量值的类型 T。第二个是 1 字节的无符号整数常量 S。第三个是字节块 B,其长度等于 S。

    TS 是类型 T 的位大小。B 的最低有效 TS 位被解释为类型 D 的值 V。它推送具有类型 D 的值 V。

    如果 D 不是当前编译单元中的 DW_TAG_base_type 调试信息条目,或者如果 TS 除以 8(字节大小)并向上舍入到整数不等于 S,则 DWARF 格式不正确。

    虽然字节块 B 的大小可以从类型 D 定义中推断出来,但它被显式地编码到操作中,以便可以轻松解析操作而无需参考 .debug_info 节。

  8. DW_OP_LLVM_push_lane 新增

    DW_OP_LLVM_push_lane 将当前 lane 作为具有通用类型的值推送。

    对于使用 SIMT 执行模型实现的源语言,这是与用户关注的源语言执行线程对应的从零开始的 lane 编号。

    该值必须大于或等于 0 且小于 DW_AT_LLVM_lanes 属性的值,否则 DWARF 表达式格式不正确。请参阅 A.3.3.5 低级信息

  9. DW_OP_LLVM_push_iteration 新增

    DW_OP_LLVM_push_iteration 将当前迭代作为具有通用类型的值推送。

    对于具有优化的源语言实现,这些优化导致多个循环迭代并发执行,这是与用户关注的源语言并发循环迭代对应的从零开始的迭代编号。

    该值必须大于或等于 0 且小于 DW_AT_LLVM_iterations 属性的值,否则 DWARF 表达式格式不正确。请参阅 A.3.3.5 低级信息

A.2.5.4.3.2 算术和逻辑运算

注意

本节与 DWARF 版本 5 第 2.5.1.4 节相同。

A.2.5.4.3.3 类型转换运算

注意

本节与 DWARF 版本 5 第 2.5.1.6 节相同。

A.2.5.4.3.4 特殊值运算

注意

本节替换了 DWARF 版本 5 第 2.5.1.2 节、2.5.1.3 节和 2.5.1.7 节的部分内容。

目前定义了以下特殊值运算

  1. DW_OP_regval_type

    DW_OP_regval_type 具有两个操作数。第一个是无符号 LEB128 整数,表示寄存器号 R。第二个是无符号 LEB128 整数 DR,它表示相对于当前编译单元的开始处的调试信息条目 D 的字节偏移量,该条目 D 提供了寄存器值的类型 T。

    此操作等效于执行 DW_OP_regx R; DW_OP_deref_type DR

    注意

    DWARF 是否应该允许类型 T 大于寄存器 R 的大小?限制更大的位大小可以避免任何转换问题,因为寄存器的(可能被截断的)位内容被简单地解释为类型 T 的值。如果需要转换,可以使用 DW_OP_convert 操作显式完成。

    GDB 具有每个寄存器的钩子,允许在每个寄存器的基础上进行目标特定的转换。它默认为截断更大的寄存器。删除目标钩子的使用不会导致常见架构中的任何测试失败。如果目标架构的编译器确实需要某种形式的转换,包括更大的结果类型,它始终可以显式地使用 DW_OP_convert 操作。

    如果 T 是大于寄存器大小的类型,则默认的 GDB 寄存器钩子会从下一个寄存器读取字节(或对于最后一个寄存器,会超出边界读取!)。删除目标钩子的使用不会导致常见架构中的任何测试失败(除了非法的手写汇编测试)。如果目标架构需要此行为,则这些扩展允许使用复合位置描述来组合多个寄存器。

  2. DW_OP_deref

    S 是通用类型的位大小除以 8(字节大小)并向上舍入到整数。DR 是当前编译单元中通用类型基本类型的假设调试信息条目 D 的偏移量。

    此操作等效于执行 DW_OP_deref_type S, DR

  3. DW_OP_deref_size

    DW_OP_deref_size 具有单个 1 字节的无符号整数常量,表示字节结果大小 S。

    TS 是通用类型位大小和 S 乘以 8(字节大小)中的较小者。如果 TS 小于通用类型位大小,则 T 是位大小为 TS 的无符号整数类型,否则 T 是通用类型。DR 是当前编译单元中基本类型 T 的假设调试信息条目 D 的偏移量。

    注意

    当 S 大于通用类型时,截断值与 GDB 的操作相匹配。这允许通用类型大小不是整数字节大小。它确实允许 S 任意大。S 是否应限制为通用类型的大小,并向上舍入到 8 的倍数?

    此操作等效于执行 DW_OP_deref_type S, DR,除非当 T 不是通用类型时,推送的值 V 会被零扩展到通用类型位大小,并且其类型更改为通用类型。

  4. DW_OP_deref_type

    DW_OP_deref_type 具有两个操作数。第一个是 1 字节的无符号整数常量 S。第二个是无符号 LEB128 整数 DR,它表示相对于当前编译单元的开始处的调试信息条目 D 的字节偏移量,该条目 D 提供了结果值类型 T。

    TS 是类型 T 的位大小。

    虽然推送的值 V 的大小可以从类型 T 推断出来,但它被显式地编码为操作数 S,以便可以轻松解析操作而无需参考 .debug_info 节。

    注意

    尚不清楚为什么需要操作数 S。与 DW_OP_const_type 不同,解析不需要大小。任何求值都需要获取基本类型 T 以与值一起推送,以了解其编码和位大小。

    它弹出一个堆栈条目,该条目必须是位置描述 L。

    从 L 的单一位置描述 SL 之一指定的位置存储 LS 中检索 TS 位的 值 V。

    如果 L 或作为 L 的子组件的任何复合位置描述部分的位置描述具有多个单一位置描述,则可以选择其中任何一个,因为它们都需要具有相同的值。对于任何单一位置描述 SL,位是从关联的存储位置检索的,从 SL 指定的位偏移量开始。对于复合位置描述,检索的位是从每个复合位置部分 PL 连接的 N 位,其中 N 限制为 PL 的大小。

    V 被推送到堆栈上,类型为 T。

    注意

    如果 L 是寄存器位置描述,但寄存器存储中剩余的位数少于 TS 位,则此定义使其成为求值错误。特别是由于这些扩展将位置描述扩展为具有位偏移量,因此将其定义为基于类型执行符号扩展或依赖于目标架构将是很奇怪的,因为剩余位数的可以是任何数字。这与 GDB 对 DW_OP_deref_type 的实现相匹配。

    这些扩展根据 DW_OP_regval_type 定义了 DW_OP_*breg*DW_OP_regval_type 根据 DW_OP_regx(使用 0 位偏移量)和 DW_OP_deref_type 定义。因此,它要求寄存器大小大于或等于地址空间的地址大小。这与 GDB 对 DW_OP_*breg* 的实现相匹配。

    如果 D 不在当前编译单元中,D 不是 DW_TAG_base_type 调试信息条目,或者如果 TS 除以 8(字节大小)并向上舍入到整数不等于 S,则 DWARF 格式不正确。

    注意

    此定义允许基本类型为位大小,因为似乎没有理由限制它。

    如果值的任何位是从未定义的位置存储中检索的,或者任何位的偏移量超过了 L 的任何单一位置描述 SL 指定的位置存储 LS 的大小,则这是一个求值错误。

    有关 DW_OP_implicit_pointerDW_OP_LLVM_aspace_implicit_pointer 操作创建的隐式位置描述的特殊规则,请参阅 A.2.5.4.4.5 隐式位置描述操作

  5. DW_OP_xderef 已弃用

    DW_OP_xderef 弹出两个堆栈条目。第一个必须是表示地址 A 的整数类型值。第二个必须是表示目标架构特定地址空间标识符 AS 的整数类型值。

    此操作等效于执行 DW_OP_swap; DW_OP_LLVM_form_aspace_address; DW_OP_deref。检索到的值 V 留在堆栈上,类型为通用类型。

    此操作已弃用,因为可以使用 DW_OP_LLVM_form_aspace_address 操作,并且它提供了更大的表达能力。

  6. DW_OP_xderef_size 已弃用

    DW_OP_xderef_size 具有单个 1 字节的无符号整数常量,表示字节结果大小 S。

    它弹出两个堆栈条目。第一个必须是表示地址 A 的整数类型值。第二个必须是表示目标架构特定地址空间标识符 AS 的整数类型值。

    此操作等效于执行 DW_OP_swap; DW_OP_LLVM_form_aspace_address; DW_OP_deref_size S。检索到的零扩展值 V 留在堆栈上,类型为通用类型。

    此操作已弃用,因为可以使用 DW_OP_LLVM_form_aspace_address 操作,并且它提供了更大的表达能力。

  7. DW_OP_xderef_type 已弃用

    DW_OP_xderef_type 具有两个操作数。第一个是 1 字节的无符号整数常量 S。第二个操作数是无符号 LEB128 整数 DR,它表示相对于当前编译单元的开始处的调试信息条目 D 的字节偏移量,该条目 D 提供了结果值类型 T。

    它弹出两个堆栈条目。第一个必须是表示地址 A 的整数类型值。第二个必须是表示目标架构特定地址空间标识符 AS 的整数类型值。

    此操作等效于执行 DW_OP_swap; DW_OP_LLVM_form_aspace_address; DW_OP_deref_type S DR。检索到的值 V 留在堆栈上,类型为 T。

    此操作已弃用,因为可以使用 DW_OP_LLVM_form_aspace_address 操作,并且它提供了更大的表达能力。

  8. DW_OP_entry_value 已弃用

    DW_OP_entry_value 推送在调用帧的上下文中求值的表达式的值。

    它可用于确定在进入当前调用帧时参数的值,前提是它们没有被破坏。

    它具有两个操作数。第一个是无符号 LEB128 整数 S。第二个是字节块,长度等于 S,被解释为 DWARF 操作表达式 E。

    E 在当前上下文中求值,除非结果类型未指定,调用帧是调用当前帧的帧,程序位置是调用帧中的调用站点,对象未指定,并且初始堆栈为空。调用帧信息通过使用调用帧信息虚拟地展开当前调用帧来获得(请参阅 A.6.4 调用帧信息)。

    如果 E 的结果是位置描述 L(请参阅 A.2.5.4.4.4 寄存器位置描述操作),并且 E 执行的最后一个操作是寄存器 R 的 DW_OP_reg*,其目标架构特定基本类型为 T,则检索寄存器的内容,就像执行了 DW_OP_deref_type DR 操作一样,其中 DR 是当前编译单元中 T 的假设调试信息条目的偏移量。结果值 V 被推送到堆栈上。

    使用 DW_OP_reg* 为子程序的入口处值在寄存器中的情况提供了更紧凑的形式。

    注意

    尚不清楚这如何提供更紧凑的表达式,因为可以使用 DW_OP_regval_type,它只是略大一些。

    如果 E 的结果是值 V,则 V 被推送到堆栈上。

    否则,DWARF 表达式格式不正确。

    DW_OP_entry_value 操作已弃用,因为它的主要用途由其他方式提供。DWARF 版本 5 为调用站点添加了 DW_TAG_call_site_parameter 调试信息条目,该条目具有 DW_AT_call_valueDW_AT_call_data_locationDW_AT_call_data_value 属性,这些属性提供了 DWARF 表达式来计算调用时的实际参数值,并要求生产者确保表达式即使在虚拟展开时也有效。 DW_OP_LLVM_call_frame_entry_reg 操作提供了对虚拟展开的调用帧中寄存器的访问。

    注意

    GDB 仅在 E 恰好是 DW_OP_reg*DW_OP_breg*; DW_OP_deref* 时实现 DW_OP_entry_value

A.2.5.4.4 位置描述操作

本节描述将位置描述推送到堆栈上的操作。

A.2.5.4.4.1 常规位置描述操作

注意

本节替换了 DWARF 版本 5 第 2.5.1.3 节的部分内容。

  1. DW_OP_LLVM_offset 新增

    DW_OP_LLVM_offset 弹出两个堆栈条目。第一个必须是表示字节位移 B 的整数类型值。第二个必须是位置描述 L。

    它将 B 的值乘以 8(字节大小)添加到 L 的每个单一位置描述 SL 的位偏移量,并推送更新后的 L。

    如果任何 SL 的更新位偏移量小于 0 或大于或等于 SL 指定的位置存储的大小,则这是一个求值错误。

  2. DW_OP_LLVM_offset_uconst 新增

    DW_OP_LLVM_offset_uconst 具有单个无符号 LEB128 整数操作数,该操作数表示字节位移 B。

    此操作等效于执行 DW_OP_constu B; DW_OP_LLVM_offset

    提供此操作专门用于能够比使用 DW_OP_lit*; DW_OP_LLVM_offset 可以在两个字节中编码更多的字段位移。

    注意

    这应该命名为 DW_OP_LLVM_offset_uconst 以匹配 DW_OP_plus_uconst,还是 DW_OP_LLVM_offset_constu 以匹配 DW_OP_constu

  3. DW_OP_LLVM_bit_offset 新增

    DW_OP_LLVM_bit_offset 弹出两个堆栈条目。第一个必须是表示位位移 B 的整数类型值。第二个必须是位置描述 L。

    它将 B 的值添加到 L 的每个单一位置描述 SL 的位偏移量,并推送更新后的 L。

    如果任何 SL 的更新位偏移量小于 0 或大于或等于 SL 指定的位置存储的大小,则这是一个求值错误。

  4. DW_OP_push_object_address

    DW_OP_push_object_address 推送当前对象的位置描述 L。

    此对象可能对应于用户提供的表达式中被求值的独立变量。对象位置描述可以从变量自身的调试信息条目中确定,或者它可以是数组、结构体或类的组件,其地址已在用户表达式求值过程的早期步骤中动态确定。

    此操作提供了显式功能(特别是对于涉及描述符的数组),类似于在求值 DW_AT_data_member_location 以访问结构体的数据成员之前,隐式推送结构体的基本位置描述。

    注意

    可以移除此操作,并将对象位置描述指定为初始堆栈,如同 DW_AT_data_member_location 一样。

    或者,可以使用此操作来代替需要指定初始堆栈。后一种方法更具可组合性,因为可能需要在表达式的任何点访问对象,并且将其作为初始堆栈传递需要整个表达式意识到它在堆栈上的位置。如果这样做,DW_AT_use_location 将需要 DW_OP_push_object2_address 操作来处理第二个对象。

    或者,更通用的方法是传入任意数量的参数,并使用诸如 DW_OP_arg N 之类的操作来获取第 N 个参数。然后,参数向量将作为表达式上下文而不是初始堆栈传入。这也可以解决 DW_OP_call* 的问题,允许指定传入和返回的特定数量的参数。DW_OP_call* 操作然后始终可以在单独的堆栈上执行:参数的数量将在新的调用操作中指定,并从调用者的堆栈中获取,类似地,返回结果的数量将被指定,并在被调用表达式完成时从被调用堆栈复制回调用者堆栈。

    唯一指定当前对象的属性是 DW_AT_data_location,因此非规范性文本似乎夸大了它的使用方式。或者,是否还有其他属性需要声明它们传递对象?

  5. DW_OP_LLVM_call_frame_entry_reg 新增

    DW_OP_LLVM_call_frame_entry_reg 具有单个无符号 LEB128 整数操作数,表示目标架构寄存器号 R。

    它推送一个位置描述 L,该描述 L 保存了在调用帧信息定义的当前子程序的入口处寄存器 R 的值(参见 A.6.4 调用帧信息)。

    如果没有定义调用帧信息,则使用目标架构的默认规则。如果寄存器规则为 undefined,则推送未定义的位置描述。如果寄存器规则为 same value,则推送 R 的寄存器位置描述。

A.2.5.4.4.2 未定义位置描述操作

注意

本节替换 DWARF 版本 5 第 2.6.1.1.1 节。

未定义位置存储表示源代码中存在但在目标代码中不存在的对象的一部分或全部(可能是由于优化)。读取或写入未定义位置存储都没有意义。

未定义位置描述指定未定义位置存储。未定义位置存储没有大小的概念,也没有未定义位置描述的位偏移。DW_OP_LLVM_*offset 操作使未定义位置描述保持不变。DW_OP_*piece 操作可以显式或隐式地指定未定义位置描述,允许指定任何大小和偏移,并产生一个所有位都未定义的部件。

  1. DW_OP_LLVM_undefined 新增

    DW_OP_LLVM_undefined 推送一个位置描述 L,该描述 L 包含一个未定义位置描述 SL。

A.2.5.4.4.3 内存位置描述操作

注意

本节替换 DWARF 版本 5 第 2.5.1.1、2.5.1.2、2.5.1.3 和 2.6.1.1.2 节的部分内容。

每个目标架构特定的地址空间都有一个对应的内存位置存储,表示该地址空间的线性可寻址内存。每个内存位置存储的大小对应于相应地址空间中地址的范围。

目标架构定义了地址空间位置存储如何映射到目标架构物理内存。例如,它们可以是独立的内存,或者多个位置存储可以别名相同的物理内存,可能在不同的偏移量和不同的交错方式下。映射也可能由源语言地址类别决定。

内存位置描述指定内存位置存储。位偏移对应于内存字节内的位位置。使用内存位置描述访问位,将访问从位偏移指定的字节内的位位置开始的相应目标架构内存。

位偏移是 8(字节大小)的倍数的内存位置描述被定义为字节地址内存位置描述。它具有一个内存字节地址 A,该地址 A 等于位偏移除以 8。

位偏移不是 8(字节大小)的倍数的内存位置描述被定义为位字段内存位置描述。它具有一个位位置 B,该位置 B 等于位偏移模 8,以及一个内存字节地址 A,该地址 A 等于位偏移减去 B,然后除以 8。

内存位置描述的地址空间 AS 被定义为与内存位置描述关联的内存位置存储相对应的地址空间。

由一个字节地址内存位置描述 SL 组成的位置描述被定义为内存字节地址位置描述。它具有一个字节地址,该地址等于 A,以及一个地址空间,该地址等于相应 SL 的 AS。

DW_ASPACE_LLVM_none 被定义为目标架构默认地址空间。参见 A.2.13 地址空间

如果堆栈条目需要是位置描述,但它是一个具有通用类型的值 V,则它会被隐式转换为具有一个内存位置描述 SL 的位置描述 L。SL 指定与目标架构默认地址空间相对应的内存位置存储,其位偏移等于 V 乘以 8(字节大小)。

注意

如果希望允许将任何整型值隐式转换为目标架构默认地址空间中的内存位置描述

如果堆栈条目需要是位置描述,但它是一个具有整型类型的值 V,则它会被隐式转换为具有一个内存位置描述 SL 的位置描述 L。如果 V 的类型大小小于通用类型大小,则值 V 将被零扩展到通用类型的大小。最低有效通用类型大小位被视为无符号值,用作地址 A。SL 指定与目标架构默认地址空间相对应的内存位置存储,其位偏移等于 A 乘以 8(字节大小)。

隐式转换也可以定义为目标架构特定的。例如,GDB 检查 V 是否为整型类型。如果不是,则会给出错误。否则,GDB 将 V 零扩展为 64 位。如果 GDB 目标定义了一个钩子函数,则会调用它。目标特定的钩子函数可以修改 64 位值,可能根据原始值类型进行符号扩展。最后,GDB 将 64 位值 V 视为内存位置地址。

如果堆栈条目需要是位置描述,但它是一个具有目标架构默认地址空间的隐式指针值 IPV,则它会被隐式转换为位置描述,该位置描述由 IPV 指定的单个位置描述组成。参见 A.2.5.4.4.5 隐式位置描述操作

注意

DWARF 版本 5 的向后兼容性是否需要此规则?如果不需要,则可以消除它,并且生产者可以使用 DW_OP_LLVM_form_aspace_address

如果堆栈条目需要是值,但它是一个位置描述 L,该位置描述 L 具有目标架构默认地址空间中位偏移 B 是 8 的倍数的内存位置描述 SL,则它会被隐式转换为一个值,该值等于 B 除以 8(字节大小),并且具有通用类型。

  1. DW_OP_addr

    DW_OP_addr 具有单个字节常量值操作数,其大小为通用类型的大小,表示地址 A。

    它在堆栈上推送一个位置描述 L,该位置描述 L 具有一个内存位置描述 SL。SL 指定与目标架构默认地址空间相对应的内存位置存储,其位偏移等于 A 乘以 8(字节大小)。

    如果 DWARF 是代码对象的一部分,则 A 可能需要重定位。例如,在 ELF 代码对象格式中,A 必须通过 ELF 段虚拟地址与段加载时的虚拟地址之差进行调整。

  2. DW_OP_addrx

    DW_OP_addrx 具有单个无符号 LEB128 整数操作数,表示相对于关联编译单元的 DW_AT_addr_base 属性值的 .debug_addr 节中的从零开始的索引。 .debug_addr 节中的地址值 A 的大小为通用类型的大小。

    它在堆栈上推送一个位置描述 L,该位置描述 L 具有一个内存位置描述 SL。SL 指定与目标架构默认地址空间相对应的内存位置存储,其位偏移等于 A 乘以 8(字节大小)。

    如果 DWARF 是代码对象的一部分,则 A 可能需要重定位。例如,在 ELF 代码对象格式中,A 必须通过 ELF 段虚拟地址与段加载时的虚拟地址之差进行调整。

  3. DW_OP_LLVM_form_aspace_address 新增

    DW_OP_LLVM_form_aspace_address 弹出堆栈顶部的两个条目。第一个必须是表示目标架构特定地址空间标识符 AS 的整型值。第二个必须是表示地址 A 的整型值。

    地址大小 S 定义为与 AS 相对应的目标架构特定地址空间的地址位大小。

    A 被调整为 S 位,必要时通过零扩展,然后将最低有效 S 位视为无符号值 A’。

    它在堆栈上推送一个位置描述 L,该位置描述 L 具有一个内存位置描述 SL。SL 指定与 AS 相对应的内存位置存储 LS,其位偏移等于 A’ 乘以 8(字节大小)。

    如果 AS 是特定于上下文元素的地址空间,则 LS 对应于与当前上下文关联的位置存储。

    例如,如果 AS 用于每个线程的存储,则 LS 是当前线程的位置存储。对于使用 SIMT 执行模型实现的语言,如果 AS 用于每个 lane 的存储,则 LS 是当前线程的当前 lane 的位置存储。因此,如果 L 被操作访问,则访问的是创建位置描述时选择的位置存储,而不是与访问操作的当前上下文关联的位置存储。

    如果 AS 不是目标架构特定的 DW_ASPACE_LLVM_* 值之一,则 DWARF 表达式格式不正确。

    有关通过解引用 DW_OP_implicit_pointerDW_OP_LLVM_aspace_implicit_pointer 操作创建的隐式位置描述而产生的隐式指针值的特殊规则,请参见 A.2.5.4.4.5 隐式位置描述操作

  4. DW_OP_form_tls_address

    DW_OP_form_tls_address 弹出一个堆栈条目,该条目必须是整型值,并将其视为线程本地存储地址 TA。

    它在堆栈上推送一个位置描述 L,该位置描述 L 具有一个内存位置描述 SL。SL 是目标架构特定的内存位置描述,对应于线程本地存储地址 TA。

    线程本地存储地址 TA 的含义由运行时环境定义。如果运行时环境支持单个线程的多个线程本地存储块,则使用与包含此 DWARF 表达式的可执行文件或共享库相对应的块。

    C、C++、Fortran 和其他语言的一些实现支持线程本地存储类。具有此存储类的变量在不同的线程中具有不同的值和地址,就像自动变量在每个子程序调用中具有不同的值和地址一样。通常,有一个包含主可执行文件中声明的所有线程本地变量的单个存储块,以及每个共享库中声明的变量的单独块。然后可以使用标识符在其块中访问每个线程本地变量。此标识符通常是块中的字节偏移量,并在 DW_OP_form_tls_address 操作之前由 DW_OP_const* 操作之一推送到 DWARF 堆栈上。计算适当块的地址可能很复杂(在某些情况下,编译器会发出函数调用来执行此操作),并且难以使用普通的 DWARF 位置描述来描述。DW_OP_form_tls_address 允许使用者根据目标架构特定的运行时环境执行计算,而不是将复杂的线程本地存储计算强制到 DWARF 表达式中。

  5. DW_OP_call_frame_cfa

    DW_OP_call_frame_cfa 将当前子程序的规范帧地址 (CFA) 的位置描述 L 从调用帧信息推送到堆栈上。参见 A.6.4 调用帧信息

    尽管可以使用位置列表表达式计算与当前子程序对应的调试信息条目的 DW_AT_frame_base 属性的值,但在某些情况下,这将需要广泛的位置列表,因为用于计算 CFA 的寄存器的值在子程序执行期间会发生变化。如果调用帧信息存在,则它已经编码了这些更改,并且使用 DW_OP_call_frame_cfa 操作引用它是节省空间的。

  6. DW_OP_fbreg

    DW_OP_fbreg 具有单个有符号 LEB128 整数操作数,表示字节位移 B。

    当前子程序的帧基址的位置描述 L 从与当前子程序对应的调试信息条目的 DW_AT_frame_base 属性中获得,如 A.3.3.5 低级信息 中所述。

    位置描述 L 被更新,就好像应用了 DW_OP_LLVM_offset_uconst B 操作一样。更新后的 L 被推送到堆栈上。

  7. DW_OP_breg0DW_OP_breg1、…、DW_OP_breg31

    DW_OP_breg<N> 操作编码最多 32 个寄存器的编号,编号从 0 到 31(包括 0 和 31)。寄存器号 R 对应于操作名称中的 N。

    它们具有单个有符号 LEB128 整数操作数,表示字节位移 B。

    地址空间标识符 AS 定义为与目标架构特定默认地址空间相对应的标识符。

    地址大小 S 定义为与 AS 相对应的目标架构特定地址空间的地址位大小。

    检索由 R 指定的寄存器的内容,就好像执行了 DW_OP_regval_type R, DR 操作一样,其中 DR 是当前编译单元中大小为 S 位的无符号整型基本类型的假设调试信息条目的偏移量。B 被添加,并且最低有效 S 位被视为无符号值,用作地址 A。

    它们在堆栈上推送一个位置描述 L,该位置描述 L 包含一个内存位置描述 LS。LS 指定与 AS 相对应的内存位置存储,其位偏移等于 A 乘以 8(字节大小)。

  8. DW_OP_bregx

    DW_OP_bregx 具有两个操作数。第一个是无符号 LEB128 整数,表示寄存器号 R。第二个是有符号 LEB128 整数,表示字节位移 B。

    该操作与 DW_OP_breg<N> 的操作相同,不同之处在于 R 用作寄存器号,B 用作字节位移。

  9. DW_OP_LLVM_aspace_bregx 新增

    DW_OP_LLVM_aspace_bregx 具有两个操作数。第一个是无符号 LEB128 整数,表示寄存器号 R。第二个是有符号 LEB128 整数,表示字节位移 B。它弹出一个堆栈条目,该条目必须是表示目标架构特定地址空间标识符 AS 的整型值。

    该操作与 DW_OP_breg<N> 的操作相同,不同之处在于 R 用作寄存器号,B 用作字节位移,AS 用作地址空间标识符。

    如果 AS 不是目标架构特定的 DW_ASPACE_LLVM_* 值之一,则 DWARF 表达式格式不正确。

    注意

    也可以考虑添加 DW_OP_LLVM_aspace_breg0, DW_OP_LLVM_aspace_breg1, ..., DW_OP_LLVM_aspace_breg31,这将节省编码大小。

A.2.5.4.4.4 寄存器位置描述操作

注意

本节替换 DWARF 版本 5 第 2.6.1.1.3 节。

存在一个寄存器位置存储,对应于每个目标架构寄存器。每个寄存器位置存储的大小对应于相应目标架构寄存器的大小。

寄存器位置描述指定寄存器位置存储。位偏移对应于寄存器内的位位置。使用寄存器位置描述访问位,将访问从指定的位偏移开始的相应目标架构寄存器。

  1. DW_OP_reg0DW_OP_reg1、…、DW_OP_reg31

    DW_OP_reg<N> 操作编码最多 32 个寄存器的编号,编号从 0 到 31(包括 0 和 31)。目标架构寄存器号 R 对应于操作名称中的 N。

    该操作等效于执行 DW_OP_regx R

  2. DW_OP_regx

    DW_OP_regx 具有单个无符号 LEB128 整数操作数,表示目标架构寄存器号 R。

    如果当前调用帧是顶部调用帧,则它推送一个位置描述 L,该位置描述 L 在堆栈上指定一个寄存器位置描述 SL。SL 指定与 R 相对应的寄存器位置存储,对于当前线程,位偏移为 0。

    如果当前调用帧不是顶部调用帧,则调用帧信息(参见 A.6.4 调用帧信息)用于确定位置描述,该位置描述保存当前线程的当前调用帧和当前程序位置的寄存器。推送生成的位置描述 L。

    请注意,如果使用调用帧信息,则生成的位置描述可能是寄存器、内存或未定义的。

    实现可以立即评估调用帧信息,或者可以延迟评估,直到位置描述 L 被操作访问。如果评估被延迟,则 R 和当前上下文可以记录在 L 中。当访问时,记录的上下文用于评估调用帧信息,而不是访问操作的当前上下文。

这些操作获取寄存器位置。要获取寄存器的内容,必须使用 DW_OP_regval_type,使用 DW_OP_breg* 基于寄存器的寻址操作之一,或者在寄存器位置描述上使用 DW_OP_deref*

A.2.5.4.4.5 隐式位置描述操作

注意

本节替换 DWARF 版本 5 第 2.6.1.1.4 节。

隐式位置存储表示对象的一部分或全部,该对象在程序中没有实际位置,但其内容仍然是已知的,要么作为常量,要么可以从程序中的其他位置和值计算得出。

隐式位置描述指定隐式位置存储。位偏移对应于隐式位置存储内的位位置。使用隐式位置描述访问位,将访问从位偏移开始的相应隐式存储值。

  1. DW_OP_implicit_value

    DW_OP_implicit_value 具有两个操作数。第一个是无符号 LEB128 整数,表示字节大小 S。第二个是字节块,其长度等于 S,被视为字面值 V。

    创建一个隐式位置存储 LS,其字面值 V 和大小为 S。

    它在堆栈上推送位置描述 L,该位置描述 L 具有一个隐式位置描述 SL。SL 指定 LS,位偏移为 0。

  2. DW_OP_stack_value

    DW_OP_stack_value 弹出一个堆栈条目,该条目必须是值 V。

    创建一个隐式位置存储 LS,其字面值 V 使用 V 的基本类型指定的大小、编码和字节序。

    它在堆栈上推送位置描述 L,该位置描述 L 具有一个隐式位置描述 SL。SL 指定 LS,位偏移为 0。

    DW_OP_stack_value 操作指定对象不存在于内存中,但其值仍然是已知的。在这种形式中,位置描述指定对象的实际值,而不是指定保存值的内存或寄存器存储。

    有关通过解引用 DW_OP_implicit_pointerDW_OP_LLVM_aspace_implicit_pointer 操作创建的隐式位置描述而产生的隐式指针值的特殊规则,请参见 DW_OP_implicit_pointer (下文)。

    注意:由于位置描述允许在堆栈上,因此 DW_OP_stack_value 操作不再像 DWARF 版本 5 中那样终止 DWARF 操作表达式的执行。

  3. DW_OP_implicit_pointer

    优化编译器可能会消除指针,同时仍然保留指针寻址的值。 DW_OP_implicit_pointer 允许生产者描述此值。

    DW_OP_implicit_pointer 指定对象是指向目标架构默认地址空间的指针,即使它将指向的值可以描述,也无法表示为真实指针。在这种形式中,位置描述指定调试信息条目,该条目表示指针将指向的对象的实际位置描述。因此,调试信息的消费者将能够访问解引用的指针,即使它无法访问指针本身。

    DW_OP_implicit_pointer 具有两个操作数。第一个操作数是以 32 位 DWARF 格式的 4 字节无符号值,或以 64 位 DWARF 格式的 8 字节无符号值,表示调试信息条目 D 相对于包含当前编译单元的 .debug_info 节的开头的字节偏移量 DR。第二个操作数是有符号 LEB128 整数,表示字节位移 B。

    请注意,D 可能不在当前编译单元中。

    第一个操作数的解释与 DW_FORM_ref_addr 完全相同。

    地址空间标识符 AS 定义为与目标架构特定默认地址空间相对应的标识符。

    地址大小 S 定义为与 AS 相对应的目标架构特定地址空间的地址位大小。

    创建一个隐式位置存储 LS,其调试信息条目 D、地址空间 AS 和大小为 S。

    它推送一个位置描述 L,该位置描述 L 包含堆栈上的一个隐式位置描述 SL。SL 指定 LS,位偏移为 0。

    如果 DW_OP_deref* 操作弹出一个位置描述 L’,并检索 S 位,使得任何检索到的位都来自与 LS 相同的隐式位置存储,则会出现评估错误,除非同时满足以下条件

    1. 所有检索到的位都来自引用与 LS 相同的隐式位置存储的隐式位置描述。

      请注意,所有位不必来自同一个隐式位置描述,因为 L’ 可能涉及复合位置描述。

    2. 位来自其各自隐式位置存储内的连续升序偏移量。

    这些规则等效于检索 LS 的完整内容。

    如果满足上述两个条件,则 DW_OP_deref* 操作推送的值 V 是一个隐式指针值 IPV,其目标架构特定地址空间为 AS,调试信息条目为 D,基本类型为 T。如果 AS 是目标架构默认地址空间,则 T 是通用类型。否则,T 是目标架构特定的整型类型,其位大小等于 S。

    如果 IPV 要么隐式转换为位置描述(仅当 AS 是目标架构默认地址空间时才执行),要么被 DW_OP_LLVM_form_aspace_address 使用(仅当 DW_OP_LLVM_form_aspace_address 弹出的地址空间是 AS 时才执行),则生成的位置描述 RL 为

    • 如果 D 具有 DW_AT_location 属性,则 DW_AT_location 属性中的 DWARF 表达式 E 将在当前上下文中求值,但结果类型是位置描述,编译单元是包含 D 的编译单元,对象未指定,初始堆栈为空。RL 是表达式结果。

      请注意,E 是在访问 IPV 的表达式的上下文中求值的,而不是在包含创建 L 的 DW_OP_implicit_pointer DW_OP_LLVM_aspace_implicit_pointer 操作的表达式的上下文中求值的。

    • 如果 D 具有 DW_AT_const_value 属性,则根据 DW_AT_const_value 属性的值创建一个隐式位置存储 RLS,其大小与 DW_AT_const_value 属性的值的大小匹配。RL 包含一个隐式位置描述 SRL。SRL 指定 RLS,位偏移为 0。

      注意

      如果使用 DW_AT_const_value 用于变量和形式参数已被弃用,并且改为将 DW_AT_location 与隐式位置描述一起使用,则此规则将不是必需的。

    • 否则,将出现评估错误。

    RL 的位偏移被更新,就好像应用了 DW_OP_LLVM_offset_uconst B 操作一样。

    如果 DW_OP_stack_value 操作弹出一个与 IPV 相同的值,则它推送一个与 L 相同的位置描述。

    如果以任何其他方式访问 LS 或 IPV,则会出现评估错误。

    DW_OP_implicit_pointer DW_OP_LLVM_aspace_implicit_pointer 创建的隐式指针位置描述的使用限制,旨在简化 DWARF 消费者。对于由 DW_OP_deref* DW_OP_stack_value 创建的隐式指针值也是如此。

  4. DW_OP_LLVM_aspace_implicit_pointer 新增

    DW_OP_LLVM_aspace_implicit_pointer 有两个操作数,与 DW_OP_implicit_pointer 的操作数相同。

    它弹出一个堆栈条目,该条目必须是表示目标架构特定地址空间标识符 AS 的整数类型值。

    压入堆栈的位置描述 L 与 DW_OP_implicit_pointer 的位置描述相同,不同之处在于使用的地址空间标识符是 AS。

    如果 AS 不是目标架构特定的 DW_ASPACE_LLVM_* 值之一,则 DWARF 表达式格式不正确。

    注意

    当完全支持地址类(如 OpenCL/SyCL 等语言所需)时,DW_OP_LLVM_aspace_implicit_pointer 的定义可能会更改。

通常,DW_OP_implicit_pointer DW_OP_LLVM_aspace_implicit_pointer 操作用于 DW_TAG_variable DW_TAG_formal_parameter 调试信息条目 D1 DW_AT_location 属性的 DWARF 表达式 E1 中。由 DW_OP_implicit_pointer DW_OP_LLVM_aspace_implicit_pointer 操作引用的调试信息条目通常本身是 DW_TAG_variable DW_TAG_formal_parameter 调试信息条目 D2,其 DW_AT_location 属性给出了第二个 DWARF 表达式 E2

D1 和 E1 描述的是指针类型对象的位置。D2 和 E2 描述的是该指针对象指向的对象的位置。

但是,D2 可以是任何包含 DW_AT_location DW_AT_const_value 属性的调试信息条目(例如,DW_TAG_dwarf_procedure)。通过使用 E2,当消费者被要求解引用 E1 描述的指针(其中包含 DW_OP_implicit_pointer DW_OP_LLVM_aspace_implicit_pointer 操作)时,消费者可以重建对象的值。

A.2.5.4.4.6 复合位置描述操作

注意

本节替换 DWARF 版本 5 第 2.6.1.2 节。

复合位置存储表示一个对象或值,该对象或值可能包含在另一个位置存储的一部分中,或者包含在多个位置存储的部分中。

每个部分都有一个部分位置描述 L 和一个部分位大小 S。L 可以有一个或多个单个位置描述 SL。如果有多个 SL,则表示该部分位于多个位置。该部分的每个位置的位由 SL 指定的位置存储 LS 中的 S 个连续位组成,从 SL 指定的位偏移量开始。所有位都必须在 LS 的大小范围内,否则 DWARF 表达式格式不正确。

一个复合位置存储可以有零个或多个部分。这些部分是连续的,因此从零开始的位置存储位索引将在每个部分上变化,它们之间没有间隙。因此,复合位置存储的大小是其各部分大小的总和。如果连续位置存储的大小大于与最大目标架构特定地址空间对应的内存位置存储的大小,则 DWARF 表达式格式不正确。

复合位置描述指定一个复合位置存储。位偏移量对应于复合位置存储内的位位置。

有一些操作可以创建复合位置存储。

还有其他操作允许增量创建复合位置存储。每个部分由单独的操作创建。可能有一个或多个操作来创建最终的复合位置存储。一系列这样的操作描述了复合位置存储的各个部分,这些部分的顺序与关联的部分操作的执行顺序相同。

为了支持增量创建,复合位置存储可以处于不完整状态。当增量操作对不完整的复合位置存储进行操作时,它会添加一个新部分,否则它会创建一个新的复合位置存储。DW_OP_LLVM_piece_end 操作显式地使不完整的复合位置存储变为完整。

指定不完整复合位置存储的复合位置描述称为不完整复合位置描述。指定完整复合位置存储的复合位置描述称为完整复合位置描述。

如果堆栈顶部条目是一个位置描述 L,在操作表达式执行完成后,L 具有一个不完整的复合位置描述 SL,则 SL 将转换为完整的复合位置描述。

请注意,在 DW_OP_call* 操作在同一堆栈上评估操作表达式完成后,不会发生此转换。此类执行不是对操作表达式的单独评估,而是包含 DW_OP_call* 操作的同一操作表达式的持续评估。

如果堆栈条目需要是位置描述 L,但 L 具有不完整的复合位置描述,则 DWARF 表达式格式不正确。例外情况是用于增量创建复合位置描述的操作,如下所述。

请注意,DWARF 操作表达式可以任意地从任何其他位置描述(包括具有多个单个位置描述的位置描述和具有复合位置描述的位置描述)组合复合位置描述。

增量复合位置描述操作被定义为与 DWARF 版本 5 中的定义兼容。

  1. DW_OP_piece

    DW_OP_piece 有一个无符号 LEB128 整数,表示字节大小 S。

    操作基于上下文

    • 如果堆栈为空,则将由一个不完整的复合位置描述 SL 组成的位置描述 L 压入堆栈。

      创建一个不完整的复合位置存储 LS,其中包含一个部分 P。P 指定位置描述 PL,位大小为 S 乘以 8(字节大小)。PL 由一个未定义的位置描述 PSL 组成。

      SL 指定 LS,位偏移量为 0。

    • 否则,如果堆栈顶部条目是由一个不完整的复合位置描述 SL 组成的位置描述 L,则 SL 指定的不完整复合位置存储 LS 将被更新以追加一个新部分 P。P 指定位置描述 PL,位大小为 S 乘以 8(字节大小)。L 留在堆栈上。

    • 否则,如果堆栈顶部条目是一个位置描述或可以转换为位置描述,则将其弹出并视为部分位置描述 PL。然后

      • 如果堆栈顶部条目(弹出 PL 后)是由一个不完整的复合位置描述 SL 组成的位置描述 L,则 SL 指定的不完整复合位置存储 LS 将被更新以追加一个新部分 P。P 指定位置描述 PL,位大小为 S 乘以 8(字节大小)。L 留在堆栈上。

      • 否则,将由一个不完整的复合位置描述 SL 组成的位置描述 L 压入堆栈。

        创建一个不完整的复合位置存储 LS,其中包含一个部分 P。P 指定位置描述 PL,位大小为 S 乘以 8(字节大小)。

        SL 指定 LS,位偏移量为 0。

    • 否则,DWARF 表达式格式不正确

    许多编译器将单个变量存储在寄存器集中,或将变量部分存储在内存中,部分存储在寄存器中。 DW_OP_piece 提供了一种描述变量部分位置的方法。

    如果需要非 0 字节位移,则可以使用 DW_OP_LLVM_offset 操作更新位置描述,然后再将其用作 DW_OP_piece 操作的部分位置描述。

    DW_OP_piece 操作的评估规则使其与 DWARF 版本 5 的定义兼容。

    注意

    由于这些扩展允许位置描述作为堆栈上的条目,因此可以定义一个更简单的操作来创建复合位置描述。例如,只需一个操作来指定有多少个部分,并弹出堆栈条目对以获取部分大小和位置描述。这不仅会是一个更简单的操作,并避免不完整的复合位置描述的复杂性,而且在实践中也可能具有更小的编码。但是,与 DWARF 版本 5 兼容的愿望可能是一个更强的考虑因素。

  2. DW_OP_bit_piece

    DW_OP_bit_piece 有两个操作数。第一个是无符号 LEB128 整数,表示部分位大小 S。第二个是无符号 LEB128 整数,表示位位移 B。

    操作与 DW_OP_piece 相同,不同之处在于,创建的任何部分都具有位大小 S,并且创建的任何部分的位置描述 PL 都被更新,就好像应用了 DW_OP_constu B; DW_OP_LLVM_bit_offset 操作一样。

    当要组装的片段不是字节大小的或不在部分位置描述的开头时,使用 DW_OP_bit_piece 而不是 DW_OP_piece

    如果需要计算的位位移,则可以使用 DW_OP_LLVM_bit_offset 操作更新位置描述,然后再将其用作 DW_OP_bit_piece 操作的部分位置描述。

    注意

    不需要位偏移操作数,因为可以在部分的位置描述上使用 DW_OP_LLVM_bit_offset

  3. DW_OP_LLVM_piece_end 新增

    如果堆栈顶部条目不是由一个不完整的复合位置描述 SL 组成的位置描述 L,则 DWARF 表达式格式不正确。

    否则,SL 指定的不完整复合位置存储 LS 将更新为具有相同部分的完整复合位置描述。

  4. DW_OP_LLVM_extend 新增

    DW_OP_LLVM_extend 有两个操作数。第一个是无符号 LEB128 整数,表示元素位大小 S。第二个是无符号 LEB128 整数,表示计数 C。

    它弹出一个堆栈条目,该条目必须是位置描述,并被视为部分位置描述 PL。

    将由一个完整的复合位置描述 SL 组成的位置描述 L 压入堆栈。

    创建一个完整的复合位置存储 LS,其中包含 C 个相同的部分 P。每个 P 指定 PL,位大小为 S。

    SL 指定 LS,位偏移量为 0。

    如果元素位大小或计数为 0,则 DWARF 表达式格式不正确。

  5. DW_OP_LLVM_select_bit_piece 新增

    DW_OP_LLVM_select_bit_piece 有两个操作数。第一个是无符号 LEB128 整数,表示元素位大小 S。第二个是无符号 LEB128 整数,表示计数 C。

    它弹出三个堆栈条目。第一个必须是表示位掩码值 M 的整数类型值。第二个必须是表示单位置描述 L1 的位置描述。第三个必须是表示零位置描述 L0 的位置描述。

    创建一个完整的复合位置存储 LS,其中包含 C 个部分 PN,按 N 从 0 到 C-1 升序排列。每个 PN 指定位置描述 PLN,位大小为 S。

    PLN 就好像对 PLXN 应用了 DW_OP_LLVM_bit_offset N*S 操作一样。

    如果 M 的第 N 个最低有效位为零,则 PLXN 与 L0 相同,否则与 L1 相同。

    将由一个完整的复合位置描述 SL 组成的位置描述 L 压入堆栈。SL 指定 LS,位偏移量为 0。

    如果 S 或 C 为 0,或者 M 的位大小小于 C,则 DWARF 表达式格式不正确。

    注意

    DW_OP_extend 和 DW_OP_select_bit_piece 的计数操作数是否应该更改为从堆栈中获取计数值?这将允许支持具有可变长度向量指令的架构,例如 ARM 和 RISC-V。

  6. DW_OP_LLVM_overlay 新增

    DW_OP_LLVM_overlay 弹出四个堆栈条目。第一个必须是表示覆盖字节大小值 S 的整数类型值。第二个必须是表示覆盖字节偏移值 O 的整数类型值。第三个必须是表示覆盖位置描述 OL 的位置描述。第四个必须是表示基本位置描述 BL 的位置描述。

    操作与 DW_OP_LLVM_bit_overlay 相同,不同之处在于使用的覆盖位大小 BS 和覆盖位偏移 BO 分别是 S 和 O 乘以 8(字节大小)。

  7. DW_OP_LLVM_bit_overlay 新增

    DW_OP_LLVM_bit_overlay 弹出四个堆栈条目。第一个必须是表示覆盖位大小值 BS 的整数类型值。第二个必须是表示覆盖位偏移值 BO 的整数类型值。第三个必须是表示覆盖位置描述 OL 的位置描述。第四个必须是表示基本位置描述 BL 的位置描述。

    如果 BS 或 BO 为负值,则 DWARF 表达式格式不正确。

    rbss(L) 是 L 的最小剩余位存储大小,定义如下。LS 是位置存储,LO 是 L 的单个位置描述 SL 指定的位置位偏移量。SL 的剩余位存储大小 RBSS 是 LS 的位大小减去 LO。rbss(L) 是 L 的每个单个位置描述 SL 的最小 RBSS。

    如果 rbss(BL) 小于 BO 加 BS,则 DWARF 表达式格式不正确。

    如果 BS 为 0,则操作压入 BL。

    如果 BO 为 0 且 BS 等于 rbss(BL),则操作压入 OL。

    否则,该操作等效于执行以下步骤来压入复合位置描述。

    复合位置描述在概念上是基本位置描述 BL,覆盖位置描述 OL 作为覆盖放置在 BL 之上,从覆盖偏移量 BO 开始,覆盖位大小 BS。

    1. 如果 BO 不为 0,则压入 BL,然后执行 DW_OP_bit_piece BO, 0 操作。

    2. 压入 OL,然后执行 DW_OP_bit_piece BS, 0 操作。

    3. 如果 rbss(BL) 大于 BO 加 BS,则压入 BL,然后执行 DW_OP_bit_piece (rbss(BL) - BO - BS), (BO + BS) 操作。

    4. 执行 DW_OP_LLVM_piece_end 操作。

A.2.5.5 DWARF 位置列表表达式

注意

本节替换 DWARF 版本 5 第 2.6.2 节。

为了满足最新计算机架构和优化技术的需求,调试信息必须能够描述对象的位置,该对象的位置在其生命周期内会发生变化,并且可能在对象生命周期的某些部分驻留在多个位置。位置列表表达式用于代替操作表达式,只要要描述其位置的对象具有这些要求。

位置列表表达式由一系列位置列表条目组成。每个位置列表条目都是以下类型之一

有界位置描述

这种类型的位置列表条目提供了一个操作表达式,该表达式评估为在由起始地址和结束地址限定的生命周期内有效的对象的位置描述。起始地址是位置有效的地址范围的最低地址。结束地址是地址范围的最高地址之后的第一个位置的地址。

当当前程序位置在给定范围内时,位置列表条目匹配。

有几种类型的有界位置描述条目,它们在指定起始地址和结束地址的方式上有所不同。

默认位置描述

这种类型的位置列表条目提供了一个操作表达式,该表达式评估为当没有有界位置描述条目适用时有效的对象的位置描述。

当当前程序位置不在任何有界位置描述条目的范围内时,位置列表条目匹配。

基地址

这种类型的位置列表条目提供了一个地址,该地址将用作某些类型的有界位置描述条目中给出的起始和结束地址偏移量的基地址。有界位置描述条目的适用基地址是同一位置列表中最接近的前一个基地址条目指定的地址。如果没有前一个基地址条目,则适用基地址默认为编译单元的基地址(请参阅 DWARF 版本 5 第 3.1.1 节)。

在编译单元的所有机器代码都包含在单个连续节中的情况下,不需要基地址条目。

列表结束

这种类型的位置列表条目标记位置列表表达式的结束。

位置列表表达式的有界位置描述条目定义的地址范围可能会重叠。当它们重叠时,它们描述了一种情况,其中对象同时存在于多个位置。

如果给定位置列表表达式中的所有地址范围没有共同覆盖对象被定义的整个范围,并且没有后续的默认位置描述条目,则假定该对象在未覆盖范围的部分中不可用。

DWARF 位置列表表达式的评估结果是

  • 如果未指定当前程序位置,则为评估错误。

    注意

    如果位置列表仅具有单个默认条目,那么在没有程序位置的情况下,是否应将其视为匹配项? 如果存在非默认条目,那么当没有程序位置时,似乎必须是评估错误,因为这表明位置取决于程序位置,而程序位置是未知的。

  • 如果没有匹配的位置列表条目,则结果是由一个未定义的位置描述组成的位置描述。

  • 否则,每个匹配的位置列表条目的操作表达式 E 都使用当前上下文进行评估,不同之处在于结果类型是位置描述,对象未指定,初始堆栈为空。位置列表条目结果是评估 E 返回的位置描述。

    结果是由每个匹配的位置列表条目的位置描述结果的单个位置描述的并集组成的位置描述。

位置列表表达式只能用作使用类 loclistloclistsptr 编码的调试器信息条目属性的值(请参阅 A.7.5.5 类和形式)。属性的值提供了进入名为 .debug_loclists.debug_loclists.dwo (对于拆分 DWARF 对象文件)的单独对象文件节的索引,该节包含位置列表条目。

DW_OP_call*DW_OP_implicit_pointer 操作可用于指定具有位置列表表达式的调试器信息条目属性。多个调试器信息条目属性允许 DWARF 表达式,这些表达式使用初始堆栈进行评估,该初始堆栈包括可能源自位置列表表达式评估的位置描述。

此位置列表表示形式、loclist loclistsptr 类以及相关的 DW_AT_loclists_base 属性在 DWARF 版本 5 中是新增的。它们共同消除了位置列表表达式先前所需的大部分或全部代码对象重定位。

注意

本节的其余部分与 DWARF 版本 5 第 2.6.2 节相同。

A.2.13 地址空间

注意

这是 DWARF 版本 5 第 2.12 节分段地址之后的新增章节。

DWARF 地址空间对应于目标架构特定的线性可寻址内存区域。它们在 DWARF 表达式位置描述中用于描述数据驻留在哪个目标架构特定内存区域中。

目标架构特定的 DWARF 地址空间可能对应于硬件支持的设施,例如利用基地址寄存器的内存、暂存内存和具有特殊交错的内存。这些地址空间中地址的大小可能会有所不同。它们的访问和分配可能由硬件管理,每个线程或线程组都可以访问独立的存储。由于这些原因,它们可能具有不允许将它们视为所有线程都可访问的统一全局虚拟地址空间一部分的属性。

是否支持多个 DWARF 地址空间以及源语言内存空间如何映射到目标架构特定的 DWARF 地址空间是目标架构特定的。目标架构可以将多个源语言内存空间映射到相同的目标架构特定的 DWARF 地址类。优化可能会确定变量生命周期和访问模式允许将它们分配到更快的暂存内存中,该暂存内存由与源语言内存空间的默认值不同的 DWARF 地址空间表示。

尽管 DWARF 地址空间标识符是目标架构特定的,但 DW_ASPACE_LLVM_none 是所有目标架构都支持的通用地址空间,并定义为目标架构默认地址空间。

DWARF 地址空间标识符由以下项使用

  • DW_AT_LLVM_address_space 属性。

  • DWARF 表达式操作:DW_OP_aspace_bregxDW_OP_form_aspace_addressDW_OP_aspace_implicit_pointerDW_OP_xderef*

  • CFI 指令:DW_CFA_def_aspace_cfaDW_CFA_def_aspace_cfa_sf

注意

目前,DWARF 将地址类值定义为目标架构特定的,并定义了 DW_AT_address_class 属性。随着 DWARF 6 中 DW_AT_segment 的移除,目前尚不清楚地址类打算如何使用,因为该术语在其他地方未使用。这些应该被此提案中更完整的地址空间所取代吗?还是它们旨在表示源语言内存空间,例如 OpenCL 中的内存空间?

A.2.14 内存空间

注意

这是 DWARF 版本 5 第 2.12 节分段地址之后的新增章节。

DWARF 内存空间用于具有内存空间概念的源语言。它们用于指针类型、引用类型、变量、形式参数和常量调试信息条目的 DW_AT_LLVM_memory_space 属性中。

每个 DWARF 内存空间在概念上都是一个单独的源语言内存空间,具有自己的生命周期和别名规则。DWARF 内存空间用于指定指针类型和引用类型值引用的源语言内存空间,以及指定变量分配在哪个源语言内存空间中。

尽管 DWARF 内存空间标识符是源语言特定的,但 DW_MSPACE_LLVM_none 是所有源语言都支持的通用内存空间,并定义为源语言默认内存空间。

当前定义的 DWARF 内存空间集以及源语言映射在 源语言内存空间 中给出。

供应商定义的源语言内存空间可以使用 DW_MSPACE_LLVM_lo_userDW_MSPACE_LLVM_hi_user 范围内的代码定义。

表 6 源语言内存空间

内存空间名称

含义

C/C++

OpenCL

CUDA/HIP

DW_MSPACE_LLVM_none

通用 (generic)

默认 (default)

通用 (generic)

默认 (default)

DW_MSPACE_LLVM_global

全局 (global)

全局 (global)

DW_MSPACE_LLVM_constant

常量 (constant)

常量 (constant)

常量 (constant)

DW_MSPACE_LLVM_group

线程组 (thread-group)

本地 (local)

共享 (shared)

DW_MSPACE_LLVM_private

线程 (thread)

私有 (private)

DW_MSPACE_LLVM_lo_user

DW_MSPACE_LLVM_hi_user

注意

源语言内存空间 中提出的方法是将默认的 DW_MSPACE_LLVM_none 定义为通用地址类,而不是全局地址类。这与 CLANG 和 LLVM 如何在现有 C++ 语言支持的基础上添加对类似 CUDA 语言的支持相匹配。这允许所有地址默认为通用地址,这与类似 CUDA 的语言相匹配。

另一种方法是将 DW_MSPACE_LLVM_none 定义为全局内存空间,然后将 DW_MSPACE_LLVM_global 更改为 DW_MSPACE_LLVM_generic。这将符合实际情况,即不支持多个内存空间的语言只有一个默认的全局内存空间。通常,在这些语言中,如果它们公开了目标架构支持多个内存空间,则默认的仍然是全局内存空间。然后,支持多个内存空间的语言必须显式指示哪些指针具有引用超出全局内存空间的能力。但是,为类似 CUDA 的语言生成 DWARF 的编译器随后必须使用 DW_MSPACE_LLVM_genericDW_AT_LLVM_memory_space 属性定义每个类似 CUDA 的语言指针类型或引用类型,以匹配语言语义。

A.3 程序作用域条目

注意

本节提供对现有调试信息条目属性的更改。这些更改将纳入相应的 DWARF 版本 5 第 3 章节。

A.3.1 单元条目

A.3.1.1 完整和部分编译单元条目

注意

本节扩充了 DWARF 版本 5 第 3.1.1 节和表 3.1。

为与 DW_AT_language 属性一起使用而定义的其他语言代码在 语言名称 中定义。

表 7 语言名称

语言名称

含义

DW_LANG_LLVM_HIP

HIP 语言。

HIP 语言 [HIP] 可以通过扩展 C++ 语言来支持。

注意

添加了以下新属性。

  1. DW_TAG_compile_unit 编译单元的调试信息条目可以具有 DW_AT_LLVM_augmentation 属性,其值是一个扩充字符串。

    扩充字符串允许生产者指示调试信息条目中存在额外的供应商或目标特定的信息。例如,这可能是关于正在使用的供应商特定扩展的版本的信息。

    如果不存在,或者如果字符串为空,则编译单元没有扩充字符串。

    扩充字符串的格式为

    [vendor:vX.Y[:options]]*

    其中 vendor 是生产者,vX.Y 指定了所用扩展的主版本号 X 和次版本号 Y,options 是一个可选字符串,提供关于扩展的附加信息。版本号必须符合语义版本控制 [SEMVER]。options 字符串不得包含 “]“ 字符。

    例如

    [abc:v0.0][def:v1.2:feature-a=on,feature-b=3]
    

A.3.3 子例程和入口点条目

A.3.3.5 底层信息
  1. 一个 DW_TAG_subprogramDW_TAG_inlined_subroutineDW_TAG_entry_point 调试器信息条目可能具有一个 DW_AT_return_addr 属性,其值是一个 DWARF 表达式 E。

    该属性的结果通过评估 E 获得,评估上下文具有位置描述的结果类型、未指定的对象、包含 E 的编译单元、空的初始栈,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。评估的结果是位置描述 L,它指示当前调用帧的子程序或入口点的返回地址存储位置。

    如果 L 不是由目标架构特定地址空间之一的一个内存位置描述组成,则 DWARF 格式不正确。

    注意

    不清楚为什么 DW_TAG_inlined_subroutine 具有 DW_AT_return_addr 属性,但没有 DW_AT_frame_baseDW_AT_static_link 属性。 似乎它应该要么全部都有,要么全部都没有。 由于内联子程序没有调用帧,因此似乎它们应该都没有这些属性。

  2. 一个 DW_TAG_subprogramDW_TAG_entry_point 调试器信息条目可能具有一个 DW_AT_frame_base 属性,其值是一个 DWARF 表达式 E。

    该属性的结果通过评估 E 获得,评估上下文具有位置描述的结果类型、未指定的对象、包含 E 的编译单元、空的初始栈,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。

    如果 E 包含 DW_OP_fbreg 操作,或者结果位置描述 L 不是由单个位置描述 SL 组成,则 DWARF 格式不正确。

    如果 SL 是寄存器 R 的寄存器位置描述,则 L 将被替换为评估 DW_OP_bregx R, 0 操作的结果。 这会在目标架构默认地址空间中计算帧基址内存位置描述。

    这允许使用更紧凑的 DW_OP_reg* 来代替 DW_OP_breg* 0

    注意

    此规则可以删除,并要求生产者直接使用 DW_OP_call_frame_cfaDW_OP_breg*DW_OP_LLVM_aspace_bregx 创建所需的位置描述。 这也允许目标在大型寄存器中实现调用帧。

    否则,如果 SL 不是任何目标架构特定地址空间中的内存位置描述,则 DWARF 格式不正确。

    结果 L 是子程序或入口点的帧基址

    通常,E 将使用 DW_OP_call_frame_cfa 操作,或者是一个堆栈指针寄存器加上或减去一些偏移量。

    子程序的帧基址通常是相对于为子程序的堆栈帧分配的第一个存储单元的地址。 DW_AT_frame_base 属性可以通过以下几种方式使用:

    1. 在需要位置列表来定位局部变量的子程序中, DW_AT_frame_base 可以保存所需的位置列表,而所有变量的位置描述可以是更简单的,涉及帧基址的位置描述。

    2. 它可以用于解析嵌套例程中的“向上层”寻址。(另请参阅下面的 DW_AT_static_link

    某些语言支持嵌套子例程。 在此类语言中,可以从内部子例程中引用外部子例程的局部变量。 DW_AT_static_link DW_AT_frame_base 属性允许调试器支持这种相同的引用方式。

  3. 如果 DW_TAG_subprogramDW_TAG_entry_point 调试器信息条目在词法上是嵌套的,则它可能具有一个 DW_AT_static_link 属性,其值是一个 DWARF 表达式 E。

    该属性的结果通过评估 E 获得,评估上下文具有位置描述的结果类型、未指定的对象、包含 E 的编译单元、空的初始栈,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。 评估的结果是规范帧地址(参见 A.6.4 调用帧信息)的位置描述 L,该地址属于立即词法包围当前调用帧的子程序或入口点的子程序实例的相关调用帧。

    如果 L 不是由目标架构特定地址空间之一的一个内存位置描述组成,则 DWARF 格式不正确。

    在支持嵌套子例程的上下文中,DW_AT_frame_base 属性值遵守以下约束

    1. 它计算的值在子程序的生命周期内不会改变,并且

    2. 计算出的值在同一子例程的实例中是唯一的。

    对于典型的 DW_AT_frame_base 用法,这意味着递归子例程的堆栈帧必须具有非零大小。

    如果调试器尝试解析对变量的向上层引用,它会使用 DWARF 的嵌套结构来确定哪个子程序是词法父级,并使用 DW_AT_static_link 值来识别父级的适当活动帧。 然后,它可以在父级的上下文中尝试查找引用。

    注意

    添加了以下新属性。

  4. 对于使用 SIMT 执行模型实现的语言,DW_TAG_subprogramDW_TAG_inlined_subroutineDW_TAG_entry_point 调试器信息条目可能具有一个 DW_AT_LLVM_lanes 属性,其值是一个整数常量,表示每个目标架构线程的源语言执行线程数。

    例如,编译器可以使用 SIMT 执行模型将源语言执行线程映射到目标架构线程的 lane 上。

    它是每个目标架构线程的源语言执行线程的静态数量。 它不是目标架构线程启动的源语言执行线程的动态数量,例如,由于较小或部分工作组。

    如果不存在,则使用默认值 1。

    如果该值小于或等于 0,则 DWARF 格式不正确。

  5. 对于使用 SIMT 执行模型实现的源语言,DW_TAG_subprogramDW_TAG_inlined_subroutineDW_TAG_entry_point 调试信息条目可能具有一个 DW_AT_LLVM_lane_pc 属性,其值是一个 DWARF 表达式 E。

    该属性的结果通过评估 E 获得,评估上下文具有位置描述的结果类型、未指定的对象、包含 E 的编译单元、空的初始栈,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。

    结果位置描述 L 是针对 lane 计数大小的通用类型元素向量。 lane 计数是 DW_AT_LLVM_lanes 属性的值。 每个元素都保存着对应 lane 的概念程序位置。 如果 lane 在调用当前子程序时未处于活动状态,则其元素是未定义的位置描述。

    如果 L 没有恰好一个单独的位置描述,则 DWARF 格式不正确。

    DW_AT_LLVM_lane_pc 允许编译器指示目标架构线程的每个 SIMT lane 在概念上所处的位置,即使它处于不活跃的发散控制流中。

    通常,结果是具有一个复合位置描述的位置描述,其中每个部分都是具有未定义位置描述或内存位置描述的位置描述。

    如果不存在,则目标架构线程未以 SIMT 方式使用,并且使用线程的当前程序位置。

  6. 对于使用 SIMT 执行模型实现的语言,DW_TAG_subprogramDW_TAG_inlined_subroutineDW_TAG_entry_point 调试器信息条目可能具有一个 DW_AT_LLVM_active_lane 属性,其值是一个 DWARF 表达式 E。

    E 在评估上下文中进行评估,该上下文具有位置描述的结果类型、未指定的对象、包含 E 的编译单元、空的初始栈,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。

    如果 L 没有恰好一个单独的位置描述 SL,则 DWARF 格式不正确。

    当前程序位置的活动 lane 位掩码 V 通过从 SL 读取获得,使用目标架构特定的整数基本类型 T,其位大小等于上下文字帧和程序位置对应的子程序的 DW_AT_LLVM_lanes 属性的值。 掩码的第 N 个最低有效位对应于第 N 个 lane。 如果该位为 1,则 lane 处于活动状态,否则为非活动状态。 属性的结果是值 V。

    某些目标可能会更新目标架构执行掩码,以用于必须使用与当前活动 lane 不同的 lane 集执行的代码区域。 例如,某些代码必须在所有 lane 临时激活的情况下执行。 DW_AT_LLVM_active_lane 允许编译器提供一种方法来确定任何程序位置的源语言活动 lane。 通常,此属性将使用 loclist 来表达不同程序位置的活动 lane 掩码的不同位置。

    如果不存在且 DW_AT_LLVM_lanes 大于 1,则使用目标架构执行掩码。

  7. 一个 DW_TAG_subprogramDW_TAG_inlined_subroutineDW_TAG_entry_point 调试器信息条目可能具有一个 DW_AT_LLVM_iterations 属性,其值是一个整数常量或 DWARF 表达式 E。 其值是目标架构为单个源语言执行线程并发执行的源语言循环迭代次数。

    编译器可能会生成代码,使用软件流水线或 SIMD 向量化等优化技术并发执行源语言循环的多次迭代。 并发迭代的次数可能因同一子程序中不同的循环嵌套而异。 通常,此属性将使用 loclist 来表达不同程序位置的不同值。

    如果该属性是整数常量,则该值是常量。 如果常量小于或等于 0,则 DWARF 格式不正确。

    否则,E 在评估上下文中进行评估,该上下文具有位置描述的结果类型、未指定的对象、包含 E 的编译单元、空的初始栈,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。 如果结果不是由一个隐式位置描述组成的位置描述,并且当作为通用类型读取时,导致值 V 小于或等于 0,则 DWARF 格式不正确。 属性的结果是值 V。

    如果不存在,则使用默认值 1。

A.3.4 调用点条目和参数

A.3.4.2 调用点参数
  1. 调用点条目可以拥有 DW_TAG_call_site_parameter 调试信息条目,表示传递给调用的参数。 调用点参数条目的出现顺序与源中相应参数的顺序相同。 每个此类条目都具有一个 DW_AT_location 属性,该属性是位置描述。 此位置描述描述了参数的传递位置(通常是某个寄存器,或可表示为堆栈寄存器的内容加上一些偏移量的内存位置)。

  2. 一个 DW_TAG_call_site_parameter 调试器信息条目可能具有一个 DW_AT_call_value 属性,其值是一个 DWARF 操作表达式 E1

    DW_AT_call_value 属性的结果通过评估 E1 获得,评估上下文具有值的结果类型、未指定的对象、包含 E 的编译单元、空的初始栈,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。 结果值 V1 是调用点发出的调用时参数的值。

    对于按引用传递的参数(代码传递指向包含参数的位置的指针),或者对于引用类型参数,DW_TAG_call_site_parameter 调试器信息条目也可能具有一个 DW_AT_call_data_location 属性(其值是一个 DWARF 操作表达式 E2)和一个 DW_AT_call_data_value 属性(其值是一个 DWARF 操作表达式 E3)。

    DW_AT_call_data_location 属性的值通过评估 E2 获得,评估上下文具有位置描述的结果类型、未指定的对象、包含 E 的编译单元、空的初始栈,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。 结果位置描述 L2 是被引用参数在调用点发出的调用期间所驻留的位置。 如果 E2 只是一个 DW_OP_push_object_address,则可以省略 DW_AT_call_data_location 属性。

    注意

    DWARF 版本 5 暗示可以使用 DW_OP_push_object_address,但没有说明必须在上下文中指定哪个对象。 要么不能使用 DW_OP_push_object_address,要么必须定义要在上下文中传递的对象。

    DW_AT_call_data_value 属性的值通过评估 E3 获得,评估上下文具有值的结果类型、未指定的对象、包含 E 的编译单元、空的初始栈,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。 结果值 V3 是调用点发出的调用时 L2 中的值。

    如果当前调用帧不是包含 DW_TAG_call_site_parameter 调试器信息条目的子程序的调用帧,或者当前程序位置不是当前调用帧中包含 DW_TAG_call_site_parameter 调试器信息条目的调用点的程序位置,则这些属性的结果未定义。

    消费者可能必须虚拟地回溯到调用点(参见 A.6.4 调用帧信息)才能评估这些属性。 这将确保用户关注的源语言执行线程对应于评估表达式所需的调用点。

    如果无法避免这些属性的表达式访问可能被调用点调用的子程序破坏的寄存器或内存位置,则不应提供关联的属性。

    限制的原因是可能需要在被调用者的执行期间访问参数。 消费者可以从被调用的子程序虚拟地回溯到调用者,然后评估属性表达式。 调用帧信息(参见 A.6.4 调用帧信息)将无法恢复已被破坏的寄存器,并且被破坏的内存将不再具有调用时的值。

  3. 每个调用点参数条目也可能具有一个 DW_AT_call_parameter 属性,该属性包含对 DW_TAG_formal_parameter 条目、引用参数类型的 DW_AT_type attribute 属性或描述参数名称的 DW_AT_name 属性的引用。

使用调用点条目和相关属性的示例可以在附录 D.15 中找到。

A.3.5 词法块条目

注意

本节与 DWARF 版本 5 第 3.5 节相同。

A.4 数据对象和对象列表条目

注意

本节提供了对现有调试器信息条目属性的更改。 这些更改将纳入相应的 DWARF 版本 5 第 4 章。

A.4.1 数据对象条目

程序变量、形参和常量由带有标签 DW_TAG_variableDW_TAG_formal_parameterDW_TAG_constant 的调试信息条目表示。

标签 DW_TAG_constant 用于具有真正的命名常量的语言。

程序变量、形参或常量的调试信息条目可能具有以下属性

  1. 一个 DW_AT_location 属性,其值是一个 DWARF 表达式 E,描述了变量或参数在运行时的位置。

    该属性的结果通过评估 E 获得,评估上下文具有位置描述的结果类型、未指定的对象、包含 E 的编译单元、空的初始栈,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。 评估的结果是数据对象基址的位置描述。

    有关 DW_OP_call* 操作使用的特殊评估规则,请参见 A.2.5.4.2 控制流操作

    注意

    删除有关 DW_OP_call* 操作如何评估 DW_AT_location 属性的描述,因为现在在操作中已对此进行描述。

    注意

    请参阅有关 DW_OP_call* 操作中 DW_AT_location 属性的讨论。 让每个属性仅具有单一用途和单一执行语义似乎是可取的。 这使得消费者更容易,不再需要跟踪上下文。 这也使得生产者更容易,因为它可以依赖于每个属性的单一语义。

    因此,将 DW_AT_location 属性限制为仅支持评估对象的位置描述,并对同一操作表达式堆栈上 DWARF 表达式过程的评估使用不同的属性和编码类,这似乎是可取的。

  2. DW_AT_const_value

    注意

    可以弃用将 DW_AT_const_value 属性用于已优化为常量的 DW_TAG_variableDW_TAG_formal_parameter 调试信息条目。 相反,DW_AT_location 可以与生成隐式位置描述的 DWARF 表达式一起使用,因为现在任何位置描述都可以在 DWARF 表达式中使用。 这允许使用 DW_OP_call* 操作来推送任何变量的位置描述,无论其如何优化。

  3. DW_AT_LLVM_memory_space

    一个 DW_AT_memory_space 属性,其常量值表示源语言特定的 DWARF 内存空间(参见 2.14 “内存空间”)。 如果省略,则默认为 DW_MSPACE_none

A.4.2 公共块条目

公共块条目也具有一个 DW_AT_location 属性,其值是一个 DWARF 表达式 E,描述了公共块在运行时的位置。 该属性的结果通过评估 E 获得,评估上下文具有位置描述的结果类型、未指定的对象、包含 E 的编译单元、空的初始栈,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。 评估的结果是公共块基址的位置描述。 有关 DW_OP_call* 操作使用的特殊评估规则,请参见 A.2.5.4.2 控制流操作

A.5 类型条目

注意

本节提供了对现有调试器信息条目属性的更改。 这些更改将纳入相应的 DWARF 版本 5 第 5 章。

A.5.1 基本类型条目

注意

添加了以下新属性。

  1. 基本类型 T 的 DW_TAG_base_type 调试器信息条目可能具有一个 DW_AT_LLVM_vector_size 属性,其值是一个整数常量,表示向量类型大小 N。

    向量基本类型的表示形式为 N 个连续元素,每个元素都具有基本类型 T' 的表示形式,该类型与没有 DW_AT_LLVM_vector_size 属性的 T 相同。

    如果 DW_TAG_base_type 调试器信息条目没有 DW_AT_LLVM_vector_size 属性,则该基本类型不是向量类型。

    如果 N 不大于 0,则 DWARF 格式不正确。

    注意

    LLVM 提到了一个非上游的调试器信息条目,旨在支持向量类型。 但是,那不是针对基本类型,因此不适合作为堆栈值条目的类型。 但也许可以使用此属性来替换它。

    注意

    将此与 GNU 支持的 DW_AT_GNU_vector 扩展进行比较。 是将属性添加到现有的 DW_TAG_base_type 调试条目更好,还是允许某些形式的 DW_TAG_array_type(那些具有 DW_AT_GNU_vector 属性的)用作堆栈条目值类型更好?

  2. 编码为 DW_ATE_addressDW_TAG_base_type 调试器信息条目可能具有一个 DW_AT_LLVM_address_space 属性,其值是架构特定的地址空间(参见 A.2.13 地址空间)。 如果省略,则默认为 DW_ASPACE_LLVM_none

A.5.3 类型修饰符条目

注意

本节扩充了 DWARF 版本 5 第 5.3 节。

描述指针或引用类型(使用 DW_TAG_pointer_typeDW_TAG_reference_typeDW_TAG_rvalue_reference_type)的修改类型条目可能具有一个 DW_AT_LLVM_memory_space 属性,其常量值表示源语言特定的 DWARF 内存空间(参见 A.2.14 内存空间)。 如果省略,则默认为 DW_MSPACE_LLVM_none。

描述指针或引用类型(使用 DW_TAG_pointer_typeDW_TAG_reference_typeDW_TAG_rvalue_reference_type)的修改类型条目可能具有一个 DW_AT_LLVM_address_space 属性,其常量值 AS 表示架构特定的 DWARF 地址空间(参见 A.2.13 地址空间)。 如果省略,则默认为 DW_ASPACE_LLVM_none。 DR 是当前编译单元中假设的调试信息条目 D 的偏移量,用于与 AS 的地址大小匹配的整数基本类型。 具有给定指针或引用类型的对象 P 被解引用,就好像评估了 DW_OP_push_object_address; DW_OP_deref_type DR; DW_OP_constu AS; DW_OP_form_aspace_address 操作表达式,但以下情况除外:结果类型是位置描述; 初始栈为空; 对象是 P 的位置描述。

注意

如果当前上下文中没有定义当前目标架构怎么办?

注意

随着对 DWARF 地址空间扩展的支持,可能值得研究它们是否可以用于以前由 DWARF 5 段支持的内容。 这将包括指定所有代码地址(编译单元、子程序、子程序条目、标签、子程序类型等)的地址空间。 要么可以扩展代码地址属性以允许 exprloc 形式(以便可以使用 DW_OP_form_aspace_address),要么允许在所有允许 DW_AT_segment 的 DIE 上使用 DW_AT_LLVM_address_space 属性。

A.5.7 结构体、联合体、类和接口类型条目

A.5.7.3 派生或扩展的结构体、类和接口
  1. 对于 DW_AT_data_member_location 属性,存在两种情况:

    1. 如果该属性是一个整数常量 B,则它提供从包含实体起始位置开始的字节偏移量。

      该属性的结果通过评估一个 DW_OP_LLVM_offset B 操作获得,该操作的初始堆栈包含包含实体起始位置的位置描述。评估的结果是成员条目基址的位置描述。

      如果包含实体的起始位置不是字节对齐的,则成员条目的起始位置在字节内具有相同的位移。

    2. 否则,该属性必须是一个 DWARF 表达式 E,该表达式在以下上下文中进行评估:结果类型为位置描述,未指定对象,包含 E 的编译单元,初始堆栈包含包含实体起始位置的位置描述,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。评估的结果是成员条目基址的位置描述。

    注意

    包含实体的起始位置现在可以是任何位置描述,包括具有多个单位置描述的位置描述,以及具有任何类型和任何位偏移的单位置描述的位置描述。

A.5.7.8 成员函数条目
  1. 虚拟函数的条目也具有一个 DW_AT_vtable_elem_location 属性,其值是一个 DWARF 表达式 E。

    该属性的结果通过评估 E 获得,评估上下文如下:结果类型为位置描述,未指定对象,包含 E 的编译单元,初始堆栈包含封闭类型的对象的位置描述,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。评估的结果是封闭类的虚函数表中函数槽位的位置描述。

A.5.14 指向成员类型的指针条目

  1. DW_TAG_ptr_to_member_type 调试信息条目具有一个 DW_AT_use_location 属性,其值是一个 DWARF 表达式 E。它用于计算指向成员条目的类成员的位置描述。

    用于查找给定类、结构体或联合体的成员的位置描述的方法,对于该类、结构体或联合体的任何实例以及指向成员类型的指针的任何实例都是通用的。因此,该方法与指向成员类型的指针相关联,而不是与每个具有指向成员类型的指针的对象相关联。

    DW_AT_use_location DWARF 表达式与指向成员类型的特定对象的以及特定结构体或类实例的位置描述结合使用。

    该属性的结果通过评估 E 获得,评估上下文如下:结果类型为位置描述,未指定对象,包含 E 的编译单元,初始堆栈包含两个条目,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。第一个堆栈条目是指向成员对象的指针本身的值。第二个堆栈条目是包含要计算其位置的成员的整个类、结构体或联合体实例的基址的位置描述。评估的结果是指向成员条目的类成员的位置描述。

A.5.18 类型的动态属性

A.5.18.1 数据位置

某些语言可能使用描述符来表示对象,以保存有关表示该对象值的location和/或运行时参数等信息。

  1. DW_AT_data_location 属性可以与任何在其表示中提供一个或多个级别隐藏间接寻址和/或运行时参数的类型一起使用。它的值是一个 DWARF 操作表达式 E,用于计算对象的数据的位置描述。当省略此属性时,数据的位置描述与对象的位置描述相同。

    该属性的结果通过评估 E 获得,评估上下文如下:结果类型为位置描述,对象是数据描述符的位置描述,包含 E 的编译单元,一个空的初始堆栈,以及其他上下文元素,这些元素对应于用户关注的源语言执行线程(如果有)。评估的结果是成员条目基址的位置描述。

    E 通常会涉及一个操作表达式,该表达式以一个 DW_OP_push_object_address 操作开始,该操作加载对象的位置描述,然后可以用作后续计算中的描述符。

    注意

    既然 DW_AT_data_member_locationDW_AT_use_locationDW_AT_vtable_elem_location 都允许操作表达式和位置列表表达式,为什么 DW_AT_data_location 不允许两者都允许呢? 在所有情况下,它们都适用于数据对象,因此优化不太可能导致不同程序位置范围的不同操作表达式。但是,如果支持某些属性,则应该支持所有属性。

    似乎很奇怪,此属性与 DW_AT_data_member_location 不同,没有包含对象位置描述的初始堆栈,因为表达式必须需要它。

A.6 其他调试信息

注意

本节提供对现有调试信息条目属性的更改。这些更改将被纳入相应的 DWARF 第 5 版第 6 章。

A.6.1 加速访问

A.6.1.1 按名称查找
A.6.1.1.1 名称索引的内容

注意

以下内容提供了对 DWARF 第 5 版第 6.1.1.1 节的更改。

可选的 .debug_names 节中的名称索引中包含的调试信息条目的规则已扩展为也包括具有 DW_AT_location 属性的命名 DW_TAG_variable 调试信息条目,该属性包含 DW_OP_LLVM_form_aspace_address 操作。

名称索引必须包含每个调试信息条目的条目,这些条目定义了命名的子程序、标签、变量、类型或命名空间,但须遵守以下规则:

  • 包含 DW_OP_addrDW_OP_LLVM_form_aspace_addressDW_OP_form_tls_address 操作的 DW_AT_location 属性的 DW_TAG_variable 调试信息条目将被包含;否则,将被排除。

A.6.1.1.4 名称索引的数据表示
A.6.1.1.4.1 节头

注意

以下内容提供了对 DWARF 第 5 版第 6.1.1.4.1 项 14 augmentation_string 的补充。

一个以 null 结尾的 UTF-8 厂商特定扩充字符串,它提供了关于此索引内容的附加信息。如果提供,建议的扩充字符串格式为:

[vendor:vX.Y[:options]]*

其中 vendor 是生产者,vX.Y 指定了编译单元 DWARF 中使用的主版本号 X 和次版本号 Y,而 options 是一个可选字符串,提供了关于扩展的附加信息。版本号必须符合语义版本控制 [SEMVER]。options 字符串不得包含 “]” 字符。

例如

[abc:v0.0][def:v1.2:feature-a=on,feature-b=3]

注意

这与 DWARF 第 5 版中的定义不同,但与其他扩充字符串一致,并允许支持多个厂商扩展。

A.6.2 行号信息

A.6.2.4 行号程序头
A.6.2.4.1 标准内容描述

注意

以下内容扩充了 DWARF 第 5 版第 6.2.4.1 节。

  1. DW_LNCT_LLVM_source

    该组件是一个以 null 结尾的 UTF-8 源代码文本字符串,使用 “\n“ 行尾符。此内容代码与 DW_LNCT_path 使用相同的形式配对。它可用于文件名条目。

    如果没有可用的源代码,则该值为一个空的以 null 结尾的字符串。如果源代码可用但文件为空,则该值为以 null 结尾的单个 “\n“。

    当 source 字段存在时,消费者可以使用嵌入的源代码,而不是尝试使用 DW_LNCT_path 字段提供的文件路径在磁盘上查找源代码。当 source 字段不存在时,消费者可以访问该文件以获取源代码文本。

    这对于支持运行时编译和运行时生成源代码文本的编程语言尤其有用。在这些情况下,源代码文本不驻留在任何永久文件中。例如,OpenCL 语言 [:ref:`OpenCL <amdgpu-dwarf-OpenCL>`] 支持在线编译。

  2. DW_LNCT_LLVM_is_MD5

    DW_LNCT_LLVM_is_MD5 指示 DW_LNCT_MD5 内容类型(如果存在)是否有效:当为 0 时无效,当为 1 时有效。如果 DW_LNCT_LLVM_is_MD5 内容类型不存在,并且 DW_LNCT_MD5 内容类型存在,则 MD5 校验和有效。

    DW_LNCT_LLVM_is_MD5 始终与 DW_FORM_udata 形式配对。

    这允许一个编译单元具有混合了包含和不包含 MD5 校验和的文件。当链接多个可重定位文件时,可能会发生这种情况。

A.6.4 调用帧信息

注意

本节提供对现有调用帧信息的更改,并定义了这些扩展添加的指令。为地址空间添加了额外的支持。寄存器展开 DWARF 表达式被泛化,以允许任何位置描述,包括具有复合和隐式位置描述的位置描述。

这些更改将被纳入 DWARF 第 5 版第 6.4 节。

A.6.4.1 调用帧信息的结构

寄存器规则如下:

未定义

具有此规则的寄存器在前一帧中没有可恢复的值。此寄存器的先前值是未定义的位置描述(参见 A.2.5.4.4.2 未定义的位置描述操作)。

按照惯例,寄存器不会被被调用者保存。

相同值

此寄存器自之前的调用者帧以来未被修改。

如果当前帧是顶层帧,则此寄存器的先前值是位置描述 L,它指定一个寄存器位置描述 SL。SL 指定寄存器位置存储,该存储对应于当前线程的位偏移量为 0 的寄存器。

如果当前帧不是顶层帧,则此寄存器的先前值是使用被当前调用者帧调用的被调用者帧和被调用者程序位置的调用帧信息获得的相同寄存器的位置描述。

按照惯例,寄存器会被被调用者保存,但被调用者没有修改它。

offset(N)

N 是一个带符号的字节偏移量。此寄存器的先前值保存在计算出的位置描述中,就像 DWARF 操作表达式 DW_OP_LLVM_offset N 在当前上下文中评估一样,但结果类型是位置描述,编译单元未指定,对象未指定,并且初始堆栈包含当前 CFA 的位置描述(参见 A.2.5.4 DWARF 操作表达式)。

val_offset(N)

N 是一个带符号的字节偏移量。此寄存器的先前值是位置描述的内存字节地址,计算方式与 DWARF 操作表达式 DW_OP_LLVM_offset N 在当前上下文中评估一样,但结果类型是位置描述,编译单元未指定,对象未指定,并且初始堆栈包含当前 CFA 的位置描述(参见 A.2.5.4 DWARF 操作表达式)。

如果 CFA 位置描述不是内存字节地址位置描述,或者寄存器大小与当前 CFA 位置描述的地址空间中的地址大小不匹配,则 DWARF 是格式错误的。

由于 CFA 位置描述必须是内存字节地址位置描述,因此 val_offset(N) 的值也将是内存字节地址位置描述,因为它是由 N 个字节偏移 CFA 位置描述得到的。此外,val_offset(N) 的值将是与 CFA 位置描述相同的地址空间中的内存字节地址。

注意

DWARF 是否应该允许地址大小与寄存器大小不同?要求它们具有相同的位大小可以避免任何转换问题,因为寄存器的位内容只是简单地解释为地址值。

GDB 具有每个寄存器的钩子,允许在每个寄存器的基础上进行目标特定的转换。默认情况下,它会截断较大的寄存器,并实际从下一个寄存器读取字节(或读取超出最后一个寄存器的范围)以获取较小的寄存器。没有 GDB 测试读取寄存器超出范围(除了一个非法的手写汇编测试)。

register(R)

此寄存器已存储在另一个编号为 R 的寄存器中。

此寄存器的先前值是使用当前帧和寄存器 R 的当前程序位置的调用帧信息获得的位置描述。

如果此寄存器的大小与寄存器 R 的大小不匹配,或者调用帧信息中存在循环依赖关系,则 DWARF 是格式错误的。

注意

这是否也应该允许 R 大于此寄存器?如果是这样,值是否存储在低位,而额外的上位存储的内容是否未定义?

expression(E)

此寄存器的先前值位于评估 DWARF 操作表达式 E(参见 A.2.5.4 DWARF 操作表达式)产生的位置描述处。

E 在当前上下文中评估,但结果类型是位置描述,编译单元未指定,对象未指定,并且初始堆栈包含当前 CFA 的位置描述(参见 A.2.5.4 DWARF 操作表达式)。

val_expression(E)

此寄存器的先前值位于从评估 DWARF 操作表达式 E(参见 A.2.5.4 DWARF 操作表达式)产生的值创建的隐式位置描述处。

E 在当前上下文中评估,但结果类型是值,编译单元未指定,对象未指定,并且初始堆栈包含当前 CFA 的位置描述(参见 A.2.5.4 DWARF 操作表达式)。

如果结果值类型大小与寄存器大小不匹配,则 DWARF 是格式错误的。

注意

这用途有限,因为 DWARF 表达式 E 只能产生最大为通用类型大小的值。这是因为不允许任何在 CFI 操作表达式中指定类型的操作。这使得它对于大于通用类型的寄存器不可用。但是,expression(E) 可以用于创建任何大小的隐式位置描述。

architectural

该规则由扩充器在此规范之外定义。

如果实际按描述构建,则此表将非常庞大。表中任何点的条目中的大多数与它们上面的条目相同。整个表可以通过仅记录程序中每个子例程的起始地址处的差异来非常紧凑地表示。

虚拟展开信息编码在名为 .debug_frame 的自包含节中。.debug_frame 节中的条目相对于节的起始位置按地址大小的倍数对齐,并采用两种形式:公共信息条目 (CIE) 和帧描述条目 (FDE)。

如果函数的代码地址范围不是连续的,则可能存在与该函数的部分相对应的多个 CIE 和 FDE。

公共信息条目 (CIE) 保存许多帧描述条目 (FDE) 共享的信息。每个非空的 .debug_frame 节中至少有一个 CIE。CIE 包含以下字段,按顺序排列:

  1. length (初始长度)

    一个常量,给出 CIE 结构的字节数,不包括长度字段本身(参见 7.2.2 初始长度值 节)。长度字段的大小加上长度的值必须是 address_size 字段中指定的地址大小的整数倍。

  2. CIE_id (4 或 8 字节,参见 A.7.4 32 位和 64 位 DWARF 格式)

    一个常量,用于区分 CIE 和 FDE。

    在 32 位 DWARF 格式中,CIE 标头中的 CIE id 值为 0xffffffff;在 64 位 DWARF 格式中,该值为 0xffffffffffffffff。

  3. version (ubyte)

    一个版本号(参见 7.24 调用帧信息 节)。此数字特定于调用帧信息,并且独立于 DWARF 版本号。

    CIE 版本号的值为 4。

    注意

    为了反映这些扩展中的更改,这是否会增加到 5?

  4. augmentation (UTF-8 字符序列)

    一个以 null 结尾的 UTF-8 字符串,用于标识对此 CIE 或使用它的 FDE 的扩充。如果读取器遇到意外的扩充字符串,则只能读取以下字段:

    • CIE:length、CIE_id、version、augmentation

    • FDE:length、CIE_pointer、initial_location、address_range

    如果没有扩充,则此值为零字节。

    扩充字符串允许用户指示 CIE 或 FDE 中存在额外的厂商和目标架构特定信息,这些信息是虚拟展开堆栈帧所必需的。例如,这可能是关于动态分配的数据的信息,这些数据需要在例程退出时释放。

    由于 .debug_frame 节独立于任何 .debug_info 节都很有用,因此扩充字符串始终使用 UTF-8 编码。

    建议的扩充字符串格式为:

    [vendor:vX.Y[:options]]*

    其中 vendor 是生产者,vX.Y 指定了所用扩展的主版本号 X 和次版本号 Y,options 是一个可选字符串,提供关于扩展的附加信息。版本号必须符合语义版本控制 [SEMVER]。options 字符串不得包含 “]“ 字符。

    例如

    [abc:v0.0][def:v1.2:feature-a=on,feature-b=3]
    
  5. address_size (ubyte)

    此 CIE 和任何使用它的 FDE 中目标地址的大小,以字节为单位。如果此帧存在编译单元,则其地址大小必须与此处的地址大小匹配。

  6. segment_selector_size (ubyte)

    此 CIE 和任何使用它的 FDE 中的段选择器的大小,以字节为单位。

  7. code_alignment_factor (unsigned LEB128)

    一个常量,从所有 advance location 指令中分解出来(参见 A.6.4.2.1 行创建指令)。结果值为 (operand * code_alignment_factor)

  8. data_alignment_factor (signed LEB128)

    一个常量,从某些 offset 指令中分解出来(参见 A.6.4.2.2 CFA 定义指令A.6.4.2.3 寄存器规则指令)。结果值为 (operand * data_alignment_factor)

  9. return_address_register (unsigned LEB128)

    一个无符号 LEB128 常量,指示规则表中哪一列表示子程序的返回地址。请注意,此列可能不对应于实际的机器寄存器。

    返回地址寄存器的值用于确定调用者帧的程序位置。顶层帧的程序位置是当前线程的目标架构程序计数器值。

  10. initial_instructions (ubyte 数组)

    一系列规则,被解释以创建表中每列的初始设置。

    在解释初始指令之前,所有列的默认规则是未定义规则。但是,ABI 编写机构或编译系统编写机构可以为任何或所有列指定备用默认值。

  11. padding (ubyte 数组)

    足够的 DW_CFA_nop 指令,使此条目的大小与上面的 length 值匹配。

FDE 包含以下字段,按顺序排列:

  1. length (初始长度)

    一个常量,给出此子程序的标头和指令流的字节数,不包括长度字段本身(参见 7.2.2 初始长度值 节)。长度字段的大小加上长度的值必须是地址大小的整数倍。

  2. CIE_pointer (4 或 8 字节,参见 A.7.4 32 位和 64 位 DWARF 格式)

    一个常量偏移量,指向 .debug_frame 节,表示与此 FDE 关联的 CIE。

  3. initial_location (段选择器和目标地址)

    与此表条目关联的第一个位置的地址。如果此 FDE 的 CIE 的 segment_selector_size 字段为非零,则初始位置前面会有一个给定长度的段选择器。

  4. address_range (目标地址)

    此条目描述的程序指令的字节数。

  5. instructions (ubyte 数组)

    一系列表定义指令,在 A.6.4.2 调用帧指令 中描述。

  6. padding (ubyte 数组)

    足够的 DW_CFA_nop 指令,使此条目的大小与上面的 length 值匹配。

A.6.4.2 调用帧指令

每个调用帧指令都被定义为接受 0 个或多个操作数。某些操作数可能被编码为操作码的一部分(参见 A.7.24 调用帧信息)。指令在以下章节中定义。

某些调用帧指令具有编码为 DWARF 操作表达式 E 的操作数(参见 A.2.5.4 DWARF 操作表达式)。可在 E 中使用的 DWARF 操作具有以下限制:

  • DW_OP_addrxDW_OP_call2DW_OP_call4DW_OP_call_refDW_OP_const_typeDW_OP_constxDW_OP_convertDW_OP_deref_typeDW_OP_fbregDW_OP_implicit_pointerDW_OP_regval_typeDW_OP_reinterpretDW_OP_xderef_type 操作是不允许的,因为调用帧信息不得依赖于其他调试节。

  • DW_OP_push_object_address 是不允许的,因为没有对象上下文来提供要推送的值。

  • DW_OP_LLVM_push_laneDW_OP_LLVM_push_iteration 是不允许的,因为调用帧指令描述的是整个目标架构线程的操作,而不是独立的通道或迭代。

  • DW_OP_call_frame_cfaDW_OP_entry_value 是不允许的,因为它们的使用会造成循环依赖。

  • 如果评估 E 导致 DW_OP_LLVM_call_frame_entry_reg 操作之间出现循环依赖,则 DW_OP_LLVM_call_frame_entry_reg 是不允许的。

    例如,如果寄存器 R1 具有一个 DW_CFA_def_cfa_expression 指令,该指令评估一个指定寄存器 R2 的 DW_OP_LLVM_call_frame_entry_reg 操作,并且寄存器 R2 具有一个 DW_CFA_def_cfa_expression 指令,该指令评估一个指定寄存器 R1 的 DW_OP_LLVM_call_frame_entry_reg 操作。

应用这些限制的调用帧指令包括 DW_CFA_def_cfa_expression DW_CFA_expression DW_CFA_val_expression

A.6.4.2.1 行创建指令

注意

这些指令与 DWARF 版本 5 第 6.4.2.1 节中的相同。

A.6.4.2.2 CFA 定义指令
  1. DW_CFA_def_cfa

    DW_CFA_def_cfa 指令接受两个无符号 LEB128 操作数,分别表示寄存器号 R 和(非因子化的)字节位移 B。AS 设置为目标架构默认地址空间标识符。所需的操作是将当前 CFA 规则定义为等效于评估 DWARF 操作表达式 DW_OP_constu AS; DW_OP_LLVM_aspace_bregx R, B 作为位置描述的结果。

  2. DW_CFA_def_cfa_sf

    DW_CFA_def_cfa_sf 指令接受两个操作数:一个无符号 LEB128 值,表示寄存器号 R;以及一个有符号 LEB128 因子化的字节位移 B。AS 设置为目标架构默认地址空间标识符。所需的操作是将当前 CFA 规则定义为等效于评估 DWARF 操作表达式 DW_OP_constu AS; DW_OP_LLVM_aspace_bregx R, B * data_alignment_factor 作为位置描述的结果。

    该操作与 DW_CFA_def_cfa 相同,不同之处在于第二个操作数是有符号且因子化的。

  3. DW_CFA_LLVM_def_aspace_cfa 新指令

    DW_CFA_LLVM_def_aspace_cfa 指令接受三个无符号 LEB128 操作数,分别表示寄存器号 R、(非因子化的) 字节位移 B 和特定于目标架构的地址空间标识符 AS。所需的操作是将当前 CFA 规则定义为等效于评估 DWARF 操作表达式 DW_OP_constu AS; DW_OP_LLVM_aspace_bregx R, B 作为位置描述的结果。

    如果 AS 不是目标架构特定的 DW_ASPACE_LLVM_* 值之一,则 DWARF 表达式格式不正确。

  4. DW_CFA_LLVM_def_aspace_cfa_sf 新指令

    DW_CFA_LLVM_def_aspace_cfa_sf 指令接受三个操作数:一个无符号 LEB128 值,表示寄存器号 R;一个有符号 LEB128 因子化的字节位移 B;以及一个无符号 LEB128 值,表示特定于目标架构的地址空间标识符 AS。所需的操作是将当前 CFA 规则定义为等效于评估 DWARF 操作表达式 DW_OP_constu AS; DW_OP_LLVM_aspace_bregx R, B * data_alignment_factor 作为位置描述的结果。

    如果 AS 不是目标架构特定的 DW_ASPACE_LLVM_* 值之一,则 DWARF 表达式格式不正确。

    该操作与 DW_CFA_aspace_def_cfa 相同,不同之处在于第二个操作数是有符号且因子化的。

  5. DW_CFA_def_cfa_register

    DW_CFA_def_cfa_register 指令接受一个无符号 LEB128 操作数,表示寄存器号 R。所需的操作是将当前 CFA 规则定义为等效于评估 DWARF 操作表达式 DW_OP_constu AS; DW_OP_LLVM_aspace_bregx R, B 作为位置描述的结果。B 和 AS 分别是旧的 CFA 字节位移和地址空间。

    如果子程序没有当前的 CFA 规则,或者规则是由 DW_CFA_def_cfa_expression 指令定义的,则 DWARF 格式不正确。

  6. DW_CFA_def_cfa_offset

    DW_CFA_def_cfa_offset 指令接受一个无符号 LEB128 操作数,表示(非因子化的)字节位移 B。所需的操作是将当前 CFA 规则定义为等效于评估 DWARF 操作表达式 DW_OP_constu AS; DW_OP_LLVM_aspace_bregx R, B 作为位置描述的结果。R 和 AS 分别是旧的 CFA 寄存器号和地址空间。

    如果子程序没有当前的 CFA 规则,或者规则是由 DW_CFA_def_cfa_expression 指令定义的,则 DWARF 格式不正确。

  7. DW_CFA_def_cfa_offset_sf

    DW_CFA_def_cfa_offset_sf 指令接受一个有符号 LEB128 操作数,表示因子化的字节位移 B。所需的操作是将当前 CFA 规则定义为等效于评估 DWARF 操作表达式 DW_OP_constu AS; DW_OP_LLVM_aspace_bregx R, B * data_alignment_factor 作为位置描述的结果。R 和 AS 分别是旧的 CFA 寄存器号和地址空间。

    如果子程序没有当前的 CFA 规则,或者规则是由 DW_CFA_def_cfa_expression 指令定义的,则 DWARF 格式不正确。

    该操作与 DW_CFA_def_cfa_offset 相同,不同之处在于操作数是有符号且因子化的。

  8. DW_CFA_def_cfa_expression

    DW_CFA_def_cfa_expression 指令接受单个操作数,该操作数编码为 DW_FORM_exprloc 值,表示 DWARF 操作表达式 E。所需的操作是将当前 CFA 规则定义为等效于评估具有当前上下文的 E 的结果,但结果类型是位置描述,编译单元未指定,对象未指定,并且初始堆栈为空。

    有关可在 E 中使用的 DWARF 表达式操作的限制,请参阅 A.6.4.2 调用帧指令

    如果评估 E 的结果不是内存字节地址位置描述,则 DWARF 格式不正确。

A.6.4.2.3 寄存器规则指令
  1. DW_CFA_undefined

    DW_CFA_undefined 指令接受单个无符号 LEB128 操作数,该操作数表示寄存器号 R。所需的操作是将 R 指定的寄存器的规则设置为 undefined

  2. DW_CFA_same_value

    DW_CFA_same_value 指令接受单个无符号 LEB128 操作数,该操作数表示寄存器号 R。所需的操作是将 R 指定的寄存器的规则设置为 same value

  3. DW_CFA_offset

    DW_CFA_offset 指令接受两个操作数:寄存器号 R(使用操作码编码)和一个无符号 LEB128 常量,表示因子化的位移 B。所需的操作是将 R 指定的寄存器的规则更改为 offset(B * data_alignment_factor) 规则。

    注意

    似乎应该将其命名为 DW_CFA_offset_uf,因为偏移量是无符号因子化的。

  4. DW_CFA_offset_extended

    DW_CFA_offset_extended 指令接受两个无符号 LEB128 操作数,分别表示寄存器号 R 和因子化的位移 B。此指令与 DW_CFA_offset 相同,不同之处在于寄存器操作数的编码和大小。

    注意

    似乎应该将其命名为 DW_CFA_offset_extended_uf,因为位移是无符号因子化的。

  5. DW_CFA_offset_extended_sf

    DW_CFA_offset_extended_sf 指令接受两个操作数:一个无符号 LEB128 值,表示寄存器号 R;以及一个有符号 LEB128 因子化的位移 B。此指令与 DW_CFA_offset_extended 相同,不同之处在于 B 是有符号的。

  6. DW_CFA_val_offset

    DW_CFA_val_offset 指令接受两个无符号 LEB128 操作数,分别表示寄存器号 R 和因子化的位移 B。所需的操作是将 R 指示的寄存器的规则更改为 val_offset(B * data_alignment_factor) 规则。

    注意

    似乎应该将其命名为 DW_CFA_val_offset_uf,因为位移是无符号因子化的。

    注意

    另一种方法是将 DW_CFA_val_offset 定义为隐式使用目标架构默认地址空间,并添加另一个指定地址空间的操作。

  7. DW_CFA_val_offset_sf

    DW_CFA_val_offset_sf 指令接受两个操作数:一个无符号 LEB128 值,表示寄存器号 R;以及一个有符号 LEB128 因子化的位移 B。此指令与 DW_CFA_val_offset 相同,不同之处在于 B 是有符号的。

  8. DW_CFA_register

    DW_CFA_register 指令接受两个无符号 LEB128 操作数,分别表示寄存器号 R1 和 R2。所需的操作是将 R1 指定的寄存器的规则设置为 register(R2) 规则。

  9. DW_CFA_expression

    DW_CFA_expression 指令接受两个操作数:一个无符号 LEB128 值,表示寄存器号 R;以及一个 DW_FORM_block 值,表示 DWARF 操作表达式 E。所需的操作是将 R 指定的寄存器的规则更改为 expression(E) 规则。

    也就是说,E 计算可以检索寄存器值的位置描述。

    有关可在 E 中使用的 DWARF 表达式操作的限制,请参阅 A.6.4.2 调用帧指令

  10. DW_CFA_val_expression

    DW_CFA_val_expression 指令接受两个操作数:一个无符号 LEB128 值,表示寄存器号 R;以及一个 DW_FORM_block 值,表示 DWARF 操作表达式 E。所需的操作是将 R 指定的寄存器的规则更改为 val_expression(E) 规则。

    也就是说,E 计算寄存器 R 的值。

    有关可在 E 中使用的 DWARF 表达式操作的限制,请参阅 A.6.4.2 调用帧指令

    如果评估 E 的结果不是具有与寄存器大小匹配的基类型大小的值,则 DWARF 格式不正确。

  11. DW_CFA_restore

    DW_CFA_restore 指令接受单个操作数(使用操作码编码),该操作数表示寄存器号 R。所需的操作是将 R 指定的寄存器的规则更改为由 CIE 中的 initial_instructions 分配的规则。

  12. DW_CFA_restore_extended

    DW_CFA_restore_extended 指令接受单个无符号 LEB128 操作数,该操作数表示寄存器号 R。此指令与 DW_CFA_restore 相同,不同之处在于寄存器操作数的编码和大小。

A.6.4.2.4 行状态指令

注意

这些指令与 DWARF 版本 5 第 6.4.2.4 节中的相同。

A.6.4.2.5 填充指令

注意

这些指令与 DWARF 版本 5 第 6.4.2.5 节中的相同。

A.6.4.3 调用帧指令用法

注意

与 DWARF 版本 5 第 6.4.3 节中的相同。

A.6.4.4 调用帧调用地址

注意

与 DWARF 版本 5 第 6.4.4 节中的相同。

A.7 数据表示

注意

本节提供对现有调试信息条目属性的更改。这些更改将纳入相应的 DWARF 版本 5 第 7 章各节。

A.7.4 32 位和 64 位 DWARF 格式

注意

这扩充了 DWARF 版本 5 第 7.4 节列表项 3 的表格。

表 8 .debug_info 节属性形式角色

形式

角色

DW_OP_LLVM_aspace_implicit_pointer

.debug_info 中的偏移量

A.7.5 调试信息格式

A.7.5.4 属性编码

注意

这扩充了 DWARF 版本 5 第 7.5.4 节和表 7.5。

下表给出了附加调试信息条目属性的编码。

表 9 属性编码

属性名称

DW_AT_LLVM_active_lane

0x3e08

exprloc, loclist

DW_AT_LLVM_augmentation

0x3e09

字符串

DW_AT_LLVM_lanes

0x3e0a

常量 (constant)

DW_AT_LLVM_lane_pc

0x3e0b

exprloc, loclist

DW_AT_LLVM_vector_size

0x3e0c

常量 (constant)

DW_AT_LLVM_iterations

0x3e0a

常量, exprloc, loclist

DW_AT_LLVM_address_space

TBA

常量 (constant)

DW_AT_LLVM_memory_space

TBA

常量 (constant)

A.7.5.5 类和形式

注意

以下内容修改了 DWARF 版本 5 第 7.5.5 节中的匹配文本。

  • 参考

    参考有四种类型。

    • 第一种类型的参考...

    • 第二种类型的参考可以标识 .debug_info 节中的任何调试信息条目;特别是,它可以引用与包含该参考的单元不同的编译单元中的条目,并且可以引用不同共享对象文件中的条目。这种类型的参考 (DW_FORM_ref_addr) 是从目标可执行文件或共享对象文件的 .debug_info 节的开头算起的偏移量,或者,对于补充对象文件中的参考,是从本地 .debug_info 节的开头算起的偏移量;它在可重定位对象文件中是可重定位的,并且在可执行文件或共享对象文件中经常被重定位。在 32 位 DWARF 格式中,此偏移量是一个 4 字节的无符号值;在 64 位 DWARF 格式中,它是一个 8 字节的无符号值(请参阅 A.7.4 32 位和 64 位 DWARF 格式)。

      可以使用 DW_FORM_ref_addr 从另一个编译单元引用的调试信息条目必须具有全局符号名称。

      对于从一个可执行文件或共享对象文件到另一个可执行文件或共享对象文件的引用,当首次读取调试信息时,或者当使用该引用时,调试器会像运行时加载程序一样解析该引用,以标识可执行文件或共享对象文件以及该文件的 .debug_info 节中的偏移量。

A.7.7 DWARF 表达式

注意

重命名 DWARF 版本 5 第 7.7 节,以反映位置描述统一到 DWARF 表达式中。

A.7.7.1 操作表达式

注意

重命名 DWARF 版本 5 第 7.7.1 节,并删除第 7.7.2 节,以反映位置描述统一到 DWARF 表达式中。

这扩充了 DWARF 版本 5 第 7.7.1 节和表 7.9,并添加了一个新表,描述了 DW_OP_LLVM_user 的供应商扩展操作。

DWARF 操作表达式存储在连续字节块中。这些字节形成一系列操作。每个操作都是一个 1 字节的代码,用于标识该操作,后跟零个或多个字节的附加数据。操作 DW_OP_LLVM_user 的编码在 DWARF 操作编码 中描述,所有 DW_OP_LLVM_user 供应商扩展操作的编码在 DWARF DW_OP_LLVM_user 供应商扩展操作编码 中描述。

表 10 DWARF 操作编码

操作

代码

操作数数量

注释

DW_OP_LLVM_user

0xe9

1+

ULEB128 供应商扩展操作码,后跟 DWARF DW_OP_LLVM_user 供应商扩展操作编码 中定义的供应商扩展操作数

表 11 DWARF DW_OP_LLVM_user 供应商扩展操作编码

操作

供应商扩展操作码

附加操作数数量

注释

DW_OP_LLVM_form_aspace_address

0x02

0

DW_OP_LLVM_push_lane

0x03

0

DW_OP_LLVM_offset

0x04

0

DW_OP_LLVM_offset_uconst

0x05

1

ULEB128 字节位移

DW_OP_LLVM_bit_offset

0x06

0

DW_OP_LLVM_call_frame_entry_reg

0x07

1

ULEB128 寄存器号

DW_OP_LLVM_undefined

0x08

0

DW_OP_LLVM_aspace_bregx

0x09

2

ULEB128 寄存器号,SLEB128 字节位移

DW_OP_LLVM_piece_end

0x0a

0

DW_OP_LLVM_extend

0x0b

2

ULEB128 位大小,ULEB128 计数

DW_OP_LLVM_select_bit_piece

0x0c

2

ULEB128 位大小,ULEB128 计数

DW_OP_LLVM_aspace_implicit_pointer

TBA

2

DIE 的 4 字节或 8 字节偏移量,SLEB128 字节位移

DW_OP_LLVM_push_iteration

TBA

0

DW_OP_LLVM_overlay

TBA

0

DW_OP_LLVM_bit_overlay

TBA

0

A.7.7.3 位置列表表达式

注意

重命名 DWARF 版本 5 第 7.7.3 节,以反映位置列表是一种 DWARF 表达式。

A.7.12 源语言

注意

这扩充了 DWARF 版本 5 第 7.12 节和表 7.17。

下表给出了附加 DWARF 语言的编码。

表 12 语言编码

语言名称

默认下限

DW_LANG_LLVM_HIP

0x8100

0

A.7.14 地址空间编码

注意

这是 DWARF 版本 5 第 7.13 节“地址类和地址空间编码”之后的新节。

通用地址空间编码 DW_ASPACE_LLVM_none 的值为 0。

A.7.15 内存空间编码

注意

这是 DWARF 版本 5 第 7.13 节“地址类和地址空间编码”之后的新节。

当前定义的内存空间使用的常量的编码在 内存空间编码 中给出。

表 13 内存空间编码

内存空间名称

DW_MSPACE_LLVM_none

0x0000

DW_MSPACE_LLVM_global

0x0001

DW_MSPACE_LLVM_constant

0x0002

DW_MSPACE_LLVM_group

0x0003

DW_MSPACE_LLVM_private

0x0004

DW_MSPACE_LLVM_lo_user

0x8000

DW_MSPACE_LLVM_hi_user

0xffff

A.7.22 行号信息

注意

这扩充了 DWARF 版本 5 第 7.22 节和表 7.27。

下表给出了附加行号头条目格式的编码。

表 14 行号头条目格式编码

行号头条目格式名称

DW_LNCT_LLVM_source

0x2001

DW_LNCT_LLVM_is_MD5

0x2002

A.7.24 调用帧信息

注意

这扩充了 DWARF 版本 5 第 7.24 节和表 7.29。

下表给出了附加调用帧信息指令的编码。

表 15 调用帧指令编码

指令

高 2 位

低 6 位

操作数 1

操作数 2

操作数 3

DW_CFA_LLVM_def_aspace_cfa

0

0x30

ULEB128 寄存器

ULEB128 偏移量

ULEB128 地址空间

DW_CFA_LLVM_def_aspace_cfa_sf

0

0x31

ULEB128 寄存器

SLEB128 偏移量

ULEB128 地址空间

A.7.32 类型签名计算

注意

这(按字母顺序)扩充了 DWARF 版本 5 第 7.32 节,表 7.32。

表 16 类型签名计算中使用的属性

DW_AT_LLVM_address_space

DW_AT_LLVM_memory_space

DW_AT_LLVM_vector_size

A. 按标记值属性(信息性)

注意

这扩充了 DWARF 版本 5 附录 A 和表 A.1。

下表提供了适用于调试信息条目的附加属性。

表 17 按标记值属性

标记名称

适用属性

DW_TAG_base_type

  • DW_AT_LLVM_vector_size

DW_TAG_pointer_type

  • DW_AT_LLVM_address_space

  • DW_AT_LLVM_memory_space

DW_TAG_reference_type

  • DW_AT_LLVM_address_space

  • DW_AT_LLVM_memory_space

DW_TAG_rvalue_reference_type

  • DW_AT_LLVM_address_space

  • DW_AT_LLVM_memory_space

DW_TAG_variable

  • DW_AT_LLVM_memory_space

DW_TAG_formal_parameter

  • DW_AT_LLVM_memory_space

DW_TAG_constant

  • DW_AT_LLVM_memory_space

DW_TAG_compile_unit

  • DW_AT_LLVM_augmentation

DW_TAG_entry_point

  • DW_AT_LLVM_active_lane

  • DW_AT_LLVM_lane_pc

  • DW_AT_LLVM_lanes

  • DW_AT_LLVM_iterations

DW_TAG_inlined_subroutine

  • DW_AT_LLVM_active_lane

  • DW_AT_LLVM_lane_pc

  • DW_AT_LLVM_lanes

  • DW_AT_LLVM_iterations

DW_TAG_subprogram

  • DW_AT_LLVM_active_lane

  • DW_AT_LLVM_lane_pc

  • DW_AT_LLVM_lanes

  • DW_AT_LLVM_iterations

D. 示例(信息性)

注意

这修改了相应的 DWARF 版本 5 附录 D 示例。

D.1 一般描述示例

D.1.3 DWARF 位置描述示例
DW_OP_offset_uconst 4

结构成员距结构实例的开头四个字节。结构实例基址的位置描述假定已在堆栈上。

DW_OP_entry_value 1 DW_OP_reg5 DW_OP_offset_uconst 16

内存位置的地址是通过将 16 加到进入当前子程序时寄存器 5 中包含的值来计算的。

D.2 聚合示例

D.2.1 Fortran 简单数组示例

图 D.4:Fortran 数组示例:DWARF 描述

 1-------------------------------------------------------------------------------
 2! Description for type of 'ap'
 3!
 41$: DW_TAG_array_type
 5        ! No name, default (Fortran) ordering, default stride
 6        DW_AT_type(reference to REAL)
 7        DW_AT_associated(expression=    ! Test 'ptr_assoc' flag
 8            DW_OP_push_object_address
 9            DW_OP_lit<n>                ! where n == offset(ptr_assoc)
10            DW_OP_offset
11            DW_OP_deref
12            DW_OP_lit1                  ! mask for 'ptr_assoc' flag
13            DW_OP_and)
14        DW_AT_data_location(expression= ! Get raw data address
15            DW_OP_push_object_address
16            DW_OP_lit<n>                ! where n == offset(base)
17            DW_OP_offset
18            DW_OP_deref)                ! Type of index of array 'ap'
192$:     DW_TAG_subrange_type
20            ! No name, default stride
21            DW_AT_type(reference to INTEGER)
22            DW_AT_lower_bound(expression=
23                DW_OP_push_object_address
24                DW_OP_lit<n>            ! where n ==
25                                        !   offset(desc, dims) +
26                                        !   offset(dims_str, lower_bound)
27                DW_OP_offset
28                DW_OP_deref)
29            DW_AT_upper_bound(expression=
30                DW_OP_push_object_address
31                DW_OP_lit<n>            ! where n ==
32                                        !   offset(desc, dims) +
33                                        !   offset(dims_str, upper_bound)
34                DW_OP_offset
35                DW_OP_deref)
36!  Note: for the m'th dimension, the second operator becomes
37!  DW_OP_lit<n> where
38!       n == offset(desc, dims)          +
39!                (m-1)*sizeof(dims_str)  +
40!                 offset(dims_str, [lower|upper]_bound)
41!  That is, the expression does not get longer for each successive
42!  dimension (other than to express the larger offsets involved).
433$: DW_TAG_structure_type
44        DW_AT_name("array_ptr")
45        DW_AT_byte_size(constant sizeof(REAL) + sizeof(desc<1>))
464$:     DW_TAG_member
47            DW_AT_name("myvar")
48            DW_AT_type(reference to REAL)
49            DW_AT_data_member_location(constant 0)
505$:     DW_TAG_member
51            DW_AT_name("ap");
52            DW_AT_type(reference to 1$)
53            DW_AT_data_member_location(constant sizeof(REAL))
546$: DW_TAG_array_type
55        ! No name, default (Fortran) ordering, default stride
56        DW_AT_type(reference to 3$)
57        DW_AT_allocated(expression=       ! Test 'ptr_alloc' flag
58            DW_OP_push_object_address
59            DW_OP_lit<n>                  ! where n == offset(ptr_alloc)
60            DW_OP_offset
61            DW_OP_deref
62            DW_OP_lit2                    ! Mask for 'ptr_alloc' flag
63            DW_OP_and)
64        DW_AT_data_location(expression=   ! Get raw data address
65            DW_OP_push_object_address
66            DW_OP_lit<n>                  ! where n == offset(base)
67            DW_OP_offset
68            DW_OP_deref)
697$:     DW_TAG_subrange_type
70            ! No name, default stride
71            DW_AT_type(reference to INTEGER)
72            DW_AT_lower_bound(expression=
73                DW_OP_push_object_address
74                DW_OP_lit<n>              ! where n == ...
75                DW_OP_offset
76                DW_OP_deref)
77            DW_AT_upper_bound(expression=
78                DW_OP_push_object_address
79                DW_OP_lit<n>              ! where n == ...
80                DW_OP_offset
81                DW_OP_deref)
828$: DW_TAG_variable
83        DW_AT_name("arrayvar")
84        DW_AT_type(reference to 6$)
85        DW_AT_location(expression=
86            ...as appropriate...)         ! Assume static allocation
87-------------------------------------------------------------------------------
D.2.3 Fortran 2008 假定秩数组示例

图 D.13:图 D.12 中数组描述符的示例 DWARF

 1----------------------------------------------------------------------------
 210$:  DW_TAG_array_type
 3        DW_AT_type(reference to real)
 4        DW_AT_rank(expression=
 5            DW_OP_push_object_address
 6            DW_OP_lit<n>
 7            DW_OP_offset
 8            DW_OP_deref)
 9        DW_AT_data_location(expression=
10            DW_OP_push_object_address
11            DW_OP_lit<n>
12            DW_OP_offset
13            DW_OP_deref)
1411$:     DW_TAG_generic_subrange
15            DW_AT_type(reference to integer)
16            !   offset of rank in descriptor
17            !   offset of data in descriptor
18            DW_AT_lower_bound(expression=
19            !   Looks up the lower bound of dimension i.
20            !   Operation                       ! Stack effect
21            !   (implicit)                      ! i
22                DW_OP_lit<n>                    ! i sizeof(dim)
23                DW_OP_mul                       ! dim[i]
24                DW_OP_lit<n>                    ! dim[i] offsetof(dim)
25                DW_OP_plus                      ! dim[i]+offset
26                DW_OP_push_object_address       ! dim[i]+offsetof(dim) objptr
27                DW_OP_swap                      ! objptr dim[i]+offsetof(dim)
28                DW_OP_offset                    ! objptr.dim[i]
29                DW_OP_lit<n>                    ! objptr.dim[i] offsetof(lb)
30                DW_OP_offset                    ! objptr.dim[i].lowerbound
31                DW_OP_deref)                    ! *objptr.dim[i].lowerbound
32            DW_AT_upper_bound(expression=
33            !   Looks up the upper bound of dimension i.
34                DW_OP_lit<n>                    ! sizeof(dim)
35                DW_OP_mul
36                DW_OP_lit<n>                    ! offsetof(dim)
37                DW_OP_plus
38                DW_OP_push_object_address
39                DW_OP_swap
40                DW_OP_offset
41                DW_OP_lit<n>                    ! offset of upperbound in dim
42                DW_OP_offset
43                DW_OP_deref)
44            DW_AT_byte_stride(expression=
45            !   Looks up the byte stride of dimension i.
46                ...
47            !   (analogous to DW_AT_upper_bound)
48                )
49----------------------------------------------------------------------------

注意

此示例表明 DW_AT_lower_boundDW_AT_upper_bound 评估 exprloc,其初始堆栈包含秩值。应更新属性定义以说明这一点。

D.2.6 Ada 示例

图 D.20:Ada 示例:DWARF 描述

 1----------------------------------------------------------------------------
 211$:  DW_TAG_variable
 3          DW_AT_name("M")
 4          DW_AT_type(reference to INTEGER)
 512$:  DW_TAG_array_type
 6          ! No name, default (Ada) order, default stride
 7          DW_AT_type(reference to INTEGER)
 813$:      DW_TAG_subrange_type
 9              DW_AT_type(reference to INTEGER)
10              DW_AT_lower_bound(constant 1)
11              DW_AT_upper_bound(reference to variable M at 11$)
1214$:  DW_TAG_variable
13          DW_AT_name("VEC1")
14          DW_AT_type(reference to array type at 12$)
15      ...
1621$:  DW_TAG_subrange_type
17          DW_AT_name("TEENY")
18          DW_AT_type(reference to INTEGER)
19          DW_AT_lower_bound(constant 1)
20          DW_AT_upper_bound(constant 100)
21      ...
2226$:  DW_TAG_structure_type
23          DW_AT_name("REC2")
2427$:      DW_TAG_member
25              DW_AT_name("N")
26              DW_AT_type(reference to subtype TEENY at 21$)
27              DW_AT_data_member_location(constant 0)
2828$:      DW_TAG_array_type
29              ! No name, default (Ada) order, default stride
30              ! Default data location
31              DW_AT_type(reference to INTEGER)
3229$:          DW_TAG_subrange_type
33                  DW_AT_type(reference to subrange TEENY at 21$)
34                  DW_AT_lower_bound(constant 1)
35                  DW_AT_upper_bound(reference to member N at 27$)
3630$:      DW_TAG_member
37              DW_AT_name("VEC2")
38              DW_AT_type(reference to array "subtype" at 28$)
39              DW_AT_data_member_location(machine=
40                  DW_OP_lit<n>                ! where n == offset(REC2, VEC2)
41                  DW_OP_offset)
42      ...
4341$:  DW_TAG_variable
44          DW_AT_name("OBJ2B")
45          DW_AT_type(reference to REC2 at 26$)
46          DW_AT_location(...as appropriate...)
47----------------------------------------------------------------------------

C. 更多示例

这些扩展中功能的 AMD GPU 特定用法,包括示例,可在AMDGPU 后端用户指南DWARF 调试信息 节中找到。

注意

当对位置描述进行操作时,将示例更改为使用 DW_OP_LLVM_offset 而不是 DW_OP_add

需要提供新功能的示例。

D. 参考

  1. [AMD] 超威半导体(AMD)

  2. [AMD-ROCgdb] AMD ROCm 调试器 (ROCgdb)

  3. [AMD-ROCm] AMD ROCm 平台

  4. [AMDGPU-DWARF-LOC] 允许 DWARF 表达式堆栈上的位置描述

  5. [AMDGPU-LLVM] AMDGPU LLVM 后端用户指南

  6. [CUDA] Nvidia CUDA 语言

  7. [DWARF] DWARF 调试信息格式

  8. [ELF] 可执行和可链接格式 (ELF)

  9. [GCC] GCC:GNU 编译器套件

  10. [GDB] GDB:GNU 项目调试器

  11. [HIP] HIP 编程指南

  12. [HSA] 异构系统架构 (HSA) 基金会

  13. [LLVM] LLVM 编译器基础设施

  14. [OpenCL] OpenCL 规范版本 2.0

  15. [Perforce-TotalView] Perforce TotalView HPC 调试软件

  16. [SEMVER] 语义化版本 2.0.0