3

I'm trying to write a program that performs the fork() syscall, and the child/parent each write a different string and exit(0).

The problem is - although fork() is successful (since I see 2 lines of output), for some reason both the parent and child process output the parent string.

Here's the code:

BITS 64

section .data
msg1:    db    "Hello from child", 0x0a
len1:    equ   $-msg1
msg2:    db    "Hello from parent", 0x0a
len2:    equ   $-msg2

section .text
global start

start:
        xor rax,rax
        mov rax,0x2000002    ; fork() syscall
        syscall

        test eax,eax
        jne flow_parent

        mov rax, 0x2000004   ; SYS_write
        mov rdi, 1           ; stdout
        mov rsi, msg1
        mov rdx, len1
        syscall

        xor rdi,rdi
        mov rax,0x2000001
        syscall

flow_parent:
        mov rax, 0x2000004   ; SYS_write
        mov rdi, 1           ; stdout
        mov rsi, msg2
        mov rdx, len2
        syscall

        xor rdi,rdi
        mov rax,0x2000001
        syscall

My output:

$ nasm -f macho64 code.s -o code.o
$ ld -o code -e start -macosx_version_min 10.7 code.o
$ ./code
Hello from parent
Hello from parent
$

Any ideas on how to fix this?

nrz
  • 10,435
  • 4
  • 39
  • 71

2 Answers2

6

First off, you're making a syscall on Mac OS X with an assumption that it will behave just like the Linux kernel, which is clearly wrong (Linux fork syscall convention != XNU fork syscall convention). Mac OS X does not support users directly making system calls without going through the libSystem library.

Now leaving that aside, I'm not sure which version of the XNU kernel that you're running, but if you actually take a look at the source code for the fork function in the libsyscall library, you'll find that the edx register is used instead (orl %edx, %edx) to determine whether the process is the child or the parent.

Again, this would still not be reliable way of implementing this since Apple may easily change the interface in the future as they wish.

JosephH
  • 8,465
  • 4
  • 34
  • 62
  • Joseph, thanks a million for the elaborate response and the source code reference, it worked. – user2418661 Nov 14 '13 at 21:16
  • Just for the record, `test same,same` is more efficient than `or same,same` to set FLAGS according to a register, especially when branching on it since `test` can macro-fuse with the JCC but `or` can't. That usage of `or` in the XNU source is [a bad legacy idiom anti-optimization](https://stackoverflow.com/questions/33721204/test-whether-a-register-is-zero-with-cmp-reg-0-vs-or-reg-reg/33724806#33724806), unless they actively need to zero-extend EDX into RDX at that point, if it's possible it might not already be zero-extended. In that case they should `and %edx,%edx` to still fuse on SnB-fam – Peter Cordes Feb 21 '22 at 08:44
-1

Don't test register eax! You need to test register rdx to get the return value!

Feng
  • 1
  • The existing answer already says to check EDX. Are you sure the return value (PID?) from fork is 64-bit in RDX, not just the low 32 bits? JosephH's answer links XNU source showing that x86-64 code still uses EDX. – Peter Cordes Feb 21 '22 at 08:43
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 21 '22 at 14:34
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/31121755) – tomerpacific Feb 24 '22 at 17:58