2

I have an executable shared_main , a shared library libbar.so and a dynamic load shared library libfoo.so (load in shared_main via dlopen).

shared_main doesn't use any symbols from libbar.so but libfoo.so does.

So gcc -g -Wall -o shared_main shared_main.c libbar.so -ldl doesn't link libbar.so to shared_main. Checked via ldd shared_main.

How to let gcc force shared_main link libbar.so?

P.S. I know I can link libfoo.so with libbar.so. But I want to try if I can force shared_main to link libbar.so here.


shared_main.c

#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>

int main(){    
    void* libHandle = dlopen("./libfoo.so", RTLD_LAZY);
    if(libHandle == NULL){
        printf("dlopen:%s", dlerror());
        exit(1);
    }
    int(*xyz)(int);
    xyz = (int(*)(int)) dlsym(libHandle, "xyz");
    if(xyz == NULL){
        printf("dlsym:%s", dlerror());
        exit(1);
    }
    int b = xyz(3);
    printf("xyz(3): %d\n", b);

}

foo.c (libfoo.so)

void func_in_bar();
int xyz(int b){
    func_in_bar();
    return b + 10;
}

bar.c (libbar.so)

//mimic python share library runtime
#include <stdio.h>
void func_in_bar(){
    printf("I am a Function in bar\n");
}

void another_func_in_bar(){
    printf("I am another function in bar\n");
}

makefile

shared_main:
    gcc -g -Wall -o shared_main shared_main.c libbar.so -ldl
shared:
    gcc -g -Wall -fPIC -shared -o libfoo.so foo.c
    gcc -g -Wall -fPIC -shared -o libbar.so bar.c
Rick
  • 7,007
  • 2
  • 49
  • 79
  • You link with shared library like `-L -l`. Like `-L. -lbar`. Not `libbar.so` as argument. – KamilCuk Jul 09 '22 at 08:22
  • @KamilCuk But in my understanding, link with fullname `libbar.so` and `-L. -lbar` should be the same. I can link `libbar.so` in `shared_main` if I call `func_in_bar();` in it. Anyway, I am trying `-L. -lbar` with `-u func_in_bar` now. – Rick Jul 09 '22 at 08:26
  • Remove `-u`, it's not needed. Just link with the library. Note also that the `-l` option has to be _after_ the source file. Consider using a better build system, like CMake, Scons or Meson, it will be much easier to work with a tool that hides that complexity. There is also the `-l:` trick https://stackoverflow.com/questions/207069/how-to-link-using-gcc-without-l-nor-hardcoding-path-for-a-library-that-does-not. Also try changing argument order `gcc ... libbar.so shared_main.c` – KamilCuk Jul 09 '22 at 08:31
  • @KamilCuk Why remove `-u`? I need to link `libbar.so` to `shared_main`, giving the condition that `shared_main` doesn't use any symbols in `libbar.so`. `gcc -g -Wall -u func_in_bar -o shared_main shared_main.c -L. -lbar -ld` doesn't work either. – Rick Jul 09 '22 at 08:34

3 Answers3

4

You have an XY-problem, where X is: libfoo has unresolved symbols, but the linker doesn't warn about it

So use the -z defs option linkage-time, and when you get the linker error about the unresolved symbol add -lfoo to the linkage command.

That's still not enough, you will have to use a -L and a -Wl,-rpath option too. Here is a complete Makefile:

# Makefile

# LIBDIR should be the final place of the shared libraries
# such as /usr/local/lib or ~/libexec/myproject

LIBDIR  := ${PWD}
TARGETS := shared_main libbar.so libfoo.so

all: ${TARGETS}

clean:
    rm -f ${TARGETS} 2>/dev/null || true

shared_main: shared_main.c
    gcc -g -Wall -o shared_main shared_main.c -ldl

libbar.so: bar.c
    gcc -g -Wall -fPIC -shared -o libbar.so bar.c

libfoo.so: foo.c libbar.so
    gcc -g -Wall -fPIC -shared -z defs -o libfoo.so foo.c \
    -L${LIBDIR} -Wl,-rpath,${LIBDIR} -lbar

Edit: nonetheless, here is a hackish solution for the original question: use option -Wl,--no-as-needed

shared_main:
    gcc -g -Wall -o shared_main shared_main.c \
    -Wl,--no-as-needed -Wl,-rpath,${PWD} libbar.so -ldl
