]> git.ipfire.org Git - thirdparty/man-pages.git/commit
dlopen.3: Clarify the rules for symbol resolution in a dlopen'ed object
authorMichael Kerrisk <mtk.manpages@gmail.com>
Wed, 3 Jul 2019 07:45:55 +0000 (09:45 +0200)
committerMichael Kerrisk <mtk.manpages@gmail.com>
Wed, 3 Jul 2019 07:45:55 +0000 (09:45 +0200)
commit43898de48866c677269c8c1821e2ceb35bc865f6
tree7d65ffba192ff44d988b93e0d11eacd5884e64f1
parentba20328e27c16306efa63dfcfe1b440970615062
dlopen.3: Clarify the rules for symbol resolution in a dlopen'ed object

The existing text wrongly implied that symbol look up first
occurred in the object and then in main, and did not mention
whether dependencies of main where used for symbol resolution.

Verified by experiment:

$ cat prog.c
#define _GNU_SOURCE
#include <link.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void            /* A function defined in both main and lib_x1 */
prog_x1(void)
{
    printf("Called %s::%s\n", __FILE__, __func__);
}

/* The following function is forced into prog's dynamic symbol table
   because of the static link-time reference in lib_m1.so */

void            /* A function defined in both main and lib_y1 */
prog_y1_exp(void)
{
    printf("Called %s::%s\n", __FILE__, __func__);
}

/* The following function is not forced into prog's dynamic symbol table */

void            /* A function defined in both main and lib_y1 */
prog_y1_noexp(void)
{
    printf("Called %s::%s\n", __FILE__, __func__);
}

static int
callback(struct dl_phdr_info *info, size_t size, void *data)
{
    printf("\tName = %s\n", info->dlpi_name);

    return 0;
}

int
main(int argc, char *argv[])
{
    void *xHandle, *yHandle;
    void (*funcp)(void);
    char *err;

    xHandle = dlopen("./lib_x1.so", RTLD_NOW | RTLD_GLOBAL);
    if (xHandle == NULL) {
        fprintf(stderr, "dlopen: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    yHandle = dlopen("./lib_y1.so", RTLD_NOW | RTLD_GLOBAL);
    if (yHandle == NULL) {
        fprintf(stderr, "dlopen: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    /* Optionally display the link map() */

    if (argc > 1) {
        printf("Link map as shown from dl_iterate_phdr() callbacks:\n");
        dl_iterate_phdr(callback, NULL);
        printf("\n");
    }

    (void) dlerror();                           /* Clear dlerror() */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
    funcp = (void (*)(void)) dlsym(yHandle, "y1_enter");
#pragma GCC diagnostic pop
    err = dlerror();
    if (err != NULL) {
        fprintf(stderr, "dlsym: %s", err);
        exit(EXIT_FAILURE);
    }

    (*funcp)();

    exit(EXIT_SUCCESS);
}

$ cat lib_m1.c
#include <stdio.h>

void /* A function defined in both lib_m1 and lib_y1 */
m1_y1(void)
{
    printf("Called %s::%s\n", __FILE__, __func__);
}

#if 1
void
dummy(void)
{
    extern void prog_y1_exp(void);

    prog_y1_exp(); /* Forces prog_y1_exp into prog's dynamic symbol table,
   so that it will be visible also to lib_y1.so */
}
#endif

$ cat lib_x1.c
#include <stdio.h>

void /* A function defined in both main and lib_x1 */
prog_x1(void)
{
    printf("Called %s::%s\n", __FILE__, __func__);
}

void /* A function defined in both lib_x1 and lib_y1 */
x1_y1(void)
{
    printf("Called %s::%s\n", __FILE__, __func__);
}

$ cat lib_y1.c
#include <stdio.h>

void /* A function defined in both lib_x1 and lib_y1 */
x1_y1(void)
{
    printf("Called %s::%s\n", __FILE__, __func__);
}

void /* A function defined in both main and lib_y1 */
prog_y1_exp(void)
{
    printf("Called %s::%s\n", __FILE__, __func__);
}

void /* A function defined in both lib_m1 and lib_y1 */
m1_y1(void)
{
    printf("Called %s::%s\n", __FILE__, __func__);
}

void /* A function defined in both main and lib_y1 */
prog_y1_noexp(void)
{
    printf("Called %s::%s\n", __FILE__, __func__);
}

void
y1_enter(void)
{
    extern void y2(void);

    printf("Called %s\n\n", __func__);

    prog_x1();
    prog_y1_exp();
    prog_y1_noexp();
    x1_y1();
    m1_y1();
    y2();
}

$ cat lib_y2.c
#include <stdio.h>

void
y2(void)
{
    printf("Called %s::%s\n", __FILE__, __func__);
}

$ cat Build.sh
#!/bin/sh

CFLAGS="-Wno-implicit-function-declaration -Wl,--no-as-needed"

cc $CFLAGS -g -fPIC -shared -o lib_x1.so lib_x1.c
cc $CFLAGS -g -fPIC -shared -o lib_y2.so lib_y2.c
cc $CFLAGS -g -fPIC -shared -o lib_y1.so lib_y1.c ./lib_y2.so
cc $CFLAGS -g -fPIC -shared -o lib_m1.so lib_m1.c

#ED="-Wl,--export-dynamic"
cc $CFLAGS $ED -Wl,--rpath,$PWD -o prog prog.c -ldl lib_m1.so

$ sh Build.sh

$ ./prog x
Link map as shown from dl_iterate_phdr() callbacks:
Name =
Name = linux-vdso.so.1
Name = /lib64/libdl.so.2
Name = /home/mtk/tlpi/code/shlibs/dlopen_sym_res_expt/lib_m1.so
Name = /lib64/libc.so.6
Name = /lib64/ld-linux-x86-64.so.2
Name = ./lib_x1.so
Name = ./lib_y1.so
Name = ./lib_y2.so

Called y1_enter

Called lib_x1.c::prog_x1
Called prog.c::prog_y1_exp
Called lib_y1.c::prog_y1_noexp
Called lib_x1.c::x1_y1
Called lib_m1.c::m1_y1
Called lib_y2.c::y2

Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
man3/dlopen.3