0

For some background, I'm pretty new to assembler, and my assembler of choice is NASM. For my first project, I'm using cygwin on Windows 8.1, and I've tried to convert the information from the second answer on this stackoverflow question from 32 bit to 64 bit: How to write hello world in assembler under Windows?

I'm using gnu ld instead of link.exe because the kernel32.Lib on my system seems to be messed up, and I prefer gnu ld anyway. So, this is my assembler:

            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

When I run nasm -fwin64 ./test.asm it assembles with no errors, only warnings about me leaving out the colons on the label names.

The command I'm using to link to the WinAPI is

    ld test.obj -lkernel32 --enable-stdcall-fixup /cygdrive/c/Windows/system32/kernel32.dll -o test.exe

This leaves me with

    test.obj:./test.asm:(.text+0x1c): relocation truncated to fit: R_X86_64_32 against `.text'

So, I used google to my advantage and found this stackoverflow question: What does this GCC error "... relocation truncated to fit..." mean?

I read the material in the first answer, and scoured google some more, and then tried the solution offered in the second answer. Here is my linker script

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

I try to link using this command:

    ld test.obj -T test.ld -lkernel32 --enable-stdcall-fixup /cygdrive/c/Windows/system32/kernel32.dll -o test.exe

And I get this error: ld: cannot find -lkernel32

I get this error regardless of where I place the -T test.ld option.

I'm stuck and my google-foo doesn't seem adequate enough to find help that way. I don't understand why LD can't find kernel32 when I specify a linker script, and I don't know how to resolve the truncation either.

If it helps, the objdump with relocation data for test.obj is:

    $ 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)

Thanks.

__SOLUTION EDIT__ for future google monkeys:

Using the information in the answer from @Jester, I rewrote the program and it now works as expected. Here is the working source:

              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 Answers1

2

The Windows 64-bit calling convention passes the first four integer-sized parameters in registers—specifically, RCX, RDX, R8, and R9. Only if there are more than 4 arguments are they passed on the stack. Note that this is unlike the common 32-bit calling conventions, which pass all parameters on the stack.

That said, your problem is the push message generating a 32-bit relocation (since push only takes a 32-bit immediate). What you should be doing is passing the address of the string, so, to fix this, use e.g.

lea  rax, [rel message]
push rax

The rel keyword here ensures that the RIP-relative address is used, which is customary for 64-bit binaries.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Jester
  • 56,577
  • 4
  • 81
  • 125
  • Okay, thanks! The binary now links, but it generates a segmentation fault on the WriteFile() call. Is this because of the calling convention on x64 (Is that ARM ABI?). In that case, should I move the arguments into the r registers as described on this page?: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/ – Tako'sko wa Jul 23 '17 at 01:25
  • I have linked you the official msdn documentation ... and no, it has nothing to do with ARM. – Jester Jul 23 '17 at 01:26
  • Okay. I got it now, and have updated the op with the working source. Thanks a bunch! – Tako'sko wa Jul 23 '17 at 02:06