动态库装载及 dlsym的RTLD_NEXT参数详解

2023-11-17

在看公司spp框架代码的时候发现了如下一段宏定义,其中的dlsym函数及其RTLD_NEXT参数的含义不是很明白,于是网上搜了下这里做个记录。

#define mt_hook_syscall(name)                                                  \
do  {                                                                          \
        if (!g_mt_syscall_tab.real_##name) {                                   \
           g_mt_syscall_tab.real_##name = (func_##name)dlsym(RTLD_NEXT, #name);\
        }                                                                      \
    } while (0)

我们知道动态库装载是由一些列动态库提供的API来完成的,准确来说就是打开动态库(dlopen)、查找符号(dlsym)、关闭动态库(dlcose)、错误处理(dlerror)四个函数。这四个函数包含dlfcn.h头文件(#include<dlfcn.h>):

1、打开动态库dlopen。

函数定义 void * dlopen(const char* pathName, int mode);

(1)pathName 指的是动态库路径。如果是绝对路径就直接打开此动态库文件;如果是相对路径则则会以一定顺序查找动态文件,顺序如下:

1)查找由环境变量LD_LIBRARY_PATH指定的一系列目录;

2)查找由/etc/ld.so.cache里面所指定的共享库路径;

注:运行“/sbin/ldconfig -v”使生效。

3)/lib、/usr/lib等默认搜索路径。

(2)mode指的是解析方式:

1)RTLD_LAZY:暂缓决定,在dlopen返回前,对于动态库中的未定义的符号不执行解析(只对函数引用有效,对于变量引用总是立即解析)

2)RTLD_NOW:立即决定,在dlopen返回前,解析出所有未定义的符号,如果解析不出来,在dlopen会返回NULL,错误为 undefined symbol:XXX...

2、查找符号dlsym。这里先介绍下dlsym函数。

函数定义 void *dlsym(void *handle, const char* symbol);

handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数的名称。dlsym函数的返回值是void*,指向要查找的函数symbol的地址,供调用使用

2.1、dlsym函数的RTLD_NEXT选项

经过查询知道其含义是使用RTLD_NEXT参数找到的的函数指针就是后面第一次出现这个函数名的函数指针。大致意思就是说我们可能会链接多个动态库,不同的动态库可能都会有symbol这个函数名,那么使用RTLD_NEXT参数后dlsym返回的就是第一个遇到(匹配上)symbol这个符号的函数的函数地址。进一步的我们使用dlsym的返回调用的也就是这个第一个匹配上的函数了。

3、关闭动态库dlerror

作用和dlopen相反,将一个动态库卸载。

4、错误处理dlerror

每次调用完上述三个函数后都可以调用dlerror来判断上次调用是否成功。

5、dlsym的RTLD_NEXT选项效果实例:

(1)如下几个文件分别编译成动态库或目标文件

/*
文件名:first_one.c
编译成动态库:gcc -fpic --shared first_one.c -o libfirst_one.so
*/

#include <stdio.h>
void print_message()
{
    printf("the first lib~~\n");
}
void first()
{
    printf("init first\n");
}
/*
文件名:second_one.c
编译动态库: gcc -fpic --shared second_one.c -o libsecond_one.so
*/

#include <stdio.h>
void print_message()
{
    printf("the second lib~~\n");
}

void second()
{
    printf("init second \n");
}
/*
文件名:wrap.c
编译动态库: gcc -fpic --shared wrap.c -o libwrap.so

注:void load_func() __attribute__((constructor))的含义是在执行main函数前,执行load_func这个函数,便于我们做一些准备工作。显然这里的作用就是触发dlsym以实现查找第一个"print_message"函数符号的目的。
具体参见   jianshu.com/p/dd425b9dc9db
*/

# define RTLD_NEXT      ((void *) -1l)
#include <stdio.h>
#include <dlfcn.h>
#include <errno.h>
void(*f)();
void load_func() __attribute__((constructor));
void load_func()
{
    f = (void(*)())dlsym(RTLD_NEXT,"print_message");
    char *error_str;
    error_str = dlerror();
    if (error_str != NULL) {
        printf("%s\n", error_str);
    }
    printf("load func first f=%p\n",f);

}
void print_message()
{
    printf("the wrap lib~~\n");
    f();
}
/*
文件名:main.c
编译动态库: gcc -c main.c

*/
#include <stdio.h>
void print_message();
void first();
void second();
int main()
{
    first();
    second();
    print_message();
    return 0;
}

(2)调整链接顺序,使链接器第一个找到/匹配到不同实现的"print_message"函数(或者说是符号)。

#优先链接libfirst_one.so
gcc -o first main.o  -lwrap -lfirst_one  -lsecond_one -ldl -L.

#优先链接libsecond_one.so
gcc -o second main.o  -lwrap -lsecond_one -lfirst_one  -ldl -L.

