dlopen参数flag的使用

Posted ho966

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dlopen参数flag的使用相关的知识,希望对你有一定的参考价值。

参考:https://blog.csdn.net/Bluenapa/article/details/119205993

使用dlopen接口动态加载共享库 ,函数原型如下

    void * dlopen( const char * pathname, int mode)

其第一个参数是包含so名称的路径,可以是相对路径;第二个参数mode是选项配置,可以结合实际场景需求配置,接下来认识下具体功能。

1、RTLD_NOW和RTLD_LAZY

  mode必须包含上述其中一个,表示解析方式,RTLD_NOW是调用dlopen后就解析所有函数符号,RTLD_LAZY是延时加载函数符号,使用的时候才解析,这样即使有一些未定义的函数符号,也能通过dlopen加载动态库。如下:main调用libA.so, 但是libA.so里面的函数里有未实现的函数,看看两个选项有啥区别。

  main.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
int main()

    // RTLD_NOW RTLD_LAZY
    void* handle = dlopen("./libA.so",RTLD_NOW);
    if (!handle)
       
            printf("dlopen libA.so failed,err:%s\\n",dlerror());
            return -1;
       
    void (*func)(void)  = (void (*)(void))dlsym(handle,"funcA");
    if (!func)
       
            printf("dlsym funcA failed, err: %s\\n",dlerror());
            return -1;
       
    func();
    return 0;

 A.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
extern "C" void funcB();
extern "C" void funcA()

funcB();
printf("this is funcA\\n");

 编译命令:

g++ -fpic --shared A.cpp -o libA.so -ldl
g++ main.cpp -o main -ldl

  执行./main ,会在dlopen时就报错:  dlopen libA.so failed,err:./libA.so: undefined symbol: funcB

  如果将dlopen参赛mode改成RTLD_LAZY,就会发现调用dlopen和调用dlsym时都未报错,执行funcA时才报错:./main: symbol lookup error: ./libA.so: undefined symbol: funcB, 如果你不使用funcA,就不会出错

2、RTLD_LOCAL和RTLD_GLOBAL

  表示加载的函数符号的作用范围,RTLD_LOCAL与RTLD_GLOBAL作用相反,RTLD_GLOBAL 动态库中符号表全局打开,因此符号可被其后打开的其它库重定位;RTLD_LOCAL  动态库中符号表非全局打开,定义的符号不能被其后打开的其它库重定位。

  不填此选项,默认是RTLD_LOCAL

  如下:main加载libA.so,main加载libB.so并调用其函数funcB,而funcB调用A中funcA。(如果是mainmain加载libA.so,并调用funcA, libA.so动态加载libB.so, 并调用funcB,效果也是一样)

main.cpp

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

int main()

    // RTLD_NOW RTLD_LAZY
    // RTLD_LOCAL和RTLD_GLOBAL
    void* handle = dlopen("./libA.so",RTLD_NOW|RTLD_LOCAL);
    if (!handle)
       
            printf("dlopen libA.so failed,err:%s\\n",dlerror());
            return -1;
       
    void* handleB = dlopen("./libB.so",RTLD_NOW);
    if (!handleB)
       
            printf("dlopen libB.so failed,err:%s\\n",dlerror());
            return -1;
       
    void (*func)(void)  = (void (*)(void))dlsym(handleB,"funcB");
    if (!func)
       
            printf("dlsym funcB failed, err: %s\\n",dlerror());
            return -1;
       
    func();
    return 0;

A.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
extern "C" void funcC();
extern "C" void funcA()

    printf("this is funcA\\n");

B.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
extern "C" void funcA();
extern "C" void funcB()

    printf("this is funcB\\n");
    funcA();

编译命令:

g++ -fpic --shared A.cpp -o libA.so -ldl
g++ -fpic --shared B.cpp -o libB.so -ldl
g++ main.cpp -o main -ldl

执行./main.cpp, 会报错:dlopen libB.so failed,err:./libB.so: undefined symbol: funcA, 将dlopen libA.so的参数改成RTLD_GLOBAL,funcB就可以找到funcA符号,程序执行就没问题了。

3、RTLD_NODELETE、RTLD_NOLOA、DRTLD_DEEPBIND

RTLD_NODELETE表示在dlclose()期间不卸载库,并且在以后使用dlopen()重新加载库时不初始化库中的静态变量

RTLD_NOLOAD用于验证动态库是否已加载,dlopen()返回NULL表示未加载,否则已加载;也可用于改变已加载库的flag,如:先前加载库的flag为RTLD_LOCAL,用dlopen(RTLD_NOLOAD|RTLD_GLOBAL)后flag将变成RTLD_GLOBAL

RTLD_DEEPBIND在规避同名符号冲突是很有用的,如果定义了此标志,在搜索全局符号时会优先搜索库内的符号,如下:main调用A里的funcA, funcA调用B里面的funcB函数,funcB调用funcC函数, libA.so和libB.so里面都有funcC函数

