2012-01-30 113 views
0

所以這對於所有熟悉程序集的人來說似乎都是一個非常簡單的問題,但是我希望有人能向我解釋下面兩段代碼之間的區別是什麼,假設結果是分段錯誤,另一個沒有,但(對我來說)他們看起來應該是邏輯上相同的。在gcc內聯彙編中加載寄存器? (簡單嗎?)

正常工作:

char *src1; int esi_out, eax; 
__asm__ 
    __volatile__(
    "lodsb\n\t;" 
    : "=&S" (esi_out), "=&a" (eax) 
    : "0" (src1) 
); 
printf("src1 %c @ %p, esi_out: %x, eax: %x\n", *src1, src1, esi_out, eax); 

和打印:

src1 w @ 0x7fffce186959, esi_out: ce18695a, eax: ce186977 

所以我的理解是,該代碼應該加載SRC1的值(這是一個地址)ESI,複製值成EAX,將ESI中的地址增加1個字節,然後在退出時將這些值輸出到本地C變量esi_out和eax中。 src1和esi_out看起來是正確的,但eax好像沒有問題。這裏發生了什麼?

代碼的第二位是我們看到的是我不能完全開始應付段錯誤:

__asm__ 
    __volatile__(
     "movl %%ebx, %%esi\n\t;" 
     //"lodsb\n\t;"                             
     : "=&S" (esi_out), "=&b" (ebx), "=&a" (eax) 
     : "1" (src1) 
); 
printf("src1 %c @ %p, esi_out: %x, eax: %x, ebx: %x\n", 
         *src1, src1, esi_out, eax, ebx); 

隨着註釋掉LODSB命令,它會產生:

src1 w @ 0x7ffff093b959, esi_out: f093b959, eax: f093b959, ebx: f093b959 

並且lodsb命令未被註釋掉,它會發生段錯誤。按照我的思路,直接加載ESI值(如上面的第一種情況),並將其加載到EBX中,然後將它移動到ESI中應該是等效的,不是嗎?

我錯過了什麼?爲什麼寫入EAX的值會顯示出來?我直接將相應的程序寫入程序集,並使用gdb逐步完成,並且工作正常。

任何有識之士將不勝感激。

+1

這確實是一個關於使用64位指針的問題,但只將低32位移入寄存器。謝謝大家。 – fromClouds 2012-01-30 22:17:57

回答

3

從printf中%p輸出的外觀來看,你編譯的是64位,但你的asm代碼假設爲32位。嘗試

__asm__ 
    __volatile__(
     "movl %%rbx, %%rsi\n\t;" 
     "lodsb\n\t;"  
     : "=&S" (esi_out), "=&b" (ebx), "=&a" (eax) 
     : "1" (src1) 
); 
printf("src1 %c @ %p, esi_out: %x, eax: %x, ebx: %x\n", 
         *src1, src1, esi_out, eax, ebx); 

你也應該申報esi_outebx爲指針型(void*char*)或uintptr_t

發生了什麼事是LODSB使用RSI在64位模式下它的源地址,但你只把指針值轉換成RSI的低32位,因此不包含一個有效的地址,故該段錯誤。 Slagh說,只有寄存器(al)的低8位被lodsb修改。您應該屏蔽其他位(eax & 0xff),清除rax(xor%rax,%rax)或聲明eax爲char

如果您對此感到驚訝,您還應該找到一個關於x86_64彙編的資源。

1

您的第一個問題 - lodsb正在修改AL,但不是EAX的其餘部分。 EAX值ce186977中的最後一個字節是0x77,它是小寫'w'的十六進制數。

我很遺憾不熟悉GCC彙編語法 - 當您運行代碼並跨越movl時,哪個寄存器是目的地?對我來說,EBX正在寫ESI。你的代碼之前在EBX中有什麼?

+0

謝謝,Slagh。 EBX被初始化爲src1的值。 gcc中的inline asm指令的格式是__asm __(。data:outputs:inputs(:clobbered)),輸出和輸入控制進出asm部分的內容。在這種情況下,輸出是從ESI(=&S)讀取的esi_out,從EBX(=&b)讀取的ebx和從EAX讀取的eax(= &a);,輸入是src1讀入EBX,其中「1」表示參數$ 1,編譯器將從$ 0開始分配給所有輸入和輸出,在這種情況下:{$ 0 = esi_out,1 = ebx等} – fromClouds 2012-01-30 20:46:17

1

你有一些錯誤:

您是在64位架構,int是不是大到足以容納一個指針。 esi_out變量應該是64位寬,或者更好的是使用ptrdiff_t。你應該命名爲rsi_out ;-)

將在64位模式lodsb指令implictly referres到rsi而不是esi。在movl %%ebx, %%esi之前的指令只設置了rsi的下半部分,上半部分被暗中清除。將其更改爲movq %%rbx, %%rsi

+0

'ptrdiff_t'不是在這裏使用的正確類型,它是針對兩個指針之間的差異和不需要足夠寬以存儲整個指針 – 2012-01-30 21:43:40