基础
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