我似乎无法通过 WinAPI NASM 教程解决此 LD 错误
I seem to be unable to resolve this LD error from a WinAPI NASM tutorial
对于某些背景知识,我对汇编程序还很陌生,我选择的汇编程序是 NASM。对于我的第一个项目,我在 Windows 8.1 上使用 cygwin,并且我尝试将这个 Whosebug 问题的第二个答案中的信息从 32 位转换为 64 位:How to write hello world in assembler under Windows?
我正在使用 gnu ld 而不是 link.exe,因为我系统上的 kernel32.Lib 似乎一团糟,而且我更喜欢 gnu ld。所以,这是我的汇编程序:
global _main
extern GetStdHandle@4
extern WriteFile@20
extern ExitProcess@4
section .text
_main
push rbp
mov rbp, rsp
sub rsp, 4
; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
push -11
call GetStdHandle@4
mov rbx, rax
; WriteFile(hStdOut, message, length(message), &bytes, 0)
push 0
lea rax, [rbp-4]
push rax
push (message_end - message)
push message
push rbx
call WriteFile@20
mov rsp, rbp
pop rbp
; ExitProcess(0)
push 0
call ExitProcess@4
;
hlt
message db 'Hello, World!', 10
message_end
当我 运行 nasm -fwin64 ./test.asm
它组装时没有错误,只有关于我在标签名称上遗漏冒号的警告。
我用来 link 到 WinAPI 的命令是
ld test.obj -lkernel32 --enable-stdcall-fixup /cygdrive/c/Windows/system32/kernel32.dll -o test.exe
这给我留下了
test.obj:./test.asm:(.text+0x1c): relocation truncated to fit: R_X86_64_32 against `.text'
所以,我利用 google 的优势发现了这个 Whosebug 问题:
What does this GCC error "... relocation truncated to fit..." mean?
我阅读了第一个答案中的material,并进一步搜索了google,然后尝试了第二个答案中提供的解决方案。这是我的 linker 脚本
SECTIONS
{
. = 0x000000000000001b;
.text :
{
*(*)
}
}
我尝试 link 使用此命令:
ld test.obj -T test.ld -lkernel32 --enable-stdcall-fixup /cygdrive/c/Windows/system32/kernel32.dll -o test.exe
我收到这个错误:
ld: cannot find -lkernel32
无论我将 -T test.ld
选项放在哪里,我都会收到此错误。
我被卡住了,我的 google-foo 似乎不足以通过这种方式寻求帮助。我不明白为什么LD在我指定linker脚本时找不到kernel32,我也不知道如何解析t运行cation
如果有帮助,test.obj
的重定位数据的 objdump 是:
$ objdump -Sr test.obj
test.obj: file format pe-x86-64
Disassembly of section .text:
0000000000000000 <_main>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 04 sub [=16=]x4,%rsp
8: 6a f5 pushq [=16=]xfffffffffffffff5
a: e8 00 00 00 00 callq f <_main+0xf>
b: R_X86_64_PC32 GetStdHandle@4
f: 48 89 c3 mov %rax,%rbx
12: 6a 00 pushq [=16=]x0
14: 48 8d 45 fc lea -0x4(%rbp),%rax
18: 50 push %rax
19: 6a 0e pushq [=16=]xe
1b: 68 32 00 00 00 pushq [=16=]x32
1c: R_X86_64_32 .text
20: 53 push %rbx
21: e8 00 00 00 00 callq 26 <_main+0x26>
22: R_X86_64_PC32 WriteFile@20
26: 48 89 ec mov %rbp,%rsp
29: 5d pop %rbp
2a: 6a 00 pushq [=16=]x0
2c: e8 00 00 00 00 callq 31 <_main+0x31>
2d: R_X86_64_PC32 ExitProcess@4
31: f4 hlt
0000000000000032 <message>:
32: 48 rex.W
33: 65 6c gs insb (%dx),%es:(%rdi)
35: 6c insb (%dx),%es:(%rdi)
36: 6f outsl %ds:(%rsi),(%dx)
37: 2c 20 sub [=16=]x20,%al
39: 57 push %rdi
3a: 6f outsl %ds:(%rsi),(%dx)
3b: 72 6c jb a9 <message_end+0x69>
3d: 64 21 0a and %ecx,%fs:(%rdx)
谢谢。
__SOLUTION EDIT__ 未来 google 只猴子:
使用@Jester 的回答中的信息,我重写了程序,现在它可以按预期运行。这是工作来源:
global main
extern GetStdHandle@4
extern WriteFile@20
extern ExitProcess@4
section .text
main
push rbp
mov rbp, rsp
sub rsp, 8
; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
; ABI, pass in registers
mov rcx, -11
call GetStdHandle@4
mov rbx, rax
; WriteFile(hStdOut, message, length(message), &bytes, 0)
mov rcx, rbx
lea rax, [rel message]
mov rdx, rax
mov r8, (message_end - message)
lea rax, [rbp-8]
mov r9, rax
push 0
call WriteFile@20
mov rsp, rbp
pop rbp
; ExitProcess(0)
mov rcx, 0
call ExitProcess@4
;
hlt
message db 'Hello, World!', 10
message_end
Windows 64-bit calling convention 在寄存器中传递前四个整数大小的参数——具体来说,RCX
、RDX
、R8
和 R9
。只有当参数超过 4 个时,它们才会在堆栈上传递。请注意,这 不同于 常见的 32 位调用约定,它在堆栈上传递 所有 参数。
就是说,您的问题是 push message
生成 32 位重定位(因为 push
只需要一个 32 位立即数)。您应该做的是传递字符串的 地址 ,因此,要解决此问题,请使用例如
lea rax, [rel message]
push rax
此处的 rel
关键字确保使用 RIP 相对地址,这是 64 位二进制文件的惯例。
对于某些背景知识,我对汇编程序还很陌生,我选择的汇编程序是 NASM。对于我的第一个项目,我在 Windows 8.1 上使用 cygwin,并且我尝试将这个 Whosebug 问题的第二个答案中的信息从 32 位转换为 64 位:How to write hello world in assembler under Windows?
我正在使用 gnu ld 而不是 link.exe,因为我系统上的 kernel32.Lib 似乎一团糟,而且我更喜欢 gnu ld。所以,这是我的汇编程序:
global _main
extern GetStdHandle@4
extern WriteFile@20
extern ExitProcess@4
section .text
_main
push rbp
mov rbp, rsp
sub rsp, 4
; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
push -11
call GetStdHandle@4
mov rbx, rax
; WriteFile(hStdOut, message, length(message), &bytes, 0)
push 0
lea rax, [rbp-4]
push rax
push (message_end - message)
push message
push rbx
call WriteFile@20
mov rsp, rbp
pop rbp
; ExitProcess(0)
push 0
call ExitProcess@4
;
hlt
message db 'Hello, World!', 10
message_end
当我 运行 nasm -fwin64 ./test.asm
它组装时没有错误,只有关于我在标签名称上遗漏冒号的警告。
我用来 link 到 WinAPI 的命令是
ld test.obj -lkernel32 --enable-stdcall-fixup /cygdrive/c/Windows/system32/kernel32.dll -o test.exe
这给我留下了
test.obj:./test.asm:(.text+0x1c): relocation truncated to fit: R_X86_64_32 against `.text'
所以,我利用 google 的优势发现了这个 Whosebug 问题: What does this GCC error "... relocation truncated to fit..." mean?
我阅读了第一个答案中的material,并进一步搜索了google,然后尝试了第二个答案中提供的解决方案。这是我的 linker 脚本
SECTIONS
{
. = 0x000000000000001b;
.text :
{
*(*)
}
}
我尝试 link 使用此命令:
ld test.obj -T test.ld -lkernel32 --enable-stdcall-fixup /cygdrive/c/Windows/system32/kernel32.dll -o test.exe
我收到这个错误:
ld: cannot find -lkernel32
无论我将 -T test.ld
选项放在哪里,我都会收到此错误。
我被卡住了,我的 google-foo 似乎不足以通过这种方式寻求帮助。我不明白为什么LD在我指定linker脚本时找不到kernel32,我也不知道如何解析t运行cation
如果有帮助,test.obj
的重定位数据的 objdump 是:
$ objdump -Sr test.obj
test.obj: file format pe-x86-64
Disassembly of section .text:
0000000000000000 <_main>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 04 sub [=16=]x4,%rsp
8: 6a f5 pushq [=16=]xfffffffffffffff5
a: e8 00 00 00 00 callq f <_main+0xf>
b: R_X86_64_PC32 GetStdHandle@4
f: 48 89 c3 mov %rax,%rbx
12: 6a 00 pushq [=16=]x0
14: 48 8d 45 fc lea -0x4(%rbp),%rax
18: 50 push %rax
19: 6a 0e pushq [=16=]xe
1b: 68 32 00 00 00 pushq [=16=]x32
1c: R_X86_64_32 .text
20: 53 push %rbx
21: e8 00 00 00 00 callq 26 <_main+0x26>
22: R_X86_64_PC32 WriteFile@20
26: 48 89 ec mov %rbp,%rsp
29: 5d pop %rbp
2a: 6a 00 pushq [=16=]x0
2c: e8 00 00 00 00 callq 31 <_main+0x31>
2d: R_X86_64_PC32 ExitProcess@4
31: f4 hlt
0000000000000032 <message>:
32: 48 rex.W
33: 65 6c gs insb (%dx),%es:(%rdi)
35: 6c insb (%dx),%es:(%rdi)
36: 6f outsl %ds:(%rsi),(%dx)
37: 2c 20 sub [=16=]x20,%al
39: 57 push %rdi
3a: 6f outsl %ds:(%rsi),(%dx)
3b: 72 6c jb a9 <message_end+0x69>
3d: 64 21 0a and %ecx,%fs:(%rdx)
谢谢。
__SOLUTION EDIT__ 未来 google 只猴子:
使用@Jester 的回答中的信息,我重写了程序,现在它可以按预期运行。这是工作来源:
global main
extern GetStdHandle@4
extern WriteFile@20
extern ExitProcess@4
section .text
main
push rbp
mov rbp, rsp
sub rsp, 8
; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
; ABI, pass in registers
mov rcx, -11
call GetStdHandle@4
mov rbx, rax
; WriteFile(hStdOut, message, length(message), &bytes, 0)
mov rcx, rbx
lea rax, [rel message]
mov rdx, rax
mov r8, (message_end - message)
lea rax, [rbp-8]
mov r9, rax
push 0
call WriteFile@20
mov rsp, rbp
pop rbp
; ExitProcess(0)
mov rcx, 0
call ExitProcess@4
;
hlt
message db 'Hello, World!', 10
message_end
Windows 64-bit calling convention 在寄存器中传递前四个整数大小的参数——具体来说,RCX
、RDX
、R8
和 R9
。只有当参数超过 4 个时,它们才会在堆栈上传递。请注意,这 不同于 常见的 32 位调用约定,它在堆栈上传递 所有 参数。
就是说,您的问题是 push message
生成 32 位重定位(因为 push
只需要一个 32 位立即数)。您应该做的是传递字符串的 地址 ,因此,要解决此问题,请使用例如
lea rax, [rel message]
push rax
此处的 rel
关键字确保使用 RIP 相对地址,这是 64 位二进制文件的惯例。