LLVM 扩展

简介

本文档描述了 LLVM 寻求兼容性的工具和格式的扩展。

通用汇编语法

C99 风格的十六进制浮点常量

如果需要,LLVM 的汇编器允许以 C99 的十六进制格式而不是十进制格式编写浮点常量。

.section .data
.float 0x1c2.2ap3

特定于机器的汇编语法

X86/COFF 相关

重定位

支持以下附加的重定位类型

@IMGREL(仅限 AT&T 语法)生成一个图像相对重定位,对应于 COFF 重定位类型 IMAGE_REL_I386_DIR32NB (32 位) 或 IMAGE_REL_AMD64_ADDR32NB (64 位)。

.text
fun:
  mov foo@IMGREL(%ebx, %ecx, 4), %eax

.section .pdata
  .long fun@IMGREL
  .long (fun@imgrel + 0x3F)
  .long $unwind$fun@imgrel

.secrel32 生成一个重定位,对应于 COFF 重定位类型 IMAGE_REL_I386_SECREL (32 位) 或 IMAGE_REL_AMD64_SECREL (64 位)。

.secidx 重定位生成包含目标的节的索引。它对应于 COFF 重定位类型 IMAGE_REL_I386_SECTION (32 位) 或 IMAGE_REL_AMD64_SECTION (64 位)。

.section .debug$S,"rn"
  .long 4
  .long 242
  .long 40
  .secrel32 _function_name + 0
  .secidx   _function_name
  ...

.linkonce 指令

语法

.linkonce [ comdat type ]

支持的 COMDAT 类型

discard

丢弃具有相同 COMDAT 符号的重复节。如果没有指定类型,则这是默认值。

one_only

如果符号被多次定义,链接器会发出错误。

same_size

重复项被丢弃,但如果任何重复项的大小不同,链接器会发出错误。

same_contents

重复项被丢弃,但如果任何重复项的内容不完全相同,链接器会发出错误。

largest

链接重复项中最大的节。

newest

链接重复项中最新的节。

.section .text$foo
.linkonce
  ...

.section 指令

MC 支持在 .section 的末尾传递 .linkonce 中的信息。例如,以下两个代码是等效的

.section secName, "dr", discard, "Symbol1"
.globl Symbol1
Symbol1:
.long 1
.section secName, "dr"
.linkonce discard
.globl Symbol1
Symbol1:
.long 1

请注意,在组合形式中,COMDAT 符号是显式的。此扩展的存在是为了支持在不同的 COMDAT 中具有相同名称的多个节

.section secName, "dr", discard, "Symbol1"
.globl Symbol1
Symbol1:
.long 1

.section secName, "dr", discard, "Symbol2"
.globl Symbol2
Symbol2:
.long 1

除了 .linkonce 允许的类型外,.section 也接受 associative。含义是,如果链接了某个其他 COMDAT 节,则链接该节。此其他节由此指令中的 comdat 符号指示。它可以是关联节中定义的任何符号,但通常是关联节的 comdat。

以下限制适用于关联节

  1. 它必须是 COMDAT 节。

  2. 它不能是另一个关联的 COMDAT 节。

在以下示例中,符号 sym.foo 的 comdat 符号,而 .bar.foo 关联。

.section        .foo,"bw",discard, "sym"
.section        .bar,"rd",associative, "sym"

MC 在 COFF .section 指令中支持以下标志

  • b: BSS 节 (IMAGE_SCN_CNT_INITIALIZED_DATA)

  • d: 数据节 (IMAGE_SCN_CNT_UNINITIALIZED_DATA)

  • n: 节未加载 (IMAGE_SCN_LNK_REMOVE)

  • r: 只读

  • s: 共享节

  • w: 可写

  • x: 可执行节

  • y: 不可读

  • D: 可丢弃 (IMAGE_SCN_MEM_DISCARDABLE)

这些标志都与 gas 兼容,除了 D 标志,gnu as 不支持该标志。为了与 gas 兼容,名称以 “.debug” 开头的节会被隐式地丢弃。

ARM64/COFF 相关

重定位

支持以下附加的符号变体

:secrel_lo12: 生成一个重定位,对应于 COFF 重定位类型 IMAGE_REL_ARM64_SECREL_LOW12AIMAGE_REL_ARM64_SECREL_LOW12L

:secrel_hi12: 生成一个重定位,对应于 COFF 重定位类型 IMAGE_REL_ARM64_SECREL_HIGH12A

