我似乎无法通过 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 在寄存器中传递前四个整数大小的参数——具体来说,RCXRDXR8R9。只有当参数超过 4 个时,它们才会在堆栈上传递。请注意,这 不同于 常见的 32 位调用约定,它在堆栈上传递 所有 参数。

就是说,您的问题是 push message 生成 32 位重定位(因为 push 只需要一个 32 位立即数)。您应该做的是传递字符串的 地址 ,因此,要解决此问题,请使用例如

lea  rax, [rel message]
push rax

此处的 rel 关键字确保使用 RIP 相对地址,这是 64 位二进制文​​件的惯例。