C Bindings help? Trying to port sunvox library

Hello all,

Been working on trying to get C Bindings for sunvox, and I’m looking for a little guidance on how to fix an issue.

I installed sunvox.so into /usr/lib, and also installed the header, sunvox.h, into /usr/include. I proved I can link it by compiling one of the sample programs included, I noticed it is both a C++ file and a C file, so I used gcc to force C compiliation using gcc -x c sunvox.c -o sunvox -lm -ldl -lsunvox . This works, and it plays the sounds and everything just fine.

I then ran the crystal_lib tool on the header to generate a binding, sunshine.cr. When trying to crystal run ./src/sunshine.cr I get this:

ian@gaming-computer:~/Documents/crystal/sunshine$ crystal run ./src/sunshine.cr 
_main.o: In function `__crystal_main':
/home/ian/Documents/crystal/sunshine/src/sunshine.cr:146: undefined reference to `sv_load_dll'
collect2: error: ld returned 1 exit status
Error: execution of command failed with code: 1: `cc "${@}" -o '/home/ian/.cache/crystal/crystal-run-sunshine.tmp'  -rdynamic  -lsunvox -lpcre /usr/bin/../lib/crystal/lib/libgc.a -lpthread /usr/sha
re/crystal/src/ext/libcrystal.a -levent -lrt -ldl -L/usr/bin/../lib/crystal/lib -L/usr/lib -L/usr/local/lib`

Why doesn’t crystal see sunvox.so? What am I doing wrong?

I do not use this feature of crystal much so I imagine other people will be able to help more.

I noticed in your example you are using gcc. Have you tried compiling with llvm?
I don’t think that should make that big of a difference but it might.

Also, As of 0.28.0 there is an enviroment varible you can set called CRYSTAL_LIBRARY_PATH that you can use to point to the .so files. Crystal will still try to find .so files but will use this env variable first. I would look into it if you are having trouble with crystal finding libraries.
Here is a link to the RFC

Thanks for the suggestion! I tried it and it worked. This is what I did to test it.

$ clang -emit-llvm -c sunvox.c -o sunvox.bc
$ clang -o sunvox sunvox.bc -lm -lsunvox -ldl
$ ./sunvox
Desired audio buffer size: 2048 frames
ALSA: pulse
ALSA HW Default rate: 44100
ALSA HW Rate: 44100 frames
ALSA HW Buffer size: 4096 frames
ALSA HW Period size: 227
ALSA HW Periods: 0
ALSA SW Avail min: 227
ALSA SW Start threshold: 1
ALSA SW Stop threshold: 4096
SunVox lib version: 1.9.4
Loading SunVox song from file...
Loaded.
Project name: Tiny Tune. by NightRadio 2008
Number of modules: 8
module 0: Output; x=739 y=479
module 1: Generator; x=168 y=170
module 2: Echo; x=433 y=370
module 3: Generator2; x=299 y=849
module 4: Generator3; x=157 y=626
module 5: Generator4; x=646 y=860
module 6: Kicker; x=530 y=180
module 7: Filter; x=478 y=735
^CSOUND: sundog_sound_deinit() begin
SOUND: sundog_sound_deinit() end
Max memory used: 1668623
ian@gaming-computer:~/Documents/crystal/sunshine/src$ 

This might be a problem with crystal_lib. Have you tried using Link in a crystal src file.

Here is an example from the docs page.
https://crystal-lang.org/reference/syntax_and_semantics/c_bindings/lib.html

I’m pretty sure I linked it correctly. I also tried @[Link("sunvox")] but that didn’t work either.

have you opened an issue on lib_crystal?

I would but, I’m not sure it’s a lib_crystal issue, the issue is with it not finding the function, sv_load_dll. Which crystal_lib did define

https://asciinema.org/a/GhzKzsx9VHNWtHSiiyEVDe6K8

Is sunvox.so compiled for C or C++? If it’s for C++ I think it won’t work.

What you can do is do @[Link(ldflags: "sunvox.o")] if you can compile the library to an .o file (you can make that path relative to the file where the lib is by doing @[Link(ldflags: "#{__DIR__}/sunvox.o")])

I think it’s compiles for both. When I use the gcc flag -x c to force compilation in C, it works fine. Is .o the only supported type? I don’t have access to the source code of the sunvox library, it’s closed source and all we have is the .so file. Is there a way to check to see if the so is C++ or C only? I would imagine it’s both, since the header is written in a way to denote both would work.

Although I am a noob when it comes to some of the C++/C stuff, not really my forte too much.

I don’t know how to do it, but maybe there’s a command or way to list all exported symbols in a. so file. Then you can see if the function/symbol you want to use is there. C++ uses strange names when it compiles names (this is called mangling) and that might be the cause Crystal can’t find the function you want.

1 Like

That’s a good idea, I’ll definitely look into a tool to do it. I did test compile using -x c which forces the compiler to use C, from my understanding.

Hey so I used radare2 to get a better idea of what’s going on, and I found out something interesting, the sv_load_dll function isn’t even defined in my symbols, depsite the fact that it’s defined in my header file. I tried another function and it did work, so I guess problem solved. There isn’t really much in the way of documentation on this thing, the only thing I have to go by is the comments in the .h file.

Upon closer inspection I noticed the sv_load_dll function has some ifdef for Windows, but I don’t see why the function wouldn’t be defined in the unix version.

int sv_load_dll( void )
{
#ifdef WIN
    return sv_load_dll2( TEXT(LIBNAME) );
#else
    return sv_load_dll2( LIBNAME );
#endif
    return -1111;
}