glibc 的 write 是如何工作的?
How does glibc's write work?
我试图用 -nostdlib
编译一个调用 write
的简单程序,但出现错误:
/path/to/file:3: undefined reference to `write'
我以为 write
是一个 Unix 的东西,并且一直在那里,但显然不是,原来 libc 有 write
功能。我找到了源代码:
/* Write NBYTES of BUF to FD. Return the number written, or -1. */
ssize_t
__libc_write (int fd, const void *buf, size_t nbytes)
{
if (nbytes == 0)
return 0;
if (fd < 0)
{
__set_errno (EBADF);
return -1;
}
if (buf == NULL)
{
__set_errno (EINVAL);
return -1;
}
__set_errno (ENOSYS);
return -1;
}
libc_hidden_def (__libc_write)
stub_warning (write)
weak_alias (__libc_write, __write)
libc_hidden_weak (__write)
weak_alias (__libc_write, write)
这一切似乎都是在设置 errno
。 __libc_write
如何写入文件描述符?
你对C
和unix
有一些误解。
I thought write
was a Unix thing
当然,write
是一个syscall which is a part of the POSIX标准。 POSIX
是与 unix
(非常古老的操作系统)兼容的标准。
这意味着名为 write
的 syscall 存在于任何 unix 或 POSIX 兼容(如 Linux)操作系统中.但是,如果您没有 C 标准库的实现(例如 Linux 中的 glibc 或 Android 中的 bionic)——您将如何进行系统调用(即询问内核OS 做某事)?系统调用依赖于体系结构(SPARC、Intel、ARM、PowerPC 等将有不同的实现)——并且由 C 标准库实现。如果您没有(例如,当您遵守 -nostdlib
时,您明确要求不要拥有 C 标准库)- 您不能调用 write
,该函数不存在。但是,您可以使用汇编自己实现它。
您提供的代码适用于 __libc_write
,与 write
不同。这里 glibc
和 gcc
之间发生了一些巫术,与存根有关,我不完全理解它。
简单的实现是这样的:
ssize_t write(int fd, const void *buf, size_t count)
{
return __set_errno(syscall(__NR_write, fd, buf, count));
}
(在bionic
、Android的libc实现中,你可以在这里看到源代码https://searchcode.com/codesearch/view/34924443/——看到它是在汇编中实现的)
syscall
函数在 glibc
(以及其他任何地方)的汇编中实现:http://code.metager.de/source/xref/gnu/glibc/sysdeps/unix/sysv/linux/x86_64/syscall.S(对于 x86_64)
但是,如果您在调用 write
时必须真正知道 glibc
中发生了什么,那就有点复杂了。据我了解,它调用 _IO_new_file_write
(实现 here). _IO_new_file_write
uses the write_not_cancel
macro defined here which is a macro for INLINE_SYSCALL (write, 3, fd, buf, len)
, which is the implementation 用于调用系统调用和设置错误号。
我试图用 -nostdlib
编译一个调用 write
的简单程序,但出现错误:
/path/to/file:3: undefined reference to `write'
我以为 write
是一个 Unix 的东西,并且一直在那里,但显然不是,原来 libc 有 write
功能。我找到了源代码:
/* Write NBYTES of BUF to FD. Return the number written, or -1. */
ssize_t
__libc_write (int fd, const void *buf, size_t nbytes)
{
if (nbytes == 0)
return 0;
if (fd < 0)
{
__set_errno (EBADF);
return -1;
}
if (buf == NULL)
{
__set_errno (EINVAL);
return -1;
}
__set_errno (ENOSYS);
return -1;
}
libc_hidden_def (__libc_write)
stub_warning (write)
weak_alias (__libc_write, __write)
libc_hidden_weak (__write)
weak_alias (__libc_write, write)
这一切似乎都是在设置 errno
。 __libc_write
如何写入文件描述符?
你对C
和unix
有一些误解。
I thought
write
was a Unix thing
当然,write
是一个syscall which is a part of the POSIX标准。 POSIX
是与 unix
(非常古老的操作系统)兼容的标准。
这意味着名为 write
的 syscall 存在于任何 unix 或 POSIX 兼容(如 Linux)操作系统中.但是,如果您没有 C 标准库的实现(例如 Linux 中的 glibc 或 Android 中的 bionic)——您将如何进行系统调用(即询问内核OS 做某事)?系统调用依赖于体系结构(SPARC、Intel、ARM、PowerPC 等将有不同的实现)——并且由 C 标准库实现。如果您没有(例如,当您遵守 -nostdlib
时,您明确要求不要拥有 C 标准库)- 您不能调用 write
,该函数不存在。但是,您可以使用汇编自己实现它。
您提供的代码适用于 __libc_write
,与 write
不同。这里 glibc
和 gcc
之间发生了一些巫术,与存根有关,我不完全理解它。
简单的实现是这样的:
ssize_t write(int fd, const void *buf, size_t count)
{
return __set_errno(syscall(__NR_write, fd, buf, count));
}
(在bionic
、Android的libc实现中,你可以在这里看到源代码https://searchcode.com/codesearch/view/34924443/——看到它是在汇编中实现的)
syscall
函数在 glibc
(以及其他任何地方)的汇编中实现:http://code.metager.de/source/xref/gnu/glibc/sysdeps/unix/sysv/linux/x86_64/syscall.S(对于 x86_64)
但是,如果您在调用 write
时必须真正知道 glibc
中发生了什么,那就有点复杂了。据我了解,它调用 _IO_new_file_write
(实现 here). _IO_new_file_write
uses the write_not_cancel
macro defined here which is a macro for INLINE_SYSCALL (write, 3, fd, buf, len)
, which is the implementation 用于调用系统调用和设置错误号。