Static linking of GraphicsMagick++ library

Hi

I’m trying to statically link an external library (GraphicsMagick++) into my C++ application (running on Rapsberry Pi with Raspberry Pi OS).

The project compiles, and the library works on my development Pi, but when I try to run it on another (clean installed) Pi it complains that it cannot find the library.

It seems to me that PIO is linking the library dynamically instead of static. When I run “ldd” on my executable it also shows the GraphicsMagic dependency:

pi@RPIZ2306:~/projects/ledboardpi/runtime $ ldd ./program
linux-vdso.so.1 (0x0000007fa3f36000)
libreadline.so.8 => /lib/aarch64-linux-gnu/libreadline.so.8 (0x0000007fa3d56000)
—> libGraphicsMagick+±Q16.so.12 => /lib/libGraphicsMagick+±Q16.so.12 (0x0000007fa3cd0000)
libstdc++.so.6 => /lib/aarch64-linux-gnu/libstdc++.so.6 (0x0000007fa3af8000)
libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (0x0000007fa3a4d000)
libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 (0x0000007fa3a29000)
libpthread.so.0 => /lib/aarch64-linux-gnu/libpthread.so.0 (0x0000007fa39f8000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000007fa3883000)
libtinfo.so.6 => /lib/aarch64-linux-gnu/libtinfo.so.6 (0x0000007fa3845000)
/lib/ld-linux-aarch64.so.1 (0x0000007fa3f06000)
—> libGraphicsMagick-Q16.so.3 => /lib/libGraphicsMagick-Q16.so.3 (0x0000007fa3579000)
libjbig.so.0 => /lib/aarch64-linux-gnu/libjbig.so.0 (0x0000007fa355c000)
libwebp.so.6 => /lib/aarch64-linux-gnu/libwebp.so.6 (0x0000007fa34fa000)
libwebpmux.so.3 => /lib/aarch64-linux-gnu/libwebpmux.so.3 (0x0000007fa34e1000)

The library itself is installed in “/home/pi/libs/GraphicsMagick-1.3.40”, the PlatformIO project is located in “/home/pi/projects/led”

This is (the relevant part of) my PlatformIO.ini file:

...
   build_flags = 
  -I../../libs/GraphicsMagick-1.3.40/Magick++/lib
  -I../../libs/GraphicsMagick-1.3.40/    
  -static -llibGraphicsMagick++              

How can I link this library into my executable to avoid dependencies on the target machine?

TIA!

Unfortunately, when you do

build_flags = -static

it only affects the compilation stage (.c/.cpp- > .o), not the linking stage (.o, .a.elf).

gcc -o .pio/build/native/src/main.o -c -static -DPLATFORMIO=60110 -Iinclude -Isrc src/main.c
gcc -o .pio/build/native/program .pio/build/native/src/main.o -L.pio/build/native

The resulting binary is still linked dynamically.

max@virtualbox:~/temp/static_ex$ ldd .pio/build/native/program 
	linux-vdso.so.1 (0x00007fffba9f7000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc5d3800000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fc5d3a89000)

Instead, you have to do this in your platformio.ini:

[env:native]
platform = native
extra_scripts = link_static.py

with link_static.py being a file at the same level as the platformio.ini with content

Import("env")
env.Append(LINKFLAGS=["-static"])

This results in

gcc -o .pio/build/native/src/main.o -c -DPLATFORMIO=60110 -Iinclude -Isrc src/main.c
gcc -o .pio/build/native/program -static .pio/build/native/src/main.o -L.pio/build/native

and as confirmed by ldd,

max@virtualbox:~/temp/static_ex$ ldd .pio/build/native/program 
	is not a dynamic executable

Thank you very much for the detailed explanation!

This indeed is a solution to instruct the linker to link the libraries statically.

However, now another question pops up: how can I enable static linking for selected libraries and keep dynamic linking for others?

At the core, that’s a GCC/Linker question. I think this answer would work there:

You should be able to give the required flags as an array ito LINKFLAGS like in the original example above. If LINKFLAGS doesn’t work with the needed order of the flags, you might be able to fall back to this method.

Actually, when I read that Stackoverflow anaswer again, it says that simply giving the full path to the .a file should link it statically. Meaning if you do a script like

Import("env")
env.Prepend(LIBS=[
   File("../../libs/GraphicsMagick-1.3.40/Magick++/lib/libGraphicsMagick++.a")
])

might work too. (File is class auto-defined in the SCons build system. The full path may be given instead of the relative path. Make sure to check the project tasks → Advanced → Verbose Build to see the full compiler invocations.)

Using the full path to the library works. Note: The full path! I tried with relative paths but then the linker returns an error.

Besides the GraphicsMagic++ lib, I also added the main Magick library, but then I get a bunch of “undefined references”, eventough the search path is correctly set.

/usr/bin/ld: /home/pi/libs/GraphicsMagick-1.3.40/magick/.libs/libGraphicsMagick.a(libGraphicsMagick_la-operator.o): in function `QuantumPowCB':
/home/pi/libs/GraphicsMagick-1.3.40/magick/operator.c:1575: undefined reference to `GOMP_critical_name_start'
/usr/bin/ld: /home/pi/libs/GraphicsMagick-1.3.40/magick/operator.c:1575: undefined reference to `GOMP_critical_name_end'
...

I guess I might have messed something up in my experimenting and building, rebuilding, installing & removing the libraries multiple times.
I’ll try again from a clean install and give an update then.

Thanks once again Max

This linking issue might arrive from a wrong linking order – try swapping the graphicsmagic++ and magick library positions in the array.