[英]How does the linker determine which function to link to?
下面的代碼是我所看到的問題的簡化版本; 基本上,外部函數testprint()
最終調用了test_xprintf.cpp
定義的printf()
,而不是標准的printf()
。
(是的,代碼看起來很奇怪,但是它只是用來表示問題,因此它本身並不一定有意義。)
為什么鏈接器鏈接到test_xprintf
定義的printf()
? 此預期行為或工具是否相關?
//
// test_xprintf.cpp
//
#include <iostream>
#include <stdio.h>
#include <stdarg.h>
#include "test_dbgprintf.h"
/**
*
* There are 3 files in total:
* - test_xprintf.cpp
* - test_dbgprintf.h
* - test_dbgprintf.cpp
*
* Create a static C lib from test_dbgprintf.c and link with test_xprintf.cpp
*
* gcc -Wall -g -c -o test_dbgprintf.o test_dbgprintf.c &&
* ar -rcs libtest_dbgprintf.a test_dbgprintf.o &&
* g++ -Wall test_xprintf.cpp -L. -ltest_dbgprintf -I.
*/
extern "C" int printf(const char *format, ...)
{
va_list ap;
va_start(ap, format);
vprintf(format, ap);
va_end(ap);
return -1;
}
int main()
{
// testprint() is a shell function which simply calls printf.
// If the printf function above is called, the return value will be -1.
int ret = testprint(4);
std::cout << "Ret value is " << ret << std::endl;
return ret;
}
//
// test_dbgprintf.h
//
#ifndef TEST_DBGPRINTF_H
#define TEST_DBGPRINTF_H
#if defined (__cplusplus)
extern "C" {
#endif
int testprint(int num);
#if defined (__cplusplus)
}
#endif
#endif
//
// test_dbgprintf.c
//
#include <stdio.h>
int testprint(int num)
{
// By right this should be calling the std printf but it is linked to the printf in test_printf.cpp instead.
return printf("This is called from testprint %d\n", num);
}
這是GNU鏈接器的已知行為。 解析符號時,它將僅檢測.o之間的多個定義。 如果.o文件中沒有定義,它只會求助於庫; 然后它將在第一個匹配項后停止搜索。
這是默認行為。 您可以使用--whole-archive
覆蓋它,盡管這可能會使生成的模塊膨脹。
好吧,我認為在閱讀了這個非常有用的博客之后,我對這種情況做出了合理的解釋。
它取決於鏈接順序。 這是我遺忘的內容,但查看了鏈接庫的方式:
g++ -Wall test_xprintf.cpp -L. -ltest_dbgprintf -I.
將其擴展為兩個步驟:
g++ -Wall -g -c -o test_xprintf.o test_xprintf.cpp
g++ -L. test_xprintf.o -ltest_dbgprintf -I.
我認為發生的是鏈接器:
test_xprintf
導出符號printf()
test_xprintf
,發現未定義的符號printf
printf()
並愉快地將它們鏈接在一起。 我相信libC最后被鏈接了,這解釋了為什么它沒有看到它。
根據解釋,我相信這種行為是可以預期的。
您額外的printf
符號(作為C函數可見)使鏈接器混亂。 很有可能,您還應該看到有關乘法定義符號的鏈接器警告。
在這種意義上,標准甚至不考慮多重定義的符號(更不用說翻譯單元之外的任何內容)。
典型的鏈接器將綁定第一個匹配的符號(在某種意義上為“第一個”),並對任何匹配的附加符號發出警告。
總結一下:此行為完全取決於鏈接程序的實現。
在某些編譯器中,std庫被視為具有弱鏈接。 您定義的符號始終具有很強的聯系,除非另有定義或具有某些屬性或類似屬性,但默認情況下它們被鏈接為“強”。
這意味着您在定義符號和標准符號之間出現的任何沖突符號都將解析為您提供的“強”符號。
當我遇到此問題(使用CodeWarrior的ColdFire編譯器)時,總是收到鏈接器警告:
Symbol _sprintf multiply defined in printf.c() and libc.a(printf.o )
Ignoring the definition in libc.a(printf.o )
可能您會看到類似的警告。
為什么有時在std lib中需要這種鏈接? 好吧,在嵌入式系統中,也許某些功能對於處理器而言無法執行,例如printf函數(不是printf本身,因為它只是一個包裝器,我認為它是vsprintf或類似的東西,但我不知道)記得哪一個很長(有時它已經消耗了我幾乎整個內存的1/4),所以我不得不提供自己的簡化版本。
我不知道這是標准的,還是依賴鏈接器的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.