简体   繁体   中英

How does glibc's write work?

I tried to compile a simple program that called write with -nostdlib , but I got the error:

/path/to/file:3: undefined reference to `write'

I thought write was a Unix thing and was always there, but apparently not, turns out libc has write function. I found the source code:

/* 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)

All this seems to be doing is setting errno . How does __libc_write write to a file descriptor?

You have some misconceptions about C and unix .

I thought write was a Unix thing

Definitely, write is a syscall which is a part of the POSIX standard. POSIX is a standard that is compatible with unix (a very old operating system).

This means that the syscall called write exists in any unix or POSIX-compliant (like Linux) operating system. However, if you don't have a an implementation of the C standard library (like glibc in Linux or bionic in Android) - how are you going to make a syscall (ie ask the kernel of the OS to do something)? Syscalls are architecture dependent (SPARC, Intel, ARM, PowerPC etc will have different implementations) - and are implemented by the C standard library. If you don't have one (eg when you complie with -nostdlib you specifically ask not to have a C standard library) - you can't call write , the function doesn't exist. You can, however, implement it yourself using assembly.

The code you provided is for __libc_write , which is not the same as write . There's some voodoo that's going on between glibc and gcc here, something that has to do with stubs and I don't completely understand it.

The trivial implementation is something like this:

ssize_t write(int fd, const void *buf, size_t count)
{
    return __set_errno(syscall(__NR_write, fd, buf, count));
}

(In bionic , Android's libc implementation, you can see the sources here https://searchcode.com/codesearch/view/34924443/ - see that it is implemented in assembly)

The syscall function is implemented in assembly in glibc (and anywhere else): http://code.metager.de/source/xref/gnu/glibc/sysdeps/unix/sysv/linux/x86_64/syscall.S (for x86_64)

However, if you must really know what's happening in glibc when you call write - it's a bit complicated. As far as I understood it, it calls _IO_new_file_write (implemented 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 for calling a syscall and setting errno.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM