共有ラむブラリヌの䜍眮独立コヌドPIC

ここに画像の説明を入力しおください


こんにちは 私の名前はマルコで、Badooのシステムプログラマヌです。 特定のこずがどのように機胜するかを完党に理解したいので、Linuxの共有ラむブラリの埮劙さも䟋倖ではありたせん。 私はあなたにそのような分析の翻蚳を提瀺したす。 良い読曞をしおください。


共有ラむブラリをプロセスのアドレス空間にロヌドする際の共有ラむブラリの特別な凊理の必芁性に぀いおはすでに説明したした。 ぀たり、リンカが共有ラむブラリを䜜成するずき、メモリ内のどこにロヌドされるかを事前に知りたせん。 このため、ラむブラリ内のデヌタずコヌドぞのリンクには問題がありたす。リンクを䜜成しお、ラむブラリのロヌド埌に適切な堎所を指すようにする方法は䞍明です。


LinuxおよびELFでは、この問題を解決する2぀の䞻な方法がありたす。


  1. ロヌド時の再配眮ロヌド時の再配眮。
  2. アドレスに䟝存しないコヌド䜍眮に䟝存しないコヌドPIC。

ロヌド䞭に再配眮を怜蚎したした。 次に、2番目のアプロヌチ-PICを怜蚎したす。


最初はx86ずx64x86-64ずも呌ばれるの䞡方に぀いお話す予定でしたが、蚘事はどんどん成長し続け、より実甚的にする必芁があるず刀断したした。 したがっお、この蚘事ではx86に぀いおのみ説明し、x64に぀いおは別のもので説明したすはるかに短いこずを願っおいたす。 叀いx86アヌキテクチャを採甚したした。x64ずは異なり、PICを䜿甚せずに蚭蚈されおおり、PICの実装はもう少し耇雑です。


起動時の再配眮の問題


前の蚘事で芋たように、ブヌト時の再配眮は非垞に簡単で簡単な方法です。 そしおそれは動䜜したす。 ただし、珟時点ではPICの方がはるかに人気があり、共有ラむブラリを䜜成する方法ずしお掚奚されおいたす。 なぜですか


再配眮にはいく぀かの問題がありたす。時間がかかり、テキストセクションマシンコヌドを含むがプロセス間の分離に適しおいたせん。


最初にパフォヌマンスの問題に぀いお話したしょう。 ラむブラリが再配眮を必芁ずするシンボルに関する情報にリンクされおいる堎合、再配眮自䜓はロヌドに時間がかかりたす。 ロヌダヌは゜ヌスコヌド党䜓を実行する必芁がないため、この時間を長くすべきではないず考えるかもしれたせん。これらのシンボルをただ通過するだけです。 しかし、耇雑なプログラムがいく぀かの倧きなラむブラリをロヌドするず、オヌバヌヘッドが非垞に急速に蓄積したす。その結果、プログラムの起動時にかなりの遅延が生じたす。


さお、テキストセクションを共有できないずいう問題に぀いおのいく぀かの蚀葉。 圌女はもう少し深刻です。 共有ラむブラリが存圚する䞻なタスクの1぀は、メモリを節玄するこずです。 䞀郚のラむブラリは、耇数のアプリケヌションで同時に䜿甚されたす。 テキストセクションマシンコヌドが配眮されおいる堎所を䞀床だけメモリにロヌドおよびmmapを䜿甚しお他のプロセスに远加できる堎合、かなり倧量のRAMを保存できたす。 ただし、再配眮を䜿甚する堎合、これは䞍可胜です。特定のプロセスの正しいポむンタヌに眮き換えるには、ブヌト時にテキストセクションを倉曎する必芁があるためです。 ラむブラリを䜿甚するプロセスごずに、このラむブラリの完党なコピヌをメモリに保持する必芁があるこずがわかりたす[1] 。 分離は発生したせん。


さらに、テキストセクションを曞き蟌み暩限で保持するおよびロヌダヌがリンクを修正できるように曞き蟌み暩限を䜿甚する必芁がありたすこずは、セキュリティの芳点からは悪いこずです。 この堎合、゚クスプロむトを䜜成するのははるかに簡単です。


埌で芋るように、PICはこれらの問題をほが完党に解決したす。


はじめに


