4

I'm trying to learn MacOS assembly using NASM and I can't get a trivial program to work. I'm trying a variation of the "Hello, World" where the two words are independently called by a macro. My source code looks like this:

%macro printString 2
    mov     rax, 0x2000004 ; write
    mov     rdi, 1 ; stdout
    mov     rsi, %1
    mov     rdx, %2
    syscall
%endmacro     

global start    

section .text    

start:
    printString str1,str1.len    

    printString str2,str2.len    

    mov     rax, 0x2000001 ; exit
    mov     rdi, 0
    syscall    


section .data    

str1:   db      "Hello,",10,
.len:  equ       $ - str1    

str2:   db      "world",10
.len:  equ       $ - str2    

The expected result should be:

$./hw
Hello,
World
$

Instead I get:

$./hw
Hello,
$

What am I missing? How do I fix it?

EDIT: I am compiling & running with the following commands:

/usr/local/bin/nasm -f macho64 hw.asm
ld -macosx_version_min 10.7.0 -lSystem -o hw hw.o
./hw
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
The Sauralph
  • 161
  • 7

1 Answers1

7

NASM 2.11.08 and 2.13.02+ have bugs with macho64 output. What you are observing seems to be something I saw specifically with 2.13.02+ recently when using absolute references. The final linked program has incorrect fixups applied so the reference to str2 is incorrect. The incorrect fixup causes us to print out memory that isn't str2.

NASM has a bug report about this issue in their system. I have added a specific example of this failure based on the code in the question. Hopefully the NASM developers will be able to reproduce the failure and create a fix.

Update: As of June 2018 my view is that there are enough recurring bugs and regressions in NASM that I do not recommend NASM at this point in time for Macho-64 development.


Another recommendation I have for Macho-64 development is to use RIP relative addressing rather than absolute. RIP relative addressing is the default for 64-bit programs on later versions of MacOS.

In NASM you can use the default rel directive in your file to change the default from absolute to RIP relative addresses. For this to work you will have to change from using mov register, variable to lea register, [variable] when trying to move the address of a variable to a register. Your revised code could look like:

default rel

%macro printString 2
    mov     rax, 0x2000004 ; write
    mov     rdi, 1 ; stdout
    lea     rsi, [%1]
    mov     rdx, %2
    syscall
%endmacro

global start

section .text

start:
    printString str1,str1.len

    printString str2,str2.len

    mov     rax, 0x2000001 ; exit
    mov     rdi, 0
    syscall


section .data

str1:   db      "Hello,",10
.len:  equ       $ - str1

str2:   db      "world",10
.len:  equ       $ - str2
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • RIP relative addressing worked perfectly! Thank you!! – The Sauralph Mar 11 '18 at 23:00
  • @TheSauralph: May I ask what version of nasm you are using. You can use the `-v` option to display it. I am curious what versions of NASM people are using that don't work. – Michael Petch Mar 11 '18 at 23:01
  • The version I'm using is NASM version 2.13.03 compiled on Feb 8 2018 – The Sauralph Apr 17 '18 at 23:55
  • The NASM 2.11.08 bug [was with RIP-relative addressing](https://stackoverflow.com/questions/30814930/nasm-issue-on-osx-64-bit). So there's no portable way that's safe on all NASM versions! Might be worth mentioning that to avoid confusion. (Of course the best thing is to use a non-buggy NASM version [and RIP-relative instead of 64-bit absolute](https://stackoverflow.com/questions/47300844/mach-o-64-bit-format-does-not-support-32-bit-absolute-addresses-nasm-accessing).) – Peter Cordes Jun 22 '18 at 15:55