当我使用as
(binutils)编译汇编代码并使用link.exe(Visual Studio 2015)进行链接时,程序因地址未分配而崩溃。
与gcc(gcc hello-64-gas.obj -o hello-64-gas.exe
)链接时,程序正常运行而不会崩溃。
我是否正确地假设as
生成的目标文件应该独立于编译器,因为汇编代码编写者手中的abi兼容性问题?
由于我是初学者,所以对我的错误/错误假设的任何解释都是值得赞赏的。
as
以下代码不正确链接:
# hello-64-gas.asm print a string using printf
# Assemble: as hello-64-gas.asm -o hello-64-gas.obj --64
# Link: link -subsystem:CONSOLE hello-64-gas.obj -out:hello-64-gas.exe libcmt.lib libvcruntime.lib libucrt.lib legacy_stdio_definitions.lib
.intel_syntax noprefix
.global main
# Declare needed C functions
.extern printf
.section .data
msg: .asciz "Hello world"
fmt: .asciz "%s(%d; %f)\n"
myDouble: .double 2.33, -1.0
.text
main:
sub rsp, 8*5
mov rcx, offset flat: fmt
mov rdx, offset flat: msg
mov r8, 0xFF
mov r9, offset flat: myDouble
mov r9, [r9]
movq xmm4, r9
call printf
add rsp, 8*5
mov rax, 0
ret
调试时似乎mov r9, offset flat: myDouble
未重新定位mov r9,18h
,18h
如果位置为零的.data
部分则正确objdump -dr hello-64-gas.obj
。使用...
19: 49 c7 c1 18 00 00 00 mov $0x18,%r9
1c: R_X86_64_32S .data
...
查看重定位表会产生:
mov
用movabs
替换# hello-64-gas.asm print a string using printf
# Assemble: as hello-64-gas.asm -o hello-64-gas.obj --64
# Link: link -subsystem:CONSOLE hello-64-gas.obj -out:hello-64-gas.exe libcmt.lib libvcruntime.lib libucrt.lib legacy_stdio_definitions.lib
.intel_syntax noprefix
.global main
# Declare needed C functions
.extern printf
.section .data
msg: .asciz "Hello world"
fmt: .asciz "%s(%d; %f)\n"
myDouble: .double 2.33, -1.0
.text
main:
sub rsp, 8*5
movabs rcx, offset flat: fmt
movabs rdx, offset flat: msg
mov r8, 0xFF
movabs r9, offset flat: myDouble
mov r9, [r9]
movq xmm4, r9
call printf
add rsp, 8*5
mov rax, 0
ret
似乎有效:
link.exe
使用 <plugin>
<groupId>...</groupId>
<artifactId>text-replace-plugin</artifactId>
<version>...</version>
<executions>
<execution>
...
<configuration>
<replacements>
<property>
<name>ABCD</name>
<value>XYZ</value>
</property>
<property>
<name>XYZ</name>
<value>PQR</value>
</property>
</replacements>
...
</configuration>
</execution>
</executions>
</plugin>
链接时,这会以某种方式正常运行。
答案 0 :(得分:4)
Microsoft的链接器不支持GNU汇编程序用于引用myDouble
以及fmt
和msg
的重定位。这种重定位由GNU实用程序调用{{1}}并且值为0x11,未在Microsoft's PECOFF specification中记录。可以通过在目标文件上使用Microsoft的DUMPBIN来证明,Microsoft的链接器似乎使用具有此值的重定位来实现其他一些未记录的目的:
R_X86_64_32S
作为解决方法,你可以使用:
按顺序将这些写成:
RELOCATIONS #1
Symbol Symbol
Offset Type Applied To Index Name
-------- ---------------- ----------------- -------- ------
00000007 EHANDLER 7 .data
0000000E EHANDLER 7 .data
0000001C EHANDLER 7 .data
00000029 REL32 00000000 C printf
这些以及lea r9, [rip + myDouble]
movabs r9, offset myDouble
mov r9d, offset myDouble
是四个不同的指令,具有不同的编码和微妙的不同语义,每个都需要不同类型的重定位。
LEA指令将mov r9, offset myDouble
编码为相对于RIP的32位有符号偏移量。这是在这里使用的首选指令,因为它只需要4个字节来编码地址,它允许将可执行文件加载到64位地址空间的任何地方。唯一的限制是可执行文件的大小必须小于2G,但这仍然是x64 PECOFF可执行文件的基本限制。
MOVABS将myDouble
编码为64位绝对地址。虽然理论上这允许myDouble
位于64位地址空间中的任何位置,甚至超过指令2G,但它需要8个字节的编码空间,并且实际上并不能在Windows下获得任何内容。 / p>
32位MOV指令将myDouble
编码为无符号32位绝对地址。它的缺点是要求将可执行文件加载到第一个4G地址空间中的某个地方。因此,您需要在Microsoft链接器中使用myDouble
标志,否则您将收到错误。
您正在使用的64位MOV指令将/LARGEADDRESSAWARE:NO
编码为32位带符号的绝对地址。这也限制了可执行文件的加载位置,并且需要一种重定位类型,Microsoft的PECOFF格式没有记录为具有Microsoft链接器并且不受其支持。