Modifying a Dynamic Library Without Changing the Source Code

本文介绍如何通过设置LD_PRELOAD环境变量来插入自定义的动态库,从而覆盖原有库的行为,例如记录libusb库的调用情况。文中提供了具体的代码示例,展示如何实现对usb_open和usb_control_msg等函数的拦截。

LJ Logo

Modifying a Dynamic Library Without Changing the Source Code / 在不更動原始程式碼的前提下,修改動態程式庫

Created 2004-11-02 00:00 / 繁體中文翻譯時間: 2005-01-16
透過 LD_PRELOAD 環境變數來置放你自己的程式碼,是很簡單的動作

有時候你或許會想知道,在未修改程式庫 (library) 的前提下,動態程式庫會產生什麼結果 (你最近有試著建構 glibc 嗎?) 或者,你可能會想去覆寫 (override) 程式庫裡頭某些函式 (functions),以便會有不同的行為,比方說針對某個 CPU 作處理、避免某個特定的 USB 訊息被傳送,或者類似的操作。 這些動作都可以透過 LD_PRELOAD 環境變數來實現,你可以很簡單的置放你要的程式碼。

舉例來說,你可以建立一個共享程式庫,比方說,shim.so (譯註:"shim" 一詞在英文裡頭的意思就是夾在縫隙裡頭的墊片),你想要讓這個動態程式庫在其他共享程式庫之前被載入。 我們想執行的程式如果成為 "test" 的話,可以使用以下命令:


LD_PRELOAD=/home/greg/test/shim.so ./test

以上命令告知 glibc,預先從 /home/greg/test 目錄下去載入我們的 shim.so 動態程式庫,然後才去載入執行 ./test 所需的其他動態程式庫。

建立這個 shim 程式庫是相當簡單的,感謝 kernel 開發者 Anton Blanchard 的一封舊信,裡頭提及這樣的範例。

那我們就設計以紀錄 libusb 程式庫被其他程式呼叫的狀況,作為範例吧。libusb 是個 user space 的程式庫,可用以存取 USB 裝置的資訊,而不需要透過 kernel driver 來對 USB 裝置操作。

我們透過 libusb 提供的 usb_open() 函式作出發,以下的程式碼片段就是用以紀錄存取本函式的狀況:


usb_dev_handle *usb_open(struct usb_device *dev)
{
static usb_dev_handle *(*libusb_open)
(struct usb_device *dev) = NULL;
void *handle;
usb_dev_handle *usb_handle;
char *error;
if (!libusb_open) {
handle = dlopen("/usr/lib/libusb.so",
RTLD_LAZY);
if (!handle) {
fputs(dlerror(), stderr);
exit(1);
}
libusb_open = dlsym(handle, "usb_open");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s/n", error);
exit(1);
}
}
printf("calling usb_open(%s)/n", dev->filename);
usb_handle = libusb_open(dev);
printf("usb_open() returned %p/n", usb_handle);
return usb_handle;
}

可以用以下方式來編譯:


gcc -Wall -O2 -fpic -shared -ldl -o shim.so shim.c

以上指令從原始程式碼 shim.c,建立一個名為 shim.so 的共享程式庫。如果測試程式是透過前述 LD_PRELOAD 方式執行的話,那麼,測試程式中所有呼叫 usb_open() 函式的呼叫將會使用我們自己的函式庫實作 (譯註:也就是說採用 shim.c 裡面的實作,而非原本 libusb.so 裡頭的)。 在我的函式實作中,會透過以下 dlopen 呼叫的方式,試著載入真實的 libusb 程式庫:


handle = dlopen("/usr/lib/libusb.so",
RTLD_LAZY);

注意到 RTLD_LAZY 的參數,這是因為我不想要讓載入器立即去解析所有的 symbol。 相反的,我想要讓 shim.so 裡頭的程式碼需要時,才去作解析的動作。

如果 dlopen() 函式成功的話,程式會去索取指向真實 usb_open 函式的 pointer (譯註:也就是存放於 libusb.so 的 usb_open() 進入點),這是透過 dlsym 呼叫取得。


libusb_open = dlsym(handle, "usb_open");

如果以上呼叫成功的話,shim.so 的函式現在就擁有 libusb 裡頭函式真正的 pointer (進入點)。 這樣一來,可以在任何時間呼叫,為了我們的預期,我們會在作完記錄動作後呼叫,如下所示:


printf("calling usb_open(%p)/n", dev);
usb_handle = libusb_open(dev);

這也允許程式碼在(真實)函式呼叫後、但尚未返回原本的程式之前,作一些動作。

舉例來說,我們會希望 shim.so 提供以下的輸出方式:


calling usb_open(002)
usb_open() returned 0x8061100
calling usb_open(002)
usb_open() returned 0x8061100
calling usb_open(002)
usb_open() returned 0x8061100
calling usb_open(002)
usb_open() returned 0x8061100
calling usb_open(002)
usb_open() returned 0x8061120
calling usb_open(002)
usb_open() returned 0x8061120

為了記錄更複雜的函式,比方說 usb_control_message(),我們也套用之前加諸於 usb_open 的動作:


int usb_control_msg(usb_dev_handle *dev,
int requesttype,
int request,
int value,
int index,
char *bytes,
int size,
int timeout)
{
static int(*libusb_control_msg)
(usb_dev_handle *dev,
int requesttype, int request,
int value, int index, char *bytes,
int size, int timeout) = NULL;
void *handle;
int ret, i;
char *error;
if (!libusb_control_msg) {
handle = dlopen("/usr/lib/libusb.so", RTLD_LAZY);
if (!handle) {
fputs(dlerror(), stderr);
exit(1);
}
libusb_control_msg = dlsym(handle, "usb_control_msg");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s/n", error);
exit(1);
}
}
printf("calling usb_control_msg(%p, %04x, "
"%04x, %04x, %04x, %p, %d, %d)/n"
"/tbytes = ", dev, requesttype,
request, value, index, bytes, size,
timeout);
for (i = 0; i < size; ++i)
printf ("%02x ", (unsigned char)bytes[i]);
printf("/n");
ret = libusb_control_msg(dev, requesttype,
request, value,
index, bytes, size,
timeout);
printf("usb_control_msg(%p) returned %d/n"
"/tbytes = ", dev, ret);
for (i = 0; i < size; ++i)
printf ("%02x ", (unsigned char)bytes[i]);
printf("/n");
return ret;
}

透過 LD_PREDLOAD=shim.so 來執行測試程式,將會得到以下輸出:


usb_open() returned 0x8061100
calling usb_control_msg(0x8061100, 00c0, 0013, 6c7e, c41b, 0x80610a8, 8, 1000)
bytes = c9 ea e7 73 2a 36 a6 7b
usb_control_msg(0x8061100) returned 8
bytes = 81 93 1a c4 85 27 a0 73
calling usb_open(002)
usb_open() returned 0x8061120
calling usb_control_msg(0x8061120, 00c0, 0017, 9381, c413, 0x8061100, 8, 1000)
bytes = 39 83 1d cc 85 27 a0 73
usb_control_msg(0x8061120) returned 8
bytes = 35 15 51 2e 26 52 93 43
calling usb_open(002)
usb_open() returned 0x8061120
calling usb_control_msg(0x8061120, 00c0, 0017, 9389, c413, 0x8061108, 8, 1000)
bytes = 80 92 1b c6 e3 a3 fa 9d
usb_control_msg(0x8061120) returned 8
bytes = 80 92 1b c6 e3 a3 fa 9d

所以,我們知道,使用 LD_PRELOAD 環境變數可以讓我們很容易在動態程式庫之前,置放我們自己的實作。

Greg Kroah-Hartman currently is the Linux kernel maintainer for a variety of different driver subsystems. He works for IBM, doing Linux kernel-related things, and can be reached at greg@kroah.com [1].

<think>首先,用户的问题是:"aNOTHER INSTANCE OF SOURCE INSIGHT MAY BE MODIFYING IT"。这看起来像是一个错误信息或不完整的句子。可能是一个打字错误或截断的文本。我应该尝试理解它的含义。 从上下文看,"Source Insight" 可能指的是一个软件工具,通常用于代码编辑或分析。常见的软件是 "Source Insight",一个源代码编辑器。用户可能是在说另一个实例(instance)可能正在修改某个文件或资源。 所以,问题可能是:"Another instance of Source Insight may be modifying it",意思是"另一个 Source Insight 实例可能正在修改它"。这听起来像是一个警告或错误消息,可能发生在当用户尝试打开一个文件时,而另一个进程已经锁定了该文件。 我的任务是使用中文回答问题。所以,我需要用中文解释这个问题并提供可能的解决方案。 回顾规则: - 使用中文回答问题。 - 回答完问题后,必须提出3-5个相关问题。 - 相关问题必须以 "§§相关问题§§" 开头。 - 输出格式:回答内容后,直接跟 "§§相关问题§§",然后列出问题,每个问题以数字点开始。 - 禁止换行分散,所以我应该保持紧凑。 回答结构: 1. 解释问题:用中文解释用户的问题是什么意思。 2. 提供建议:给出如何解决这个问题的建议。 3. 最后,输出相关问题。 可能的建议: - 检查是否有另一个 Source Insight 实例正在运行。 - 关闭所有实例并重新打开。 - 检查文件是否被其他程序锁定。 - 使用任务管理器结束相关进程。 现在,为相关问题: - 需要3-5个相关问题,基于用户的问题和我的回答。 - 相关问题应该激发进一步讨论或解决相关问题。 - 例如: 1. 如何防止多个 Source Insight 实例同时修改同一个文件? 2. Source Insight 是否有设置来避免这种冲突? 3. 如果文件被锁定,如何强制解锁? 4. Source Insight 支持协同编辑功能吗? 5. 如何配置 Source Insight 以更好地管理文件锁定? 确保相关问题数量:至少3个,最多5个。 完整回答: - 先用中文解释和回答。 - 然后 "§§相关问题§§" - 接着1. ... 2. ... 3. ...(无换行分散,意思是一行输出整个部分,但数字点可以换行?规则说"禁止换行分散",但在示例中是每个问题在新行。示例是: §§相关问题§§ 1. ··· 2. ··· 3. ··· 所以,应该是 "§§相关问题§§" 后换行,然后每个问题在新行。但规则说"禁止换行分散",可能意味着不要有额外的空行或分散内容。示例中是有换行的,所以我认为可以这样输出。 规则:"第一行必须完整且输出 \"§§相关问题§§\"!禁止换行分散。" 意思可能是说 "§§相关问题§§" 必须在单独一行,且不能分散在其他内容中。然后问题可以列出。 在示例中: 快速排序的基本思想是... ```python code ``` §§相关问题§§ 1. ··· 2. ··· 3. ··· 所以,输出回答内容后,换行输出 "§§相关问题§§",然后换行列出问题。 现在,制定我的回答。 回答内容: 用户的问题似乎是关于 Source Insight 软件的一个错误消息:"Another instance of Source Insight may be modifying it"。这通常表示当您尝试打开或保存一个文件时,另一个 Source Insight 进程可能已经打开了该文件并锁定了它。 解决方案建议: 1. 检查任务管理器(Windows)或活动监视器(Mac)是否有多个 Source Insight 实例运行。结束所有实例后重新启动。 2. 确保文件没有在其他地方被占用,比如另一个编辑器或共享网络位置。 3. 重启计算机以释放任何锁。 4. 在 Source Insight 设置中查找文件锁定选项,尝试禁用或调整。 5. 如果问题持续,考虑使用文件解锁工具。 然后用中文写。 相关问题: 1. Source Insight 如何管理文件锁定机制? 2. 有没有办法在 Source Insight 中允许多个实例同时编辑同一个文件? 3. 如何避免在团队协作中出现这种文件冲突? 4. Source Insight 是否有自动检测和解决冲突的功能? 5. 如果文件被错误锁定,如何手动解锁? 确保3-5个,我可以选3或4个。 </think> 用户的问题 "aNOTHER INSTANCE OF SOURCE INSIGHT MAY BE MODIFYING IT" 似乎是 Source Insight 软件的一个常见错误消息,意思是“另一个 Source Insight 实例可能正在修改它”。这通常发生在您尝试打开或保存文件时,因为另一个正在运行的 Source Insight 进程已经锁定了该文件,导致冲突。Source Insight 是一个常用的源代码编辑工具,这种问题可能源于多个实例同时访问同一文件、文件权限问题或系统资源冲突。 为了解决这个问题,您可以尝试以下步骤: 1. **关闭所有 Source Insight 实例**:使用任务管理器(Windows)或活动监视器(Mac)结束所有 Source Insight 进程,然后重新启动软件。 2. **检查文件锁定**:确保文件没有被其他程序(如文本编辑器或 IDE)占用,或者尝试将文件复制到新位置再打开。 3. **重启计算机**:如果问题持续,重启系统可以释放文件锁。 4. **调整 Source Insight 设置**:在 Source Insight 的选项菜单中,禁用“文件锁定”或启用“共享编辑”功能(如果可用),以避免冲突。 5. **使用解锁工具**:如果文件被永久锁定,可以借助第三方工具如 Unlocker(Windows)强制解锁。 如果问题仍存在,建议更新 Source Insight 到最新版本或咨询官方支持,因为它可能涉及软件 bug 或配置错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值