先说结论,
同一个函数,在不同的程序模块里面的引用地址是不相同的。
描述一下这个问题:
假设有一个函数mydllfunc(),定义在mydllcall1.dll文件里面,当然在mydllcall1模块里面也可以引用它;同时这个函数也在另一个模块mydllcall2.dll以及主程序myapp.exe里面引用。
引用方式大致如此:
在mydllcall1.dll里面:
__declspec(dllexport) void * mydllfunc(int a)
{
...
return X;
}
void caller1(...)
{
...
mydllfunc(1);
...
}
在mydllcall2.dll里面:
extern void * mydllfunc(int a);
void caller2(...)
{
...
mydllfunc(1);
...
}
在主程序myapp.exe里面:
extern void * mydllfunc(int a);
void caller0(...)
{
...
mydllfunc(1);
...
}
这里mydllfunc在mydllcall1里面被定义,然后在三个地方被调用(mydllcall1, mydllcall2, 和myapp),如果我们在调用的时候把函数mydllfunc的地址打出来,会发现他们是三个不同的值。
printf("Address of mydllfunc is 0x%p\n", mydllfunc);
mydllfunc(X);
然而同样的代码逻辑,在linux环境下三个地方打印出来的mydllfunc的地址是一样的。这个应该是和windows DLL与linux so的不同的动态库实现原理不一致导致的。
我们踩到这个坑,是因为有一个代码逻辑要比较两个函数是否相同,使用的是函数地址的比较(个人认为通过比较地址决定是否相同的逻辑,不是一个好的实现),导致不一致了,而在linux平台下面是相同的,所以记录一下这个问题。
附上完整的测试代码:
mydllcall1.c
#include <stdio.h>
#if defined(WIN32) || defined(_WIN32)
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
DLLEXPORT void * mydllfunc(int a)
{
// printf("==== in mydllfunc : mydllfunc=0x%p, a=%d\n", mydllfunc, a);
return mydllfunc;
}
DLLEXPORT void * mydllcall1(int a)
{
printf("==== in mydllcall1: mydllfunc=0x%p\n", mydllfunc);
mydllfunc(1);
return mydllcall1;
}
mydllcall2.c
#include <stdio.h>
#if defined(WIN32) || defined(_WIN32)
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
extern void * mydllfunc(int a);
DLLEXPORT void * mydllcall2(int a)
{
printf("==== in mydllcall2: mydllfunc=0x%p\n", mydllfunc);
mydllfunc(2);
return mydllcall2;
}
myapp.c
#include <stdio.h>
#define SLEEPSEC 60
extern void * mydllfunc(int a);
extern void * mydllcall1(int a);
extern void * mydllcall2(int a);
void * (*func_ptr)(int);
int main(int argc, char * argv[]) {
printf("==== in main : mydllfunc=0x%p\n", mydllfunc);
mydllfunc(0);
mydllcall1(1);
mydllcall2(2);
#if defined(WIN32) || defined(_WIN32)
Sleep(SLEEPSEC * 1000);
#else
sleep(SLEEPSEC);
#endif
return 0;
}
makefile.unix
TARGET=myapp mydllcall1.so mydllcall2.so
all: $(TARGET)
myapp: myapp.c mydllcall1.so mydllcall2.so
$(CC) -o $@ $^
mydllcall1.so: mydllcall1.c
$(CC) -fPIC -shared -o $@ $<
mydllcall2.so: mydllcall2.c
$(CC) -fPIC -shared -o $@ $<
.PHONY: run clean
run:
PATH=.:$PATH LD_LIBRARY_PATH=.:${LD_LIBRARY_PATH} myapp
clean:
rm -f $(TARGET)
makefile.nt
TARGET=myapp.exe mydllcall1.dll mydllcall2.dll
all: $(TARGET)
myapp.exe: myapp.c mydllcall1.dll mydllcall2.dll
cl $*.c mydllcall1.lib mydllcall2.lib
mydllcall1.dll: mydllcall1.c
cl /LD $**
mydllcall2.dll: mydllcall2.c mydllcall1.lib
cl /LD $**
.PHONY: run clean
run:
myapp.exe
clean:
if exist *.obj del *.obj
if exist *.dll del *.dll
if exist *.exe del *.exe
if exist *.exp del *.exp
if exist *.lib del *.lib
运行结果:
windows:
==== in main : mydllfunc=0x00007FF6A75D1107
==== in mydllcall1: mydllfunc=0x00007FF8B51D1000
==== in mydllcall2: mydllfunc=0x00007FF8B30C10F7
linux:
==== in main : mydllfunc=0x0x400630
==== in mydllcall1: mydllfunc=0x0x400630
==== in mydllcall2: mydllfunc=0x0x400630
可见在windows下打印出三个不同的值,分别位于三个模块的地址空间内,而在linux下打印出三个相同的值。