Sistem pengendalian biasanya menggunakan pemautan dinamik untuk meningkatkan kecekapan program berjalan. Dalam kes pemautan dinamik, semua fungsi dalam pustaka pautan tidak dimuatkan bersama-sama apabila program dimuatkan, sebaliknya, ia dimuatkan atas permintaan apabila program dijalankan Jika fungsi tidak dipanggil, maka ia tidak akan dimuatkan program itu dimuatkan ke dalam kehidupan. Reka bentuk ini boleh meningkatkan kelancaran operasi program dan mengurangkan ruang ingatan. Selain itu, sistem pengendalian moden tidak membenarkan pengubahsuaian segmen kod, hanya segmen data, jadi jadual GOT dan jadual PLT wujud.
Mari kita lihat contoh ringkas
Mari kita ikuti up with scanf@plt
Anda akan mendapati terdapat tiga baris kod
jmp 一个地址 push 一个值到栈里面 jmp 一个地址
Melihat nama fungsi, anda boleh tahu bahawa ini adalah jadual plt fungsi scanf Jangan tergesa-gesa untuk memahami apa yang digunakan untuk apa?
Malah, ini ialah jadual mendapat fungsi yang sepadan dengan jadual plt, dan kami akan mendapati bahawa nilai 0x201020 ialah alamat arahan tolak, dan tempat lain ialah 0. Pada masa ini, saya ingin bertanya :
1. Apakah kepentingan got table dan plt table?
2. Apakah kaitan antara jadual got dan jadual plt?
Jadi jika anda mempunyai soalan, lihat jawapannya dahulu, dan kemudian sahkannya. .
Dalam contoh di atas, kita dapat melihat panggilan scanf —> jadual plt scanf —> scanf's got table Bagi nilai jadual got, kami tidak mengambil berat mengenainya buat masa ini pemikiran sedemikian pada masa ini. Fungsi scanf sebenar boleh didapati dari jadual got untuk program dimuatkan dan dijalankan.
Selepas kami fikir begitu, ia menjadi proses pengalamatan tidak langsung
Kami mendapat segmen data yang menyimpan alamat fungsi Sekeping kecil kod dipanggil PLT (Jadual Pautan Prosedur) Segmen data di mana alamat fungsi disimpan dipanggil GOT (Global Offset Table). Selepas kita membentuk pemikiran sedemikian, kita boleh memahami dengan teliti butiran di dalamnya.
Setelah memahami proses umum seperti itu, mari kita lihat bagaimana ia dipanggil langkah demi langkah. Terdapat beberapa keraguan di atas . Untuk menyelesaikan:
1. Bagaimanakah jadual mendapat tahu alamat sebenar fungsi scanf?
2. Apakah struktur got table dan plt table? Mari lihat jadual plt dahulu Kami baru mendapati bahawa baris ketiga kod dalam jadual scanf@plt ialah alamat jmp 🎜>Malah, ini adalah jadual PLT program Bermula dengan (plt[0]), apa yang dilakukannya ialah:
push got[1] jmp **got[2]
diikuti dengan jadual plt setiap fungsi. Pada masa ini, mari kita lihat lagi jadual GOT yang misteri ini
Kecuali dua ini (alamat push 0xn bagi fungsi printf dan scanf, iaitu alamat kedua daripada alamat kod jadual plt yang sepadan), yang lain mendapat[1], mendapat[2] ialah 0, jadi apakah yang ditunjukkan oleh jadual plt kepada jadual mendapat iaitu 0? Oleh kerana kami telah ketinggalan dalam keadaan, sistem pengendalian moden tidak membenarkan pengubahsuaian segmen kod, hanya segmen data, iaitu nama yang lebih profesional adalah penempatan semula masa jalan. Apabila kita menjalankan program, alamat sebelumnya dan kandungan yang disimpan telah berubah Sebelum itu, mari kita simpan kandungan semasa pautan dan buat perbandingan
② 寻找printf的plt表 ③ jmp到plt[0] ④ jmp got[2] -> 0x00000 ⑤⑥ printf和scanf的got[3] got[4] -> plt[1] plt[2]的第二条代码的地址 ⑦⑧ 证实上面一点
Jalankan program dan tetapkan. titik putus di scanf
Anda boleh mendapati bahawa jadual scanf@plt telah berubah pada masa ini 🎜>
Teruskan penyahpepijatan di alamat push 0x1, sehingga di sini, alamat got[4] telah diubah suai
Saya ingin bertanya di sini masa, ini di mana?
Kemudian hubungi<_dl_fixup> dalam got[2] untuk mengubah suai alamat dalam got[3];
那么问题就来了,刚才got[2]处不是0吗,怎么现在又是这个(_dl_runtime_resolve)?这就是运行时重定位。
其实got表的前三项是:
got[0]:address of .dynamic section 也就是本ELF动态段(.dynamic段)的装载地址 got[1]:address of link_map object( 编译时填充0)也就是本ELF的link_map数据结构描述符地址,作用:link_map结构,结合.rel.plt段的偏移量,才能真正找到该elf的.rel.plt got[2]:address of _dl_runtime_resolve function (编译时填充为0) 也就是_dl_runtime_resolve函数的地址,来得到真正的函数地址,回写到对应的got表位置中。
那么此刻,got表怎么知道scanf函数的真实地址?
这个问题已经解决了。我们可以看一下其中的装载过程:
说到这个,可以看到在_dl_runtimw_resolve之前和之后,会将真正的函数地址,也就是glibc运行库中的函数的地址,回写到代码段,就是got[n](n>=3)中。也就是说在函数第一次调用时,才通过连接器动态解析并加载到.got.plt中,而这个过程称之为延时加载或者惰性加载。
到这里,也要接近尾声了,当第二次调用同一个函数的时候,就不会与第一次一样那么麻烦了,因为got[n]中已经有了真实地址,直接jmp该地址即可。
Atas ialah kandungan terperinci Bagaimana untuk memahami jadual GOT dan jadual PLT dengan mendalam. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!