2

I wrote a snippet of x86-64 to get current time of day on a mac using a syscall. Everything worked fine. I got a time. Then I thought to add a little error checking.

To get an idea of what to do, I disassembled the "gettimeofday" wrapper. It uses a jae instruction after the syscall to handle a successful result.

___gettimeofday:
    4969:   48 c7 c2 00 00 00 00    movq    $0x0, %rdx
    4970:   b8 74 00 00 02  movl    $0x2000074, %eax
    4975:   49 89 ca    movq    %rcx, %r10
    4978:   0f 05   syscall
    497a:   73 08   jae 0x4984
    497c:   48 89 c7    movq    %rax, %rdi
    497f:   e9 41 d8 ff ff  jmp _cerror_nocancel
    4984:   48 83 f8 00 cmpq    $0x0, %rax
    4988:   74 08   je  0x4992
    498a:   48 89 07    movq    %rax, (%rdi)
    498d:   89 57 08    movl    %edx, 0x8(%rdi)
    4990:   31 c0   xorl    %eax, %eax
    4992:   c3  retq

I think this is simply checking the carry flag. According to "Introduction to 64 Bit Assembly Programming for Linux and OS X" (Seyfarth, 2013) this is an alias for jnc. So I figured I can use jc as an error check. Jump to error if there is a carry flag set. Initially this seemed to work, but then weirdness ensued.

The second time I executed this syscall in my program, the carry flag was set, but the new time was also fetched. After messing around with it for a bit, I ended up with this bit of code demonstrating the issue:

    # get current time
    movq $0x2000074, %rax
    leaq curtime(%rip), %rdi
    syscall
    jc error

    # get current time again
    movq $0x2000074, %rax
    leaq curtime(%rip), %rdi
    syscall
    jc error

    # add 5 seconds to the start time and stash in rdx
    movq curtime(%rip), %rdx
    addq $5, %rdx

    movq $0x2000074, %rax
    leaq curtime(%rip), %rdi
    syscall
    jc error

If I comment out the code that sets up %rdx with current time plus five seconds, then the jc error instruction does not jump to the error label.

Thing is, if I have a look at what's happening to $curtime(%rip), it is getting updated every time in a way that makes it look like things are working. The values go up by a few seconds each time. When I check the carry flag (low rflags bit) after the failed system call, it is set. Also, rax contains 0xe(15).

What am I doing wrong? Why do the move and add instructions seem to be messing something up?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Chris
  • 342
  • 1
  • 12
  • Oh-no! I figured out a remedy based on [this post](https://stackoverflow.com/questions/28509308/osx-gettimeofday-syscall-on-x86-64-seems-to-not-work), but now I don't know why the change matters. – Chris Sep 07 '22 at 20:11
  • 1
    Yes, x86-64 MacOS uses CF as out-of-band signalling of error vs. no-error, unlike Linux where -4095 .. -1 means RAX is a -errno code. – Peter Cordes Sep 10 '22 at 03:47

1 Answers1

3

In the post OSX gettimeofday syscall on x86_64 seems to not work, Heider Sati mentions setting RSI and RDX. That would mean three parameters, and according to man 2 gettimeofday on my system there are only two parameters.

However, I found this dyjakan github in a SO post or comment (lost track), which lead me to macOS BSD System Calls which .... does quote 3 parameters. Someone else here at SO mentioned the 3rd one being mach_absolute_time.

Anyway, I'm using RDX to hold the finish time for the app. I presume that is being passed into the gettimeofday system call thus breaking it. So ... I moved that value to RBX since RBX is't part of the kernel interface. Now no errors are reported.

Now that I take another look at the disassembled syscall, I notice something I wondered about before. The first instruction is movq $0x0, %rdx. I wondered why that was there, and now I know. Guess I should not have stopped wondering.

Chris
  • 342
  • 1
  • 12
  • 1
    It's odd that the MacOS wrapper would use such an inefficient way to zero RDX, 7-byte `mov $0, %rdx` instead of 2-byte `xor %edx,%edx`. But yeah, looks like there's a difference between the library API vs. the underlying kernel system-call API. On Linux the man pages mention when that's the case (e.g. [`brk(2)`](https://man7.org/linux/man-pages/man2/sbrk.2.html#NOTES) and [`nice(2)`](https://man7.org/linux/man-pages/man2/nice.2.html#NOTES), the latter only being a thing because of Linux's in-band signalling of error codes as small negatives, unlike MacOS using CF.) – Peter Cordes Sep 10 '22 at 03:52