mian.cpp

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

int main()

    // RTLD_NOW RTLD_LAZY
    // RTLD_LOCAL和RTLD_GLOBAL
    void* handle = dlopen("./libA.so",RTLD_NOW|RTLD_GLOBAL);
    if (!handle)
       
            printf("dlopen libA.so failed,err:%s\\n",dlerror());
            return -1;
       
    void (*func)(void)  = (void (*)(void))dlsym(handle,"funcA");
    if (!func)
       
            printf("dlsym funcA failed, err: %s\\n",dlerror());
            return -1;
       
    func();
    return 0;

A.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
extern "C" void funcC()

    printf("this is libA funcC\\n");

extern "C" void funcA()

    printf("this is funcA\\n");
    // RTLD_NOW RTLD_LAZY
    // RTLD_LOCAL和RTLD_GLOBAL
    //  RTLD_DEEPBIND
    void* handle = dlopen("./libB.so",RTLD_NOW|RTLD_GLOBAL|RTLD_DEEPBIND);
    if (!handle)
       
            printf("dlopen libB.so failed,err:%s\\n",dlerror());
            return;
       
    void (*func)(void)  = (void (*)(void))dlsym(handle,"funcB");
    if (!func)
       
            printf("dlsym funcB failed, err: %s\\n",dlerror());
            return;
       
    func();

B.cpp

#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
extern "C" void funcC()

    printf("this is libB funcC\\n");

extern "C" void funcB()

    printf("this is funcB\\n");
    funcC();

编译命令:

g++ -fpic --shared B.cpp -o libB.so -ldl
g++ -fpic --shared A.cpp -o libA.so -ldl
g++ main.cpp -o main -ldl

A.cpp里面的dlopen使用了RTLD_DEEPBIND选项,输出打印是this is libB funcC, 如果去掉RTLD_DEEPBIND选项,输出打印是 this is libA funcC

 

  

 

     

C:尽管添加了 `-ldl` 标志,但未定义对 `dlopen`/`dlsym` 的引用

【中文标题】C:尽管添加了 `-ldl` 标志,但未定义对 `dlopen`/`dlsym` 的引用【英文标题】:C: Undefined reference to `dlopen`/`dlsym` despite adding `-ldl` flags 【发布时间】:2014-10-31 19:58:19 【问题描述】:

TL;DR:我正在做一个 C 练习,它使用 dlfcn.h 打开共享库。尽管根据其他帖子添加了(我认为是)正确的标志,但我仍然收到dlopendlsymdlfcn.h 中定义的其他一些函数的undefined reference to 错误(错误消息和生成文件包括在下面)。

我在 .c 文件的开头包含以下内容:#include &lt;dlfcn.h&gt;

我做错了什么?

详细版本:

我正在处理exercise 30 of Learn C The Hard Way,我不知道我还需要做什么才能使libex29_tests.c 程序(页面上的最后一段代码)正确编译。正如您可能知道的那样,共享库/makefiles/编译器标志目前对我来说是新的。

到目前为止我所做的尝试:根据以下帖子,我尝试通过将 LIBS=-ldl fPIC 和/或 LDFLAGS+=-ldl 添加到我的 Makefile 的各个部分来添加 -ldl 标志,但仍然遇到问题。书中的 makefile 版本(包括在下面,作为参考)确实包含一个-ldl 标志,尽管语法略有不同。在任何情况下,我都会继续收到相同的错误消息。

Linux c++ error: undefined reference to 'dlopen' g++ cannot link to libdl even with -ldl flag undefined reference to 'dlopen' in installing phonetisaurus C++: Undefined symbols when loading shared library with dlopen()

有什么建议吗?


这些是我得到的错误。这些错误假定下面包含makefile 的版本。但是,正如我所提到的,更改 -ldl 标志的语法会导致几乎相同的错误消息。

~/.../lchw/ex30_automated$ make
cc -std=gnu99 -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/libex29.o src/libex29.c
src/libex29.c: In function ‘fail_on_purpose’:
src/libex29.c:42:33: warning: unused parameter ‘msg’ [-Wunused-parameter]
 int fail_on_purpose(const char *msg)
                                 ^
ar rcs build/libYOUR_LIBRARY.a src/libex29.o
ranlib build/libYOUR_LIBRARY.a
cc -shared -o build/libYOUR_LIBRARY.so src/libex29.o
cc -std=gnu99 -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  build/libYOUR_LIBRARY.a    tests/libex29_tests.c   -o tests/libex29_tests
In file included from tests/libex29_tests.c:1:0:
tests/libex29_tests.c: In function ‘main’:
src/minunit.h:14:38: warning: parameter ‘argc’ set but not used [-Wunused-but-set-parameter]
 #define RUN_TESTS(name) int main(int argc, char *argv[]) \
                                      ^
