簡體   English   中英

當我們編譯包含'main'而沒有鏈接的源代碼時,為什么我們不能運行它?

[英]When we compile a source code that contains a 'main' without linking, why can't we run it?

我正在學習編譯過程,我知道鏈接主要用於鏈接包含'main'函數的二進制文件和包含在我們的主函數中使用的其他輔助函數的其他二進制文件。

但是,當我嘗試使用代碼運行目標文件時:

int main() {
    return 0;
}

在Ubuntu上使用gcc中的-c命令編譯,我嘗試運行它並得到錯誤:

“bash:./ source.o:無法執行二進制文件:exec格式錯誤”

閱讀Levine的連接器和裝載器

了解ELF

嘗試使用gcc -v編譯(你會看到使用的實際程序是什么: cc1將C代碼編譯成某個匯編程序,然后as匯編到某個目標文件中,將ldcollect2鏈接 )。 還可以使用gcc -S -fverbose-asm -O查看生成的匯編程序文件。 請注意, gcc知道(並特別編譯) main函數。 並且可執行文件的起點由一些crt0等提供(它不是 main但是一些_start例程在匯編程序中編碼,它調用你的main ....)。

對象文件可執行文件不同 可執行文件包含諸如crt0C標准庫之類的東西,或者某種方式它作為共享對象 動態鏈接 (並且需要將source.o -compiled從source.c的空main鏈接到可執行文件中,因為這樣) )。

在Linux上,使用objdump(1)readelf(1) (在一些現有的二進制文件上,以及在你的source.o目標文件中)

另請參閱elf(5)execve(2)ld-linux(8)Linux匯編howtosyscalls(2)高級Linux編程操作系統:三個簡單的部分 ,以及(了解libc.so )Drepper的如何寫共享圖書館龍書 ......

(你需要閱讀整本書才能理解細節;我給了一些參考資料)

另請參閱 Common Lisp和SBCL 它的編譯器有一個非常不同的模型(與C完全不同)。

你沒有引導程序。 你是在這個雞和蛋的問題。

代碼(用於該函數)就在那里,但有一些假設,首先你需要一個堆棧。 例如,根據架構,您的返回地址可能位於該堆棧上。 返回值可以在該堆棧上。 C語言本身並不直接在語言中提供,因此總是至少需要一些程序集或一些其他語言才能“引導”你的函數。 例如在ARM中用於gnu:

bs.s

.globl _start
_start:
    mov sp,#0x8000
    bl main
    b .

so.c

int main ( void )
{
    return(0);
}

對於ARM,函數完成后,鏈接器不需要修改指令。 但是沒有定義地址空間,無論是指定還是反匯編程序都假定零作為此對象的地址,但它是一個對象而不是可加載的二進制文件。

00000000 <main>:
   0:   e3a00000    mov r0, #0
   4:   e12fff1e    bx  lr

現在,如果我們添加引導程序並鏈接到某個地址,我們將獲得一個真實的,可執行的程序

00008000 <_start>:
    8000:   e3a0d902    mov sp, #32768  ; 0x8000
    8004:   eb000000    bl  800c <main>
    8008:   eafffffe    b   8008 <_start+0x8>

0000800c <main>:
    800c:   e3a00000    mov r0, #0
    8010:   e12fff1e    bx  lr

它並不意味着無法創建操作系統,也無法使用編譯器對象輸出以這種方式加載函數。 但這就是單詞鏈,工具鏈的原因。 編譯器使用匯編語言,匯編程序匯編匯編語言,結合其他必要的對象(引導程序加編譯器庫和C庫等),鏈接器為所有內容定義地址空間,並根據需要修改代碼/數據以解析外部。 獲得最終結果的序列或事件鏈。

即使像exit這樣的最基本的命令也不是直接在語言中,需要鏈接。

http://en.cppreference.com/w/c/program/exit

暫無
暫無

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

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