Home > other >  Undefined symbol in C when function which is declared in header is custom defined
Undefined symbol in C when function which is declared in header is custom defined

Time:10-24

I'm building a library for android. eglGetNativeClientBufferANDROID function is available for android .so above 26 but I want the library to support all the versions from API 23. So I'm linking my file with libEGL.so version 23 and dynamically loading the .so at runtime and getting the function from the .so file (this picks up the .so in the actual phone which can be of a later version).

File 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);
...

File B.cpp:

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

I'm linking A.cpp and B.cpp i.e both the obj files are linked to form the library. However, I get the following error:

ld.lld: error: undefined symbol: eglGetNativeClientBufferANDROID

which would not make sense because it has clearly been defined. This method worked for several other APIs which I dynamically loaded from libandroid.so. This made me more curious to strip down the symbols and see what is happening.

I see that from A.obj:

U eglGetNativeClientBufferANDROID

which means that eglGetNativeClientBufferANDROID has to be looked up.

From B.obj:

0000000000000000 T _Z31eglGetNativeClientBufferANDROIDPK15AHardwareBuffer

So clearly it is defined but it is looking on another table to get the symbols (and not from B.obj). I was more curios and I stripped down what the other symbols from the same library would look like:

For A.obj symbols for eglCreateImageKHR is declared like this:

     U eglCreateImageKHR

Which it picks up from libEGL.so which is like this:

0000000000002000 d _DYNAMIC
...
0000000000001018 T eglCreateImageKHR

Clearly libEGL.so does not provide more type details etc from its symbol.

I changed eglGetNativeClientBufferANDROID to eglGetNativeClientBufferANDROIDCustom, declared it in A.cpp and everything starts working fine.

I'm now very confused as to:

  1. Why eglGetNativeClientBufferANDROID is not being picked up from B.obj?
  2. 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).

Also, I did not want to change eglGetNativeClientBufferANDROID name to something else to solve this issue as I might later remove all the dynamic loading infuture.

I solved it by forcefully defining the function in A.cpp like:

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

and defining doSomething in B.cpp.

Is there any other better way for this?

CodePudding user response:

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

Because the exported method in B does not match the declaration in 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).

Because the "type infos" in your B object are the result of the C name mangling (as C allows function overloading where the same function name is used with different argument types, so the actual symbol name must contain the whole signature for the linker to find the right variant).

If you write C and want C linkage, you have to use the extern "C" qualifier:

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