欧美性猛交xxxx免费看_牛牛在线视频国产免费_天堂草原电视剧在线观看免费_国产粉嫩高清在线观看_国产欧美日本亚洲精品一5区

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

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

3天內(nèi)不再提示

Linux下C語言共享庫的位置無關實現(xiàn)原理分析

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

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

背景簡介

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

PIC = position independent code

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

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

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

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

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

什么是位置無關

同樣以 hello.c 為例:

#include

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

return0;
}

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

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

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

$readelf-lhello|grepLOAD|head-1
LOAD0x0000000x080480000x080480000x005b00x005b0RE0x1000

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

如果 Load Address 改變,數(shù)據(jù)地址就指向別的內(nèi)容了,這就是“位置有關”。

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

如何做到位置無關(Part1)

位置無關,意味著運行時可以靈活調(diào)整 Load Address,當 Load Address 在運行時發(fā)生改變后,代碼還能被執(zhí)行到,數(shù)據(jù)也能被正確訪問。

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

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

查看main函數(shù)的初始地址

$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而言,無論把共享庫裝載到哪里,動態(tài)鏈接器總能根據(jù) Load Address 以及.dynsym中的偏移把main的運行時地址算出來(見 glibc:_dl_fixup)。

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

$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 的數(shù)據(jù)地址(push %edx)已經(jīng)是通過動態(tài)計算的,那是怎么算的呢?

上面有個內(nèi)聯(lián)進來的函數(shù)很關鍵:

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

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

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

callbackward==>pusheip;
jmpbackward

所以,數(shù)據(jù)地址是運行時計算的,跟運行時的 “eip” 給關聯(lián)上了。

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

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

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

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

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

再去取數(shù)據(jù):

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

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

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

如何做到位置無關(Part2)

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

$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 [email protected]@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_來計算,這個是用來做動態(tài)地址重定位的,暫且不表。

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

call__x86.get_pc_thunk.ax
.eip:
#計算eip+(.LC0-.eip)剛好指向內(nèi)存中的數(shù)據(jù)"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

驗證結(jié)果:

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

小結(jié)

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

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

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

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

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

    關注

    87

    文章

    11350

    瀏覽量

    210459
  • C語言
    +關注

    關注

    180

    文章

    7614

    瀏覽量

    137773
  • main
    +關注

    關注

    0

    文章

    38

    瀏覽量

    6208

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

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

收藏 人收藏

    評論

    相關推薦

    C語言-文件編程

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

    Linux操作系統(tǒng)-C語言編程入門-pdf

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

    linuxc語言編程pdf

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

    Linux系統(tǒng)共享編程

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

    Linux靜態(tài)和動態(tài)共享)的制作與使用

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

    LINUX環(huán)境CLIPS動態(tài)鏈接實現(xiàn)方法

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

    LinuxC語言編程概述

    分享到:標簽:C語言編程 Linux 編譯鏈接器 調(diào)試器 操作系統(tǒng) 3.1 LinuxC
    發(fā)表于 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操作系統(tǒng)C語言編程入門

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

    linux靜態(tài)和動態(tài)分析

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

    Linux操作系統(tǒng)C語言編程入門.pdf

    Linux操作系統(tǒng)C語言編程入門
    發(fā)表于 05-17 10:08 ?96次下載

    Linux的常用C函數(shù)中文手冊免費下載

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

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

    本文是Linux C 語言編程入門教程。主要介紹了Linux 的發(fā)展與特點、C
    發(fā)表于 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 共享
    的頭像 發(fā)表于 11-01 10:15 ?2950次閱讀

    Linux中的靜態(tài)共享

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

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

    的朋友們來說,一些系統(tǒng)的文件路徑根本就不知道在什么地方。 所以本文我們就來聊一 C 語言的頭文件路徑相關的問題 ,包括系統(tǒng)路徑位置,絕
    的頭像 發(fā)表于 06-22 10:05 ?6393次閱讀
    <b class='flag-5'>C</b> <b class='flag-5'>語言</b>的頭文件路徑<b class='flag-5'>位置</b>問題