允許 DWARF 表達式堆疊上的位置描述¶
1. 擴展¶
在 DWARF 5 中,表達式使用類型化值堆疊、獨立的位置區域和獨立的 loclist 機制進行評估。此擴展將所有這三種機制統一到單一通用的 DWARF 表達式評估模型中,該模型允許類型化值和位置描述在評估堆疊上進行操作。堆疊上同時支援單一和多個位置描述。此外,呼叫框架資訊 (CFI) 得到擴展,以支援位置描述的完整通用性。這是以向後相容於 DWARF 5 的方式完成的。此擴展涉及對 DWARF 5 第 2.5 節(第 26-38 頁)、第 2.6 節(第 38-45 頁)和第 6.4 節(第 171-182 頁)的修改。
此擴展允許以增量、一致且可組合的方式對位置描述執行操作。它允許定義少量的操作來滿足異質裝置的需求,並為非異質裝置帶來好處。它作為基礎,為已提出的其他問題提供支援,這些問題將使所有裝置受益。
還探索了其他方法,包括添加專門的操作和規則。然而,這些方法導致需要更多無法組合的操作。它還導致了具有上下文敏感語義和必須定義的邊角案例的操作。觀察到,對於生產者和消費者來說,眾多專門的上下文敏感操作比少量具有一致語義(無論上下文如何)的通用可組合操作更難。
首先,2. 異質運算裝置 節描述了異質裝置及其具有的 DWARF 5 未解決的功能。然後,3. DWARF5 節簡要概述了 DWARF 5 表達式評估模型,重點介紹了支援異質功能的困難。接下來,4. 擴展解決方案 節概述了該提案,使用簡化的範例來說明它如何解決異質裝置的問題,並使非異質裝置受益。然後,5. 結論 節涵蓋了總體結論。附錄 A. DWARF 偵錯資訊格式版本 5 的變更 給出了相對於 DWARF 版本 5 標準的變更。最後,附錄 B. 更多資訊 提供了更多資訊的參考。
2. 異質運算裝置¶
GPU 和其他異質運算裝置具有 CPU 運算裝置不常見的功能。
這些裝置通常比 CPU 擁有更多的暫存器。這有助於減少記憶體存取,由於同時執行的執行緒數量更多,記憶體存取往往比 CPU 更昂貴。除了 CPU 的傳統純量暫存器外,這些裝置通常還具有許多寬向量暫存器。
它們可能支援遮罩向量指令,編譯器使用這些指令將高階語言執行緒對應到向量暫存器的通道上。因此,當向量指令執行時,多個語言執行緒以鎖步方式執行。這稱為單指令多執行緒 (SIMT) 執行。
除了 CPU 的單一全域記憶體位址空間之外,GPU 還可以有多個記憶體位址空間。這些額外的位址空間使用不同的指令存取,並且通常是特定執行緒或執行緒群組的本機位址空間。
例如,GPU 可能具有每個執行緒區塊的位址空間,該位址空間實作為暫存記憶體,並具有明確的硬體支援,可將各部分隔離到作為單一執行緒區塊建立的特定執行緒群組。
GPU 也可能以非線性方式使用全域記憶體。例如,為了有效支援提供 SIMT 每通道位址空間,可能存在支援交錯存取的指令。
透過最佳化,來源變數可以位於這些不同的儲存類型中。SIMT 執行需要位置能夠表達執行時間定義的向量暫存器片段的選擇。由於位置更複雜,因此能夠分解其計算是有益的,這需要統一支援所有位置類型,否則就必須重複。
3. DWARF 5¶
在介紹支援異質裝置的建議解決方案之前,將簡要概述 DWARF 5 表達式評估模型,以重點介紹擴展所解決的方面。
3.1 DWARF 如何將來源語言對應到硬體¶
DWARF 是一種標準化的方式來指定偵錯資訊。它描述了來源語言實體,例如編譯單元、函數、類型、變數等。它可以直接嵌入到程式碼物件可執行檔的區段中,或分割成它們引用的單獨檔案。
DWARF 在來源程式語言實體及其硬體表示之間建立對應。例如
它將硬體指令程式計數器對應到來源語言程式行,反之亦然。
它將來源語言函數對應到其進入點的硬體指令程式計數器。
它將來源語言變數對應到在特定程式計數器處的硬體位置。
它提供資訊以允許對來源語言函數呼叫堆疊的硬體暫存器進行虛擬展開。
此外,它還提供有關來源語言程式的許多其他資訊。
特別是,來源語言實體可以對應到硬體位置的方式存在很大的多樣性。位置可能涉及執行時間值。例如,來源語言變數位置可以是
在暫存器中。
在記憶體位址。
從目前堆疊指標的偏移量。
最佳化掉,但具有已知的編譯器時間值。
最佳化掉,但具有未知值,例如未使用的變數的情況。
分散在上述位置類型的組合中。
在記憶體位址,但也暫時載入到暫存器中。
為了支援這一點,DWARF 5 定義了一種豐富的表達式語言,包括 loclist 表達式和操作表達式。Loclist 表達式允許結果根據 PC 而變化。操作表達式由在簡單堆疊機器上評估的操作列表組成。
DWARF 表達式可以用作不同偵錯資訊條目 (DIE) 的不同屬性的值。DWARF 表達式也可以用作呼叫框架資訊 (CFI) 條目操作的引數。表達式在由其使用位置決定的上下文中評估。上下文可能包括
表達式是否需要產生值或實體的位置。
目前的執行點,包括程序、執行緒、PC 和堆疊框架。
某些表達式在堆疊使用特定值或使用 DW_OP_push_object_address 操作可用的基礎物件位置初始化的情況下進行評估。
3.2 範例¶
以下範例說明了如何在 DWARF 5 中評估涉及操作的 DWARF 表達式。DWARF 也有涉及位置列表的表達式,這些表達式在這些範例中未涵蓋。
3.2.1 動態陣列大小¶
第一個範例是與 DIE 屬性關聯的操作表達式,該屬性提供了動態陣列類型中元素的數量。此類屬性規定表達式必須在提供值結果類型的上下文中評估。
在這個假設的範例中,編譯器在記憶體中分配了一個陣列描述符,並將描述符的位址放在架構暫存器 SGPR0 中。陣列描述符的第一個位置是陣列的執行時間大小。
用於檢索陣列動態大小的可能表達式是
DW_OP_regval_type SGPR0 Generic
DW_OP_deref
表達式一次評估一個操作。操作有運算元,可以彈出和推送堆疊上的條目。
表達式評估從第一個 DW_OP_regval_type 操作開始。此操作讀取由其第一個運算元 SGPR0 指定的架構暫存器的目前值。第二個運算元指定要讀取的資料大小。讀取的值被推送到堆疊上。每個堆疊元素都是一個值及其關聯的類型。
類型必須是 DWARF 基礎類型。它指定類型的編碼、位元組順序和大小。DWARF 定義每個架構都有一個預設通用類型:它是架構特定的整數編碼和位元組順序,即架構全域記憶體位址的大小。
DW_OP_deref 操作從堆疊中彈出一個值,將其視為全域記憶體位址,並使用通用類型讀取該位置的內容。它將讀取的值作為值及其關聯的通用類型推送到堆疊上。
當評估到達表達式的末尾時停止。在值結果類型上下文中評估的表達式的結果是堆疊的頂端元素,它提供值及其類型。
3.2.2 暫存器中的變數位置¶
此範例適用於與 DIE 屬性關聯的操作表達式,該屬性提供了來源語言變數的位置。此類屬性規定表達式必須在提供位置結果類型的上下文中評估。
DWARF 以位置描述的形式定義物件的位置。
在此範例中,編譯器在架構暫存器 SGPR0 中分配了一個來源語言變數。
用於指定變數位置的可能表達式是
DW_OP_regx SGPR0
DW_OP_regx 操作建立一個位置描述,指定由運算元 SGPR0 指定的架構暫存器的位置。與值不同,位置描述不會推送到堆疊上。相反,它們在概念上放置在位置區域中。與值不同,位置描述沒有關聯的類型,它們僅表示物件基底的位置。
同樣,當評估到達表達式的末尾時停止。在位置結果類型上下文中評估的表達式的結果是位置區域中的位置描述。
3.2.3 記憶體中的變數位置¶
下一個範例適用於與 DIE 屬性關聯的操作表達式,該屬性提供了在堆疊框架中分配的來源語言變數的位置。編譯器已將堆疊框架指標放在架構暫存器 SGPR0 中,並從堆疊框架基底偏移 0x10 處分配了變數。堆疊框架分配在全域記憶體中,因此 SGPR0 包含全域記憶體位址。
用於指定變數位置的可能表達式是
DW_OP_regval_type SGPR0 Generic
DW_OP_plus_uconst 0x10
與上一個範例一樣,DW_OP_regval_type 操作將堆疊框架指標全域記憶體位址推送到堆疊上。通用類型是全域記憶體位址的大小。
DW_OP_plus_uconst 操作從堆疊中彈出一個值,該值必須具有整數編碼的類型,加上其運算元的值,並將結果連同相同的關聯類型推回堆疊上。在此範例中,它計算來源語言變數的全域記憶體位址。
當評估到達表達式的末尾時停止。如果評估的表達式具有位置結果類型上下文,並且位置區域為空,則頂端堆疊元素必須是具有通用類型的值。該值會隱式地從堆疊中彈出,並被視為全域記憶體位址,以建立全域記憶體位置描述,並將其放置在位置區域中。表達式的結果是位置區域中的位置描述。
3.2.4 分散在不同位置的變數¶
此範例適用於部分在暫存器中、部分未定義且部分在記憶體中的來源變數。
DWARF 定義了可以有一個或多個部分組成的複合位置描述。每個部分都指定一個位置描述以及從中使用的位元組數。以下操作表達式建立一個複合位置描述。
DW_OP_regx SGPR3
DW_OP_piece 4
DW_OP_piece 2
DW_OP_bregx SGPR0 0x10
DW_OP_piece 2
DW_OP_regx 操作在位置區域中建立暫存器位置描述。
第一個 DW_OP_piece 操作在位置區域中建立一個具有單一部分的不完整複合位置描述。位置區域中的位置描述用於定義該部分的開頭,大小由運算元指定,即 4 個位元組。
後續的 DW_OP_piece 將新部分添加到位置區域中已有的不完整複合位置描述中。這些部分形成一組連續的位元組。如果位置區域中沒有其他位置描述,並且堆疊上沒有值,則該部分會隱式地使用未定義的位置描述。同樣,運算元指定該部分的大小(以位元組為單位)。未定義的位置描述可用於指示已最佳化掉的部分。在本例中,為 2 個位元組的未定義值。
DW_OP_bregx 操作讀取由第一個運算元 (SGPR0) 指定的架構暫存器作為通用類型,加上第二個運算元 (0x10) 的值,並將該值推送到堆疊上。
下一個 DW_OP_piece 操作將另一個部分添加到已建立的不完整複合位置中。
如果位置區域中沒有其他位置,但堆疊上有值,則新部分是記憶體位置描述。使用的記憶體位址從堆疊中彈出。在本例中,運算元 2 表示有 2 個位元組來自記憶體。
當評估到達表達式的末尾時停止。如果評估的表達式具有位置結果類型上下文,並且位置區域具有不完整的複合位置描述,則不完整的複合位置描述會隱式地轉換為完整的複合位置描述。表達式的結果是位置區域中的位置描述。
3.2.5 偏移複合位置¶
此範例嘗試擴展先前的範例,以偏移其建立的複合位置描述。 3.2.3 記憶體中的變數位置 範例方便地使用了 DW_OP_plus 操作來偏移記憶體位址。
DW_OP_regx SGPR3
DW_OP_piece 4
DW_OP_piece 2
DW_OP_bregx SGPR0 0x10
DW_OP_piece 2
DW_OP_plus_uconst 5
但是,DW_OP_plus 無法用於偏移複合位置。它僅對堆疊上的值進行操作。
為了偏移複合位置描述,編譯器需要建立不同的複合位置描述,從對應於偏移量的部分開始。例如
DW_OP_piece 1
DW_OP_bregx SGPR0 0x10
DW_OP_piece 2
這說明堆疊值上的操作與位置描述上的操作不可組合。
3.2.6 成員指標¶
注意:在不失一般性的情況下,此範例中使用的是 DWARF 4,因為所用工具的版本中未完全支援 DWARF 5。DWARF 5 的任何功能都無法解決此問題。
此範例重點說明了 DWARF 5 無法描述 C++ 成員指標的使用語義。
DWARF 5 提供的用於描述成員指標使用的機制是 DW_AT_use_location
,它被定義為編碼一個位置描述,該位置描述計算由成員指標指向的成員的位址,給定成員指標物件和包含物件的位址。
也就是說,當偵錯代理想要評估成員指標存取操作時,它首先將兩個值推送到 DWARF 表達式堆疊上
成員指標物件
包含物件的位址
然後,它評估與成員指標類型的 DW_AT_use_location
關聯的位置描述,並將結果解釋為由成員指標指向的成員的位址。
考慮以下 C++ 原始檔 s.cc
struct s {
int m;
int n;
};
int s::* p;
使用 GCC 編譯並使用 dwarfdump 檢查時
$ g++ -gdwarf-5 -O3 -c s.cc
$ dwarfdump s.o
< 1><0x0000001e> DW_TAG_structure_type
DW_AT_name s
DW_AT_byte_size 0x00000008
DW_AT_sibling <0x0000003c>
< 2><0x00000029> DW_TAG_member
DW_AT_name m
DW_AT_type <0x0000003c>
DW_AT_data_member_location 0
< 2><0x00000032> DW_TAG_member
DW_AT_name n
DW_AT_type <0x0000003c>
DW_AT_data_member_location 4
< 1><0x0000003c> DW_TAG_base_type
DW_AT_byte_size 0x00000004
DW_AT_encoding DW_ATE_signed
DW_AT_name int
< 1><0x00000043> DW_TAG_ptr_to_member_type
DW_AT_containing_type <0x0000001e>
DW_AT_type <0x0000003c>
DW_AT_use_location len 0x0001: 22: DW_OP_plus
< 1><0x0000004e> DW_TAG_variable
DW_AT_name p
DW_AT_type <0x00000043>
DW_AT_external yes(1)
DW_AT_location len 0x0009: 030000000000000000: DW_OP_addr 0x00000000
請注意,DW_AT_use_location
的位置描述是 DW_OP_plus
,它反映了 GCC 將成員指標實作為包含物件內的整數位元組偏移量。例如,在此實作中,&s::m
的值為 offsetof(s, m)
,而 &s::n
的值為 offsetof(s, n)
struct s {
int m; // offsetof(s, m) == 0
int n; // offsetof(s, n) == 4
} o; // &o == 0xff00
int s::* p;
int *i;
p = &s::m; // p == 0
i = &(o.*p); // i == 0xff00 + 0
p = &s::n; // p == 4
i = &(o.*p); // i == 0xff00 + 4
只要整個包含物件都駐留在預設位址空間的記憶體中,表達式 DW_OP_plus
就能準確地描述此實作。
但是,如果包含物件或指向的成員不在任何預設位址空間位址怎麼辦?
編譯器可能會將包含物件儲存在任何位址空間的記憶體中、暫存器中,在每次使用時重新計算其值,或以任意方式組合這些方式。
現有 DWARF 5 表達式語言的豐富性反映了影響程式中物件位置的可能實作策略和最佳化選擇的多樣性,並且(模數位址空間)它可以描述變數的所有這些位置。但是,當我們查看成員指標使用時,我們僅限於駐留在預設位址空間中連續記憶體區塊中的物件。
為了示範問題,請考慮一個程式,GCC 選擇以這樣一種方式進行最佳化,即包含物件根本不在記憶體中
ptm.h
struct s {
int m;
int n;
};
void i(int);
extern int t;
void f(s x, int s::* p);
ptm.cc
#include "ptm.h"
void f(s x, int s::* p) {
for (int a = 0; a < t; ++a) {
x.m += a + x.n;
i(x.*p);
}
}
main.cc
#include "ptm.h"
int t = 100;
void i(int) {}
int main(int argc, char *argv[]) {
s x = { 0, 1 };
f(x, &s::m);
}
在 GDB 下編譯和執行時
$ g++-9 -gdwarf-4 -O3 -c main.cc -o main.o
$ g++-9 -gdwarf-4 -O3 -c ptm.cc -o ptm.o
$ g++-9 main.o ptm.o -o use_location.out
$ gdb ./use_location.out
(gdb) maint set dwarf always-disassemble
(gdb) b ptm.cc:5
Breakpoint 1 at 0x119e: file ptm.cc, line 5.
(gdb) r
Breakpoint 1, f (x=..., p=<optimized out>) at ptm.cc:5
5 i(x.*p);
請注意,編譯器已將整個物件 x
提升到迴圈主體的暫存器 rdi
中
(gdb) info addr x
Symbol "x" is multi-location:
Range 0x555555555160-0x5555555551af: a complex DWARF expression:
0: DW_OP_reg5 [$rdi]
Range 0x5555555551af-0x5555555551ba: a complex DWARF expression:
0: DW_OP_fbreg -56
.
(gdb) p $pc
$1 = (void (*)(void)) 0x55555555519e <f(s, int s::*)+62>
因此,在這種情況下,無法解釋 DW_OP_use_location
(gdb) p x.*p
Address requested for identifier "x" which is in register $rdi
透過堆疊上的位置描述,可以修改 DW_OP_use_location
的定義,方法是將每個「位址」實例替換為「位置描述」,如 類型條目 中所述。
為了實作此屬性的完全通用版本,GCC 只需要將表達式從 DW_OP_plus
變更為 DW_OP_swap, DW_OP_LLVM_offset
即可。
3.2.7 虛擬基底類別¶
注意:在不失一般性的情況下,此範例中使用的是 DWARF 4,因為所用工具的版本中未完全支援 DWARF 5。DWARF 5 的任何功能都無法解決此問題。
此範例重點說明了 DWARF 5 無法描述 C++ 虛擬繼承語義。
DWARF 5 提供的用於描述繼承子物件位置的機制是 DW_AT_data_member_location
。此屬性被重載以描述資料成員位置和繼承子物件位置,並且在每種情況下都有多種可能的形式
如果是整數常數形式,它會編碼從衍生物件到資料成員或子物件的位元組偏移量。
否則,它會編碼一個位置描述,以計算資料成員或子物件的位址(給定衍生物件的位址)。
此處僅考慮描述子物件的屬性,以及僅考慮位置描述形式。
在這種情況下,當偵錯代理想要定位子物件時,它首先將衍生物件的位址推送到 DWARF 表達式堆疊上。然後,它評估與對應於繼承子物件的 DW_TAG_inheritence
DIE 的 DW_AT_data_member_location
關聯的位置描述。
考慮以下 C++ 原始檔 ab.cc
class A {
public:
char x;
};
class B
: public virtual A {} o;
使用 GCC 編譯並使用 dwarfdump 檢查時
$ g++ -gdwarf-5 -O3 -c ab.cc
$ dwarfdump ab.o
< 1><0x0000002a> DW_TAG_class_type
DW_AT_name A
DW_AT_byte_size 0x00000001
DW_AT_sibling <0x00000042>
< 1><0x00000049> DW_TAG_class_type
DW_AT_name B
DW_AT_byte_size 0x00000010
DW_AT_containing_type <0x00000049>
DW_AT_sibling <0x000000f9>
< 2><0x00000058> DW_TAG_inheritance
DW_AT_type <0x0000002a>
DW_AT_data_member_location len 0x0006: 1206481c0622:
DW_OP_dup DW_OP_deref DW_OP_lit24 DW_OP_minus DW_OP_deref DW_OP_plus
DW_AT_virtuality DW_VIRTUALITY_virtual
DW_AT_accessibility DW_ACCESS_public
此 DW_AT_data_member_location
表達式根據 Itanium ABI 描述了定位 B
中 A
子物件的動態過程。類別 B
的邏輯佈局圖如下:
0: class B
0: vptr B
8: class A
8: A::x
也就是說,類別 B
物件的位址等同於 B
的 vtable
指標的位址。由於類別 B
沒有其他直接資料成員,因此類別 A
的主要基底類別子物件緊隨其後,並且由於子物件對齊要求已滿足,因此沒有介入填充。
B
的 vtable
指標包含每個虛擬基底類別的條目 vbase_offset
。在這種情況下,該表格佈局為
-24: vbase_offset[A]=8
-16: offset_to_top=0
-8: B RTTI
0: <vtable for B>
也就是說,為了找到 B
中 A
子物件的 vbase_offset
,位址 vptr B
按靜態確定的值 -24
偏移。
因此,為了實作 B
中 A
的 DW_AT_data_member_location
,表達式需要透過 vptr B
索引到靜態已知的位元組偏移量 -24
,以查找 B
中 A
的 vbase_offset
。然後,它必須將 B
的位置偏移動態值 vbase_offset
(在本例中為 8
),以到達繼承子物件的位置。
此定義與範例 3.2.6 存在相同的問題,因為它依賴於衍生物件和繼承子物件的位址,而無法保證兩者都具有任何位址。
為了示範問題,請考慮一個程式,GCC 選擇以這樣一種方式進行最佳化,即衍生物件根本不在記憶體中
f.h
class A {
public:
char x;
};
class B
: public virtual A {};
void f(B b);
f.cc
#include "f.h"
void f(B b) {}
main.cc
#include "f.h"
int main(int argc, char *argv[]) {
B b;
b.x = 42;
f(b);
return b.x;
}
在 GDB 下編譯和執行時
$ g++-9 -gdwarf-4 -O3 -c main.cc -o main.o
$ g++-9 -gdwarf-4 -O3 -c f.cc -o f.o
$ g++-9 main.o f.o -o cpp-vbase.out
(gdb) maint set dwarf always-disassemble
(gdb) b main.cc:6
Breakpoint 1 at 0x1090: file main.cc, line 6.
(gdb) r
Breakpoint 1, main (argc=<optimized out>, argv=<optimized out>) at main.cc:6
6 return b.x;
請注意,編譯器已省略了 main()
主體中整個物件 x
的儲存
(gdb) info addr b
Symbol "b" is multi-location:
Range 0x555555555078-0x5555555550af: a complex DWARF expression:
0: DW_OP_piece 8 (bytes)
2: DW_OP_const1u 42
4: DW_OP_stack_value
5: DW_OP_piece 1 (bytes)
7: DW_OP_piece 7 (bytes)
.
(gdb) p $pc
$1 = (void (*)(void)) 0x555555555090 <main(int, char**)+48>
因此,在這種情況下,無法解釋 DW_OP_data_member_location
(gdb) p b
$2 = {<A> = <invalid address>, _vptr.B = <optimized out>}
注意:應該佔用物件
b
的前 8 個位元組的vptr B
在 DWARF 中未定義,但可以由編譯器描述為隱含值。此變更將是微不足道的,並且會直接暴露此處描述的 DWARF 5 中的問題。
透過堆疊上的位置描述,可以修改 DW_OP_data_member_location
的定義,方法是將每個「位址」實例替換為「位置描述」,如 A.5 類型條目 中所述。
為了實作此屬性的完全通用版本,GCC 只需要將表達式中的最後一個操作從 DW_OP_plus
變更為 DW_OP_LLVM_offset
即可。
3.3 限制¶
DWARF 5 無法描述執行時間索引的暫存器部分中的變數。這是描述位於 SIMT 向量暫存器通道中的來源變數所必需的。
某些功能僅在位於全域記憶體中時才有效。類型屬性表達式需要基礎物件,該物件可能位於任何類型的位置。
DWARF 程序只能接受全域記憶體位址引數。這限制了分解建立涉及其他位置類型的位置的能力。
沒有向量基礎類型。這是描述向量暫存器所必需的。
沒有操作可以建立非全域位址空間中的記憶體位置。只有取消引用操作支援提供位址空間。
CFI 位置表達式不允許複合位置或非全域位址空間記憶體位置。這兩者在具有向量暫存器和位址空間的裝置的最佳化程式碼中都是需要的。
位元欄位偏移量僅以有限的方式支援暫存器位置。以統一的方式支援所有位置類型中的位元欄位偏移量,對於支援具有位元大小實體的語言是必需的。
4. 擴展解決方案¶
本節概述了擴展,以通用化 DWARF 表達式評估模型,以允許在堆疊上操作位置描述。它提供了許多簡化的範例,以示範其優點以及擴展如何解決異質裝置的問題。它介紹了如何以向後相容於 DWARF 5 的方式完成此操作。
4.1 位置描述¶
為了具有一致的、可組合的操作,這些操作作用於位置描述,擴展定義了一種統一的方式來處理所有位置類型。這包括記憶體、暫存器、隱含、隱含指標、未定義和複合位置描述。
每種位置描述在概念上都是儲存空間內基於零的偏移量。儲存空間是特定位元組數的連續線性組織(請參閱下文,了解如何擴展以支援位元大小的儲存空間)。
對於全域記憶體,儲存空間是架構位址大小的位元組線性串流。
對於每個單獨的架構暫存器,它是該特定暫存器大小的位元組線性串流。
對於隱含值,它是使用值的基礎類型表示時的值的位元組線性串流,該基礎類型指定編碼、大小和位元組順序。
對於未定義值,它是無限大小的線性串流,其中每個位元組都是未定義的。
對於複合值,它是由複合值的各部分定義的位元組線性串流。
4.2 堆疊位置描述操作¶
DWARF 表達式堆疊被擴展為允許每個堆疊條目是值或位置描述。
定義了評估規則,以隱式地將作為值的堆疊元素轉換為位置描述,反之亦然,以便所有 DWARF 5 表達式繼續具有相同的語義。這反映了記憶體位址有效地用作記憶體位置描述的代理。
對於允許指定 DWARF 表達式的每個位置,都會定義表達式是要評估為值還是位置描述。
用於對記憶體位址進行操作的現有 DWARF 表達式操作被通用化為對任何位置描述類型進行操作。例如,DW_OP_deref 操作從堆疊中彈出位置描述,而不是記憶體位址值,並讀取與從位置描述的偏移量開始的位置類型關聯的儲存空間。
建立位置描述的現有 DWARF 表達式操作已更改為在堆疊上彈出和推送位置描述。例如,DW_OP_value、DW_OP_regx、DW_OP_implicit_value、DW_OP_implicit_pointer、DW_OP_stack_value 和 DW_OP_piece。
可以添加對位置描述進行操作的新操作。例如,DW_OP_offset 操作,用於修改堆疊頂端位置描述的偏移量。與僅適用於記憶體位址的 DW_OP_plus 操作不同,DW_OP_offset 操作可以適用於任何位置類型。
為了允許增量和巢狀建立複合位置描述,可以定義 DW_OP_piece_end 來明確指示複合的最後一部分。目前,建立複合值必須始終是表達式的最後一個操作。
可以定義 DW_OP_undefined 操作,以明確建立未定義的位置描述。目前,這僅在堆疊為空時才可能作為複合的一部分。
4.3 範例¶
本節提供一些動機範例,以說明允許堆疊上的位置描述所帶來的好處。
4.3.1 原始語言變數溢出到向量暫存器的一部分¶
為 GPU 產生程式碼的編譯器可能會配置一個原始語言變數,它證明該變數對於 SIMT 執行緒的每個通道都具有相同的值,並將其放在純量暫存器中。然後,它可能需要溢出該純量暫存器。為了避免溢出到記憶體的高成本,它可以溢出到眾多向量暫存器的其中一個固定通道。
以下運算式定義了一個原始語言變數的位置,編譯器將其配置在純量暫存器中,但在程式碼的此點必須溢出到向量暫存器的通道 5。
DW_OP_regx VGPR0
DW_OP_offset_uconst 20
DW_OP_regx 將暫存器位置描述推送到堆疊上。暫存器的儲存空間大小為向量暫存器的大小。暫存器位置描述在概念上以 0 的初始偏移量引用該儲存空間。架構定義了暫存器的位元組順序。
DW_OP_offset_uconst 從堆疊中彈出一個位置描述,將其運算元值加到偏移量,然後將更新後的位置描述推回堆疊。在本例中,原始語言變數被溢出到通道 5,並且每個通道的組件為 32 位元(4 個位元組),因此偏移量為 5*4=20。
運算式評估的結果是堆疊頂端的位置描述。
另一種替代方法可能是讓目標為每個向量暫存器的每個部分定義不同的暫存器名稱。然而,由於必須定義的暫存器數量龐大,這對於 GPU 來說並不實際。它也不允許使用執行階段索引到整個暫存器的一部分,如下一個範例所示。
4.3.2 原始語言變數分散在多個向量暫存器中¶
編譯器可能會為 GPU 產生 SIMT 程式碼。每個原始語言執行緒都會映射到 GPU 執行緒的單個通道。映射到暫存器的原始語言變數會映射到向量暫存器的通道組件,該組件對應於原始語言的執行緒。
因此,此類變數的位置運算式必須在目前聚焦的原始語言執行緒的上下文中執行。可以定義 DW_OP_push_lane 運算,以推送目前聚焦的原始語言執行緒的通道值。要使用的值將由 DWARF 的消費者在評估位置運算式時提供。
如果原始語言變數大於向量暫存器通道組件的大小,則會使用多個向量暫存器。每個原始語言執行緒將僅使用與其關聯通道的向量暫存器組件。
以下運算式定義了一個必須佔用兩個向量暫存器的原始語言變數的位置。建立一個複合位置描述,將兩個部分組合在一起。無論哪個通道對應於使用者聚焦的原始語言執行緒,它都會給出正確的結果。
DW_OP_regx VGPR0
DW_OP_push_lane
DW_OP_uconst 4
DW_OP_mul
DW_OP_offset
DW_OP_piece 4
DW_OP_regx VGPR1
DW_OP_push_lane
DW_OP_uconst 4
DW_OP_mul
DW_OP_offset
DW_OP_piece 4
DW_OP_regx VGPR0 推送第一個暫存器的位置描述。
DW_OP_push_lane; DW_OP_uconst 4; DW_OP_mul 計算聚焦通道的向量暫存器組件的偏移量,即通道號碼的 4 倍。
DW_OP_offset 將暫存器位置描述的偏移量調整為執行階段計算的值。
DW_OP_piece 要么建立一個新的複合位置描述,要么將一個新部分添加到現有的不完整複合位置描述。它會彈出要用於新部分的位置描述。然後,如果下一個堆疊元素是不完整複合位置描述,它會彈出該元素,否則它會建立一個沒有部分的新不完整複合位置描述。最後,它會在添加新部分後推送不完整的複合位置描述。
在本例中,暫存器位置描述被添加到新的不完整複合位置描述中。DW_OP_piece 的 4 指定了組成該部分的暫存器儲存空間的大小。請注意,這 4 個位元組從計算出的暫存器偏移量開始。
為了向後相容性,如果堆疊為空或頂端堆疊元素是不完整複合位置描述,則會將未定義的位置描述用於該部分。如果頂端堆疊元素是泛型基本類型值,則會將其隱式轉換為全域記憶體位置描述,偏移量等於該值。
運算式的其餘部分對 VGPR1 執行相同的操作。但是,當評估 DW_OP_piece 時,堆疊上存在不完整的複合位置描述。因此,VGPR1 暫存器位置描述作為第二部分添加。
在運算式結束時,如果頂端堆疊元素是不完整複合位置描述,則會將其轉換為完整位置描述,並作為結果傳回。
4.3.3 原始語言變數分散在多種位置類型中¶
此範例與前一個範例相同,只是第二個向量暫存器的前 2 個位元組已溢出到記憶體,而最後 2 個位元組已被證明是常數並被最佳化掉。
DW_OP_regx VGPR0
DW_OP_push_lane
DW_OP_uconst 4
DW_OP_mul
DW_OP_offset
DW_OP_piece 4
DW_OP_addr 0xbeef
DW_OP_piece 2
DW_OP_uconst 0xf00d
DW_OP_stack_value
DW_OP_piece 2
DW_OP_piece_end
前 6 個運算與前一個範例相同。
DW_OP_addr 運算在堆疊上推送一個全域記憶體位置描述,偏移量等於位址。
下一個 DW_OP_piece 將全域記憶體位置描述添加為複合位置描述的下一個 2 位元組部分。
DW_OP_uconst 0xf00d; DW_OP_stack_value 在堆疊上推送一個隱式位置描述。隱式位置描述的儲存空間是使用泛型基本類型的編碼、大小和位元組順序表示值 0xf00d。
最後的 DW_OP_piece 將隱式位置描述的 2 個位元組添加為複合位置描述的第三部分。
DW_OP_piece_end 運算明確地將不完整複合位置描述轉換為完整位置描述。這允許在堆疊上建立完整複合位置描述,可用作後續運算的另一個位置描述。例如,DW_OP_offset 可以應用於它。更實際地說,它允許在堆疊上建立多個複合位置描述,可用於使用 DW_OP_call* 運算將引數傳遞給 DWARF 程序。這有助於分解位置描述的增量建立。
4.3.4 位址空間¶
異質設備可以具有多個硬體支援的位址空間,這些位址空間使用特定的硬體指令來存取它們。
例如,使用 SIMT 執行的 GPU 可以提供硬體支援來存取記憶體,以便每個通道都可以看到線性記憶體視圖,而後備記憶體實際上是以交錯方式存取的,以便每個通道的第 N 個雙字組的位置是連續的。這最大限度地減少了 SIMT 執行讀取的快取行。
以下運算式定義了一個原始語言變數的位置,該變數配置在目前子程式堆疊框架中偏移量 0x10 的位置。子程式堆疊框架是每個通道的,並且駐留在交錯位址空間中。
DW_OP_regval_type SGPR0 Generic
DW_OP_uconst 1
DW_OP_form_aspace_address
DW_OP_offset 0x10
DW_OP_regval_type 運算將 SGPR0 的內容作為泛型值推送。這是保存目前堆疊框架位址的暫存器。
DW_OP_uconst 運算推送位址空間編號。每個架構都會在 DWARF 中定義它使用的編號。在本例中,位址空間 1 被用作每個通道的記憶體。
DW_OP_form_aspace_address 運算彈出一個值和一個位址空間編號。每個位址空間都與一個單獨的儲存空間關聯。推送一個記憶體位置描述,該描述引用位址空間的儲存空間,偏移量為彈出的值。
所有對位置描述進行運算的運算都適用於記憶體位置,無論其位址空間如何。
每個架構都將位址空間 0 定義為預設的全域記憶體位址空間。
將記憶體位置描述通用化以包含位址空間組件,避免了建立專用運算來處理位址空間的需要。
原始變數位於堆疊框架中的偏移量 0x10 處。DW_OP_offset 運算適用於具有位址空間的記憶體位置描述,就像適用於任何其他類型的位置描述一樣。
DWARF 5 中唯一採用位址空間的運算是 DW_OP_xderef*。它們將值視為指定位址空間中的位址,並讀取其內容。沒有運算可以實際建立引用位址空間的位置描述。沒有辦法將位址空間記憶體位置包含在複合位置的部分中。
由於 DW_OP_piece 現在採用任何類型的位置描述作為其部分,因此現在複合位置的部分可能涉及不同位址空間中的位置。例如,當配置在暫存器中的原始變數的部分溢出到駐留在非全域位址空間中的堆疊框架時,可能會發生這種情況。
4.3.5 位元偏移量¶
隨著堆疊上位置描述的通用化,可以定義 DW_OP_bit_offset 運算,該運算以位元而不是位元組為單位調整任何類型位置的偏移量。偏移量可以是執行階段計算的值。這對於任何支援位元大小實體的原始語言以及暫存器大小不是整數位元組的暫存器通常很有用。
DWARF 5 僅支援使用 DW_OP_bit_piece 的複合位置中的位元欄位。它不支援執行階段計算的偏移量,這可能會在位元欄位封裝陣列中發生。它也不具有一般的可組合性,因為它必須是運算式的最後一部分。
以下範例定義了從暫存器的位元 20 開始配置的原始變數的位置描述。如果原始變數位於記憶體或特定位址空間內的位元偏移量處,或者如果偏移量是執行階段值,則可以使用類似的運算式。
DW_OP_regx SGPR3
DW_OP_uconst 20
DW_OP_bit_offset
DW_OP_bit_offset 運算從堆疊中彈出一個值和位置描述。它在使用該值作為位元計數更新其偏移量後推送位置描述。
位元組內的位元順序(如位元組順序)由目標架構定義。除了位元組順序之外,還可以擴展基本類型以指定位元順序。
4.4 呼叫框架資訊 (CFI)¶
DWARF 定義了呼叫框架資訊 (CFI),可用於虛擬地展開子程式呼叫堆疊。這涉及確定暫存器值溢出的位置。DWARF 5 將這些位置限制為暫存器或全域記憶體。如先前的範例所示,異質設備可能會將暫存器溢出到其他暫存器的部分、非全域記憶體位址空間,甚至不同位置類型的複合位置。
因此,此擴展擴展了 CFI 規則,以支援任何類型的位置描述,以及在位址空間中建立位置的運算。
4.5 不在位元組對齊全域記憶體中的物件¶
DWARF 5 僅透過使用全域記憶體位址作為記憶體位置描述的代理,有效地支援堆疊上的位元組對齊記憶體位置。對於定義 DWARF 運算式的屬性來說,這是一個問題,這些屬性需要某些未在位元組對齊全域記憶體中配置的原始語言實體的位置。
例如,DW_AT_data_member_location 屬性的 DWARF 運算式是在初始堆疊中評估的,該初始堆疊包含類型實例物件的位置。該物件可能位於暫存器中、非全域記憶體位址空間中,由複合位置描述描述,甚至可能是隱式位置描述。
對於使用 DW_OP_push_object_address 運算的 DWARF 運算式,也存在類似的問題。此運算推送與定義運算式的屬性關聯的程式物件的位置。
允許堆疊上的任何類型的位置描述允許使用 DW_OP_call* 運算來分解位置描述的建立。呼叫的輸入和輸出在堆疊上傳遞。例如,在 GPU 上,可以定義一個運算式來描述非活動 SIMT 執行通道的有效 PC。這自然是透過組合每個巢狀控制流程區域的運算式結果來完成的。這可以透過使每個控制流程區域都有自己的 DWARF 程序,然後從巢狀控制流程區域的運算式中呼叫它來完成。另一種方法是使每個控制流程區域都有完整的運算式,這會導致更大的 DWARF,並且產生起來不太方便。
GPU 編譯器努力將物件配置在更多暫存器中以減少記憶體存取,它們必須使用不同的記憶體位址空間,並且它們執行最佳化,從而產生這些的複合位置。允許運算處理任何類型的位置描述,使得能夠建立支援所有這些的位置描述。
對位元欄位和隱式位置的完整通用支援有益於任何目標的最佳化。
4.6 高階運算¶
通用化允許以優雅的方式添加高階運算,以通用可組合的方式從其他位置描述中建立位置描述。
例如,DW_OP_extend 運算可以從位置描述、元素大小和元素計數中建立複合位置描述。產生的複合位置將有效地成為元素計數元素的向量,每個元素都是指定位元大小的相同位置描述。
DW_OP_select_bit_piece 運算可以從兩個位置描述、一個位元遮罩值和一個元素大小中建立複合位置描述。產生的複合位置將有效地成為元素向量,根據位元遮罩從兩個輸入位置之一中選擇。
這些可用於計算 SIMT 執行通道的有效 PC 的屬性的運算式中。向量結果有效地一次計算每個 SIMT 通道的 PC。遮罩可以是硬體執行遮罩暫存器,用於控制哪些 SIMT 通道正在執行。對於活動的分歧通道,向量元素將是目前的 PC,對於非活動的分歧通道,PC 將對應於通道在邏輯上定位的原始語言行。
類似地,可以定義 DW_OP_overlay_piece 運算,以從兩個位置描述、一個偏移量值和一個大小中建立複合位置描述。產生的複合位置將由等效於其中一個位置描述的部分組成,但另一個位置描述取代了由偏移量和大小定義的切片。這可用於有效地表達原始語言陣列,該陣列在以 SIMD 方式執行一組迴圈迭代時已將一組元素提升到向量暫存器中。
4.7 多個位置中的物件¶
編譯器可能會在堆疊框架記憶體中配置原始變數,但在某些程式碼範圍內可能會將其提升到暫存器。如果產生的程式碼沒有更改暫存器值,則無需將其儲存回記憶體。實際上,在該範圍內,原始變數既在記憶體中又在暫存器中。如果消費者(例如偵錯器)允許使用者在該 PC 範圍內更改原始變數的值,則它需要更改這兩個位置。
DWARF 5 支援 loclist,它可以指定原始語言實體在不同 PC 位置位於不同位置。它也可以表達原始語言實體同時位於多個位置。
DWARF 5 將運算式和 loclist 分開定義。一般來說,這是足夠的,因為非記憶體位置描述只能作為運算式評估的最後一步計算。
但是,允許堆疊上的位置描述允許在運算式評估的中間使用非記憶體位置描述。例如,DW_OP_call* 和 DW_OP_implicit_pointer 運算可能會導致評估 DIE 的 DW_AT_location 屬性的運算式。DW_AT_location 屬性允許 loclist 形式。因此,結果可能包含多個位置描述。
類似地,與 DW_AT_data_member_location 等屬性關聯的 DWARF 運算式是在初始堆疊中評估的,該初始堆疊包含位置描述,或者使用 DW_OP_push_object_address 運算的 DWARF 運算式,可能希望對另一個運算式的結果進行運算,該運算式傳回了涉及多個位置的位置描述。
因此,此擴展需要定義使用這些結果的運算式運算的行為方式。此擴展透過通用化運算式堆疊來實現此目的,以允許一個條目是一個或多個單個位置描述。透過這樣做,它以自然的方式統一了 DWARF 運算式和 loclist 運算式的定義。
所有對位置描述進行運算的運算都已擴展為對多個單個位置描述進行運算。例如,DW_OP_offset 運算將偏移量添加到每個單個位置描述。DW_OP_deref* 運算只是讀取其中一個單個位置描述的儲存空間,因為多個單個位置描述必須都保存相同的值。同樣地,如果 DWARF 運算式的評估產生多個單個位置描述,則消費者可以確保對所有位置描述執行任何更新,並且任何讀取都可以使用其中任何一個。
5. 結論¶
DWARF 的優勢在於,它通常致力於提供通用的可組合解決方案,以解決許多問題,而不是僅解決一次性問題的解決方案。此擴展嘗試遵循該傳統,透過定義向後相容的可組合通用化,可以解決一系列重要的問題。它解決了異質運算設備的特定問題,為非異質設備提供了好處,並且可以幫助解決許多其他先前報告的問題。
A. DWARF 除錯資訊格式版本 5 的變更¶
注意:本附錄提供相對於 DWARF 版本 5 的變更。它已被定義為與 DWARF 版本 5 向後相容。非規範性文字以斜體顯示。除非另有說明,否則章節編號通常與 DWARF 版本 5 標準中的編號對應。給出定義以闡明現有的運算式運算、CFI 運算和屬性如何針對支援多個位置的通用位置描述運作。
注意:包含註解以描述如何將變更應用於 DWARF 版本 5 標準。它們還描述了可能需要進一步考慮的基本原理和問題。
A.2 一般描述¶
A.2.5 DWARF 運算式¶
注意:本節及其巢狀章節取代了 DWARF 版本 5 的第 2.5 節和第 2.6 節。它基於現有 DWARF 版本 5 標準的文字。
DWARF 運算式描述如何計算值或指定位置。
DWARF 運算式的評估可以提供物件的位置、陣列邊界的值、動態字串的長度、所需的值本身等等。
如果 DWARF 運算式的評估未遇到錯誤,則它可以產生值(請參閱2.5.2 DWARF 運算式值)或位置描述(請參閱2.5.3 DWARF 位置描述)。當評估 DWARF 運算式時,可以指定結果種類是需要值還是位置描述。
如果指定了結果種類,並且評估的結果與指定的結果種類不符,則如果有效,則執行2.5.4.4.3 記憶體位置描述運算中描述的隱式轉換。否則,DWARF 運算式格式錯誤。
如果 DWARF 運算式的評估遇到評估錯誤,則結果為評估錯誤。
注意:決定定義評估錯誤的概念。另一種替代方法是以類似於位置描述具有未定義位置描述的方式引入未定義值基本類型。然後,遇到評估錯誤的運算可以傳回未定義位置描述或具有未定義基本類型的值。
所有對值進行運算的運算,如果給定未定義值,將傳回未定義實體。然後,運算式將始終評估完成,並且可以進行測試以確定它是否為未定義實體。
但是,這將增加相當大的額外複雜性,並且與 GDB 在發生這些評估錯誤時拋出例外的情況不符。
如果 DWARF 運算式格式錯誤,則結果未定義。
以下章節詳細說明了 DWARF 運算式何時格式錯誤或導致評估錯誤的規則。
DWARF 運算式可以編碼為運算式運算(請參閱2.5.4 DWARF 運算式運算),或編碼為位置列表運算式(請參閱2.5.5 DWARF 位置列表運算式)。
A.2.5.1 DWARF 運算式評估上下文¶
DWARF 運算式在可以包含多個上下文元素的上下文中評估。如果指定了多個上下文元素,則它們必須是自我一致的,否則評估結果未定義。可以指定的上下文元素是
目前的結果種類
DWARF 運算式評估所需的結果種類。如果指定,它可以是位置描述或值。
目前的執行緒
原始程式執行緒的目標架構執行緒識別碼,使用者呈現的運算式目前正在為該執行緒評估。
這是與目標架構執行緒相關的運算所必需的。
例如,
DW_OP_regval_type
運算。目前的呼叫框架
目標架構呼叫框架識別碼。它識別對應於目前執行緒中子程式的活動調用的呼叫框架。它由其在呼叫堆疊上的位址識別。該位址稱為標準框架位址 (CFA)。呼叫框架資訊用於確定目前執行緒呼叫堆疊的呼叫框架的 CFA(請參閱6.4 呼叫框架資訊)。
這是指定目標架構暫存器以支援呼叫堆疊的虛擬展開的運算所必需的。
例如,
DW_OP_*reg*
運算。如果指定,它必須是目前執行緒中的活動呼叫框架。否則,結果未定義。
如果是目前正在執行的呼叫框架,則稱為頂端呼叫框架。
目前的程式位置
對應於目前執行緒的目前呼叫框架的目標架構程式位置。
頂端呼叫框架的程式位置是目前執行緒的目標架構程式計數器。呼叫框架資訊用於取得傳回位址暫存器的值,以確定其他呼叫框架的程式位置(請參閱6.4 呼叫框架資訊)。
這是評估位置列表運算式以在多個程式位置範圍之間進行選擇所必需的。這是指定目標架構暫存器以支援呼叫堆疊的虛擬展開所必需的(請參閱6.4 呼叫框架資訊)。
如果指定
如果目前的呼叫框架是頂端呼叫框架,則它必須是目前的目標架構程式位置。
如果目前的呼叫框架 F 不是頂端呼叫框架,則它必須是與目前呼叫者框架 F 中調用被呼叫者框架的呼叫站點關聯的程式位置。
否則,結果未定義。
目前的編譯單元
包含正在評估的 DWARF 運算式的編譯單元除錯資訊條目。
這是參考與同一編譯單元關聯的除錯資訊的運算所必需的,包括指示此類參考是否使用 32 位元或 64 位元 DWARF 格式。如果未指定目前的目標架構,它還可以提供預設位址空間位址大小。
例如,
DW_OP_constx
和DW_OP_addrx
運算。請注意,此編譯單元可能與從對應於目前程式位置的已載入程式碼物件確定的編譯單元不同。例如,與
DW_OP_call*
運算的除錯資訊條目運算元的DW_AT_location
屬性關聯的運算式 E 的評估是在包含 E 的編譯單元中評估的,而不是在包含DW_OP_call*
運算式運算的編譯單元中評估的。目前的目標架構
目標架構。
這是指定目標架構特定實體的運算所必需的。
例如,目標架構特定實體包括 DWARF 暫存器識別碼、DWARF 位址空間識別碼、預設位址空間和位址空間位址大小。
如果指定
如果指定了目前框架,則目前的目標架構必須與目前框架的目標架構相同。
如果指定了目前框架並且是頂端框架,並且如果指定了目前的執行緒,則目前的目標架構必須與目前執行緒的目標架構相同。
如果指定了目前的編譯單元,則目前的目標架構預設位址空間位址大小必須與目前編譯單元的標頭中的
address_size
欄位以及.debug_aranges
區段中任何關聯的條目相同。如果指定了目前的程式位置,則目前的目標架構必須與對應於目前程式位置的任何行號資訊條目(請參閱6.2 行號資訊)的目標架構相同。
如果指定了當前程式位置,則當前目標架構預設位址空間的位址大小,必須與
.debug_addr
、.debug_line
、.debug_rnglists
、.debug_rnglists.dwo
、.debug_loclists
和.debug_loclists.dwo
區段中,任何對應於當前程式位置的條目標頭中的address_size
欄位相同。否則,結果未定義。
當前物件
程式物件的位置描述。
這是
DW_OP_push_object_address
操作所必需的。例如,類型偵錯資訊條目上的
DW_AT_data_location
屬性,在評估其相關聯的表達式時,會將對應於執行時期描述器的程式物件指定為當前物件。如果位置描述無效(請參閱 2.5.3 DWARF 位置描述),則結果是未定義的。
初始堆疊
這是值或位置描述的列表,在操作表達式評估開始之前,將按照提供的順序推送到操作表達式評估堆疊上。
某些偵錯器資訊條目具有屬性,這些屬性使用初始堆疊條目評估其 DWARF 表達式值。在所有其他情況下,初始堆疊為空。
如果任何位置描述無效(請參閱 2.5.3 DWARF 位置描述),則結果是未定義的。
如果評估需要未指定的上下文元素,則評估的結果是錯誤。
位置描述的 DWARF 表達式可能可以在沒有執行緒、呼叫框架、程式位置或架構上下文的情況下進行評估。例如,全域變數的位置可能可以在沒有此類上下文的情況下進行評估。如果表達式評估時出現錯誤,則可能表示該變數已被最佳化,因此需要更多上下文。
呼叫框架資訊 (請參閱 6.4 呼叫框架資訊) 操作的 DWARF 表達式僅限於那些不需要指定編譯單元上下文的操作。
如果 .debug_info
、.debug_addr
、.debug_line
、.debug_rnglists
、.debug_rnglists.dwo
、.debug_loclists
和 .debug_loclists.dwo
區段中,對應於任何給定程式位置的所有條目標頭中的所有 address_size
欄位不匹配,則 DWARF 格式不正確。
A.2.5.2 DWARF 表達式值¶
值具有類型和字面值。它可以表示目標架構任何支援的基礎類型的字面值。基礎類型指定字面值的大小、編碼和位元組序。
注意:可能需要新增隱含指標基礎類型編碼。它將用於當
DW_OP_deref*
操作檢索由DW_OP_implicit_pointer
操作建立的隱含指標位置儲存的完整內容時,所產生值的類型。字面值將記錄由相關聯的DW_OP_implicit_pointer
操作指定的偵錯資訊條目和位元組位移。
有一個稱為通用類型的特殊基礎類型,它是一種整數類型,其大小與目標架構預設位址空間中的位址大小相同,具有目標架構定義的位元組序,以及未指定的正負號。
通用類型與 DWARF 第 4 版及更早版本中定義的堆疊操作所使用的未指定類型相同。
整數類型是一種基礎類型,其編碼為 DW_ATE_signed
、DW_ATE_signed_char
、DW_ATE_unsigned
、DW_ATE_unsigned_char
、DW_ATE_boolean
,或任何目標架構定義的整數編碼,範圍在 DW_ATE_lo_user
到 DW_ATE_hi_user
(包含)之間。
注意:目前尚不清楚
DW_ATE_address
是否為整數類型。GDB 似乎不認為它是整數類型。
A.2.5.3 DWARF 位置描述¶
偵錯資訊必須為消費者提供一種方法來尋找程式變數的位置、確定動態陣列和字串的邊界,並可能找到子程式呼叫框架的基底位址或子程式的返回位址。此外,為了滿足近期電腦架構和最佳化技術的需求,偵錯資訊必須能夠描述物件位置在物件生命週期內變更,並且在物件生命週期的某些部分可能同時駐留在多個位置的物件的位置。
關於程式物件位置的資訊由位置描述提供。
位置描述可以由一個或多個單一位置描述組成。
單一位置描述指定了保存程式物件的位置儲存,以及程式物件在位置儲存內開始的位置。位置儲存內的位置表示為相對於位置儲存開始處的位元偏移。
位置儲存是可以用來保存值的線性位元流。每個位置儲存都有一個以位元為單位的大小,並且可以使用從零開始的位元偏移來存取。位置儲存內位元的排序使用適用於目標架構上當前語言的位元編號和方向慣例。
有五種位置儲存類型
記憶體位置儲存
對應於目標架構記憶體位址空間。
暫存器位置儲存
對應於目標架構暫存器。
隱含位置儲存
對應於只能讀取的固定值。
未定義位置儲存
表示沒有可用的值,因此無法讀取或寫入。
複合位置儲存
允許這些的混合,其中一些位元來自一個位置儲存,而另一些位元來自另一個位置儲存,或來自同一個位置儲存的不相交部分。
注意:最好新增一種由
DW_OP_implicit_pointer
操作使用的隱含指標位置儲存類型。它將指定操作提供的偵錯資訊條目和位元組偏移。
位置描述是定址規則的語言獨立表示形式。
它們可以是評估偵錯資訊條目屬性的結果,該屬性指定任意複雜度的操作表達式。在這種用法中,只要物件的生命週期是靜態的,或者與擁有它的詞法區塊 (請參閱 3.5 詞法區塊條目) 相同,並且在物件的生命週期內不會移動,它們就可以描述物件的位置。
它們可以是評估偵錯資訊條目屬性的結果,該屬性指定位置列表達式。在這種用法中,它們可以描述生命週期有限、在生命週期內位置會變更,或在部分或全部生命週期內具有多個位置的物件的位置。
如果位置描述具有多個單一位置描述,則如果每個單一位置描述的位置在關聯的位置儲存中保存的物件值不是相同的值(未初始化的值部分除外),則 DWARF 表達式格式不正確。
具有多個單一位置描述的位置描述只能由具有重疊程式位置範圍的位置列表達式,或對具有多個單一位置描述的位置描述進行操作的某些表達式操作來建立。沒有可以直接建立具有多個單一位置描述的位置描述的操作表達式操作。
具有多個單一位置描述的位置描述可用於描述同時駐留在多個儲存空間中的物件。物件可能由於最佳化而具有多個位置。例如,僅讀取的值可能會為了某些程式碼區域從記憶體提升到暫存器,但後續程式碼可能會恢復從記憶體讀取值,因為暫存器可能用於其他目的。對於值位於暫存器的程式碼區域,對物件值的任何變更都必須在暫存器和記憶體中進行,以便兩個程式碼區域都將讀取更新後的值。
具有多個單一位置描述的位置描述的消費者可以從任何單一位置描述中讀取物件的值(因為它們都指向具有相同值的位置儲存),但必須將任何變更的值寫入所有單一位置描述。
將位置描述 L 按位元偏移 B 更新定義為將 B 的值加到 L 的每個單一位置描述 SL 的位元偏移。如果任何 SL 的更新後位元偏移小於 0 或大於或等於 SL 指定的位置儲存的大小,則會發生評估錯誤。
表達式的評估可能需要上下文元素來建立位置描述。如果存取了這樣的位置描述,則它表示的儲存是與建立位置描述時指定的上下文元素值相關聯的儲存,這可能與存取時的上下文不同。
例如,建立暫存器位置描述需要執行緒上下文:位置儲存用於該執行緒的指定暫存器。為位址空間建立記憶體位置描述可能需要執行緒上下文:位置儲存是與該執行緒關聯的記憶體。
如果建立位置描述所需的任何上下文元素發生變更,則位置描述將變為無效,並且存取它是未定義的。
可能使位置描述無效的上下文範例包括
需要執行緒上下文,且執行導致執行緒終止。
需要呼叫框架上下文,且進一步執行導致呼叫框架返回呼叫框架。
需要程式位置,且執行緒進一步執行。這可能會變更適用的位置列表條目或呼叫框架資訊條目。
操作使用呼叫框架資訊
虛擬呼叫框架展開中使用的任何框架返回。
使用頂部呼叫框架,程式位置用於選擇呼叫框架資訊條目,並且執行緒進一步執行。
DWARF 表達式可用於計算物件的位置描述。可以將物件位置描述作為物件上下文或初始堆疊上下文提供給後續的 DWARF 表達式評估,以計算物件的組件。如果在兩個表達式評估之間物件位置描述變為無效,則最終結果是未定義的。
執行緒程式位置的變更可能不會使位置描述無效,但仍可能使其不再有意義。存取此類位置描述,或將其用作表達式評估的物件上下文或初始堆疊上下文,可能會產生未定義的結果。
例如,位置描述可能會指定一個暫存器,該暫存器在程式位置變更後不再保存預期的程式物件。避免此類問題的一種方法是在執行緒的程式位置變更時,重新計算與執行緒關聯的位置描述。
A.2.5.4 DWARF 操作表達式¶
操作表達式由一系列操作組成,每個操作都包含一個操作碼,後跟零個或多個運算元。運算元的數量由操作碼暗示。
操作表示簡單堆疊機器上的後綴操作。每個堆疊條目可以保存值或位置描述。操作可以作用於堆疊上的條目,包括新增條目和移除條目。如果堆疊條目的種類與操作所需的種類不符,且無法隱式轉換為所需的種類(請參閱 2.5.4.4.3 記憶體位置描述操作),則 DWARF 操作表達式格式不正確。
操作表達式的評估從空堆疊開始,上下文提供的初始堆疊中的條目按照提供的順序推送到空堆疊上。然後評估操作,從流的第一個操作開始。評估持續進行,直到操作發生評估錯誤,或直到到達流的最後一個操作之後的位置。
評估的結果是
如果操作發生評估錯誤,或操作評估的表達式發生評估錯誤,則結果為評估錯誤。
如果當前結果種類指定位置描述,則
如果堆疊為空,則結果是一個具有一個未定義位置描述的位置描述。
此規則是為了向後相容 DWARF 第 5 版,該版本為此目的使用了空操作表達式。
如果頂部堆疊條目是位置描述,或可以轉換為位置描述(請參閱 2.5.4.4.3 記憶體位置描述操作),則結果是該(可能已轉換的)位置描述。堆疊上的任何其他條目都會被丟棄。
否則,DWARF 表達式格式不正確。
注意:可以將這種情況定義為返回隱含位置描述,就好像執行了
DW_OP_implicit
操作一樣。
如果當前結果種類指定值,則
如果頂部堆疊條目是值,或可以轉換為值(請參閱 2.5.4.4.3 記憶體位置描述操作),則結果是該(可能已轉換的)值。堆疊上的任何其他條目都會被丟棄。
否則,DWARF 表達式格式不正確。
如果當前結果種類未指定,則
如果堆疊為空,則結果是一個具有一個未定義位置描述的位置描述。
此規則是為了向後相容 DWARF 第 5 版,該版本為此目的使用了空操作表達式。
注意:此規則與上面請求位置描述時的規則一致。但是,GDB 似乎將此報告為錯誤,並且沒有 GDB 測試似乎會導致這種情況下的空堆疊。
否則,返回頂部堆疊條目。堆疊上的任何其他條目都會被丟棄。
操作表達式被編碼為位元組區塊,帶有一些形式的前綴,用於指定位元組計數。它可以被使用
作為使用
exprloc
類別編碼的偵錯資訊條目屬性的值(請參閱 7.5.5 類別和格式),作為某些操作表達式操作的運算元,
作為某些呼叫框架資訊操作的運算元(請參閱 6.4 呼叫框架資訊),
以及在位置列表條目中(請參閱 2.5.5 DWARF 位置列表達式)。
A.2.5.4.1 堆疊操作¶
注意:本節取代 DWARF 第 5 版第 2.5.1.3 節。
以下操作會操作 DWARF 堆疊。索引堆疊的操作假設堆疊的頂部(最近新增的條目)的索引為 0。它們允許堆疊條目是值或位置描述。
如果堆疊操作存取的任何堆疊條目是不完整的複合位置描述(請參閱 [2.5.4.4.6 複合位置描述操作] (#composite-location-description-operations)),則 DWARF 表達式格式不正確。
注意:這些操作現在支援作為值和位置描述的堆疊條目。
注意:如果也希望它們適用於不完整的複合位置描述,則需要定義當推送副本時,不完整的複合位置描述指定複合位置儲存也會被複製。這確保了不完整的複合位置描述的每個副本都可以獨立更新它們指定的複合位置儲存。
DW_OP_dup
DW_OP_dup
複製堆疊頂部的堆疊條目。DW_OP_drop
DW_OP_drop
彈出堆疊頂部的堆疊條目並將其丟棄。DW_OP_pick
DW_OP_pick
有一個單個無符號 1 位元組運算元,表示索引 I。索引為 I 的堆疊條目的副本會被推送到堆疊上。DW_OP_over
DW_OP_over
推送索引為 1 的條目的副本。這等效於
DW_OP_pick 1
操作。DW_OP_swap
DW_OP_swap
交換頂部的兩個堆疊條目。堆疊頂部的條目變成第二個堆疊條目,第二個堆疊條目變成堆疊的頂部。DW_OP_rot
DW_OP_rot
旋轉前三個堆疊條目。堆疊頂部的條目變成第三個堆疊條目,第二個條目變成堆疊的頂部,第三個條目變成第二個條目。
說明許多這些堆疊操作的範例可以在第 289 頁的附錄 D.1.2 中找到。
A.2.5.4.2 控制流程操作¶
注意:本節取代 DWARF 第 5 版第 2.5.1.5 節。
以下操作提供對 DWARF 操作表達式流程的簡單控制。
DW_OP_nop
DW_OP_nop
是一個佔位符。它對 DWARF 堆疊條目沒有任何影響。DW_OP_le
、DW_OP_ge
、DW_OP_eq
、DW_OP_lt
、DW_OP_gt
、DW_OP_ne
注意:與 DWARF 第 5 版第 2.5.1.5 節相同。
DW_OP_skip
DW_OP_skip
是一個無條件分支。它的單個運算元是一個 2 位元組的帶符號整數常數。這個 2 位元組常數是要從當前操作向前或向後跳過的 DWARF 表達式的位元組數,從 2 位元組常數之後開始計算。如果更新後的位置在最後一個操作結束之後的位置,則操作表達式評估完成。
否則,如果更新後的操作位置不在第一個到最後一個操作(包含)的範圍內,或者不在操作的開始位置,則 DWARF 表達式格式不正確。
DW_OP_bra
DW_OP_bra
是一個條件分支。它的單個運算元是一個 2 位元組的帶符號整數常數。此操作彈出堆疊的頂部。如果彈出的值不是常數 0,則 2 位元組常數運算元是要從當前操作向前或向後跳過的 DWARF 操作表達式的位元組數,從 2 位元組常數之後開始計算。如果更新後的位置在最後一個操作結束之後的位置,則操作表達式評估完成。
否則,如果更新後的操作位置不在第一個到最後一個操作(包含)的範圍內,或者不在操作的開始位置,則 DWARF 表達式格式不正確。
DW_OP_call2、 DW_OP_call4、 DW_OP_call_ref
DW_OP_call2
、DW_OP_call4
和DW_OP_call_ref
在 DWARF 操作表達式的評估期間執行 DWARF 程序呼叫。DW_OP_call2
和DW_OP_call4
各有一個運算元,分別是一個 2 位元組或 4 位元組的無符號偏移 DR,它表示相對於當前編譯單元開頭的偵錯資訊條目 D 的位元組偏移。DW_OP_call_ref
有一個運算元,它是在 32 位元 DWARF 格式中的 4 位元組無符號值,或在 64 位元 DWARF 格式中的 8 位元組無符號值,它表示相對於包含當前編譯單元的.debug_info
區段開頭的偵錯資訊條目 D 的位元組偏移 DR。D 可能不在當前編譯單元中。注意:DWARF 第 5 版聲明 DR 可以是
.debug_info
區段中的偏移,而不是包含當前編譯單元的區段。它聲明從一個可執行檔或共享物件檔到另一個的參照重定位必須由消費者執行。但是,鑑於 DR 被定義為.debug_info
區段中的偏移,這似乎是不可能的。如果 DR 被定義為實作定義的值,則消費者可以選擇以實作定義的方式解釋該值,以參照另一個可執行檔或共享物件中的偵錯資訊。在 ELF 中,
.debug_info
區段位於非PT_LOAD
段中,因此無法使用標準動態重定位。但是,即使它們是載入的段並使用了動態重定位,DR 也需要是 D 的位址,而不是.debug_info
區段中的偏移。這也需要 DR 具有全域位址的大小。因此,在 64 位元全域位址空間中不可能使用 32 位元 DWARF 格式。此外,消費者需要確定重定位位址位於哪個可執行檔或共享物件中,以便它可以確定包含的編譯單元。GDB 僅將 DR 解釋為包含當前編譯單元的
.debug_info
區段中的偏移。此註解也適用於
DW_OP_implicit_pointer
。DW_OP_call2
、DW_OP_call4
和DW_OP_call_ref
的運算元解釋與DW_FORM_ref2
、DW_FORM_ref4
和DW_FORM_ref_addr
的運算元解釋完全相同。呼叫操作的評估方式為
如果 D 具有編碼為
exprloc
的DW_AT_location
屬性,該屬性指定操作表達式 E,則當前操作表達式的執行將從 E 的第一個操作繼續。執行持續進行,直到到達 E 的最後一個操作之後的位置,此時執行將繼續執行呼叫操作之後的操作。E 的操作使用相同的當前上下文進行評估,但當前編譯單元是包含 D 的編譯單元,並且堆疊與呼叫操作使用的堆疊相同。在評估呼叫操作之後,堆疊因此與 E 的操作評估後的堆疊相同。由於 E 在與呼叫操作相同的堆疊上評估,因此 E 可以使用和/或移除堆疊上已有的條目,並且可以向堆疊新增新條目。呼叫時堆疊上的值可以用作被呼叫表達式的參數,而被呼叫表達式留在堆疊上的值可以用作呼叫和被呼叫表達式之間事先約定的返回值。
如果 D 具有編碼為
loclist
或loclistsptr
的DW_AT_location
屬性,則評估指定的位置列表達式 E。E 的評估使用當前上下文,但結果種類是位置描述,編譯單元是包含 D 的編譯單元,並且初始堆疊為空。位置描述結果被推送到堆疊上。注意:當有多個匹配項時,此規則避免了必須定義如何在與呼叫相同的堆疊上執行匹配的位置列表條目操作表達式。但它允許呼叫獲取變數或形式參數的位置描述,這些變數或形式參數可能使用位置列表達式。
另一種方法是將 D 具有編碼為
loclist
或loclistsptr
的DW_AT_location
屬性,並且指定的位置列表達式 E' 與具有操作表達式 E 的單個位置列表條目匹配的情況,視為與exprloc
情況相同,並在相同的堆疊上評估。但這並不吸引人,因為如果屬性是用於以非單例堆疊結尾的變數,它將不會僅僅將位置描述放在堆疊上。據推測,在變數或形式參數偵錯器資訊條目上使用
DW_OP_call*
的目的是僅將一個位置描述推送到堆疊上。該位置描述可能具有多個單一位置描述。先前針對
exprloc
的規則也存在相同的問題,因為通常變數或形式參數位置表達式可能會在堆疊上留下多個條目,並且僅返回頂部條目。GDB 通過始終在相同的堆疊上執行 E 來實作
DW_OP_call*
。如果位置列表有多個匹配的條目,它只會選擇第一個條目並忽略其餘條目。這似乎從根本上與支持變數多個位置的願望相矛盾。因此,感覺
DW_OP_call*
應該既支援將變數或形式參數的位置描述推送到堆疊上,又支援能夠在相同的堆疊上執行操作表達式。能夠為不同的程式位置指定不同的操作表達式似乎是一個值得保留的理想功能。一個解決方案是為
DW_TAG_dwarf_procedure
偵錯資訊條目提供一個不同的DW_AT_proc
屬性。然後,DW_AT_location
屬性表達式始終單獨執行,並推送位置描述(可能具有多個單一位置描述),而DW_AT_proc
屬性表達式始終在相同的堆疊上執行,並且可以在堆疊上留下任何內容。DW_AT_proc
屬性可以具有新的類別exprproc
、loclistproc
和loclistsptrproc
,以指示表達式在相同的堆疊上執行。exprproc
的編碼與exprloc
相同。loclistproc
和loclistsptrproc
的編碼與它們的非proc
對應項相同,除非位置列表與恰好一個位置列表條目不匹配並且需要預設條目,否則 DWARF 格式不正確。這些形式明確指示匹配的單個操作表達式必須在相同的堆疊上執行。這比針對loclistproc
和loclistsptrproc
的臨時特殊規則更好,這些規則目前已明確定義為始終返回位置描述。然後,生產者通過屬性類別明確指出意圖。這種變更對於 GDB 如何實作
DW_OP_call*
來說將是一個破壞性變更。但是,破壞性案例實際上是否在實務中發生?GDB 可以為 DWARF 第 5 版實作當前方法,並為 DWARF 第 6 版實作新的語意,這已針對某些其他功能完成。另一個選項是限制執行,使其僅限於評估表達式 E,而 E 是
DW_TAG_dwarf_procedure
除錯資訊條目的DW_AT_location
屬性的值。如果 E 是一個位置列表表達式,但不完全符合一個位置列表條目,則 DWARF 會格式不正確。在所有其他情況下,評估作為DW_AT_location
屬性值的表達式 E 時,將使用目前的上下文評估 E,但結果種類是位置描述,編譯單元是包含 D 的那個,且初始堆疊為空。位置描述結果會被推入堆疊。如果 D 具有帶有值 V 的
DW_AT_const_value
屬性,則相當於執行了DW_OP_implicit_value V
運算。這允許使用呼叫運算來計算任何變數或形式參數的位置描述,無論產生器是否已將其最佳化為常數。這與
DW_OP_implicit_pointer
運算一致。注意:或者,可以棄用對
DW_TAG_variable
和DW_TAG_formal_parameter
除錯資訊條目(它們是常數)使用DW_AT_const_value
,而是改用DW_AT_location
以及產生具有一個隱含位置描述的位置描述的運算表達式。那麼就不需要此規則。否則,不會有任何效果,堆疊也不會進行任何變更。
注意:在 DWARF 版本 5 中,如果 D 沒有
DW_AT_location
,則DW_OP_call*
定義為沒有效果。目前尚不清楚這是否是正確的定義,因為產生器應該能夠依賴使用DW_OP_call*
來取得任何非DW_TAG_dwarf_procedure
除錯資訊條目的位置描述。此外,產生器不應建立針對不具有DW_AT_location
屬性的DW_TAG_dwarf_procedure
的DW_OP_call*
的 DWARF。因此,這種情況是否應定義為格式不正確的 DWARF 表達式?
DW_TAG_dwarf_procedure
除錯資訊條目可用於定義可以呼叫的 DWARF 程序。
A.2.5.4.3 值運算¶
本節描述將值推入堆疊的運算。
每個值堆疊條目都有一個型別和一個字面值。它可以表示目標架構任何支援的基礎型別的字面值。基礎型別指定字面值的大小、編碼和位元組順序。
值堆疊條目的基礎型別可以是區分的泛型型別。
A.2.5.4.3.1 字面值運算¶
注意:本節取代 DWARF 版本 5 第 2.5.1.1 節。
以下運算都會將字面值推入 DWARF 堆疊。
除了 DW_OP_const_type
之外的運算會將具有泛型型別的值 V 推入。如果 V 大於泛型型別,則 V 會被截斷為泛型型別大小,並使用低位元。
DW_OP_lit0
、DW_OP_lit1
、…、DW_OP_lit31
DW_OP_lit<N>
運算編碼一個從 0 到 31(含)的無號字面值 N。它們會將值 N 與泛型型別一起推入。DW_OP_const1u
、DW_OP_const2u
、DW_OP_const4u
、DW_OP_const8u
DW_OP_const<N>u
運算有一個單一運算元,它是一個分別為 1、2、4 或 8 位元組的無號整數常數 U。它們會將值 U 與泛型型別一起推入。DW_OP_const1s
、DW_OP_const2s
、DW_OP_const4s
、DW_OP_const8s
DW_OP_const<N>s
運算有一個單一運算元,它是一個分別為 1、2、4 或 8 位元組的帶號整數常數 S。它們會將值 S 與泛型型別一起推入。DW_OP_constu
DW_OP_constu
有一個單一的無號 LEB128 整數運算元 N。它會將值 N 與泛型型別一起推入。DW_OP_consts
DW_OP_consts
有一個單一的帶號 LEB128 整數運算元 N。它會將值 N 與泛型型別一起推入。DW_OP_constx
DW_OP_constx
有一個單一的無號 LEB128 整數運算元,它表示相對於關聯編譯單元的DW_AT_addr_base
屬性值,在.debug_addr
區段中從零開始的索引。.debug_addr
區段中的值 N 具有泛型型別的大小。它會將值 N 與泛型型別一起推入。DW_OP_constx
運算用於需要連結時重定位的常數,但不應被消費者解釋為可重定位的位址(例如,執行緒本地儲存的偏移量)。DW_OP_const_type
DW_OP_const_type
有三個運算元。第一個是無號 LEB128 整數 DR,表示除錯資訊條目 D 相對於目前編譯單元開頭的位元組偏移量,它提供常數值的型別 T。第二個是 1 位元組的無號整數常數 S。第三個是位元組區塊 B,其長度等於 S。TS 是型別 T 的位元大小。B 的最低有效 TS 位元被解釋為型別 D 的值 V。它會將值 V 與型別 D 一起推入。
如果 D 不是目前編譯單元中的
DW_TAG_base_type
除錯資訊條目,或者如果 TS 除以 8(位元組大小)並向上捨入為整數不等於 S,則 DWARF 格式不正確。雖然可以從型別 D 定義推斷位元組區塊 B 的大小,但它被明確地編碼到運算中,以便可以輕鬆地剖析運算,而無需參考
.debug_info
區段。
A.2.5.4.3.2 算術和邏輯運算¶
注意:本節與 DWARF 版本 5 第 2.5.1.4 節相同。
A.2.5.4.3.3 型別轉換運算¶
注意:本節與 DWARF 版本 5 第 2.5.1.6 節相同。
A.2.5.4.3.4 特殊值運算¶
注意:本節取代 DWARF 版本 5 第 2.5.1.2 節、2.5.1.3 節和 2.5.1.7 節的部分內容。
目前定義了以下這些特殊值運算
DW_OP_regval_type
DW_OP_regval_type
有兩個運算元。第一個是無號 LEB128 整數,表示暫存器編號 R。第二個是無號 LEB128 整數 DR,表示除錯資訊條目 D 相對於目前編譯單元開頭的位元組偏移量,它提供暫存器值的型別 T。此運算等效於執行
DW_OP_regx R; DW_OP_deref_type DR
。注意:DWARF 是否應允許型別 T 大於暫存器 R 的大小?限制較大的位元大小可以避免任何轉換問題,因為暫存器的(可能被截斷的)位元內容僅被解釋為型別 T 的值。如果需要轉換,可以使用
DW_OP_convert
運算明確地完成。GDB 具有每個暫存器的鉤子,允許在每個暫存器的基礎上進行目標特定的轉換。預設值是截斷較大的暫存器。移除目標鉤子的使用不會在常見架構中導致任何測試失敗。如果目標架構的編譯器確實需要某種形式的轉換,包括更大的結果型別,它始終可以明確地使用
DW_OP_convert
運算。如果 T 是大於暫存器大小的型別,則預設的 GDB 暫存器鉤子會從下一個暫存器讀取位元組(或為最後一個暫存器讀取超出範圍!)。移除目標鉤子的使用不會在常見架構中導致任何測試失敗(除非是非法手寫組合語言測試)。如果目標架構需要此行為,則這些擴充功能允許使用複合位置描述來組合多個暫存器。
DW_OP_deref
S 是泛型型別的位元大小除以 8(位元組大小)並向上捨入為整數。DR 是目前編譯單元中假設的除錯資訊條目 D 的偏移量,用於泛型型別的基礎型別。
此運算等效於執行
DW_OP_deref_type S, DR
。DW_OP_deref_size
DW_OP_deref_size
有一個單一的 1 位元組無號整數常數,表示位元組結果大小 S。TS 是泛型型別位元大小和 S 縮放 8 倍(位元組大小)中較小的值。如果 TS 小於泛型型別位元大小,則 T 是位元大小為 TS 的無號整數型別,否則 T 是泛型型別。DR 是目前編譯單元中假設的除錯資訊條目 D 的偏移量,用於基礎型別 T。
注意:當 S 大於泛型型別時,截斷值符合 GDB 的行為。這允許泛型型別大小不是整數位元組大小。它確實允許 S 任意大。S 是否應限制為捨入為 8 的倍數的泛型型別大小?
此運算等效於執行
DW_OP_deref_type S, DR
,除非 T 不是泛型型別,否則推入的值 V 會零擴展為泛型型別位元大小,並且其型別變更為泛型型別。DW_OP_deref_type
DW_OP_deref_type
有兩個運算元。第一個是 1 位元組無號整數常數 S。第二個是無號 LEB128 整數 DR,表示除錯資訊條目 D 相對於目前編譯單元開頭的位元組偏移量,它提供結果值的型別 T。TS 是型別 T 的位元大小。
雖然可以從型別 T 推斷推入的值 V 的大小,但它被明確地編碼為運算元 S,以便可以輕鬆地剖析運算,而無需參考
.debug_info
區段。注意:目前尚不清楚為什麼需要運算元 S。與
DW_OP_const_type
不同,剖析不需要大小。任何評估都需要取得基礎型別 T 與值一起推入,以知道其編碼和位元大小。它彈出一個堆疊條目,該條目必須是位置描述 L。
從 L 的單一位置描述 SL 之一指定的位置儲存 LS 中檢索 TS 位元的值 V。
如果 L 或任何作為 L 子組件的複合位置描述部分的的位置描述有多個單一位置描述,則可以選擇其中任何一個,因為它們都必須具有相同的值。對於任何單一位置描述 SL,位元是從關聯的儲存位置檢索的,從 SL 指定的位元偏移量開始。對於複合位置描述,檢索的位元是來自每個複合位置部分 PL 的 N 個位元的串聯,其中 N 限制為 PL 的大小。
V 會與型別 T 一起推入堆疊。
注意:如果 L 是暫存器位置描述,其暫存器儲存空間中剩餘的位元少於 TS 位元,則此定義會使其成為評估錯誤。特別是因為這些擴充功能擴充了位置描述以具有位元偏移量,因此將其定義為基於型別執行符號擴充或依賴於目標架構將會很奇怪,因為剩餘位元的數量可以是任何數字。這符合 GDB 針對
DW_OP_deref_type
的實作。這些擴充功能根據
DW_OP_regval_type
定義DW_OP_*breg*
。DW_OP_regval_type
是根據DW_OP_regx
(它使用 0 位元偏移量)和DW_OP_deref_type
定義的。因此,它要求暫存器大小大於或等於位址空間的位址大小。這符合 GDB 針對DW_OP_*breg*
的實作。如果 D 不在目前的編譯單元中,D 不是
DW_TAG_base_type
除錯資訊條目,或者如果 TS 除以 8(位元組大小)並向上捨入為整數不等於 S,則 DWARF 格式不正確。注意:此定義允許基礎型別為位元大小,因為似乎沒有理由限制它。
如果從未定義的位置儲存檢索到任何位元,或者任何位元的偏移量超過 L 的任何單一位置描述 SL 指定的位置儲存 LS 的大小,則會發生評估錯誤。
請參閱 2.5.4.4.5 隱含位置描述運算,以瞭解有關
DW_OP_implicit_pointer
運算建立的隱含位置描述的特殊規則。DW_OP_xderef
DW_OP_xderef
彈出兩個堆疊條目。第一個必須是表示位址 A 的整數型別值。第二個必須是表示目標架構特定的位址空間識別碼 AS 的整數型別值。位址大小 S 定義為對應於 AS 的目標架構特定位址空間的位址位元大小。
A 會調整為 S 位元,必要時進行零擴展,然後將最低有效 S 位元視為無號值 A'。
它建立一個具有一個記憶體位置描述 SL 的位置描述 L。SL 指定對應於 AS 的記憶體位置儲存 LS,其位元偏移量等於 A' 縮放 8 倍(位元組大小)。
如果 AS 是特定於上下文元素的位址空間,則 LS 對應於與目前上下文關聯的位置儲存。
例如,如果 AS 用於每個執行緒的儲存,則 LS 是目前執行緒的位置儲存。因此,如果 L 由運算存取,則存取的是在建立位置描述時選取的位置儲存,而不是與存取運算的目前上下文關聯的位置儲存。
如果 AS 不是目標架構特定的
DW_ASPACE_*
值定義的值之一,則 DWARF 表達式格式不正確。此運算等效於彈出 A 和 AS,推入 L,然後執行
DW_OP_deref
。檢索到的值 V 會保留在堆疊上,並具有泛型型別。DW_OP_xderef_size
DW_OP_xderef_size
有一個單一的 1 位元組無號整數常數,表示位元組結果大小 S。它彈出兩個堆疊條目。第一個必須是表示位址 A 的整數型別值。第二個必須是表示目標架構特定的位址空間識別碼 AS 的整數型別值。
它會建立位置描述 L,如
DW_OP_xderef
所述。此運算等效於彈出 A 和 AS,推入 L,然後執行
DW_OP_deref_size S
。檢索到的零擴展值 V 會保留在堆疊上,並具有泛型型別。DW_OP_xderef_type
DW_OP_xderef_type
有兩個運算元。第一個是 1 位元組無號整數常數 S。第二個運算元是無號 LEB128 整數 DR,表示除錯資訊條目 D 相對於目前編譯單元開頭的位元組偏移量,它提供結果值的型別 T。它彈出兩個堆疊條目。第一個必須是表示位址 A 的整數型別值。第二個必須是表示目標架構特定的位址空間識別碼 AS 的整數型別值。
它會建立位置描述 L,如
DW_OP_xderef
所述。此運算等效於彈出 A 和 AS,推入 L,然後執行
DW_OP_deref_type DR
。檢索到的值 V 會保留在堆疊上,並具有型別 T。DW_OP_entry_value
已棄用DW_OP_entry_value
推入在呼叫框架的上下文中評估的表達式的值。它可用於判斷進入目前呼叫框架時的引數值,前提是它們未被覆寫。
它有兩個運算元。第一個是無號 LEB128 整數 S。第二個是位元組區塊,其長度等於 S,被解釋為 DWARF 運算表達式 E。
E 是使用目前的上下文評估的,但結果種類未指定,呼叫框架是呼叫目前框架的框架,程式位置是呼叫框架中的呼叫點,物件未指定,且初始堆疊為空。呼叫框架資訊是透過使用呼叫框架資訊(請參閱 6.4 呼叫框架資訊)虛擬地展開目前呼叫框架來取得的。
如果 E 的結果是位置描述 L(請參閱 2.5.4.4.4 暫存器位置描述運算),並且 E 執行的最後一個運算是針對暫存器 R 的
DW_OP_reg*
,其目標架構特定基礎型別為 T,則會檢索暫存器的內容,如同執行了DW_OP_deref_type DR
運算一樣,其中 DR 是目前編譯單元中假設的 T 型別除錯資訊條目的偏移量。產生的值 V 會被推入堆疊。使用
DW_OP_reg*
為值在進入子程式時位於暫存器中的情況提供了更緊湊的形式。注意:目前尚不清楚這如何提供更緊湊的表達式,因為可以使用
DW_OP_regval_type
,它只是稍微大一點。如果 E 的結果是值 V,則 V 會被推入堆疊。
否則,DWARF 表達式格式不正確。
DW_OP_entry_value
運算已棄用,因為其主要用途由其他方式提供。DWARF 版本 5 為呼叫點新增了DW_TAG_call_site_parameter
除錯資訊條目,它具有DW_AT_call_value
、DW_AT_call_data_location
和DW_AT_call_data_value
屬性,這些屬性提供了 DWARF 表達式來計算呼叫時的實際參數值,並要求產生器確保表達式即使在虛擬展開時也有效評估。注意:GDB 僅在 E 完全是
DW_OP_reg*
或DW_OP_breg*; DW_OP_deref*
時實作DW_OP_entry_value
。
A.2.5.4.4 位置描述運算¶
本節描述將位置描述推入堆疊的運算。
A.2.5.4.4.1 一般位置描述運算¶
注意:本節取代 DWARF 版本 5 第 2.5.1.3 節的部分內容。
DW_OP_push_object_address
DW_OP_push_object_address
推入目前物件的位置描述 L。此物件可能對應於作為正在評估的使用者呈現的表達式一部分的獨立變數。物件位置描述可以從變數自己的除錯資訊條目中判斷,或者它可能是陣列、結構或類別的組件,其位址已在使用者表達式評估期間的較早步驟中動態判斷。
此運算提供了明確的功能(特別是對於涉及描述器的陣列),該功能類似於在評估
DW_AT_data_member_location
以存取結構的資料成員之前隱含地推入結構的基礎位置描述。注意:可以移除此運算,並將物件位置描述指定為初始堆疊,如同
DW_AT_data_member_location
一樣。或者,可以使用此運算而不是需要指定初始堆疊。後者的方法更具可組合性,因為可能需要在表達式的任何點存取物件,並且將其作為初始堆疊傳遞需要整個表達式知道它在堆疊上的位置。如果這樣做,
DW_AT_use_location
將需要DW_OP_push_object2_address
運算來表示第二個物件。或者,更通用的方法是傳遞任意數量的引數,並使用運算(例如
DW_OP_arg N
)來取得第 N 個引數。然後,引數向量將在表達式上下文中傳遞,而不是初始堆疊。這也可以解決DW_OP_call*
的問題,方法是允許指定傳入和傳回的特定數量的引數。DW_OP_call*
運算然後始終可以在單獨的堆疊上執行:引數的數量將在新的呼叫運算中指定,並從呼叫者的堆疊中取得,類似地,傳回結果的數量將被指定,並在呼叫的表達式完成時從呼叫的堆疊複製回被呼叫者的堆疊。唯一指定目前物件的屬性是
DW_AT_data_location
,因此非規範性文字似乎誇大了此運算的使用方式。或者,是否還有其他屬性需要聲明它們傳遞物件?
A.2.5.4.4.2 未定義位置描述運算¶
注意:本節取代 DWARF 版本 5 第 2.6.1.1.1 節。
未定義的位置儲存表示來源中存在但在物件程式碼中不存在的物件的一部分或全部(可能是由於最佳化)。讀取或寫入未定義的位置儲存都沒有意義。
未定義的位置描述指定未定義的位置儲存。未定義的位置儲存沒有大小的概念,也未定義位置描述的位元偏移量。DW_OP_*piece
運算可以隱含地指定未定義的位置描述,允許指定任何大小和偏移量,並產生具有所有未定義位元的部分。
A.2.5.4.4.3 記憶體位置描述運算¶
注意:本節取代 DWARF 版本 5 第 2.5.1.1 節、2.5.1.2 節、2.5.1.3 節和 2.6.1.1.2 節的部分內容。
每個目標架構特定的位址空間都有一個對應的記憶體位置儲存,表示該位址空間的線性可定址記憶體。每個記憶體位置儲存的大小對應於對應位址空間中位址的範圍。
目標架構定義了位址空間位置儲存如何對應到目標架構實體記憶體。例如,它們可以是獨立的記憶體,或者多個位置儲存可以別名相同的實體記憶體,可能在不同的偏移量和具有不同的交錯方式。對應關係也可能由來源語言位址類別決定。
記憶體位置描述指定記憶體位置儲存。位元偏移量對應於記憶體位元組內的位元位置。使用記憶體位置描述存取的位元,存取對應的目標架構記憶體,從位元偏移量指定的位元組內的位元位置開始。
位元偏移量是 8(位元組大小)的倍數的記憶體位置描述被定義為位元組位址記憶體位置描述。它具有記憶體位元組位址 A,該位址等於位元偏移量除以 8。
位元偏移量不是 8(位元組大小)的倍數的記憶體位置描述被定義為位元欄位記憶體位置描述。它具有位元位置 B,等於位元偏移量模數 8,以及記憶體位元組位址 A,等於位元偏移量減去 B,然後除以 8。
記憶體位置描述的位址空間 AS 被定義為對應於與記憶體位置描述關聯的記憶體位置儲存的位址空間。
由一個位元組位址記憶體位置描述 SL 組成的位置描述被定義為記憶體位元組位址位置描述。它具有位元組位址,等於 A,以及位址空間,等於對應 SL 的 AS。
DW_ASPACE_none
被定義為目標架構預設位址空間。
如果堆疊條目需要是位置描述,但它是具有泛型型別的值 V,則它會隱含地轉換為具有一個記憶體位置描述 SL 的位置描述 L。SL 指定對應於目標架構預設位址空間的記憶體位置儲存,其位元偏移量等於 V 縮放 8 倍(位元組大小)。
注意:如果想要允許將任何整數型別值隱含地轉換為目標架構預設位址空間中的記憶體位置描述
如果堆疊條目需要是位置描述,但它是具有整數型別的值 V,則它會隱含地轉換為具有一個記憶體位置描述 SL 的位置描述 L。如果 V 的型別大小小於泛型型別大小,則值 V 會零擴展為泛型型別的大小。最低有效泛型型別大小位元被視為無號值,用作位址 A。SL 指定對應於目標架構預設位址空間的記憶體位置儲存,其位元偏移量等於 A 縮放 8 倍(位元組大小)。
隱含轉換也可以定義為目標架構特定的。例如,GDB 檢查 V 是否為整數型別。如果不是,則會產生錯誤。否則,GDB 會將 V 零擴展為 64 位元。如果 GDB 目標定義了鉤子函數,則會呼叫它。目標特定的鉤子函數可以修改 64 位元值,可能基於原始值型別進行符號擴展。最後,GDB 將 64 位元值 V 視為記憶體位置位址。
如果堆疊條目需要是位置描述,但它是具有目標架構預設位址空間的隱含指標值 IPV,則它會隱含地轉換為具有 IPV 指定的一個單一位置描述的位置描述。請參閱 2.5.4.4.5 隱含位置描述運算。
如果堆疊條目需要是值,但它是具有目標架構預設位址空間中的一個記憶體位置描述 SL 的位置描述 L,且位元偏移量 B 是 8 的倍數,則它會隱含地轉換為等於 B 除以 8(位元組大小)的值,並具有泛型型別。
DW_OP_addr
DW_OP_addr
有一個單一位元組常數值運算元,其大小為泛型型別的大小,表示位址 A。它將具有一個記憶體位置描述 SL 的位置描述 L 推入堆疊。SL 指定對應於目標架構預設位址空間的記憶體位置儲存,其位元偏移量等於 A 縮放 8 倍(位元組大小)。
如果 DWARF 是程式碼物件的一部分,則 A 可能需要重定位。例如,在 ELF 程式碼物件格式中,A 必須透過 ELF 段虛擬位址與段載入時的虛擬位址之間的差值進行調整。
DW_OP_addrx
DW_OP_addrx
有一個單一的無號 LEB128 整數運算元,它表示相對於關聯編譯單元的DW_AT_addr_base
屬性值,在.debug_addr
區段中從零開始的索引。.debug_addr
區段中的位址值 A 具有泛型型別的大小。它將具有一個記憶體位置描述 SL 的位置描述 L 推入堆疊。SL 指定對應於目標架構預設位址空間的記憶體位置儲存,其位元偏移量等於 A 縮放 8 倍(位元組大小)。
如果 DWARF 是程式碼物件的一部分,則 A 可能需要重定位。例如,在 ELF 程式碼物件格式中,A 必須透過 ELF 段虛擬位址與段載入時的虛擬位址之間的差值進行調整。
DW_OP_form_tls_address
DW_OP_form_tls_address
彈出一個堆疊條目,該條目必須是整數型別值,並將其視為執行緒本地儲存位址 TA。它將具有一個記憶體位置描述 SL 的位置描述 L 推入堆疊。SL 是目標架構特定的記憶體位置描述,對應於執行緒本地儲存位址 TA。
執行緒本地儲存位址 TA 的含義由執行時期環境定義。如果執行時期環境支援單一執行緒的多個執行緒本地儲存區塊,則會使用對應於包含此 DWARF 表達式的可執行檔或共享函式庫的區塊。
C、C++、Fortran 和其他語言的一些實作支援執行緒本地儲存類別。具有此儲存類別的變數在不同的執行緒中具有不同的值和位址,就像自動變數在每個子程式調用中具有不同的值和位址一樣。通常,有一個單一的儲存區塊,其中包含在主可執行檔中宣告的所有執行緒本地變數,以及每個共享函式庫中宣告的變數的單獨區塊。然後可以使用識別符在其區塊中存取每個執行緒本地變數。此識別符通常是區塊的位元組偏移量,並在
DW_OP_form_tls_address
運算之前由DW_OP_const*
運算之一推入 DWARF 堆疊。計算適當區塊的位址可能很複雜(在某些情況下,編譯器會發出函數呼叫來執行此操作),並且難以使用普通的 DWARF 位置描述來描述。與其將複雜的執行緒本地儲存計算強制放入 DWARF 表達式中,不如使用DW_OP_form_tls_address
允許消費者根據目標架構特定的執行時期環境執行計算。DW_OP_call_frame_cfa
DW_OP_call_frame_cfa
會推送目前子程式的標準框架位址 (Canonical Frame Address, CFA) 的位置描述 L,該位址從堆疊上的呼叫框架資訊取得。請參閱6.4 呼叫框架資訊。儘管對應於目前子程式的偵錯資訊條目的
DW_AT_frame_base
屬性的值可以使用位置列表達式計算,但在某些情況下,這會需要廣泛的位置列表,因為用於計算 CFA 的暫存器的值在子程式執行期間會改變。如果呼叫框架資訊存在,則它已經編碼了這些變化,並且使用DW_OP_call_frame_cfa
運算來參考它是節省空間的。DW_OP_fbreg
DW_OP_fbreg
有一個單一帶正負號的 LEB128 整數運算元,代表位元組位移量 B。目前子程式的框架基底的位置描述 L 是從對應於目前子程式的偵錯資訊條目的
DW_AT_frame_base
屬性取得,如3.3.5 低階資訊中所述。位置描述 L 會透過位元偏移量 B 縮放 8 (位元組大小) 來更新,並推送到堆疊上。
DW_OP_breg0
、DW_OP_breg1
、…、DW_OP_breg31
DW_OP_breg<N>
運算編碼最多 32 個暫存器的編號,編號從 0 到 31 (包含)。暫存器編號 R 對應於運算名稱中的 N。它們有一個單一帶正負號的 LEB128 整數運算元,代表位元組位移量 B。
位址空間識別符 AS 定義為對應於目標架構特定預設位址空間的識別符。
位址大小 S 定義為對應於 AS 的目標架構特定位址空間的位址位元大小。
擷取由 R 指定的暫存器的內容,如同執行了
DW_OP_regval_type R, DR
運算,其中 DR 是目前編譯單元中假設的偵錯資訊條目的偏移量,該條目用於大小為 S 位元的無號整數基底類型。B 會被加總,且最低有效 S 位元會被視為無號值,以用作位址 A。它們會推送位置描述 L,其中包含堆疊上的一個記憶體位置描述 LS。LS 指定對應於 AS 的記憶體位置儲存,其位元偏移量等於 A 縮放 8 (位元組大小)。
DW_OP_bregx
DW_OP_bregx
有兩個運算元。第一個是無號 LEB128 整數,代表暫存器編號 R。第二個是帶正負號的 LEB128 整數,代表位元組位移量 B。動作與
DW_OP_breg<N>
相同,不同之處在於 R 用作暫存器編號,而 B 用作位元組位移量。
A.2.5.4.4.4 暫存器位置描述運算¶
注意:本節取代 DWARF 第 5 版第 2.6.1.1.3 節。
存在一個暫存器位置儲存,其對應於每個目標架構暫存器。每個暫存器位置儲存的大小對應於相應目標架構暫存器的大小。
暫存器位置描述指定一個暫存器位置儲存。位元偏移量對應於暫存器內的位元位置。使用暫存器位置描述存取的位元會存取對應的目標架構暫存器,從指定的位元偏移量開始。
DW_OP_reg0
、DW_OP_reg1
、…、DW_OP_reg31
DW_OP_reg<N>
運算編碼最多 32 個暫存器的編號,編號從 0 到 31 (包含)。目標架構暫存器編號 R 對應於運算名稱中的 N。該運算等效於執行
DW_OP_regx R
。DW_OP_regx
DW_OP_regx
有一個單一無號 LEB128 整數運算元,代表目標架構暫存器編號 R。如果目前的呼叫框架是最頂層的呼叫框架,它會推送位置描述 L,該描述指定堆疊上的一個暫存器位置描述 SL。SL 指定對應於 R 的暫存器位置儲存,針對目前執行緒的位元偏移量為 0。
如果目前的呼叫框架不是最頂層的呼叫框架,則會使用呼叫框架資訊 (請參閱6.4 呼叫框架資訊) 來決定保存目前呼叫框架和目前執行緒的程式位置的暫存器的位置描述。推送產生的位置描述 L。
請注意,如果使用呼叫框架資訊,則產生的位置描述可能是暫存器、記憶體或未定義。
實作可以立即評估呼叫框架資訊,或者可以延遲評估直到 L 被運算存取。如果評估被延遲,R 和目前的上下文可以記錄在 L 中。當被存取時,會使用記錄的上下文來評估呼叫框架資訊,而不是存取運算的目前上下文。
這些運算取得暫存器位置。若要提取暫存器的內容,必須使用 DW_OP_regval_type
、使用其中一個 DW_OP_breg*
基於暫存器的定址運算,或在暫存器位置描述上使用 DW_OP_deref*
。
A.2.5.4.4.5 隱含位置描述運算¶
注意:本節取代 DWARF 第 5 版第 2.6.1.1.4 節。
隱含位置儲存代表一個物件的一部分或全部,該物件在程式中沒有實際位置,但其內容仍然是已知的,可以是常數,也可以從程式中的其他位置和值計算出來。
隱含位置描述指定一個隱含位置儲存。位元偏移量對應於隱含位置儲存內的位元位置。使用隱含位置描述存取的位元,會存取對應的隱含儲存值,從位元偏移量開始。
DW_OP_implicit_value
DW_OP_implicit_value
有兩個運算元。第一個是無號 LEB128 整數,代表位元組大小 S。第二個是位元組區塊,其長度等於 S,被視為字面值 V。建立一個隱含位置儲存 LS,其具有字面值 V 和大小 S。
它會推送位置描述 L,其中包含堆疊上的一個隱含位置描述 SL。SL 指定 LS,位元偏移量為 0。
DW_OP_stack_value
DW_OP_stack_value
彈出一個堆疊條目,該條目必須是值 V。建立一個隱含位置儲存 LS,其具有字面值 V,並使用 V 的基底類型指定的大小、編碼和位元組序。
它會推送位置描述 L,其中包含堆疊上的一個隱含位置描述 SL。SL 指定 LS,位元偏移量為 0。
DW_OP_stack_value
運算指定物件不存在於記憶體中,但其值仍然是已知的。在這種形式中,位置描述指定物件的實際值,而不是指定保存該值的記憶體或暫存器儲存。請參閱
DW_OP_implicit_pointer
(如下),以了解由DW_OP_implicit_pointer
運算建立的隱含位置描述取消參考產生的隱含指標值的特殊規則。注意:由於位置描述允許在堆疊上,
DW_OP_stack_value
運算不再像在 DWARF 第 5 版中那樣終止 DWARF 運算式執行。DW_OP_implicit_pointer
最佳化編譯器可能會消除指標,同時仍然保留指標所指向的值。
DW_OP_implicit_pointer
允許產生器描述這個值。DW_OP_implicit_pointer
指定物件是指向目標架構預設位址空間的指標,即使它可以描述它將指向的值,但它不能表示為真實的指標。在這種形式中,位置描述指定一個偵錯資訊條目,該條目表示指標將指向的物件的實際位置描述。因此,偵錯資訊的消費者將能夠存取取消參考的指標,即使它無法存取指標本身。DW_OP_implicit_pointer
有兩個運算元。第一個運算元是一個 4 位元組的無號值 (在 32 位元 DWARF 格式中) 或一個 8 位元組的無號值 (在 64 位元 DWARF 格式中),代表偵錯資訊條目 D 相對於包含目前編譯單元的.debug_info
區段開頭的位元組偏移量 DR。第二個運算元是一個帶正負號的 LEB128 整數,代表位元組位移量 B。請注意,D 可能不在目前的編譯單元中。
第一個運算元的解釋與
DW_FORM_ref_addr
完全相同。位址空間識別符 AS 定義為對應於目標架構特定預設位址空間的識別符。
位址大小 S 定義為對應於 AS 的目標架構特定位址空間的位址位元大小。
建立一個隱含位置儲存 LS,其具有偵錯資訊條目 D、位址空間 AS 和大小 S。
它會推送位置描述 L,其中包含堆疊上的一個隱含位置描述 SL。SL 指定 LS,位元偏移量為 0。
如果
DW_OP_deref*
運算彈出位置描述 L',並擷取 S 位元,使得任何擷取的位元來自與 LS 相同的隱含位置儲存,則會發生評估錯誤,除非滿足以下兩個條件所有擷取的位元都來自隱含位置描述,該描述引用與 LS 相同的隱含位置儲存。
請注意,所有位元不必來自相同的隱含位置描述,因為 L' 可能涉及複合位置描述。
這些位元來自其各自隱含位置儲存中連續遞增的偏移量。
這些規則等效於擷取 LS 的完整內容。
如果滿足上述兩個條件,則由
DW_OP_deref*
運算推送的值 V 是一個隱含指標值 IPV,其具有目標架構特定位址空間 AS、偵錯資訊條目 D 和基底類型 T。如果 AS 是目標架構預設位址空間,則 T 是通用類型。否則,T 是目標架構特定整數類型,其位元大小等於 S。如果 IPV 被隱含地轉換為位置描述 (僅在 AS 是目標架構預設位址空間時執行),則產生的位置描述 RL 是
如果 D 具有
DW_AT_location
屬性,則會使用目前的上下文評估來自DW_AT_location
屬性的 DWARF 運算式 E,但結果種類是位置描述,編譯單元是包含 D 的單元,物件未指定,且初始堆疊為空。RL 是運算式結果。請注意,E 是使用存取 IPV 的運算式的上下文來評估的,而不是包含建立 L 的
DW_OP_implicit_pointer
運算的運算式的上下文。如果 D 具有
DW_AT_const_value
屬性,則會從DW_AT_const_value
屬性的值建立一個隱含位置儲存 RLS,其大小與DW_AT_const_value
屬性的值的大小相符。RL 包含一個隱含位置描述 SRL。SRL 指定 RLS,位元偏移量為 0。注意:如果針對變數和形式參數使用
DW_AT_const_value
已被棄用,並且改為將DW_AT_location
與隱含位置描述一起使用,則此規則將不是必需的。否則,會發生評估錯誤。
位置描述 RL 會透過位元偏移量 B 縮放 8 (位元組大小) 來更新。
如果
DW_OP_stack_value
運算彈出與 IPV 相同的值,則它會推送與 L 相同的位置描述。如果以任何其他方式存取 LS 或 IPV,則會發生評估錯誤。
對於由
DW_OP_implicit_pointer
建立的隱含指標位置描述的使用方式的限制,是為了簡化 DWARF 消費者。對於由DW_OP_deref*
和DW_OP_stack_value
建立的隱含指標值也是如此。
通常,DW_OP_implicit_pointer
運算會用於 DW_TAG_variable
或 DW_TAG_formal_parameter
偵錯資訊條目 D1 的 DW_AT_location
屬性的 DWARF 運算式 E1 中。DW_OP_implicit_pointer
運算引用的偵錯資訊條目通常本身是一個 DW_TAG_variable
或 DW_TAG_formal_parameter
偵錯資訊條目 D2,其 DW_AT_location
屬性給出第二個 DWARF 運算式 E2。
D1 和 E1 描述指標類型物件的位置。D2 和 E2 描述該指標物件指向的物件的位置。
但是,D2 可能是任何包含 DW_AT_location
或 DW_AT_const_value
屬性的偵錯資訊條目 (例如,DW_TAG_dwarf_procedure
)。透過使用 E2,當要求取消參考由包含 DW_OP_implicit_pointer
運算的 E1 描述的指標時,消費者可以重建物件的值。
A.2.5.4.4.6 複合位置描述運算¶
注意:本節取代 DWARF 第 5 版第 2.6.1.2 節。
複合位置儲存代表一個物件或值,該物件或值可能包含在另一個位置儲存的一部分中,或包含在多個位置儲存的部分中。
每個部分都有一個部分位置描述 L 和一個部分位元大小 S。L 可以有一個或多個單一位置描述 SL。如果有多個 SL,則表示該部分位於多個位置。每個位置的部分的位元包含來自 SL 指定的位置儲存 LS 的 S 個連續位元,從 SL 指定的位元偏移量開始。所有位元都必須在 LS 的大小範圍內,否則 DWARF 運算式格式不正確。
複合位置儲存可以有零個或多個部分。這些部分是連續的,因此從零開始的位置儲存位元索引將在每個部分上範圍,且它們之間沒有間隙。因此,複合位置儲存的大小是其部分大小的總和。如果連續位置儲存的大小大於對應於最大目標架構特定位址空間的記憶體位置儲存的大小,則 DWARF 運算式格式不正確。
複合位置描述指定一個複合位置儲存。位元偏移量對應於複合位置儲存內的位元位置。
有一些運算會建立複合位置儲存。
還有其他運算允許以遞增方式建立複合位置儲存。每個部分都由一個單獨的運算建立。可能有一個或多個運算來建立最終的複合位置儲存。一系列這樣的運算描述了複合位置儲存的部分,這些部分是依照相關部分運算的執行順序排列的。
為了支援遞增建立,複合位置儲存可以處於不完整狀態。當遞增運算作用於不完整的複合位置儲存時,它會新增一個新的部分。
指定不完整複合位置儲存的複合位置描述稱為不完整複合位置描述。指定完整複合位置儲存的複合位置描述稱為完整複合位置描述。
如果在運算式執行完成後,最頂層堆疊條目是具有一個不完整複合位置描述 SL 的位置描述,則 SL 會轉換為完整複合位置描述。
請注意,此轉換不會在由 DW_OP_call*
運算在同一個堆疊上評估的運算式完成後發生。此類執行不是運算式的單獨評估,而是包含 DW_OP_call*
運算的相同運算式的持續評估。
如果需要堆疊條目為位置描述 L,但 L 具有不完整複合位置描述,則 DWARF 運算式格式不正確。例外情況是用於遞增建立複合位置描述的運算,如下所述。
請注意,DWARF 運算式可以任意組合來自任何其他位置描述的複合位置描述,包括具有多個單一位置描述的位置描述,以及具有複合位置描述的位置描述。
遞增複合位置描述運算被定義為與 DWARF 第 5 版中的定義相容。
DW_OP_piece
DW_OP_piece
有一個單一無號 LEB128 整數,代表位元組大小 S。動作基於上下文
如果堆疊為空,則會將由一個不完整複合位置描述 SL 組成的位置描述 L 推送到堆疊上。
建立一個不完整複合位置儲存 LS,其具有單一部分 P。P 指定位置描述 PL,並具有 S 縮放 8 (位元組大小) 的位元大小。PL 由一個未定義位置描述 PSL 組成。
SL 指定 LS,位元偏移量為 0。
否則,如果最頂層堆疊條目是由一個不完整複合位置描述 SL 組成的位置描述 L,則 SL 指定的不完整複合位置儲存 LS 會被更新以附加一個新的部分 P。P 指定位置描述 PL,並具有 S 縮放 8 (位元組大小) 的位元大小。PL 由一個未定義位置描述 PSL 組成。L 仍保留在堆疊上。
否則,如果最頂層堆疊條目是位置描述或可以轉換為位置描述,則會彈出它並將其視為部分位置描述 PL。然後
如果最頂層堆疊條目 (在彈出 PL 之後) 是由一個不完整複合位置描述 SL 組成的位置描述 L,則 SL 指定的不完整複合位置儲存 LS 會被更新以附加一個新的部分 P。P 指定位置描述 PL,並具有 S 縮放 8 (位元組大小) 的位元大小。L 仍保留在堆疊上。
否則,會將由一個不完整複合位置描述 SL 組成的位置描述 L 推送到堆疊上。
建立一個不完整複合位置儲存 LS,其具有單一部分 P。P 指定位置描述 PL,並具有 S 縮放 8 (位元組大小) 的位元大小。
SL 指定 LS,位元偏移量為 0。
否則,DWARF 運算式格式不正確
許多編譯器將單一變數儲存在暫存器集中,或將變數部分儲存在記憶體中,部分儲存在暫存器中。
DW_OP_piece
提供了一種描述變數的一部分位於何處的方法。DW_OP_piece
運算的評估規則使其能夠與 DWARF 第 5 版定義相容。注意:由於這些擴充功能允許位置描述作為堆疊上的條目,因此可以定義一個更簡單的運算來建立複合位置描述。例如,只有一個運算指定有多少部分,並彈出堆疊條目對,用於部分大小和位置描述。這不僅會是一個更簡單的運算,並避免不完整複合位置描述的複雜性,而且在實務中也可能具有更小的編碼。但是,與 DWARF 第 5 版相容的願望可能是一個更強的考量。
DW_OP_bit_piece
DW_OP_bit_piece
有兩個運算元。第一個是無號 LEB128 整數,代表部分位元大小 S。第二個是無號 LEB128 整數,代表位元位移量 B。動作與
DW_OP_piece
相同,不同之處在於任何建立的部分都具有位元大小 S,並且任何建立的部分的位置描述 PL 都會透過位元偏移量 B 更新。當要組裝的部分不是位元組大小或不在部分位置描述的開頭時,會使用
DW_OP_bit_piece
而不是DW_OP_piece
。
A.2.5.5 DWARF 位置列表達式¶
注意:本節取代 DWARF 第 5 版第 2.6.2 節。
為了滿足近期電腦架構和最佳化技術的需求,偵錯資訊必須能夠描述物件位置,該物件的位置在其生命週期內會改變,並且在物件生命週期的某些部分可能位於多個位置。位置列表達式用於取代運算式,每當要描述其位置的物件具有這些需求時。
位置列表達式由一系列位置列表條目組成。每個位置列表條目都是以下種類之一
有界位置描述
這種位置列表條目提供一個運算式,該運算式評估為在起始和結束位址限定的生命週期內有效的物件的位置描述。起始位址是位置有效的位址範圍的最低位址。結束位址是高於位址範圍最高位址的第一個位置的位址。
當目前程式位置在給定範圍內時,位置列表條目會符合。
有幾種有界位置描述條目,它們在指定起始和結束位址的方式上有所不同。
預設位置描述
這種位置列表條目提供一個運算式,該運算式評估為當沒有有界位置描述條目適用時有效的物件的位置描述。
當目前程式位置不在任何有界位置描述條目的範圍內時,位置列表條目會符合。
基底位址
這種位置列表條目提供一個位址,用於作為某些種類的有界位置描述條目中給出的起始和結束位址偏移量的基底位址。有界位置描述條目的適用基底位址是同一位置列表中最接近的前一個基底位址條目指定的位址。如果沒有前一個基底位址條目,則適用基底位址預設為編譯單元的基底位址 (請參閱 DWARF 第 5 版第 3.1.1 節)。
在所有機器碼都包含在單一連續區段中的編譯單元的情況下,不需要基底位址條目。
列表結束
這種位置列表條目標記位置列表達式的結束。
由位置列表達式的有界位置描述條目定義的位址範圍可能會重疊。當它們重疊時,它們描述物件同時存在於多個位置的情況。
如果給定位置列表達式中的所有位址範圍沒有共同涵蓋物件有定義的整個範圍,並且沒有後續的預設位置描述條目,則假定物件在未涵蓋的範圍部分不可用。
DWARF 位置列表達式的評估結果是
如果未指定目前程式位置,則會發生評估錯誤。
注意:如果位置列表只有一個預設條目,如果沒有程式位置,是否應該將其視為符合?如果存在非預設條目,那麼當沒有程式位置時,似乎必須是評估錯誤,因為這表示位置取決於未知的程式位置。
如果沒有符合的位置列表條目,則結果是包含一個未定義位置描述的位置描述。
否則,每個符合的位置列表條目的運算式 E 都會使用目前的上下文進行評估,但結果種類是位置描述,物件未指定,且初始堆疊為空。位置列表條目結果是由 E 的評估返回的位置描述。
結果是一個位置描述,該描述由每個符合的位置列表條目的位置描述結果的單一位置描述的聯集組成。
位置列表達式只能用作使用類別 loclist
或 loclistsptr
編碼的偵錯資訊條目屬性的值 (請參閱7.5.5 類別和形式)。屬性的值提供一個索引,指向一個單獨的物件檔案區段,稱為 .debug_loclists
或 .debug_loclists.dwo
(對於分割 DWARF 物件檔案),其中包含位置列表條目。
DW_OP_call*
和 DW_OP_implicit_pointer
運算可以用於指定具有位置列表達式的偵錯資訊條目屬性。多個偵錯資訊條目屬性允許 DWARF 運算式,這些運算式使用初始堆疊進行評估,該初始堆疊包含可能源自位置列表達式評估的位置描述。
此位置列表表示法、loclist
和 loclistsptr
類別以及相關的 DW_AT_loclists_base
屬性在 DWARF 第 5 版中是新的。它們共同消除了位置列表達式先前需要的大部分或全部程式碼物件重定位。
注意:本節的其餘部分與 DWARF 第 5 版第 2.6.2 節相同。
A.3 程式範圍條目¶
注意:本節提供對現有偵錯資訊條目屬性的變更。這些變更將被納入相應的 DWARF 第 5 版第 3 章節中。
A.3.3 子常式和進入點條目¶
A.3.3.5 低階資訊¶
DW_TAG_subprogram
、DW_TAG_inlined_subroutine
或DW_TAG_entry_point
偵錯資訊條目可能具有DW_AT_return_addr
屬性,其值為 DWARF 運算式 E。屬性的結果是透過評估 E 取得的,其上下文具有位置描述的結果種類、未指定的物件、包含 E 的編譯單元、空的初始堆疊,以及對應於使用者關注的原始碼語言執行緒的其他上下文元素 (如果有的話)。評估的結果是目前呼叫框架的子程式或進入點的回傳位址儲存位置的位置描述 L。
如果 L 不是由目標架構特定位址空間之一的一個記憶體位置描述組成,則 DWARF 格式不正確。
注意:不清楚為什麼
DW_TAG_inlined_subroutine
具有DW_AT_return_addr
屬性,但沒有DW_AT_frame_base
或DW_AT_static_link
屬性。似乎它要么全部都有,要么全部都沒有。由於內嵌子程式沒有呼叫框架,因此似乎它們都不應該有這些屬性。DW_TAG_subprogram
或DW_TAG_entry_point
偵錯資訊條目可能具有DW_AT_frame_base
屬性,其值為 DWARF 運算式 E。屬性的結果是透過評估 E 取得的,其上下文具有位置描述的結果種類、未指定的物件、包含 E 的編譯單元、空的初始堆疊,以及對應於使用者關注的原始碼語言執行緒的其他上下文元素 (如果有的話)。
如果 E 包含
DW_OP_fbreg
運算,或者產生的位置描述 L 不是由一個單一位置描述 SL 組成,則 DWARF 格式不正確。如果 SL 是暫存器 R 的暫存器位置描述,則 L 會被評估
DW_OP_bregx R, 0
運算的結果取代。這會計算目標架構預設位址空間中的框架基底記憶體位置描述。這允許使用更緊湊的
DW_OP_reg*
而不是DW_OP_breg* 0
。注意:可以移除此規則,並要求產生器直接使用
DW_OP_call_frame_cfa
或DW_OP_breg*
建立所需的位置描述。這也將允許目標在大型暫存器中實作呼叫框架。否則,如果 SL 不是任何目標架構特定位址空間中的記憶體位置描述,則 DWARF 格式不正確。
產生的 L 是子程式或進入點的框架基底。
通常,E 將使用
DW_OP_call_frame_cfa
運算,或是一個堆疊指標暫存器加上或減去一些偏移量。如果
DW_TAG_subprogram
或DW_TAG_entry_point
偵錯資訊條目在語法上是巢狀的,則它可能具有DW_AT_static_link
屬性,其值為 DWARF 運算式 E。屬性的結果是通過評估 E 獲得的,其上下文具有位置描述的結果類型、未指定的物件、包含 E 的編譯單元、空的初始堆疊,以及對應於使用者關注的原始語言執行緒的其他上下文元素(如果有的話)。評估的結果是位置描述 L,它代表直接詞法封閉當前呼叫框架的子程式或進入點的子程式實例的相關呼叫框架的標準框架位址(請參閱 6.4 呼叫框架資訊)。
如果 L 不是由目標架構特定位址空間之一的一個記憶體位置描述組成,則 DWARF 格式不正確。
A.3.4 呼叫點條目和參數¶
A.3.4.2 呼叫點參數¶
一個
DW_TAG_call_site_parameter
偵錯資訊條目可能具有一個DW_AT_call_value
屬性,其值為 DWARF 運算式 E1。DW_AT_call_value
屬性的結果是通過評估 E1 獲得的,其上下文具有值的結果類型、未指定的物件、包含 E 的編譯單元、空的初始堆疊,以及對應於使用者關注的原始語言執行緒的其他上下文元素(如果有的話)。產生的值 V1 是呼叫點進行呼叫時參數的值。對於傳參考參數,其中程式碼傳遞指向包含參數之位置的指標,或者對於參考類型參數,
DW_TAG_call_site_parameter
偵錯資訊條目也可能具有一個DW_AT_call_data_location
屬性(其值為 DWARF 運算式 E2)和一個DW_AT_call_data_value
屬性(其值為 DWARF 運算式 E3)。DW_AT_call_data_location
屬性的值是通過評估 E2 獲得的,其上下文具有位置描述的結果類型、未指定的物件、包含 E 的編譯單元、空的初始堆疊,以及對應於使用者關注的原始語言執行緒的其他上下文元素(如果有的話)。產生的位置描述 L2 是被參考參數在呼叫點進行呼叫期間存在的位置。如果 E2 僅為DW_OP_push_object_address
,則可以省略DW_AT_call_data_location
屬性。注意:DWARF 版本 5 暗示
DW_OP_push_object_address
可能可以使用,但未說明必須在上下文中指定哪個物件。要么DW_OP_push_object_address
無法使用,要么必須定義要在上下文中傳遞的物件。DW_AT_call_data_value
屬性的值是通過評估 E3 獲得的,其上下文具有值的結果類型、未指定的物件、包含 E 的編譯單元、空的初始堆疊,以及對應於使用者關注的原始語言執行緒的其他上下文元素(如果有的話)。產生的值 V3 是呼叫點進行呼叫時 L2 中的值。如果當前呼叫框架不是針對包含
DW_TAG_call_site_parameter
偵錯資訊條目的子程式,或者當前程式位置不是針對當前呼叫框架中包含DW_TAG_call_site_parameter
偵錯資訊條目的呼叫點,則這些屬性的結果未定義。消費者可能必須虛擬回溯到呼叫點(請參閱 6.4 呼叫框架資訊)才能評估這些屬性。這將確保使用者關注的原始語言執行緒對應於評估運算式所需的呼叫點。
如果無法避免這些屬性的運算式存取可能被呼叫點正在呼叫的子程式覆寫的暫存器或記憶體位置,則不應提供相關聯的屬性。
此限制的原因是參數可能需要在被呼叫者的執行期間存取。消費者可能會從被呼叫的子程式虛擬回溯到呼叫者,然後評估屬性運算式。呼叫框架資訊(請參閱 6.4 呼叫框架資訊)將無法還原已被覆寫的暫存器,並且被覆寫的記憶體將不再具有呼叫時的值。
A.3.5 詞法區塊條目¶
注意:本節與 DWARF 版本 5 第 3.5 節相同。
A.4 資料物件和物件列表條目¶
注意:本節提供了對現有偵錯資訊條目屬性的變更。這些變更將被納入對應的 DWARF 版本 5 第 4 章節。
A.4.1 資料物件條目¶
程式變數、形式參數和常數分別由具有標籤 DW_TAG_variable
、DW_TAG_formal_parameter
和 DW_TAG_constant
的偵錯資訊條目表示。
標籤 DW_TAG_constant
用於具有真正的具名常數的語言。
程式變數、形式參數或常數的偵錯資訊條目可能具有以下屬性
一個
DW_AT_location
屬性,其值為 DWARF 運算式 E,用於描述變數或參數在執行時的位置。屬性的結果是通過評估 E 獲得的,其上下文具有位置描述的結果類型、未指定的物件、包含 E 的編譯單元、空的初始堆疊,以及對應於使用者關注的原始語言執行緒的其他上下文元素(如果有的話)。評估的結果是資料物件基底的位置描述。
有關
DW_OP_call*
運算使用的特殊評估規則,請參閱 2.5.4.2 控制流程運算。注意:刪除關於
DW_OP_call*
運算如何評估DW_AT_location
屬性的描述,因為現在已在運算中描述了。注意:請參閱關於
DW_OP_call*
運算中DW_AT_location
屬性的討論。讓每個屬性僅具有單一用途和單一執行語義似乎是理想的。這讓消費者更容易,因為不再需要追蹤上下文。這也讓生產者更容易,因為它可以依賴每個屬性的單一語義。因此,將
DW_AT_location
屬性限制為僅支援評估物件的位置描述,並在相同的運算式堆疊上使用不同的屬性和編碼類別來評估 DWARF 運算式程序似乎是理想的。DW_AT_const_value
注意:可以考慮棄用將
DW_AT_const_value
屬性用於已最佳化為常數的DW_TAG_variable
或DW_TAG_formal_parameter
偵錯資訊條目。相反,DW_AT_location
可以與產生隱含位置描述的 DWARF 運算式一起使用,因為現在任何位置描述都可以在 DWARF 運算式中使用。這允許使用DW_OP_call*
運算來推送任何變數的位置描述,無論其最佳化方式如何。
A.4.2 Common Block 條目¶
common block 條目也具有一個 DW_AT_location 屬性,其值為 DWARF 運算式 E,用於描述 common block 在執行時的位置。屬性的結果是通過評估 E 獲得的,其上下文具有位置描述的結果類型、未指定的物件、包含 E 的編譯單元、空的初始堆疊,以及對應於使用者關注的原始語言執行緒的其他上下文元素(如果有的話)。評估的結果是 common block 基底的位置描述。有關 DW_OP_call* 運算使用的特殊評估規則,請參閱 2.5.4.2 控制流程運算。
A.5 類型條目¶
注意:本節提供了對現有偵錯資訊條目屬性的變更。這些變更將被納入對應的 DWARF 版本 5 第 5 章節。
A.5.7 結構、聯集、類別和介面類型條目¶
A.5.7.3 衍生或擴充的結構、類別和介面¶
對於
DW_AT_data_member_location
屬性,有兩種情況如果屬性是整數常數 B,則它提供從包含實體的開頭開始的位元組偏移量。
屬性的結果是通過將包含實體開頭的位置描述的位元偏移量更新 B 縮放 8 倍(位元組大小)而獲得的。結果是成員條目基底的位置描述。
如果包含實體的開頭未位元組對齊,則成員條目的開頭在位元組內具有相同的位元位移。
否則,屬性必須是 DWARF 運算式 E,它在上下文中使用位置描述的結果類型、未指定的物件、包含 E 的編譯單元、包含包含實體開頭的位置描述的初始堆疊,以及對應於使用者關注的原始語言執行緒的其他上下文元素(如果有的話)進行評估。評估的結果是成員條目基底的位置描述。
注意:包含實體的開頭現在可以是任何位置描述,包括具有多個單一位置描述的位置描述,以及具有任何類型且具有任何位元偏移量的單一位置描述。
A.5.7.8 成員函式條目¶
虛擬函式的條目也具有一個
DW_AT_vtable_elem_location
屬性,其值為 DWARF 運算式 E。屬性的結果是通過評估 E 獲得的,其上下文具有位置描述的結果類型、未指定的物件、包含 E 的編譯單元、包含封閉類型物件的位置描述的初始堆疊,以及對應於使用者關注的原始語言執行緒的其他上下文元素(如果有的話)。評估的結果是封閉類別的虛擬函式表中函式的槽的位置描述。
A.5.14 成員指標類型條目¶
DW_TAG_ptr_to_member_type
偵錯資訊條目具有一個DW_AT_use_location
屬性,其值為 DWARF 運算式 E。它用於計算成員指標條目指向的類別成員的位置描述。用於查找類別、結構或聯集的給定成員的位置描述的方法,對於該類別、結構或聯集的任何實例以及成員指標類型的任何實例都是通用的。因此,該方法與成員指標類型相關聯,而不是與每個具有成員指標類型的物件相關聯。
DW_AT_use_location
DWARF 運算式與給定成員指標類型的特定物件以及特定結構或類別實例的位置描述結合使用。屬性的結果是通過評估 E 獲得的,其上下文具有位置描述的結果類型、未指定的物件、包含 E 的編譯單元、包含兩個條目的初始堆疊,以及對應於使用者關注的原始語言執行緒的其他上下文元素(如果有的話)。第一個堆疊條目是成員指標物件本身的值。第二個堆疊條目是包含正在計算其位置之成員的整個類別、結構或聯集實例的基底的位置描述。評估的結果是成員指標條目指向的類別成員的位置描述。
A.5.18 類型的動態屬性¶
A.5.18.1 資料位置¶
DW_AT_data_location
屬性可以用於任何在其表示法中提供一或多層隱藏間接和/或執行時參數的類型。其值為 DWARF 運算式 E,用於計算物件資料的位置描述。當省略此屬性時,資料的位置描述與物件的位置描述相同。屬性的結果是通過評估 E 獲得的,其上下文具有位置描述的結果類型、作為資料描述器的位置描述的物件、包含 E 的編譯單元、空的初始堆疊,以及對應於使用者關注的原始語言執行緒的其他上下文元素(如果有的話)。評估的結果是成員條目基底的位置描述。
E 通常會涉及以
DW_OP_push_object_address
運算開始的運算式,該運算載入物件的位置描述,然後該位置描述可以在後續計算中充當描述器。注意:由於
DW_AT_data_member_location
、DW_AT_use_location
和DW_AT_vtable_elem_location
都允許運算式和位置列表達式,為什麼DW_AT_data_location
不都允許?在所有情況下,它們都適用於資料物件,因此最佳化不太可能導致不同程式位置範圍的不同運算式。但是,如果支援某些,則應該支援所有。注意:似乎很奇怪,此屬性與
DW_AT_data_member_location
不同,沒有包含物件位置描述的初始堆疊,因為運算式必須需要它。
A.6 其他偵錯資訊¶
注意:本節提供了對現有偵錯資訊條目屬性的變更。這些變更將被納入對應的 DWARF 版本 5 第 6 章節。
A.6.2 行號資訊¶
注意:本節與 DWARF 版本 5 第 6.2 節相同。
A.6.4 呼叫框架資訊¶
注意:本節提供了對 DWARF 版本 5 第 6.4 節的變更。暫存器回溯 DWARF 運算式已通用化,以允許任何位置描述,包括具有複合和隱含位置描述的位置描述。
A.6.4.1 呼叫框架資訊的結構¶
暫存器規則為
undefined
具有此規則的暫存器在前一個框架中沒有可恢復的值。此暫存器的先前值是未定義的位置描述(請參閱 2.5.4.4.2 未定義位置描述運算)。
依照慣例,暫存器不會由被呼叫者保留。
same value
此暫存器未從前一個呼叫者框架修改。
如果當前框架是最頂層框架,則此暫存器的先前值是位置描述 L,它指定一個暫存器位置描述 SL。SL 指定暫存器位置儲存,該儲存對應於當前執行緒位元偏移量為 0 的暫存器。
如果當前框架不是最頂層框架,則此暫存器的先前值是使用被當前呼叫者框架針對相同暫存器呼叫的被呼叫者框架和被呼叫者程式位置的呼叫框架資訊獲得的位置描述。
依照慣例,暫存器由被呼叫者保留,但被呼叫者未修改它。
offset(N)
N 是一個帶符號的位元組偏移量。此暫存器的先前值保存在位置描述 L。其中 L 是當前 CFA(請參閱 2.5.4 DWARF 運算式)的位置描述,該位置描述已更新位元偏移量 N 縮放 8 倍(位元組大小)。
val_offset(N)
N 是一個帶符號的位元組偏移量。此暫存器的先前值是位置描述 L 的記憶體位元組位址。其中 L 是當前 CFA(請參閱 2.5.4 DWARF 運算式)的位置描述,該位置描述已更新位元偏移量 N 縮放 8 倍(位元組大小)。
如果 CFA 位置描述不是記憶體位元組位址位置描述,或者如果暫存器大小與目標架構預設位址空間中的位址大小不符,則 DWARF 格式不正確。
由於 CFA 位置描述需要為記憶體位元組位址位置描述,因此 val_offset(N) 的值也將是記憶體位元組位址位置描述,因為它是將 CFA 位置描述偏移 N 個位元組。此外,val_offset(N) 的值將是目標架構預設位址空間中的記憶體位元組位址。
注意:DWARF 是否應允許位址大小與暫存器大小不同?要求它們具有相同的位元大小可以避免任何轉換問題,因為暫存器的位元內容只是被解釋為位址的值。
GDB 具有每個暫存器的鉤子,允許以暫存器為基礎進行目標特定的轉換。對於較小的暫存器,它預設為截斷較大的暫存器,並實際從下一個暫存器讀取位元組(或對於最後一個暫存器讀取超出範圍)。沒有 GDB 測試會讀取超出範圍的暫存器(除非是非法的手寫組合語言測試)。
register(R)
此暫存器已儲存在另一個編號為 R 的暫存器中。
此暫存器的先前值是使用當前框架和當前程式位置的呼叫框架資訊針對暫存器 R 獲得的位置描述。
如果此暫存器的大小與暫存器 R 的大小不符,或者如果呼叫框架資訊中存在循環依賴,則 DWARF 格式不正確。
注意:這是否也應允許 R 大於此暫存器?如果是,值是否儲存在低位位元中,而額外的高位位元中儲存的內容是否未定義?
expression(E)
此暫存器的先前值位於通過評估 DWARF 運算式 E(請參閱 2.5.4 DWARF 運算式)產生的位置描述。
E 在當前上下文中評估,但結果類型為位置描述、編譯單元未指定、物件未指定,以及包含當前 CFA(請參閱 2.5.4 DWARF 運算式)的位置描述的初始堆疊除外。
val_expression(E)
此暫存器的先前值位於從通過評估 DWARF 運算式 E(請參閱 2.5.4 DWARF 運算式)產生的值建立的隱含位置描述。
E 在當前上下文中評估,但結果類型為值、編譯單元未指定、物件未指定,以及包含當前 CFA(請參閱 2.5.4 DWARF 運算式)的位置描述的初始堆疊除外。
如果產生的值類型大小與暫存器大小不符,則 DWARF 格式不正確。
注意:這具有有限的實用性,因為 DWARF 運算式 E 只能產生最大為通用類型大小的值。這是由於不允許在 CFI 運算式中指定類型的任何運算。這使得它對於大於通用類型的暫存器不可用。但是,expression(E) 可用於建立任何大小的隱含位置描述。
architectural
此規則在本規範之外由擴充器定義。
通用資訊條目 (CIE) 保存許多框架描述條目 (FDE) 之間共享的資訊。每個非空的 .debug_frame
區段中至少有一個 CIE。一個 CIE 依序包含以下欄位
length
(初始長度)一個常數,表示 CIE 結構的位元組數,不包括 length 欄位本身。length 欄位的大小加上 length 的值必須是
address_size
欄位中指定的位址大小的整數倍數。CIE_id
(4 或 8 個位元組,請參閱 7.4 32 位元和 64 位元 DWARF 格式)一個常數,用於區分 CIE 和 FDE。
在 32 位元 DWARF 格式中,CIE 標頭中的 CIE id 值為 0xffffffff;在 64 位元 DWARF 格式中,值為 0xffffffffffffffff。
version
(ubyte)版本號碼。此號碼特定於呼叫框架資訊,並且獨立於 DWARF 版本號碼。
CIE 版本號碼的值為 4。
注意:這是否會增加到 5 以反映這些擴充功能的變更?
augmentation
(UTF-8 字元序列)一個以 null 結尾的 UTF-8 字串,用於識別此 CIE 或使用它的 FDE 的擴充功能。如果讀取器遇到意外的擴充功能字串,則只能讀取以下欄位
CIE:length、CIE_id、version、augmentation
FDE:length、CIE_pointer、initial_location、address_range
如果沒有擴充功能,則此值為零位元組。
擴充功能字串允許使用者指示 CIE 或 FDE 中存在額外的供應商和目標架構特定資訊,這些資訊是虛擬回溯堆疊框架所需的。例如,這可能是關於動態分配的資料的資訊,這些資料需要在從例程退出時釋放。
由於
.debug_frame
區段獨立於任何.debug_info
區段都很有用,因此擴充功能字串始終使用 UTF-8 編碼。address_size
(ubyte)此 CIE 和任何使用它的 FDE 中的目標位址大小(以位元組為單位)。如果此框架存在編譯單元,則其位址大小必須與此處的位址大小相符。
segment_selector_size
(ubyte)此 CIE 和任何使用它的 FDE 中的區段選擇器大小(以位元組為單位)。
code_alignment_factor
(無符號 LEB128)一個常數,從所有 advance location 指令中分解出來(請參閱 6.4.2.1 列建立指令)。結果值為
(operand * code_alignment_factor)
。data_alignment_factor
(帶符號 LEB128)一個常數,從某些 offset 指令中分解出來(請參閱 6.4.2.2 CFA 定義指令 和 6.4.2.3 暫存器規則指令)。結果值為
(operand * data_alignment_factor)
。return_address_register
(無符號 LEB128)一個無符號 LEB128 常數,指示規則表中的哪一列表示子程式的返回位址。請注意,此列可能不對應於實際的機器暫存器。
返回位址暫存器的值用於確定呼叫者框架的程式位置。最頂層框架的程式位置是當前執行緒的目標架構程式計數器值。
initial_instructions
(ubyte 陣列)一系列規則,經解釋後用於建立表中每列的初始設定。
在解釋初始指令之前,所有列的預設規則是未定義規則。但是,ABI 撰寫機構或編譯系統撰寫機構可以為任何或所有列指定備用預設值。
padding
(ubyte 陣列)足夠的
DW_CFA_nop
指令,使此條目的大小與上面的 length 值相符。
一個 FDE 依序包含以下欄位
length
(初始長度)一個常數,表示此子程式的標頭和指令流的位元組數,不包括 length 欄位本身。length 欄位的大小加上 length 的值必須是位址大小的整數倍數。
CIE_pointer
(4 或 8 個位元組,請參閱 7.4 32 位元和 64 位元 DWARF 格式).debug_frame
區段中的常數偏移量,表示與此 FDE 相關聯的 CIE。initial_location
(區段選擇器和目標位址)與此表條目關聯的第一個位置的位址。如果此 FDE 的 CIE 的 segment_selector_size 欄位為非零,則初始位置前面會加上給定長度的區段選擇器。
address_range
(目標位址)此條目描述的程式指令的位元組數。
instructions
(ubyte 陣列)一系列表定義指令,這些指令在 6.4.2 呼叫框架指令 中描述。
padding
(ubyte 陣列)足夠的
DW_CFA_nop
指令,使此條目的大小與上面的 length 值相符。
A.6.4.2 呼叫框架指令¶
某些呼叫框架指令具有編碼為 DWARF 運算式 E 的運算元(請參閱 2.5.4 DWARF 運算式)。可以在 E 中使用的 DWARF 運算具有以下限制
DW_OP_addrx
、DW_OP_call2
、DW_OP_call4
、DW_OP_call_ref
、DW_OP_const_type
、DW_OP_constx
、DW_OP_convert
、DW_OP_deref_type
、DW_OP_fbreg
、DW_OP_implicit_pointer
、DW_OP_regval_type
、DW_OP_reinterpret
和DW_OP_xderef_type
運算不允許,因為呼叫框架資訊不得依賴其他偵錯區段。不允許
DW_OP_push_object_address
,因為沒有物件上下文來提供要推送的值。不允許
DW_OP_call_frame_cfa
和DW_OP_entry_value
,因為它們的使用會形成循環。
這些限制適用於的呼叫框架指令包括 DW_CFA_def_cfa_expression
、DW_CFA_expression
和 DW_CFA_val_expression
。
A.6.4.2.1 列建立指令¶
注意:這些指令與 DWARF 版本 5 第 6.4.2.1 節中的指令相同。
A.6.4.2.2 CFA 定義指令¶
DW_CFA_def_cfa
DW_CFA_def_cfa
指令採用兩個無符號 LEB128 運算元,分別表示暫存器編號 R 和(非因子化的)位元組位移 B。必要操作是將當前 CFA 規則定義為等效於評估 DWARF 運算式DW_OP_bregx R, B
作為位置描述的結果。DW_CFA_def_cfa_sf
DW_CFA_def_cfa_sf
指令採用兩個運算元:一個無符號 LEB128 值,表示暫存器編號 R,以及一個帶符號 LEB128 因子化的位元組位移 B。必要操作是將當前 CFA 規則定義為等效於評估 DWARF 運算式DW_OP_bregx R, B * data_alignment_factor
作為位置描述的結果。此操作與
DW_CFA_def_cfa
相同,除了第二個運算元是帶符號且因子化的之外。DW_CFA_def_cfa_register
DW_CFA_def_cfa_register
指令接受一個無號 LEB128 運算元,代表暫存器編號 R。必要動作是將目前的 CFA 規則定義為等同於評估 DWARF 運算式DW_OP_bregx R, B
作為位置描述的結果。B 是舊的 CFA 位元組位移。如果子程式沒有目前的 CFA 規則,或規則是由
DW_CFA_def_cfa_expression
指令所定義,則 DWARF 格式不正確。DW_CFA_def_cfa_offset
DW_CFA_def_cfa_offset
指令接受一個無號 LEB128 運算元,代表(非因數分解的)位元組位移 B。必要動作是將目前的 CFA 規則定義為等同於評估 DWARF 運算式DW_OP_bregx R, B
作為位置描述的結果。R 是舊的 CFA 暫存器編號。如果子程式沒有目前的 CFA 規則,或規則是由
DW_CFA_def_cfa_expression
指令所定義,則 DWARF 格式不正確。DW_CFA_def_cfa_offset_sf
DW_CFA_def_cfa_offset_sf
指令接受一個有號 LEB128 運算元,代表因數分解的位元組位移 B。必要動作是將目前的 CFA 規則定義為等同於評估 DWARF 運算式DW_OP_bregx R, B * data_alignment_factor
作為位置描述的結果。R 是舊的 CFA 暫存器編號。如果子程式沒有目前的 CFA 規則,或規則是由
DW_CFA_def_cfa_expression
指令所定義,則 DWARF 格式不正確。此動作與
DW_CFA_def_cfa_offset
相同,除了運算元為有號且為因數分解。DW_CFA_def_cfa_expression
DW_CFA_def_cfa_expression
指令接受一個以DW_FORM_exprloc
值編碼的單一運算元,代表 DWARF 運算式 E。必要動作是將目前的 CFA 規則定義為等同於評估 E 與目前上下文的結果,但結果種類為位置描述、編譯單元未指定、物件未指定,以及空的初始堆疊。關於可用於 E 中的 DWARF 運算式運算的限制,請參閱6.4.2 呼叫框架指令。
如果評估 E 的結果不是記憶體位元組位址位置描述,則 DWARF 格式不正確。
A.6.4.2.3 暫存器規則指令¶
DW_CFA_undefined
DW_CFA_undefined
指令接受一個無號 LEB128 運算元,代表暫存器編號 R。必要動作是將 R 指定的暫存器規則設定為undefined
。DW_CFA_same_value
DW_CFA_same_value
指令接受一個無號 LEB128 運算元,代表暫存器編號 R。必要動作是將 R 指定的暫存器規則設定為same value
。DW_CFA_offset
DW_CFA_offset
指令接受兩個運算元:暫存器編號 R(以運算碼編碼)和一個無號 LEB128 常數,代表因數分解的位移 B。必要動作是將 R 指定的暫存器規則變更為 offset(B * data_alignment_factor) 規則。注意:此指令似乎應命名為
DW_CFA_offset_uf
,因為位移是無號因數分解的。DW_CFA_offset_extended
DW_CFA_offset_extended
指令接受兩個無號 LEB128 運算元,分別代表暫存器編號 R 和因數分解的位移 B。此指令與DW_CFA_offset
相同,除了暫存器運算元的編碼和大小。注意:此指令似乎應命名為
DW_CFA_offset_extended_uf
,因為位移是無號因數分解的。DW_CFA_offset_extended_sf
DW_CFA_offset_extended_sf
指令接受兩個運算元:一個無號 LEB128 值,代表暫存器編號 R,以及一個有號 LEB128 因數分解的位移 B。此指令與DW_CFA_offset_extended
相同,除了 B 是有號的。DW_CFA_val_offset
DW_CFA_val_offset
指令接受兩個無號 LEB128 運算元,分別代表暫存器編號 R 和因數分解的位移 B。必要動作是將 R 指示的暫存器規則變更為 val_offset(B * data_alignment_factor) 規則。注意:此指令似乎應命名為
DW_CFA_val_offset_uf
,因為位移是無號因數分解的。DW_CFA_val_offset_sf
DW_CFA_val_offset_sf
指令接受兩個運算元:一個無號 LEB128 值,代表暫存器編號 R,以及一個有號 LEB128 因數分解的位移 B。此指令與DW_CFA_val_offset
相同,除了 B 是有號的。DW_CFA_register
DW_CFA_register
指令接受兩個無號 LEB128 運算元,分別代表暫存器編號 R1 和 R2。必要動作是將 R1 指定的暫存器規則設定為 register(R2) 規則。DW_CFA_expression
DW_CFA_expression
指令接受兩個運算元:一個無號 LEB128 值,代表暫存器編號 R,以及一個DW_FORM_block
值,代表 DWARF 運算式 E。必要動作是將 R 指定的暫存器規則變更為 expression(E) 規則。也就是說,E 計算可以檢索暫存器值的位置描述。
關於可用於 E 中的 DWARF 運算式運算的限制,請參閱6.4.2 呼叫框架指令。
DW_CFA_val_expression
DW_CFA_val_expression
指令接受兩個運算元:一個無號 LEB128 值,代表暫存器編號 R,以及一個DW_FORM_block
值,代表 DWARF 運算式 E。必要動作是將 R 指定的暫存器規則變更為 val_expression(E) 規則。也就是說,E 計算暫存器 R 的值。
關於可用於 E 中的 DWARF 運算式運算的限制,請參閱6.4.2 呼叫框架指令。
如果評估 E 的結果不是具有與暫存器大小相符之基本類型大小的值,則 DWARF 格式不正確。
DW_CFA_restore
DW_CFA_restore
指令接受一個運算元(以運算碼編碼),代表暫存器編號 R。必要動作是將 R 指定的暫存器規則變更為 CIE 中initial_instructions
指定的規則。DW_CFA_restore_extended
DW_CFA_restore_extended
指令接受一個無號 LEB128 運算元,代表暫存器編號 R。此指令與DW_CFA_restore
相同,除了暫存器運算元的編碼和大小。
A.6.4.2.4 列狀態指令¶
注意:這些指令與 DWARF 第 5 版 6.4.2.4 節中的指令相同。
A.6.4.2.5 填充指令¶
注意:這些指令與 DWARF 第 5 版 6.4.2.5 節中的指令相同。
A.6.4.3 呼叫框架指令用法¶
注意:與 DWARF 第 5 版 6.4.3 節中的內容相同。
A.6.4.4 呼叫框架呼叫位址¶
注意:與 DWARF 第 5 版 6.4.4 節中的內容相同。
A.7 資料表示法¶
注意:本節提供對現有偵錯資訊項目屬性的變更。這些變更將納入 DWARF 第 5 版第 7 章的對應章節中。
A.7.4 32 位元和 64 位元 DWARF 格式¶
注意:本節擴充了 DWARF 第 5 版 7.4 節項目 3 的表格。
Form Role
------------------------ --------------------------------------
DW_OP_implicit_pointer offset in `.debug_info`
A.7.5 偵錯資訊的格式¶
A.7.5.5 類別和格式¶
注意:與 DWARF 第 5 版 7.5.5 節中的內容相同。
A.7.7 DWARF 運算式¶
注意:重新命名 DWARF 第 5 版 7.7 節,以反映位置描述統一到 DWARF 運算式中。
A.7.7.1 運算式¶
注意:重新命名 DWARF 第 5 版 7.7.1 節並刪除 7.7.2 節,以反映位置描述統一到 DWARF 運算式中。
A.7.7.3 位置列表達式¶
注意:重新命名 DWARF 第 5 版 7.7.3 節,以反映位置列表是 DWARF 運算式的一種。
B. 更多資訊¶
以下參考資料提供關於此延伸的額外資訊。
提供 DWARF 標準的參考資料。
此延伸的格式化版本可在 LLVM 網站上取得。它包含許多圖表,有助於說明文字描述,尤其是 DWARF 運算式評估的範例。
Linux Plumbers Conference 2021 上關於此延伸的簡報投影片和影片已提供。
LLVM 編譯器延伸包含動機範例中提到的運算。它還涵蓋異質裝置所需的其他延伸。
用於最佳化 SIMT/SIMD (GPU) 偵錯的 DWARF 延伸 - Linux Plumbers Conference 2021