我的编译器如何找到 stat(文件状态)函数?

How does my compiler find the stat (file status) function?

我有以下 C 程序:

#include <sys/stat.h>

int main(int argc, char **argv) {
    struct stat fileStat;
    if(stat(argv[1],&fileStat) < 0)    
        return 1;
}

当我使用 Clang 将其编译为 LLVM IR 时,我可以看到 stat 声明如下:

declare i32 @stat(i8*, %struct.stat*)

通常,这种对系统函数的外部调用直接映射到C标准库函数。例如,我可以通过以下方式找到 malloc

nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep malloc

然而,stat函数似乎被区别对待。当搜索 stat 时,我可以找到相关函数,例如 __xstat 但找不到 stat 函数本身。

当我使用 ltrace 跟踪对外部库的调用时,我看到以下调用:__xstat(1, ".", 0x7fff7928c6f0)。此外,可执行文件中的代码确认调用了 __xstat 函数,而不是调用 stat 函数。

我没有观察到对 C 标准库的其他函数调用具有与 C 程序中声明的名称不同的名称。为什么标准库中没有直接等价物?我的编译器如何发现它应该产生对 __xstat 而不是 stat 的调用?

Header sys/stat.hstat 定义为在 glibc 中调用 __xstat 的宏:

#define stat(fname, buf) __xstat (_STAT_VER, fname, buf)

我在 /usr/include/x86_64-linux-gnu/sys/stat.h 中发现了以下评论:

/* To allow the `struct stat' structure and the file type `mode_t'
   bits to vary without changing shared library major version number,
   the `stat' family of functions and `mknod' are in fact inline
   wrappers around calls to `xstat', `fxstat', `lxstat', and `xmknod',
   which all take a leading version-number argument designating the
   data structure and bits used.  <bits/stat.h> defines _STAT_VER with
   the version number corresponding to `struct stat' as defined in
   that file; and _MKNOD_VER with the version number corresponding to
   the S_IF* macros defined therein.  It is arranged that when not
   inlined these function are always statically linked; that way a
   dynamically-linked executable always encodes the version number
   corresponding to the data structures it uses, so the `x' functions
   in the shared library can adapt without needing to recompile all
   callers.  */

# ifdef __REDIRECT_NTH
extern int __REDIRECT_NTH (stat, (const char *__restrict __file,
                  struct stat *__restrict __buf), stat64)
     __nonnull ((1, 2));
# endif

__REDIRECT_NTH 定义在 /usr/include/x86_64-linux-gnu/sys/cdefs.h:

/* __asm__ ("xyz") is used throughout the headers to rename functions
   at the assembly language level.  This is wrapped by the __REDIRECT
   macro, in order to support compilers that can do this some other
   way.  When compilers don't support asm-names at all, we have to do
   preprocessor tricks instead (which don't have exactly the right
   semantics, but it's the best we can do).

   Example:
   int __REDIRECT(setpgrp, (__pid_t pid, __pid_t pgrp), setpgid); */

#if defined __GNUC__ && __GNUC__ >= 2

# define __REDIRECT(name, proto, alias) name proto __asm__ (__ASMNAME (#alias))
# ifdef __cplusplus
#  define __REDIRECT_NTH(name, proto, alias) \
     name proto __THROW __asm__ (__ASMNAME (#alias))
#  define __REDIRECT_NTHNL(name, proto, alias) \
     name proto __THROWNL __asm__ (__ASMNAME (#alias))
# else
#  define __REDIRECT_NTH(name, proto, alias) \
     name proto __asm__ (__ASMNAME (#alias)) __THROW
#  define __REDIRECT_NTHNL(name, proto, alias) \
     name proto __asm__ (__ASMNAME (#alias)) __THROWNL
# endif
# define __ASMNAME(cname)  __ASMNAME2 (__USER_LABEL_PREFIX__, cname)
# define __ASMNAME2(prefix, cname) __STRING (prefix) cname

从注释和宏定义看来,别名似乎是在内联汇编程序中指定的。