summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Wilcox <awilcox@wilcox-tech.com>2018-02-01 22:05:36 +0000
committerA. Wilcox <awilcox@wilcox-tech.com>2018-02-01 22:05:36 +0000
commit88815225fdabe7fa862f1c07abfcc971fdf5b04e (patch)
tree7aaae7c04fcf06aea85505fc028f476a131b9c50
parentc545f7393f0a10c182c2c085a36d7099b9c07a7f (diff)
parent78722ef403ffadcb78917e93e1859d43b9bf9df5 (diff)
downloadgcompat-88815225fdabe7fa862f1c07abfcc971fdf5b04e.tar.gz
gcompat-88815225fdabe7fa862f1c07abfcc971fdf5b04e.tar.bz2
gcompat-88815225fdabe7fa862f1c07abfcc971fdf5b04e.tar.xz
gcompat-88815225fdabe7fa862f1c07abfcc971fdf5b04e.zip
Merge branch 'patch-1' into 'master'
Clean up everything and add lots of new functions I hope everything here is okay. I fixed some bugs in existing functions (mostly wrong prototypes or off-by-one errors) and formatted everything to a consistent style. If you'd like me to adjust the style, that's no problem. It wasn't very consistent to start with (within the code, and compared to the documentation). *I added specific notes you may want to comment on to some of the commit messages.* Major features: * Pass correct `argv[0]` in loader * Intercept `readlink("/proc/self/exe")` to allow re-exec * Add almost all reasonable-to-implement functions in LSB 5.0.0 core generic libc. Remaining functions are: - Impossible-to-implement: sigreturn - Not useful: reentrant random (`*rand48_r`, etc.), argz, envz, pmap, rpc (clnt_*, svc*, xdr*), bindresvport - Got tired of it: checked wchar * Add additional functions used by android and its NDK tools (clang, cmake, lldb, ninja, etc.). At this point, I am able to run Android Studio with the bundled prebuilt JDK, and compile, install, and run an android application (including one with native libraries) on a real device, with only a few minor issues: * must export `LD_LIBRARY_PATH=/opt/android-studio/jre/jre/lib/amd64/server` because musl and glibc interpret the variable differently with regards to `dlopen`. This is something that has to be patched in musl builds of openjdk, so it's not a gcompat issue. * ld.bfd fails to parse the argument `--sysroot=/path`, but can parse `--sysroot /path`. So there's some difference with `getopt_long_only` (or getopt in general). May be a bug, may be just an API difference. May be it can be patched up. * LLDB fails to connect to the android phone for native debugging -- I haven't tried it on glibc yet, so it may not be a gcompat issue at all (may be a phone or the app issue). I'd be happy to send some documentation later. See merge request !1
-rw-r--r--.clang-format52
-rw-r--r--Makefile34
-rw-r--r--libgcompat/alias.h9
-rw-r--r--libgcompat/backtrace.c44
-rw-r--r--libgcompat/dlfcn.c25
-rw-r--r--libgcompat/dlmopen.c15
-rw-r--r--libgcompat/dlvsym.c14
-rw-r--r--libgcompat/error.c27
-rw-r--r--libgcompat/execinfo.c97
-rw-r--r--libgcompat/gnulib.c2
-rw-r--r--libgcompat/grp.c98
-rw-r--r--libgcompat/malloc.c74
-rw-r--r--libgcompat/math.c96
-rw-r--r--libgcompat/misc.c33
-rw-r--r--libgcompat/netdb.c79
-rw-r--r--libgcompat/pthread.c30
-rw-r--r--libgcompat/pwd.c125
-rw-r--r--libgcompat/readlink.c85
-rw-r--r--libgcompat/resolv.c36
-rw-r--r--libgcompat/resource.c24
-rw-r--r--libgcompat/signal.c11
-rw-r--r--libgcompat/socket.c31
-rw-r--r--libgcompat/stdio.c280
-rw-r--r--libgcompat/stdlib.c124
-rw-r--r--libgcompat/string.c249
-rw-r--r--libgcompat/sysctl.c22
-rw-r--r--libgcompat/syslog.c32
-rw-r--r--libgcompat/ucontext.c2
-rw-r--r--libgcompat/unistd.c172
-rw-r--r--libgcompat/utmp.c14
-rw-r--r--libgcompat/version.c28
-rw-r--r--libgcompat/wchar.c40
-rw-r--r--loader/loader.c98
33 files changed, 1713 insertions, 389 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..05db5a1
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,52 @@
+---
+# for clang-format 4.0.0
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands: false
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: true
+BinPackArguments: true
+BinPackParameters: true
+BreakBeforeBinaryOperators: NonAssignment
+BreakBeforeBraces: WebKit
+BreakBeforeTernaryOperators: true
+BreakStringLiterals: true
+ColumnLimit: 80
+Cpp11BracedListStyle: false
+IncludeCategories:
+ - Priority: 1
+ Regex: '^<'
+ - Priority: 2
+ Regex: '^"'
+IndentCaseLabels: false
+IndentWidth: 8
+IndentWrappedFunctionNames: false
+KeepEmptyLinesAtTheStartOfBlocks: false
+Language: Cpp
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+PointerAlignment: Right
+ReflowComments: true
+SortIncludes: true
+SpaceAfterCStyleCast: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInCStyleCastParentheses: false
+SpacesInContainerLiterals: true
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp11
+TabWidth: 8
+UseTab: ForIndentation
+...
diff --git a/Makefile b/Makefile
index c595290..b6bee29 100644
--- a/Makefile
+++ b/Makefile
@@ -1,21 +1,33 @@
+LIBGCOMPAT_INCLUDE = \
+ libgcompat/alias.h
LIBGCOMPAT_SRC = \
- libgcompat/backtrace.c \
- libgcompat/dlmopen.c \
- libgcompat/dlvsym.c \
+ libgcompat/dlfcn.c \
+ libgcompat/error.c \
+ libgcompat/execinfo.c \
libgcompat/gnulib.c \
+ libgcompat/grp.c \
libgcompat/malloc.c \
libgcompat/math.c \
+ libgcompat/misc.c \
+ libgcompat/netdb.c \
libgcompat/pthread.c \
libgcompat/pwd.c \
+ libgcompat/readlink.c \
libgcompat/resolv.c \
libgcompat/resource.c \
libgcompat/setjmp.c \
+ libgcompat/signal.c \
+ libgcompat/socket.c \
libgcompat/stdio.c \
libgcompat/stdlib.c \
libgcompat/string.c \
libgcompat/sysctl.c \
+ libgcompat/syslog.c \
libgcompat/ucontext.c \
- libgcompat/version.c
+ libgcompat/unistd.c \
+ libgcompat/utmp.c \
+ libgcompat/version.c \
+ libgcompat/wchar.c
LIBGCOMPAT_OBJ = ${LIBGCOMPAT_SRC:.c=.o}
LIBGCOMPAT_SOVERSION = 0
LIBGCOMPAT_NAME = libgcompat.so.${LIBGCOMPAT_SOVERSION}
@@ -33,17 +45,25 @@ ${LIBGCOMPAT_NAME}: ${LIBGCOMPAT_OBJ}
$(CC) -o ${LIBGCOMPAT_NAME} -Wl,-soname,${LIBGCOMPAT_NAME} \
-shared ${LIBGCOMPAT_OBJ}
+${LIBGCOMPAT_OBJ}: ${LIBGCOMPAT_INCLUDE}
+
${LOADER_NAME}: ${LOADER_OBJ}
$(CC) -o ${LOADER_NAME} -fPIE -static ${LOADER_OBJ}
.c.o:
- $(CC) -std=c99 -D_BSD_SOURCE -fPIC -DPIC -DLINKER=\"${LINKER_PATH}\" \
- -DLIBGCOMPAT=\"${LIBGCOMPAT_PATH}\" ${CFLAGS} ${CPPFLAGS} \
- -c -o $@ $<
+ $(CC) -c -D_BSD_SOURCE -DLIBGCOMPAT=\"${LIBGCOMPAT_PATH}\" \
+ -DLINKER=\"${LINKER_PATH}\" -DLOADER=\"${LOADER_NAME}\" \
+ -fPIC -std=c99 -Wall -Wextra -Wno-frame-address \
+ -Wno-unused-parameter ${CFLAGS} ${CPPFLAGS} -o $@ $<
clean:
rm -f libgcompat/*.o loader/*.o ${LIBGCOMPAT_NAME} ${LOADER_NAME}
+format:
+ clang-format -i ${LIBGCOMPAT_SRC} ${LOADER_SRC}
+
install: all
install -D -m755 ${LIBGCOMPAT_NAME} ${DESTDIR}/${LIBGCOMPAT_PATH}
install -D -m755 ${LOADER_NAME} ${DESTDIR}/${LOADER_PATH}
+
+.PHONY: all clean format install
diff --git a/libgcompat/alias.h b/libgcompat/alias.h
new file mode 100644
index 0000000..3f54672
--- /dev/null
+++ b/libgcompat/alias.h
@@ -0,0 +1,9 @@
+#ifndef _ALIAS_H_
+#define _ALIAS_H_
+
+#define alias(old, new) \
+ extern __typeof(old) new __attribute__((__alias__(#old)))
+#define weak_alias(old, new) \
+ extern __typeof(old) new __attribute__((weak, __alias__(#old)))
+
+#endif /* _ALIAS_H_ */
diff --git a/libgcompat/backtrace.c b/libgcompat/backtrace.c
deleted file mode 100644
index 2ceb334..0000000
--- a/libgcompat/backtrace.c
+++ /dev/null
@@ -1,44 +0,0 @@
-#include <dlfcn.h>
-#include <stddef.h>
-#include <stdlib.h>
-
-#define _frame_level(addr_buf, curr, frame, size) \
- if(__builtin_frame_address(frame) != NULL && (curr = __builtin_return_address(frame)) > 0x1000 && frame <= size) addr_buf[frame] = curr; \
- else return size;
-
-int backtrace(void **addr_buf, int size)
-{
- void *curr;
- _frame_level(addr_buf, curr, 0, size)
- _frame_level(addr_buf, curr, 1, size)
- _frame_level(addr_buf, curr, 2, size)
- _frame_level(addr_buf, curr, 3, size)
- _frame_level(addr_buf, curr, 4, size)
- _frame_level(addr_buf, curr, 5, size)
- _frame_level(addr_buf, curr, 6, size)
- _frame_level(addr_buf, curr, 7, size)
- _frame_level(addr_buf, curr, 8, size)
- _frame_level(addr_buf, curr, 9, size)
- return 9;
-}
-
-char **backtrace_symbols(void * const *addr_buf, int size)
-{
- char **result = calloc(sizeof(char *), size);
- if(result == NULL) return result;
-
- for(int next = 0; next < size; next++)
- {
- Dl_info info;
- int err = dladdr(addr_buf[next], &info);
-
- if(err != 0)
- {
- result[next] = "??:0";
- } else {
- result[next] = info.dli_sname;
- }
- }
-
- return result;
-}
diff --git a/libgcompat/dlfcn.c b/libgcompat/dlfcn.c
new file mode 100644
index 0000000..f2eaa45
--- /dev/null
+++ b/libgcompat/dlfcn.c
@@ -0,0 +1,25 @@
+#include <dlfcn.h> /* dlopen, dlsym */
+#include <stddef.h> /* NULL */
+#include <stdio.h> /* fprintf */
+#include <stdlib.h> /* getenv */
+
+void *dlmopen(long lmid, const char *pathname, int mode)
+{
+ if (getenv("GLIBC_FAKE_DEBUG") != NULL) {
+ fprintf(stderr,
+ "loading library %s was requested in namespace %ld",
+ pathname, lmid);
+ }
+
+ return dlopen(pathname, mode);
+}
+
+void *dlvsym(void *handle, char *symbol, char *version)
+{
+ if (getenv("GLIBC_FAKE_DEBUG") != NULL) {
+ fprintf(stderr, "symbol %s with version %s is being redirected",
+ symbol, version);
+ }
+
+ return dlsym(handle, symbol);
+}
diff --git a/libgcompat/dlmopen.c b/libgcompat/dlmopen.c
deleted file mode 100644
index fecb29c..0000000
--- a/libgcompat/dlmopen.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <dlfcn.h> // dlopen
-#include <stdio.h> // fprintf
-#include <stdlib.h> // getenv
-
-void *dlmopen(long lmid, const char *pathname, int mode)
-{
- if(getenv("GLIBC_FAKE_DEBUG"))
- {
- fprintf(stderr, "library %s was requested to load in %ld namespace",
- pathname, lmid);
- }
-
- return dlopen(pathname, mode);
-}
-
diff --git a/libgcompat/dlvsym.c b/libgcompat/dlvsym.c
deleted file mode 100644
index 3d744ea..0000000
--- a/libgcompat/dlvsym.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <dlfcn.h> // dlsym
-#include <stdio.h> // fprintf
-#include <stdlib.h> // getenv
-
-void *dlvsym(void *handle, char *symbol, char *version)
-{
- if(getenv("GLIBC_FAKE_DEBUG"))
- {
- fprintf(stderr, "symbol %s with version %s is being redirected",
- symbol, version);
- }
-
- return dlsym(handle, symbol);
-}
diff --git a/libgcompat/error.c b/libgcompat/error.c
new file mode 100644
index 0000000..c759b19
--- /dev/null
+++ b/libgcompat/error.c
@@ -0,0 +1,27 @@
+#define _GNU_SOURCE /* program_invocation_name */
+#include <errno.h> /* program_invocation_name */
+#include <stdarg.h> /* va_list, va_start, va_end */
+#include <stdio.h> /* fflush, fputc, fputs, stderr, stdout, vfprintf */
+#include <string.h> /* strerror */
+
+/**
+ * Print an error message.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-error-n.html
+ */
+void error(int status, int errnum, const char *format, ...)
+{
+ va_list ap;
+
+ fflush(stdout);
+ fputs(program_invocation_name, stderr);
+ fputs(": ", stderr);
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ if (errnum != 0) {
+ fputs(": ", stderr);
+ fputs(strerror(errnum), stderr);
+ fputc('\n', stderr);
+ }
+}
diff --git a/libgcompat/execinfo.c b/libgcompat/execinfo.c
new file mode 100644
index 0000000..59441aa
--- /dev/null
+++ b/libgcompat/execinfo.c
@@ -0,0 +1,97 @@
+#include <dlfcn.h> /* dladdr */
+#include <stddef.h> /* NULL */
+#include <stdint.h> /* uintptr_t */
+#include <stdlib.h> /* calloc */
+#include <string.h> /* strlen */
+#include <unistd.h> /* write */
+
+#define get_frame_level(array, size, n) \
+ do { \
+ if (n >= size || __builtin_frame_address(n) == NULL) { \
+ return n; \
+ } \
+ void *address = __builtin_return_address(n); \
+ array[n] = __builtin_extract_return_addr(address); \
+ if ((uintptr_t) array[n] < 0x1000) { \
+ return n; \
+ } \
+ } while (0)
+
+/**
+ * Obtain a backtrace for the calling program.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-backtrace-1.html
+ */
+int backtrace(void **array, int size)
+{
+ get_frame_level(array, size, 0);
+ get_frame_level(array, size, 1);
+ get_frame_level(array, size, 2);
+ get_frame_level(array, size, 3);
+ get_frame_level(array, size, 4);
+ get_frame_level(array, size, 5);
+ get_frame_level(array, size, 6);
+ get_frame_level(array, size, 7);
+ get_frame_level(array, size, 8);
+ get_frame_level(array, size, 9);
+ return 10;
+}
+
+/**
+ * Translate addresses into symbol information.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-backtrace-1.html
+ */
+const char **backtrace_symbols(void *const *array, int size)
+{
+ const char **result = calloc(size, sizeof(char *));
+
+ if (result == NULL) {
+ return NULL;
+ }
+ for (int i = 0; i < size; ++i) {
+ Dl_info info;
+
+ if (dladdr(array[i], &info) && info.dli_sname != NULL) {
+ result[i] = info.dli_sname;
+ } else {
+ result[i] = "??:0";
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Write symbol information to a file without allocating memory.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-backtrace-1.html
+ */
+void backtrace_symbols_fd(void *const *array, int size, int fd)
+{
+ for (int i = 0; i < size; ++i) {
+ Dl_info info;
+ const char *line;
+ int len;
+
+ if (dladdr(array[i], &info) && info.dli_sname != NULL) {
+ line = info.dli_sname;
+ len = strlen(line);
+ } else {
+ line = "??:0";
+ len = sizeof("??:0") - 1;
+ }
+ while (len > 0) {
+ int written = write(fd, line, len);
+
+ if (written < 1) {
+ return;
+ }
+ line += written;
+ len -= written;
+ }
+ if (write(fd, "\n", 1) != 1) {
+ return;
+ }
+ }
+}
diff --git a/libgcompat/gnulib.c b/libgcompat/gnulib.c
index 08544fc..e0ee953 100644
--- a/libgcompat/gnulib.c
+++ b/libgcompat/gnulib.c
@@ -4,5 +4,5 @@
unsigned long __fdelt_chk(unsigned long size)
{
assert(size < FD_SETSIZE);
- return size / (sizeof(unsigned long)<<3);
+ return size / (sizeof(unsigned long) << 3);
}
diff --git a/libgcompat/grp.c b/libgcompat/grp.c
new file mode 100644
index 0000000..5674fda
--- /dev/null
+++ b/libgcompat/grp.c
@@ -0,0 +1,98 @@
+/* some musl versions incorrectly mark fgetgrent() as a GNU extension */
+#define _GNU_SOURCE
+#include <assert.h> /* assert */
+#include <errno.h> /* ENOENT, ERANGE */
+#include <grp.h> /* fgetgrent, getgrent, struct group */
+#include <pthread.h> /* pthread_mutex_* */
+#include <stddef.h> /* NULL, size_t */
+#include <stdint.h> /* ptrdiff_t, uintptr_t */
+#include <stdio.h> /* FILE */
+#include <string.h> /* memcpy, stpcpy, strlcpy, strlen */
+
+#define ALIGN_PTR_TO_SIZE_OF(ptr, type) \
+ ((type *) ((((uintptr_t)(ptr)) + sizeof(type) - 1) \
+ & ~(sizeof(type) - 1)))
+
+static pthread_mutex_t grent_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static int __fgetgrent_r(FILE *stream, struct group *grp, char *buf, size_t len,
+ struct group **result)
+{
+ struct group *grtmp;
+ char *cursor = buf, *end = buf + len;
+
+ *result = NULL;
+ pthread_mutex_lock(&grent_mutex);
+ grtmp = stream != NULL ? fgetgrent(stream) : getgrent();
+ if (grtmp == NULL) {
+ pthread_mutex_unlock(&grent_mutex);
+ return ENOENT;
+ }
+ memcpy(grp, grtmp, sizeof(*grp));
+ if (grtmp->gr_name != NULL) {
+ grp->gr_name = cursor;
+ cursor += strlcpy(cursor, grtmp->gr_name, end - cursor) + 1;
+ if (cursor > end) {
+ goto err_unlock;
+ }
+ }
+ if (grtmp->gr_passwd != NULL) {
+ grp->gr_passwd = cursor;
+ cursor += strlcpy(cursor, grtmp->gr_passwd, end - cursor) + 1;
+ if (cursor > end) {
+ goto err_unlock;
+ }
+ }
+ if (grtmp->gr_mem != NULL) {
+ char **members = ALIGN_PTR_TO_SIZE_OF(cursor, char *);
+ ptrdiff_t nameslen = 0;
+ size_t nmem = 0;
+
+ /* Calculate total size of strings plus their pointers. */
+ while (grtmp->gr_mem[nmem++] != NULL) {
+ nameslen += strlen(grtmp->gr_mem[nmem - 1]) + 1;
+ }
+ nameslen += nmem * sizeof(*members);
+ if (nameslen > end - ((char *) members)) {
+ goto err_unlock;
+ }
+ /* Copy the pointers, including the NULL sentinel. */
+ for (size_t i = 0; i < nmem; ++i) {
+ members[i] = grtmp->gr_mem[i];
+ }
+ /* Copy the strings (the NULL sentinel doesn't point to one). */
+ cursor = (char *) &members[nmem];
+ for (size_t i = 0; i < nmem - 1; ++i) {
+ cursor = stpcpy(cursor, members[i]) + 1;
+ }
+ }
+ pthread_mutex_unlock(&grent_mutex);
+ *result = grp;
+
+ return 0;
+
+err_unlock:
+ pthread_mutex_unlock(&grent_mutex);
+ return ERANGE;
+}
+
+/**
+ * Get group file entry.
+ */
+int fgetgrent_r(FILE *stream, struct group *grp, char *buf, size_t len,
+ struct group **result)
+{
+ assert(stream != NULL);
+
+ return __fgetgrent_r(stream, grp, buf, len, result);
+}
+
+/**
+ * Get group database entry.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-getgrent-r-1.html
+ */
+int getgrent_r(struct group *grp, char *buf, size_t len, struct group **result)
+{
+ return __fgetgrent_r(NULL, grp, buf, len, result);
+}
diff --git a/libgcompat/malloc.c b/libgcompat/malloc.c
index 7478834..14fbf66 100644
--- a/libgcompat/malloc.c
+++ b/libgcompat/malloc.c
@@ -1,16 +1,17 @@
-/* struct mallinfo pulled from mallinfo.3:
+/*
+ * struct mallinfo pulled from mallinfo.3:
*
* Copyright (c) 2012 by Michael Kerrisk <mtk.manpages@gmail.com>
- *
+ *
* Permission is granted to make and distribute verbatim copies of this
* manual provided the copyright notice and this permission notice are
* preserved on all copies.
- *
+ *
* Permission is granted to copy and distribute modified versions of this
* manual under the conditions for verbatim copying, provided that the
* entire resulting derived work is distributed under the terms of a
* permission notice identical to this one.
- *
+ *
* Since the Linux kernel and libraries are constantly changing, this
* manual page may be incorrect or out-of-date. The author(s) assume no
* responsibility for errors or omissions, or for damages resulting from
@@ -18,62 +19,63 @@
* have taken the same level of care in the production of this manual,
* which is licensed free of charge, as they might when working
* professionally.
- *
+ *
* Formatted or processed versions of this manual, if unaccompanied by
* the source, must acknowledge the copyright and authors of this work.
*/
-#include <string.h> /* memset */
-#include <stdlib.h> /* {m,c,re}alloc, free */
-#include <malloc.h> /* memalign */
+#include <malloc.h> /* memalign */
+#include <stdlib.h> /* {m,c,re}alloc, free */
+#include <string.h> /* memset */
+
+#include "alias.h" /* alias */
struct mallinfo {
- int arena; /* Non-mmapped space allocated (bytes) */
- int ordblks; /* Number of free chunks */
- int smblks; /* Number of free fastbin blocks */
- int hblks; /* Number of mmapped regions */
- int hblkhd; /* Space allocated in mmapped regions (bytes) */
- int usmblks; /* Maximum total allocated space (bytes) */
- int fsmblks; /* Space in freed fastbin blocks (bytes) */
- int uordblks; /* Total allocated space (bytes) */
- int fordblks; /* Total free space (bytes) */
- int keepcost; /* Top-most, releasable space (bytes) */
+ int arena; /* Non-mmapped space allocated (bytes) */
+ int ordblks; /* Number of free chunks */
+ int smblks; /* Number of free fastbin blocks */
+ int hblks; /* Number of mmapped regions */
+ int hblkhd; /* Space allocated in mmapped regions (bytes) */
+ int usmblks; /* Maximum total allocated space (bytes) */
+ int fsmblks; /* Space in freed fastbin blocks (bytes) */
+ int uordblks; /* Total allocated space (bytes) */
+ int fordblks; /* Total free space (bytes) */
+ int keepcost; /* Top-most, releasable space (bytes) */
};
-struct mallinfo mallinfo(void)
+void *__libc_calloc(size_t nmemb, size_t size)
{
- struct mallinfo my_info;
- memset(&my_info, 0, sizeof(struct mallinfo));
- return my_info;
+ return calloc(nmemb, size);
}
+alias(__libc_calloc, __calloc);
-void *__libc_malloc(size_t size)
+void __libc_free(void *ptr)
{
- return malloc(size);
+ free(ptr);
}
+alias(__libc_free, __free);
-void __libc_free(void *ptr)
+void *__libc_malloc(size_t size)
{
- return free(ptr);
+ return malloc(size);
}
+alias(__libc_malloc, __malloc);
-void *__libc_calloc(size_t nmemb, size_t size)
+void *__libc_memalign(size_t align, size_t len)
{
- return calloc(nmemb, size);
+ return memalign(align, len);
}
+alias(__libc_memalign, __memalign);
void *__libc_realloc(void *ptr, size_t size)
{
return realloc(ptr, size);
}
+alias(__libc_realloc, __realloc);
-void *__libc_memalign(size_t align, size_t len)
+struct mallinfo mallinfo(void)
{
- return memalign(align, len);
+ struct mallinfo info;
+ memset(&info, 0, sizeof(info));
+ return info;
}
-
-extern __typeof(__libc_malloc) __malloc __attribute__((weak, alias("__libc_malloc")));
-extern __typeof(__libc_calloc) __calloc __attribute__((weak, alias("__libc_calloc")));
-extern __typeof(__libc_realloc) __realloc __attribute__((weak, alias("__libc_realloc")));
-extern __typeof(__libc_free) __free __attribute__((weak, alias("__libc_free")));
-
diff --git a/libgcompat/math.c b/libgcompat/math.c
index 2c793e4..db123f8 100644
--- a/libgcompat/math.c
+++ b/libgcompat/math.c
@@ -1,26 +1,96 @@
-#include <math.h> // isinf, isnan
+#include <math.h> /* isfinite, isinf, isnan */
-int __isinff(float number)
+#include "alias.h" /* weak_alias */
+
+/**
+ * Test for finite value.
+ */
+int __finite(double arg)
+{
+ return isfinite(arg);
+}
+weak_alias(__finite, finite);
+
+/**
+ * Test for finite value.
+ */
+int __finitef(float arg)
{
- return isinf(number);
+ return isfinite(arg);
}
+weak_alias(__finitef, finitef);
-int __isinf(double number)
+/**
+ * Test for finite value.
+ */
+int __finitel(long double arg)
{
- return isinf(number);
+ return isfinite(arg);
}
+weak_alias(__finitel, finitel);
-int __isnanf(float number)
+/**
+ * Test for infinity.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---isinf.html
+ */
+int __isinf(double arg)
{
- return isnan(number);
+ return isinf(arg);
}
+weak_alias(__isinf, isinf);
-int __isnan(double number)
+/**
+ * Test for infinity.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---isinff.html
+ */
+int __isinff(float arg)
{
- return isnan(number);
+ return isinf(arg);
}
+weak_alias(__isinff, isinff);
-extern __typeof(__isnanf) isnanf __attribute__((weak, alias("__isnanf")));
-extern __typeof(__isnan) isnan __attribute__((weak, alias("__isnan")));
-extern __typeof(__isinff) isinff __attribute__((weak, alias("__isinff")));
-extern __typeof(__isinf) isinf __attribute__((weak, alias("__isinf")));
+/**
+ * Test for infinity.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---isinfl.html
+ */
+int __isinfl(long double arg)
+{
+ return isinf(arg);
+}
+weak_alias(__isinfl, isinfl);
+
+/**
+ * Test for a NaN.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---isnan.html
+ */
+int __isnan(double arg)
+{
+ return isnan(arg);
+}
+weak_alias(__isnan, isnan);
+
+/**
+ * Test for a NaN.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---isnanf.html
+ */
+int __isnanf(float arg)
+{
+ return isnan(arg);
+}
+weak_alias(__isnanf, isnanf);
+
+/**
+ * Test for a NaN.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---isnanl.html
+ */
+int __isnanl(long double arg)
+{
+ return isnan(arg);
+}
+weak_alias(__isnanl, isnanl);
diff --git a/libgcompat/misc.c b/libgcompat/misc.c
new file mode 100644
index 0000000..aee490f
--- /dev/null
+++ b/libgcompat/misc.c
@@ -0,0 +1,33 @@
+#include <stdlib.h> /* abort, at_quick_exit */
+#include <sys/stat.h> /* dev_t */
+#include <sys/sysmacros.h> /* major, makedev, minor */
+
+/**
+ * Terminate a function in case of buffer overflow.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---chk-fail-1.html
+ */
+void __chk_fail(void)
+{
+ abort();
+}
+
+int __cxa_at_quick_exit(void (*func)(void), void *__dso_handle)
+{
+ return at_quick_exit(func);
+}
+
+unsigned int gnu_dev_major(dev_t dev)
+{
+ return major(dev);
+}
+
+dev_t gnu_dev_makedev(unsigned int maj, unsigned int min)
+{
+ return makedev(maj, min);
+}
+
+unsigned int gnu_dev_minor(dev_t dev)
+{
+ return minor(dev);
+}
diff --git a/libgcompat/netdb.c b/libgcompat/netdb.c
new file mode 100644
index 0000000..ae7713a
--- /dev/null
+++ b/libgcompat/netdb.c
@@ -0,0 +1,79 @@
+#include <errno.h> /* ENOENT */
+#include <netdb.h> /* getproto* */
+#include <stddef.h> /* NULL, size_t */
+#include <string.h> /* memcpy */
+
+/**
+ * Retrieve information from the network protocol database by protocol name,
+ * reentrantly.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-getprotobyname-r.html
+ */
+int getprotobyname_r(const char *name, struct protoent *result_buf, char *buf,
+ size_t buflen, struct protoent **result)
+{
+ struct protoent *prototmp = getprotobyname(name);
+
+ /* musl does not reuse static storage, so no race is possible. */
+ if (prototmp == NULL) {
+ *result = NULL;
+ return ENOENT;
+ }
+ *result = memcpy(result_buf, prototmp, sizeof(*result_buf));
+
+ return 0;
+}
+
+/**
+ * Retrieve information from the network protocol database by protocol number,
+ * reentrantly.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-getprotobynumber-r.html
+ */
+int getprotobynumber_r(int proto, struct protoent *result_buf, char *buf,
+ size_t buflen, struct protoent **result)
+{
+ struct protoent *prototmp = getprotobynumber(proto);
+
+ /* musl does not reuse static storage, so no race is possible. */
+ if (prototmp == NULL) {
+ *result = NULL;
+ return ENOENT;
+ }
+ *result = memcpy(result_buf, prototmp, sizeof(*result_buf));
+
+ return 0;
+}
+
+/**
+ * Read the next entry of the protocol database, reentrantly.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-getprotoent-r.html
+ */
+int getprotoent_r(struct protoent *result_buf, char *buf, size_t buflen,
+ struct protoent **result)
+{
+ struct protoent *prototmp = getprotoent();
+
+ /* musl does not reuse static storage, so no race is possible. */
+ if (prototmp == NULL) {
+ *result = NULL;
+ return ENOENT;
+ }
+ *result = memcpy(result_buf, prototmp, sizeof(*result_buf));
+
+ return 0;
+}
+
+/**
+ * Read the next entry of the network services database, reentrantly.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-getservent-r.html
+ */
+int getservent_r(struct servent *result_buf, char *buf, size_t buflen,
+ struct servent **result)
+{
+ /* musl does not implement getservent(). */
+ *result = NULL;
+ return ENOENT;
+}
diff --git a/libgcompat/pthread.c b/libgcompat/pthread.c
index ddfc570..4ebbe6b 100644
--- a/libgcompat/pthread.c
+++ b/libgcompat/pthread.c
@@ -1,15 +1,29 @@
#include <pthread.h>
-int __register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) {
- return pthread_atfork(prepare, parent, child);
-}
-
-int register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) __attribute__ ((weak, alias("__register_atfork")));
+#include "alias.h" /* weak_alias */
-
-void __pthread_register_cancel(void *buf) {
+/**
+ * Underlying function for pthread_cleanup_push.
+ */
+void __pthread_register_cancel(void *buf)
+{
}
+/**
+ * Underlying function for pthread_cleanup_push.
+ */
+void __pthread_unregister_cancel(void *buf)
+{
+}
-void __pthread_unregister_cancel(void *buf) {
+/**
+ * Register fork handlers.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---register-atfork.html
+ */
+int __register_atfork(void (*prepare)(void), void (*parent)(void),
+ void (*child)(void), void *__dso_handle)
+{
+ return pthread_atfork(prepare, parent, child);
}
+weak_alias(__register_atfork, register_atfork);
diff --git a/libgcompat/pwd.c b/libgcompat/pwd.c
index 2e09aea..34c5d7b 100644
--- a/libgcompat/pwd.c
+++ b/libgcompat/pwd.c
@@ -1,55 +1,92 @@
/* some musl versions incorrectly mark fgetpwent() as a GNU extension */
#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <errno.h>
-#include <pwd.h>
-#include <string.h>
-
-
-int getpwent_r(struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp) {
- struct passwd *pwd;
-
- if (pwbufp == NULL || pwbuf == NULL)
- return ERANGE;
-
- if (buflen < 1)
- return ERANGE;
-
- if (buf != NULL)
- *buf = '\0';
-
- if ((pwd = getpwent()) == NULL) {
- *pwbufp = NULL;
+#include <assert.h> /* assert */
+#include <errno.h> /* ENOENT, ERANGE */
+#include <pthread.h> /* pthread_mutex_* */
+#include <pwd.h> /* fgetpwent, getpwent, struct passwd */
+#include <stddef.h> /* NULL, size_t */
+#include <stdio.h> /* FILE */
+#include <string.h> /* memcpy, stpcpy, strlcpy, strlen */
+
+static pthread_mutex_t pwent_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static int __fgetpwent_r(FILE *stream, struct passwd *pwd, char *buf,
+ size_t len, struct passwd **result)
+{
+ struct passwd *pwtmp;
+ char *cursor = buf, *end = buf + len;
+
+ *result = NULL;
+ pthread_mutex_lock(&pwent_mutex);
+ pwtmp = stream != NULL ? fgetpwent(stream) : getpwent();
+ if (pwtmp == NULL) {
+ pthread_mutex_unlock(&pwent_mutex);
return ENOENT;
}
-
- memcpy(pwbuf, pwd, sizeof(*pwd));
- *pwbufp = pwbuf;
+ memcpy(pwd, pwtmp, sizeof(*pwd));
+ if (pwtmp->pw_name != NULL) {
+ pwd->pw_name = cursor;
+ cursor += strlcpy(cursor, pwtmp->pw_name, end - cursor) + 1;
+ if (cursor > end) {
+ goto err_unlock;
+ }
+ }
+ if (pwtmp->pw_passwd != NULL) {
+ pwd->pw_passwd = cursor;
+ cursor += strlcpy(cursor, pwtmp->pw_passwd, end - cursor) + 1;
+ if (cursor > end) {
+ goto err_unlock;
+ }
+ }
+ if (pwtmp->pw_gecos != NULL) {
+ pwd->pw_gecos = cursor;
+ cursor += strlcpy(cursor, pwtmp->pw_gecos, end - cursor) + 1;
+ if (cursor > end) {
+ goto err_unlock;
+ }
+ }
+ if (pwtmp->pw_dir != NULL) {
+ pwd->pw_dir = cursor;
+ cursor += strlcpy(cursor, pwtmp->pw_dir, end - cursor) + 1;
+ if (cursor > end) {
+ goto err_unlock;
+ }
+ }
+ if (pwtmp->pw_shell != NULL) {
+ pwd->pw_shell = cursor;
+ cursor += strlcpy(cursor, pwtmp->pw_shell, end - cursor) + 1;
+ if (cursor > end) {
+ goto err_unlock;
+ }
+ }
+ pthread_mutex_unlock(&pwent_mutex);
+ *result = pwd;
return 0;
-}
-
-
-int fgetpwent_r(FILE *filp, struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp) {
- struct passwd *pwd;
-
- if (pwbufp == NULL || pwbuf == NULL)
- return ERANGE;
-
- if (buflen < 1)
- return ERANGE;
- if (buf != NULL)
- *buf = '\0';
+err_unlock:
+ pthread_mutex_unlock(&pwent_mutex);
+ return ERANGE;
+}
- if ((pwd = fgetpwent(filp)) == NULL) {
- *pwbufp = NULL;
- return ENOENT;
- }
+/**
+ * Get passwd file entry.
+ */
+int fgetpwent_r(FILE *stream, struct passwd *pwd, char *buf, size_t len,
+ struct passwd **result)
+{
+ assert(stream != NULL);
- memcpy(pwbuf, pwd, sizeof(*pwd));
- *pwbufp = pwbuf;
+ return fgetpwent_r(stream, pwd, buf, len, result);
+}
- return 0;
+/**
+ * Get user database entry.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-getpwent-r-1.html
+ */
+int getpwent_r(struct passwd *pwd, char *buf, size_t len,
+ struct passwd **result)
+{
+ return __fgetpwent_r(NULL, pwd, buf, len, result);
}
diff --git a/libgcompat/readlink.c b/libgcompat/readlink.c
new file mode 100644
index 0000000..63def96
--- /dev/null
+++ b/libgcompat/readlink.c
@@ -0,0 +1,85 @@
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef LINKER
+#error LINKER must be defined
+#endif
+
+static char exe[PATH_MAX], *linker;
+static ssize_t (*real_readlink)(const char *, char *, size_t);
+
+ssize_t readlink(const char *path, char *buf, size_t len)
+{
+ if (real_readlink == NULL) {
+ real_readlink = dlsym(RTLD_NEXT, "readlink");
+ if (real_readlink == NULL) {
+ return -1;
+ }
+ }
+
+ if (!strcmp(path, "/proc/self/exe")) {
+ int fd;
+
+ if (exe[0] == '\0') {
+ if (linker == NULL) {
+ linker = realpath(LINKER, NULL);
+ if (linker == NULL) {
+ return -1;
+ }
+ }
+ if (real_readlink(path, exe, sizeof(exe)) < 1) {
+ goto fail;
+ }
+ if (!strcmp(exe, linker)) {
+ char c;
+ int arg = 0;
+ ssize_t arglen;
+
+ fd = open("/proc/self/cmdline",
+ O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ goto fail;
+ }
+ /* Skip the --argv0/--preload ldso args.
+ * This number must be kept in sync with the
+ * argument order in loader/loader.c */
+ while (arg < 5) {
+ if (read(fd, &c, 1) != 1) {
+ goto fail_close;
+ }
+ if (c == '\0') {
+ ++arg;
+ }
+ }
+ /* Read the executable path from the cmdline. */
+ arglen = read(fd, exe, sizeof(exe));
+ if (arglen < 1) {
+ goto fail_close;
+ }
+ close(fd);
+ /* Ensure the path exists, fits, and has NUL. */
+ if (exe[0] == '\0') {
+ goto fail;
+ }
+ if (strnlen(exe, arglen) == (size_t) arglen) {
+ goto fail;
+ }
+ }
+ }
+
+ return stpncpy(buf, exe, len) - buf;
+
+ fail_close:
+ close(fd);
+ fail:
+ exe[0] = '\0';
+ return -1;
+ }
+
+ return real_readlink(path, buf, len);
+}
diff --git a/libgcompat/resolv.c b/libgcompat/resolv.c
index 9ded8c9..5943bcc 100644
--- a/libgcompat/resolv.c
+++ b/libgcompat/resolv.c
@@ -1,32 +1,42 @@
-/* Original author: Khem Raj <raj.khem@gmail.com> */
-/***************************************************************************
- * resolv_compat.h
+/*
+ * Original author: Khem Raj <raj.khem@gmail.com>
*
* Mimick GLIBC's res_ninit() and res_nclose() for musl libc
* Note: res_init() is actually deprecated according to
* http://docs.oracle.com/cd/E36784_01/html/E36875/res-nclose-3resolv.html
- **************************************************************************/
-#include <string.h> /* memcpy, memset */
-#include <resolv.h> /* res_state */
+ */
-static inline int res_ninit(res_state statp)
+#include <resolv.h> /* res_state */
+#include <stddef.h> /* NULL */
+#include <string.h> /* memcpy, memset */
+
+#include "alias.h" /* weak_alias */
+
+int __res_ninit(res_state statp)
{
- int rc = res_init();
+ int rc;
+
+ if (statp == NULL) {
+ return -1;
+ }
+ rc = res_init();
if (statp != &_res) {
memcpy(statp, &_res, sizeof(*statp));
}
+
return rc;
}
+weak_alias(__res_ninit, res_ninit);
-static inline int res_nclose(res_state statp)
+int __res_nclose(res_state statp)
{
- if (!statp)
+ if (statp == NULL) {
return -1;
+ }
if (statp != &_res) {
memset(statp, 0, sizeof(*statp));
}
+
return 0;
}
-
-extern __typeof(res_ninit) __res_ninit __attribute__((weak, alias("res_ninit")));
-extern __typeof(res_nclose) __res_nclose __attribute__((weak, alias("res_nclose")));
+weak_alias(__res_nclose, res_nclose);
diff --git a/libgcompat/resource.c b/libgcompat/resource.c
index 029b31e..836d5e2 100644
--- a/libgcompat/resource.c
+++ b/libgcompat/resource.c
@@ -1,8 +1,8 @@
-#include <sys/resource.h> /* setrlimit, struct rlimit */
-#include <assert.h> /* assert */
-#include <dlfcn.h> /* dlsym, RTLD_NEXT */
-#include <stdlib.h> /* NULL */
-#include <string.h> /* memcpy */
+#include <assert.h> /* assert */
+#include <dlfcn.h> /* dlsym, RTLD_NEXT */
+#include <stdlib.h> /* NULL */
+#include <string.h> /* memcpy */
+#include <sys/resource.h> /* setrlimit, struct rlimit */
/* Sigh.
* Valve compiled Steam against the glibc2.2 version of setrlimit.
@@ -11,24 +11,24 @@
* So, what you have to do is: if you want to run steam with this gcompat,
* ensure you compile *without* defining NO_BROKEN_SHADOW_SETRLIMIT.
* If you do *not* want to run steam with this gcompat, define it.
- *
+ *
* The only problem with enabling this all the time is that if a binary
* really does need a ulimit to be 0 for any reason (such as coredumps), it
* very obviously won't work here.
*/
#ifndef NO_BROKEN_SHADOW_SETRLIMIT
-int (*real_rlimit)(int, const struct rlimit *);
+static int (*real_rlimit)(int, const struct rlimit *);
int setrlimit(int resource, const struct rlimit *rlim)
{
struct rlimit my_rlim;
- real_rlimit = dlsym(RTLD_NEXT, "setrlimit");
- assert(real_rlimit != NULL);
+ if (real_rlimit == NULL) {
+ real_rlimit = dlsym(RTLD_NEXT, "setrlimit");
+ assert(real_rlimit);
+ }
memcpy(&my_rlim, rlim, sizeof(struct rlimit));
-
- if(my_rlim.rlim_cur == 0)
- {
+ if (my_rlim.rlim_cur == 0) {
my_rlim.rlim_cur = my_rlim.rlim_max;
}
diff --git a/libgcompat/signal.c b/libgcompat/signal.c
new file mode 100644
index 0000000..17e5443
--- /dev/null
+++ b/libgcompat/signal.c
@@ -0,0 +1,11 @@
+#include <signal.h> /* sigpause */
+
+/**
+ * Remove a signal from the signal mask and suspend the thread.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---xpg-sigpause.html
+ */
+int __xpg_sigpause(int sig)
+{
+ return sigpause(sig);
+}
diff --git a/libgcompat/socket.c b/libgcompat/socket.c
new file mode 100644
index 0000000..927ebe4
--- /dev/null
+++ b/libgcompat/socket.c
@@ -0,0 +1,31 @@
+#include <assert.h> /* assert */
+#include <stddef.h> /* NULL */
+#include <sys/socket.h> /* recv, recvfrom */
+
+/**
+ * Receive a message from a connected socket, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---recv-chk-1.html
+ */
+ssize_t __recv_chk(int fd, void *buf, size_t len, size_t buflen, int flags)
+{
+ assert(buf != NULL);
+ assert(buflen >= len);
+
+ return recv(fd, buf, len, flags);
+}
+
+/**
+ * Receive a message from a socket, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---recvfrom-chk-1.html
+ */
+ssize_t __recvfrom_chk(int fd, void *buf, size_t len, size_t buflen, int flags,
+ struct sockaddr *address, socklen_t *address_len)
+{
+ assert(buf != NULL);
+ assert(buflen >= len);
+ assert(address != NULL ? address_len != NULL : address_len == NULL);
+
+ return recvfrom(fd, buf, len, flags, address, address_len);
+}
diff --git a/libgcompat/stdio.c b/libgcompat/stdio.c
index 89bd677..0ff5feb 100644
--- a/libgcompat/stdio.c
+++ b/libgcompat/stdio.c
@@ -1,130 +1,236 @@
-#include <assert.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <wchar.h>
-
-size_t __fread_chk(void *ptr, size_t size, size_t nmemb, FILE *stream)
+#define _GNU_SOURCE /* fgets_unlocked */
+#include <assert.h> /* assert */
+#include <stdarg.h> /* va_list, va_start, va_end */
+#include <stddef.h> /* NULL, size_t */
+#include <stdio.h> /* feof, fgets, fread, puts, v*printf */
+
+int __vasprintf_chk(char **strp, int flag, const char *format, va_list ap);
+int __vfprintf_chk(FILE *stream, int flag, const char *format, va_list ap);
+int __vsnprintf_chk(char *s, size_t n, int flag, size_t slen,
+ const char *format, va_list ap);
+int __vsprintf_chk(char *s, int flag, size_t slen, const char *format,
+ va_list ap);
+
+/**
+ * Test end-of-file indicator on a stream.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib--io-feof-3.html
+ */
+int _IO_feof(FILE *stream)
{
- assert(ptr != NULL);
- assert(stream != NULL);
- return fread(ptr, size, nmemb, stream);
+ return feof(stream);
}
-int __printf_chk(int flag, const char *format, ...)
+/**
+ * Put a string on standard output.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib--io-puts-3.html
+ */
+int _IO_puts(const char *c)
{
- va_list argp;
- int result;
+ return puts(c);
+}
- if(flag > 0)
- {
- assert(format != NULL);
- }
+/**
+ * Print to allocated string, with stack checking.
+ */
+int __asprintf_chk(char **strp, int flag, const char *format, ...)
+{
+ int ret;
+ va_list ap;
- va_start(argp, format);
- result = vprintf(format, argp);
- va_end(argp);
+ va_start(ap, format);
+ ret = __vasprintf_chk(strp, flag, format, ap);
+ va_end(ap);
- return result;
+ return ret;
}
-int __fprintf_chk(FILE *stream, int flag, const char *format, ...)
+/**
+ * String input, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---fgets-chk-1.html
+ */
+char *__fgets_chk(char *s, size_t slen, int n, FILE *stream)
{
- va_list argp;
- int result;
+ assert(s != NULL);
+ assert(slen >= (size_t) n);
+ assert(n > 0);
+ assert(stream != NULL);
- if(flag > 0)
- {
- assert(stream != NULL);
- assert(format != NULL);
- }
+ return fgets(s, n, stream);
+}
- va_start(argp, format);
- result = vfprintf(stream, format, argp);
- va_end(argp);
+/**
+ * Non-locking string input, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---fgets-unlocked-chk-1.html
+ */
+char *__fgets_unlocked_chk(char *s, size_t slen, int n, FILE *stream)
+{
+ assert(s != NULL);
+ assert(slen >= (size_t) n);
+ assert(n > 0);
+ assert(stream != NULL);
- return result;
+ return fgets_unlocked(s, n, stream);
}
-int __sprintf_chk(char *str, int flag, size_t strlen, const char *format, ...)
+/**
+ * Convert formatted output, with stack checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---fprintf-chk-1.html
+ */
+int __fprintf_chk(FILE *stream, int flag, const char *format, ...)
{
- va_list argp;
- int result;
-
- assert(strlen > 0);
+ int ret;
+ va_list ap;
- va_start(argp, format);
- result = vsnprintf(str, strlen, format, argp);
- va_end(argp);
+ va_start(ap, format);
+ ret = __vfprintf_chk(stream, flag, format, ap);
+ va_end(ap);
- return result;
+ return ret;
}
-int __snprintf_chk(char *str, size_t size, int flag, size_t strlen, const char *format, ...)
+/**
+ * Binary input, with stack checking.
+ */
+size_t __fread_chk(void *buf, size_t buflen, size_t size, size_t nitems,
+ FILE *stream)
{
- va_list argp;
- int result;
+ assert(buf != NULL);
+ assert(size > 0);
+ assert(buflen / size >= nitems);
+ assert(stream != NULL);
- if(flag > 0)
- {
- assert(format != NULL);
- }
- // must always be done per LFS
- assert(size <= strlen);
+ return fread(buf, size, nitems, stream);
+}
- va_start(argp, format);
- result = vsnprintf(str, size, format, argp);
- va_end(argp);
+/**
+ * Format and print data, with stack checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---printf-chk-1.html
+ */
+int __printf_chk(int flag, const char *format, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, format);
+ ret = __vfprintf_chk(stdout, flag, format, ap);
+ va_end(ap);
- return result;
+ return ret;
}
-int __swprintf_chk(wchar_t *wcs, size_t maxlen, int flag, size_t wcslen, const wchar_t *format, ...)
+/**
+ * Convert formatted output, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---snprintf-chk-1.html
+ */
+int __snprintf_chk(char *s, size_t n, int flag, size_t slen, const char *format,
+ ...)
{
- va_list argp;
- int result;
+ int ret;
+ va_list ap;
- if(flag > 0)
- {
- assert(format != NULL);
- }
- // must always be done per LFS
- assert(maxlen <= wcslen);
+ va_start(ap, format);
+ ret = __vsnprintf_chk(s, n, flag, slen, format, ap);
+ va_end(ap);
- va_start(argp, format);
- result = vswprintf(wcs, maxlen, format, argp);
- va_end(argp);
+ return ret;
+}
- return result;
+/**
+ * Convert formatted output, with stack checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---sprintf-chk-1.html
+ */
+int __sprintf_chk(char *s, int flag, size_t slen, const char *format, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, format);
+ ret = __vsprintf_chk(s, flag, slen, format, ap);
+ va_end(ap);
+
+ return ret;
}
-int __vasprintf_chk(char **strp, int flag, const char *fmt, va_list ap)
+/**
+ * Print to allocated string, with stack checking.
+ */
+int __vasprintf_chk(char **strp, int flag, const char *format, va_list ap)
{
- if(flag > 0)
- {
- assert(strp != NULL);
- assert(fmt != NULL);
- }
- return vasprintf(strp, fmt, ap);
+ assert(strp != NULL);
+ assert(format != NULL);
+
+ return vasprintf(strp, format, ap);
}
+/**
+ * Convert formatted output, with stack checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---vfprintf-chk-1.html
+ */
int __vfprintf_chk(FILE *stream, int flag, const char *format, va_list ap)
{
- if(flag > 0)
- {
- assert(stream != NULL);
- assert(format != NULL);
- }
+ assert(stream != NULL);
+ assert(format != NULL);
+
return vfprintf(stream, format, ap);
}
-int __vsnprintf_chk(char *str, size_t size, int flag, size_t strlen, const char *format, va_list ap)
+/**
+ * Convert formatted output, with stack checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---vprintf-chk-1.html
+ */
+int __vprintf_chk(int flag, const char *format, va_list ap)
{
- if(flag > 0)
- {
- assert(format != NULL);
- }
- // must always be done per LFS
- assert(size <= strlen);
- return vsnprintf(str, size, format, ap);
+ return __vfprintf_chk(stdout, flag, format, ap);
}
+/**
+ * Convert formatted output, with stack checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---vsnprintf-chk-1.html
+ */
+int __vsnprintf_chk(char *s, size_t n, int flag, size_t slen,
+ const char *format, va_list ap)
+{
+ assert(s != NULL || n == 0);
+ assert(slen >= n);
+ assert(format != NULL);
+
+ return vsnprintf(s, n, format, ap);
+}
+
+/**
+ * Convert formatted output, with stack checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---vsprintf-chk-1.html
+ */
+int __vsprintf_chk(char *s, int flag, size_t slen, const char *format,
+ va_list ap)
+{
+ assert(s != NULL);
+ assert(slen > 0);
+ assert(format != NULL);
+
+ return vsprintf(s, format, ap);
+}
+
+/**
+ * Create a name for a temporary file.
+ */
+char *tmpnam_r(char *s)
+{
+ if (s == NULL) {
+ return NULL;
+ }
+
+ return tmpnam(s);
+}
diff --git a/libgcompat/stdlib.c b/libgcompat/stdlib.c
index 30c904e..a08f914 100644
--- a/libgcompat/stdlib.c
+++ b/libgcompat/stdlib.c
@@ -1,14 +1,126 @@
-#include <assert.h> // assert
-#include <stdlib.h> // strtod
+#include <assert.h> /* assert */
+#include <limits.h> /* PATH_MAX */
+#include <locale.h> /* locale_t */
+#include <stddef.h> /* NULL, size_t */
+#include <stdlib.h> /* getenv, realpath, strto* */
+#include <unistd.h> /* get*id */
-char *__realpath_chk(const char *path, char *resolved_path)
+/**
+ * Resolve a pathname, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---realpath-chk-1.html
+ */
+char *__realpath_chk(const char *path, char *resolved_path, size_t resolved_len)
{
assert(path != NULL);
+ assert(resolved_path != NULL);
+ assert(resolved_len >= PATH_MAX);
+
return realpath(path, resolved_path);
}
-double __strtod_internal(const char *__nptr, char **__endptr, int __group)
+/**
+ * Get an environment variable.
+ */
+char *__secure_getenv(const char *name)
+{
+ if (geteuid() != getuid() || getegid() != getgid()) {
+ return NULL;
+ }
+
+ return getenv(name);
+}
+
+/**
+ * Underlying function for strtod.
+ *
+ * "__group shall be 0 or the behavior of __strtod_internal() is undefined."
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---strtod-internal-1.html
+ */
+double __strtod_internal(const char *nptr, char **endptr, int group)
+{
+ assert(group == 0);
+
+ return strtod(nptr, endptr);
+}
+
+/**
+ * Underlying function for strtof.
+ *
+ * "__group shall be 0 or the behavior of __strtof_internal() is undefined."
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---strtof-internal.html
+ */
+float __strtof_internal(const char *nptr, char **endptr, int group)
+{
+ assert(group == 0);
+
+ return strtof(nptr, endptr);
+}
+
+/**
+ * Underlying function for strtol.
+ */
+long __strtol_internal(const char *nptr, char **endptr, int base, int group)
+{
+ assert(group == 0);
+
+ return strtol(nptr, endptr, base);
+}
+
+/**
+ * Underlying function for strtold.
+ *
+ * "__group shall be 0 or the behavior of __strtold_internal() is undefined."
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---strtold-internal-1.html
+ */
+long double __strtold_internal(const char *nptr, char **endptr, int group)
+{
+ assert(group == 0);
+
+ return strtold(nptr, endptr);
+}
+
+/**
+ * Convert string value to a long long integer.
+ *
+ * Some day, when musl supports LC_NUMERIC, we can probably remove this.
+ */
+long long int strtoll_l(const char *nptr, char **endptr, int base,
+ locale_t locale)
+{
+ return strtoll(nptr, endptr, base);
+}
+
+/**
+ * Convert string value to a long long integer.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-strtoq-3.html
+ */
+long long strtoq(const char *nptr, char **endptr, int base)
+{
+ return strtoll(nptr, endptr, base);
+}
+
+/**
+ * Convert a string to an unsigned long long.
+ *
+ * Some day, when musl supports LC_NUMERIC, we can probably remove this.
+ */
+unsigned long long int strtoull_l(const char *nptr, char **endptr, int base,
+ locale_t locale)
+{
+ return strtoull(nptr, endptr, base);
+}
+
+/**
+ * Convert a string to an unsigned long long.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-strtouq-3.html
+ */
+unsigned long long strtouq(const char *nptr, char **endptr, int base)
{
- assert(__group == 0);
- return strtod(__nptr, __endptr);
+ return strtoull(nptr, endptr, base);
}
diff --git a/libgcompat/string.c b/libgcompat/string.c
index 9045d5d..1d387a8 100644
--- a/libgcompat/string.c
+++ b/libgcompat/string.c
@@ -1,96 +1,247 @@
-#include <assert.h> /* assert */
-#include <string.h> /* memcpy, strcpy, strncat, strndup */
-#include <stdlib.h> /* strto[u?]ll */
+#define _GNU_SOURCE /* mempcpy */
+#include <assert.h> /* assert */
+#include <stddef.h> /* NULL, size_t */
+#include <stdint.h> /* SIZE_MAX */
+#include <string.h> /* memcpy, strcpy, strncat, strndup */
-/* "Checked" memcpy */
-void *__memcpy_chk(void *dest, const void *src, size_t len, size_t destlen)
+#include "alias.h" /* weak_alias */
+
+/**
+ * Copy bytes in memory, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---memcpy-chk-1.html
+ */
+void *__memcpy_chk(void *dest, const void *src, size_t n, size_t destlen)
{
assert(dest != NULL);
assert(src != NULL);
- assert(len <= destlen);
- if(src < dest)
- {
- assert(!(src + len >= dest));
+ assert(destlen >= n);
+ if (dest < src) {
+ assert((char *) dest + n <= (char *) src);
} else {
- assert(!(dest + len >= src));
+ assert((char *) src + n <= (char *) dest);
}
- return memcpy(dest, src, len);
+
+ return memcpy(dest, src, n);
}
-/* "Checked" strncat */
-char *__strncat_chk(char *dest, const char *src, size_t n, size_t destlen)
+/**
+ * Copy bytes in memory with overlapping areas, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---memmove-chk-1.html
+ */
+void *__memmove_chk(void *dest, const void *src, size_t n, size_t destlen)
{
assert(dest != NULL);
assert(src != NULL);
- assert(n <= destlen);
+ assert(destlen >= n);
- return strncat(dest, src, n);
+ return memmove(dest, src, n);
}
-/* "Checked" strcat */
-char *__strcat_chk(char *dest, const char *src, size_t destlen)
+/**
+ * Copy bytes in memory.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---mempcpy.html
+ */
+void *__mempcpy(void *dest, const void *src, size_t n)
{
- return strncat(dest, src, destlen - 1);
+ return mempcpy(dest, src, n);
}
-/* "Checked" strncpy */
-char *__strncpy_chk(char *dest, const char *src, size_t n, size_t destlen)
+/**
+ * Copy bytes in memory, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---mempcpy-chk-1.html
+ */
+void *__mempcpy_chk(void *dest, const void *src, size_t n, size_t destlen)
{
assert(dest != NULL);
assert(src != NULL);
- assert(strlen(src) < destlen);
+ assert(destlen >= n);
+ if (dest < src) {
+ assert((char *) dest + n <= (char *) src);
+ } else {
+ assert((char *) src + n <= (char *) dest);
+ }
- return strncpy(dest, src, n);
+ return mempcpy(dest, src, n);
}
-/* "Checked" strcpy */
-char *__strcpy_chk(char *dest, const char *src, size_t destlen)
+/**
+ * Set bytes in memory, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---memset-chk-1.html
+ */
+void *__memset_chk(void *s, int c, size_t n, size_t buflen)
+{
+ assert(s != NULL);
+ assert(buflen >= n);
+
+ return memset(s, c, n);
+}
+
+/**
+ * Find byte in memory.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---rawmemchr.html
+ */
+void *__rawmemchr(const void *s, int c)
+{
+ return memchr(s, c, SIZE_MAX);
+}
+weak_alias(__rawmemchr, rawmemchr);
+
+/**
+ * Copy a string and return a pointer to the end of the result, with buffer
+ * overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---stpcpy-chk-1.html
+ */
+char *__stpcpy_chk(char *dest, const char *src, size_t destlen)
{
+ size_t n = strlen(src) + 1;
+
assert(dest != NULL);
assert(src != NULL);
- assert(strlen(src) < destlen);
+ assert(destlen >= n);
+ if (dest < src) {
+ assert(dest + n <= src);
+ } else {
+ assert(src + n <= dest);
+ }
- return strcpy(dest, src);
+ return stpcpy(dest, src);
}
-/* Literally a useless __ alias. */
-char *__strndup(const char *str, size_t count)
+/**
+ * Copy a fixed-length string, returning a pointer to the array end, with buffer
+ * overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---stpncpy-chk-1.html
+ */
+char *__stpncpy_chk(char *dest, const char *src, size_t n, size_t destlen)
{
- return strndup(str, count);
+ assert(dest != NULL);
+ assert(src != NULL);
+ assert(destlen >= n);
+ if (dest < src) {
+ assert(dest + n <= src);
+ } else {
+ assert(src + n <= dest);
+ }
+
+ return stpncpy(dest, src, n);
}
-/* The existance of this method, and the fact it is used in real code, gives
- * me nightmares. */
-void *rawmemchr(const void *s, int c)
+/**
+ * Concatenate two strings, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---strcat-chk-1.html
+ */
+char *__strcat_chk(char *dest, const char *src, size_t destlen)
{
- const unsigned char *haystack = s;
- unsigned char needle = (unsigned char)c;
- while(*haystack++ != needle);
- return (void *)haystack;
+ size_t n = strlen(src) + 1;
+ size_t total = strnlen(dest, destlen) + n;
+
+ assert(dest != NULL);
+ assert(src != NULL);
+ assert(destlen >= total);
+ if (dest < src) {
+ assert(dest + total <= src);
+ } else {
+ assert(src + n <= dest);
+ }
+
+ return strcat(dest, src);
}
-extern __typeof(rawmemchr) __rawmemchr __attribute__((weak, alias("rawmemchr")));
+/**
+ * Copy a string, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---strcpy-chk-1.html
+ */
+char *__strcpy_chk(char *dest, const char *src, size_t destlen)
+{
+ size_t n = strlen(src) + 1;
+
+ assert(dest != NULL);
+ assert(src != NULL);
+ assert(destlen >= n);
+ if (dest < src) {
+ assert(dest + n <= src);
+ } else {
+ assert(src + n <= dest);
+ }
+
+ return strcpy(dest, src);
+}
+
+/**
+ * Concatenate a string with part of another, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---strncat-chk-1.html
+ */
+char *__strncat_chk(char *dest, const char *src, size_t n, size_t destlen)
+{
+ size_t total = strnlen(dest, destlen) + n + 1;
+
+ assert(dest != NULL);
+ assert(src != NULL);
+ assert(destlen >= total);
+ if (dest < src) {
+ assert(dest + total <= src);
+ } else {
+ assert(src + n <= dest);
+ }
+
+ return strncat(dest, src, n);
+}
-/* Another useless __ alias */
-char *__strtok_r(char *str, const char *delim, char **saveptr)
+/**
+ * Copy a fixed-length string, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---strncpy-chk-1.html
+ */
+char *__strncpy_chk(char *dest, const char *src, size_t n, size_t destlen)
{
- return strtok_r(str, delim, saveptr);
+ assert(dest != NULL);
+ assert(src != NULL);
+ assert(destlen >= n);
+ if (dest < src) {
+ assert(dest + n <= src);
+ } else {
+ assert(src + n <= dest);
+ }
+
+ return strncpy(dest, src, n);
}
-/* The "global" definition of strsep in glibc, used when architecture dependent
- * assembler versions aren't good enough. */
-char *__strsep_g(char **stringp, const char *delim)
+/**
+ * Duplicate a specific number of bytes from a string.
+ */
+char *__strndup(const char *s, size_t size)
{
- return strsep(stringp, delim);
+ return strndup(s, size);
}
-/* Some day, when musl supports LC_NUMERIC, we can probably remove these */
-long long int strtoll_l(const char *nptr, char **endptr, int base, locale_t locale)
+/**
+ * Extract token from string.
+ *
+ * The "global" definition of strsep in glibc, used when architecture dependent
+ * assembler versions aren't good enough.
+ */
+char *__strsep_g(char **stringp, const char *delim)
{
- return strtoll(nptr, endptr, base);
+ return strsep(stringp, delim);
}
-unsigned long long int strtoull_l(const char *nptr, char **endptr, int base, locale_t locale)
+/**
+ * Split string into tokens.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---strtok-r-1.html
+ */
+char *__strtok_r(char *s, const char *delim, char **save_ptr)
{
- return strtoull(nptr, endptr, base);
+ return strtok_r(s, delim, save_ptr);
}
diff --git a/libgcompat/sysctl.c b/libgcompat/sysctl.c
index a14c2b8..e7218c1 100644
--- a/libgcompat/sysctl.c
+++ b/libgcompat/sysctl.c
@@ -1,6 +1,5 @@
-#include <unistd.h> /* size_t */
#include <sys/syscall.h>
-
+#include <unistd.h> /* size_t */
#ifdef SYS__sysctl
@@ -13,16 +12,15 @@ struct __sysctl_args {
size_t newlen;
};
-
-int sysctl (int *name, int nlen, void *oldval, size_t *oldlenp, void *newval, size_t newlen) {
- struct __sysctl_args args = {
- .name = name,
- .nlen = nlen,
- .oldval = oldval,
- .oldlenp = oldlenp,
- .newval = newval,
- .newlen = newlen
- };
+int sysctl(int *name, int nlen, void *oldval, size_t *oldlenp, void *newval,
+ size_t newlen)
+{
+ struct __sysctl_args args = { .name = name,
+ .nlen = nlen,
+ .oldval = oldval,
+ .oldlenp = oldlenp,
+ .newval = newval,
+ .newlen = newlen };
return syscall(SYS__sysctl, &args);
}
diff --git a/libgcompat/syslog.c b/libgcompat/syslog.c
new file mode 100644
index 0000000..5409eba
--- /dev/null
+++ b/libgcompat/syslog.c
@@ -0,0 +1,32 @@
+#include <assert.h> /* assert */
+#include <stdarg.h> /* va_list, va_start, va_end */
+#include <stddef.h> /* NULL */
+#include <syslog.h> /* vsyslog */
+
+void __vsyslog_chk(int priority, int flag, const char *format, va_list ap);
+
+/**
+ * Log a message, with stack checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---syslog-chk-1.html
+ */
+void __syslog_chk(int priority, int flag, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ __vsyslog_chk(priority, flag, format, ap);
+ va_end(ap);
+}
+
+/**
+ * Log a message, with stack checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---vsyslog-chk-1.html
+ */
+void __vsyslog_chk(int priority, int flag, const char *format, va_list ap)
+{
+ assert(format != NULL);
+
+ vsyslog(priority, format, ap);
+}
diff --git a/libgcompat/ucontext.c b/libgcompat/ucontext.c
index 2d6006e..4827263 100644
--- a/libgcompat/ucontext.c
+++ b/libgcompat/ucontext.c
@@ -1,4 +1,4 @@
-#include <errno.h> /* errno, ENOSYS */
+#include <errno.h> /* errno, ENOSYS */
int getcontext(void *ucp)
{
diff --git a/libgcompat/unistd.c b/libgcompat/unistd.c
new file mode 100644
index 0000000..e19ec8e
--- /dev/null
+++ b/libgcompat/unistd.c
@@ -0,0 +1,172 @@
+#include <assert.h> /* assert */
+#include <limits.h> /* NGROUPS_MAX */
+#include <stddef.h> /* NULL, size_t */
+#include <unistd.h> /* confstr, getcwd, getgroups, ... */
+
+#include "alias.h" /* alias */
+
+/**
+ * Get configurable variables, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---confstr-chk-1.html
+ */
+size_t __confstr_chk(int name, char *buf, size_t len, size_t buflen)
+{
+ assert(buf != NULL ? buflen >= len : len == 0);
+
+ return confstr(name, buf, len);
+}
+
+/**
+ * Get the pathname of the current working directory, with buffer overflow
+ * checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---getcwd-chk-1.html
+ */
+char *__getcwd_chk(char *buf, size_t len, size_t buflen)
+{
+ assert(buf != NULL);
+ assert(buflen >= len);
+
+ return getcwd(buf, len);
+}
+
+/**
+ * Get supplementary group IDs, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---getgroups-chk-1.html
+ */
+int __getgroups_chk(int gidsetsize, gid_t *grouplist, size_t listlen)
+{
+ assert(grouplist != NULL);
+ assert(listlen / sizeof(*grouplist) >= (size_t) gidsetsize);
+
+ return getgroups(gidsetsize, grouplist);
+}
+
+/**
+ * Get name of current host, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---gethostname-chk-1.html
+ */
+int __gethostname_chk(char *name, size_t namelen, size_t buflen)
+{
+ assert(name != NULL);
+ assert(buflen >= namelen);
+
+ return gethostname(name, namelen);
+}
+
+/**
+ * Get login name, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---getlogin-r-chk-1.html
+ */
+int __getlogin_r_chk(char *name, size_t namelen, size_t buflen)
+{
+ assert(name != NULL);
+ assert(buflen >= namelen);
+
+ return getlogin_r(name, namelen);
+}
+
+/**
+ * Get memory page size.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---getpagesize.html
+ */
+int __getpagesize(void)
+{
+ return getpagesize();
+}
+
+/**
+ * Get the process group ID for a process.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---getpgid-1.html
+ */
+pid_t __getpgid(pid_t pid)
+{
+ return getpgid(pid);
+}
+
+/**
+ * Read from a file, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---pread-chk-1.html
+ */
+ssize_t __pread_chk(int fd, void *buf, size_t nbytes, off_t offset,
+ size_t buflen)
+{
+ assert(buf != NULL);
+ assert(buflen >= nbytes);
+
+ return pread(fd, buf, nbytes, offset);
+}
+alias(__pread_chk, __pread64_chk);
+
+/**
+ * Read from a file, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---read-chk-1.html
+ */
+ssize_t __read_chk(int fd, void *buf, size_t nbytes, size_t buflen)
+{
+ assert(buf != NULL);
+ assert(buflen >= nbytes);
+
+ return read(fd, buf, nbytes);
+}
+
+/**
+ * Read the contents of a symbolic link, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---readlink-chk-1.html
+ */
+ssize_t __readlink_chk(const char *path, char *buf, size_t len, size_t buflen)
+{
+ assert(buf != NULL);
+ assert(buflen >= len);
+
+ return readlink(path, buf, len);
+}
+
+/**
+ * Get configurable system variables.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---sysconf.html
+ */
+long __sysconf(int name)
+{
+ return sysconf(name);
+}
+
+/**
+ * Find the pathname of a terminal, with buffer overflow checking.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---ttyname-r-chk-1.html
+ */
+int __ttyname_r_chk(int fd, char *name, size_t namelen, size_t buflen)
+{
+ assert(name != NULL);
+ assert(buflen >= namelen);
+
+ return ttyname_r(fd, name, namelen);
+}
+
+/**
+ * Test whether a process is in a group.
+ */
+int group_member(gid_t gid)
+{
+ gid_t groups[NGROUPS_MAX];
+ int ngroups = getgroups(NGROUPS_MAX, groups);
+
+ for (int i = 0; i < ngroups; ++i) {
+ if (groups[i] == gid) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/libgcompat/utmp.c b/libgcompat/utmp.c
new file mode 100644
index 0000000..c2bdd91
--- /dev/null
+++ b/libgcompat/utmp.c
@@ -0,0 +1,14 @@
+#include <stddef.h> /* NULL */
+#include <utmp.h> /* struct utmp */
+
+/**
+ * Get user accounting database entries.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-getutent-r-3.html
+ */
+int getutent_r(struct utmp *buffer, struct utmp **result)
+{
+ /* musl does not implement getutent(). */
+ *result = NULL;
+ return -1;
+}
diff --git a/libgcompat/version.c b/libgcompat/version.c
index 63146f8..1b7687c 100644
--- a/libgcompat/version.c
+++ b/libgcompat/version.c
@@ -1,14 +1,28 @@
-#include <stdlib.h> // getenv
+#include <stddef.h> /* NULL */
+#include <stdlib.h> /* getenv */
+/**
+ * Get glibc release.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-gnu-get-libc-version-1.html
+ */
+const char *gnu_get_libc_release(void)
+{
+ return "stable";
+}
+
+/**
+ * Get glibc version.
+ *
+ * LSB 5.0: LSB-Core-generic/baselib-gnu-get-libc-version-1.html
+ */
const char *gnu_get_libc_version(void)
{
char *ver = getenv("GLIBC_FAKE_VERSION");
- if(ver == NULL) ver = "2.8";
- return ver;
-}
+ if (ver == NULL) {
+ ver = "2.8";
+ }
-const char *gnu_get_libc_release(void)
-{
- return "stable";
+ return ver;
}
diff --git a/libgcompat/wchar.c b/libgcompat/wchar.c
new file mode 100644
index 0000000..1ddaa1f
--- /dev/null
+++ b/libgcompat/wchar.c
@@ -0,0 +1,40 @@
+#include <assert.h> /* assert */
+#include <stdarg.h> /* va_list, va_start, va_end */
+#include <stddef.h> /* size_t */
+#include <wchar.h> /* wchar_t, *wprintf */
+
+int __vswprintf_chk(wchar_t *s, size_t n, int flag, size_t slen,
+ const wchar_t *format, va_list ap);
+
+/**
+ * Convert formatted wide-character output, with stack checking
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---swprintf-chk-1.html
+ */
+int __swprintf_chk(wchar_t *s, size_t n, int flag, size_t slen,
+ const wchar_t *format, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, format);
+ ret = __vswprintf_chk(s, n, flag, slen, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+/**
+ * Convert formatted wide-character output, with stack checking
+ *
+ * LSB 5.0: LSB-Core-generic/baselib---vswprintf-chk-1.html
+ */
+int __vswprintf_chk(wchar_t *s, size_t n, int flag, size_t slen,
+ const wchar_t *format, va_list ap)
+{
+ assert(s != NULL || n == 0);
+ assert(slen >= n);
+ assert(format != NULL);
+
+ return vswprintf(s, n, format, ap);
+}
diff --git a/loader/loader.c b/loader/loader.c
index f2942a4..53a2c38 100644
--- a/loader/loader.c
+++ b/loader/loader.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2017 William Pitcock <nenolod@dereferenced.org>
+ * Copyright (c) 2018 Samuel Holland <samuel@sholland.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -10,42 +11,99 @@
* from the use of this software.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
+#include <limits.h> /* PATH_MAX */
+#include <stddef.h> /* NULL */
+#include <stdio.h> /* fputs, fwrite, stderr */
+#include <stdlib.h> /* calloc, EXIT_FAILURE, getenv */
+#include <string.h> /* strcpy, strlcpy */
+#include <unistd.h> /* execve, ssize_t */
#ifndef PATH_MAX
-#define PATH_MAX 16384
+#define PATH_MAX 16384
+#endif
+
+#ifndef LIBGCOMPAT
+#error LIBGCOMPAT must be defined
#endif
#ifndef LINKER
#error LINKER must be defined
#endif
-#ifndef LIBGCOMPAT
-#error LIBGCOMPAT must be defined
+#ifndef LOADER
+#error LOADER must be defined
#endif
-int main(int argc, char *argv[], char *envp[]) {
- size_t i = 0;
+/*
+ * Given the argv { "foo", "bar", "baz" }, and the environment variable
+ * "LD_PRELOAD=/another/preload.so", the new argv will be {
+ * "ld-linux-$ARCH.so.N",
+ * "--argv0",
+ * "foo",
+ * "--preload",
+ * "/path/to/libgcompat.so /another/preload.so",
+ * "/path/to/foo",
+ * "bar",
+ * "baz"
+ * }.
+ *
+ * NOTE: The new argv needs 6 more entries than the original argc: five for
+ * arguments added at the beginning, and one for the NULL sentinel at the end
+ * not included in argc. Because calloc() is used, the sentinel is not
+ * explicitly set on its own, but *it is still there*!
+ *
+ * NOTE: The name given in argv[0] to musl *must* match glibc's ld.so filename.
+ * Many glibc-linked programs have an explicit dependency on its ld.so, but the
+ * file placed at that path (this loader) is not a real shared library. If musl
+ * tries to load this file as a shared library, it will fail. Thankfully, since
+ * musl treats argv[0] as "its" name when run directly, it considers a library
+ * by that name to already be loaded, and ignores the problematic dependency.
+ *
+ * NOTE: LD_PRELOAD entries are processed in order, and the environment variable
+ * is ignored if the parameter is given. In case the glibc-linked program needs
+ * another preloaded library, it should be appended to the argument so it gets
+ * loaded after libgcompat. Leave the environment variable as is, so if the
+ * program runs other glibc-linked programs, the same transformation gets
+ * applied there as well.
+ *
+ * NOTE: In order for the program to be able to re-exec itself it needs the
+ * fully-resolved path from /proc/self/exe ("target" below). This path is given
+ * to musl on the command line in new_argv[5], but it is not directly available
+ * to the program. An intercepted readlink("/proc/self/exe") must read it from
+ * /proc/self/cmdline. Thus, if its position in new_argv changes, that function
+ * must also be updated to match.
+ */
+int main(int argc, char *argv[], char *envp[])
+{
+ char **new_argv = calloc(argc + 6, sizeof(char *));
+ char preload[PATH_MAX];
char target[PATH_MAX];
- char **new_argv = calloc(sizeof(char *), argc + 5);
+ ssize_t i, j, len;
- memset(target, 0, sizeof(target));
- if (readlink("/proc/self/exe", target, sizeof(target)) < 0) {
+ strcpy(preload, LIBGCOMPAT " ");
+ if (getenv("LD_PRELOAD") != NULL) {
+ len = strlcat(preload, getenv("LD_PRELOAD"), sizeof(preload));
+ if ((size_t) len >= sizeof(preload)) {
+ fputs("too many preloaded libraries", stderr);
+ return EXIT_FAILURE;
+ }
+ }
+
+ len = readlink("/proc/self/exe", target, sizeof(target));
+ if (len < 0 || len == sizeof(target)) {
perror("readlink");
return EXIT_FAILURE;
}
+ target[len] = '\0';
- new_argv[0] = argv[0];
- new_argv[1] = "--preload";
- new_argv[2] = LIBGCOMPAT;
- new_argv[3] = target;
-
- for (i = 1; i < argc; i++) {
- new_argv[3 + i] = argv[i];
+ new_argv[0] = LOADER;
+ new_argv[1] = "--argv0";
+ new_argv[2] = argv[0];
+ new_argv[3] = "--preload";
+ new_argv[4] = preload;
+ new_argv[5] = target;
+ for (i = 6, j = 1; j < argc; ++i, ++j) {
+ new_argv[i] = argv[j];
}
execve(LINKER, new_argv, envp);