理解链接:加载二进制动态库

理解链接:加载二进制动态库

文章目录

  • 理解链接:加载二进制动态库
    • 前情提要
    • 基本方式1 - 显式连接 dlopen
    • 基本方式 2 - 隐式链接 compile + link + ld
    • 衍生方式 3 - 弱链接 weak linking
    • 衍生方式 4 - dlmopen 加载到独立命名空间
    • 调试所有符号
  • 补充知识
    • 1. 动态库的创建与编译
    • 2. 动态库的搜索路径
    • 3. 符号可见性控制
    • 4. 动态库的版本控制
    • 5. 动态库的初始化与清理函数
    • 6. 调试工具
      • 7. 安全性注意事项

前情提要

之前做的 rev 题一般动态库都是直接在编译时 link 好了动态库,从反编译代码里看不出来调用的过程

机缘巧合仔细了解一下才发现二进制的调库原来跟 python, js 这种语言的依赖引用是很像的,把库以二进制 dll/so 的形式放在 path 下面,让系统能找到,然后用路径和名称调用符号就行了。

基本方式1 - 显式连接 dlopen

runtime linking / manually linking

通过 dlopen 可以打开任意动态库,然后通过函数指针使用里面的函数。

int main() {
    // 加载共享库,RTLD_LAZY 表示延迟解析符号(只有当实际调用时才解析)
    void *handle = dlopen("libm.so", RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "dlopen error: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    // 清除之前的错误
    dlerror();

    // 从库中查找符号,例如查找数学库中的 cos 函数
    double (*cos_func)(double) = (double (*)(double)) dlsym(handle, "cos");
    char *error = dlerror();
    if (error != NULL) {
        fprintf(stderr, "dlsym error: %s\n", error);
        dlclose(handle);
        exit(EXIT_FAILURE);
    }

    // 调用动态加载的函数
    double result = cos_func(2.0);
    printf("cos(2.0) = %f\n", result);

    // 使用完毕后关闭共享库
    dlclose(handle);
    return 0;
}

基本方式 2 - 隐式链接 compile + link + ld

load time linking

在编译和链接阶段已经将动态库的依赖信息写入到可执行文件中,运行时,操作系统的动态链接器会自动加载这些库并解析其中的符号。

程序启动时,系统加载器(例如 /lib/ld-linux.so.*)自动读取 ELF 文件中的依赖信息,并加载所有必需的动态库。符号解析和重定位工作由动态链接器完成,开发者在代码中直接调用库函数即可

加载器如何识别依赖的动态库?链接器(通常是 ld)会在编译器把依赖的共享库的信息写入到最终生成的 ELF 文件中。这个信息主要记录在动态段 .dynamic 里,其中有一个重要的字段 DT_NEEDED,用于列出程序运行时所需要加载

衍生方式 3 - 弱链接 weak linking

弱链接允许在编译阶段声明某个符号为“可选”的(或说“弱”),这样在运行时即使目标动态库中不存在该符号,程序也不会链接失败。开发者可以在运行时检测该符号是否为 NULL,然后决定是否调用或使用备用实现。当应用需要兼容不同版本的库,某些新版本的 API 在旧版本中可能不存在时很有用。也可以用于实现缺失时回退到默认行为或其他实现。

在 GCC 中,可以这样声明一个函数为弱符号:

extern void some_optional_function() __attribute__((weak));

int main() {
    if (some_optional_function) {
        some_optional_function();
    } else {
        // 使用备用方案
    }
    return 0;
}

这样,如果链接的库中没有 some_optional_function,程序依然可以运行,并根据检测结果选择调用。

衍生方式 4 - dlmopen 加载到独立命名空间

防止名称冲突的衍生系统调用

 // LM_ID_NEWLM 表示在一个新的命名空间中加载
    void *handle = dlmopen(LM_ID_NEWLM, "libexample.so", RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "dlmopen error: %s\n", dlerror());
        return -1;
    }
    double (*cos_func)(double) = (double (*)(double)) dlsym(handle, "cos");
    ...
}

调试所有符号

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

int main() {
    // 使用 RTLD_DEFAULT 搜索全局符号
    void (*printf_ptr)(const char*, ...) = dlsym(RTLD_DEFAULT, "printf");
    if (printf_ptr) {
        printf_ptr("Found printf via RTLD_DEFAULT\n");
    } else {
        fprintf(stderr, "Symbol not found: %s\n", dlerror());
    }
    return 0;
}

补充知识

1. 动态库的创建与编译

动态库的生成步骤

# 编译为位置无关代码(-fPIC)
gcc -c -fPIC mylib.c -o mylib.o

# 生成共享库(-shared)
gcc -shared -o libmylib.so mylib.o

# 隐式链接时,编译主程序需指定链接库路径和名称
gcc main.c -L. -lmylib -o main
  • -fPIC:生成位置无关代码(Position-Independent Code),确保动态库可被加载到任意内存地址。
  • -shared:指示生成共享库(.so 文件)。
  • -L.:添加库搜索路径(此处为当前目录)。
  • -lmylib:链接名为 libmylib.so 的库。

2. 动态库的搜索路径

系统按以下顺序查找动态库:

  1. 编译时指定的 -rpath-rpath-link 路径。
  2. 环境变量 LD_LIBRARY_PATH 中的路径。
  3. /etc/ld.so.cache 中缓存的路径(通过 ldconfig 更新)。
  4. 默认路径:/lib, /usr/lib, /lib64, /usr/lib64 等。

示例:临时添加库路径:

export LD_LIBRARY_PATH=/path/to/libs:$LD_LIBRARY_PATH
./main

3. 符号可见性控制

默认情况下,动态库会导出所有全局符号。可通过编译选项或源码属性限制符号导出,避免污染全局命名空间。

方法1:编译时隐藏符号
使用 -fvisibility=hidden,然后显式导出需要的符号:

// 在源码中声明导出符号
__attribute__((visibility("default"))) void public_func() { ... }

方法2:版本脚本
使用链接器脚本 libmylib.version 控制符号可见性:

gcc -shared -o libmylib.so mylib.o -Wl,--version-script=libmylib.version

脚本内容:

VERS_1.0 { 
  global: public_func; 
  local: *; 
};

4. 动态库的版本控制

通过 soname(Shared Object Name)管理兼容性:

# 编译时指定 soname
gcc -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1.0 mylib.o

# 创建软链接供程序查找
ln -s libmylib.so.1.0 libmylib.so.1
ln -s libmylib.so.1 libmylib.so
  • 主版本号(Major)libmylib.so.1,表示二进制兼容性。
  • 次版本号(Minor)libmylib.so.1.0,表示新增功能但兼容。
  • 发布版本(Release):可选,如 libmylib.so.1.0.0

5. 动态库的初始化与清理函数

可在库中定义构造函数(加载时执行)和析构函数(卸载时执行):

__attribute__((constructor)) void init() {
    printf("Library loaded!\n");
}

__attribute__((destructor)) void cleanup() {
    printf("Library unloaded!\n");
}

6. 调试工具

  • ldd:查看程序的动态库依赖关系。
    ldd ./main
    
  • nm:列出库中的符号。
    nm -D libmylib.so
    
  • readelf:查看 ELF 文件详细信息。
    readelf -d libmylib.so  # 查看动态段(DT_NEEDED 等)
    
  • LD_DEBUG:启用动态链接器的调试输出。
    LD_DEBUG=files,libs ./main
    

7. 安全性注意事项

  • LD_PRELOAD:强制优先加载指定库,可用于劫持函数(慎用)。
    LD_PRELOAD=/path/to/malicious.so ./main
    
  • 符号冲突:若两个动态库导出同名符号,先加载的符号会被使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/965792.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实验3 词法分析(二)

实验3 词法分析(二) [实验目的]&#xff1a; 1 . 熟悉给定的词法分析程序&#xff1b; 2 . 改进词法分析程序。 [实验内容]&#xff1a; 1.尝试多方面改进TEST语言的文法&#xff0c;参考教材附录B词法分析程序TESTscan.c&#xff0c;在此词法分析程序的基础上改进程序&#x…

[创业之路-286]:《产品开发管理-方法.流程.工具 》-2- 人的管理是任何组织首要解决的问题 - 企业与研发组织组成、构架、组织分工

目录 一、产品开发的部门组成&#xff08;系统关键组成要素&#xff09; 1、产品开发中的市场规划部门与研发内部的市场/产品/技术预研部门的职责区别&#xff1a; 2、研发的分类&#xff1a;技术预研、平台开发、产品开发 相同点 差异点 相互联系 二、研发的组织架构 1…

使用jmeter进行压力测试

使用jmeter进行压力测试 jmeter安装 官网安装包下载&#xff0c;选择二进制文件&#xff0c;解压。 tar -xzvf apache-jmeter-x.tgz依赖jdk安装。 yum install java-1.8.0-openjdk环境变量配置&#xff0c;修改/etc/profile文件&#xff0c;添加以下内容。 export JMETER/…

matlab simulink LNG广义预测控制

1、内容简介 略 matlab simulink 120-LNG广义预测控制 可以交流、咨询、答疑 2、内容说明 略 模型分为2部分&#xff0c;一部分是simulink的结果&#xff0c;用的是pid和模糊pid控制&#xff0c;第二个模型考虑到代码计算的方便性&#xff0c;采用的m文件做仿真&#xff0…

git submodule使用

git submodule 用于关联其他独立的仓库。 它有着几点好处&#xff1a; 代码复用&#xff1a;可以将工具代码放到单独的仓库&#xff0c;再通过 submodule 关联。模块化开发&#xff1a;可以将项目拆分成多个模块&#xff0c;每个模块设置单独仓库独立开发&#xff0c;再通过 su…

python怎么求 一个数是否包含3

python求一个数包含3的方法&#xff1a; 1、使用“for i in 列表名”循环遍历列表中的每一个元素并将每个元素用str()函数转换成字符串格式 2、用“if str(3) in i”判断该元素中是否含有3 完整代码如下&#xff1a; 执行结果如下&#xff1a;

数据库系统概念第六版记录 三

外码约束&#xff08;Foreign Key Constraint&#xff09; 外码&#xff08;Foreign Key, FK&#xff09;是关系数据库中的一个约束&#xff0c;它用于保证表之间的引用完整性。外码的值必须&#xff1a; 要么存在于被引用表的主键列中&#xff0c;要么为空&#xff08;NULL&…

修改SSH登录密码,只需要登录SSH,之后输入命令即可,这里登录使用的软件为 MobaXterm1

在登入终端之后输入命令 passwd {用户名} 即可进行修改。需要注意的是&#xff0c;输入的密码不会有星号代替&#xff0c;也不会出现明文。 如果想要修改SSH的登录端口&#xff0c;比如修改为1433&#xff0c;则只需要执行以下命令即可&#xff1a; /usr/sbin/sshd -p 1433…

电商平台的设计与实现(代码+数据库+LW)

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统商品交易信息管理难度大&#xff0c;容错率低&#xff0…

【R语言】plyr包和dplyr包

一、plyr包 plyr扩展包主要是实现数据处理中的“分割-应用-组合”&#xff08;split-apply-combine&#xff09;策略。此策略是指将一个问题分割成更容易操作的部分&#xff0c;再对每一部分进行独立的操作&#xff0c;最后将各部分的操作结果组合起来。 plyr扩展包中的主要函…

oscp备考,oscp系列——VulnOSv2靶场,两种方法获取低权限shell

前言 oscp备考&#xff0c;oscp系列——VulnOSv2靶场&#xff0c;两种方法获取低权限shell 难度简单 对于低权限shell获取涉及&#xff1a;drupal 7 getshell漏洞&#xff0c;opendocman sql注入&#xff0c;ssh连接对于提权&#xff1a;内核提权 下载地址&#xff1a; http…

