我似乎无法从WinAPI NASM教程

时间:2017-07-23 00:39:29

标签: winapi assembly linker nasm ld

对于某些背景知识,我对汇编程序很新,而我选择的汇编程序是NASM。对于我的第一个项目,我在Windows 8.1上使用了cygwin,并且我试图将此stackoverflow问题的第二个答案的信息从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时,它组装时没有错误,只有关于我在标签名称上留下冒号的警告。

我用来链接到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'

所以,我使用谷歌的优势,发现了这个stackoverflow问题: What does this GCC error "... relocation truncated to fit..." mean?

我在第一个答案中阅读了这些材料,并对谷歌进行了一些搜索,然后尝试了第二个答案中提供的解决方案。这是我的链接描述文件

    SECTIONS
    {
            . = 0x000000000000001b;
            .text :
            {
                    *(*)
            }
    }

我尝试使用此命令进行链接:

    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不能找到kernel32,而且我也不知道如何解决截断。

如果有帮助,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    $0x4,%rsp
       8:   6a f5                   pushq  $0xfffffffffffffff5
       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  $0x0
      14:   48 8d 45 fc             lea    -0x4(%rbp),%rax
      18:   50                      push   %rax
      19:   6a 0e                   pushq  $0xe
      1b:   68 32 00 00 00          pushq  $0x32
                            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  $0x0
      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    $0x20,%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)

感谢。

__解决方案编辑未来的谷歌猴子:

使用@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

1 个答案:

答案 0 :(得分:2)

Windows 64-bit calling convention传递寄存器中的前四个整数大小的参数,具体为RCXRDXR8R9。只有当有超过4个参数时,它们才会在堆栈上传递。请注意,这与不同于常见的32位调用约定,它会在堆栈上传递所有参数。

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

lea  rax, [rel message]
push rax

此处的rel关键字确保使用RIP相对地址,这通常适用于64位二进制文​​件。