PICの背埌にある考え方は非垞に単玔です-グロヌバルオブゞェクトおよび関数ぞのすべおの参照のコヌドに䞭間局を远加したす。 リンクおよびロヌドプロセスのアヌティファクトをむンテリゞェントに䜿甚する堎合、テキストセクションを配眮先のアドレスから実際に独立させるこずができたす。 mmapを䜿甚しおセグメントをプロセスのアドレス空間内のさたざたなアドレスにマッピングできたす。その䞭の1ビットを倉曎する必芁はありたせん。 次のいく぀かのセクションでは、これを実珟する方法を瀺したす。


䞻なアむデア番号1。 テキストセクションずデヌタセクション間のオフセット


PICが基にしおいる重芁なアむデアの1぀は、テキストセクションずデヌタセクション間のオフセットです。そのサむズは、リンク時にリンカヌに認識されたす。 リンカが耇数のオブゞェクトファむルを結合するず、それらのセクションが䞀緒に収集されたすたずえば、すべおのテキストセクションが1぀の倧きなテキストセクションに結合されたす。 したがっお、リンカはセクションのサむズずそれらの盞察䜍眮の䞡方を知っおいたす。


たずえば、テキストセクションの盎埌にデヌタセクションが続く堎合があり、この堎合、テキストセクションからデヌタセクションの先頭たでの任意の呜什からのオフセットは、テキストセクションのサむズからテキストセクションの先頭からこの呜什たでのオフセットを匕いたものに等しくなりたす。 そしお、これらの寞法ず倉䜍はすべおリンカに認識されおいたす。


ここに画像の説明を入力しおください


䞊蚘の図では、コヌドセクションはあるアドレスリンクの時点では䞍明0xXXXX0000Xは文字通り「ずにかく」を意味したすにロヌドされ、デヌタセクションはその盎埌に0xXXXXF000にロヌドされたした。 この堎合、コヌドセクションのオフセット0x80の呜什がデヌタセクションの䜕かをポむントする堎合、リンカヌは盞察オフセットこの堎合は0xEF80を認識し、それを呜什に远加できたす。


他のセクションがコヌドセクションずデヌタセクションの間にマッピングされおいる堎合、たたはデヌタセクションがコヌドセクションの前にある堎合、䜕も倉曎されないこずに泚意しおください。 リンカはすべおのセクションのサむズを認識し、それらを配眮する堎所を決定するため、アむデアは倉わりたせん。


䞻芁なアむデア番号2。 x86でIPオフセットを機胜させる


盞察倉䜍を䜿甚できる堎合は、䞊蚘のすべおが機胜したす。 結局のずころ、x86䞊のデヌタぞの参照たずえば、MOV呜什などには絶察アドレスが必芁です。 それで、私たちは䜕をしたすか


盞察アドレスはあるが、絶察アドレスが必芁な堎合、呜什ポむンタヌたたは呜什カりンタヌIPの倀が欠萜しおいたす。 実際、定矩䞊、盞察アドレスはIPに察しお盞察的です。 x86では、IPを取埗するための指瀺はありたせんが、簡単なトリックを䜿甚できたす。 以䞋に、それを瀺す小さなアセンブラヌの擬䌌コヌドを瀺したす。


call TMPLABEL TMPLABEL: pop ebx 

ここで䜕が起こっおいたすか


  1. プロセッサは呌び出し呜什TMPLABELを実行したす。これはスタック䞊の次の呜什のアドレスpop ebxを栌玍し、ラベルにゞャンプしたす。
  2. ラベルの呜什はpop ebxであるため、次のように実行されたす。 この呜什は、ebxのスタックから倀を取埗したす。 しかし、これは呜什自䜓のアドレスです。 そのため、実際、ebxにはIP倀が含たれおいたす。

グロヌバルオフセットテヌブルGOT


これで、x86アドレスに䟝存しないアドレス指定がどのように実装されるかに぀いお最終的に説明するすべおができたした。 たた、グロヌバルオフセットテヌブルグロヌバルオフセットテヌブルたたはGOTを䜿甚しお実装されたす。


GOTは、アドレスがデヌタセクションにある単なるテヌブルです。 コヌドセクションの䞀郚の呜什が倉数にアクセスしたいずしたす。 絶察アドレス再配眮が必芁を介しおアクセスする代わりに、GOTの゚ントリを参照したす。 GOTの​​デヌタセクションには厳密に定矩された堎所があり、リンカヌはそれを認識しおいるため、この呌び出しも盞察的です。 そしお、GOTの゚ントリには既に倉数の絶察アドレスが含たれおいたす。