add x0, x0, :secrel_hi12:symbol
ldr x0, [x0, :secrel_lo12:symbol]

add x1, x1, :secrel_hi12:symbol
add x1, x1, :secrel_lo12:symbol
...

ELF 相关

.section 指令

为了支持创建具有相同名称和 comdat 的多个节,可以在 .section 指令的末尾添加唯一的数字。例如,以下代码创建了两个名为 .text 的节。

.section        .text,"ax",@progbits,unique,1
nop

.section        .text,"ax",@progbits,unique,2
nop

唯一的数字根本不会出现在结果对象中。它仅在汇编器中用于区分节。

“o”标志映射到 SHF_LINK_ORDER。如果它存在,则必须给出一个符号,该符号标识要放置在 .sh_link 中的节。

.section .foo,"a",@progbits
.Ltmp:
.section .bar,"ao",@progbits,.Ltmp

这等效于

.section .foo,"a",@progbits
.section .bar,"ao",@progbits,.foo

.linker-options 节(链接器选项)

为了支持从前端向链接器传递链接器选项,使用类型为 SHT_LLVM_LINKER_OPTIONS 的特殊节(通常命名为 .linker-options,尽管名称并不重要,因为它由类型标识)。此节的内容是链接器要考虑的指令的简单成对编码。字符串被编码为标准的 null 结尾 UTF-8 字符串。它们以内联方式发出,以避免链接器遍历对象文件来检索值。允许链接器不遵守该选项,而是向用户提供警告/错误,说明请求的选项未被遵守。

该节的类型为 SHT_LLVM_LINKER_OPTIONS,并具有 SHF_EXCLUDE 标志,以确保不支持该特性的链接器将该节视为不透明,并且不会将其发出到最终链接的二进制文件中。

这等效于以下原始汇编

.section ".linker-options","e",@llvm_linker_options
.asciz "option 1"
.asciz "value 1"
.asciz "option 2"
.asciz "value 2"

指定了以下指令

  • lib

    参数标识要链接的库。将在默认库搜索路径和任何指定的库搜索路径(指定到此点)中查找该库。

  • libpath

    参数标识一个附加的库搜索路径,在包含此选项后查找库时应考虑该路径。

SHT_LLVM_DEPENDENT_LIBRARIES 节(依赖库)

此节包含指定要由链接器添加到链接中的库的字符串。

该节应由链接器使用,而不是写入输出。

字符串被编码为标准的 null 结尾 UTF-8 字符串。

例如

.section ".deplibs","MS",@llvm_dependent_libraries,1
.asciz "library specifier 1"
.asciz "library specifier 2"

库说明符的解释由使用者链接器定义。

SHT_LLVM_CALL_GRAPH_PROFILE 节(调用图谱分析)

此节用于将调用图谱分析传递给链接器,链接器可以使用该分析来优化节的放置。它包含(从符号,到符号,权重)元组的序列。

它应具有 SHT_LLVM_CALL_GRAPH_PROFILE 类型 (0x6fff4c02),应设置 SHF_EXCLUDE 标志,sh_link 成员应保存关联符号表的节头索引,并且应具有 16 的 sh_entsize。它应命名为 .llvm.call-graph-profile

节的内容应为 Elf_CGProfile 条目的序列。

typedef struct {
  Elf_Word cgp_from;
  Elf_Word cgp_to;
  Elf_Xword cgp_weight;
} Elf_CGProfile;
cgp_from

边的源的符号索引。

cgp_to

边的目标的符号索引。

cgp_weight

边的权重。

这在汇编中表示为

.cg_profile from, to, 42

.cg_profile 指令在文件末尾处理。如果 fromto 是未定义的临时符号,则会出错。如果任一符号是临时符号,则使用节符号代替。如果任一符号未定义,则将该符号定义为如果在文件末尾写入了 .weak symbol。这会强制符号出现在符号表中。

SHT_LLVM_ADDRSIG 节(地址重要性表)

此节用于将符号标记为地址重要的,即符号的地址用于比较或泄漏到转换单元之外。它与缺少 LLVM 属性 unnamed_addrlocal_unnamed_addr 具有相同的含义。

任何对象文件中未标记为地址重要的符号引用的任何节都可能被链接器安全地合并,而不会破坏 C 和 C++ 语言标准提供的地址唯一性保证。

节的内容是 ULEB128 编码的整数序列,这些整数引用地址重要符号的符号表索引。

有两个相关的汇编指令