设置执行时的环境变量: 

#环境变量LD_LIBRARY_PATH主要用于查找共享库时除了默认路径外的其他路径;
#此处是把当前路径加入到查找路径的意思
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

(3)使用ldd(List Dynamic Dependencies)指令查看库的加载顺序

ldd:列出一个程序所需要的动态链接库。

  

(4)执行结果如下:

  

(5)执行结果分析:

int main()
{
    first();
    second();
    print_message();
    return 0;
}
0.第一行输出“load func first”,就是__attribute__((constructor))的效果{main函数前先执行某预备函数}

1.first函数无异议,就是执行的first_one中的函数;对应输出中的第二行。

2.second函数无异议,就是执行的second_one中的函数;对应输出中的第三行。

3.print_message执行的实际上是wrap.c中的实现——看链接顺序第一个匹配的是libwrap.so
其中又调用了再之后dlsym查找到的print_message函数;这个时候进一步往后匹配到的就是-lwrap后面的-lfirst_one或者-lsecond_one了。

这里贴两段《程序员的自我修养》一书中关于全局符号介入和dlsym函数的描述:

1 全局符号介入
    linux下的动态链接器存在以下原则:当共享对象被load进来的时候,它的符号表会被合并到进程的全局符号表中(这里说的全局符号表并不是指里面的符号全部是全局符号,而是指这是一个汇总的符号表),当一个符号需要加入全局符号表时,如果相同的符号名已经存在,则后面加入的符号被忽略。

2 dlsym函数
    查找符号的地址。对应于函数,即函数地址,对应于变量,即变量地址。通过传入RTLD_NEXT参数,在当前库之后load进来的动态库中寻找对应符号的地址,显然在这里找到的会是glibc中相关socket函数的地址(这句话在下文中的波折部分被证明是错的,原因是并不是只有glibc中有socket函数的定义,不过在这里这么理解没毛病)。

参看:dlsym参数 RTLD_NEXT详解 – 夕月阁 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