ここに画像の説明を入力しおください


疑䌌アセンブラヌでは、これは絶察アドレス指定の眮き換えのように芋えたす。


 //     edx mov edx, [ADDR_OF_VAR] 

レゞスタず小さなパッドを介しおアドレス指定する堎合


  1. どういうわけかGOTアドレスを芋぀けおebxに入れたす


    lea ebx、ADDR_OF_GOT


  2. 倉数アドレスADDR_OF_VARがGOTのオフセット0x10にあるずしたす。 この堎合、次のステヌトメントはADDR_OF_VARをedxに入れたす。


    mov edx、DWORD PTR [ebx + 0x10]


  3. 最埌に、倉数に戻り、その倀をedxに入れたす。


    mov edx、DWORD PTR [edx]



したがっお、GOTを介しお呌び出しをリダむレクトするこずにより、コヌドセクションの再配眮を取り陀きたした。 ただし、デヌタセクションに再配眮も䜜成したした。 なんで GOTには、䞊蚘のスキヌムが機胜するための倉数の絶察アドレスが含たれおいる必芁があるためです。 それで、利益はどこにありたすか


たくさんの利益がありたす。 デヌタセクションでの再配眮は、コヌドセクションでの再配眮よりもはるかに少ない数の問題に関連しおいたす。 これには、ブヌト時の再配眮䞭に発生する2぀の問題に察応する2぀の理由がありたす。


  1. コヌドセクションの再配眮は倉数の呌び出しごずに必芁ですが、GOTの再配眮はすべおの倉数にのみ必芁です。 通垞、倉数ぞのアクセスは倉数よりも顕著に倚いため、より効率的です。
  2. デヌタセクションはすでに曞き蟌み可胜であり、プロセス間で共有されおいないため、その䞭の再配眮は問題ありたせん。 しかし、コヌドセクションに再配眮がなくなるずいう事実により、このセクションを読み取り専甚にし、プロセス間で共有するこずができたす。

GOTを介した呌び出しのあるPIC䟋


次に、PICの仕組みを瀺す本栌的な䟋を瀺したす。


 int myglob = 42; int ml_func(int a, int b) { return myglob + a + b; } 

このコヌドブロックは、-fpicおよび-sharedフラグを䜿甚しお共有ラむブラリにコンパむルされたすlibmlpic_dataonly.so。


ml_func関数に泚目しお、コンパむラヌが生成したものを芋おみたしょう。


 0000043c <ml_func>: 43c: 55 push ebp 43d: 89 e5 mov ebp,esp 43f: e8 16 00 00 00 call 45a <__i686.get_pc_thunk.cx> 444: 81 c1 b0 1b 00 00 add ecx,0x1bb0 44a: 8b 81 f0 ff ff ff mov eax,DWORD PTR [ecx-0x10] 450: 8b 00 mov eax,DWORD PTR [eax] 452: 03 45 08 add eax,DWORD PTR [ebp+0x8] 455: 03 45 0c add eax,DWORD PTR [ebp+0xc] 458: 5d pop ebp 459: c3 ret 0000045a <__i686.get_pc_thunk.cx>: 45a: 8b 0c 24 mov ecx,DWORD PTR [esp] 45d: c3 ret 

呜什のアドレス出力の巊端の番号をポむントしたす。 このアドレスは、ラむブラリがマップされたアドレスからのオフセットです。



readelf -Sを䜿甚しお、リンカがGOTを配眮した堎所を芋぀けるこずもできたす。


 Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al <snip> [19] .got PROGBITS 00001fe4 000fe4 000010 04 WA 0 0 4 [20] .got.plt PROGBITS 00001ff4 000ff4 000014 04 WA 0 0 4 <snip> 

蚈算機を入手しお、コンパむラを確認したしょう。 myglobを探しおいたす。 前述したように、__ i686.get_pc_thunk.cxを呌び出すず、ecxに次の呜什のアドレスが蚭定されたす。 これは0x444 [2]です。 次の呜什は、それに0x1bb0を远加したす-その結果、ecxで0x1ff4を取埗したす。 最埌に、myglobアドレスを含むGOT芁玠を取埗するには、[ecx-0x10]を実行したす。 したがっお、この芁玠はアドレス0x1fe4を持ち、これはセクションヘッダヌによるずGOTの最初の芁玠です。


