William Breathitt Gray
William Breathitt Gray

Reputation: 11996

Understanding simple Linux syscall in libc.a

I'm running a 64-bit Debian 4.7.2-5 Linux system, using glibc-2.13-1. While I was searching for the assembly code of some function calls in libc.a I came across this:

file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <sync>:
   0:   b9 a2 00 00 00        mov    eax,0xa2
   5:   0f 05                 syscall
   7:   48 3d 01 f0 ff ff     cmp    rax,0xfffffffffffff001
   d:   0f 83 00 00 00 00     jae    13 <sync+0x13>
  13:   c3                    ret

I'm a bit confused on what this is doing. What is the point of mov eax,0xa2 and why isn't the rax register being used if this is a 64-bit machine (how does syscall know what system call to make)? In short: what do these 5 lines of code do?

Upvotes: 2

Views: 910

Answers (2)

Igor Skochinsky
Igor Skochinsky

Reputation: 25318

0xa2 is the syscall number. The kernel uses it to decide what actual function to perform, since the syscall instruction itself does not contain any information.

As for rax, it does get used. To continue the tradition started in old days, the eax is an alias to the low 32 bits of rax. However, there is one not very well-known quirk of x64 architecture: whenever you modify that low 32-bit part, the top 32-bits get zeroed. So, in fact mov eax, 0xa2 is equivalent to mov rax, 0xa2, except its encoding is shorter. NASM, or as -O2, will even do that optimization for you.

The last three instructions perform the error handling. In case %rax is between -1 and -4095, it means the syscall returned an error, for any Linux system call. Here's how it looks in the original source:

cmpq $-4095, %rax    /* Check %rax for error.  */
jae __syscall_error  /* Branch forward if it failed.  */
ret                  /* Return to caller.  */

You see wrong target for the jae because you're disassembling a relocatable object, and the relocatable fields have been set to 0 since they will be patched at the final link time.

To see the relocation targets, add -r switch to objdump command line:

0000000000000000 <sync>:
   0:   b8 a2 00 00 00          mov    $0xa2,%eax
   5:   0f 05                   syscall 
   7:   48 3d 01 f0 ff ff       cmp    $0xfffffffffffff001,%rax
   d:   0f 83 00 00 00 00       jae    13 <sync+0x13>
                        f: R_X86_64_PC32        __syscall_error-0x4
  13:   c3                      retq   

You can see that the bytes at offset f will be patched so that jump goes to __syscall_error.

Read https://cs.lmu.edu/~ray/notes/syscalls/ for more info on syscalls.
Also The Definitive Guide to Linux System Calls blog article.

Upvotes: 5

nrz
nrz

Reputation: 10580

mov eax,0xa2 is sufficient to set the entire rax, because modifying 32-bit general registers such as eax zeroes always the top 32 bits of the corresponding 64-bit register (in this case rax).

Syscall number a2 is sync, see /usr/src/linux/usr/include/asm/unistd_64.h for the list of Linux x86-64 syscalls (replace /usr/src/linux/ with the directory where you have your Linux source installed).

As a whole, these lines open a file defined already before these instructions, then compare the return value of syscall (in rax) with 0xfffffffffffff001, and then there something a bit strange: 0f 83 00 00 00 00 jae 13 is a conditional jump to the next instruction, that would be reached anyway.

Upvotes: 1

Related Questions