Как работает запись glibc?

Я попытался скомпилировать простую программу, вызывающую write с -nostdlib, но получил ошибку:

/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 пишет в файловый дескриптор?


person MD XF    schedule 06.03.2017    source источник
comment
Хм, stub_warning позволяет мне думать, что этот исходный код является всего лишь функцией заглушки... Где вы ее нашли?   -  person Serge Ballesta    schedule 06.03.2017


Ответы (1)


У вас есть некоторые неправильные представления о C и unix.

Я думал, что write это вещь Unix

Определенно, write — это системный вызов, который является частью Стандарт POSIX. POSIX — это стандарт, совместимый с unix (очень старой операционной системой).

Это означает, что системный вызов с именем write существует в любой Unix или POSIX-совместимой (например, Linux) операционной системе. Однако, если у вас нет реализации стандартной библиотеки C (например, glibc в Linux или bionic в Android) — как вы собираетесь выполнять системный вызов (т. е. просить ядро ​​ОС что-то сделать)? Системные вызовы зависят от архитектуры (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)

Однако, если вам нужно действительно знать, что происходит в glibc, когда вы звоните write - это немного сложно. Насколько я понял, он вызывает _IO_new_file_write (реализовано здесь ). _IO_new_file_write использует макрос write_not_cancel, определенный здесь это макрос для INLINE_SYSCALL (write, 3, fd, buf, len), то есть реализация для вызова системного вызова и установки errno.

person Mark Segal    schedule 06.03.2017
comment
Насколько я понял, он вызывает _IO_new_file_write -- вы неправильно поняли и перепутали write и fwrite. Эта часть вашего ответа совершенно неверна. - person Employed Russian; 08.03.2017
comment
@MarkSegal write реализуется ядром (linux). - person yyny; 26.02.2019