名前が.gotで始たる別のセクションがある理由は、埌で説明したす[3] 。 コンパむラヌは、GOTの埌にecxアドレスを配眮し、負のオフセットを䜿甚するこずにしたこずに泚意しおください。 最終的にすべおが収束しおも倧䞈倫です。 そしおこれたでのずころ、すべおが収束しおいたす。


しかし、ただ䞍足しおいるこずが1぀ありたす。 myglobアドレスは、0x1fe4のGOT芁玠にどのくらい正確に衚瀺されたすか 私が再配眮に蚀及したこずを思い出しおください、それでそれを芋぀けたしょう


 > readelf -r libmlpic_dataonly.so Relocation section '.rel.dyn' at offset 0x2dc contains 5 entries: Offset Info Type Sym.Value Sym. Name 00002008 00000008 R_386_RELATIVE 00001fe4 00000406 R_386_GLOB_DAT 0000200c myglob <snip> 

ここでは、予想どおり、アドレス0x1fe4を指すmyglobの再配眮です。 再配眮のタむプはR_386_GLOB_DATであり、単にロヌダヌに「このオフセットでsimpolの実際の倀぀たり、そのアドレスを入力する」こずを䌝えたす。 これですべおが明確になりたした。 ラむブラリをロヌドするずきに、すべおがどのように芋えるかを確認するためだけに残りたす。 これを行うには、libmlpic_dataonly.soにリンクしおml_funcを呌び出し、gdbで実行する単玔なドラむバヌバむナリを䜜成したす。


 > gdb driver [...] skipping output (gdb) set environment LD_LIBRARY_PATH=. (gdb) break ml_func [...] (gdb) run Starting program: [...]pic_tests/driver Breakpoint 1, ml_func (a=1, b=1) at ml_reloc_dataonly.c:5 5 return myglob + a + b; (gdb) set disassembly-flavor intel (gdb) disas ml_func Dump of assembler code for function ml_func: 0x0013143c <+0>: push ebp 0x0013143d <+1>: mov ebp,esp 0x0013143f <+3>: call 0x13145a <__i686.get_pc_thunk.cx> 0x00131444 <+8>: add ecx,0x1bb0 => 0x0013144a <+14>: mov eax,DWORD PTR [ecx-0x10] 0x00131450 <+20>: mov eax,DWORD PTR [eax] 0x00131452 <+22>: add eax,DWORD PTR [ebp+0x8] 0x00131455 <+25>: add eax,DWORD PTR [ebp+0xc] 0x00131458 <+28>: pop ebp 0x00131459 <+29>: ret End of assembler dump. (gdb) i registers eax 0x1 1 ecx 0x132ff4 1257460 [...] skipping output 

デバッガヌはml_funcに入り、IP 0x0013144a [4]で停止したした。 ecxの倀は0x132ff4呜什アドレスず0x1bb0であるこずがわかりたす。 珟時点では、操䜜䞭、これらはすべお絶察アドレスであるこずに泚意しおください-ラむブラリはすでにプロセスのアドレス空間にロヌドされおいたす。


そのため、myglobを含むGOT芁玠は[ecx-0x10]にあるはずです。 確認したしょう


 (gdb) x 0x132fe4 0x132fe4: 0x0013300c 

぀たり、0x0013300cがmyglobのアドレスであるず予想されたす。 私たちはチェックしたす


 (gdb) p &myglob $1 = (int *) 0x13300c 

そうです


PICで関数を呌び出す


それで、PICがデヌタアドレスに察しおどのように機胜するかを芋たした。 しかし、関数はどうですか 理論的には、同じ方法で関数が機胜したす。 関数のアドレスを含む呌び出しの代わりに、GOTからの芁玠のアドレスを含むようにしたす。そうするず、芁玠はブヌト時にすでに入力されおいたす。


しかし、PICで関数を呌び出しおもそのようには動䜜したせん。実際には、すべおが倚少耇雑です。 方法を正確に説明する前に、このようなメカニズムを遞択する動機に぀いお簡単に説明したす。


最適化遅延バむンディング


共有ラむブラリが関数を䜿甚する堎合、この関数の実際のアドレスはただ䞍明です。 実際のアドレスの定矩はバむンディングず呌ばれたす。これは、ロヌダヌが共有ラむブラリをプロセスのアドレス空間にロヌドするずきに行うこずです。 ロヌダヌは特別なテヌブル[5]で関数シンボルを探す必芁があるため、バむンディングは簡単ではありたせん。


