Date: Wed, 7 Jan 2015 05:29:41 +0200 From: Konstantin Belousov <kostikbel@gmail.com> To: Jilles Tjoelker <jilles@stack.nl> Cc: threads@freebsd.org, arch@freebsd.org Subject: Re: Fixing dlopen("libpthread.so") Message-ID: <20150107032941.GJ42409@kib.kiev.ua> In-Reply-To: <20150106205046.GA24971@stack.nl> References: <20141226165337.GJ1754@kib.kiev.ua> <20150103212837.GC46373@stack.nl> <20150104114600.GC42409@kib.kiev.ua> <20150104182026.GA61250@stack.nl> <20150105023708.GD42409@kib.kiev.ua> <20150106205046.GA24971@stack.nl>
next in thread | previous in thread | raw e-mail | index | archive | help
On Tue, Jan 06, 2015 at 09:50:46PM +0100, Jilles Tjoelker wrote: > On Mon, Jan 05, 2015 at 04:37:08AM +0200, Konstantin Belousov wrote: > > accept <- calls accept4 now > > This is wrong since accept() copies various properties from the > listening to the new socket, which accept4() never does. (Note that > Linux accept() is equivalent to accept4() with 0 flags, so the > linuxolator can use such a shortcut.) Reverted this part. So it is not possible to emulate accept(2) with accept4(2) ? Does it make sense to add flag(s) to accept4(2) to copy nonblock and async from the listen socket ? > > > creat \ > > open <- openat > > pause <- there were actual bugs, pause called _sigprocmask and _sigsuspend > > raise <- I converted it to threaded kill > > sleep <- this one is tricky, but seems to work > > usleep > > wait > > wait3 > > waitpid > > These look OK. Updated patch is at the end. > > > > The changes look good to me. > > Next natural question is about the __error calls through PLT in the > > .cerror asm. Before the work was committed, libthr interposed __error, > > which was required for correct operation. Now, we need __error exported, > > but do we need support its interposing ? This is an implementation > > detail for errno, I do not see any loss from not allowing to override > > errno location. > > Indeed, there is no need to allow interposing __error (as with many > libc-internal calls to its exported symbols). Additionally, the current > namespace.h mechanism could be used to redirect __error calls from C > code. > > Glibc uses macro trickery with the asm labels compiler feature, making > libc code see things like > int *__error(void) asm("__hidden__error"); > and defining the hidden alias somewhere. This also works for > compiler-generated calls like to memcpy(). I'm not sure whether it is a > good idea to add this extra trickery and depend on the compiler feature. I might look at this after the current change is committed. > Glibc also has build-time checks that only specifically listed symbols > are referenced via the PLT. As well as e.g. libunwind. This would be a nice addition to currently empty set of ABI stability tests. diff --git a/lib/libc/compat-43/Symbol.map b/lib/libc/compat-43/Symbol.map index 2827705..bd49f99 100644 --- a/lib/libc/compat-43/Symbol.map +++ b/lib/libc/compat-43/Symbol.map @@ -28,5 +28,4 @@ FBSD_1.2 { FBSDprivate_1.0 { __creat; _creat; - __libc_creat; }; diff --git a/lib/libc/compat-43/creat.c b/lib/libc/compat-43/creat.c index 5ee4531..4545482 100644 --- a/lib/libc/compat-43/creat.c +++ b/lib/libc/compat-43/creat.c @@ -38,21 +38,16 @@ __FBSDID("$FreeBSD$"); #include "un-namespace.h" #include "libc_private.h" -__weak_reference(__libc_creat, __creat); -__weak_reference(__libc_creat, _creat); +__weak_reference(__creat, creat); +__weak_reference(__creat, _creat); #pragma weak creat int -creat(const char *path, mode_t mode) +__creat(const char *path, mode_t mode) { - return (((int (*)(const char *, mode_t)) - __libc_interposing[INTERPOS_creat])(path, mode)); + return (((int (*)(int, const char *, int, ...)) + __libc_interposing[INTERPOS_openat])(AT_FDCWD, path, O_WRONLY | + O_CREAT | O_TRUNC, mode)); } -int -__libc_creat(const char *path, mode_t mode) -{ - - return(__sys_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode)); -} diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map index e01a897..ee4d619 100644 --- a/lib/libc/gen/Symbol.map +++ b/lib/libc/gen/Symbol.map @@ -533,14 +533,7 @@ FBSDprivate_1.0 { _libc_sem_post_compat; _libc_sem_getvalue_compat; - __libc_pause; - __libc_raise; - __libc_sleep; __libc_tcdrain; - __libc_usleep; - __libc_wait; - __libc_wait3; - __libc_waitpid; __elf_aux_vector; __pthread_map_stacks_exec; diff --git a/lib/libc/gen/pause.c b/lib/libc/gen/pause.c index 97fbb01..ef48c1c 100644 --- a/lib/libc/gen/pause.c +++ b/lib/libc/gen/pause.c @@ -33,10 +33,8 @@ static char sccsid[] = "@(#)pause.c 8.1 (Berkeley) 6/4/93"; #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include "namespace.h" #include <signal.h> #include <unistd.h> -#include "un-namespace.h" #include "libc_private.h" @@ -44,23 +42,14 @@ __FBSDID("$FreeBSD$"); * Backwards compatible pause. */ int -__libc_pause(void) +__pause(void) { sigset_t oset; - if (_sigprocmask(SIG_BLOCK, NULL, &oset) == -1) + if (sigprocmask(SIG_BLOCK, NULL, &oset) == -1) return (-1); - return (_sigsuspend(&oset)); + return (sigsuspend(&oset)); } -#pragma weak pause -int -pause(void) -{ - - return (((int (*)(void)) - __libc_interposing[INTERPOS_pause])()); -} - -__weak_reference(__libc_pause, __pause); -__weak_reference(__libc_pause, _pause); +__weak_reference(__pause, pause); +__weak_reference(__pause, _pause); diff --git a/lib/libc/gen/raise.c b/lib/libc/gen/raise.c index d605639..994fea5 100644 --- a/lib/libc/gen/raise.c +++ b/lib/libc/gen/raise.c @@ -38,21 +38,15 @@ __FBSDID("$FreeBSD$"); #include "libc_private.h" -__weak_reference(__libc_raise, __raise); -__weak_reference(__libc_raise, _raise); +__weak_reference(__raise, raise); +__weak_reference(__raise, _raise); -#pragma weak raise int -raise(int s) +__raise(int s) { + long id; - return (((int (*)(int)) - __libc_interposing[INTERPOS_raise])(s)); -} - -int -__libc_raise(int s) -{ - - return (kill(getpid(), s)); + if (__sys_thr_self(&id) == -1) + return (-1); + return (__sys_thr_kill(id, s)); } diff --git a/lib/libc/gen/sleep.c b/lib/libc/gen/sleep.c index f558e36..6bb4ecd 100644 --- a/lib/libc/gen/sleep.c +++ b/lib/libc/gen/sleep.c @@ -42,17 +42,8 @@ __FBSDID("$FreeBSD$"); #include "libc_private.h" -#pragma weak sleep unsigned int -sleep(unsigned int seconds) -{ - - return (((unsigned int (*)(unsigned int)) - __libc_interposing[INTERPOS_sleep])(seconds)); -} - -unsigned int -__libc_sleep(unsigned int seconds) +__sleep(unsigned int seconds) { struct timespec time_to_sleep; struct timespec time_remaining; @@ -62,17 +53,19 @@ __libc_sleep(unsigned int seconds) * the maximum value for a time_t is >= INT_MAX. */ if (seconds > INT_MAX) - return (seconds - INT_MAX + __libc_sleep(INT_MAX)); + return (seconds - INT_MAX + __sleep(INT_MAX)); time_to_sleep.tv_sec = seconds; time_to_sleep.tv_nsec = 0; - if (_nanosleep(&time_to_sleep, &time_remaining) != -1) + if (((int (*)(const struct timespec *, struct timespec *)) + __libc_interposing[INTERPOS_nanosleep])( + &time_to_sleep, &time_remaining) != -1) return (0); if (errno != EINTR) return (seconds); /* best guess */ return (time_remaining.tv_sec + - (time_remaining.tv_nsec != 0)); /* round up */ + (time_remaining.tv_nsec != 0)); /* round up */ } -__weak_reference(__libc_sleep, __sleep); -__weak_reference(__libc_sleep, _sleep); +__weak_reference(__sleep, sleep); +__weak_reference(__sleep, _sleep); diff --git a/lib/libc/gen/usleep.c b/lib/libc/gen/usleep.c index 1d58b98..7c35f6c 100644 --- a/lib/libc/gen/usleep.c +++ b/lib/libc/gen/usleep.c @@ -40,24 +40,16 @@ __FBSDID("$FreeBSD$"); #include "libc_private.h" -#pragma weak usleep int -usleep(useconds_t useconds) -{ - - return (((int (*)(useconds_t)) - __libc_interposing[INTERPOS_usleep])(useconds)); -} - -int -__libc_usleep(useconds_t useconds) +__usleep(useconds_t useconds) { struct timespec time_to_sleep; time_to_sleep.tv_nsec = (useconds % 1000000) * 1000; time_to_sleep.tv_sec = useconds / 1000000; - return (_nanosleep(&time_to_sleep, NULL)); + return (((int (*)(const struct timespec *, struct timespec *)) + __libc_interposing[INTERPOS_nanosleep])(&time_to_sleep, NULL)); } -__weak_reference(__libc_usleep, __usleep); -__weak_reference(__libc_usleep, _usleep); +__weak_reference(__usleep, usleep); +__weak_reference(__usleep, _usleep); diff --git a/lib/libc/gen/wait.c b/lib/libc/gen/wait.c index dc1351d..46a3fdd 100644 --- a/lib/libc/gen/wait.c +++ b/lib/libc/gen/wait.c @@ -42,21 +42,13 @@ __FBSDID("$FreeBSD$"); #include "libc_private.h" -#pragma weak wait pid_t -wait(int *istat) +__wait(int *istat) { - return (((pid_t (*)(int *)) - __libc_interposing[INTERPOS_wait])(istat)); + return (((pid_t (*)(pid_t, int *, int, struct rusage *)) + __libc_interposing[INTERPOS_wait4])(WAIT_ANY, istat, 0, NULL)); } -pid_t -__libc_wait(int *istat) -{ - - return (__sys_wait4(WAIT_ANY, istat, 0, NULL)); -} - -__weak_reference(__libc_wait, __wait); -__weak_reference(__libc_wait, _wait); +__weak_reference(__wait, wait); +__weak_reference(__wait, _wait); diff --git a/lib/libc/gen/wait3.c b/lib/libc/gen/wait3.c index 2e116be..965effe 100644 --- a/lib/libc/gen/wait3.c +++ b/lib/libc/gen/wait3.c @@ -42,20 +42,12 @@ __FBSDID("$FreeBSD$"); #include "libc_private.h" -#pragma weak wait3 pid_t -wait3(int *istat, int options, struct rusage *rup) +__wait3(int *istat, int options, struct rusage *rup) { - return (((pid_t (*)(int *, int, struct rusage *)) - __libc_interposing[INTERPOS_wait3])(istat, options, rup)); + return (((pid_t (*)(pid_t, int *, int, struct rusage *)) + __libc_interposing[INTERPOS_wait4])(WAIT_ANY, istat, options, rup)); } -__weak_reference(__libc_wait3, __wait3); - -pid_t -__libc_wait3(int *istat, int options, struct rusage *rup) -{ - - return (__sys_wait4(WAIT_ANY, istat, options, rup)); -} +__weak_reference(__wait3, wait3); diff --git a/lib/libc/gen/waitpid.c b/lib/libc/gen/waitpid.c index 27e920f..5177591 100644 --- a/lib/libc/gen/waitpid.c +++ b/lib/libc/gen/waitpid.c @@ -42,21 +42,13 @@ __FBSDID("$FreeBSD$"); #include "libc_private.h" -#pragma weak waitpid pid_t -waitpid(pid_t pid, int *istat, int options) +__waitpid(pid_t pid, int *istat, int options) { - return (((pid_t (*)(pid_t, int *, int)) - __libc_interposing[INTERPOS_waitpid])(pid, istat, options)); + return (((pid_t (*)(pid_t, int *, int, struct rusage *)) + __libc_interposing[INTERPOS_wait4])(pid, istat, options, NULL)); } -pid_t -__libc_waitpid(pid_t pid, int *istat, int options) -{ - - return (__sys_wait4(pid, istat, options, NULL)); -} - -__weak_reference(__libc_waitpid, __waitpid); -__weak_reference(__libc_waitpid, _waitpid); +__weak_reference(__waitpid, waitpid); +__weak_reference(__waitpid, _waitpid); diff --git a/lib/libc/include/libc_private.h b/lib/libc/include/libc_private.h index ea1a97b..347b463 100644 --- a/lib/libc/include/libc_private.h +++ b/lib/libc/include/libc_private.h @@ -187,17 +187,14 @@ enum { INTERPOS_aio_suspend, INTERPOS_close, INTERPOS_connect, - INTERPOS_creat, INTERPOS_fcntl, INTERPOS_fsync, INTERPOS_fork, INTERPOS_msync, INTERPOS_nanosleep, - INTERPOS_open, INTERPOS_openat, INTERPOS_poll, INTERPOS_pselect, - INTERPOS_raise, INTERPOS_recvfrom, INTERPOS_recvmsg, INTERPOS_select, @@ -212,16 +209,10 @@ enum { INTERPOS_sigwaitinfo, INTERPOS_swapcontext, INTERPOS_system, - INTERPOS_sleep, INTERPOS_tcdrain, - INTERPOS_usleep, - INTERPOS_pause, INTERPOS_read, INTERPOS_readv, - INTERPOS_wait, - INTERPOS_wait3, INTERPOS_wait4, - INTERPOS_waitpid, INTERPOS_write, INTERPOS_writev, INTERPOS__pthread_mutex_init_calloc_cb, @@ -353,23 +344,17 @@ int __sys_sigwait(const __sigset_t *, int *); int __sys_sigwaitinfo(const __sigset_t *, struct __siginfo *); int __sys_swapcontext(struct __ucontext *, const struct __ucontext *); +int __sys_thr_kill(long, int); +int __sys_thr_self(long *); int __sys_truncate(const char *, __off_t); __pid_t __sys_wait4(__pid_t, int *, int, struct rusage *); __ssize_t __sys_write(int, const void *, __size_t); __ssize_t __sys_writev(int, const struct iovec *, int); -int __libc_creat(const char *path, __mode_t mode); -int __libc_pause(void); -int __libc_raise(int); int __libc_sigwait(const __sigset_t * __restrict, int * restrict sig); int __libc_system(const char *); -unsigned int __libc_sleep(unsigned int); int __libc_tcdrain(int); -int __libc_usleep(__useconds_t); -__pid_t __libc_wait(int *); -__pid_t __libc_wait3(int *, int, struct rusage *); -__pid_t __libc_waitpid(__pid_t, int *, int); int __fcntl_compat(int fd, int cmd, ...); /* execve() with PATH processing to implement posix_spawnp() */ diff --git a/lib/libc/sys/accept.c b/lib/libc/sys/accept.c index 38e32f2..1797783 100644 --- a/lib/libc/sys/accept.c +++ b/lib/libc/sys/accept.c @@ -38,11 +38,11 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include "libc_private.h" -__weak_reference(__sys_accept, __accept); +__weak_reference(__accept, accept); #pragma weak accept int -accept(int s, struct sockaddr *addr, socklen_t *addrlen) +__accept(int s, struct sockaddr *addr, socklen_t *addrlen) { return (((int (*)(int, struct sockaddr *, socklen_t *)) diff --git a/lib/libc/sys/interposing_table.c b/lib/libc/sys/interposing_table.c index 9987bf0..d303779 100644 --- a/lib/libc/sys/interposing_table.c +++ b/lib/libc/sys/interposing_table.c @@ -44,17 +44,14 @@ interpos_func_t __libc_interposing[INTERPOS_MAX] = { SLOT(aio_suspend, __sys_aio_suspend), SLOT(close, __sys_close), SLOT(connect, __sys_connect), - SLOT(creat, __libc_creat), SLOT(fcntl, __fcntl_compat), SLOT(fsync, __sys_fsync), SLOT(fork, __sys_fork), SLOT(msync, __sys_msync), SLOT(nanosleep, __sys_nanosleep), - SLOT(open, __sys_open), SLOT(openat, __sys_openat), SLOT(poll, __sys_poll), SLOT(pselect, __sys_pselect), - SLOT(raise, __libc_raise), SLOT(read, __sys_read), SLOT(readv, __sys_readv), SLOT(recvfrom, __sys_recvfrom), @@ -71,14 +68,8 @@ interpos_func_t __libc_interposing[INTERPOS_MAX] = { SLOT(sigwaitinfo, __sys_sigwaitinfo), SLOT(swapcontext, __sys_swapcontext), SLOT(system, __libc_system), - SLOT(sleep, __libc_sleep), SLOT(tcdrain, __libc_tcdrain), - SLOT(usleep, __libc_usleep), - SLOT(pause, __libc_pause), - SLOT(wait, __libc_wait), - SLOT(wait3, __libc_wait3), SLOT(wait4, __sys_wait4), - SLOT(waitpid, __libc_waitpid), SLOT(write, __sys_write), SLOT(writev, __sys_writev), SLOT(_pthread_mutex_init_calloc_cb, _pthread_mutex_init_calloc_cb_stub), diff --git a/lib/libc/sys/open.c b/lib/libc/sys/open.c index 57c0324..e0273c6 100644 --- a/lib/libc/sys/open.c +++ b/lib/libc/sys/open.c @@ -54,6 +54,6 @@ open(const char *path, int flags, ...) } else { mode = 0; } - return (((int (*)(const char *, int, ...)) - __libc_interposing[INTERPOS_open])(path, flags, mode)); + return (((int (*)(int, const char *, int, ...)) + __libc_interposing[INTERPOS_openat])(AT_FDCWD, path, flags, mode)); } diff --git a/lib/libthr/thread/thr_private.h b/lib/libthr/thread/thr_private.h index 0c16b2a..d62de98 100644 --- a/lib/libthr/thread/thr_private.h +++ b/lib/libthr/thread/thr_private.h @@ -917,8 +917,6 @@ void _thr_stack_fix_protection(struct pthread *thrd); int *__error_threaded(void) __hidden; void __thr_interpose_libc(void) __hidden; pid_t __thr_fork(void); -int __thr_pause(void) __hidden; -int __thr_raise(int sig); int __thr_setcontext(const ucontext_t *ucp); int __thr_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) __hidden; diff --git a/lib/libthr/thread/thr_sig.c b/lib/libthr/thread/thr_sig.c index 1ec64f0..7cd0f75 100644 --- a/lib/libthr/thread/thr_sig.c +++ b/lib/libthr/thread/thr_sig.c @@ -515,23 +515,6 @@ _thr_signal_deinit(void) } int -__thr_pause(void) -{ - sigset_t oset; - - if (_sigprocmask(SIG_BLOCK, NULL, &oset) == -1) - return (-1); - return (__thr_sigsuspend(&oset)); -} - -int -__thr_raise(int sig) -{ - - return (_thr_send_sig(_get_curthread(), sig)); -} - -int __thr_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) { struct sigaction newact, oldact, oldact2; diff --git a/lib/libthr/thread/thr_syscalls.c b/lib/libthr/thread/thr_syscalls.c index a4fe7e8..06b63c8 100644 --- a/lib/libthr/thread/thr_syscalls.c +++ b/lib/libthr/thread/thr_syscalls.c @@ -99,10 +99,6 @@ __FBSDID("$FreeBSD$"); extern int __fcntl_compat(int, int, ...); #endif -/* - * Cancellation behavior: - * If thread is canceled, no socket is created. - */ static int __thr_accept(int s, struct sockaddr *addr, socklen_t *addrlen) { @@ -189,25 +185,6 @@ __thr_connect(int fd, const struct sockaddr *name, socklen_t namelen) return (ret); } - -/* - * Cancellation behavior: - * If thread is canceled, file is not created. - */ -static int -__thr_creat(const char *path, mode_t mode) -{ - struct pthread *curthread; - int ret; - - curthread = _get_curthread(); - _thr_cancel_enter(curthread); - ret = __libc_creat(path, mode); - _thr_cancel_leave(curthread, ret == -1); - - return (ret); -} - /* * Cancellation behavior: * According to specification, only F_SETLKW is a cancellation point. @@ -300,35 +277,6 @@ __thr_nanosleep(const struct timespec *time_to_sleep, * If the thread is canceled, file is not opened. */ static int -__thr_open(const char *path, int flags,...) -{ - struct pthread *curthread; - int mode, ret; - va_list ap; - - /* Check if the file is being created: */ - if ((flags & O_CREAT) != 0) { - /* Get the creation mode: */ - va_start(ap, flags); - mode = va_arg(ap, int); - va_end(ap); - } else { - mode = 0; - } - - curthread = _get_curthread(); - _thr_cancel_enter(curthread); - ret = __sys_open(path, flags, mode); - _thr_cancel_leave(curthread, ret == -1); - - return (ret); -} - -/* - * Cancellation behavior: - * If the thread is canceled, file is not opened. - */ -static int __thr_openat(int fd, const char *path, int flags, ...) { struct pthread *curthread; @@ -523,19 +471,6 @@ __thr_sendto(int s, const void *m, size_t l, int f, const struct sockaddr *t, return (ret); } -static unsigned int -__thr_sleep(unsigned int seconds) -{ - struct pthread *curthread; - unsigned int ret; - - curthread = _get_curthread(); - _thr_cancel_enter(curthread); - ret = __libc_sleep(seconds); - _thr_cancel_leave(curthread, 1); - return (ret); -} - static int __thr_system(const char *string) { @@ -567,55 +502,6 @@ __thr_tcdrain(int fd) return (ret); } -static int -__thr_usleep(useconds_t useconds) -{ - struct pthread *curthread; - int ret; - - curthread = _get_curthread(); - _thr_cancel_enter(curthread); - ret = __libc_usleep(useconds); - _thr_cancel_leave(curthread, 1); - return (ret); -} - -/* - * Cancellation behavior: - * Thread may be canceled at start, but if the system call returns - * a child pid, the thread is not canceled. - */ -static pid_t -__thr_wait(int *istat) -{ - struct pthread *curthread; - pid_t ret; - - curthread = _get_curthread(); - _thr_cancel_enter(curthread); - ret = __libc_wait(istat); - _thr_cancel_leave(curthread, ret <= 0); - return (ret); -} - -/* - * Cancellation behavior: - * Thread may be canceled at start, but if the system call returns - * a child pid, the thread is not canceled. - */ -static pid_t -__thr_wait3(int *status, int options, struct rusage *rusage) -{ - struct pthread *curthread; - pid_t ret; - - curthread = _get_curthread(); - _thr_cancel_enter(curthread); - ret = __libc_wait3(status, options, rusage); - _thr_cancel_leave(curthread, ret <= 0); - return (ret); -} - /* * Cancellation behavior: * Thread may be canceled at start, but if the system call returns @@ -636,24 +522,6 @@ __thr_wait4(pid_t pid, int *status, int options, struct rusage *rusage) /* * Cancellation behavior: - * Thread may be canceled at start, but if the system call returns - * a child pid, the thread is not canceled. - */ -static pid_t -__thr_waitpid(pid_t wpid, int *status, int options) -{ - struct pthread *curthread; - pid_t ret; - - curthread = _get_curthread(); - _thr_cancel_enter(curthread); - ret = __libc_waitpid(wpid, status, options); - _thr_cancel_leave(curthread, ret <= 0); - return (ret); -} - -/* - * Cancellation behavior: * Thread may be canceled at start, but if the thread wrote some data, * it is not canceled. */ @@ -701,17 +569,14 @@ __thr_interpose_libc(void) SLOT(aio_suspend); SLOT(close); SLOT(connect); - SLOT(creat); SLOT(fcntl); SLOT(fsync); SLOT(fork); SLOT(msync); SLOT(nanosleep); - SLOT(open); SLOT(openat); SLOT(poll); SLOT(pselect); - SLOT(raise); SLOT(read); SLOT(readv); SLOT(recvfrom); @@ -728,14 +593,8 @@ __thr_interpose_libc(void) SLOT(sigwaitinfo); SLOT(swapcontext); SLOT(system); - SLOT(sleep); SLOT(tcdrain); - SLOT(usleep); - SLOT(pause); - SLOT(wait); - SLOT(wait3); SLOT(wait4); - SLOT(waitpid); SLOT(write); SLOT(writev); #undef SLOT
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20150107032941.GJ42409>