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 類型 ]

支援的 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-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 相關的分析資料可以透過可選的 pgo-analysis-map 標誌在 SHT_LLVM_BB_ADDR_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 檔案編號 檔案名稱 [ 校驗和 ] [ 校驗和種類 ]

.cv_func_id 指令

引入一個可以用於 .cv_loc 的函數 ID。

語法

.cv_func_id 函數 ID

.cv_inline_site_id 指令

引入一個可以用於 .cv_loc 的函數 ID。 包含用於呼叫端行表的 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 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

這會導致組譯器選擇採用 8 位元立即數運算元並符號擴展為 64 位元的 64 位元 cmpq 指令形式,而不是採用 32 位元立即數運算元的 cmpq $foo, %rdi。這也不同於進行 8 位元比較的 cmpb $foo, %dil

@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