动态库装载及 dlsym的RTLD_NEXT参数详解 的相关文章

  • ssh 连接超时

    我无法在 git 中 ssh 到 github bitbucket 或 gitlab 我通常会收到以下错误消息 如何避免它 输出 ssh T email protected cdn cgi l email protection i ssh
  • Tomcat Intellij Idea:远程部署

    RackSpace 云服务器 Ubuntu 12 04 Intellij Idea 11 1 2 Windows 8 Tomcat 7 0 26 JDK 6 在 Intellij Idea 上 当我尝试在远程 Tomcat 7 服务器上运行
  • ioctl 命令的用户权限检查

    我正在实现 char 驱动程序 Linux 并且我的驱动程序中有某些 IOCTL 命令仅需要由 ADMIN 执行 我的问题是如何在 ioctl 命令实现下检查用户权限并限制非特权用户访问 IOCTL 您可以使用bool capable in
  • 有没有一种快速方法可以从 Jar/war 中删除文件,而无需提取 jar 并重新创建它?

    所以我需要从 jar war 文件中删除一个文件 我希望有类似 jar d myjar jar file I donot need txt 的内容 但现在我能看到从 Linux 命令行执行此操作的唯一方法 不使用 WinRAR Winzip
  • 在 .gitconfig 中隐藏 GitHub 令牌

    我想将所有点文件存储在 GitHub 上 包括 gitconfig 这需要我将 GitHub 令牌隐藏在 gitconfig 中 为此 我有一个 gitconfig hidden token 文件 这是我打算编辑并放在隐藏令牌的 git 下
  • 如何使用 Cloud Init 挂载未格式化的 EBS 卷

    Context 我正在使用https wiki jenkins io display JENKINS Amazon EC2 Plugin https wiki jenkins io display JENKINS Amazon EC2 Pl
  • tcpdump 是否受 iptables 过滤影响?

    如果我的开发机器有iptables规则到FORWARD一些数据包 这些数据包是否被 tcpdump 捕获 我有这个问题 因为我知道存在其他链称为INPUT如果数据包路由到 它会过滤发往应用程序的数据包FORWARD链 它会到达吗tcpdum
  • 就分页分段内存而言的程序寿命

    我对 x86 Linux 机器中的分段和分页过程有一个令人困惑的概念 如果有人能澄清从开始到结束所涉及的所有步骤 我们将很高兴 x86 使用分页分段内存技术进行内存管理 任何人都可以解释一下从可执行的 elf 格式文件从硬盘加载到主内存到它
  • 如何在 Ubuntu 中创建公共 HTML 文件夹?

    简单的问题 但由于某种原因我无法在谷歌上找到确切的答案 我在 Slicehost 上安装了全新的 Ubuntu 并且想在我的主目录中为包含一堆静态 HTML 文件的简单网站创建一个公共目录 我该怎么做呢 只是打字的问题吗mkdir publ
  • 执行“minikube start”命令时出现问题

    malik malik minikube start minikube v1 12 0 on Ubuntu 18 04 Using the docker driver based on existing profile Starting c
  • Linux 上的 Pervasive ODBC 错误 [01000][unixODBC][驱动程序管理器]无法打开 lib '/usr/local/psql/lib/odbcci.so':找不到文件

    我正在尝试让 Pervasive v10 客户端 ODBC 在 Centos 6 上运行 据我所知 没有 64 位 ODBC 客户端 因此我必须使用 32 位客户端 我终于成功安装了它 但尝试使用时出现以下错误 isql v mydsn 0
  • 如何在linux中以编程方式获取dir的大小?

    我想通过 C 程序获取 linux 中特定目录的确切大小 我尝试使用 statfs path struct statfs 但它没有给出确切的大小 我也尝试过 stat 但它返回任何目录的大小为 4096 请建议我如何获取 dir 的确切大小
  • C 语言的符号表

    我目前正在开发一种执行模式匹配的静态分析工具 我在用Flex https github com westes flex生成词法分析器 我编写了代码来管理符号表 我不太有经验C 所以我决定将符号表实现为线性链表 include
  • 使用循环在 C 中管道传输两个或多个 shell 命令

    我正在尝试执行ls wc l通过 C 语言程序 而不是使用命令行 这是我当前的工作代码 int main int pfds 2 pipe pfds pid t pid fork if pid 0 The child process clos
  • 使用包管理器时如何管理 Perl 模块?

    A 最近的问题 https stackoverflow com questions 397817 unable to find perl modules in intrepid ibex ubuntu这让我开始思考 在我尝试过的大多数 Li
  • 静态方法的 Java 内存模型

    我来自操作系统和 C 语言背景 在代码编译时 世界很简单 需要处理和理解堆栈 堆文本部分等 当我开始学习 Java 时 我确实了解 JVM 和垃圾收集器 我对静态方法感到很有趣 根据我的理解 类的所有实例都会在堆中创建 然后被清理 但是 对
  • 如何查找哪个 Yocto 项目配方填充图像根文件系统上的特定文件

    我经常与 Yocto 项目合作 一个常见的挑战是确定文件为何 或来自什么配方 包含在 rootfs 中 这有望从构建系统的环境 日志和元数据中得出 理想情况下 一组命令将允许将文件链接回源 即配方 我通常的策略是对元数据执行搜索 例如gre
  • 如何在 *nix 中登录时运行脚本?

    我知道我曾经知道如何做到这一点 但是 如何在 unix 中登录时运行脚本 bash 可以 From 维基百科 Bash http en wikipedia org wiki Bash 28Unix shell 29 当 Bash 启动时 它
  • Linux/POSIX:为什么 fork() 不分叉*所有*线程

    众所周知 POSIX下创建新进程的默认方式是使用fork 在 Linux 下 这在内部映射到clone 我想知道的是 众所周知 当一个人打电话时fork 子进程是用单个线程创建的 调用的线程fork cf https linux die n
  • cdc_acm:无法设置 dtr/rts - 无法与 USB cdc 设备通信

    我试图使用 pic24fj128gb206 枚举 usb cdc 设备 设备似乎已正确枚举 但是当我将设备连接到 Linux PC 时 我从内核收到以下警告消息 cdc acm 1 8 1 6 7 1 0 failed to set dtr

随机推荐

  • vue3`

    1 ref是一个函数 响应式数据 使用前要引入 impor ref fro vue let name ref 张三 改变数据 name value 李四 vue3对不同对象处理不同 数据劫持才是响应式的根本 getset 处理对象用prox
  • SpringBoot生成二维码

    目录 Zxing原生方式 添加依赖 二维码生成工具类 添加Controller 添加测试页面 使用postman测试效果 Hutool的方式 添加依赖 创建QRCodeService 添加Controller 效果测试 我们使用两种方式 去
  • mutex_init() / mutex_lock() / mutex_unlock()

    请求 1 初始化互斥体 mutex init 2 获得互斥体 mutex lock 3 释放互斥体 mutex unlock 1 mutex init 注意mutex使用之前都需要先init void mutex init struct m
  • 最小交换次数-华为OD

    整数数组nums 整数k 输出将数组A中小于k的整数组合到一起的最小交换次数 组合在一起是指满足条件的数字相邻 不要求相邻后在数组中的位置 样例1 nums 1 3 1 4 0 k 2 输出 1 解析 交换第一个1和4 样例2 nums 0
  • Django跳坑:objects.all()、objects.get()与objects.filter()之间的区别

    文章目录 1 三者之间的区别 2 获取数据 2 1 取单个数据 3 序列化 3 1 QuerySet序列化 3 2 models序列化 1 三者之间的区别 all返回的是QuerySet对象 程序并没有真的在数据库中执行SQL语句查询数据
  • Github账号开启账号双重验证

    Github账号开启账号双重验证 发现问题 解决步骤 插件使用 发现问题 今天在浏览开源项目的时候 突然Github有个提示我要在10月12日前开启双重验证 说是不完成的话 到时候的Github账号会受到限制 如下图 通过设置也可以找到 解
  • win11设置任务栏不合并的方法教程

    win11系统的任务栏窗口默认设置是合并的 有些小伙伴表示用起来还不太习惯 那么win11任务栏怎么设置不合并呢 下面小编为大家分享下win11设置任务栏不合并的方法 感兴趣的小伙伴一起来看看吧 win11设置任务栏不合并的方法教程 1 我
  • Elasticsearch学习笔记2:ES核心概念 -- 索引、倒排索引、类型、文档

    一 ES和关系型数据库的对比 Elasticsearch Relational DB 索引 index 数据库 database 类型 types 表 tables 文档 documents 行 rows 字段 fields 列 colum
  • OLED透明屏报价:实现高质量展示的成本与选择

    引言 OLED透明屏作为商业展示领域的新兴技术 受到了广泛的关注和需求 然而 对于OLED透明屏的报价 人们常常存在疑虑 在这篇文章中 尼伽将详细解析OLED透明屏报价的构成和选择因素 希望能帮助您更好呢地了解OLED透明屏 一 OLED透
  • vue中textarea高度的设置_vue中textarea自适应高度

    HTML data return pltxt 评论 inputText isHeight true minHeight 0 methods autoTextarea var extra 0 设置光标与输入框保持的距离 默认0 maxHeig
  • SQL操作

    一 查询语句 1 基本查询 SELECT FROM lt 表名 gt 查询表的所有行 SELECT FROM students 2 条件查询 SELECT FROM lt 表名 gt WHERE lt 条件表达式 gt 查询分数在80分以上
  • vscode+phpstudy连接使用mysql(解决phpstudy中mysql无法启动的问题)

    vscode phpstudy连接使用mysql 解决phpstudy中mysql无法启动的问题 使用vscode phpstudy配置php开发环境网上很文章都是挺好的 都成功解决了我的问题 但是对于使用mysql方面始终找不到很系统的文
  • 数据结构系列——先进先出队列queue

    本期主题 先进先出队列实现 目录 1 队列定义 2 实现一个简单的队列以及分析 1 代码实现分析 2 code 3 优缺点分析 3 循环队列实现 1 循环队列原理 2 循环队列实现分析 3 code 1 队列定义 队列是什么 定义 一个先进
  • unity,网格碰撞器(Mesh Collider)

    介绍 网格碰撞器 Mesh Collider 在实现物理碰撞检测时 可以自动检测凸面 但是它并不总是能够准确地生成凸多面体 这是因为在将一个网格模型转换为凸多面体的过程中 可能会出现模型内部空洞或者交叉的情况 这些情况会导致凸多面体的生成失
  • H5页面长按识别二维码

    vue 写的H5 内嵌在小程序上 img src 图片路径 style width 200px height 200px 直接在微信访问长按就可以实现 微信原生直接写长按识别二维码 aaa e let img e target datase
  • 八. springboot 的指标监控 (3、定制 Endpoint )

    3 定制 Endpoint 3 1 定制 Health 信息 import org springframework boot actuate health Health import org springframework boot act
  • http、https以及状态码超全详解

    文章目录 HTTP 概念 作用 http 请求 http 头部 http 连接 各版本的区别 http1 0 http1 1 http2 0 http3 0 HTTPS 概念 作用 工作原理 优点 缺点 http 和 https 区别 ht
  • [网络安全自学篇] 八.Web漏洞及端口扫描之Nmap、ThreatScan和DirBuster原理详解

    Web渗透技术的核心是发现Web漏洞 发现漏洞有手工和软件自动化扫描两种方式 对于用户验证漏洞 用户凭证管理问题 权限特权及访问控制漏洞 缓存漏洞 跨站脚本漏洞 加密漏洞 路径切换漏洞 代码注入漏洞 配置漏洞 数据和信息泄露 输入验证码漏洞
  • CA证书服务搭建

    准备一 域服务搭建 步骤一 搭建CA证书服务器 设置固定IP地址 192 168 0 与主机名 DC 打开服务器管理器 gt 点击添加角色和功能 步骤二 一路回车到达选择服务器角色位置 选中Active Directory域服务与Activ
  • 动态库装载及 dlsym的RTLD_NEXT参数详解

    在看公司spp框架代码的时候发现了如下一段宏定义 其中的dlsym函数及其RTLD NEXT参数的含义不是很明白 于是网上搜了下这里做个记录 define mt hook syscall name do if g mt syscall ta