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

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>
            国产伦一区二区三区色一情| 久久人人爽人人| 久久精品国产一区二区三区免费看 | 亚洲欧美日韩精品久久久久| 久久精品综合网| 国产精品乱码一区二区三区| 久久午夜精品| 亚洲自拍另类| 久久国产主播| 亚洲电影下载| 国产精品99久久久久久久vr| 99精品国产在热久久婷婷| 一区二区欧美激情| 先锋影音网一区二区| 久久九九全国免费精品观看| 老司机免费视频一区二区| 开元免费观看欧美电视剧网站| 欧美a一区二区| 亚洲精品乱码久久久久久蜜桃麻豆| 亚洲午夜一区二区| 久久精品国产99国产精品澳门| 久久国产精品免费一区| 欧美精品一区二区三区在线看午夜| 国产精品久久久久9999| 激情文学一区| 亚洲天堂视频在线观看| 玖玖在线精品| 亚洲一区日韩在线| 欧美电影免费观看大全| 国产欧美精品在线观看| 日韩网站在线| 美女精品在线| 亚洲女ⅴideoshd黑人| 嫩草成人www欧美| 国产精一区二区三区| 99在线热播精品免费99热| 亚洲在线网站| 亚洲欧洲日韩在线| 亚洲一区二区三区欧美| 欧美11—12娇小xxxx| 国产一区二区日韩| 美女黄毛**国产精品啪啪| 欧美日本网站| 亚洲美女av网站| 欧美成人精品高清在线播放| 欧美一级理论片| 国产精品久久福利| 国产视频一区二区三区在线观看| 日韩视频在线观看一区二区| 久久久久久久激情视频| 欧美xxx成人| 香蕉免费一区二区三区在线观看| 99re66热这里只有精品4| 欧美在线欧美在线| 欧美性色aⅴ视频一区日韩精品| 国产乱人伦精品一区二区 | 欧美激情视频一区二区三区免费 | 久久精品一区二区三区不卡| 美国十次成人| 老司机67194精品线观看| 亚洲一区二区三区四区五区黄| 亚洲免费精品| 1024成人网色www| 欧美黄色网络| 性久久久久久久久| 久久综合久久美利坚合众国| 久久婷婷影院| 国产视频观看一区| 亚洲一区二区三区免费观看| 亚洲黑丝在线| 久久综合久久综合九色| 欧美成人免费在线| 一区二区国产精品| 国产欧美欧洲在线观看| 亚洲精品久久久蜜桃| 国产精品成av人在线视午夜片| 91久久极品少妇xxxxⅹ软件| 亚洲一区在线观看免费观看电影高清| 国产精品久久久久久久久免费桃花| 欧美视频不卡| 国产一区二区三区久久久| 日韩一级精品视频在线观看| 国产精品日韩欧美| 香蕉免费一区二区三区在线观看 | 欧美一二三区精品| 樱花yy私人影院亚洲| 欧美激情精品久久久久久变态| 亚洲国产第一| 欧美激情网友自拍| 午夜国产精品影院在线观看| 欧美中文字幕在线| 亚洲精品免费网站| 亚洲无限av看| 亚洲午夜电影| 欧美va天堂va视频va在线| 久久er99精品| 欧美四级伦理在线| 一个色综合av| 亚洲欧美日韩电影| 欧美日韩免费精品| 欧美在现视频| 日韩亚洲一区二区| 欧美精品在线网站| 欧美成人a视频| 欧美成人官网二区| 久久精品综合网| 久久久久久999| 黄色亚洲在线| 久久精品亚洲乱码伦伦中文 | 亚洲成色精品| 欧美一区免费| 曰本成人黄色| 欧美一区二区啪啪| 久久av一区二区三区| 欧美劲爆第一页| 欧美午夜电影在线观看| 欧美三级韩国三级日本三斤| 欧美精品久久99| 免播放器亚洲| 久久久.com| 一区二区三区蜜桃网| 欧美1区免费| av成人免费在线| 欧美激情一区二区三区在线| 性欧美大战久久久久久久久| 亚洲免费视频一区二区| 午夜精品久久久久久久久 | 亚洲精选91| 久久国产精品亚洲va麻豆| 国产精品美女999| 欧美在线网站| 久久全球大尺度高清视频| 91久久精品国产91性色tv| 亚洲国产精品一区二区第一页 | 中文av字幕一区| 亚洲一区二区三区乱码aⅴ蜜桃女| 亚洲欧美视频一区| 亚洲电影免费观看高清完整版| 免费久久99精品国产| 国产一区二区高清不卡| 亚洲黄色一区二区三区| 亚洲一区久久久| 亚洲欧洲精品一区二区三区不卡 | 久久精品国产成人| 久久久久亚洲综合| 亚洲美女中出| 欧美精品入口| 日韩视频中文字幕| 夜夜嗨一区二区| 最新国产成人在线观看| 免费成人高清视频| 性欧美长视频| 久久久www免费人成黑人精品| 欧美激情一区二区三区在线视频观看 | 一区二区三区免费网站| 欧美va天堂在线| 久久久最新网址| 欧美亚洲一区在线| 久久精品首页| 久久精品一区二区三区不卡牛牛 | 欧美激情在线| 国产精品一级久久久| 性高湖久久久久久久久| 亚洲欧美日韩精品久久久久| 欧美体内谢she精2性欧美| 精品999久久久| 亚洲午夜免费视频| 久久亚洲精品一区| 99精品国产在热久久婷婷| 在线亚洲免费视频| 国产精品尤物| 亚洲视频欧洲视频| 性做久久久久久| 国产视频一区欧美| 久久免费高清| 亚洲免费在线精品一区| 免费日韩av电影| 国产视频亚洲精品| 亚洲免费av电影| 91久久线看在观草草青青| 欧美亚洲网站| 久久精品人人做人人综合 | 久久久久久9999| 亚洲在线播放电影| 免费在线欧美黄色| 欧美波霸影院| 亚洲一级片在线看| 国语自产偷拍精品视频偷| 亚洲专区一区| 久久成人一区二区| 国产午夜亚洲精品不卡| 久久久久久国产精品mv| 小黄鸭精品aⅴ导航网站入口| 国产精品美腿一区在线看| 一区二区免费在线播放| 美女精品国产| 亚洲素人一区二区| 91久久精品一区二区三区| 久久精品久久99精品久久| 国产一区二区高清视频|