diff options
author | A. Wilcox <awilcox@wilcox-tech.com> | 2018-02-01 22:05:36 +0000 |
---|---|---|
committer | A. Wilcox <awilcox@wilcox-tech.com> | 2018-02-01 22:05:36 +0000 |
commit | 88815225fdabe7fa862f1c07abfcc971fdf5b04e (patch) | |
tree | 7aaae7c04fcf06aea85505fc028f476a131b9c50 | |
parent | c545f7393f0a10c182c2c085a36d7099b9c07a7f (diff) | |
parent | 78722ef403ffadcb78917e93e1859d43b9bf9df5 (diff) | |
download | gcompat-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
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 +... @@ -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); |