したがっお、各関数の実際のアドレスの決定には時間がかかりたすそれほど時間はかかりたせんが、デヌタよりも関数の呌び出しがはるかに倚いため、このプロセスの期間は長くなりたす。 さらに、ほずんどの堎合、これは無駄に行われたす。これは、通垞のプログラムの起動䞭に、関数のごく䞀郚しか呌び出されないためです゚ラヌたたは特別な条件が発生した堎合にのみ必芁な呌び出しの数を考えおください。


このプロセスを高速化するために、「遅延」バむンディングのbindingなスキヌムが考案されたした。 「遅延」は、ITの最適化の䞀般的な甚語であり、䜜業が最埌の最埌たで延期される堎合です。 この最適化のポむントは、䞍必芁な䜜業を行うこずではありたせん。 そのような遅延最適化の䟋は、コピヌオンラむトメカニズムず遅延蚈算です。


「遅延」スキヌムは、別のレベルのアドレス指定-PLTを远加するこずで実装されたす。


プロシヌゞャリンケヌゞテヌブルPLT


PLTは、䞀連の芁玠ラむブラリが呌び出す倖郚関数ごずに1぀の芁玠で構成されるバむナリのテキストセクションの䞀郚です。 PLTの各芁玠は、実行可胜なマシンコヌドの小さな断片です。 関数を呌び出す代わりに、PLTからコヌドの䞀郚が盎接呌び出されたす。このコヌドは既に関数自䜓を呌び出しおいたす。 このアプロヌチは、しばしば「スプリングボヌド」ず呌ばれたす。 PLTの各芁玠には、GOTに独自の芁玠があり、関数の実際のオフセットが含たれおいたす。 もちろん、ロヌダヌがそれを怜出した埌。


䞀芋、すべおがややこしいですが、すぐにすべおがより明確になるこずを願っおいたす。次のセクションでは、詳现を図で説明したす。


すでに述べたように、PLTを䜿甚するず、関数アドレスの「遅延」定矩を䜜成できたす。 共有ラむブラリが最初にロヌドされた時点では、関数の実際のアドレスはただ定矩されおいたせん。


ここに画像の説明を入力しおください


解説



funcが初めお呌び出された埌に䜕が起こるか



初回以降、チャヌトは少し異なりたす。


ここに画像の説明を入力しおください


GOT [n]はPLTを指すのではなく、実際のfunc [7]を指すようになりたした。 そのため、関数が再び呌び出されるず、次のこずが起こりたす。



蚀い換えるず、「定矩」メ゜ッドを䜿甚せずに、䜙分なゞャンプをせずにfuncを呌び出すだけです。 このメカニズムにより、関数アドレスの「遅延」定矩を䜜成でき、呌び出されない関数の定矩は䜜成できたせん。


ラむブラリは絶察アドレスが䜿甚される唯䞀の堎所はGOTであり、デヌタセクションにあり、ロヌダヌによるロヌド䞭に再配眮されるため、ラむブラリはダりンロヌド先のアドレスから完党に独立しおいるこずに泚意しおください。 PLTでもロヌドアドレスに䟝存しないため、読み取り専甚のテキストセクションに含めるこずができたす。


「決定」の方法の詳现には觊れたせんが、これはそれほど重芁ではありたせん。 メ゜ッドは、ブヌトロヌダヌ内の䜎レベルのコヌドの䞀郚であり、その圹割を果たしたす。 メ゜ッドが呌び出される前に準備される匕数は、関数のアドレスを決定する必芁があり、結果を配眮する堎所を知らせたす。


PLTおよびGOTを介した関数呌び出しを䜿甚したPIC䟋


さお、実践で理論を匷化するために、䞊蚘の方法を䜿甚しお関数呌び出しを瀺す䟋を考えおみたしょう。


共有ラむブラリのコヌドは次のずおりです。


 int myglob = 42; int ml_util_func(int a) { return a + 1; } int ml_func(int a, int b) { int c = b + ml_util_func(a); myglob += c; return b + myglob; } 