.addrsig

这指示汇编器发出地址重要性表。如果没有此指令,所有符号都被认为是地址重要的。

.addrsig_sym sym

如果 sym 在文件中其他任何地方都没有被引用或定义,则此指令是空操作。否则,将 sym 标记为地址重要的。

SHT_LLVM_SYMPART 节(符号分区规范)

此节用于标记符号及其所属的分区.llvm_sympart 节由一个 null 结尾的字符串组成,该字符串指定分区的名称,后跟一个引用属于该分区的符号的重定位。它可以按如下方式构造

.section ".llvm_sympart","",@llvm_sympart
.asciz "libpartition.so"
.word symbol_in_partition

SHT_LLVM_BB_ADDR_MAP 节(基本块地址映射)

此节存储基本块的二进制地址以及其他相关元数据。此信息可用于将二进制分析(如 perf 分析)直接映射到机器基本块。此节使用 -basic-block-address-map 发出,并将包含每个函数的 BB 地址映射表。

SHT_LLVM_BB_ADDR_MAP 类型提供向后兼容性,以允许读取旧编译器生成的旧版本 BB 地址映射。每个函数条目都以一个版本字节开头,该版本字节指定要使用的编码版本。当前支持以下版本控制方案。

版本 1(最新):基本块地址偏移量是相对于前一个块的末尾计算的。

示例

.section  ".llvm_bb_addr_map","",@llvm_bb_addr_map
.byte     1                             # version number
.byte     0                             # feature byte (reserved for future use)
.quad     .Lfunc_begin0                 # address of the function
.byte     2                             # number of basic blocks
# BB record for BB_0
 .uleb128  .Lfunc_beign0-.Lfunc_begin0  # BB_0 offset relative to function entry (always zero)
 .uleb128  .LBB_END0_0-.Lfunc_begin0    # BB_0 size
 .byte     x                            # BB_0 metadata
# BB record for BB_1
 .uleb128  .LBB0_1-.LBB_END0_0          # BB_1 offset relative to the end of last block (BB_0).
 .uleb128  .LBB_END0_1-.LBB0_1          # BB_1 size
 .byte     y                            # BB_1 metadata

版本 0:基本块地址偏移量是相对于函数地址计算的。这使用未版本化的 SHT_LLVM_BB_ADDR_MAP_V0 节类型,并且在语义上等效于使用版本字段为零的 SHT_LLVM_BB_ADDR_MAP

示例

.section  ".llvm_bb_addr_map","",@llvm_bb_addr_map_v0
.quad     .Lfunc_begin0                 # address of the function
.byte     2                             # number of basic blocks
# BB record for BB_0
 .uleb128  .Lfunc_beign0-.Lfunc_begin0  # BB_0 offset relative to the function entry (always zero)
 .uleb128  .LBB_END0_0-.Lfunc_begin0    # BB_0 size
 .byte     x                            # BB_0 metadata
# BB record for BB_1
 .uleb128  .LBB0_1-.Lfunc_begin0        # BB_1 offset relative to the function entry
 .uleb128  .LBB_END0_1-.LBB0_1          # BB_1 size
 .byte     y                            # BB_1 metadata
PGO 分析映射

PGO 相关分析数据可以在 SHT_LLVM_BB_ADDR_MAP 中的每个函数之后通过可选的 pgo-analysis-map 标志发出。当前支持的分析是函数入口计数、基本块频率和分支概率。

每个分析都通过功能字节中的一位启用或禁用。当前这些位是

  1. 函数入口计数 - 从 PGO 分析中获取的函数被调用的次数。如果未使用 PGO 或在分析中未遇到该函数,则此值始终为零。

  2. 基本块频率 - 编码为从 MBFI 分析中获取的原始块频率值。此值是一个整数,它编码相对于入口块的相对频率。更多信息可以在 'llvm/Support/BlockFrequency.h' 中找到。

  3. 分支概率 - 编码为从 MBPI 分析中获取的分支概率的原始分子。此值是在 'llvm/Support/BranchProbability.h' 中定义的定点比率的分子。它指示在执行期间块后跟给定后继块的概率。

此额外数据需要版本 2 或更高版本。这是必要的,因为基本块的后继者不知道其索引,但会知道其 BB ID。

带有 PGO 数据的 BBAddrMap 示例

