在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux下C語言共享庫的位置無關實現原理分析

Linux閱碼場 ? 來源:未知 ? 2019-11-28 16:20 ? 次閱讀

description: "本文詳細介紹了 Linux 下 C 語言共享庫的位置無關(PIC)實現原理。"

背景簡介

吳章金:如何創建一個*可執行*的共享庫一文談完了如何讓共享庫可直接執行,本文再來談談共享庫的運行時位置無關(PIC)是如何做到的。

PIC = position independent code

-fpic Generate position-independent code (PIC) suitable for use in a shared library

共享庫有一個很重要的特征,就是可以被多個可執行文件共享,以達到節省磁盤和內存空間的目標:

共享意味著不僅磁盤上只有一份拷貝,加載到內存以后也只有一份拷貝,那么代碼部分在運行時也不能被修改,否則就得有多個拷貝存在

同時意味著,需要能夠靈活映射在不同的虛擬地址空間,以便適應不同程序,避免地址沖突

這兩點要求共享庫的代碼和數據都是位置無關的,接下來先看看什么是“位置無關”。

什么是位置無關

同樣以 hello.c 為例:

#include

intmain(void)
{
printf("hello
");

return0;
}

以普通的方式來編譯并反匯編一個可執行文件看看:

$gcc-m32-ohellohello.c
$objdump-dhello|grep-B1"call.*puts@plt>"
8048416:68b0840408push$0x80484b0
804841b:e8c0feffffcall80482e0

可以看到上面傳遞給puts(printf)的字符串地址是“寫死的”,在編譯時就是確定的,這意味著 Load Address 也必須是固定的:

$readelf-lhello|grepLOAD|head-1
LOAD0x0000000x080480000x080480000x005b00x005b0RE0x1000

上面可以看到 Load Address 為 0x8048000。

如果 Load Address 改變,數據地址就指向別的內容了,這就是“位置有關”。

共享庫的話,必須摒棄這種“寫死的”地址,要做到“位置無關”(注:prelink 是特殊需求,暫且不表)。

如何做到位置無關(Part1)

位置無關,意味著運行時可以靈活調整 Load Address,當 Load Address 在運行時發生改變后,代碼還能被執行到,數據也能被正確訪問。

那么代碼和數據都變成跟 Load Address 相關的,不能再是絕對地址,而需要采用某個相對 Load Address 的地址。

動態鏈接器會負責找到可執行文件的共享庫并裝載它們,所以動態鏈接器是知道這個 Load Address 的,那么函數符號其實是很容易確定的,來看看不帶-fpic時編譯生成一個共享庫:

查看main函數的初始地址

$gcc-m32-shared-olibhello.sohello.c
$objdump-dlibhello.so|grep-A2"main>:"
000004a9
: 4a9:8d4c2404lea0x4(%esp),%ecx 4ad:83e4f0and$0xfffffff0,%esp

查看“裝載地址”,編譯后初始化為 0

$readelf-llibhello.so|grepLOAD|head-1
LOAD0x0000000x000000000x000000000x0057c0x0057cRE0x1000

確認main在文件中的偏移

$readelf--dyn-symslibhello.so|grepm
Symboltable'.dynsym'contains12entries:
Num:ValueSizeTypeBindVisNdxName
4:000000000NOTYPEWEAKDEFAULTUND__gmon_start__
9:000004a946FUNCGLOBALDEFAULT11main

$hexdump-C-s$((0x4a9))-n10libhello.so
000004a98d4c240483e4f0ff71fc|.L$.....q.|
000004b3

可以看到,對于main而言,無論把共享庫裝載到哪里,動態鏈接器總能根據 Load Address 以及.dynsym中的偏移把main的運行時地址算出來(見 glibc:_dl_fixup)。

但是,這個時候(不用-fpic的話),數據地址也是“寫死的”:

$objdump-dlibhello.so|grep-B1"call.*main"
4bd:68ec040000push$0x4ec
4c2:e8fcffffffcall4c3

作為對比,來看看加上-fpic的效果:

$gcc-m32-shared-fpic-olibhello.sohello.c
$objdump-drlibhello.so|grep-B6"call.*puts@plt>"
4c8:e828000000call4f5<__x86.get_pc_thunk.ax>
4cd:05331b0000add$0x1b33,%eax
4d2:83ec0csub$0xc,%esp
4d5:8d9010e5fffflea-0x1af0(%eax),%edx
4db:52push%edx
4dc:89c3mov%eax,%ebx
4de:e8bdfeffffcall3a0

可以看到,用上-fpic以后,傳遞給 puts 的數據地址(push %edx)已經是通過動態計算的,那是怎么算的呢?

上面有個內聯進來的函數很關鍵:

$objdump-drlibhello.so|grep-A3"__x86.get_pc_thunk.ax>:"
000004f5<__x86.get_pc_thunk.ax>:
4f5:8b0424mov(%esp),%eax
4f8:c3ret

這個函數賊簡單,從棧頂取了一個數據就跳回去了,取的數據是什么呢?這就要了解調用它的call指令了。

call指令會把下一條指令的eip壓棧然后 jump 到目標地址:

callbackward==>pusheip;
jmpbackward

所以,數據地址是運行時計算的,跟運行時的 “eip” 給關聯上了。

不難猜測,如果知道當前指令的位置,又提前保存了數據離當前位置的偏移,那么數據地址是可以直接計算的,只是上面那一段代碼還是略微復雜了,因為有一堆 “Magic Number”。

不管怎么樣,先來模擬計算一下,假設裝載到的地址就是 0x0,那么執行到add指令時存到 eax 的 eip,恰好是call返回后下一條指令的地址,即 0x4cd:

4c8:e828000000call4f5<__x86.get_pc_thunk.ax>
4cd:05331b0000add$0x1b33,%eax
4d5:8d9010e5fffflea-0x1af0(%eax),%edx

根據上述指令,那么%edx計算出來就是 0x510:

$echo"obase=16;$((0x4cd+0x1b33-0x1af0))"|bc
510

再去取數據:

$hexdump-C-s$((0x510))-n10libhello.so
0000051068656c6c6f000000011b|hello.....|
0000051a

果然是字符串的地址,所以,相對偏移其實被拆分成了兩部分:0x1b33和-0x1af0。兩個 "Magic Number" 一加就出來了。

所以,小結一下,“位置無關” 是通過運行時動態獲取 “eip” 并加上一個編譯時記錄好的偏移計算出來的,這樣的話,無論加載到什么位置,都能訪問到數據。

如何做到位置無關(Part2)

這對 “Magic Number” 還是需要再看一看,既然是編譯時確定的,看看匯編狀態是怎么回事:

$gcc-m32-shared-fpic-Shello.c
$cathello.s|grep-v.cfi
...
.LC0:
.string"hello"
.text
.globlmain
.typemain,@function
main:
.LFB0:
leal4(%esp),%ecx
andl$-16,%esp
pushl-4(%ecx)
pushl%ebp
movl%esp,%ebp
pushl%ebx
pushl%ecx
call__x86.get_pc_thunk.ax
addl$_GLOBAL_OFFSET_TABLE_,%eax
subl$12,%esp
leal.LC0@GOTOFF(%eax),%edx
pushl%edx
movl%eax,%ebx
callputs@PLT
...

從 i386 的 archABI 不難找到這塊的定義(P61~P62),name@GOTOFF(%eax)直接表示 name 符號相對 %eax 保存的 GOT 的偏移地址。

首先,編譯時要計算$_GLOBAL_OFFSET_TABLE和.LC0@GOTOFF。

$_GLOBAL_OFFSET_TABLE_為 GOT 相對eip的偏移,可計算為:

>

$_GLOBAL_OFFSET_TABLE_ = .got.plt - eip

計算過程如下:

$readelf-Slibhello.so|grep.got.plt
[21].got.pltPROGBITS0000200000100000001004WA004
$echo"obase=16;$((0x2000-0x4cd))"|bc
1B33

接著,計算.LC0@GOTOFF:

.LC0 - eip =GLOBAL_OFFSET_TABLE+ .LC0@GOTOFF .LC0@GOTOFF = .LC0 - eip -GLOBALOFFSETTABLE+.LC0@GOTOFF.LC0@GOTOFF=.LC0?eip?GLOBAL_OFFSET_TABLE

計算過程如下:

$echo"obase=16;$((0x510-0x4cd-0x1B33))"|bc
-1AF0

反過來,運行時的計算公式為:

.LC0 =GLOBAL_OFFSET_TABLE+ .LC0@GOTOFF + eip
.LC0 = 0x1B33 + (-1AF0) + eip

.got.plt =GLOBALOFFSETTABLE+.LC0@GOTOFF+eip.LC0=0x1B33+(?1AF0)+eip.got.plt=GLOBAL_OFFSET_TABLE+ eip
.got.plt = 0x1B33 + eip

實際上,只有 .got.plt 的地址,即ebx需要$_GLOBAL_OFFSET_TABLE_來計算,這個是用來做動態地址重定位的,暫且不表。

.LC0的地址,完全可以換一種方式,直接用.LC0到 eip 的偏移即可,匯編代碼改造完如下:

call__x86.get_pc_thunk.ax
.eip:
#計算eip+(.LC0-.eip)剛好指向內存中的數據"hello"所在位置
movl%eax,%ebx
leal(.LC0-.eip)(%eax),%edx

#計算 .got.plt 地址,_GLOBAL_OFFSET_TABLE_是相對 eip 的偏移,所以必須加上這個 offset:. - .eip
addl$_GLOBAL_OFFSET_TABLE_+[.-.eip],%ebx
subl$12,%esp
pushl%edx
callputs@PLT

驗證結果:

$gcc-m32-g-shared-fpic-olibhello.sohello.s
$gcc-m32-g-ohello.noc-L./-lhello
$LD_LIBRARY_PATH=$LD_LIBRARY_PATH:././hello.noc
hello

小結

本文詳細介紹了 Linux 下 C 語言共享庫“位置無關”(PIC)的核心實現原理:即用 EIP 相對地址來取代絕對地址。

“位置無關” 代碼會帶來很大的內存使用靈活性,也會帶來一定的安全性,因為“位置無關”以后就可以帶來加載地址的隨機性,給代碼注入帶來一定的難度。

由于有上述好處,各大平臺的 gcc 都開始默認打開可執行文件的-pie -fpie了,因為 gcc 編譯時開啟了:--enable-default-pie。這也可能導致一些“衰退”,大家可以根據需要關閉它:-no-pie,-fno-pie。

當然,共享庫的實現精髓不止于此,最核心的還是函數符號地址的動態解析過程,而這些則跟上面的.got.plt地址密切相關,受限于篇幅,暫時不做詳細展開。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11345

    瀏覽量

    210406
  • C語言
    +關注

    關注

    180

    文章

    7614

    瀏覽量

    137733
  • main
    +關注

    關注

    0

    文章

    38

    瀏覽量

    6203

原文標題:吳章金: 深度剖析 Linux共享庫的“位置無關”實現原理

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    C語言-文件編程

    這篇文章介紹C語言的文件編程函數,案例代碼是在Linux環境運行測試的分別介紹了C語言標準
    的頭像 發表于 09-09 11:33 ?2072次閱讀

    Linux操作系統-C語言編程入門-pdf

    Linux操作系統-C語言編程入門介紹在LINUX 進行C
    發表于 12-08 09:55 ?193次下載
    <b class='flag-5'>Linux</b>操作系統-<b class='flag-5'>C</b><b class='flag-5'>語言</b>編程入門-pdf

    linuxc語言編程pdf

    linuxc語言編程內容為::基礎知識,進程介紹,文件操作,時間概念,信號處理,消息管理,線程操作,網絡編程,Linux
    發表于 12-08 10:00 ?0次下載

    Linux系統共享編程

    一、說明 類似Windows系統中的動態鏈接Linux中也有相應的共享用以支持代碼的復用。Windows中為*.dll,而Linux
    發表于 09-13 16:49 ?24次下載

    Linux靜態和動態共享)的制作與使用

    Linux靜態和動態共享)的制作與使用Linux
    發表于 07-09 14:39 ?1194次閱讀

    LINUX環境CLIPS動態鏈接實現方法

    LINUX環境,為了簡便、快捷地制作出CLIPS動態鏈接,本文采用了CNU AUTOTOOLS把CLIPS嵌入式高級語言編譯成動態鏈接
    發表于 04-14 21:18 ?30次下載

    LinuxC語言編程概述

    分享到:標簽:C語言編程 Linux 編譯鏈接器 調試器 操作系統 3.1 LinuxC
    發表于 10-18 14:36 ?0次下載
    <b class='flag-5'>Linux</b><b class='flag-5'>下</b><b class='flag-5'>C</b><b class='flag-5'>語言</b>編程概述

    基于Linux操作系統C語言編程入門

    基于Linux操作系統C語言編程入門
    發表于 10-27 15:36 ?11次下載
    基于<b class='flag-5'>Linux</b>操作系統<b class='flag-5'>下</b><b class='flag-5'>C</b><b class='flag-5'>語言</b>編程入門

    linux靜態和動態分析

    的二進制是不兼容的。 本文僅限于介紹linux。 2.的種類 linux
    發表于 11-02 10:12 ?1次下載

    Linux操作系統C語言編程入門.pdf

    Linux操作系統C語言編程入門
    發表于 05-17 10:08 ?96次下載

    Linux的常用C函數中文手冊免費下載

    本文檔的主要內容詳細介紹的是Linux的常用C函數中文手冊免費下載,包含幾乎所有LinuxC
    發表于 10-28 08:00 ?9次下載
    <b class='flag-5'>Linux</b>的常用<b class='flag-5'>C</b>函數<b class='flag-5'>庫</b>中文手冊免費下載

    LinuxC語言編程入門教程詳細說明

    本文是Linux C 語言編程入門教程。主要介紹了Linux 的發展與特點、C
    發表于 08-25 18:05 ?39次下載
    <b class='flag-5'>Linux</b><b class='flag-5'>下</b><b class='flag-5'>C</b><b class='flag-5'>語言</b>編程入門教程詳細說明

    C++基礎語法知識之鏈接裝載Linux共享

    Linux共享(Shared Library) Linux 共享
    的頭像 發表于 11-01 10:15 ?2945次閱讀

    Linux中的靜態共享

    是一個二進制文件,包含的代碼可被程序調用。例如標準C、數學、線程等等。有源碼,可下載后
    的頭像 發表于 05-10 09:34 ?1092次閱讀

    C 語言的頭文件路徑位置問題

    的朋友們來說,一些系統的文件路徑根本就不知道在什么地方。 所以本文我們就來聊一 C 語言的頭文件路徑相關的問題 ,包括系統路徑位置,絕
    的頭像 發表于 06-22 10:05 ?6391次閱讀
    <b class='flag-5'>C</b> <b class='flag-5'>語言</b>的頭文件路徑<b class='flag-5'>位置</b>問題
    主站蜘蛛池模板: 亚洲爱爱图| 国模龙园园私拍337p | 欧美另类高清xxxxx | 九七婷婷狠狠成人免费视频 | www.激情网.com | a一级黄| 色婷婷色丁香 | 四虎影院在线网址 | 四虎影裤| 天天综合色天天综合网 | 人人爱天天做夜夜爽毛片 | 久久国产乱子伦精品免费强 | 九九福利视频 | 久久国产视频网站 | 视频在线二区 | 一区二区不卡免费视频 | 亚洲一区在线观看视频 | 五月天婷婷视频在线观看 | 狠狠一区 | 视频在线观看h | 午夜精品免费 | 国产三级三级三级 | 欧美亚洲天堂网 | 看屁屁www视频免费观看 | 女人张开腿给男人桶爽免费 | 99久久精品久久久久久婷婷 | 免费看欧美理论片在线 | 天天射天天射天天干 | 日本黄色片免费看 | 1024手机在线看片 | 伊人久久大香线蕉综合网站 | 91日本视频 | 777777777妇女亚洲 | 五月婷婷丁香在线 | 新版天堂中文资源官网 | 操的好爽| 色啦啦影院 | 韩国一级网站 | 中文在线1区二区六区 | 国产操视频 | 欧美在线你懂的 |