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

除了 D 标志外,所有这些标志都与 gas 兼容,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

唯一的数字根本不会出现在生成的 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 指令在文件末尾处理。如果 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 节包含一个指定分区名称的空终止字符串,后跟一个引用属于该分区的符号的重定位。它可以按如下方式构建

.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 标志发出。目前支持的分析包括函数入口计数、基本块频率和分支概率。

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

  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 节(用于胖 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 FunctionId within Function inlined_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 指令

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

语法

.cv_def_range 起始范围 结束范围 [ 间隙起始 间隙结束 ] , 字节数

.cv_stringtable 指令

.cv_filechecksums 指令

.cv_filechecksumoffset 指令

语法

.cv_filechecksumoffset 文件编号

.cv_fpo_data 指令

语法

.cv_fpo_data 过程符号

目标特定行为

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