Linux C下的动态链接库的符号的scope

基础

DSOs: Dynamic Shared Ojbects
使用下面的环境变量来输出动态链接库相关的debug信息

export LD_DEBUG_OUTPUT=/tmp/txt
export LD_DEBUG=all

lookup scope:

global scope:

global scope是通过读取ELF文件中的DT_NEEDED条目,来构建的,使用的是广度优先顺序(breadth-first order)

     97088: Initial object scopes
     97088: object=/home/jialiang/cpp-workspace/Test2/build/make.debug.linux.x86_64/p3 [0]
     97088:  scope 0: /home/jialiang/cpp-workspace/Test2/build/make.debug.linux.x86_64/p3 /lib64/libdl.so.2 /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2

LD_PRELOAD will put the so into global scope, and after the execution
LD_PRELOAD=libm.so

     97088: Initial object scopes
     97088: object=/home/jialiang/cpp-workspace/Test2/build/make.debug.linux.x86_64/p3 [0]
     97088:  scope 0: /home/jialiang/cpp-workspace/Test2/build/make.debug.linux.x86_64/p3 **/lib64/libm.so** /lib64/libdl.so.2 /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2

DF_SYMBOLIC add to the front to global scope(acutally in still under ./libhello.so)
-Wl,-Bsymbolic

     97456: object=./libhello.so [0]
     97456:  scope 0: **./libhello.so**
     97456:  scope 1: /home/jialiang/cpp-workspace/Test2/build/make.debug.linux.x86_64/p3 /lib64/libdl.so.2 /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2
     97456:  scope 2: ./libhello.so /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2

增加scope的方法,我现在知道的有dlopen,和-Wl,-Bsymbolic。global scope应该永远只有一个,因为它是从可执行文件的DT_NEEDED中读取相应的lib的。
dlopen首先会将global scope作为自己的第一个scope,然后将要调用的so的DT_NEEDED中的lib和它们的依赖做成scope 2

dlopen

每次dlopen调用生成的link map都是local lookup scope,除非你制定了RTLD_GLLBAL

    102235: add ./libhello.so [0] to global scope #add libhello.so to global
    102235: object=./libhello3.so [0] #load libhello3.so with dlopen
    102235:  scope 0: /home/jialiang/cpp-workspace/Test2/build/make.debug.linux.x86_64/p3 /lib64/libm.so /lib64/libdl.so.2 /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2 ./libhello.so
    102235:  scope 1: ./libhello3.so /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2
# after dlclose handle for libhello.so
    102235: object=./libhello4.so [0] #load libhello4.so with dlopen
    102235:  scope 0: /home/jialiang/cpp-workspace/Test2/build/make.debug.linux.x86_64/p3 /lib64/libm.so /lib64/libdl.so.2 /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2 ./libhello.so
    102235:  scope 1: ./libhello4.so /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2

    # use global handle to get hello symbol in libhello.so, still can work
hello1

    # use handle for libhello3.so to get hello3 symbol(hello3 call hello symbol in libhello.so), still can work
hello3
hello1  

-rdynamic(DF_DYNAMIC) 将main程序的符合表共享给dlsys函数,否则的话,dlopen加载上来的so文件是不能访问main程序的符号表的。
就算你使用dlopen拿到global lookup scope,也不能得到main程序的符号。
但是使用-rdynamic选项,有一个缺点就是:main程序中的同名符号会覆盖通过dlopen加载上来的so文件中的同名符号。
这是就需要使用-Wl,-Bsymbolic选项了

RTLD_NEXT 简单来说就是找第二个同名的symbol。在哪里找呢?找当前的loopkup scope,从scope的什么地方开始找呢?
从scope中,调用dlsys的so中开始调用。
例如:如果调用dlsys的地方是主main程序,那就从global lookup scope中从头开始找。
如果调用dlsys的地方是主程序在DT_NEEDED中指定的某个so,那还是从global lookup scope中,从这个so开始往后找
如果调用dlsys的地方通过dlopen来加载的话,那就从这个dlopen对应的local lookup scope中,从这个so开始往后找
所以在使用这个handle的时候,要注意lib在编译时的先后顺序
可以使用dladdr()来得到关于dlsys返回的symbol的信息,检查获得的symbol是不是你需要的lib中的

http://vishalchovatiya.com/rtld_next-example/
http://linux4u.jinr.ru/usoft/WWW/www_debian.org/Documentation/elf/elf.html
http://s.eresi-project.org/inc/articles/elf-rtld.txt
https://stackoverflow.com/questions/12666248/elf-dynamic-loader-symbol-lookup-ordering

