2010-01-22 70 views
1

將這更多地視爲僞代碼而不是任何東西。如果您覺得應該包含一些宏觀或其他元素,請告訴我。cedecl調用約定 - 編譯後的asm指令導致崩潰

我很喜歡裝配。我在大學時在一臺PIC處理器上編程,但從此沒有任何事。

這裏的問題(分段故障)是「編譯功能入口,設置堆棧幀」之後的第一條指令。或「推送%ebp」。下面是我發現了關於這兩個指令:

http://unixwiz.net/techtips/win32-callconv-asm.html

保存和更新%EBP:

現在,我們在新的功能,我們需要一個新的本地棧幀指以%ebp表示,所以這是通過保存當前的%ebp(屬於前一個函數的框架)並將其指向棧頂來完成的。

push ebp 
    mov ebp, esp // ebp « esp 

一旦%ebp的已經被改變,它現在可以直接參考函數的參數爲​​8(%EBP),12(%EBP)。請注意,0(%ebp)是舊的基本指針,4(%ebp)是舊的指令指針。

這是代碼。這是來自我正在開發的項目的JIT編譯器。我比其他任何東西都更喜歡學習體驗。

IL_CORE_COMPILE(avs_x86_compiler_compile) 
{ 
    X86GlobalData *gd = X86_GLOBALDATA(ctx); 
    ILInstruction *insn; 

    avs_debug(print("X86: Compiling started...")); 
    /* Initialize X86 Assembler opcode context */ 
    x86_context_init(&gd->ctx, 4096, 1024*1024); 

    /* Compile function entrance, setup stack frame*/ 
    x86_emit1(&gd->ctx, pushl, ebp); 
    x86_emit2(&gd->ctx, movl, esp, ebp); 

    /* Setup floating point rounding mode to integer truncation */ 
    x86_emit2(&gd->ctx, subl, imm(8), esp); 
    x86_emit1(&gd->ctx, fstcw, disp(0, esp)); 
    x86_emit2(&gd->ctx, movl, disp(0, esp), eax); 
    x86_emit2(&gd->ctx, orl, imm(0xc00), eax); 
    x86_emit2(&gd->ctx, movl, eax, disp(4, esp)); 
    x86_emit1(&gd->ctx, fldcw, disp(4, esp)); 

    for (insn=avs_il_tree_base(tree); insn != NULL; insn = insn->next) { 
     avs_debug(print("X86: Compiling instruction: %p", insn)); 
     compile_opcode(gd, obj, insn); 
    } 

    /* Restore floating point rounding mode */ 
    x86_emit1(&gd->ctx, fldcw, disp(0, esp)); 
    x86_emit2(&gd->ctx, addl, imm(8), esp); 

    /* Cleanup stack frame */ 
    x86_emit0(&gd->ctx, emms); 
    x86_emit0(&gd->ctx, leave); 
    x86_emit0(&gd->ctx, ret); 

    /* Link machine */ 
    obj->run = (AvsRunnableExecuteCall) gd->ctx.buf; 
    return 0; 
} 

當obj->運行被調用,這就是所謂的所有與obj作爲其唯一參數:

obj->run(obj); 

如果有幫助,這裏是整個函數調用的指令。這基本上是一項分配操作:foo=3*0.2;。 FOO指向一個浮在C.

0x8067990: push %ebp 
0x8067991: mov %esp,%ebp 
0x8067993: sub $0x8,%esp 
0x8067999: fnstcw (%esp) 
0x806799c: mov (%esp),%eax 
0x806799f: or  $0xc00,%eax 
0x80679a4: mov %eax,0x4(%esp) 
0x80679a8: fldcw 0x4(%esp) 
0x80679ac: flds 0x806793c 
0x80679b2: fsts 0x805f014 
0x80679b8: fstps 0x8067954 
0x80679be: fldcw (%esp) 
0x80679c1: add $0x8,%esp 
0x80679c7: emms 
0x80679c9: leave 
0x80679ca: ret  

編輯:如我上面所說的,在本功能的第一條指令,%EBP是無效的。這也是導致分段錯誤的指令。那是因爲它是無效的,還是我在尋找別的東西?

編輯:劃痕。我繼續輸入edp而不是ebp。這裏是ebp和esp的值。

(gdb) print $esp 
$1 = (void *) 0xbffff14c 
(gdb) print $ebp 
$3 = (void *) 0xbffff168 

編輯:上面的這些值是錯誤的。我應該用「X」命令,如下圖所示:

(gdb) x/x $ebp 
0xbffff168: 0xbffff188 
(gdb) x/x $esp 
0xbffff14c: 0x0804e481 

下面是從別人對此郵件列表上的回覆。任何人都在意照亮他的意思嗎?如何檢查堆棧設置?

一個直接的問題,我看到的是, 堆棧指針未對齊。 這是32位代碼,英特爾 手冊指出堆棧應該是 ,它們在32位地址處對齊。也就是說, esp 中的最低有效位應該是0,4,8或c。

我也注意到ebp和 esp的值相差很遠。通常, 它們包含相似的值 - 地址在堆棧中的某處。

我會看看如何在這個程序中設置堆棧 。

他回覆了對上述評論的更正。經過進一步的投入後,他無法看到任何問題。

另一編輯:有人回覆說,代碼頁可能沒有標記爲可執行文件。我怎樣才能確保它被標記爲這樣?

+0

在另一個說明中,您是使用Intel還是AT&T彙編語法?您似乎在使用AT&T(基於'mov%esp,%ebp'行); unixwiz的例子使用英特爾。 – outis 2010-01-22 03:28:11

+0

'$ esp'的價值是什麼? – outis 2010-01-22 03:29:22

+0

@outis:是的,我在這裏使用AT&T。 – Scott 2010-01-22 03:29:51

回答

1

如果push %ebp導致段錯誤,那麼您的堆棧指針不指向有效堆棧。控制如何達到這一點?你在什麼平臺上,運行環境有什麼奇怪的地方?在函數入口處,%esp應指向堆棧上調用者的返回地址。可以?

除此之外,整個功能很奇怪。您可以自行設置fp控制字中的舍入位,然後不要執行任何受舍入影響的操作。所有的函數都是複製一些數據,但是當你也可以使用整數寄存器時,使用浮點寄存器來完成它。然後是虛假的emms,這是你在使用MMX指令後需要的,而不是在做x87計算之後。

編輯請參閱Scott的(原始提問者)回答崩潰的實際原因。

+0

通過調用函數指針來達到控制。函數指針的內容是我在問題中提供的函數中正在編譯的內容。 obj-> run是函數指針。我在Linux上。我不確定如何檢查第一段中的最後一個問題。至於舍入位,該函數中的循環用於編譯許多不同的場景,所以我想這是出於某種目的。我沒有寫這篇文章,所以我對此並不積極。另外,AvsNumber(JIT環境中的C類型變量)是一個浮點數,所以我認爲使用浮點寄存器是合適的。 – Scott 2010-01-22 04:52:59

1

該問題與代碼無關。向鏈接器添加-z execstack可解決問題。

+0

啊,這很有道理。 – 2010-01-22 15:07:02