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 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 RangeStart RangeEnd [ GapStart GapEnd ] , bytes

.cv_stringtable 指令

.cv_filechecksums 指令

.cv_filechecksumoffset 指令

語法

.cv_filechecksumoffset FileNumber

.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 位元立即運算元,而不是採用 32 位元立即運算元的 cmpq $foo, %rdi。這也與 8 位元比較 cmpb $foo, %dil 不同。

@GOTPCREL_NORELAX 可以代替 @GOTPCREL 使用,以保證組譯器發射 R_X86_64_GOTPCREL 重定位,而不是可放鬆的 R_X86_64[_REX]_GOTPCRELX 重定位。

Windows on ARM

堆疊探測發射

參考實作 (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 的程式碼。

Windows on ARM64

堆疊探測發射

參考實作 (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