考虑 x64 Intel 程序集中的以下变量引用,其中变量a在.data节中声明:
mov eax, dword ptr [rip + _a]
Run Code Online (Sandbox Code Playgroud)
我很难理解这个变量引用是如何工作的。既然a是对应于变量运行时地址的符号(带重定位),如何[rip + _a]解引用正确的内存位置a?事实上,rip保存当前指令的地址,它是一个大的正整数,所以加法会导致错误的地址a?
相反,如果我使用 x86 语法(非常直观):
mov eax, dword ptr [_a]
Run Code Online (Sandbox Code Playgroud)
,我收到以下错误:64 位模式不支持 32 位绝对寻址。
有什么解释吗?
1 int a = 5;
2
3 int main() {
4 int b = a;
5 return b;
6 }
Run Code Online (Sandbox Code Playgroud)
编译gcc -S -masm=intel abs_ref.c -o abs_ref::
1 .section __TEXT,__text,regular,pure_instructions
2 .build_version macos, 10, 14
3 …Run Code Online (Sandbox Code Playgroud) 我试图在nasm中创建一个应该显示该字母的简单程序a.然而,它给了我一个Segfault并说:
./a.out: Symbol `printf' causes overflow in R_X86_64_PC32 relocation
Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud)
基本上,我试图将值0x61(字母a的十六进制)移动到内存地址1234,然后将其作为参数传递给printf.这是我的确切代码:
extern printf
section .text
global main
main:
push rbp
mov rax,0
mov qword [1234], 0x61 ; move 0x61 into address 1234
mov rdi, qword [1234] ; mov address 1234 into rdi
call printf ; should print the letter a
pop rbp
mov rax,0
ret
Run Code Online (Sandbox Code Playgroud)
我正在运行Linux x86_64
我正在Rust写一个JIT(尽管Rust只与我的一些问题有关,其中很大一部分与JIT约定有关).我有我在Rust中实现的内部函数,我需要能够call从我的JIT发出的代码.我的JIT mmap(_, _, PROT_EXEC, MAP_ANONYMOUS | MAP_SHARED)是jitted代码的一些页面.我有我的内在函数的地址,但不幸的是它们比32位偏移更远.我现在试图决定如何发出对这些内在函数的调用.我考虑过以下选项:
(特定于Rust)以某种方式使Rust将内在函数放置在堆附近(可能在?上),以便calls将在32位偏移量内.目前还不清楚Rust是否可行(有一种方法可以指定自定义链接器args,但是我无法告诉它们应用了什么以及我是否可以将单个函数作为重定位目标.即使我可以在哪里放置它?).如果堆足够大,似乎这可能会失败.
(特定于Rust)将我的JIT页面分配给内部函数更接近.这可以通过实现mmap(_, _, PROT_EXEC, MAP_FIXED),但我不确定如何选择一个不会破坏现有Rust代码的地址(并保持在arch限制内 - 是否有一种理智的方式来获得这些限制?).
在JIT页面中创建处理绝对跳转(下面的代码)call的存根,然后是存根.这有利于JITted代码中的(初始)调用站点是一个很好的小型相对调用.但是必须跳过某些东西感觉不对.这似乎对性能有害(可能干扰RAS /跳转地址预测).此外,似乎这种跳转会更慢,因为它的地址是间接的,它取决于mov该地址.
mov rax, {ABSOLUTE_AOTC_FUNCTION_ADDRESS}
jmp rax
Run Code Online (Sandbox Code Playgroud)
与(3)相反,只是在JITed代码中的每个内部调用站点处内联上述内容.这解决了间接问题,但使JITted代码变大(可能这会产生指令缓存和解码后果).它仍然存在跳跃是间接的并且取决于的问题mov.
将内在函数的地址放在JIT页面附近的PROT_READ(仅)页面上.使所有呼叫站点接近,绝对间接呼叫(下面的代码).这从(2)中删除了第二级间接.但遗憾的是,该指令的编码很大(6个字节),因此它与(4)具有相同的问题.另外,现在不是依赖于寄存器,而是不必要地跳转(只要地址在JIT时间已知)依赖于内存,这肯定会影响性能(尽管可能这个页面被缓存了?).
aotc_function_address:
.quad 0xDEADBEEF
# Then at the call site
call qword ptr [rip+aotc_function_address]
Run Code Online (Sandbox Code Playgroud)
具有段寄存器的Futz使其更接近内在函数,以便可以相对于该段寄存器进行调用.这种调用的编码很长(所以这可能有解码管道问题),但除此之外,这在很大程度上避免了它之前的一切棘手的问题.但是,相对于非(正如@prl所指出的,没有远程调用就行不通,这对性能来说太糟糕了)cs细分市场的调用可能表现不佳.或者也许这样的未来是不明智的(例如,与Rust运行时混淆).
不是真正的解决方案,但我可以使编译器32位并且根本没有这个问题.这不是一个很好的解决方案,它也会阻止我使用扩展的通用寄存器(我利用它们).
所有选项都有缺点.简而言之,1和2是唯一似乎没有性能影响的,但目前还不清楚是否有一种非hacky方式来实现它们(或者根本没有任何方式).3-5独立于Rust,但具有明显的性能缺陷.
考虑到这一点,一些(Rust)具体问题:
对于方法(1),是否可以强制Rust链接extern "C"特定地址(堆附近)的某些函数?我应该如何选择这样的地址(在编译时)?假设由mmap(由Rust分配)返回的任何地址都在此位置的32位偏移量内是否安全?
对于方法(2),我如何找到一个合适的位置放置JIT页面(这样它不会破坏现有的Rust代码)?
还有一些JIT(非Rust)特定问题:
对于方法(3),存根是否会妨碍我应该关心的性能?间接jmp怎么样?我知道这有点类似于链接器存根,除非我理解链接器存根至少只解析一次(因此它们不需要是间接的?).任何JIT都采用这种技术吗?
对于方法(4),如果3中的间接呼叫是可以的,那么内联呼叫值得吗?如果JIT通常采用方法(3/4),这个选项更好吗?
对于方法(5),跳转对内存的依赖性(假设地址在编译时已知)是不是很糟糕?这会使(3)或(4)的性能降低吗?任何JIT都采用这种技术吗?
对于方法(6),这样的未来是不明智的吗?(特定于Rust)是否有可用的段寄存器(运行时或ABI未使用)?相对于非cs细分市场的调用是否与相对于cs?的调用相同?
和最后(也是最重要的),是否有我在这里失踪更好的方法(可能由即时编译器采用更常见)?
如果没有我的Rust问题有答案,我无法实现(1)或(2).当然,我可以实现和测试3-5(可能是6,虽然事先了解段寄存器会很好),但考虑到这些是截然不同的方法,我希望有关于此的现有文献我找不到,因为我不知道谷歌的正确条款(我目前也正在研究这些基准).或者也许有人深入研究JIT内部人员可以分享他们的经验或他们常见的东西? …
尝试将非PIC代码编译到x64上的共享库中,gcc导致错误,例如:
/usr/bin/ld: /tmp/ccQ2ttcT.o: relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC
Run Code Online (Sandbox Code Playgroud)
这个问题是为什么会这样.我知道x64具有RIP相对寻址,旨在提高PIC代码的效率.但是,这并不意味着加载时重定位不能(理论上)应用于此类代码.
一些在线资源,包括这个(在这个问题上被广泛引用)声称,由于 RIP相对寻址,在共享库中禁止非PIC代码存在一些固有的限制.我不明白为什么这是真的.
考虑"旧x86" - call指令也有一个IP相关操作数.然而,其中的x86代码可以call很好地编译成没有PIC的共享库,但使用加载时重定位 R_386_PC32.对于x64中的数据RIP相对寻址,不能做同样的事情吗?
请注意,我完全理解PIC代码的好处,并且性能损失RIP相对寻址有助于缓解.不过,我很好奇不允许使用非PIC代码的原因.它背后是否有真正的技术推理,还是鼓励编写PIC代码?
我有这个简短的你好世界计划:
#include <stdio.h>
static const char* msg = "Hello world";
int main(){
printf("%s\n", msg);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我用gcc将它编译成以下汇编代码:
.file "hello_world.c"
.section .rodata
.LC0:
.string "Hello world"
.data
.align 4
.type msg, @object
.size msg, 4
msg:
.long .LC0
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $16, %esp
movl msg, %eax
movl %eax, (%esp)
call puts
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa …Run Code Online (Sandbox Code Playgroud) 我正在尝试将“main”的地址加载到 GNU 汇编器中的寄存器 (R10) 中。我没办法。在这里,我有什么和我收到的错误消息。
main:
lea main, %r10
Run Code Online (Sandbox Code Playgroud)
我还尝试了以下语法(这次使用 mov)
main:
movq $main, %r10
Run Code Online (Sandbox Code Playgroud)
使用以上两种方法,我都会收到以下错误:
/usr/bin/ld: /tmp/ccxZ8pWr.o: relocation R_X86_64_32S against symbol `main' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)
使用 -fPIC 编译不能解决问题,只会给我同样的错误。
我目前正在使用32位代码替换方案,其中代码移动到另一个位置,读取变量和类指针.由于x86_64不支持绝对寻址,因此无法在代码的新位置获取变量的正确地址.问题在于,由于rip相对寻址,指令指针地址与编译时不同.
那么有没有办法在x86_64中使用绝对寻址或以其他方式获取变量的地址而非指令指针相对?
类似的东西:leaq variable(%%rax), %%rbx也会有所帮助.我只想依赖指令指针.
我编写了可以编译的汇编代码:
as power.s -o power.o
Run Code Online (Sandbox Code Playgroud)
当我链接power.o目标文件时出现问题:
ld power.o -o power
Run Code Online (Sandbox Code Playgroud)
为了在64位操作系统(Ubuntu 14.04)上运行,我.code32在power.s文件的开头添加了,但是我仍然得到错误:
分段故障(核心转储)
power.s:
.code32
.section .data
.section .text
.global _start
_start:
pushl $3
pushl $2
call power
addl $8, %esp
pushl %eax
pushl $2
pushl $5
call power
addl $8, %esp
popl %ebx
addl %eax, %ebx
movl $1, %eax
int $0x80
.type power, @function
power:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %ebx
movl 12(%ebp), %ecx
movl %ebx, -4(%ebp)
power_loop_start:
cmpl …Run Code Online (Sandbox Code Playgroud) 这是什么<some symbol>@GOTPCREL(%rip)意思?
我遇到过这一行mov var@GOTPCREL(%rip), %rax,并对奇怪的语法感到有点困惑。
有人可以推荐我应该阅读以理解这一点的相关文档吗?谢谢!
我写了下面的汇编代码,它可以直接通过as和ld构建pass。
as cpuid.s -o cpuid.o
ld cpuid.o -o cpuid
Run Code Online (Sandbox Code Playgroud)
但是当我使用gcc来完成整个过程时。我遇到以下错误。
$ gcc cpuid.s -o cpuid
/tmp/cctNMsIU.o: In function `_start':
(.text+0x0): multiple definition of `_start'
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o:(.text+0x0): first defined here
/usr/bin/ld: /tmp/cctNMsIU.o: relocation R_X86_64_32 against `.data' can not be used when making a shared object; recompile with -fPIC
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
/usr/bin/ld: final link failed: Invalid operation
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)
然后我将 _start 修改为 main,并将 -fPIC 添加到 gcc 参数。但这并不能解决我的 ld 错误。错误消息更改为以下。
$ gcc …Run Code Online (Sandbox Code Playgroud)