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
)
除了 D
标志外,所有这些标志都与 gas 兼容,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
唯一的数字根本不会出现在生成的 object 文件中。它仅在汇编器中用于区分段。
‘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
节(调用图概要)¶
本节用于将调用图概要传递给链接器,链接器可以使用它来优化节的放置。它包含一系列 (from symbol, to symbol, weight) 元组。
它应具有 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
节包含一个指定分区名称的空终止字符串,后跟一个引用属于该分区的符号的重定位。它可以按如下方式构建
.section ".llvm_sympart","",@llvm_sympart
.asciz "libpartition.so"
.word symbol_in_partition
SHT_LLVM_BB_ADDR_MAP
节(基本块地址映射)¶
本节存储基本块的二进制地址以及其他相关元数据。此信息可用于将二进制概要文件(如 perf 概要文件)直接映射到机器基本块。本节使用 -basic-block-sections=labels
发出,并将包含每个函数的 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
节(用于胖 LTO 的 LLVM 位码)¶
本节存储用于在链接时执行常规 LTO 或 ThinLTO 的 LLVM 位码。在编译器启用胖 LTO 时生成此节。此节具有 SHF_EXCLUDE
标志,以便将其从最终的可执行文件或共享库中剥离。
CodeView 相关¶
.cv_file
指令¶
- 语法
.cv_file
FileNumber FileName [ checksum ] [ checksumkind ]
.cv_func_id
指令¶
引入一个函数 ID,可与 .cv_loc
一起使用。
- 语法
.cv_func_id
FunctionId
.cv_inline_site_id
指令¶
引入一个函数 ID,可与 .cv_loc
一起使用。包括 inlined at
源位置信息,用于在调用者的行表中使用,无论调用者是真实函数还是另一个内联调用站点。
- 语法
.cv_inline_site_id
FunctionIdwithin
Functioninlined_at
FileNumber Line [ Column ]
.cv_loc
指令¶
第一个数字是文件编号,必须以前使用 .file
指令分配过,第二个数字是行号,第三个数字(如果指定)是列位置(如果未指定则为零)。其余可选项目是 .loc
子指令。
- 语法
.cv_loc
FunctionId FileNumber [ Line ] [ Column ] [ prologue_end ] [is_stmt
value ]
.cv_linetable
指令¶
- 语法
.cv_linetable
FunctionId,
FunctionStart,
FunctionEnd
.cv_inline_linetable
指令¶
- 语法
.cv_inline_linetable
PrimaryFunctionId,
FileNumber Line FunctionStart FunctionEnd
.cv_def_range
指令¶
可以根据需要重复 GapStart 和 GapEnd 选项。
- 语法
.cv_def_range
起始范围 结束范围 [ 间隙起始 间隙结束 ],
字节数
.cv_stringtable
指令¶
.cv_filechecksums
指令¶
.cv_filechecksumoffset
指令¶
- 语法
.cv_filechecksumoffset
文件编号
.cv_fpo_data
指令¶
- 语法
.cv_fpo_data
过程符号
目标特定行为¶
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