C++ Dylib in Swift Project - Undefined Symbols for Function Exposed in Dylib

C++ dylib in Swift project - undefined symbols for function exposed in dylib

In the C++ library, your function must be marked with extern "C"
for C linkage:

extern "C" __attribute__((visibility("default"))) int addNumber(int number) {
return 0;
}

so that the compiler does not mangle the exported name. Swift can only
call C functions, not C++ functions.

See also What is the effect of extern "C" in C++?.

ERROR ld: 9 duplicate symbols for architecture x86_64 - Making a dylib

It looks like that you link three version of a same lib, of course there are same function or variable names among them. Try only link one of them.

Since linker said:

duplicate symbol

"symbol" means function name or variable name, so "duplicate symbol" mean there are many same names in the files you are linking. By your error log those names look like function names, and those functions always appear in the other two .o file, so I made the conclusion that those three lib are the same one but different version.

dylib dynamic library calling a dylib : Undefined symbols for architecture i386

Without resorting to #define EXPORT __attribute__((visibility("default"))) or any -fvisibility=hidden

g++-5.2.0 -m32 -Wall -fpic -g -c ./tmp8bis_dylib.cpp
g++-5.2.0 -m32 -shared ./tmp8bis_dylib.o -o ./libtmp8bis_dylib.dylib
g++-5.2.0 -m32 -Wall -g -c ./tmp8bis.cpp
g++-5.2.0 -m32 -shared ./tmp8bis.o -o ./tmp8bis.dylib -L. -ltmp8bis_dylib

finally worked. I did not managed to succeed without -fpic, naming libtmp8bis_dylib.dylib and using -ltmp8bis_dylib.

Link dylib library

You need to pass libs to linker, not to the compiler:

OBJS = main.o besselJ.o
CC = c++
CFLAGS = -std=c++11 -stdlib=libc++
LIBS = -L/usr/lib -lcomplex_bessel
PROGRAM_NAME = test

all: $(PROGRAM_NAME)
$(PROGRAM_NAME): $(OBJS)
$(CC) $(OBJS) -o $@ $(LIBS)
main.o: main.cpp
$(CC) $(CFLAGS) -c $< -o $@
besselJ.o: besselJ.cpp
$(CC) $(CFLAGS) -c $< -o $@

Mac .dylib Linking Cannot Find Header

Transferring comments into an answer.

  • You have -Iinc in the first command line so the compiler can find the header.
  • You don’t have -Iinc in the second, and the compiler can’t find the header.
  • The fix is as simple and obvious as “add -Iinc to the second command line".

In general, do .dylib files require accompanying .h files?

All libraries require a header that declares to the compiler what facilities are available from the library. Some libraries provide (require) several headers — witness the main system C library and the standard C and POSIX headers.

The main difference between the standard or system libraries and their headers and the libraries and headers that you provide is that the compiler knows where to find the system libraries and headers automatically, whereas you have to tell it where to find yours, usually via the -L and -I options. If your library is installed in /usr/local/lib and the header in /usr/local/include, you probably won't have to tell your compiler to search there.

On macOS, is it possible to modify the symbol names of an already compiled dynamic library?

You could patch the symbols (simply searching for the string in a hex editor and changing some bytes should do the trick, so long as the overall length stays the same), but I think there is a more elegant solution.

Mach-Os record which symbol they want to import from which library, and by default run in a non-flat namespace, i.e. symbols from different libraries won't collide with each other at runtime.

As you may have observed though, they do collide at link-time. But messing with things at link-time is a lot easier than patching binaries.

I'm assuming both of your libraries have the same "install name" (if in doubt, check otool -l your.dylib | fgrep -A2 LC_ID_DYLIB). If this is the case, then you'll have to rename one of them. If your dylib's original install name is /usr/local/lib/libstuff.dylib, then rename one of them to /usr/local/lib/libstuff_alt.dylib and run this command on it:

install_name_tool -id /usr/local/lib/libstuff_alt.dylib /usr/local/lib/libstuff_alt.dylib

If your library was or needs to be signed, you'll now need to re-sign it:

codesign -f -s - /usr/local/lib/libstuff_alt.dylib

If you're curious about how install names work, see this answer of mine.

Once the two versions of the library have different names, let's do a setup that you can follow along. I've created the following C files:

a.c:

int f(void)
{
return 10;
}

int g(void)
{
return 11;
}

b.c:

int f(void)
{
return 20;
}

int g(void)
{
return 21;
}

And compiled them both to libraries:

cc -shared -o liba.dylib a.c -Wall -O3
cc -shared -o libb.dylib b.c -Wall -O3

Then I've created another file called t.c which uses the f() and g() functions:

#include <stdio.h>

extern int f(void);
extern int g(void);

int main(void)
{
printf("%d %d\n", f(), g());
return 0;
}

If you compile and link this against the two libraries, then it will currently import both symbols from whatever library you specify first:

% cc -o t t.c -Wall -O3 -L. -la -lb
% ./t
10 11
% cc -o t t.c -Wall -O3 -L. -lb -la
% ./t
20 21

So what we're gonna do is cheat a bit, using the "text-based stub files" that can be used for linking instead of the actual dylibs.

xcrun tapi stubify -o liba.tbd liba.dylib
xcrun tapi stubify -o libb.tbd libb.dylib

This creates the files liba.tbd and libb.tbd like so:

--- !tapi-tbd
tbd-version: 4
targets: [ arm64-macos ]
uuids:
- target: arm64-macos
value: 2AACA829-4039-3B2A-8751-2AB617189F29
flags: [ not_app_extension_safe ]
install-name: liba.dylib
current-version: 0
compatibility-version: 0
exports:
- targets: [ arm64-macos ]
symbols: [ _f, _g ]
...
--- !tapi-tbd
tbd-version: 4
targets: [ arm64-macos ]
uuids:
- target: arm64-macos
value: 02EE57B9-3074-3EE7-8B3C-EF2BDFA1D26F
flags: [ not_app_extension_safe ]
install-name: libb.dylib
current-version: 0
compatibility-version: 0
exports:
- targets: [ arm64-macos ]
symbols: [ _f, _g ]
...

At this point we can trivially delete the symbols we don't want from those files. In my example, I made sure that liba.tbd only has [ _f ] and libb.tbd only has [ _g ]. Once that's done, we can try again:

% cc -o t t.c -Wall -O3 -L. -la -lb
% ./t
10 21

And there you go.

The only caveat when doing this is that you need to make sure that the functions you're using don't have any kind of internal dependency on the library they're coming from, like global variables.



Related Topics



Leave a reply



Submit