gcc ビルトイン関数の呼び出し

ダイナミックリンカのソースコード(elf/rtld.c)を見ていると興味深いコメントがあった。

  /* Partly clean the `bootstrap_map' structure up.  Don't use
     `memset' since it might not be built in or inlined and we cannot
     make function calls at this point.  Use '__builtin_memset' if we
     know it is available.  We do not have to clear the memory if we
     do not have to use the temporary bootstrap_map.  Global variables
     are initialized to zero by default.  */

memsetはビルドインでないもしくはインライン化されていない可能性があるため、ここでは呼び出してはいけない」。このコメントはダイナミックリンカの起動直後にあるため、再配置が終わっていない関数を呼び出すとアドレスが埋まっていないGOTを参照してしまい、Segmentation Faultが発生することを危惧したものだと考えられる。

ということはgccのビルトイン関数は必ずインライン化されなければならない。実際gccのビルトイン関数の説明には以下のような記述がある。

With the exception of built-ins that have library equivalents such as the standard C library functions discussed below, or that expand to library calls, GCC built-in functions are always expanded inline and thus do not have corresponding entry points and their address cannot be obtained. Attempting to use them in an expression other than a function call results in a compile-time error.

ビルトイン関数を使う小さなプログラムでも実験的に確かめることができる。

> cat main.c
#include <stdio.h>

int main() {
    int c = __builtin_clz(0x10101);
    printf("c = %d\n", c);
    return 0;
}
> gcc main.c -static -o main
> objdump -d main | grep "<main>" -A 16
0000000000401ce5 <main>:
  401ce5:       f3 0f 1e fa             endbr64 
  401ce9:       55                      push   %rbp
  401cea:       48 89 e5                mov    %rsp,%rbp
  401ced:       48 83 ec 10             sub    $0x10,%rsp
  401cf1:       c7 45 fc 0f 00 00 00    movl   $0xf,-0x4(%rbp)
  401cf8:       8b 45 fc                mov    -0x4(%rbp),%eax
  401cfb:       89 c6                   mov    %eax,%esi
  401cfd:       48 8d 3d 00 33 09 00    lea    0x93300(%rip),%rdi        # 495004 <_IO_stdin_used+0x4>
  401d04:       b8 00 00 00 00          mov    $0x0,%eax
  401d09:       e8 92 ee 00 00          callq  410ba0 <_IO_printf>
  401d0e:       b8 00 00 00 00          mov    $0x0,%eax
  401d13:       c9                      leaveq 
  401d14:       c3                      retq   
  401d15:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  401d1c:       00 00 00 
  401d1f:       90                      nop

__builtin_clzはインライン化されているようだ。