.section  ".llvm_bb_addr_map","",@llvm_bb_addr_map
.byte     2                             # version number
.byte     7                             # feature byte - PGO analyses enabled mask
.quad     .Lfunc_begin0                 # address of the function
.uleb128  4                             # number of basic blocks
# BB record for BB_0
 .uleb128  0                            # BB_0 BB ID
 .uleb128  .Lfunc_begin0-.Lfunc_begin0  # BB_0 offset relative to function entry (always zero)
 .uleb128  .LBB_END0_0-.Lfunc_begin0    # BB_0 size
 .byte     0x18                         # BB_0 metadata (multiple successors)
# BB record for BB_1
 .uleb128  1                            # BB_1 BB ID
 .uleb128  .LBB0_1-.LBB_END0_0          # BB_1 offset relative to the end of last block (BB_0).
 .uleb128  .LBB_END0_1-.LBB0_1          # BB_1 size
 .byte     0x0                          # BB_1 metadata (two successors)
# BB record for BB_2
 .uleb128  2                            # BB_2 BB ID
 .uleb128  .LBB0_2-.LBB_END1_0          # BB_2 offset relative to the end of last block (BB_1).
 .uleb128  .LBB_END0_2-.LBB0_2          # BB_2 size
 .byte     0x0                          # BB_2 metadata (one successor)
# BB record for BB_3
 .uleb128  3                            # BB_3 BB ID
 .uleb128  .LBB0_3-.LBB_END0_2          # BB_3 offset relative to the end of last block (BB_2).
 .uleb128  .LBB_END0_3-.LBB0_3          # BB_3 size
 .byte     0x0                          # BB_3 metadata (zero successors)
# PGO Analysis Map
.uleb128  1000                          # function entry count (only when enabled)
# PGO data record for BB_0
 .uleb128  1000                         # BB_0 basic block frequency (only when enabled)
 .uleb128  3                            # BB_0 successors count (only enabled with branch probabilities)
 .uleb128  1                            # BB_0 successor 1 BB ID (only enabled with branch probabilities)
 .uleb128  0x22222222                   # BB_0 successor 1 branch probability (only enabled with branch probabilities)
 .uleb128  2                            # BB_0 successor 2 BB ID (only enabled with branch probabilities)
 .uleb128  0x33333333                   # BB_0 successor 2 branch probability (only enabled with branch probabilities)
 .uleb128  3                            # BB_0 successor 3 BB ID (only enabled with branch probabilities)
 .uleb128  0xaaaaaaaa                   # BB_0 successor 3 branch probability (only enabled with branch probabilities)
# PGO data record for BB_1
 .uleb128  133                          # BB_1 basic block frequency (only when enabled)
 .uleb128  2                            # BB_1 successors count (only enabled with branch probabilities)
 .uleb128  2                            # BB_1 successor 1 BB ID (only enabled with branch probabilities)
 .uleb128  0x11111111                   # BB_1 successor 1 branch probability (only enabled with branch probabilities)
 .uleb128  3                            # BB_1 successor 2 BB ID (only enabled with branch probabilities)
 .uleb128  0x11111111                   # BB_1 successor 2 branch probability (only enabled with branch probabilities)
# PGO data record for BB_2
 .uleb128  18                           # BB_2 basic block frequency (only when enabled)
 .uleb128  1                            # BB_2 successors count (only enabled with branch probabilities)
 .uleb128  3                            # BB_2 successor 1 BB ID (only enabled with branch probabilities)
 .uleb128  0xffffffff                   # BB_2 successor 1 branch probability (only enabled with branch probabilities)
# PGO data record for BB_3
 .uleb128  1000                         # BB_3 basic block frequency (only when enabled)
 .uleb128  0                            # BB_3 successors count (only enabled with branch probabilities)

SHT_LLVM_OFFLOADING 节(卸载数据)

此节存储用于执行卸载设备链接和执行的二进制数据,从而创建胖二进制文件。此节在编译卸载语言(如 OpenMP 或 CUDA)期间发出。如果数据旨在仅供设备链接器使用,则应使用 SHF_EXCLUDE 标志,以便自动从最终可执行文件或共享库中剥离。

此节中存储的二进制数据符合用于存储卸载元数据的自定义二进制格式。此格式实际上是一个字符串表,其中包含元数据以及设备映像。

SHT_LLVM_LTO 节(用于 Fat LTO 的 LLVM 位代码)

此节存储用于在链接时执行常规 LTO 或 ThinLTO 的 LLVM 位代码。当编译器启用 Fat LTO 时,会生成此节。此节具有 SHF_EXCLUDE 标志,因此会从最终可执行文件或共享库中剥离。