Intel平台下linux中ELF文件动态链接的加载、解析及实例分析
https://www.ibm.com/developerworks/cn/linux/l-elf/part2/index.html
Michael Kerrisk-The Linux programming interface_ a Linux and UNIX system programming handbook

source code:
Makefile:

PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))

CC=gcc


all:    
    $(CC) -shared -fPIC -o libhello.so $(PROJECT_ROOT)/hello.c
    $(CC) -shared -fPIC -o libhello2.so $(PROJECT_ROOT)/hello2.c
    $(CC) -shared -fPIC -o libhello4.so $(PROJECT_ROOT)/hello4.c
    $(CC) -shared -fPIC -Wl,-Bsymbolic -o libhello3.so $(PROJECT_ROOT)/hello3.c ./libhello4.so

    #$(CC) -rdynamic -g -o p3 $(PROJECT_ROOT)/DLTest.c ./libhello4.so -ldl
    $(CC) -rdynamic -g -o p3 $(PROJECT_ROOT)/DLTest.c  -ldl

    #$(CC) -g -o p3 $(PROJECT_ROOT)/DLTest.c  -ldl

clean:
    rm -fr Test2 $(OBJS)

DLTest.c

/**
 *
 * @author  jialiang
 * @date    May 7, 2018
 */

// gcc -rdynamic -O2 -o p3 DLTest.c -ldl
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

struct s_test {
    int a;
    int b;
    int c;
    int d;
    int e;
};

void main_hello() {
    printf("main_hello\n");
}

int main() {
    void *handle, *handle3, *handle4, *global;
    void (*hello)(void);
    char *error;

    global = dlopen(NULL, RTLD_NOW);
    if (!global) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
    }

    handle = dlopen("./libhello.so", RTLD_NOW | RTLD_GLOBAL);
    if (!handle) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
    }

    hello = dlsym(handle, "hello");
    if ((error = dlerror()) != NULL) {
        fprintf(stderr, "%s\n", error);
        exit(1);
    }

    hello();

    //check the global scope
    handle3 = dlopen("./libhello3.so", RTLD_LAZY);
    if (!handle3) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
    }

    hello = dlsym(handle3, "hello3");
    if ((error = dlerror()) != NULL) {
        fprintf(stderr, "%s\n", error);
        exit(1);
    }

    hello();

    //check RTLD_NEXT
    hello = dlsym(RTLD_DEFAULT, "main_hello");
    if ((error = dlerror()) != NULL) {
        fprintf(stderr, "%s\n", error);
        exit(1);
    }

    printf("RTLD_DEFAULT\n");
    hello();
    printf("RTLD_DEFAULT\n");

    /* Unload the shared library */
    if (dlclose(handle) < 0) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
    }

    // even you close handle for libhello.so,
    // if libhello3.so still ref to libhello.so, the libhello.so still exist
    // if you want libhello3 ref to libhello.so, you should call some method for libhello.so in libhello3.so
    // or dlclose will unload libhello.so
    hello = dlsym(global, "hello");
    if ((error = dlerror()) != NULL) {
        fprintf(stderr, "%s\n", error);
        exit(1);
    }

    hello();

    // check -rdynamic
    hello = dlsym(global, "main_hello");
    if ((error = dlerror()) != NULL) {
        fprintf(stderr, "%s\n", error);
        exit(1);
    }

    hello();


    //check the libhello.so still exist
    handle4 = dlopen("./libhello4.so", RTLD_NOW);
    if (!handle4) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
    }

    hello = dlsym(handle4, "hello4");
    if ((error = dlerror()) != NULL) {
        fprintf(stderr, "%s\n", error);
        exit(1);
    }

    hello();

    if (dlclose(handle4) < 0) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
    }

    if (dlclose(handle3) < 0) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
    }

    if (dlclose(global) < 0) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
    }

    //test for struct *
//  struct s_test *p;
//
//  printf("size of *p %d \n", sizeof(*p)); //this print out 20, the size of struct
//  printf("size of *p %d \n", sizeof(struct s_test *)); //this print out 8, this is pointer in x64

    return 0;
}

hello.c

/**
 *
 * @author  jialiang
 * @date    May 7, 2018
 */

// gcc -shared -o libhello.so hello.c

// gcc -shared -fPIC -o libhello.so hello.c

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

void hello(void)
{
    printf("hello1\n");

}

hellox.c looks like hello.c

猜你喜欢

转载自blog.csdn.net/lantianjialiang/article/details/81127978
今日推荐