tests/libex29_tests.c:64:1: note: in expansion of macro ‘RUN_TESTS’
 RUN_TESTS(all_tests);
 ^
/tmp/dchaudh/ccwzxpC3.o: In function `check_function':
/home/dchaudh/Dropbox/dchaudh/wc/lchw/ex30_automated/tests/libex29_tests.c:10: undefined reference to `dlsym'
/home/dchaudh/Dropbox/dchaudh/wc/lchw/ex30_automated/tests/libex29_tests.c:11: undefined reference to `dlerror'
/tmp/dchaudh/ccwzxpC3.o: In function `test_dlopen':
/home/dchaudh/Dropbox/dchaudh/wc/lchw/ex30_automated/tests/libex29_tests.c:23: undefined reference to `dlopen'
/tmp/dchaudh/ccwzxpC3.o: In function `test_dlclose':
/home/dchaudh/Dropbox/dchaudh/wc/lchw/ex30_automated/tests/libex29_tests.c:46: undefined reference to `dlclose'
collect2: error: ld returned 1 exit status
make: *** [tests/libex29_tests] Error 1

这是本书中最新的makefile。它确实包含-ldl 标志,正如您在第二行中看到的那样。

CFLAGS=-std=gnu99 -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS)
LIBS=-ldl $(OPTLIBS)
PREFIX?=/usr/local

SOURCES=$(wildcard src/**/*.c src/*.c)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))

TEST_SRC=$(wildcard tests/*_tests.c)
TESTS=$(patsubst %.c,%,$(TEST_SRC))

TARGET=build/libYOUR_LIBRARY.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))

# The Target Build
all: $(TARGET) $(SO_TARGET) tests

dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all

$(TARGET): CFLAGS += -fPIC
$(TARGET): build $(OBJECTS)
    ar rcs $@ $(OBJECTS)
    ranlib $@

$(SO_TARGET): $(TARGET) $(OBJECTS)
    $(CC) -shared -o $@ $(OBJECTS)

build:
    @mkdir -p build
    @mkdir -p bin

# The Unit Tests
.PHONY: tests
tests: CFLAGS += $(TARGET)
tests: $(TESTS)
    sh ./tests/runtests.sh

valgrind:
    VALGRIND="valgrind --log-file=/tmp/valgrind-%p.log" $(MAKE)

# The Cleaner
clean:
    rm -rf build $(OBJECTS) $(TESTS)
    rm -f tests/tests.log
    find . -name "*.gc*" -exec rm  \;
    rm -rf `find . -name "*.dSYM" -print`

# The Install
install: all
    install -d $(DESTDIR)/$(PREFIX)/lib/
    install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/

# The Checker
BADFUNCS='[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)|stpn?cpy|a?sn?printf|byte_)'
check:
    @echo Files with potentially dangerous functions.
    @egrep $(BADFUNCS) $(SOURCES) || true

【问题讨论】:

【参考方案1】:

$LIBS(和你的-ldl)在makefile中没有使用。看来,您正在使用内置规则进行编译,并且可以使用 LDLIBS 而不是 LIBS 作为此变量的名称。

除此之外,

  tests: CFLAGS += $(TARGET)

在您的示例中是错误的,因为 TARGET 是一个必须进入 (LD)LIBS 的库。

$(TARGET): build $(OBJECTS)
build:
    @mkdir -p build

也不理智。这将在并行构建中中断($(OBJECTS) 应该依赖于build)并且会导致不需要的构建(build 是一个目录,并且每个编译/链接操作都会改变)。普通的 build 对于 VPATH 构建会有问题,我建议使用

$(OBJECTS): | build/.dirstamp
build/.dirstamp:
    mkdir $@D

这里。

【讨论】:

我设法通过将第 2 行更改为 DLIBS=-ldl $(OPTLIBS)tests: CFLAGS += $(TARGET) 更改为 tests: LDLIBS += $(TARGET) 来使这个示例工作。 Make 执行如下汇编的编译命令:“cc CFLAGS program.c LDLIBS -o program”,在此示例中为cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG tests/libex29_tests.c -ldl build/libYOUR_LIBRARY.a -o tests/libex29_tests

以上是关于dlopen参数flag的使用的主要内容,如果未能解决你的问题,请参考以下文章

程序中动态加载共享库

Android 逆向Android 进程注入工具开发 ( 注入代码分析 | 获取 linker 中的 dlopen 函数地址 并 通过 远程调用 执行该函数 )

解决:linux eclipse 对‘dlopen’未定义的引用, 对‘xxx’未定义的引用

Linux下C/C++动态库在运行时是怎样加载进来的

用dlopen和dlsym得到的符号 在dlclose后还能继续使用么

dlopen 中对 __dlopen 的未知引用