SHT_LLVM_JT_SIZES 节(跳转表地址和大小)

此节存储(跳转表地址,条目数)对。此信息对于需要静态重建可执行文件的控制流的工具很有用。

CodeView 相关

.cv_file 指令

语法

.cv_file 文件编号 文件名 [ 校验和 ] [ 校验和类型 ]

.cv_func_id 指令

引入一个函数 ID,该 ID 可以与 .cv_loc 一起使用。

语法

.cv_func_id 函数 ID

.cv_inline_site_id 指令

引入一个函数 ID,该 ID 可以与 .cv_loc 一起使用。包括用于调用者行表的 inlined at 源位置信息,无论调用者是真实函数还是另一个内联调用站点。

语法

.cv_inline_site_id 函数 ID within 函数 inlined_at 文件编号 行号 [ 列号 ]

.cv_loc 指令

第一个数字是文件编号,必须先前已使用 .file 指令分配,第二个数字是行号,可选的第三个数字是列位置(如果未指定则为零)。其余可选项目是 .loc 子指令。

语法

.cv_loc 函数 ID 文件编号 [ 行号 ] [ 列号 ] [ prologue_end ] [ is_stmt ]

.cv_linetable 指令

语法

.cv_linetable 函数 ID , 函数开始 , 函数结束

.cv_inline_linetable 指令

语法

.cv_inline_linetable 主函数 ID , 文件编号 行号 函数开始 函数结束

.cv_def_range 指令

GapStartGapEnd 选项可以根据需要重复。

语法

.cv_def_range 范围开始 范围结束 [ GapStart GapEnd ] , 字节数

.cv_stringtable 指令

.cv_filechecksums 指令

.cv_filechecksumoffset 指令

语法

.cv_filechecksumoffset 文件编号

.cv_fpo_data 指令

语法

.cv_fpo_data procsym

目标特定行为

X86

重定位

@ABS8 可以应用于作为立即操作数出现在指令中的符号,这些指令对于该操作数具有 8 位立即形式。它导致汇编器对符号使用 8 位形式和 8 位重定位(例如 R_386_8R_X86_64_8)。

例如

cmpq $foo@ABS8, %rdi

这导致汇编器选择 64 位 cmpq 指令的形式,该指令采用符号扩展为 64 位的 8 位立即操作数,而不是 cmpq $foo, %rdi,后者采用 32 位立即操作数。这也与 cmpb $foo, %dil 不同,后者是 8 位比较。

@GOTPCREL_NORELAX 可以代替 @GOTPCREL 使用,以保证汇编器发出 R_X86_64_GOTPCREL 重定位,而不是可松弛的 R_X86_64[_REX]_GOTPCRELX 重定位。

ARM 上的 Windows

栈探测发射

参考实现 (Microsoft Visual Studio 2012) 以以下方式发出栈探测

movw r4, #constant
bl __chkstk
sub.w sp, sp, r4

但是,这具有 32 MiB (±16MiB) 的限制。为了容纳更大的二进制文件,LLVM 支持使用 -mcmodel=large 通过细微的偏差来允许 4GiB 范围。它将生成一个间接跳转,如下所示

movw r4, #constant
movw r12, :lower16:__chkstk
movt r12, :upper16:__chkstk
blx r12
sub.w sp, sp, r4

变长数组

参考实现 (Microsoft Visual Studio 2012) 不允许发出变长数组 (VLA)。

Windows ARM Itanium ABI 通过添加对发出动态栈分配的支持来扩展基本 ABI。当发出可变栈分配时,会无条件地发出对 __chkstk 的调用,以确保正确设置保护页。此栈探测发射的处理方式类似于标准栈探测发射。

MSVC 环境目前不发出 VLA 的代码。

ARM64 上的 Windows

栈探测发射

参考实现 (Microsoft Visual Studio 2017) 以以下方式发出栈探测

mov x15, #constant
bl __chkstk
sub sp, sp, x15, lsl #4

但是,这具有 256 MiB (±128MiB) 的限制。为了容纳更大的二进制文件,LLVM 支持使用 -mcmodel=large 通过细微的偏差来允许 8GiB (±4GiB) 范围。它将生成一个间接跳转,如下所示

mov x15, #constant
adrp x16, __chkstk
add x16, x16, :lo12:__chkstk
blr x16
sub sp, sp, x15, lsl #4