Revert "Remove __sinit and __sdidinit."
This reverts commit 4371961e00ad83fca033992c8a19c7d262fe6f84.
This broke booting; ueventd crashes with a null pointer dereference
somewhere in __sfp (but the kernel doesn't unwind, so I don't know
what was calling __sfp).
Change-Id: I65375fdfdf1d339a06558b4057b580cacd6324e2
diff --git a/libc/stdio/findfp.c b/libc/stdio/findfp.c
index 4054d5f..2696cfd 100644
--- a/libc/stdio/findfp.c
+++ b/libc/stdio/findfp.c
@@ -44,6 +44,8 @@
#define ALIGNBYTES (sizeof(uintptr_t) - 1)
#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES)
+int __sdidinit;
+
#define NDYNAMIC 10 /* add ten more whenever necessary */
#define std(flags, file) \
@@ -112,6 +114,9 @@
int n;
struct glue *g;
+ if (!__sdidinit)
+ __sinit();
+
_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
for (g = &__sglue; g != NULL; g = g->next) {
for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
@@ -144,21 +149,48 @@
return (fp);
}
-static void __stdio_cleanup(void) {
+/*
+ * exit() and abort() call _cleanup() through the callback registered
+ * with __atexit_register_cleanup(), set whenever we open or buffer a
+ * file. This chicanery is done so that programs that do not use stdio
+ * need not link it all in.
+ *
+ * The name `_cleanup' is, alas, fairly well known outside stdio.
+ */
+void
+_cleanup(void)
+{
/* (void) _fwalk(fclose); */
(void) _fwalk(__sflush); /* `cheating' */
}
-void __libc_init_stdio(void) {
- // Initialize stdin/stdout/stderr (for the recursive mutex). http://b/18208568.
+/*
+ * __sinit() is called whenever stdio's internal variables must be set up.
+ */
+void
+__sinit(void)
+{
+ _THREAD_PRIVATE_MUTEX(__sinit_mutex);
+
+ _THREAD_PRIVATE_MUTEX_LOCK(__sinit_mutex);
+ if (__sdidinit) {
+ /* bail out if caller lost the race */
+ _THREAD_PRIVATE_MUTEX_UNLOCK(__sinit_mutex);
+ return;
+ }
+
+ /* Initialize stdin/stdout/stderr (for the recursive mutex). http://b/18208568. */
for (size_t i = 0; i < 3; ++i) {
_FILEEXT_SETUP(__sF+i, __sFext+i);
}
- // Initialize the pre-allocated (but initially unused) streams.
+ /* Initialize the pre-allocated (but initially unused) streams. */
for (size_t i = 0; i < FOPEN_MAX - 3; ++i) {
_FILEEXT_SETUP(usual+i, usualext+i);
}
- // Make sure we clean up on exit.
- __atexit_register_cleanup(__stdio_cleanup); /* conservative */
+ /* make sure we clean up on exit */
+ __atexit_register_cleanup(_cleanup); /* conservative */
+ __sdidinit = 1;
+
+ _THREAD_PRIVATE_MUTEX_UNLOCK(__sinit_mutex);
}
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index db3068d..3ae7059 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -153,8 +153,10 @@
__LIBC32_LEGACY_PUBLIC__ int __swsetup(FILE*);
/* These were referenced by a couple of different pieces of middleware and the Crystax NDK. */
+__LIBC32_LEGACY_PUBLIC__ extern int __sdidinit;
__LIBC32_LEGACY_PUBLIC__ int __sflags(const char*, int*);
__LIBC32_LEGACY_PUBLIC__ FILE* __sfp(void);
+__LIBC32_LEGACY_PUBLIC__ void __sinit(void);
__LIBC32_LEGACY_PUBLIC__ void __smakebuf(FILE*);
/* These are referenced by the Greed for Glory franchise. */
@@ -168,6 +170,7 @@
#pragma GCC visibility push(hidden)
int __sflush_locked(FILE *);
+void _cleanup(void);
int __swhatbuf(FILE *, size_t *, int *);
wint_t __fgetwc_unlock(FILE *);
wint_t __ungetwc(wint_t, FILE *);
@@ -234,10 +237,6 @@
extern int __sfvwrite(FILE *, struct __suio *);
wint_t __fputwc_unlock(wchar_t wc, FILE *fp);
-/* Remove the if (!__sdidinit) __sinit() idiom from untouched upstream stdio code. */
-extern void __sinit(void); // Not actually implemented.
-#define __sdidinit 1
-
#pragma GCC visibility pop
__END_DECLS
diff --git a/libc/stdio/refill.c b/libc/stdio/refill.c
index 5b0811f..e87c7b9 100644
--- a/libc/stdio/refill.c
+++ b/libc/stdio/refill.c
@@ -51,6 +51,11 @@
int
__srefill(FILE *fp)
{
+
+ /* make sure stdio is set up */
+ if (!__sdidinit)
+ __sinit();
+
fp->_r = 0; /* largely a convenience for callers */
#if !defined(__ANDROID__)