[英]How to access a C global variable through GOT in GAS assembly on x86-64 Linux?
我正在嘗試編寫一個共享庫(不是可執行文件,所以請不要告訴我使用-no-pie
)與單獨文件中的程序集和 C(不是內聯程序集)。
我想通過匯編代碼中的全局偏移表訪問 C 全局變量,因為調用的函數可能在任何其他共享庫中定義。
我知道 PLT/GOT 的東西,但我不確定如何告訴編譯器為鏈接器正確生成重定位信息(語法是什么),以及如何告訴鏈接器使用該信息實際重定位我的代碼(什么是是鏈接器選項)。
我的代碼編譯時出現鏈接錯誤
/bin/ld: tracer.o: relocation R_X86_64_PC32 against
/bin/ld: final link failed: bad value
此外,如果有人可以分享一些有關搬遷的 GAS 組件的詳細文檔,那就更好了。 例如,關於如何使用 GNU 匯編器在 C 和匯編之間進行插值的詳盡列表。
編譯 C 和匯編代碼並將它們鏈接到一個共享庫中。
# Makefile
liba.so: tracer2.S target2.c
gcc -shared -g -o liba.so tracer2.S target2.c
// target2.c
// NOTE: This is a variable, not a function.
int (*read_original)(int fd, void *data, unsigned long size) = 0;
// tracer2.S
.text
// external symbol declarition
.global read_original
read:
lea read_original(%rip), %rax
mov (%rax), %rax
jmp *%rax
我希望鏈接器愉快地鏈接我的目標文件,但它說
g++ -shared -g -o liba.so tracer2.o target2.c -ldl
/bin/ld: tracer.o: relocation R_X86_64_PC32 against
/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status
make: *** [Makefile:2: liba.so] Error 1
並注釋掉該行
// lea read_original(%rip), %rax
使錯誤消失。
lea read_original@GOTPCREL(%rip), %rax
關鍵字GOTPCREL
將告訴編譯器這是到 GOT 表的與 PC 相關的重定位。 鏈接器將計算從當前rip
到目標 GOT 表條目的偏移量。
你可以驗證
$ objdump -d liba.so
10e9: 48 8d 05 f8 2e 00 00 lea 0x2ef8(%rip),%rax # 3fe8 <read_original@@Base-0x40>
10f0: 48 8b 00 mov (%rax),%rax
10f3: ff e0 jmpq *%rax
感謝彼得。
$ objdump -d liba.so
...
0000000000001109 <read1>:
1109: e8 22 ff ff ff callq 1030 <read@plt>
110e: ff e0 jmpq *%rax
objdump
顯示它調用了正確的 PLT 條目。
$ objdump -d liba.so ... 0000000000001109 <read1>: 1109: e8 22 ff ff ff callq 1030 <read@plt> 110e: ff e0 jmpq *%rax
2.我可以lea
正確PLT條目地址
0xffffff23 是 -0xdd,0x1109 - 0xdd = 102c
$ uname -a
Linux alex-arch 5.2.6-arch1-1-ARCH #1 SMP PREEMPT Sun Aug 4 14:58:49 UTC 2019 x86_64 GNU/Linux
$ uname -a Linux alex-arch 5.2.6-arch1-1-ARCH #1 SMP PREEMPT Sun Aug 4 14:58:49 UTC 2019 x86_64 GNU/Linux
$ gcc -v Using built-in specs. COLLECT_GCC=/bin/gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/lto-wrapper Target: x86_64-pc-linux-gnu Configured with: /build/gcc/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --enable-multilib --disable-werror --enable-checking=release --enable-default-pie --enable-default-ssp --enable-cet=auto Thread model: posix gcc version 9.1.0 (GCC)
$ ld --version GNU ld (GNU Binutils) 2.32 Copyright (C) 2019 Free Software Foundation, Inc. This program is free software; you may redistribute it under the terms of the GNU General Public License version 3 or (at your option) a later version. This program has absolutely no warranty.
顯然,鏈接器對 ELF 共享對象中的符號強制執行全局可見性與隱藏可見性,不允許“后門”訪問參與符號插入的符號(因此可能超過 2GB。)
要使用正常的 RIP 相對尋址直接從同一共享對象中的其他代碼訪問它,請通過設置其 ELF 可見性來隱藏符號。 (另請參閱https://www.macieira.org/blog/2012/01/sorry-state-of-dynamic-libraries-on-linux/和 Ulrich Drepper 的如何編寫共享庫)
__attribute__ ((visibility("hidden")))
int (*read_original)(int fd, void *data, unsigned long size) = 0;
然后gcc -save-temps tracer2.S target2.c -shared -fPIC
編譯/匯編 + 鏈接共享庫。 GCC也有類似的選項-fvisibility=hidden
,使默認,要求在符號明確的屬性,你想出口的動態鏈接。 如果您在庫中使用了任何全局變量,那么這是一個非常好的主意,可以讓編譯器發出使用它們的高效代碼。 它還可以保護您免受與其他庫的全局名稱沖突。 GCC 手冊強烈推薦它。
它也適用於g++
; C++ 名稱修改僅適用於函數名稱,不適用於變量(包括函數指針)。 但通常不要使用 C++ 編譯器編譯.c
文件。
如果您確實想支持符號插入,則需要使用 GOT; 顯然你可以看看編譯器是如何做到的:
int glob; // with default visibility = default
int foo() { return glob; }
使用 GCC -O3 -fPIC
編譯為這個 asm (沒有任何可見性選項,因此全局符號是完全全局可見的:從共享對象導出並參與符號插入)。
foo:
movq glob@GOTPCREL(%rip), %rax
movl (%rax), %eax
ret
顯然,這比mov glob(%rip), %eax
效率低mov glob(%rip), %eax
因此更喜歡將全局變量的范圍限制在庫中(隱藏),而不是真正的全局變量。
您可以使用弱別名執行一些技巧,讓您導出僅該庫定義的符號,並通過“隱藏”別名有效地訪問該定義。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.