日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ELF动态库加载技术

發布時間:2023/12/10 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ELF动态库加载技术 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

庫用于將相似函數打包在一個單元中。Linux支持兩種類型的庫:靜態庫(在編譯時靜態綁定到程序)和動態庫(在運行時綁定到程序)。Linux系統使用的動態庫是ELF格式,后綴名為so

1 加載

動態庫內部劃分為段,段分為不同的類型:

  • ?PT_LOAD段:包含代碼或數據,是需要被映射到內存中的,每個段有不同的訪問權限(讀、些、執行);

  • PT_DYNAMIC段:包含動態鏈接信息,如符號表、重定位表、引用的其他庫等。

其他段類型暫不說明。

加載器將庫文件第一個PT_LOAD段和最后一個PT_LOAD段之間的內容映射到一段連續的內存地址空間(好處是任意代碼和數據的相對地址固定),其首地址稱為基地址(如圖)。

庫的加載只是把文件內容映射到內存地址,但沒有真正讀取文件數據,在發生內存缺頁異常時才由操作系統讀入對應的文件數據到內存。延遲讀取文件可以加快庫的加載速度。

1.1 預鏈接

一般來說,映射的基地址是不固定的,但如果動態庫使用了預鏈接(prelink技術,則會被映射到預定的地址(保存在文件上)。如果預定的地址范圍已經被占用了,則加載失敗(Androidlinker是這樣,其他加載器可能不同)。Prelink的好處是簡化重定位,加快加載速度。

2 重定位

2.1 內部函數和變量

在沒有使用prelink的情況下,庫的基地址不是固定的(運行時才確定),其全局變量和函數的絕對地址也不是固定的。由于庫加載之后任意代碼和數據的相對地址是固定的(如前一節所述),因此一些系統(如x86)可以使用相對地址來訪問全局變量和函數。ARM系統由于指令長度限制(32位),無法在指令中直接使用大范圍的偏移量(但可通過寄存器指定),另外絕對地址在執行效率上要優于相對地址,因此還是需要重定位。

如這個例子:

[cpp] view plaincopyprint?
  • __attribute__((visibility("hidden")))int?errBase?=?1;??
  • void?setErr(){?errBase?=?0x999;?}??
  • __attribute__((visibility("hidden")))int errBase = 1; void setErr(){ errBase = 0x999; }

    編譯得到so,然后反編譯(ARM架構):

    [plain] view plaincopyprint?
  • $?gcc?-shared-nostdlib?-o?libtest.so?test.c??
  • $?objdump?-dlibtest.so??
  • 000002c4<setErr>:??
  • 2c4:??mov????ip,sp??
  • 2c8:??push???{fp,ip,?lr,?pc}??
  • 2cc:??sub????fp,ip,?#4??
  • 2d0:??ldr????r2,[pc,?#12]??????;?r2?=?&errBase??
  • 2d4:??mov????r3,#2448??????????;?r3?=?990??
  • 2d8:??add????r3,r3,?#9?????????;?r3?+=?9??
  • 2dc:??str????r3,[r2]???????????;?*r2?=?r3??
  • 2e0:??ldm????sp,{fp,?sp,?pc}??
  • 2e4:??.word??0x0000109c????????;?這里保存著errBase變量的地址??
  • $ gcc -shared-nostdlib -o libtest.so test.c $ objdump -dlibtest.so 000002c4<setErr>: 2c4: mov ip,sp 2c8: push {fp,ip, lr, pc} 2cc: sub fp,ip, #4 2d0: ldr r2,[pc, #12] ; r2 = &errBase 2d4: mov r3,#2448 ; r3 = 990 2d8: add r3,r3, #9 ; r3 += 9 2dc: str r3,[r2] ; *r2 = r3 2e0: ldm sp,{fp, sp, pc} 2e4: .word 0x0000109c ; 這里保存著errBase變量的地址

    查看重定位表:

    [plain] view plaincopyprint?
  • $?readelf?-rlibtest.so??
  • Relocationsection?'.rel.dyn'?at?offset?0x2bc?contains?1?entries:??
  • Offset????Info???????Type????????????Sym.Value??Sym.?Name??
  • 000002e4??00000017???R_ARM_RELATIVE??
  • $ readelf -rlibtest.so Relocationsection '.rel.dyn' at offset 0x2bc contains 1 entries: Offset Info Type Sym.Value Sym. Name 000002e4 00000017 R_ARM_RELATIVE

    對比匯編代碼和重定位表,2e4即是保存errBase變量地址的偏移量。

    重定位表中一個RELATIVE類型的表項,指向變量和函數的相對地址,加載器把它加上基地址,使成為絕對地址。如果使用了prelink,則不需要進行重定位。

    2.2 外部函數和變量

    外部變量和函數是指目標庫引用依賴庫的變量和函數,需要加載器在依賴庫的符號表查找對應的名稱和絕對地址,然后寫入目標庫的全局偏移量表(GlobalOffset Table,簡稱GOT。目標庫通過GOT來訪問外部變量和函數。

    外部變量重定位對應一個GLOB_DAT類型的表項,外部函數重定位對應一個JMP_SLOT類型表項,表項的值是外部變量或函數的絕對地址,由加載器進行設置。

    如這個例子:

    [cpp] view plaincopyprint?
  • extern?interrBase;??
  • void?setErr(){?errBase?=?0x999;?}??
  • extern interrBase; void setErr(){ errBase = 0x999; }

    編譯得到so,然后反編譯(ARM架構):

    [cpp] view plaincopyprint?
  • $?gcc?-shared-nostdlib?-o?libtest.so?test.c??
  • $?objdump?-dlibtest.so??
  • 00000218<setErr>:??
  • 218:??push???{fp}??
  • 21c:??add????fp,sp,?#0??
  • 220:??ldr????r3,[pc,?#28]????;?r3=GOT偏移??
  • 224:??add????r3,pc,?r3???????;?r3=GOT地址??
  • 228:??ldr????r2,[pc,?#24]????;?r2=errBase項在GOT的偏移??
  • 22c:??ldr????r3,[r3,?r2]?????;?r3=errBase的地址??
  • 230:??ldr????r2,[pc,?#20]????;?r2=0x999??
  • 234:??str????r2,[r3]?????????;?r3=r2??
  • 238:??add????sp,fp,?#0??
  • 23c:??pop????{fp}??
  • 240:??bx?????lr??
  • 244:??.word??0x00008dc4??????;?GOT偏移??
  • 248:??.word??0x0000000c??????;?errBase在GOT的偏移??
  • 24c:??.word??0x00000999??
  • $ gcc -shared-nostdlib -o libtest.so test.c $ objdump -dlibtest.so 00000218<setErr>: 218: push {fp} 21c: add fp,sp, #0 220: ldr r3,[pc, #28] ; r3=GOT偏移 224: add r3,pc, r3 ; r3=GOT地址 228: ldr r2,[pc, #24] ; r2=errBase項在GOT的偏移 22c: ldr r3,[r3, r2] ; r3=errBase的地址 230: ldr r2,[pc, #20] ; r2=0x999 234: str r2,[r3] ; r3=r2 238: add sp,fp, #0 23c: pop {fp} 240: bx lr 244: .word 0x00008dc4 ; GOT偏移 248: .word 0x0000000c ; errBase在GOT的偏移 24c: .word 0x00000999

    查看重定位表:

    [cpp] view plaincopyprint?
  • $readelf?-rlibtest.so??
  • Relocationsection?'.rel.dyn'?at?offset?0x210?contains?1?entries:??
  • Offset??????Info????????Type??????????????Sym.Value????Sym.Name??
  • 00008ffc????00000415????R_ARM_GLOB_DAT????00000000?????errBase??
  • $readelf -rlibtest.so Relocationsection '.rel.dyn' at offset 0x210 contains 1 entries: Offset Info Type Sym.Value Sym.Name 00008ffc 00000415 R_ARM_GLOB_DAT 00000000 errBase

    8ffcc正好對應errBaseGOT表項地址。

    2.3 延遲綁定

    外部函數和變量的重定位需要查找依賴庫的符號表,并進行字符串比較,效率較低,不過一般一個庫使用的外部變量和函數都不會太多。如果使用了較多的外部函數,為了加快動態庫加載速度,可以使用過程鏈接表(ProcedureLinkageTable,簡稱PLT,把外部函數的定位延遲到第一次調用的時候(稱為延遲綁定)。函數延遲綁定需要編譯器對函數調用生成額外的代碼,主要由編譯器實現。

    看這個例子:

    [cpp] view plaincopyprint?
  • voidprintf1(const?char*,?...);??
  • void?setErr(){?printf1("setErr\n");?}??
  • voidprintf1(const char*, ...); void setErr(){ printf1("setErr\n"); }

    對應匯編代碼(x86-64):

    [html] view plaincopyprint?
  • 4c0<printf1@plt>:??
  • 4c0:??jmpq???*0x200b3a(%rip)??
  • 4c6:??pushq??$0x0??
  • 4cb:??jmpq???4b0?<_init+0x18>??
  • ??
  • 5ac?<setErr>:??
  • 5ac:??push???%rbp??
  • 5ad:??mov????%rsp,%rbp??
  • 5b0:??lea????0x5f(%rip),%rdi??
  • 5b7:??mov????$0x0,%eax??
  • 5bc:??callq??4c0??
  • 5c1:??pop????%rbp??
  • 5c2:??retq??
  • 4c0<printf1@plt>: 4c0: jmpq *0x200b3a(%rip) 4c6: pushq $0x0 4cb: jmpq 4b0 <_init+0x18>5ac <setErr>: 5ac: push %rbp 5ad: mov %rsp,%rbp 5b0: lea 0x5f(%rip),%rdi 5b7: mov $0x0,%eax 5bc: callq 4c0 5c1: pop %rbp 5c2: retq

    調用printf1會調用printf1@plt,然后跳轉到*0x200b3a(%rip),即*(基地址+0x201000)。

    如果是第一次執行,*0x(基地址+0x201000)的值是(基地址+4c6),后面的代碼會進行函數綁定,對應的重定位項是:

    [plain] view plaincopyprint?
  • $?readelf?-rlibtest.so??
  • Relocationsection?'.rela.plt'?at?offset?0x468?contains?2?entries:??
  • 201000?000300000007?R_X86_64_JUMP_SLO??printf1?+?0??
  • $ readelf -rlibtest.so Relocationsection '.rela.plt' at offset 0x468 contains 2 entries: 201000 000300000007 R_X86_64_JUMP_SLO printf1 + 0

    綁定之后*0x(基地址+0x201000)會對應printf1函數的地址,下次再進入printf1@plt,就可以直接跳轉到printf1函數了。

    2.4 位置無關代碼

    一般來說,程序和動態庫的代碼和只讀數據被加載到內存之后,可以被多個進程共享,但被寫的臟數據則不能被多個進程共享。RELATIVE類型的重定位會修改代碼段的變量地址,導致代碼段被污染,從而不能被多個進程共享。為了讓動態庫的代碼段可以在進程間共享,可以讓編譯器編譯出位置無關代碼(簡稱PIC,通過GOT來訪問變量和函數。

    PIC使代碼段可在進程間共享,從而節省了內存,但是通過GOT表來訪問變量和函數會比相對定位慢一點,如果沒有需要則可以不使用PIC

    總結

    以上是生活随笔為你收集整理的ELF动态库加载技术的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 大奶一区| 国产高清在线不卡 | 在线观看免费中文字幕 | 黄色小网站入口 | 国产精选一区 | 中文人妻一区二区三区 | 色综合视频网 | 日本泡妞xxxx免费视频软件 | 日本在线加勒比 | 日韩欧美视频在线播放 | 西西人体www大胆高清 | 欧美一级淫片免费视频黄 | 国产精品久久久免费 | 精品国产一区二区三区四区阿崩 | 中文字幕在线视频免费观看 | 欧美激情精品久久久久 | 国产高清在线观看 | 国产精品二区三区 | 国产第一页在线 | 韩国毛片一区二区 | 粉嫩小箩莉奶水四溅在线观看 | 91精品在线免费观看 | 黄色录像一级大片 | 欧美天堂久久 | 97色在线观看 | 久久久久久久久久网 | 成人在线视屏 | 精品成在人线av无码免费看 | 国产精品无码无卡无需播放器 | 朋友人妻少妇精品系列 | 亚洲性生活片 | 久久综合导航 | 爱爱激情网 | 3d毛片 | www嫩草 | 日本肉体xxxⅹ裸体交 | 在线免费看av | 伊人网站在线观看 | 日本亲子乱子伦xxxx | 少妇精品无码一区二区免费视频 | 国产白拍| 国产一级免费观看 | 女人毛片视频 | 超碰在线小说 | 天天爽一爽 | 免费毛片网站 | 午夜少妇 | 肉视频在线观看 | 日本亚洲一区 | 国产av国片偷人妻麻豆 | 欧美激情成人在线 | 久久久久国产免费 | 狠狠躁日日躁夜夜躁2022麻豆 | 成人免费一区二区三区 | 精品理论片 | 成年人免费网站 | 成人免费毛片足控 | 韩日在线视频 | 999午夜| 日日干,夜夜操 | 亚洲精品一区二区三区影院忠贞 | 另类国产 | 色先锋在线 | 日日夜夜人人 | 中文字幕在线观看网站 | 视频在线观看一区二区 | 欧美亚洲国产一区二区三区 | 国产一区二区网 | 亚洲精品国产精华液 | 久久国精品 | 亚洲自拍偷拍欧美 | 91操碰| 天堂а√在线中文在线新版 | 免费污片网站 | 欧美日韩六区 | 久久永久免费视频 | 少妇高潮一区二区三区69 | 开心黄色网 | 中文字幕22页 | 另类综合在线 | 91中文字幕在线 | 色久av | 欧美另类69xxxx| 姐姐的秘密韩剧免费观看全集中文 | 国产乱码一区二区 | 丁香在线| ⅹxxxxhd亚洲日本hd老师 | 久久青草视频 | 国产精品视频一二区 | 牛牛精品视频 | 公侵犯一区二区三区四区中文字幕 | 亚洲午夜不卡 | www日本在线 | 无码人妻久久一区二区三区 | 欧美日韩激情视频在线观看 | 日本中文在线 | 暗呦丨小u女国产精品 | 天天婷婷 | 亚洲成人av|