2013-02-24 75 views
0

我有一個寫入64位x86程序集(gcc和GAS的AT & T語法)的函數,它執行一些SSE2操作。我通過反彙編使用gdb檢查了結果並查看了寄存器值,所以我知道它產生了正確的結果。在retq指令之後,我得到了segementation錯誤。由於我是彙編新手(並且從未接受過任何類),我猜測我沒有正確處理函數/主程序接口。該函數需要2個指針和一個int,並且需要返回一個浮點數。 這是我如何處理的投入在我的彙編函數/輸出:函數終止時的Segfault

float foo(float *x,float *y,unsigned int s) 
{ 
    __asm__ __volatile__(
    "movl -0x14(%%rbp),%%ecx \n\t" //ecx = s 
    "movq -0x8(%%rbp),%%rax \n\t" //rax -> x 
    "movq -0x10(%%rbp),%%rdx \n\t" //rdx -> y 
    "subq $4,%%rsp \n\t"    //function result 
    #sse2 operations that end up with the answer in xmm4... 
    "movss %%xmm4,(%%rsp) \n\t"  //store result 
    "flds (%%rsp) \n\t"    //load function result 
    "addq $4,%%rsp \n\t"    //adjust stack 
    "ret \n\t" 
    : 
    :"g"(s) 
    :"%ecx","%rax","%rdx" 
    ); 
} 

而這裏似乎導致段錯誤(這是指令RET之後在拆卸)行:

0x00007fffffffe0d0 in ??() 
=> 0x00007fffffffe0d0: 00 00 add %al,(%rax) 

我不知道爲什麼它在執行我的函數後將rax的低位值加回到rax中,但它似乎讓事情崩潰。我是否被允許在我的彙編函數中使用rax,即使它是通用目的,而且我聲明它會被破壞?

我不確定你是否需要看到這部分,但這是gcc希望如何處理該功能;我已經提供了調用我的函數線的拆卸:

#asm dealing with function inputs 
    callq 0x400520 <foo> 
    movss %xmm0,-0x48(%rbp) 
    mov -0x48(%rbp),%eax 
    mov %eax,-0x34(%rbp) 

這讓我想起我的第二個問題,爲什麼它在任意的XMM0移動價值兩個地方?如果我的函數最終在xmm0中產生了結果,或者這是否意味着我應該避免使用xmm0?我很困惑,並會感謝任何幫助。在此先感謝任何花時間閱讀我的noob帖子:)

回答

7

您的問題是內聯彙編不會取代函數。你的函數編譯成這樣:

_foo: 
push %rbp    ; function prologue 
mov %rsp,%rbp 
mov %rdi,-0x8(%rbp) 
mov %rsi,-0x10(%rbp) 
mov %edx,-0x14(%rbp) 
mov -0x14(%rbp),%eax 
mov %eax,-0x1c(%rbp) 

mov -0x14(%rbp),%ecx ; your code 
mov -0x8(%rbp),%rax 
mov -0x10(%rbp),%rdx 
sub $0x4,%rsp 
movss %xmm4,(%rsp) 
flds (%rsp) 
add $0x4,%rsp 
retq      ; your return 

movss -0x18(%rbp),%xmm0 ; function epilogue 
pop %rbp 
retq      ; gcc's return 

retq彈出堆棧的值,並跳轉到它。如果一切順利,這是一個值callqgcc生成了一個函數序言(上面的前兩條指令),包括push %rbp。所以當你的retq運行時,它彈出rbp(一個指向堆棧的指針)並跳轉到它。這可能會導致分段錯誤,因爲堆棧不可執行(也可能是因爲%rax是無效指針,如果由於某種原因,堆棧可執行)。它碰巧指向的堆棧中的值是00 00(這在內存中顯示很多,不出所料),並巧妙地將其拆分爲add %al,(%rax)

現在,我是SSE的新手,我只使用了GCC內聯程序集很少,所以我不確定這是否是一個可行的解決方案。你真的不應該看堆棧或返回,因爲不同的編譯器將有不同的函數序幕的代碼運行時堆棧上的參數的相對位置。

試着這麼做:

#include <stdio.h> 

float foo(float *x,float *y,unsigned int s) 
{ 
    float result; 

    __asm__ __volatile__(
    "movss (%%rax),%%xmm4 \n\t"  // xmm4 = *x 
    "movss (%%rdx),%%xmm5 \n\t"  // xmm5 = *y 
    "addss %%xmm5,%%xmm4 \n\t"  // xmm4 += xmm5 

    "movss %%xmm4,(%%rbx) \n\t"  // result = xmm4 
    : 
    :"c"(s), "a"(x), "d"(y), "b"(&result) // ecx = s, eax = x, edx = y, ebx = &result 
    :"memory", "cc" 
    ); 

    return result; 
} 

int main() { 
    float x = 1.0, y = 2.0; 
    printf("%f", foo(&x, &y, 99)); 
    return 0; 
} 

所有堆棧分配,參數處理並返回在C.做它還傳遞了一個指針,用於存儲float結果。

這將生成以下組裝,這大致就是你要找的人:

_foo: 
push %rbp    ; prologue 
mov %rsp,%rbp 
push %rbx 

lea -0xc(%rbp),%rbx ; set up registers 
mov %edx,%ecx 
mov %rdi,%rax 
mov %rsi,%rdx 

movss (%rax),%xmm4  ; your code 
movss (%rdx),%xmm5 
addss %xmm5,%xmm4 
movss %xmm4,(%rbx) 

movss -0xc(%rbp),%xmm0 ; retrieve result to xmm0 (the return register) 

pop %rbx    ; epilogue 
pop %rbp 
retq 

另一種選擇總是把它寫在一個彙編文件,並鏈接與C代碼之後。

我希望這有些幫助,但如果它沒有完全回答您的問題,我很抱歉。

編輯:已更新的代碼,以實際爲我運行的東西。

+0

我剛打印到一個.s文件,我認爲你是對的。讓我試試你的建議,看看它是否有效! – 2013-02-24 07:34:52

+0

那麼,到目前爲止,我已經從我的函數中省略了ret,它不再崩潰。 – 2013-02-24 07:38:48

+0

我應該提到,我認爲你使用的是錯誤的調用約定。你從堆棧中讀取參數,而不是從寄存器中讀出。在Linux/BSD/OS X上,它們分別位於'rdi','rsi'和'rdx',Windows使用'rcx','rdx'和'r8'。 [維基百科上的x86-64調用約定](https://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions) – Dougall 2013-02-24 07:42:59