当在 header 中声明的函数是自定义定义时,C++ 中的未定义符号

Undefined symbol in C++ when function which is declared in header is custom defined

我正在为 android 构建一个库。 eglGetNativeClientBufferANDROID 函数可用于 android .so 高于 26 但我希望库支持 API 23 的所有版本。所以我将我的文件与 libEGL.so 版本链接23 并在运行时动态加载 .so 并从 .so 文件中获取函数(这会在实际的 phone 中获取 .so,它可以是更高版本)。

文件A.cpp:

...
#define EGL_EGLEXT_PROTOTYPES
#include <EGL/egl.h>
#include <EGL/eglext.h> // This provides declaration for eglGetNativeClientBufferANDROID
#include <GLES/gl.h>

...
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(hardwareBuffer);
...

文件B.cpp:

// defining my custom function for it
EGLClientBuffer eglGetNativeClientBufferANDROID(const struct AHardwareBuffer *buffer)
{
    return doSomething();
}

我正在链接 A.cpp 和 B.cpp 即两个 obj 文件都被链接以形成库。但是,我收到以下错误:

ld.lld: error: undefined symbol: eglGetNativeClientBufferANDROID

这没有意义,因为它已经明确定义。此方法适用于我从 libandroid.so 动态加载的其他几个 API。这让我更好奇地剥离符号,看看发生了什么。

我从 A.obj 看到:

U eglGetNativeClientBufferANDROID

这意味着eglGetNativeClientBufferANDROID必须要查找。

来自 B.obj:

0000000000000000 T _Z31eglGetNativeClientBufferANDROIDPK15AHardwareBuffer

很明显它已定义,但它正在寻找另一个 table 来获取符号(而不是来自 B.obj)。我比较好奇,我去掉了同一个库中的其他符号的样子:

对于 A.obj eglCreateImageKHR 的符号声明如下:

     U eglCreateImageKHR

它从 libEGL.so 中提取的是这样的:

0000000000002000 d _DYNAMIC
...
0000000000001018 T eglCreateImageKHR

显然 libEGL.so 没有从其符号中提供更多类型详细信息等。

我将 eglGetNativeClientBufferANDROID 更改为 eglGetNativeClientBufferANDROIDCustom,在 A.cpp 中声明它,然后一切开始正常工作。

我现在很困惑:

  1. 为什么 eglGetNativeClientBufferANDROID 没有被从 B.obj 接走?
  2. libEGL.so 中的 _DYNAMIC 是什么意思,为什么方法符号只是名称而没有其他类型信息? (我认为这是因为 egl 仅将函数动态加载到 fps 但我不是 100% 确定)。

此外,我不想将 eglGetNativeClientBufferANDROID 名称更改为其他名称来解决此问题,因为我以后可能会删除所有动态加载 infuture。

我通过在A.cpp中强行定义函数来解决它,比如:

EGLClientBuffer eglGetNativeClientBufferANDROID(const struct AHardwareBuffer *buffer)
{
    return doSomething();
}

并在 B.cpp 中定义 doSomething。

还有其他更好的方法吗?

Why eglGetNativeClientBufferANDROID is not being picked up from B.obj?

因为B中导出的方法与A中的声明不匹配

What _DYNAMIC means in libEGL.so and why are the method symbols just the names and no other type info are present? (I think it is because egl only loads the functions to fps dynamically but I'm not 100% sure).

因为 B 对象中的“类型信息”是 C++ 名称修改的结果(因为 C++ 允许函数重载,其中相同的函数名称用于不同的参数类型,因此实际的符号名称必须包含整个链接器找到正确变体的签名)。

如果您编写 C++ 并想要 C 链接,则必须使用 extern "C" 限定符:

extern "C" EGLClientBuffer eglGetNativeClientBufferANDROID(const struct AHardwareBuffer *buffer) {...}