我的書講了“動態資料段”和“全域資料段”。在下面的 arm 代碼中,字串“Hello World!”在哪里?得救了,又是如何得救的?每個字母是一個位元組嗎?如果是這樣,它如何知道從哪里開始和結束?
.text
.global main
main:
push {lr}
ldr r0, =string
bl printf
mov r0, $0
pop {lr}
bx lr
.data
string: .asciz "Hello World!\n"
uj5u.com熱心網友回復:
聽起來你應該買一本更好的書!該程式不正確,因為它在堆疊未對齊時呼叫了 printf 函式。ARM 平臺上使用的所有主要 ABI 都要求堆疊在呼叫函式時是 8 位元組對齊的。
要回答您的問題,如果您用 C 語言撰寫程式,則取決于您的編譯器將字串放在哪里,盡管有一些既定的約定。因為你的程式是用匯編撰寫的,所以你必須告訴它放在哪里。在這里,.data指令將字串放在該.data部分中。這可能就是你那本狡猾的書所說的“全球資料段”。如果我不得不猜測,我會認為它使用術語“動態資料段”來指代堆,它實際上并不是輸出程式中的一個段,而是通過malloc.
uj5u.com熱心網友回復:
選擇這些東西的不是編譯器,而是程式員最終選擇這些東西的去向。如果您選擇為您的平臺使用諸如 gnu 工具之類的預構建包。對于 gnu,C 庫以及引導程式和聯結器腳本都密切相關,并且該聯結器腳本定義了事物所在的地址空間。
您可以看到 .asciz,它表示 ASCII,您可以輕松搜索并查看這些字符是如何以二進制表示的。
是的,未對齊的堆疊不符合當前的 ARM ABI,但此代碼仍將匯編。和其他人一樣驚訝的是,$0 有效,而不是 #0,這進一步證明了匯編語言特定于工具而不是目標。
我洗掉了 printf 以使這個示例變得簡單,因為它并不重要。
.text
.global main
main:
push {lr}
ldr r0, =string
@bl printf
mov r0, $0
pop {lr}
bx lr
.data
string: .asciz "Hello World!\n"
組裝和拆卸
Disassembly of section .text:
00000000 <main>:
0: e52de004 push {lr} ; (str lr, [sp, #-4]!)
4: e59f0008 ldr r0, [pc, #8] ; 14 <main 0x14>
8: e3a00000 mov r0, #0
c: e49de004 pop {lr} ; (ldr lr, [sp], #4)
10: e12fff1e bx lr
14: 00000000 andeq r0, r0, r0
Disassembly of section .data:
00000000 <string>:
0: 6c6c6548 cfstr64vs mvdx6, [ip], #-288 ; 0xfffffee0
4: 6f57206f svcvs 0x0057206f
8: 21646c72 smccs 18114 ; 0x46c2
c: Address 0x000000000000000c is out of bounds.
我使用了反匯編程式,因此它試圖將 ASCII 資料作為指令進行反匯編,您可以查看位元組并將其與網路上的內容進行比較。
這是未鏈接的,因此這些部分還沒有基地址,因此對于物件它們為零。您可以看到偽語言 ldr r0, =string 變成了附近單詞的 pc 相對負載,因為匯編器在匯編時不知道該值。我們可以把它和這樣簡單的東西聯系起來
MEMORY
{
one : ORIGIN = 0x00001000, LENGTH = 0x1000
two : ORIGIN = 0x00002000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > one
.data : { *(.data*) } > two
}
給予
Disassembly of section .text:
00001000 <main>:
1000: e52de004 push {lr} ; (str lr, [sp, #-4]!)
1004: e59f0008 ldr r0, [pc, #8] ; 1014 <main 0x14>
1008: e3a00000 mov r0, #0
100c: e49de004 pop {lr} ; (ldr lr, [sp], #4)
1010: e12fff1e bx lr
1014: 00002000 andeq r2, r0, r0
Disassembly of section .data:
00002000 <string>:
2000: 6c6c6548 cfstr64vs mvdx6, [ip], #-288 ; 0xfffffee0
2004: 6f57206f svcvs 0x0057206f
2008: 21646c72 smccs 18114 ; 0x46c2
200c: Address 0x000000000000200c is out of bounds.
所以你可以看到作為程式員我選擇了這些東西去哪里,你也可以看到在代碼中字串的地址已經被聯結器填充了。
顯然,這不是我們可以期望運行的可執行檔案,你有你需要的引導代碼和許多其他東西。
地址空間是特定于目標的,所以當我們程式員控制事物的去向時,作業系統對事物的去向有規則,如果 .data 和 .bss 由作業系統設定,或者我們必須在引導程式中進行設定等等。當然,如果您選擇使用 C 庫,它與作業系統密切相關,因為大多數呼叫都需要系統呼叫,并且系統呼叫對作業系統(和版本)和目標(處理器)都非常具體/建筑學)。因此,引導程式、C 庫和鏈接描述檔案是不可分割的,您不能混搭并期望取得很大的成功。如果您的工具鏈安裝了 C 庫并與之關聯,那么如果您為同一臺計算機/作業系統/處理器選擇不同的工具鏈。然后不假定每個鏈接描述檔案將使用確切的記憶體位置。因為他們可以從作業系統規則中自由選擇應用程式的地址空間。(同樣,顯然,匯編語言不應該在同一系統上從一個工具鏈移植到另一個工具鏈,因此您可能需要進行修改或嘗試使用 int 5; int main(void) { return(0); } 來查看聯結器的作用。
字串的二進制格式,很明顯,您指定了它。事情發生在哪里,聯結器根據一些必須符合目標的規則將物件鏈接在一起,無論是作業系統還是微控制器地址空間等。
它如何知道從哪里開始和結束,我們在上面討論了開始的主題。最后,您正在呼叫一個 C 函式并將其傳遞給一個 C 字串,這樣就涵蓋了這一點。您還在代碼中指定了字串的終止,因此您幾乎已經知道結尾是如何定義的。
uj5u.com熱心網友回復:
指令告訴程式部分去哪里,然后實作為這些部分分配一個起始地址。例如,.text 表示后面是代碼/指令,.data 表示后面是資料。現在,實作(在硬體上)可能因機器而異。例如,.text 可以從地址 0x00000000 開始,.data 可以從 0x00010000 開始。這取決于!
你問題的第二部分,關于它如何知道它在哪里結束。在裝配中,您的作業水平非常低。是的,每個字符都是一個位元組(8 位)。機器不知道字串在哪里結束,對機器來說,記憶體中的一切都是 0 和 1。但是,通常字串以空字符結尾。所以會有一種機制來列印字符,直到達到空值,然后停止。printf 函式列印字符直到達到空值,這就是它知道字串結束的方式。
請注意,在您的代碼段中:
.data
字串:.asciz "Hello World!\n"
它使用 .asciz 指令,該指令自動在字串末尾插入空字符。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/519513.html
標籤:部件手臂
上一篇:在gdb中轉換標簽型別
