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。
以下限制适用于关联节
它必须是 COMDAT 节。
它不能是另一个关联的 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_LOW12A
或 IMAGE_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
指令在文件末尾处理。如果 from
或 to
是未定义的临时符号,则会出错。如果任一符号是临时符号,则使用节符号代替。如果任一符号未定义,则将该符号定义为如果在文件末尾写入了 .weak symbol
。这会强制符号出现在符号表中。
SHT_LLVM_ADDRSIG
节(地址重要性表)¶
此节用于将符号标记为地址重要的,即符号的地址用于比较或泄漏到转换单元之外。它与缺少 LLVM 属性 unnamed_addr
和 local_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
标志发出。当前支持的分析是函数入口计数、基本块频率和分支概率。
每个分析都通过功能字节中的一位启用或禁用。当前这些位是
函数入口计数 - 从 PGO 分析中获取的函数被调用的次数。如果未使用 PGO 或在分析中未遇到该函数,则此值始终为零。
基本块频率 - 编码为从 MBFI 分析中获取的原始块频率值。此值是一个整数,它编码相对于入口块的相对频率。更多信息可以在 'llvm/Support/BlockFrequency.h' 中找到。
分支概率 - 编码为从 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
函数 IDwithin
函数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
指令¶
GapStart 和 GapEnd 选项可以根据需要重复。
- 语法
.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_8
或 R_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