东方财富股吧发帖与评论爬虫

东方财富股吧发帖与评论爬虫 东方财富股吧爬虫 写在开头项目介绍主要功能文件介绍爬取逻辑 a. 爬取帖子信息b. 爬取评论信息 使用步骤 1. 下载代码2. MongoDB 安装3. Webdriver 安装4. 运行 main.py5. 查看数据 踩过的坑附录&#xff08;运行结果&#xff09; 东方财富股吧爬…

wxWidgets生成HTML文件,带图片转base64数据

编译环境大家可以看我之前的文章,CodeBlocks + msys2 + wx3.2,win10 这里功能就是生成HTML文件,没用HTML库,因为是自己固定的格式,图片是一个vector,可以动态改变数量的。 效果如下: #include <wx/string.h> #include <wx/file.h> #include <wx/ima…

网络编程 day2

题目 代码 服务器 typedef char DataType[32]; //普通节点数据类型typedef struct NODE {union{DataType data; //普通节点数据域int len; //头节点数据域};struct NODE *next; //指针域 }node,*nodePtr;struct PACK {int size; //告知 通信传输的数据的大小int type; //决定…

【鸿蒙HarmonyOS Next实战开发】实现ArkTS/JS和C/C++的交互-Node-API

一、HarmonyOS Node-API简介 在HarmonyOS应用开发中&#xff0c;通常以ArkTS/JS语言为主&#xff0c;但在一些特殊场景下&#xff0c;例如游戏开发、物理模拟等&#xff0c;由于对性能、效率等有较高要求&#xff0c;需要借助现有的C/C库来实现。为了满足这种需求&#xff0c;…

Postgresql的三种备份方式_postgresql备份

这种方式可以在数据库正在使用的时候进行完整一致的备份&#xff0c;并不阻塞其它用户对数据库的访问。它会产生一个脚本文件&#xff0c;里面包含备份开始时&#xff0c;已创建的各种数据库对象的SQL语句和每个表中的数据。可以使用数据库提供的工具pg_dumpall和pg_dump来进行…

51单片机之使用Keil uVision5创建工程以及使用stc-isp进行程序烧录步骤

一、Keil uVision5创建工程步骤 1.点击项目&#xff0c;新建 2.新建目录 3.选择目标机器&#xff0c;直接搜索at89c52选择&#xff0c;然后点击OK 4.是否添加起吊文件&#xff0c;一般选择否 5.再新建的项目工程中添加文件 6.选择C文件 7.在C文件中右键&#xff0c;添加…

基础篇05-直方图操作

本节将简要介绍Halcon中有关图像直方图操作的算子&#xff0c;重点介绍直方图获取和显示两类算子&#xff0c;以及直方图均衡化处理算子。 目录 1. 引言 2. 获取并显示直方图 2.1 获取&#xff08;灰度&#xff09;直方图 (1) gray_histogram (2) gray_histo_abs (3) gr…

3.攻防世界 weak_auth

题目描述提示 是一个登录界面&#xff0c;需要密码登录 进入题目页面如下 弱口令密码爆破 用1 or 1 #试试 提示用admin登录 则尝试 用户名admin密码&#xff1a;123456 直接得到flag 常用弱口令密码&#xff08;可复制&#xff09; 用户名 admin admin-- admin or -- admin…

金蛇祈福,鸿运开年!广州白云皮具城2025开市大吉!

锣鼓一响&#xff0c;黄金万两&#xff01;2月6日大年初九&#xff0c;广州白云皮具城举行盛大的醒狮开市仪式&#xff01;象征吉祥如意的醒狮&#xff0c;将好运、财运传递给全体商户和八方来客。 醒狮点睛 金鼓一响黄金万两&#xff0c;十头醒狮登台&#xff0c;董事总经理刘…