簡體   English   中英

如何將C ++子例程鏈接到x86匯編程序?

[英]How do I link a C++ subroutine to an x86 assembly program?

我正在嘗試制作一個簡單的匯編程序,打印出“Hello!” 一次,等待一秒鍾,然后再打印出來。 由於睡眠功能在組裝時相對復雜,而且我不擅長它,所以我決定使用C ++來制作Sleep子程序。 這是C ++程序:

// Sleep.cpp
#include <thread>
#include <chrono>

void Sleep(int TimeMs) {
    std::this_thread::sleep_for(std::chrono::milliseconds(TimeMs));
}

然后我使用“gcc -S Sleep.cpp”將這個睡眠函數編譯成匯編程序,然后使用“gcc -c Sleep.s”將其編譯成目標文件。

我試圖從匯編中調用這個C ++子例程。 我聽說你通過將它們推入堆棧來為C ++子程序提供參數,這是我到目前為止的匯編代碼:

        global    _main
        extern    _puts
        extern    Sleep
        section   .text
_main:    
        push    rbp
        mov     rbp,    rsp
        sub     rsp,    32


        ;Prompt user:
        lea     rdi,    [rel prompt]        ; First argument is address of message
        call    _puts                       ; puts(message)

        push    1000 ; Wait 1 second (Sleep time is in milliseconds)
        call    Sleep

        lea     rdi,    [rel prompt] ; Print hello again
        call    _puts

        xor     rax,    rax                 ; Return 0
        leave
        ret

        section   .data

prompt:
    db      "Hello!", 0

這兩個文件都保存到桌面/程序。 我正在嘗試使用NASM和GCC編譯它,我的編譯器調用是:

nasm -f macho64 Program.asm && gcc Program.o Sleep.s -o Program && ./Program

但我得到錯誤:

"Sleep", referenced from:
      _main in Program.o
     (maybe you meant: __Z5Sleepi)
  "std::__1::this_thread::sleep_for(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l> > const&)", referenced from:
      void std::__1::this_thread::sleep_for<long long, std::__1::ratio<1l, 1000l> >(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000l> > const&) in Sleep-7749e0.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

將代碼更改為“extern __Z5Sleepi”並調用“__Z5Sleepi”而不是Sleep似乎無法解決問題。 (我得到了相同的錯誤信息,沒有“也許你的意思__Z5Sleepi”位。我也嘗試使用_Sleep而不是Sleep而沒有成功。)我做錯了什么? 如何正確使用和鏈接此C ++子例程與匯編程序? 到目前為止我使用的方法從頭開始做錯了嗎?

任何幫助都非常感激,瀏覽堆棧溢出,似乎有很多關於此的問題,但它們實際上都沒有進入鏈接過程。 (他們似乎在詢問是否將程序集與C ++相關聯,而不是將C ++與匯編相關聯。)我正在使用NASM和GCC進行編譯,而我的平台是Mac OSX。

正如傑斯特所指出的那樣,問題產生於兩件事。 一個是我需要更改Sleep.cpp程序以使用extern“C”,如下所示:

#include <thread>
#include <chrono>

extern "C" void Sleep(int TimeMS);
extern "C"
{
   void Sleep(int TimeMs) {
    std::this_thread::sleep_for(std::chrono::milliseconds(TimeMs));
   }
}

這可以防止編譯器“命名修改”該函數。 這樣做會將Sleep()的已編譯函數名從“__Z5Sleepi”更改為“_Sleep”,並減輕了我的鏈接器錯誤。

然后我將編譯器調用更改為與g++而不是gcc鏈接,以鏈接C ++標准庫,用於函數,如std::__1::this_thread::sleep_for ,以及C標准庫。

nasm -f macho64 Program.asm && g++ Program.o Sleep.o -o Program && ./Program

在此之后,編譯器告訴我我需要將extern Sleep更改為extern _Sleep並且call _Sleep而不是call Sleep ,因為OS X用前導_裝飾C符號名稱。

在完成所有這些之后,程序正確鏈接但產生了分段錯誤。 Jester指出這是因為x86-64調用約定不會在棧上傳遞整數/指針函數參數。 您可以像調用_printf或_puts一樣使用寄存器,因為這些庫函數也遵循相同的標准調用約定。

在x86-64 System V調用約定(用於OS X,Linux和Windows以外的所有內容)中, rdi是參數1

所以我把push 1000改為mov rdi, 1000

完成所有這些更改后,程序將正確編譯並完全按照它應該執行的操作:打印Hello !,等待1秒鍾,然后再次打印。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM