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

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 周強 閱讀(453) 評論(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>
            免费一级欧美在线大片| 亚洲视频日本| 亚洲黄色av一区| 国产精品入口尤物| 免费在线国产精品| 免费久久精品视频| 老妇喷水一区二区三区| 午夜激情一区| 欧美在线3区| 久久综合福利| 欧美不卡激情三级在线观看| 免费一级欧美片在线观看| 巨乳诱惑日韩免费av| 久久一区二区三区国产精品| 久久婷婷国产综合尤物精品| 狂野欧美激情性xxxx欧美| 美国十次成人| 亚洲欧美日本日韩| 欧美一区2区视频在线观看| 亚洲一区二区高清| 亚洲影院污污.| 欧美有码在线观看视频| 美日韩精品视频免费看| 日韩一级欧洲| 久久精品99久久香蕉国产色戒| 亚洲小视频在线观看| 亚洲午夜精品久久久久久app| 亚洲欧美另类在线| 麻豆精品一区二区综合av| 欧美在线视频免费播放| 欧美激情一区三区| 亚洲欧美第一页| 欧美丝袜一区二区三区| 亚洲国产婷婷香蕉久久久久久| 午夜精品999| 亚洲国产欧美久久| 亚洲先锋成人| 国产精品美女主播| 91久久线看在观草草青青| 亚洲一二三区在线观看| 亚洲高清视频的网址| 性高湖久久久久久久久| 欧美日韩在线视频一区| 亚洲精品国产视频| 欧美成人一区二区三区片免费| 欧美在线日韩在线| 国产一区久久久| 欧美在线不卡| 亚洲欧美综合国产精品一区| 欧美黑人一区二区三区| 欧美成人免费视频| 99国产精品久久久久老师 | 免费成人黄色片| 国外成人在线| 亚洲大胆在线| 欧美午夜精品久久久久久久| 亚洲欧美日韩久久精品| 欧美中文字幕久久| 亚洲免费观看视频| 欧美在线日韩精品| 日韩午夜在线| 猛男gaygay欧美视频| 另类尿喷潮videofree| 亚洲六月丁香色婷婷综合久久| 这里只有精品在线播放| 精品粉嫩aⅴ一区二区三区四区| 欧美粗暴jizz性欧美20| 欧美性大战久久久久久久| 久久综合九色九九| 国产精品美女久久久浪潮软件| 亚洲国产一二三| 亚洲国产小视频| 久久久久国产成人精品亚洲午夜| 亚洲视频欧美视频| 欧美成人日韩| 亚洲黄色天堂| 亚洲日韩视频| 99精品视频免费在线观看| 欧美高清在线观看| 亚洲第一在线综合在线| 一区二区三区在线观看国产| 亚洲欧美网站| 久久久综合精品| 国内自拍一区| 免费看的黄色欧美网站| 欧美a级理论片| 日韩一二三在线视频播| 欧美日韩精品福利| 亚洲深夜福利视频| 先锋影音一区二区三区| 国内精品久久久| 欧美电影免费| 亚洲欧美日韩成人| 欧美a级片网| 亚洲欧美国产日韩天堂区| 国产日韩欧美二区| 久久人人爽人人爽爽久久| 亚洲激情视频在线| 欧美亚洲一区二区三区| 午夜欧美精品| 老司机精品福利视频| 中文久久乱码一区二区| 狠狠色丁香久久婷婷综合丁香| 欧美激情 亚洲a∨综合| 亚洲欧美日韩综合国产aⅴ| 欧美激情精品久久久久久| 亚洲欧美电影院| 一区二区三区日韩欧美| 亚洲国产精品第一区二区三区| 欧美午夜视频一区二区| 免费观看日韩| 亚洲视频免费观看| 亚洲综合导航| 日韩一级成人av| 日韩视频在线观看一区二区| 国语自产精品视频在线看一大j8 | 欧美一区二区三区久久精品| 亚洲激情第一区| 欧美不卡视频一区| 欧美专区18| 久久久人成影片一区二区三区 | 亚洲视频久久| 亚洲精品日韩在线| 亚洲人在线视频| 日韩午夜电影av| 一区二区日韩精品| 亚洲综合导航| 巨乳诱惑日韩免费av| 欧美激情视频给我| 亚洲视频一二区| 欧美一区二粉嫩精品国产一线天| 牛牛影视久久网| 亚洲欧洲av一区二区| 欧美在线www| 欧美日本高清视频| 国产亚洲网站| 99香蕉国产精品偷在线观看| 亚洲欧美日韩第一区| 亚洲女人天堂av| 亚洲欧美大片| 欧美激情精品久久久久久久变态| 亚洲美女黄网| 久久夜色精品亚洲噜噜国产mv| 欧美日韩爆操| 亚洲国产精品精华液网站| 亚洲在线视频网站| 亚洲国产精品美女| 亚洲欧美在线免费| 欧美深夜福利| 亚洲天堂成人在线观看| 欧美激情精品久久久久久久变态| 亚洲欧美综合网| 国产一区二区三区久久悠悠色av | 激情久久影院| 亚洲自拍偷拍色片视频| 亚洲人成人99网站| 欧美绝品在线观看成人午夜影视 | 老色批av在线精品| 在线电影院国产精品| 久久久午夜精品| 欧美伊人久久久久久久久影院| 国产精品一卡二卡| 亚洲视频精品在线| 亚洲一区二区高清视频| 国产精品美女久久久| 久久精品国产精品亚洲综合| 性欧美大战久久久久久久免费观看| 国产精品日韩专区| 久久精品国产亚洲一区二区| 久久精品国产亚洲a| 亚洲视频日本| 国产欧美亚洲视频| 欧美成人嫩草网站| 99精品免费视频| 欧美人与禽猛交乱配视频| 亚洲在线国产日韩欧美| 久久9热精品视频| 亚洲视频网站在线观看| 欧美在线免费播放| 亚洲一二三四区| 欧美成人亚洲成人| 久久精品成人欧美大片古装| 欧美成人有码| 久久久国产成人精品| 国产精品v欧美精品∨日韩| 久久综合伊人77777| 国产精品久久久久久久久| 亚洲国产91精品在线观看| 国产精品乱人伦一区二区| 久久先锋影音| 国产亚洲一区在线| 亚洲综合视频在线| 亚洲一区二区三区色| 欧美日韩精品久久| 亚洲国产精品尤物yw在线观看 | 蜜臀久久99精品久久久画质超高清 | 亚洲精品美女久久7777777| 激情综合久久| 蜜臀久久99精品久久久久久9| 久久精品一区二区三区四区|