簡體   English   中英

鏈接器如何知道extern函數的定義在哪里?

[英]How does the linker know where is the definition of an extern function?

我讀了幾篇帖子並得出結論,extern告訴編譯器“這個函數存在,但它的代碼在其他地方。不要驚慌。” 但鏈接器如何知道函數的定義位置。

我的案例: - 我正在研究Keil uvision 4.有一個頭文件grlib.h,主函數在grlib_demo.c(它包含grlib.h)。 現在,有一個函數GrCircleDraw()在Circle.c中定義並在grlib_demo.c中調用,還有一個語句

extern void GrCircleDraw(所有參數);

在grlib.h中。 我的查詢是鏈接器如何知道GrCircleDraw()的定義,因為Circle.c不包含在grlib.h和grlib_demo.c中

注意: - 文件grlib.h和Circle.c位於同一文件夾中。 代碼運行成功。

簡單的答案是“編譯器不需要知道,但鏈接器必須能夠找到它”。 通過多個.o文件或通過庫,鏈接器必須能夠找到GrCircleDraw函數的單個定義。

編譯器只將extern函數的名稱放入.obj文件中。 編譯器不需要了解更多信息。

當您開始鏈接時,作為開發人員,您有責任將所有必需的目標文件和庫文件提供給鏈接器。 鏈接器會將所有這些功能安排到二進制文件中。 如果您沒有指定正確的庫或.obj文件,那么鏈接將會因unresolved blah-blah失敗。

通常隱式包含默認庫。 這使事情復雜化並產生幻想。 您始終可以指定不需要任何隱式庫並明確包含所有內容。 不幸的是,每個系統都以自己的方式完成。

當你編譯的.o文件ELF格式 ,你對很多事情.o文件如:

  • 包含代碼的.text部分;
  • 包含全局變量的.data.rodata.rss部分;
  • .symtab包含.o中的符號列表(函數,全局變量等)(以及它們在文件中的位置)以及.o文件使用的符號;
  • 諸如.rela.text部分,它們是重定位列表 - 這些是鏈接編輯器(和/或動態鏈接器)必須進行的修改,以便將程序的不同部分鏈接在一起。

在來電方面

讓我們編譯一個簡單的C文件:

extern void GrCircleDraw(int x);

int foo()
{
  GrCircleDraw(42);
  return 3;
}

int bla()
{
  return 2;
}

有:

gcc -o test.o test.c -c

(我正在使用我的系統的本機編譯器,但在交叉編譯到ARM時它將工作完全相同)。

您可以使用以下命令查看.o文件的內容:

readelf -a test.o

在符號表中,您將找到:

Symbol table '.symtab' contains 10 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
[...]
     8: 0000000000000000    21 FUNC    GLOBAL DEFAULT    1 foo
     9: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND GrCircleDraw
    10: 0000000000000015    11 FUNC    GLOBAL DEFAULT    1 bla

我們的foo函數有一個符號, bla有一個符號。 值字段在.text部分中給出它們的位置。

使用的符號GrCircleDraw有一個符號:它未定義,因為此函數未在此.o文件中定義,但仍可在其他位置找到。

.text部分( .rela.text )的重定位表中,您會發現:

Relocation section '.rela.text' at offset 0x260 contains 1 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
00000000000a  000900000002 R_X86_64_PC32     0000000000000000 GrCircleDraw - 4

該地址在foo :鏈接編輯器將使用GrCircleDraw函數的地址修補此地址處的指令。

在被叫方

現在讓我們自己編譯一個GrCircleDraw的實現:

void GrCircleDraw(int x)
{

}

我們來看看它的符號表:

Symbol table '.symtab' contains 9 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
[...]
     8: 0000000000000000     9 FUNC    GLOBAL DEFAULT    1 GrCircleDraw

它有一個GrCircleDraw條目,在其.text部分中定義它的位置。

將它們連接在一起

因此,當鏈接編輯器將兩個文件組合在一起時,它知道:

  • 哪個函數定義在哪個.o文件及其位置;
  • 在調用者的代碼中,它必須使用被調用者的地址進行更新。

鏈接通常以這種方式發生:迭代命令行並給出每個參數

  1. 如果是目標文件則直接使用,
  2. 在需要的范圍內使用(=實現到目前為止尚未解決的所有參考文獻)。

最后,必須完成每個參考才能成功鏈接。 鏈接器命令行中給出的行的順序很重要。

暫無
暫無

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

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