Lorinczy Zsigmond
  • 1,749
  • 1
  • 14
  • 21
  • `-Wl,--no-as-needed` is all I need and I am happy to know `-z defs`, though I must not use it when compiling `libfoo.so`. I don't want the linker to report as errors. `-Wl,-rpath` is not necessary for me , I use `LD_LIBRARY_PATH=./` for this small experiment. For production I would put everything into one of the system library search directories. – Rick Jul 09 '22 at 13:53
  • It's not a XY problem actually. Allow me to provide more background. Python interpreter can be statically built into `python3` or built with `python3` and `libpython.x.y.so`. For shared built python, python's C extension used to link with `libpython.x.y.so`. But this kind of extension can not be used directly by a statically built python (`libpython.x.y.so` not found), which doesn't make sense, because all symbols have already been built into the statically built python interpreter. So after python3.8 it removes all c extension's explictly linking of `libpython.x.y.so`. – Rick Jul 09 '22 at 13:54
  • So that's why I said I can't link `libfoo.so` with `libbar.so`. But in real life, `shared_main`(shared built python interpreter) would definitely use symbols from `libbar.so` (`libpython.x.y.so`, the runtime), so `-Wl,--no-as-needed` is not necessary. So I am just curious whether there's a way to force an executable to link a unused shared library. And thank you for your help again ! – Rick Jul 09 '22 at 14:00
  • That's true, a program that uses plugins often provides a shared library for the plugins, such as libperl.so, libpython.so, libapr.so This shared library is to be used when linking the main executable (perl, python, httpd) instead of statically linking its content: that would lead to duplicate symbols when a plugin is loaded. – Lorinczy Zsigmond Jul 09 '22 at 15:42
1

Everything works just fine for me, with unmodified files from OP.

$ make shared
gcc -g -Wall -fPIC -shared -o libfoo.so foo.c
gcc -g -Wall -fPIC -shared -o libbar.so bar.c

$ make shared_main
gcc -g -Wall -o shared_main shared_main.c libbar.so -ldl

$ LD_LIBRARY_PATH=. ldd shared_main
linux-vdso.so.1 (0x00007ffccb5f2000)
libbar.so => ./libbar.so (0x00007f78f6ce0000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f78f6cd1000)
libc.so.6 => /lib64/libc.so.6 (0x00007f78f6b06000)
/lib64/ld-linux-x86-64.so.2 (0x00007f78f6ce7000)

$ LD_LIBRARY_PATH=. ./shared_main 
I am a Function in bar
xyz(3): 13

I only needed to help the library loader out a bit using LD_LIBRARY_PATH.

$ gcc --version
gcc (GCC) 11.3.1 20220421 (Red Hat 11.3.1-2)
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ ld --version
GNU ld version 2.37-17.fc35
Copyright (C) 2021 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.
Cheatah
  • 1,825
  • 2
  • 13
  • 21
  • 2
    Depends on linker-version, whether `--as-needed` or `--no-as-needed` is the default setting. – Lorinczy Zsigmond Jul 09 '22 at 09:53
  • @LorinczyZsigmond Yes, I just found out `--no-as-needed` can directly solve my problem. Anyway, let me read your answer :P. Thank you very much – Rick Jul 09 '22 at 09:56
  • What is your linker version? I know `LD_LIBRARY_PATH` and that's how I run my experiment too. – Rick Jul 09 '22 at 14:02
  • @rick I added the versions. – Cheatah Jul 09 '22 at 17:08
  • Thanks. Interesting, I have `GNU ld (GNU Binutils for Ubuntu) 2.34` and `gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0`. But I guess `--as-needed` would be default setting for most verions. – Rick Jul 10 '22 at 02:41
0

The answer is -Wl,--no-as-needed.

For my example, the full command is:

gcc -g -Wall -o shared_main shared_main.c -Wl,--no-as-needed libbar.so -ldl

From https://man7.org/linux/man-pages/man1/ld.1.html :

 --as-needed
 --no-as-needed
           This option affects ELF DT_NEEDED tags for dynamic libraries
           mentioned on the command line after the --as-needed option.
           Normally the linker will add a DT_NEEDED tag for each dynamic
           library mentioned on the command line, regardless of whether
           the library is actually needed or not.  --as-needed causes a
           DT_NEEDED tag to only be emitted for a library that at that
           point in the link satisfies a non-weak undefined symbol
           reference from a regular object file or, if the library is
           not found in the DT_NEEDED lists of other needed libraries, a
           non-weak undefined symbol reference from another needed
           dynamic library.  Object files or libraries appearing on the
           command line after the library in question do not affect
           whether the library is seen as needed.  This is similar to
           the rules for extraction of object files from archives.
           --no-as-needed restores the default behaviour.
Rick
  • 7,007
  • 2
  • 49
  • 79