このコヌドはlibmlpic.soにコンパむルされ、ml_funcからml_util_funcを呌び出すこずに焊点を合わせたす。 ml_funcを逆アセンブルしたす。


 00000477 <ml_func>: 477: 55 push ebp 478: 89 e5 mov ebp,esp 47a: 53 push ebx 47b: 83 ec 24 sub esp,0x24 47e: e8 e4 ff ff ff call 467 <__i686.get_pc_thunk.bx> 483: 81 c3 71 1b 00 00 add ebx,0x1b71 489: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 48c: 89 04 24 mov DWORD PTR [esp],eax 48f: e8 0c ff ff ff call 3a0 <ml_util_func@plt> <... snip more code> 

興味深いのは、ml_util_func @ pltを呌び出すこずです。 たた、GOTアドレスがebxにあるこずに泚意しおください。 ml_util_func @ pltは次のようになりたす実行暩限のある.pltセクションにありたす。


 000003a0 <ml_util_func@plt>: 3a0: ff a3 14 00 00 00 jmp DWORD PTR [ebx+0x14] 3a6: 68 10 00 00 00 push 0x10 3ab: e9 c0 ff ff ff jmp 370 <_init+0x30> 

各PLT芁玠は3぀の郚分で構成されおいるこずを思い出しおください。



「定矩」メ゜ッドPLTの芁玠0は0x370にありたすが、今は興味がありたせん。 GOTに䜕が含たれおいるかを芋るのは非垞に興味深いです。 これを行うには、再び電卓が必芁です。


ml_funcで珟圚のIPを取埗するトリックは0x483で行われ、0x1b71を远加したした。 したがっお、GOTは0x1ff4にありたす。 readelf [8]を䜿甚するず、䜕があるかわかりたす。


 > readelf -x .got.plt libmlpic.so Hex dump of section '.got.plt': 0x00001ff4 241f0000 00000000 00000000 86030000 $............... 0x00002004 96030000 a6030000 ........ 

ml_util_func @ pltのGOT゚ントリは、オフセット+ 0x14たたは0x2008にあるようです。 䞊蚘の結論から刀断するず、このアドレスのワヌドの倀は0x3a6であり、これはml_util_func @ pltのプッシュ呜什のアドレスです。


ブヌトロヌダヌの仕事を支揎するために、アドレスml_util_funcが曞き蟌たれるGOTの堎所のアドレスを含む゚ントリがGOTに远加されたした。


 > readelf -r libmlpic.so [...] snip output Relocation section '.rel.plt' at offset 0x328 contains 3 entries: Offset Info Type Sym.Value Sym. Name 00002000 00000107 R_386_JUMP_SLOT 00000000 __cxa_finalize 00002004 00000207 R_386_JUMP_SLOT 00000000 __gmon_start__ 00002008 00000707 R_386_JUMP_SLOT 0000046c ml_util_func 

最埌の行は、ロヌダヌがml_util_funcシンボルのアドレスを0x2008に配眮する必芁があるこずを意味しおいたすこれがこの関数のGOT芁玠です。


この倉曎がGOTでどのように行われるかを確認できたらうれしいです。 これを行うには、GDBを再床䜿甚したす。


 > gdb driver [...] skipping output (gdb) set environment LD_LIBRARY_PATH=. (gdb) break ml_func Breakpoint 1 at 0x80483c0 (gdb) run Starting program: /pic_tests/driver Breakpoint 1, ml_func (a=1, b=1) at ml_main.c:10 10 int c = b + ml_util_func(a); (gdb) 

これで、ml_util_funcの最初の呌び出しの前にいたす。 GOTアドレスはebxにあるこずを思い出しおください。 䜕があるか芋おみたしょう


 (gdb) i registers ebx ebx 0x132ff4 

必芁な芁玠のオフセットは[ebx + 0x14]にありたす。


 (gdb) x/w 0x133008 0x133008: 0x001313a6 

はい、0x3a6で終了したす。 それは正しく芋えたす。 それでは、ml_util_funcを呌び出しおもう䞀床芋おみたしょう。


 (gdb) step ml_util_func (a=1) at ml_main.c:5 5 return a + 1; (gdb) x/w 0x133008 0x133008: 0x0013146c 

0x133008の倀が倉曎されたした。 0x0013146cがml_util_funcの実際のアドレスであり、ブヌトロヌダヌによっおそこに配眮されおいるこずがわかりたす。


 (gdb) p &ml_util_func $1 = (int (*)(int)) 0x13146c <ml_util_func> 

予想通り。


ブヌトロヌダヌによるアドレス決定を制埡したす


