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

By Greg Kroah-Hartman / 繁體中文翻譯: Jim Huang (黃敬群) - jserv AT kaffe.org -
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 0x8061100calling 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 0x8061120calling 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 0x8061120calling 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].

Links

[1] http://www.linuxjournal.com/

Source URL: http://www.linuxjournal.com/article/7795

Modifying a Dynamic Library Without Changing the Source Code相关推荐

  1. Could not load dynamic library ‘cudart64_101.dll‘

    tenforboard用到了tensorflow 2020-09-18 22:59:53.641152: W tensorflow/stream_executor/platform/default/d ...

  2. dso_loader.cc:55] Could not load dynamic library ‘cudart64_100.dll‘

    dso_loader.cc:55] Could not load dynamic library 'cudart64_100.dll' dso_loader.cc:55] Could not load ...

  3. tensorflow: Could not load dynamic library ‘cudart64_101.dll‘ 解决办法

    问题描述: 安装 tensorfolw-gpu2.1.0 之后调用 显示 GPU False,不能调用GPU 在命令行中 import tensorflow 时显示  " Could not ...

  4. "unable to load dynamic library"错误的解决方法

    1.  问题的描述 今天php的mysql突然不好用了.先后提示下面的错误信息: 没有找到 libmysql.dll 因此这个应用程序未能启动 apache php startup: Unable t ...

  5. Tensorflow——[Could not load dynamic library cudart64_101.dll]解决方案

    问题描述 Could not load dynamic library 'cudart64_101.dll'; dlerror: cudart64_101.dll not found Ignore a ...

  6. 解决Could not load dynamic library ‘cudnn64_7.dll‘; dlerror cudnn64_7.dll not found

    解决Could not load dynamic library 'cudnn64_7.dll'; dlerror: cudnn64_7.dll not found 如果你使用的是tensorflow ...

  7. 再次遇到的问题:Unable to load dynamic library myext.so: undefined symbol

    2019独角兽企业重金招聘Python工程师标准>>> google到了这里 http://stackoverflow.com/questions/7271792/php-warni ...

  8. cmd运行php文件以及环境配置出现的问题、 php.exe不是内部或外部命令,也不是可运行的程序 或批处理文件、PHP startup: Unable to load dynamic library

    我用php.exe远行php文件出现了几个问题,先说一下怎么解决这些问题的,然后再说怎么运行 首先是出现 'php.exe' 不是内部或外部命令,也不是可运行的程序 或批处理文件,查了一下,是没有配置 ...

  9. php load dynamic library,PHP startup: Unable to load dynamic library错误的错误

    怪啦!今天的Apache和IIS都没法正确加载php_mysql.dll.google了一下,原来发现出现这个问题的人还不少,PHP startup: Unable to load dynamic l ...

  10. dll文件 修改特征码_Could not load dynamic library cublas64_10.dll

    在按照 dragen1860/Deep-Learning-with-TensorFlow-book 一书安装tensorflow环境时遇到如下如所示的could not load dynamic li ...

最新文章

  1. 【CyberSecurityLearning 30】Linux操作系统的用户和组、文件及目录权限
  2. python 任务调度 celery_python任务调度模块celery(二)
  3. 健康饮食五谷杂粮系列PPT模板
  4. 一分钟了解:zigbee的三种关键角色(视频讲解)
  5. vue中获取输入框中得到值_如何获取vue input的值
  6. H5混合开发二维码扫描以及调用本地摄像头
  7. Kotlin的魔能机甲——KtArmor插件篇(二)
  8. java map存储格式_java HashMap HashSet的存储方式
  9. python贪吃蛇设计思路_Python深度剖析贪吃蛇游戏的设计与实现
  10. cmmi实践访谈测试ppt_CMMI3_实践篇.ppt
  11. 游戏开发计划——数据元素设计(技能)
  12. 业界 | 数据科学家要先学逻辑回归?图样图森破!
  13. 图像超分之——寻找两张图差异的区域
  14. Windows 10 21H1 官方正式版下载
  15. 今日头条前端面试总结
  16. 构建一个额外的语义网络进行关键词生成
  17. bzoj 2876: [Noi2012]骑行川藏 拉格朗日数乘
  18. 三种方法进行图片锐化
  19. 小班安全优质课教案《防止拥挤踩踏事故》
  20. OpenGauss一主一从搭建(全网最详细,最简单)

热门文章

  1. 慕课网仿去哪儿项目笔记--(五)-详情页面的开发
  2. shell编程四剑客之 grep
  3. python安装scrapy教程_Python实用工具包Scrapy安装教程
  4. 超能搜索系统输入关键字_电商后台设计——搜索
  5. 测视力距离5米还是3米_高度近视眼怎么恢复视力?
  6. oracle 字段对错,oracle 两表之间字段赋值错误解析
  7. c++ 应输入表达式_【C语言编程入门系列】—— 第五章,C语言基本运算和表达式(一)...
  8. CMU 15-213 Introduction to Computer Systems学习笔记(8) Machine-Level Programming-Advanced
  9. java统计单机次数_java流类,快速统计出字符次数+++
  10. Java虚拟机类加载机制--类加载的过程详解