Where is userspace wrapper for ioctl system call defined on x86_64 Linux?

928 views Asked by At

I have (out of curiosity) been wondering where the user space wrapper for the ioctl system call is defined on x86_64 Linux. My first thought was glibc - after checking the exposed symbols on the installed version on my Fedora 24 box, I can see that (unless I'm doing it wrong) libc exposes the ioctl symbol as 'W' meaning it is a weak symbol with a default implementation. The default implementation in the glibc source tree at misc/ioctl.c seems to be a stub though, and just sets errno to ENOSYS and returns -1.

Never the less, ioctl works (obviously or my system wouldn't be very usable). I am aware that it is probably assembly code somewhere in a file that is assembled and linked in somehow, thus overriding the weak symbol exposed by glibc. I am also aware that it is perfectly possible for applications to call ioctl directly using a system call, either via the glibc syscall wrapper or directly with assembly.

That said, given the library source code I happened to be observing (libdrm) includes the standard ioctl header /usr/include/sys/ioctl.h, and doesn't seem to contain its own implementation of the wrapper that I can see either, I am wondering where I should be looking.

This is part of my push to understand the lowest levels of a GNU/Linux system more deeply. Thanks for any pointers, and apologies if this has been asked before but I can't see any answer if it has.

UPDATE: I neglected to mention above but I also checked the virtual vdso library mapped by the kernel - I could find only the following in it:

0000000000000a00 W clock_gettime
0000000000000db0 W getcpu
0000000000000c40 W gettimeofday
0000000000000000 A LINUX_2.6
0000000000000d90 W time
0000000000000a00 T __vdso_clock_gettime
0000000000000db0 T __vdso_getcpu
0000000000000c40 T __vdso_gettimeofday
0000000000000d90 T __vdso_time

UPDATE: It would appear I was incorrect about the glibc default definition being a stub. As nos pointed out in the comments, disassembly shows it is performing the real system call. I have posted an answer to reflect this.

1

There are 1 answers

0
PhilPotter1987 On BEST ANSWER

As nos mentioned in the comment to my original question, it is indeed actually defined in libc, in my case as follows:

00000000000f8ce0 <ioctl>:
   f8ce0:       b8 10 00 00 00          mov    $0x10,%eax
   f8ce5:       0f 05                   syscall
   f8ce7:       48 3d 01 f0 ff ff       cmp    $0xfffffffffffff001,%rax
   f8ced:       73 01                   jae    f8cf0 <ioctl+0x10>
   f8cef:       c3                      retq
   f8cf0:       48 8b 0d 71 31 2c 00    mov    0x2c3171(%rip),%rcx        # 3bbe68 <_DYNAMIC+0x308>
   f8cf7:       f7 d8                   neg    %eax
   f8cf9:       64 89 01                mov    %eax,%fs:(%rcx)
   f8cfc:       48 83 c8 ff             or     $0xffffffffffffffff,%rax
   f8d00:       c3                      retq
   f8d01:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
   f8d08:       00 00 00
   f8d0b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

It is clearly making a system call here - as nos says, it must be autogenerated which is why I couldn't find it straight off in the glibc source tree.