青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Just enjoy programming

技巧:多共享動態庫中同名對象重復析構問題的解決方法(轉載)

Linux 支持的共享程序庫(lib*.so)技術不僅能夠有效利用系統資源,而且還對程序設計帶來了很大的便利性、通用性等,因此被各種級別的應用系統廣泛采用。 動態鏈接的共享庫是在加載應用程序時被加載的,而且它與應用程序是在運行時綁定的:通過動態鏈接器,將動態共享庫映射進應用程序的可執行內存中(動態鏈接);在啟動應用程序時,動態裝載器將所需的共享目標庫映射到應用程序的內存(動態裝載)。

在通常情況下,共享庫都是通過使用附加選項 -fpic 或 -fPIC 進行編譯,從目標代碼產生位置無關的代碼(Position Independent Code,PIC),使用 -shared選項將目標代碼放進共享目標庫中。位置無關代碼需要能夠被加載到不同進程的不同地址,并且能得以正確的執行,故其代碼要經過特別的編譯處理:位置無關代碼(PIC)對常量和函數入口地址的操作都是采用基于基寄存器(base register)BASE+ 偏移量的相對地址的尋址方式。即使程序被裝載到內存中的不同地址,即 BASE 值不同,而偏移量是不變的,所以程序仍然可以找到正確的入口地址或者常量。

然而,當應用程序鏈接了多個共享庫,如果在這些共享庫中,存在相同作用域范圍的同名靜態成員變量或者同名 ( 非靜態 ) 全局變量,那么當程序訪問完靜態成員變量或全局變量結束析構時,由于某內存塊的 double free 會導致 core dump,這是由于 Linux 編譯器的缺陷造成的。

應用場景原型

該問題源于筆者所從事的開發項目:IBM Tivoli Workload Scheduler (TWS) LoadLevelerLoadLeveler是 IBM在高性能計算(High Performance Computing,HPC)領域的一款作業調度軟件。它主要分為兩個大的模塊,分別是調度模塊(scheduler)和資源管理模塊(resource manger)。 兩個模塊中分別含有關于配置管理功能的共享庫,由于某些配置管理選項為兩模塊所共同采用,所以兩模塊之間共享了部分源文件代碼,其中包含有同名的類靜態成員。

可以通過以下簡單的模型進行描述:


圖 1. 應用場景
圖片示例 

對應的各模塊代碼片段如下圖所示:


圖 2. 應用場景模擬代碼
圖片示例 

其中,test.c 是主程序,包含有兩個頭文件:api1.h 與 api2.h;頭文件 api1.h 包含頭文件 lib1/lib.h 和一功能函數 func_api1(),api2.h 包含頭文件 lib2/lib.h 和一功能函數 func_api2();目錄 lib1 和 lib2 下的源文件分別編譯生成共享庫 lib1.so 和 lib2.so。同時,頭文件 lib1/lib.h 與 lib2/lib.h 鏈接到同一共享文件 lib.h。在文件 lib.h 中定義有一靜態成員變量“static std::vector<int> vec_int”。

回頁首

功能函數與各靜態成員函數代碼清單

功能函數 func_api1() 與 func_api2() 的實現類似,通過調用靜態成員函數達到訪問靜態成員變量 vec_int的目的:


清單 1. 功能函數 func_api1(int)
          void func_api1(int i) {      printf("%s.\n", __FILE__);       A::set(i);      A::print();      return;   }      

靜態成員函數 A::set() 與 A::print() 的實現如下:


清單 2. 靜態成員函數 A::set(int)
          void A::set(int num) {      vec_int.clear();      for (int i = 0; i < num; i++) {          vec_int.push_back(i);      }      return;   }      


清單 3. 靜態成員函數 A::print()
          void A::print() {      for (int i = 0; i < vec_int.size(); i++) {          printf("vec_int[%d] = %d, addr: %p.\n", i, vec_int[i], &vec_int[i]);      }      printf("vec_int addr: %p.\n", &vec_int);      return;   }      

A::set() 對靜態成員 vec_int進行賦值操作,而 A::print() 則打印其中的值與當前項的內存地址。

回頁首

運行結果

如果兩個共享庫是通過選項 -fpic或 -fPIC編譯的話,運行程序 test,輸出如下:


清單 4. 選項 -fPIC 的測試結果
          $ export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH   $ g++ -g -o lib1.so -fPIC-rdynamic -shared lib1/lib.c   $ g++ -g -o lib2.so -fPIC-rdynamic -shared lib2/lib.c   $ g++ -g -o test -L./ -l1 -l2 test.c   $ ./test  api1.h.   vec_int[0] = 0, addr: 0x9cbf028.   vec_int[1] = 1, addr: 0x9cbf02c.   vec_int[2] = 2, addr: 0x9cbf030.   vec_int[3] = 3, addr: 0x9cbf034.   vec_int addr: 0xe89228.   *** glibc detected *** ./test: double free or corruption (fasttop): 0x09cbf028***   ======= Backtrace:=========   /lib/libc.so.6[0x2b2b16]   /lib/libc.so.6(cfree+0x90)[0x2b6030]   /usr/lib/libstdc++.so.6(_ZdlPv+0x21)[0x5d1731]   ./lib1.so(_ZN9__gnu_cxx13new_allocatorIiE10deallocateEPij+0x1d)[0xe88417]         ./lib1.so(_ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPij+0x33)[0xe88451]         ./lib1.so(_ZNSt12_Vector_baseIiSaIiEED2Ev+0x42)[0xe8849a]         ./lib1.so(_ZNSt6vectorIiSaIiEED1Ev+0x60)[0xe8850c]  ./lib2.so[0x961d6c]   /lib/libc.so.6(__cxa_finalize+0xa9)[0x275c79]   ./lib2.so[0x961c34]   ./lib2.so[0x962d3c]   /lib/ld-linux.so.2[0x23a7de]   /lib/libc.so.6(exit+0xe9)[0x2759c9]   /lib/libc.so.6(__libc_start_main+0xe4)[0x25fdf4]   ./test(__gxx_personality_v0+0x45)[0x80484c1]   ======= Memory map:========   ......   00960000-00963000 r-xp 00000000 00:1b 7668734    ./lib2.so   00963000-00964000 rwxp 00003000 00:1b 7668734    ./lib2.so   00970000-00971000 r-xp 00970000 00:00 0          [vdso]   00e86000-00e89000 r-xp 00000000 00:1b 7668022    ./lib1.so   00e89000-00e8a000 rwxp 00003000 00:1b 7668022    ./lib1.so  08048000-08049000 r-xp 00000000 00:1b 7668748    ./test   08049000-0804a000 rw-p 00000000 00:1b 7668748    ./test   09cbf000-09ce0000 rw-p 09cbf000 00:00 0          [heap]  ......   Abort(coredump)   $      

從程序的輸出直觀的看到,core 產生是由于堆內存區域(09cbf000-09ce0000)中起始地址為 0x09cbf028的內存區被釋放了兩次導致的,該地址正式靜態成員變量 vec_int的第一個元素的地址。

為什么會出現同一塊內存區,被釋放兩次的情形呢?

回頁首

原因分析

我們知道,靜態成員變量與全局變量類似,都采用了靜態存儲方式。對于加了選項 -fpic或 -fPIC的共享庫,這些變量的地址都存放在該共享庫的全局偏移表(Global Offset Table,GOT)中。

通過 objdump或者 readelf命令分析共享庫 lib1.so,結果如下:


清單 5. objdump 分析共享庫 lib1.so 的輸出
          $ objdump -x -R lib1.so    lib1.so:     file format elf32-i386   ......   Sections:   Idx Name          Size      VMA       LMA       File off  Algn    0 .gnu.hash     000001e8  000000d4  000000d4  000000d4  2**2                    CONTENTS, ALLOC, LOAD, READONLY, DATA   ......   18 .dynamic      000000d8  0000301c  0000301c  0000301c  2**2                    CONTENTS, ALLOC, LOAD, DATA   19 .got          00000014  000030f4  000030f4  000030f4  2**2                   CONTENTS, ALLOC, LOAD, DATA   20 .got.plt      00000114  00003108  00003108  00003108  2**2                    CONTENTS, ALLOC, LOAD, DATA   ......   DYNAMIC RELOCATION RECORDS   OFFSET   TYPE              VALUE   ......   000030f4 R_386_GLOB_DAT    __gmon_start__   000030f8 R_386_GLOB_DAT    _Jv_RegisterClasses   000030fc R_386_GLOB_DAT    _ZN1A7vec_intE  00003104 R_386_GLOB_DAT    __cxa_finalize   ......      


清單 6. readelf 分析共享庫 lib1.so 的輸出
          $ objdump -x -R lib1.so    lib1.so:     file format elf32-i386   ......   Sections:   Idx Name          Size      VMA       LMA       File off  Algn    0 .gnu.hash     000001e8  000000d4  000000d4  000000d4  2**2                    CONTENTS, ALLOC, LOAD, READONLY, DATA   ......   18 .dynamic      000000d8  0000301c  0000301c  0000301c  2**2                    CONTENTS, ALLOC, LOAD, DATA   19 .got          00000014  000030f4  000030f4  000030f4  2**2                   CONTENTS, ALLOC, LOAD, DATA   20 .got.plt      00000114  00003108  00003108  00003108  2**2                    CONTENTS, ALLOC, LOAD, DATA   ......   DYNAMIC RELOCATION RECORDS   OFFSET   TYPE              VALUE   ......   000030f4 R_386_GLOB_DAT    __gmon_start__   000030f8 R_386_GLOB_DAT    _Jv_RegisterClasses   000030fc R_386_GLOB_DAT    _ZN1A7vec_intE  00003104 R_386_GLOB_DAT    __cxa_finalize   ......      

從上面兩個命令的輸出結果中可以看出,共享庫 lib1.so中 GOT段的起始內存地址為 000030f4,大小為 20 字節 (0x14);靜態成員變量 vec_int在共享庫 lib1.so中的起始偏移地址為 000030fc。顯然,vec_int位于該共享庫的 GOT段內。

當應用程序同時鏈接 lib1.so和 lib2.so時,同名靜態成員變量 vec_int分別位于其共享庫的 GOT區。當程序運行時,系統從符號表中查找并裝載構造一份 vec_int數據,這點從程序運行的輸出結果(清單 4)的“Backtrace”部分可以看到:只有 lib1.so中的靜態成員變量被裝載構造;同時,通過內存映射(Memory map)部分(清單 4),可以觀察到 vec_int對象的地址 0xe89228正好處在為共享庫 lib1.so分配的可讀內存區 00e89000-00e8a000中:

        00e89000-00e8a000 rwxp 00003000 00:1b 7668022    ./lib1.so

然后,當程序結束時,卻對該變量進行了兩次析構操作,通過 gdb分析 core 文件:


清單 7. core 文件分析結果
          $ gdb ./test core.28440 ……  Core was generated by `./test'.   Program terminated with signal 6, Aborted.   #0  0x00970402 in __kernel_vsyscall ()   (gdb)   (gdb) where   #0  0x00970402 in __kernel_vsyscall ()   #1  0x00272d10 in raise () from /lib/libc.so.6   #2  0x00274621 in abort () from /lib/libc.so.6   #3  0x002aae5b in __libc_message () from /lib/libc.so.6   #4  0x002b2b16 in _int_free () from /lib/libc.so.6   #5  0x002b6030 in free () from /lib/libc.so.6   #6  0x005d1731 in operator delete () from /usr/lib/libstdc++.so.6   #7  0x00e88417 in __gnu_cxx::new_allocator<int>::deallocate       (this=0xe89228, __p=0x9cbf028)      at /usr/lib/gcc/i386-redhat-linux/.../ext/new_allocator.h:94   #8  0x00e88451 in std::_Vector_base<int, ... (this=0xe89228, __p=0x9cbf028, __n=4)      at /usr/lib/gcc/.../include/c++/4.1.2/bits/stl_vector.h:133   #9  0x00e8849a in ~_Vector_base (this=0xe89228)      at /usr/lib/gcc/.../include/c++/4.1.2/bits/stl_vector.h:119   #10 0x00e8850cin ~vector (this=0xe89228) at /usr/lib/gcc/.../stl_vector.h:272   #11 0x00961d6c in __tcf_0 () at lib2/lib.c:3   #12 0x00275c79 in __cxa_finalize () from /lib/libc.so.6   #13 0x00961c34 in __do_global_dtors_aux () from ./lib2.so   #14 0x00962d3c in _fini () from ./lib2.so  #15 0x0023a7de in _dl_fini () from /lib/ld-linux.so.2   #16 0x002759c9 in exit () from /lib/libc.so.6   #17 0x0025fdf4 in __libc_start_main () from /lib/libc.so.6   #18 0x080484c1 in _start ()   (gdb)      

從清單 7 中可以看出,從幀 #14 開始,程序進行 lib2.so中的析構操作,直到 #11,都運行在 lib2.so中,當進入幀 #10 時,進行變量析構時,其地址為 0x00e8850c,該地址中的對象是程序啟動時由共享庫 lib1.so裝載構造出來的(清單 1):

        ./lib1.so(_ZNSt6vectorIiSaIiEED1Ev+0x60)[0xe8850c]

當程序結束時,運行庫 glibc檢測到共享庫 lib2.so析構了并非由其構造的對象,導致了 core dump。

這種情況下,如果替換使用選項 -fpie或 -fPIE,操作步驟與運行結果如下所示:


清單 8. 選項 -fPIE 的測試結果
          $ export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH   $ g++ -g -o lib1.so -fPIE-rdynamic -shared lib1/lib.c   $ g++ -g -o lib2.so -fPIE-rdynamic -shared lib2/lib.c   $ g++ -g -pie -o test -L./ -l1 -l2 test.c   $ ./test  api1.h.   vec_int[0] = 0, addr: 0x80e3028.   vec_int[1] = 1, addr: 0x80e302c.   vec_int[2] = 2, addr: 0x80e3030.   vec_int[3] = 3, addr: 0x80e3034.   vec_int addr: 0x75e224.   $      

程序運行結果符合期望并正常結束。

這是因為,當使用選項 -fpie或 -fPIE時,生成的共享庫不會為靜態成員變量或全局變量在 GOT中創建對應的條目(通過 objdumpreadelf命令可以查看,此處不再贅述),從而避免了由于靜態對象“構造一次,析構兩次”而對同一內存區域釋放兩次引起的程序 core dump。

選項 -fpie和 -fPIE與 -fpic及 -fPIC的用法很相似,區別在于前者總是將生成的位置無關代碼看作是屬于程序本身,并直接鏈接進該可執行程序,而非存入全局偏移表 GOT中;這樣,對于同名的靜態或全局對象的訪問,其構造與析構操作將保持一一對應。

回頁首

結束語

通過使用選項 -fpie或 -fPIE代替 -fpic或者 -fPIC,使得生成的共享庫不會為靜態成員變量或全局變量在 GOT中創建對應的條目,同時也就避免了針對同名靜態對象“構造一次,析構兩次”的不當操作。

轉自:http://www.ibm.com/developerworks/cn/linux/l-cn-sdlstatic/ 





posted on 2012-03-02 16:04 周強 閱讀(455) 評論(0)  編輯 收藏 引用 所屬分類: linux

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美搞黄网站| 久久精品道一区二区三区| 日韩一区二区久久| 亚洲老板91色精品久久| 亚洲精品日日夜夜| 一区二区三区av| 香蕉成人伊视频在线观看| 欧美亚洲在线| 美女脱光内衣内裤视频久久网站| 久久综合狠狠综合久久综青草| 女同性一区二区三区人了人一| 亚洲第一在线综合网站| 久久一区视频| 亚洲欧洲一二三| 亚洲主播在线| 久久中文欧美| 欧美日韩少妇| 国产有码一区二区| 国产一区二区三区在线免费观看 | 欧美日韩国产高清视频| 欧美四级在线| 在线看无码的免费网站| 一本到高清视频免费精品| 欧美在线播放| 亚洲国产精品专区久久| 亚洲天堂男人| 欧美不卡高清| 国产一区二区三区免费不卡 | 国产精品蜜臀在线观看| 影音先锋中文字幕一区| 亚洲午夜激情网站| 欧美激情第10页| 欧美一区二区三区免费视| 欧美日韩成人综合| 精品1区2区| 欧美综合国产| 亚洲一区二区三| 欧美激情免费在线| 樱花yy私人影院亚洲| 久久成人免费视频| 亚洲伦理网站| 欧美大片免费观看| 在线观看欧美| 久热精品在线| 小处雏高清一区二区三区 | 国产精品卡一卡二| 99国产精品视频免费观看| 卡通动漫国产精品| 欧美怡红院视频| 国产区日韩欧美| 性欧美大战久久久久久久免费观看 | 欧美性天天影院| 亚洲美洲欧洲综合国产一区| 免费观看国产成人| 久久精品一区二区| 黄色亚洲精品| 麻豆国产va免费精品高清在线| 午夜精品久久久久久久99热浪潮| 国产精品高清网站| 亚洲综合另类| 亚洲主播在线播放| 国产亚洲激情视频在线| 久久久久久999| 久久久久久一区| 红桃视频亚洲| 欧美成人精品| 欧美日本国产视频| 亚洲在线视频观看| 亚洲自拍啪啪| 狠狠色狠狠色综合日日tαg| 久久综合久久久久88| 久久亚洲精选| 日韩一级免费观看| 在线视频欧美一区| 国内偷自视频区视频综合| 欧美夫妇交换俱乐部在线观看| 免费高清在线视频一区·| 亚洲国产日韩欧美在线动漫| 最新日韩在线| 国产精品久久久久久av下载红粉| 香蕉成人伊视频在线观看| 久久精品国产综合精品| 亚洲精品久久久久久久久久久久 | 亚洲精品精选| 亚洲一级电影| 亚洲成人影音| 99亚洲一区二区| 国产一区二区日韩| 亚洲二区在线| 国产欧美日韩激情| 亚洲国产欧美久久| 国产欧美一区二区视频| 欧美sm视频| 国产精品免费看片| 欧美国产先锋| 国产亚洲成av人在线观看导航| 欧美高清在线一区| 国产精品一区二区你懂得| 欧美 亚欧 日韩视频在线| 欧美视频一区二区三区四区| 麻豆成人在线播放| 国产精品午夜视频| 最新日韩中文字幕| 狠狠色丁香婷婷综合久久片| 99精品视频免费全部在线| 国产一区在线免费观看| 亚洲精品在线观看免费| 欧美国产日韩精品| 国产精品综合不卡av| 欧美国产日韩xxxxx| 国产精品亚洲不卡a| 欧美国产日韩精品| 韩日精品在线| 亚洲欧美视频一区| 中文在线资源观看网站视频免费不卡| 欧美自拍偷拍| 香蕉久久一区二区不卡无毒影院 | 国产欧美日韩综合一区在线播放| 亚洲国产成人高清精品| 国产亚洲成av人片在线观看桃| 日韩亚洲一区二区| 亚洲精品中文字幕在线观看| 久久精品视频99| 久久九九久精品国产免费直播| 欧美性大战久久久久| 亚洲精品日韩欧美| aa日韩免费精品视频一| 欧美xart系列高清| 亚洲国产成人不卡| 日韩一级二级三级| 欧美日产一区二区三区在线观看| 欧美高清一区二区| 亚洲国产精品久久久久婷婷老年| 久久国产精品久久久久久| 久久激情视频免费观看| 国产日本欧美一区二区三区在线 | 欧美精品 国产精品| 亚洲成色777777女色窝| 伊人蜜桃色噜噜激情综合| 久久精品国产91精品亚洲| 久久一本综合频道| 亚洲高清在线精品| 欧美xx69| 妖精视频成人观看www| 一区二区久久久久| 欧美视频福利| 亚洲综合电影| 久久精品主播| 在线观看视频免费一区二区三区| 久久成人精品视频| 欧美大片一区二区| 一本高清dvd不卡在线观看| 国产精品v亚洲精品v日韩精品 | 精品动漫3d一区二区三区| 久久精品日韩| 亚洲欧洲精品一区二区三区| 夜夜嗨av色综合久久久综合网| 欧美日韩一区二区高清| 亚洲欧美日韩爽爽影院| 麻豆av一区二区三区久久| 亚洲毛片一区二区| 国产伦精品一区二区三区照片91| 久久精品一区二区三区四区| 亚洲国产一成人久久精品| 亚洲欧美不卡| 怡红院精品视频在线观看极品| 欧美成人免费一级人片100| 99视频+国产日韩欧美| 国产精品久久波多野结衣| 亚洲黄色免费| 欧美一进一出视频| 91久久精品一区二区三区| 欧美视频观看一区| 久久久视频精品| 一本久久a久久免费精品不卡| 久久黄色网页| 亚洲一区欧美| 亚洲激情综合| 国产欧美一区二区视频| 欧美国产日韩xxxxx| 午夜欧美精品久久久久久久| 亚洲国产成人porn| 久久亚洲美女| 午夜一区不卡| 一本色道久久综合精品竹菊| 国产一区在线观看视频| 欧美日韩中国免费专区在线看| 欧美一乱一性一交一视频| 日韩一级成人av| 欧美福利一区二区三区| 欧美一区二区三区精品电影| 一二美女精品欧洲| 亚洲高清自拍| 激情综合自拍| 国产亚洲aⅴaaaaaa毛片| 欧美日韩国产综合一区二区| 久久伊人精品天天| 欧美在线欧美在线| 亚洲在线中文字幕| 一区二区三区欧美在线观看|