与link.exe链接时未定位的地址

时间:2016-03-24 17:06:06

标签: windows assembly linker x86-64 mingw-w64

问题

当我使用as(binutils)编译汇编代码并使用link.exe(Visual Studio 2015)进行链接时,程序因地址未分配而崩溃。

与gcc(gcc hello-64-gas.obj -o hello-64-gas.exe)链接时,程序正常运行而不会崩溃。 我是否正确地假设as生成的目标文件应该独立于编译器,因为汇编代码编写者手中的abi兼容性问题? 由于我是初学者,所以对我的错误/错误假设的任何解释都是值得赞赏的。

平台

  • Windows 10,64位
  • 链接器:使用本机命令工具命令提示符(x64)
  • 的Visual Studio 2015
  • 编译器:来自MinGW-w64的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,18h18h如果位置为零的.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> 链接时,这会以某种方式正常运行。

1 个答案:

答案 0 :(得分:4)

Microsoft的链接器不支持GNU汇编程序用于引用myDouble以及fmtmsg的重定位。这种重定位由GNU实用程序调用{​​{1}}并且值为0x11,未在Microsoft's PECOFF specification中记录。可以通过在目标文件上使用Microsoft的DUMPBIN来证明,Microsoft的链接器似乎使用具有此值的重定位来实现其他一些未记录的目的:

R_X86_64_32S

作为解决方法,你可以使用:

  • 具有RIP相对寻址的LEA指令,生成R_X86_64_PC32 / REL32重定位
  • 正如您自己发现的那样,MOVABS指令生成R_X86_64_64 / ADDR64重定位
  • 生成R_X86_64_32 / ADDR32重定位的32位MOV指令

按顺序将这些写成:

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链接器并且不受其支持。