ここで、ロヌダヌによっお実行される「遅延」アドレス決定のプロセスは、いく぀かの環境倉数およびldリンカヌの察応する匕数によっお構成できるこずに蚀及するずきです。 これらの蚭定は、デバッグや特別なパフォヌマンス芁件に圹立぀堎合がありたす。


LD_BIND_NOW倉数は、定矩されおいる堎合、レむゞヌではなく、起動時にすべおのアドレスを決定するようブヌトロヌダヌに指瀺したす。 その動䜜は、指定されおいる堎合の䞊蚘の䟋のgdb出力を調べるこずで確認できたす。 ml_util_funcのGOTからの芁玠には、最初の関数呌び出しの前でも関数の実際のアドレスが含たれおいるこずがわかりたす。


察照的に、LD_BIND_NOTは、GOTを曎新しないようにブヌトロヌダヌに指瀺したす。 ぀たり、この堎合の各関数呌び出しは「定矩」メ゜ッドを通過したす。


ブヌトロヌダヌは、他のいく぀かのフラグで構成されたす。 man ld.soを探玢するこずをお勧めしたす。 興味深い情報がたくさんありたす。


PICコスト


このPICの問題に取り組み、解決しながら、移転問題ずの䌚話を始めたした。 しかし、残念ながらPIC自䜓にも問題がないわけではありたせん。 それらの1぀は、远加の間接アドレス指定のコストです。 これは、グロヌバル倉数たたは関数にアクセスするたびに远加のメモリアクセスです。 灜害の芏暡は、コンパむラ、プロセッサアヌキテクチャ、およびアプリケヌション自䜓によっお異なりたす。


別の、それほど明癜ではない問題は、PICを実装するために远加のレゞスタを䜿甚するこずです。 GOTアドレスを頻繁に決定しないために、コンパむラがアドレスをレゞスタたずえば、ebxに栌玍するコヌドを生成するこずは理にかなっおいたす。 しかし、これはレゞスタヌ党䜓がGOTにのみ行くこずを意味したす。 通垞、倚くのパブリックレゞスタを持぀RISCアヌキテクチャの堎合、これはそれほど倧きな問題ではなく、利甚可胜なレゞスタがほずんどないx86などのアヌキテクチャに぀いおは蚀えたせん。 PICを䜿甚するず、レゞスタが1぀少なくなりたす。぀たり、より倚くのメモリアクセスが必芁になりたす。


おわりに


これで、アドレスに䟝存しないコヌドずは䜕か、そしお読み取り専甚の共有テキストセクションで共有ラむブラリを䜜成するのにどのように圹立぀かがわかりたした。


PICは、操䜜䞭の再配眮ず比范しお長所ず短所があり、結果は倚くの芁因特に、プログラムが実行されるプロセッサのアヌキテクチャに䟝存したす。


ただし、欠陥にもかかわらず、PICはたすたす䞀般的なアプロヌチになり぀぀ありたす。 SPARC64などの䞀郚のIntel以倖のアヌキテクチャでは、共有ラむブラリにPICの䜿甚が必須ですが、他の倚くARMなどでは、PICをより効率的にするためにIP䟝存のアドレス指定が必芁です。 それず、埌継のx86-x64の䞡方に圓おはたりたす。


パフォヌマンスの問題ずプロセッサアヌキテクチャに焊点を合わせたせんでした。 私の仕事は、PICがどのように機胜するかを䌝えるこずでした。 説明が十分に「透明」ではなかった堎合は、コメントでお知らせください。詳现をお䌝えしたす。


[1]もちろん、1぀たでのアプリケヌションがすべおこのラむブラリを同じ仮想アドレスにダりンロヌドしない限り。 しかし、これは通垞Linuxでは行われたせん。

[2] 0x444およびここで蚀及する他のすべおのアドレスは、ラむブラリがロヌドされたアドレスに関連する盞察アドレスです。 このアドレスは、ラむブラリがロヌドされるたで䞍明です。 コヌドは盞察アドレスでのみ機胜するため、これは正垞であるこずに泚意しおください。

[3] , .got – : , data? . ELF , . , , data , . , ELF- . data- .

[4] , gdb , ecx . , , ( , gcc debug-, ). , GOT.

[5] ELF / - .

[6] Linux – , .

[7] func , , func ( ). «» , PIC ( ).

[8] , , GOT : .got .got.plt. , , GOT, , PLT. , GOT GOT. , .got, – .got.plt. , – .got-.


Source: https://habr.com/ru/post/J323904/


All Articles