aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 01:01:29 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 01:01:29 +0000
commit531ce5205104086bc5a9644c9ff4af56e48e99a4 (patch)
tree28523674f3e6fad32dbec47e22b3ba1d223b80eb
parent0790ccba6a82147e2fadcc4b94996475512d817a (diff)
parent23e8e5196456b787d5e78c4744b76946a8535732 (diff)
downloadmusl-android14-mainline-wifi-release.tar.gz
Change-Id: I19ce127f5adb3e5f155a3e42408b1a1fd97e2edb
-rw-r--r--Android.bp233
-rw-r--r--android/include/signal.h34
-rw-r--r--android/relinterp.c930
-rw-r--r--android/relinterp.script4
-rw-r--r--arch/arm/syscall_arch.h7
-rwxr-xr-xconfigure5
-rw-r--r--include/elf.h8
-rw-r--r--include/signal.h1
-rw-r--r--include/strings.h1
-rw-r--r--include/sys/sysinfo.h2
-rw-r--r--include/unistd.h4
-rw-r--r--ldso/dlstart.c15
-rw-r--r--ldso/dynlink.c141
-rw-r--r--sources.bp3
-rw-r--r--src/conf/confstr.c2
-rw-r--r--src/conf/sysconf.c13
-rw-r--r--src/include/sys/stat.h9
-rw-r--r--src/internal/dynlink.h2
-rw-r--r--src/internal/syscall.h26
-rw-r--r--src/linux/epoll.c1
-rw-r--r--src/linux/membarrier.c2
-rw-r--r--src/misc/getopt.c4
-rw-r--r--src/misc/getopt_long.c21
-rw-r--r--src/misc/getrlimit.c6
-rw-r--r--src/misc/mntent.c12
-rw-r--r--src/misc/setrlimit.c6
-rw-r--r--src/network/getaddrinfo.c2
-rw-r--r--src/network/gethostbyaddr_r.c5
-rw-r--r--src/network/gethostbyname2_r.c3
-rw-r--r--src/network/lookup_name.c5
-rw-r--r--src/network/netlink.h2
-rw-r--r--src/network/res_mkquery.c1
-rw-r--r--src/network/res_msend.c8
-rw-r--r--src/process/aarch64/vfork.s9
-rw-r--r--src/stat/fchmodat.c15
-rw-r--r--src/stat/fstat.c6
-rw-r--r--src/stat/fstatat.c14
-rw-r--r--src/stdio/freopen.c2
-rw-r--r--src/stdio/open_wmemstream.c6
-rw-r--r--src/stdio/tempnam.c8
-rw-r--r--src/stdio/tmpnam.c8
-rw-r--r--src/stdlib/qsort_nr.c2
-rw-r--r--src/temp/__randname.c3
-rw-r--r--src/thread/pthread_cancel.c2
-rw-r--r--src/thread/pthread_sigqueue.c22
-rw-r--r--src/thread/synccall.c2
-rw-r--r--src/time/__map_file.c5
-rw-r--r--src/time/clock_getcpuclockid.c1
-rw-r--r--src/time/clock_gettime.c7
-rw-r--r--src/time/timer_create.c6
50 files changed, 1357 insertions, 279 deletions
diff --git a/Android.bp b/Android.bp
index d6d7c642..1627f0f6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -78,7 +78,7 @@ cc_library_headers {
}
cc_defaults {
- name: "libc_musl_defaults",
+ name: "libc_musl_base_defaults",
host_supported: true,
device_supported: false,
system_shared_libs: [],
@@ -86,12 +86,10 @@ cc_defaults {
// CFLAGS_C99FSE
"-nostdinc",
"-ffreestanding",
- "-frounding-math",
"-Wa,--noexecstack",
// CFLAGS_AUTO
- //"-Os",
- "-O0",
+ "-Os",
"-pipe",
"-fomit-frame-pointer",
"-fno-unwind-tables",
@@ -138,6 +136,21 @@ cc_defaults {
"-Wno-unused-parameter",
],
+ arch: {
+ x86: {
+ cflags: ["-frounding-math"],
+ },
+ x86_64: {
+ cflags: ["-frounding-math"],
+ },
+ arm: {
+ cflags: ["-Wno-ignored-pragmas"],
+ },
+ arm64: {
+ cflags: ["-Wno-ignored-pragmas"],
+ },
+ },
+
ldflags: [
"-Wl,--sort-section,alignment",
"-Wl,--sort-common",
@@ -157,15 +170,6 @@ cc_defaults {
asflags: ["-Wno-unused-command-line-argument"],
- header_libs: [
- // The order here is very important, private headers like src/include/features.h override
- // public headers like include/features.h, and arch headers like arch/x86_64/ksigaction.h
- // override private headers like src/internal/ksigaction.h.
- "libc_musl_arch_headers",
- "libc_musl_private_headers",
- "libc_musl_public_headers",
- ],
-
stl: "none",
c_std: "c99",
sanitize: {
@@ -184,6 +188,19 @@ cc_defaults {
},
}
+cc_defaults {
+ name: "libc_musl_defaults",
+ defaults: ["libc_musl_base_defaults"],
+ header_libs: [
+ // The order here is very important, private headers like src/include/features.h override
+ // public headers like include/features.h, and arch headers like arch/x86_64/ksigaction.h
+ // override private headers like src/internal/ksigaction.h.
+ "libc_musl_arch_headers",
+ "libc_musl_private_headers",
+ "libc_musl_public_headers",
+ ],
+}
+
cc_library_headers {
name: "libc_musl_headers",
visibility: ["//bionic/libc"],
@@ -219,6 +236,14 @@ cc_library {
"-nostdlib",
],
dynamic_list: "dynamic.list",
+ export_header_lib_headers: [
+ "libc_musl_arch_headers",
+ "libc_musl_public_headers",
+ "libc_llndk_headers",
+ ],
+ header_libs: [
+ "libc_llndk_headers",
+ ],
}
// All the static parts of the main musl libc. Don't use this directly, use
@@ -307,19 +332,6 @@ cc_library_static {
],
}
-// Musl sources for the dynamic linker when used in the sysroot.
-cc_library_static {
- name: "libc_musl_ldso_sysroot",
- defaults: [
- "libc_musl_defaults",
- "libc_musl_ldso_sources",
- ],
- cflags: [
- "-fno-stack-protector",
- "-DLIBC_SONAME=libc.so",
- ],
-}
-
// An attempt to compile the dynamic linker as a standalone library separate from libc_musl.so.
// Not used yet.
cc_library_shared {
@@ -339,65 +351,55 @@ cc_library_shared {
],
}
-// Convert the linker (which is actually libc_musl.so) into a .s file for embedding in crtbegin.
-cc_genrule {
- name: "musl_linker_asm",
- host_supported: true,
- device_supported: false,
- tools: ["extract_linker"],
- cmd: "$(location) -s $(out) $(in)",
- srcs: [":libc_musl"],
- out: ["linker.s"],
- target: {
- darwin: {
- enabled: false,
- },
- bionic: {
- enabled: false,
- },
- glibc: {
- enabled: false,
- },
- },
-}
-
-// Convert the linker (which is actually libc_musl.so) into a linker script for embedding in
-// crtbegin.
-cc_genrule {
- name: "musl_linker_script",
- visibility: ["//visibility:public"],
- host_supported: true,
- device_supported: false,
- tools: ["extract_linker"],
- cmd: "$(location) -T $(out) $(in)",
- srcs: [":libc_musl"],
- out: ["linker.script"],
- target: {
- darwin: {
- enabled: false,
- },
- bionic: {
- enabled: false,
- },
- glibc: {
- enabled: false,
- },
- },
-}
-
//
// The musl CRT objects
//
cc_defaults {
name: "libc_musl_crt_defaults",
- defaults: ["libc_musl_defaults"],
+ defaults: ["libc_musl_base_defaults"],
cflags: [
// These are required to make sure the C code in crt/*.c
// doesn't have any dependencies on libc.
"-fno-stack-protector",
"-ftrivial-auto-var-init=uninitialized",
],
+ ldflags: [
+ "-Wl,--no-gc-sections",
+ ],
+
+ // The headers below are the same as the header_libs in
+ // libc_musl_defaults, but bazel considers the crt depending
+ // on a header lib to be a circular dependency (toolchain ->
+ // crt objects -> header lib -> toolchain).
+ // TODO(b/263407827): go back to using header_libs once bazel
+ // supports it without introducing a dependency on the toolchain.
+ arch: {
+ arm: {
+ local_include_dirs: ["arch/arm"],
+ },
+ arm64: {
+ local_include_dirs: ["arch/aarch64"],
+ },
+ x86: {
+ local_include_dirs: ["arch/i386"],
+ },
+ x86_64: {
+ local_include_dirs: ["arch/x86_64"],
+ },
+ },
+ generated_headers: [
+ "libc_musl_alltypes.h",
+ "libc_musl_syscall.h",
+ "libc_musl_version.h",
+ ],
+ local_include_dirs: [
+ "arch/generic",
+ "src/include",
+ "src/internal",
+ "android/include",
+ "include",
+ ],
}
cc_object {
@@ -447,28 +449,21 @@ cc_object {
cc_object {
name: "libc_musl_crtbegin_dynamic",
- defaults: ["libc_musl_defaults"],
+ defaults: ["libc_musl_crt_defaults"],
visibility: ["//visibility:public"],
objs: [
- "libc_musl_crt1",
"libc_musl_crti",
"clang_rt.crtbegin",
],
srcs: [
- ":musl_linker_asm",
- "android/ldso_trampoline.cpp",
- ],
- cflags: [
- // These are required to make sure the C code in ldso_trampoline.c
- // doesn't have any dependencies on libc.
- "-fno-stack-protector",
- "-ftrivial-auto-var-init=uninitialized",
+ "android/relinterp.c",
],
+ cflags: ["-DLOADER_PATH=\"libc_musl.so\""],
}
cc_object {
name: "libc_musl_crtbegin_static",
- defaults: ["libc_musl_defaults"],
+ defaults: ["libc_musl_crt_defaults"],
visibility: ["//visibility:public"],
objs: [
"libc_musl_Scrt1",
@@ -479,7 +474,7 @@ cc_object {
cc_object {
name: "libc_musl_crtend",
- defaults: ["libc_musl_defaults"],
+ defaults: ["libc_musl_crt_defaults"],
visibility: ["//visibility:public"],
objs: [
"clang_rt.crtend",
@@ -489,7 +484,7 @@ cc_object {
cc_object {
name: "libc_musl_crtbegin_so",
- defaults: ["libc_musl_defaults"],
+ defaults: ["libc_musl_crt_defaults"],
visibility: ["//visibility:public"],
objs: [
"libc_musl_crti",
@@ -499,7 +494,7 @@ cc_object {
cc_object {
name: "libc_musl_crtend_so",
- defaults: ["libc_musl_defaults"],
+ defaults: ["libc_musl_crt_defaults"],
visibility: ["//visibility:public"],
objs: [
"clang_rt.crtend",
@@ -554,7 +549,7 @@ cc_genrule {
},
},
tool_files: ["tools/mkalltypes.sed"],
- out: ["bits/alltypes.h"],
+ out: ["bits/arch/alltypes.h"],
cmd: "sed -f $(location tools/mkalltypes.sed) $(in) > $(out)",
}
@@ -602,22 +597,6 @@ cc_genrule {
//
-// A copy of libc_musl that uses libc.so as its soname for putting in the sysroot.
-cc_library_shared {
- name: "libc_musl_for_sysroot",
- defaults: ["libc_musl_defaults"],
- whole_static_libs: ["libc_musl_static"],
- shared: {
- whole_static_libs: ["libc_musl_ldso_sysroot"],
- },
- ldflags: [
- "-Wl,-e,_dlstart",
- "-nostdlib",
- "-Wl,--soname,libc.so",
- ],
- dynamic_list: "dynamic.list",
-}
-
// An empty static library that will be copied to libdl.a, etc. in the sysroot.
// Shouldn't be used by anything else besides the sysroot cc_genrule.
cc_library_static {
@@ -631,17 +610,11 @@ cc_library_static {
// crt.
cc_object {
name: "libc_musl_linker_object",
- defaults: ["libc_musl_defaults"],
+ defaults: ["libc_musl_crt_defaults"],
srcs: [
- ":musl_linker_asm",
- "android/ldso_trampoline.cpp",
- ],
- cflags: [
- // These are required to make sure the C code in ldso_trampoline.c
- // doesn't have any dependencies on libc.
- "-fno-stack-protector",
- "-ftrivial-auto-var-init=uninitialized",
+ "android/relinterp.c",
],
+ cflags: ["-DLOADER_PATH=\"libc_musl.so\""],
}
// The architecture-specific bits have to be handled separately because the label varies based
@@ -687,6 +660,11 @@ genrule {
"chmod a+x $(out)",
}
+cc_library_host_static {
+ name: "libz_static_for_sysroot",
+ whole_static_libs: ["libz"],
+}
+
cc_genrule {
name: "libc_musl_sysroot",
host_supported: true,
@@ -716,14 +694,15 @@ cc_genrule {
// libc++ headers
":libc_musl_sysroot_libc++_headers",
":libc_musl_sysroot_libc++abi_headers",
+ ":libc_musl_sysroot_zlib_headers",
// Libraries
":libc_musl",
- ":libc_musl_for_sysroot",
":libc_musl_static",
":libc++abi",
":libc++",
":libc++_static",
+ ":libz_static_for_sysroot",
// Objects
":libc_musl_crti",
@@ -736,7 +715,7 @@ cc_genrule {
// Embedded linker objects and linker scripts
":libc_musl_linker_object",
- ":musl_linker_script",
+ "android/relinterp.script",
// Wrapper scripts
":libc_musl_clang_wrapper",
@@ -755,7 +734,7 @@ cc_genrule {
"bits=($(locations arch/generic/bits/*.h)) && " +
"android_bits=($(locations android/include/bits/*.h)) && " +
"ln -s libc_musl.so $(genDir)/ld-musl.so.1 && " +
- "echo -e 'GROUP ( Scrt1-real.o libc_musl_linker_object.o )\nINCLUDE linker.script' > $(genDir)/Scrt1.ld && " +
+ "echo -e 'GROUP ( libc_musl_linker_object.o )\nINCLUDE relinterp.script' > $(genDir)/Scrt1.ld && " +
"echo -e 'GROUP ( libc_musl.so )' > $(genDir)/libc.so && " +
"$(location soong_zip) -o $(genDir)/sysroot.zip " +
" -j " +
@@ -772,14 +751,9 @@ cc_genrule {
" -D $$(dirname $${android_bits[0]}) " +
" -P include/sys -j " +
" -f $(location android/include/sys/cdefs.h) " +
- // crt objects
- " -P lib -j " +
- " -f $(location :libc_musl_crti) " +
- " -f $(location :libc_musl_crtn) " +
- " -f $(location :libc_musl_crt1) " +
- " -f $(location :libc_musl_rcrt1) " +
// embedded linker crt objects
- " -f $(location :musl_linker_script) " +
+ " -P lib -j " +
+ " -f $(location android/relinterp.script) " +
" -f $(location :libc_musl_linker_object) " +
// libs
" -f $(location :libc_musl) " +
@@ -798,10 +772,15 @@ cc_genrule {
" -f $(location :libc_musl_static) " +
" -f $(location :libc_musl_sysroot_static_empty) " +
" -f $(genDir)/Scrt1.ld " +
+ " -f $(location :libc_musl_crti) " +
+ " -f $(location :libc_musl_crtn) " +
+ " -f $(location :libc_musl_crt1) " +
+ " -f $(location :libc_musl_rcrt1) " +
" -f $(location :libc_musl_Scrt1) " +
" -f $(location :libc++_static) " +
" -f $(location :clang_rt.crtbegin) " +
" -f $(location :clang_rt.crtend) " +
+ " -f $(location :libz_static_for_sysroot) " +
" && " +
"$(location zip2zip) -i $(genDir)/libs.zip -o $(genDir)/libs_renamed.zip " +
// rename libs from module names to desired names in sysroot
@@ -825,11 +804,19 @@ cc_genrule {
" lib/clang_rt.crtbegin.o:lib/crtbeginT.o " +
" lib/clang_rt.crtend.o:lib/crtend.o " +
" lib/clang_rt.crtend.o:lib/crtendS.o " +
+ // rename crt objects
+ " lib/libc_musl_crti.o:lib/crti.o " +
+ " lib/libc_musl_crtn.o:lib/crtn.o " +
+ " lib/libc_musl_crt1.o:lib/crt1.o " +
+ " lib/libc_musl_rcrt1.o:lib/rcrt1.o " +
+ // rename static libz
+ " lib/libz_static_for_sysroot.a:lib/libz.a " +
" && " +
"$(location merge_zips) -ignore-duplicates $(out) " +
" $(location :libc_musl_sysroot_bionic_headers) " +
" $(location :libc_musl_sysroot_libc++_headers) " +
" $(location :libc_musl_sysroot_libc++abi_headers) " +
+ " $(location :libc_musl_sysroot_zlib_headers) " +
" $(location :libc_musl_sysroot_bits) " +
" $(genDir)/sysroot.zip " +
" $(genDir)/libs_renamed.zip",
diff --git a/android/include/signal.h b/android/include/signal.h
deleted file mode 100644
index 0d7d03c7..00000000
--- a/android/include/signal.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#pragma once
-
-#include_next <signal.h>
-
-#define __SIGRTMIN 32
-#define __SIGRTMAX _NSIG-1
diff --git a/android/relinterp.c b/android/relinterp.c
new file mode 100644
index 00000000..c900e79c
--- /dev/null
+++ b/android/relinterp.c
@@ -0,0 +1,930 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define SYSCALL_NO_TLS 1
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <link.h>
+#include <stdalign.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/syscall.h>
+#include <sys/user.h>
+#include <unistd.h>
+
+#include "reloc.h"
+#include "syscall.h"
+
+typedef void EntryFunc(void);
+
+// arm64 doesn't have a constant page size and has to use the value from AT_PAGESZ.
+#ifndef PAGE_SIZE
+#define PAGE_SIZE g_page_size
+#endif
+
+#define PAGE_START(x) ((x) & (~(PAGE_SIZE-1)))
+#define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE - 1))
+
+#define START "_start"
+#include "crt_arch.h"
+
+int main();
+weak void _init();
+weak void _fini();
+int __libc_start_main(int (*)(), int, char **,
+ void (*)(), void(*)(), void(*)());
+
+static ElfW(Phdr) replacement_phdr_table[64];
+static char replacement_interp[PATH_MAX];
+
+static bool g_debug = false;
+static const char* g_prog_name = NULL;
+static uintptr_t g_page_size = 0;
+static int g_errno = 0;
+
+__attribute__((visibility("hidden"))) extern ElfW(Dyn) _DYNAMIC[];
+
+__attribute__((used))
+static long ri_set_errno(unsigned long val) {
+ if (val > -4096UL) {
+ g_errno = -val;
+ return -1;
+ }
+ return val;
+}
+
+#define ri_syscall(...) ri_set_errno(__syscall(__VA_ARGS__))
+
+static ssize_t ri_write(int fd, const void* buf, size_t amt) {
+ return ri_syscall(SYS_write, fd, buf, amt);
+}
+
+__attribute__((noreturn))
+static void ri_exit(int status) {
+ ri_syscall(SYS_exit, status);
+ __builtin_unreachable();
+}
+
+static int ri_open(const char* path, int flags, mode_t mode) {
+ return ri_syscall(SYS_openat, AT_FDCWD, path, flags, mode);
+}
+
+static int ri_close(int fd) {
+ return ri_syscall(SYS_close, fd);
+}
+
+static off_t ri_lseek(int fd, off_t offset, int whence) {
+ return ri_syscall(SYS_lseek, fd, offset, whence);
+}
+
+static ssize_t ri_readlink(const char* path, char* buf, size_t size) {
+ return ri_syscall(SYS_readlinkat, AT_FDCWD, path, buf, size);
+}
+
+static void* ri_mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) {
+#ifdef SYS_mmap2
+ return (void*)ri_syscall(SYS_mmap2, addr, length, prot, flags, fd, offset/SYSCALL_MMAP2_UNIT);
+#else
+ return (void*)ri_syscall(SYS_mmap, addr, length, prot, flags, fd, offset);
+#endif
+}
+
+static void* ri_munmap(void* addr, size_t length) {
+ return (void*)ri_syscall(SYS_munmap, addr, length);
+}
+
+static int ri_mprotect(void* addr, size_t len, int prot) {
+ return ri_syscall(SYS_mprotect, addr, len, prot);
+}
+
+static ssize_t ri_pread(int fd, void* buf, size_t size, off_t ofs) {
+ return ri_syscall(SYS_pread, fd, buf, size, __SYSCALL_LL_PRW(ofs));
+}
+
+static size_t ri_strlen(const char* src) {
+ for (size_t len = 0;; ++len) {
+ if (src[len] == '\0') return len;
+ }
+}
+
+static char* ri_strcpy(char* dst, const char* src) {
+ char* result = dst;
+ while ((*dst = *src) != '\0') {
+ ++dst;
+ ++src;
+ }
+ return result;
+}
+
+static char* ri_strcat(char* dst, const char* src) {
+ ri_strcpy(dst + ri_strlen(dst), src);
+ return dst;
+}
+
+static void* ri_memset(void* dst, int val, size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ ((char*)dst)[i] = val;
+ }
+ return dst;
+}
+
+__attribute__ ((unused))
+static void* ri_memcpy(void* dst, const void* src, size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ ((char*)dst)[i] = ((char*)src)[i];
+ }
+ return dst;
+}
+
+static int ri_strncmp(const char* x, const char *y, size_t maxlen) {
+ for (size_t i = 0;; ++i) {
+ if (i == maxlen) return 0;
+ int result = (unsigned char)x[i] - (unsigned char)y[i];
+ if (result != 0) return result;
+ if (x[i] == '\0') return 0;
+ }
+}
+
+static int ri_strcmp(const char* x, const char *y) {
+ return ri_strncmp(x, y, SIZE_MAX);
+}
+
+static char* ri_strrchr(const char* str, int ch) {
+ char* result = NULL;
+ while (true) {
+ if (*str == ch) result = (char*)str;
+ if (*str == '\0') break;
+ ++str;
+ }
+ return result;
+}
+
+static char* ri_strchr(const char* str, int ch) {
+ while (*str) {
+ if (*str == ch) return (char*)str;
+ ++str;
+ }
+ return NULL;
+}
+
+static void ri_dirname(char* path) {
+ char* last_slash = ri_strrchr(path, '/');
+ if (last_slash == NULL) {
+ path[0] = '.'; // returns "."
+ path[1] = '\0';
+ } else if (last_slash == path) {
+ path[1] = '\0'; // returns "/"
+ } else {
+ *last_slash = '\0';
+ }
+}
+
+static void out_str_n(const char* str, size_t n) {
+ ri_write(STDERR_FILENO, str, n);
+}
+
+static void out_str(const char* str) {
+ out_str_n(str, ri_strlen(str));
+}
+
+static char* ul_to_str(unsigned long i, char* out, unsigned char base) {
+ char buf[65];
+ char* cur = &buf[65];
+ *--cur = '\0';
+ do {
+ *--cur = "0123456789abcdef"[i % base];
+ i /= base;
+ } while (i > 0);
+ return ri_strcpy(out, cur);
+}
+
+static char* l_to_str(long i, char* out, unsigned char base) {
+ if (i < 0) {
+ *out = '-';
+ ul_to_str(-(unsigned long)i, out + 1, base);
+ return out;
+ } else {
+ return ul_to_str(i, out, base);
+ }
+}
+
+static const char* ri_strerror(int err) {
+ switch (err) {
+ case EPERM: return "Operation not permitted";
+ case ENOENT: return "No such file or directory";
+ case EIO: return "I/O error";
+ case ENXIO: return "No such device or address";
+ case EAGAIN: return "Try again";
+ case ENOMEM: return "Out of memory";
+ case EACCES: return "Permission denied";
+ case ENODEV: return "No such device";
+ case ENOTDIR: return "Not a directory";
+ case EINVAL: return "Invalid argument";
+ case ENFILE: return "File table overflow";
+ case EMFILE: return "Too many open files";
+ case ESPIPE: return "Illegal seek";
+ case ENAMETOOLONG: return "File name too long";
+ case ELOOP: return "Too many symbolic links encountered";
+ }
+ static char buf[64];
+ ri_strcpy(buf, "Unknown error ");
+ l_to_str(err, buf + ri_strlen(buf), 10);
+ return buf;
+}
+
+static void outv(const char *fmt, va_list ap) {
+ char buf[65];
+ while (true) {
+ if (fmt[0] == '\0') break;
+
+#define NUM_FMT(num_fmt, type, func, base) \
+ if (!ri_strncmp(fmt, num_fmt, sizeof(num_fmt) - 1)) { \
+ out_str(func(va_arg(ap, type), buf, base)); \
+ fmt += sizeof(num_fmt) - 1; \
+ continue; \
+ }
+ NUM_FMT("%d", int, l_to_str, 10);
+ NUM_FMT("%ld", long, l_to_str, 10);
+ NUM_FMT("%u", unsigned int, ul_to_str, 10);
+ NUM_FMT("%lu", unsigned long, ul_to_str, 10);
+ NUM_FMT("%zu", size_t, ul_to_str, 10);
+ NUM_FMT("%x", unsigned int, ul_to_str, 16);
+ NUM_FMT("%lx", unsigned long, ul_to_str, 16);
+ NUM_FMT("%zx", size_t, ul_to_str, 16);
+#undef NUM_FMT
+
+ if (!ri_strncmp(fmt, "%p", 2)) {
+ out_str(ul_to_str((unsigned long)va_arg(ap, void*), buf, 16));
+ fmt += 2;
+ } else if (!ri_strncmp(fmt, "%s", 2)) {
+ const char* arg = va_arg(ap, const char*);
+ out_str(arg ? arg : "(null)");
+ fmt += 2;
+ } else if (!ri_strncmp(fmt, "%%", 2)) {
+ out_str("%");
+ fmt += 2;
+ } else if (fmt[0] == '%') {
+ buf[0] = fmt[1];
+ buf[1] = '\0';
+ out_str("relinterp error: unrecognized output specifier: '%");
+ out_str(buf);
+ out_str("'\n");
+ ri_exit(1);
+ } else {
+ size_t len = 0;
+ while (fmt[len] != '\0' && fmt[len] != '%') ++len;
+ out_str_n(fmt, len);
+ fmt += len;
+ }
+ }
+}
+
+__attribute__((format(printf, 1, 2)))
+static void debug(const char* fmt, ...) {
+ if (!g_debug) return;
+ out_str("relinterp: ");
+
+ va_list ap;
+ va_start(ap, fmt);
+ outv(fmt, ap);
+ va_end(ap);
+ out_str("\n");
+}
+
+__attribute__((format(printf, 1, 2), noreturn))
+static void fatal(const char* fmt, ...) {
+ out_str("relinterp: ");
+ if (g_prog_name) {
+ out_str(g_prog_name);
+ out_str(": ");
+ }
+ out_str("fatal error: ");
+
+ va_list ap;
+ va_start(ap, fmt);
+ outv(fmt, ap);
+ va_end(ap);
+ out_str("\n");
+ ri_exit(1);
+}
+
+static void* optimizer_barrier(void* val) {
+ __asm__ volatile ("nop" :: "r"(&val) : "memory");
+ return val;
+}
+
+typedef struct {
+ unsigned long key;
+ unsigned long value;
+} AuxEntry;
+
+typedef struct {
+ int argc;
+ char **argv;
+ char **envp;
+ size_t envp_count;
+ AuxEntry* auxv;
+ size_t auxv_count;
+} KernelArguments;
+
+static KernelArguments read_args(void* raw_args) {
+ KernelArguments result;
+ result.argc = *(long*)raw_args;
+ result.argv = (char**)((void**)raw_args + 1);
+ result.envp = result.argv + result.argc + 1;
+
+ char** envp = result.envp;
+ while (*envp != NULL) ++envp;
+ result.envp_count = envp - result.envp;
+ ++envp;
+
+ result.auxv = (AuxEntry*)envp;
+ size_t count = 0;
+ while (result.auxv[count].key != 0) {
+ ++count;
+ }
+ result.auxv_count = count;
+ return result;
+}
+
+static void dump_auxv(const KernelArguments* args) {
+ for (size_t i = 0; i < args->auxv_count; ++i) {
+ const char* name = "";
+ switch (args->auxv[i].key) {
+ case AT_BASE: name = " [AT_BASE]"; break;
+ case AT_EGID: name = " [AT_EGID]"; break;
+ case AT_ENTRY: name = " [AT_ENTRY]"; break;
+ case AT_EUID: name = " [AT_EUID]"; break;
+ case AT_GID: name = " [AT_GID]"; break;
+ case AT_PAGESZ: name = " [AT_PAGESZ]"; break;
+ case AT_PHDR: name = " [AT_PHDR]"; break;
+ case AT_PHENT: name = " [AT_PHENT]"; break;
+ case AT_PHNUM: name = " [AT_PHNUM]"; break;
+ case AT_SECURE: name = " [AT_SECURE]"; break;
+ case AT_SYSINFO: name = " [AT_SYSINFO]"; break;
+ case AT_SYSINFO_EHDR: name = " [AT_SYSINFO_EHDR]"; break;
+ case AT_UID: name = " [AT_UID]"; break;
+ }
+ debug(" %lu => 0x%lx%s", args->auxv[i].key, args->auxv[i].value, name);
+ }
+}
+
+static unsigned long ri_getauxval(const KernelArguments* args, unsigned long kind,
+ bool allow_missing) {
+ for (size_t i = 0; i < args->auxv_count; ++i) {
+ if (args->auxv[i].key == kind) return args->auxv[i].value;
+ }
+ if (!allow_missing) fatal("could not find aux vector entry %lu", kind);
+ return 0;
+}
+
+static int elf_flags_to_prot(int flags) {
+ int result = 0;
+ if (flags & PF_R) result |= PROT_READ;
+ if (flags & PF_W) result |= PROT_WRITE;
+ if (flags & PF_X) result |= PROT_EXEC;
+ return result;
+}
+
+typedef struct {
+ int fd;
+ char path[PATH_MAX];
+} OpenedLoader;
+
+typedef struct {
+ void* base_addr;
+ EntryFunc* entry;
+} LoadedInterp;
+
+static LoadedInterp load_interp(const OpenedLoader *loader, ElfW(Ehdr)* hdr) {
+ ElfW(Phdr)* phdr = (ElfW(Phdr)*)((char*)hdr + hdr->e_phoff);
+ size_t phdr_count = hdr->e_phnum;
+
+ size_t max_vaddr = 0;
+
+ // Find the virtual address extent.
+ for (size_t i = 0; i < phdr_count; ++i) {
+ if (phdr[i].p_type == PT_LOAD) {
+ max_vaddr = PAGE_END(MAX(max_vaddr, phdr[i].p_vaddr + phdr[i].p_memsz));
+ }
+ }
+
+ // Map an area to fit the loader.
+ void* loader_vaddr = ri_mmap(NULL, max_vaddr, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (loader_vaddr == (void*)MAP_FAILED) {
+ fatal("reservation mmap of 0x%zx bytes for %s failed: %s", max_vaddr, loader->path,
+ ri_strerror(g_errno));
+ }
+
+ // Map each PT_LOAD.
+ for (size_t i = 0; i < phdr_count; ++i) {
+ if (phdr[i].p_type == PT_LOAD) {
+ size_t start = PAGE_START(phdr[i].p_vaddr);
+ const size_t end = PAGE_END(phdr[i].p_vaddr + phdr[i].p_memsz);
+ if (phdr[i].p_filesz > 0) {
+ const size_t file_end = phdr[i].p_vaddr + phdr[i].p_filesz;
+ void* tmp = ri_mmap((char*)loader_vaddr + start,
+ file_end - start,
+ elf_flags_to_prot(phdr[i].p_flags),
+ MAP_PRIVATE | MAP_FIXED, loader->fd, PAGE_START(phdr[i].p_offset));
+ if (tmp == (void*)MAP_FAILED) {
+ fatal("PT_LOAD mmap failed (%s segment #%zu): %s", loader->path, i,
+ ri_strerror(g_errno));
+ }
+ start = file_end;
+ if (phdr[i].p_flags & PF_W) {
+ // The bytes between p_filesz and PAGE_END(p_filesz) currently come from the file mapping,
+ // but they need to be zeroed. (Apparently this zeroing isn't necessary if the segment isn't
+ // writable, and zeroing a non-writable page would be inconvenient.)
+ ri_memset((char*)loader_vaddr + start, '\0', PAGE_END(start) - start);
+ }
+ start = PAGE_END(start);
+ }
+ if (start < end) {
+ // The memory is already zeroed, because it comes from an anonymous file mapping. Just set
+ // the protections correctly.
+ int result = ri_mprotect((char*)loader_vaddr + start, end - start,
+ elf_flags_to_prot(phdr[i].p_flags));
+ if (result != 0) {
+ fatal("mprotect of PT_LOAD failed (%s segment #%zu): %s", loader->path, i,
+ ri_strerror(g_errno));
+ }
+ }
+ }
+ }
+
+ return (LoadedInterp) {
+ .base_addr = loader_vaddr,
+ .entry = (EntryFunc*)((uintptr_t)loader_vaddr + hdr->e_entry),
+ };
+}
+
+typedef struct {
+ ElfW(Phdr)* phdr;
+ size_t phdr_count;
+ uintptr_t load_bias;
+ uintptr_t page_size;
+ char* search_paths;
+ ElfW(Ehdr)* ehdr;
+ ElfW(Phdr)* first_load;
+ bool secure;
+} ExeInfo;
+
+static ExeInfo get_exe_info(const KernelArguments* args) {
+ ExeInfo result = { 0 };
+ result.phdr = (ElfW(Phdr)*)ri_getauxval(args, AT_PHDR, false);
+ result.phdr_count = ri_getauxval(args, AT_PHNUM, false);
+ result.page_size = ri_getauxval(args, AT_PAGESZ, false);
+
+ unsigned long uid = ri_getauxval(args, AT_UID, false);
+ unsigned long euid = ri_getauxval(args, AT_EUID, false);
+ unsigned long gid = ri_getauxval(args, AT_GID, false);
+ unsigned long egid = ri_getauxval(args, AT_EGID, false);
+ unsigned long secure = ri_getauxval(args, AT_SECURE, true);
+ result.secure = uid != euid || gid != egid || secure;
+
+ debug("orig phdr = %p", (void*)result.phdr);
+ debug("orig phnum = %zu", result.phdr_count);
+
+ for (size_t i = 0; i < result.phdr_count; ++i) {
+ if (result.phdr[i].p_type == PT_DYNAMIC) {
+ result.load_bias = (uintptr_t)&_DYNAMIC - result.phdr[i].p_vaddr;
+ }
+ }
+ debug("load_bias = 0x%lx", (unsigned long)result.load_bias);
+
+ for (size_t i = 0; i < result.phdr_count; ++i) {
+ ElfW(Phdr)* phdr = &result.phdr[i];
+ if (phdr->p_type != PT_LOAD) continue;
+ result.first_load = phdr;
+ if (phdr->p_offset != 0) {
+ fatal("expected zero p_offset for first PT_LOAD, found 0x%zx instead",
+ (size_t)phdr->p_offset);
+ }
+ result.ehdr = (ElfW(Ehdr)*)(phdr->p_vaddr + result.load_bias);
+ break;
+ }
+ debug("ehdr = %p", (void*)result.ehdr);
+
+ ElfW(Word) runpath_offset = -1;
+ char* strtab = NULL;
+ for (ElfW(Dyn)* dyn = _DYNAMIC; dyn->d_tag != DT_NULL; dyn++) {
+ switch (dyn->d_tag) {
+ case DT_RUNPATH:
+ runpath_offset = dyn->d_un.d_val;
+ break;
+ case DT_RPATH:
+ if (runpath_offset == -1) runpath_offset = dyn->d_un.d_val;
+ break;
+ case DT_STRTAB:
+ strtab = (char*)(dyn->d_un.d_ptr + result.load_bias);
+ break;
+ }
+ }
+
+ if (strtab && runpath_offset != -1) {
+ result.search_paths = strtab + runpath_offset;
+ debug("dt_runpath = %s", result.search_paths);
+ }
+ return result;
+}
+
+// Loaders typically read the PT_INTERP of the executable, e.g. to set a pathname on the loader.
+// glibc insists on the executable having PT_INTERP, and aborts if it's missing. Musl passes it
+// to debuggers to find symbols for the loader, which includes all the libc symbols.
+//
+// Make a copy of the phdr table and insert PT_INTERP into the copy.
+//
+static void insert_pt_interp_into_phdr_table(const KernelArguments* args, const ExeInfo* exe,
+ const char* loader_realpath) {
+ // Reserve extra space for the inserted PT_PHDR and PT_INTERP segments and a null terminator.
+ if (exe->phdr_count + 3 > sizeof(replacement_phdr_table) / sizeof(replacement_phdr_table[0])) {
+ fatal("too many phdr table entries in executable");
+ }
+
+ ElfW(Phdr) newPhdr = {
+ .p_type = PT_PHDR,
+ // The replacement phdr is in the BSS section, which has no file location.
+ // Use 0 for the offset. If this causes a problem the replacement phdr could
+ // be moved to the data section and the correct p_offset calculated.
+ .p_offset = 0,
+ .p_vaddr = (uintptr_t)&replacement_phdr_table - exe->load_bias,
+ .p_paddr = (uintptr_t)&replacement_phdr_table - exe->load_bias,
+ .p_memsz = (exe->phdr_count + 1) * sizeof(ElfW(Phdr)),
+ .p_filesz = (exe->phdr_count + 1) * sizeof(ElfW(Phdr)),
+ .p_flags = PF_R,
+ .p_align = alignof(ElfW(Phdr)),
+ };
+
+ ElfW(Phdr*) cur = replacement_phdr_table;
+ if (exe->phdr[0].p_type != PT_PHDR) {
+ // ld.bfd does not insert a PT_PHDR if there is no PT_INTERP, fake one.
+ // It has to be first. We're adding an entry so increase memsz and filesz.
+ newPhdr.p_memsz += sizeof(ElfW(Phdr));
+ newPhdr.p_filesz += sizeof(ElfW(Phdr));
+ *cur = newPhdr;
+ ++cur;
+ }
+
+ for (size_t i = 0; i < exe->phdr_count; ++i) {
+ switch (exe->phdr[i].p_type) {
+ case 0:
+ fatal("unexpected null phdr entry at index %zu", i);
+ break;
+ case PT_PHDR:
+ *cur = newPhdr;
+ break;
+ default:
+ *cur = exe->phdr[i];
+ }
+ ++cur;
+ }
+
+ // Insert PT_INTERP at the end.
+ cur->p_type = PT_INTERP;
+ cur->p_offset = 0;
+ cur->p_vaddr = (uintptr_t)&replacement_interp - exe->load_bias;
+ cur->p_paddr = cur->p_vaddr;
+ cur->p_filesz = ri_strlen(replacement_interp) + 1;
+ cur->p_memsz = ri_strlen(replacement_interp) + 1;
+ cur->p_flags = PF_R;
+ cur->p_align = 1;
+ ++cur;
+
+ ri_strcpy(replacement_interp, loader_realpath);
+
+ debug("new phdr = %p", (void*)&replacement_phdr_table);
+ debug("new phnum = %zu", cur - replacement_phdr_table);
+
+ // Update the aux vector with the new phdr+phnum.
+ for (size_t i = 0; i < args->auxv_count; ++i) {
+ if (args->auxv[i].key == AT_PHDR) {
+ args->auxv[i].value = (unsigned long)&replacement_phdr_table;
+ } else if (args->auxv[i].key == AT_PHNUM) {
+ args->auxv[i].value = cur - replacement_phdr_table;
+ }
+ }
+
+ // AT_PHDR and AT_PHNUM are now updated to point to the replacement program
+ // headers, but the e_phoff and e_phnum in the ELF headers still point to the
+ // original program headers. dynlink.c doesn't use e_phoff value from the
+ // main application's program headers. The e_phoff and e_phnum values could
+ // be updated, but that would require using mprotect to allow modifications
+ // to the read-only first page.
+}
+
+static void realpath_fd(int fd, const char* orig_path, char* out, size_t len) {
+ char path[64];
+ ri_strcpy(path, "/proc/self/fd/");
+ ul_to_str(fd, path + ri_strlen(path), 10);
+ ssize_t result = ri_readlink(path, out, len);
+ if (result == -1) fatal("could not get realpath of %s: %s", orig_path, ri_strerror(g_errno));
+ if ((size_t)result >= len) fatal("realpath of %s too long", orig_path);
+}
+
+static int open_loader(const ExeInfo* exe, const char* path, OpenedLoader* loader) {
+ debug("trying to open '%s'", path);
+ loader->fd = ri_open(path, O_RDONLY, 0);
+ if (loader->fd < 0) {
+ debug("could not open loader %s: %s", path, ri_strerror(g_errno));
+ return -1;
+ }
+
+ ElfW(Ehdr) hdr;
+ ssize_t l = ri_pread(loader->fd, &hdr, sizeof(hdr), 0);
+ if (l < 0) {
+ debug("reading elf header from %s failed: %s", path, ri_strerror(g_errno));
+ return -1;
+ }
+ if (l != sizeof(hdr)) {
+ debug("file %s too short to contain elf header", path);
+ return -1;
+ }
+
+ if (hdr.e_ident[0] != ELFMAG0 ||
+ hdr.e_ident[1] != ELFMAG1 ||
+ hdr.e_ident[2] != ELFMAG2 ||
+ hdr.e_ident[3] != ELFMAG3) {
+ debug("file %s is not an elf file", path);
+ return -1;
+ }
+
+ if (hdr.e_machine != exe->ehdr->e_machine) {
+ debug("incorrect elf machine for loader %s, expected %d got %d",
+ path, exe->ehdr->e_machine, hdr.e_machine);
+ return -1;
+ }
+
+ if (hdr.e_ident[EI_CLASS] != exe->ehdr->e_ident[EI_CLASS]) {
+ debug("incorrect elf class for loader %s, expected %d got %d",
+ path, exe->ehdr->e_ident[EI_CLASS], hdr.e_ident[EI_CLASS]);
+ return -1;
+ }
+
+ realpath_fd(loader->fd, path, loader->path, sizeof(loader->path));
+
+ return 0;
+}
+
+static int open_rel_loader(const ExeInfo* exe, const char* dir, const char* rel, OpenedLoader* loader) {
+ char buf[PATH_MAX];
+
+ size_t dir_len = ri_strlen(dir);
+
+ if (dir_len + (dir_len == 0 ? 1 : 0) + ri_strlen(rel) + 2 > sizeof(buf)) {
+ debug("path to loader exceeds PATH_MAX: %s/%s", dir, rel);
+ return 1;
+ }
+
+ if (dir_len == 0) {
+ ri_strcpy(buf, ".");
+ } else {
+ ri_strcpy(buf, dir);
+ if (dir[dir_len-1] != '/') {
+ ri_strcat(buf, "/");
+ }
+ }
+ ri_strcat(buf, rel);
+
+ return open_loader(exe, buf, loader);
+}
+
+static void get_origin(char* buf, size_t buf_len) {
+ ssize_t len = ri_readlink("/proc/self/exe", buf, buf_len);
+ if (len <= 0 || (size_t)len >= buf_len) {
+ fatal("could not readlink /proc/self/exe: %s", ri_strerror(g_errno));
+ }
+ buf[len] = '\0';
+
+ ri_dirname(buf);
+}
+
+static int search_path_list_for_loader(const ExeInfo* exe, const char* loader_rel_path, const char* search_path,
+ const char* search_path_name, bool expand_origin, OpenedLoader *loader) {
+ char origin_buf[PATH_MAX];
+ char* origin = NULL;
+
+ const char* p = search_path;
+ while (p && p[0]) {
+ const char* start = p;
+ const char* end = ri_strchr(p, ':');
+ if (end == NULL) {
+ end = start + ri_strlen(p);
+ p = NULL;
+ } else {
+ p = end + 1;
+ }
+ size_t n = end - start;
+ char search_path_entry[PATH_MAX];
+ if (n >= sizeof(search_path_entry)) {
+ // Too long, skip.
+ debug("%s entry too long: %s", search_path_name, start);
+ continue;
+ }
+
+ ri_memcpy(search_path_entry, start, n);
+ search_path_entry[n] = '\0';
+
+ char buf[PATH_MAX];
+ char* d = NULL;
+ if (expand_origin) {
+ d = ri_strchr(search_path_entry, '$');
+ }
+ if (d && (!ri_strncmp(d, "$ORIGIN", 7) || !ri_strncmp(d, "${ORIGIN}", 9))) {
+ if (!origin) {
+ get_origin(origin_buf, sizeof(origin_buf));
+ origin = origin_buf;
+ }
+
+ size_t s = 7;
+ if (d[1] == '{') {
+ s += 2;
+ }
+ ri_memcpy(buf, search_path_entry, d - search_path_entry);
+ buf[d - search_path_entry] = '\0';
+ if (ri_strlen(buf) + ri_strlen(origin) + ri_strlen(d+s) >= sizeof(buf)) {
+ debug("path to loader %s%s%s too long", buf, origin, d+s);
+ continue;
+ }
+
+ ri_strcat(buf, origin);
+ ri_strcat(buf, d+s);
+ } else {
+ ri_strcpy(buf, search_path_entry);
+ }
+ debug("trying loader %s at %s", loader_rel_path, buf);
+ if (!open_rel_loader(exe, buf, loader_rel_path, loader)) {
+ debug("opened loader %s at %s", loader_rel_path, buf);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int find_and_open_loader(const ExeInfo* exe, const char* ld_library_path, OpenedLoader* loader) {
+ const char* loader_rel_path = LOADER_PATH;
+
+ if (loader_rel_path[0] == '/') {
+ return open_loader(exe, loader_rel_path, loader);
+ }
+
+ if (exe->secure) {
+ fatal("relinterp not supported for secure executables");
+ }
+
+ if (!search_path_list_for_loader(exe, loader_rel_path, ld_library_path, "LD_LIBRARY_PATH", false, loader)) {
+ return 0;
+ }
+
+ if (!exe->search_paths || ri_strlen(exe->search_paths) == 0) {
+ // If no DT_RUNPATH search relative to the exe.
+ char origin[PATH_MAX];
+ get_origin(origin, sizeof(origin));
+ return open_rel_loader(exe, origin, loader_rel_path, loader);
+ }
+
+ if (!search_path_list_for_loader(exe, loader_rel_path, exe->search_paths, "rpath", true, loader)) {
+ return 0;
+ }
+
+ fatal("unable to find loader %s in rpath %s", loader_rel_path, exe->search_paths);
+}
+
+// Use a trick to determine whether the executable has been relocated yet. This variable points to
+// a variable in libc. It will be NULL if and only if the program hasn't been linked yet. This
+// should accommodate these situations:
+// - The program was actually statically-linked instead.
+// - Either a PIE or non-PIE dynamic executable.
+// - Any situation where the loader calls the executable's _start:
+// - In normal operation, the kernel calls the executable's _start, _start jumps to the loader's
+// entry point, which jumps to _start again after linking it.
+// - The executable actually has its PT_INTERP set after all.
+// - The user runs the loader, passing it the path of the executable.
+// This C file must always be compiled as PIC, or else the linker will use a COPY relocation and
+// duplicate "environ" into the executable.
+static bool is_exe_relocated(void) {
+ // Use the GOT to get the address of environ.
+ extern char** environ;
+ void* read_environ = optimizer_barrier(&environ);
+ debug("read_environ = %p", read_environ);
+ return read_environ != NULL;
+}
+
+void _start_c(long* raw_args) {
+ const KernelArguments args = read_args(raw_args);
+ const char* ld_library_path = NULL;
+
+ for (size_t i = 0; i < args.envp_count; ++i) {
+ if (!ri_strcmp(args.envp[i], "RELINTERP_DEBUG=1")) {
+ g_debug = true;
+ }
+ if (!ri_strncmp(args.envp[i], "LD_LIBRARY_PATH=", 16)) {
+ ld_library_path = args.envp[i] + 16;
+ }
+ }
+ if (args.argc >= 1) {
+ g_prog_name = args.argv[0];
+ }
+
+ if (is_exe_relocated()) {
+ debug("exe is already relocated, starting main executable");
+ int argc = raw_args[0];
+ char **argv = (void *)(raw_args+1);
+ __libc_start_main(main, argc, argv, _init, _fini, 0);
+ }
+
+ debug("entering relinterp");
+
+ const ExeInfo exe = get_exe_info(&args);
+ g_page_size = exe.page_size;
+
+ OpenedLoader loader;
+ if (find_and_open_loader(&exe, ld_library_path, &loader)) {
+ fatal("failed to open loader");
+ }
+ off_t len = ri_lseek(loader.fd, 0, SEEK_END);
+ if (len == (off_t)-1) fatal("lseek on %s failed: %s", loader.path, ri_strerror(g_errno));
+
+ void* loader_data = ri_mmap(NULL, len, PROT_READ, MAP_PRIVATE, loader.fd, 0);
+ if (loader_data == (void*)MAP_FAILED) {
+ fatal("could not mmap %s: %s", loader.path, ri_strerror(g_errno));
+ }
+
+ LoadedInterp interp = load_interp(&loader, (ElfW(Ehdr)*)loader_data);
+ if (ri_munmap(loader_data, len) != 0) fatal("munmap failed: %s", ri_strerror(g_errno));
+
+ debug("original auxv:");
+ dump_auxv(&args);
+
+ // Create a virtual phdr table that includes PT_INTERP, for the benefit of loaders that read the
+ // executable PT_INTERP.
+ insert_pt_interp_into_phdr_table(&args, &exe, loader.path);
+ ri_close(loader.fd);
+
+ // TODO: /proc/pid/auxv isn't updated with the new auxv vector. Is it possible to update it?
+ // XXX: If we try to update it, we'd use prctl(PR_SET_MM, PR_SET_MM_AUXV, &vec, size, 0)
+ // Maybe updating it would be useful as a way to communicate the loader's base to a debugger.
+ // e.g. lldb uses AT_BASE in the aux vector, but it caches the values at process startup, so
+ // it wouldn't currently notice a changed value.
+
+ // The loader uses AT_BASE to locate itself, so search for the entry and update it. Even though
+ // its value is always zero, the kernel still includes the entry[0]. If this changes (or we want
+ // to make weaker assumptions about the kernel's behavior), then we can copy the kernel arguments
+ // onto the stack (e.g. using alloca) before jumping to the loader's entry point.
+ // [0] https://github.com/torvalds/linux/blob/v5.13/fs/binfmt_elf.c#L263
+ for (size_t i = 0; i < args.auxv_count; ++i) {
+ if (args.auxv[i].key == AT_BASE) {
+ args.auxv[i].value = (unsigned long)interp.base_addr;
+ debug("new auxv:");
+ dump_auxv(&args);
+ debug("transferring to real loader");
+ CRTJMP(interp.entry, raw_args);
+ }
+ }
+ fatal("AT_BASE not found in aux vector");
+}
+
+
+// Normally gdb and lldb look for a symbol named "_dl_debug_state" in the
+// interpreter to get notified when the dynamic loader has modified the
+// list of shared libraries. When using relinterp, the debugger is not
+// aware of the interpreter (PT_INTERP is unset and auxv AT_BASE is 0) so it
+// doesn't know where to look for the symbol. It falls back to looking in the
+// executable, so provide a symbol for it to find. The dynamic loader will
+// need to forward its calls to its own _dl_debug_state symbol to this one.
+//
+// This has to be defined in a .c file because lldb looks for a symbol with
+// DWARF language type DW_LANG_C.
+extern void _dl_debug_state() {
+}
diff --git a/android/relinterp.script b/android/relinterp.script
new file mode 100644
index 00000000..9085231d
--- /dev/null
+++ b/android/relinterp.script
@@ -0,0 +1,4 @@
+SECTIONS {
+ /DISCARD/ : { *(.interp) }
+}
+INSERT BEFORE .text;
diff --git a/arch/arm/syscall_arch.h b/arch/arm/syscall_arch.h
index a877b2cf..624e992e 100644
--- a/arch/arm/syscall_arch.h
+++ b/arch/arm/syscall_arch.h
@@ -101,3 +101,10 @@ static inline long __syscall6(long n, long a, long b, long c, long d, long e, lo
#define SYSCALL_FADVISE_6_ARG
#define SYSCALL_IPC_BROKEN_MODE
+
+#define VDSO_USEFUL
+#define VDSO_CGT32_SYM "__vdso_clock_gettime"
+#define VDSO_CGT32_VER "LINUX_2.6"
+#define VDSO_CGT_SYM "__vdso_clock_gettime64"
+#define VDSO_CGT_VER "LINUX_2.6"
+#define VDSO_CGT_WORKAROUND 1
diff --git a/configure b/configure
index ca5cbc0b..6f5453f5 100755
--- a/configure
+++ b/configure
@@ -723,11 +723,6 @@ fi
test "$SUBARCH" \
&& printf "configured for %s variant: %s\n" "$ARCH" "$ARCH$SUBARCH"
-case "$ARCH$SUBARCH" in
-arm) ASMSUBARCH=el ;;
-*) ASMSUBARCH=$SUBARCH ;;
-esac
-
#
# Some archs (powerpc) have different possible long double formats
# that the compiler can be configured for. The logic for whether this
diff --git a/include/elf.h b/include/elf.h
index 86e2f0bb..9e980a29 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -385,7 +385,8 @@ typedef struct {
#define SHT_PREINIT_ARRAY 16
#define SHT_GROUP 17
#define SHT_SYMTAB_SHNDX 18
-#define SHT_NUM 19
+#define SHT_RELR 19
+#define SHT_NUM 20
#define SHT_LOOS 0x60000000
#define SHT_GNU_ATTRIBUTES 0x6ffffff5
#define SHT_GNU_HASH 0x6ffffff6
@@ -754,7 +755,10 @@ typedef struct {
#define DT_PREINIT_ARRAY 32
#define DT_PREINIT_ARRAYSZ 33
#define DT_SYMTAB_SHNDX 34
-#define DT_NUM 35
+#define DT_RELRSZ 35
+#define DT_RELR 36
+#define DT_RELRENT 37
+#define DT_NUM 38
#define DT_LOOS 0x6000000d
#define DT_HIOS 0x6ffff000
#define DT_LOPROC 0x70000000
diff --git a/include/signal.h b/include/signal.h
index b6711bc4..c347f861 100644
--- a/include/signal.h
+++ b/include/signal.h
@@ -229,7 +229,6 @@ int sigqueue(pid_t, int, union sigval);
int pthread_sigmask(int, const sigset_t *__restrict, sigset_t *__restrict);
int pthread_kill(pthread_t, int);
-int pthread_sigqueue(pthread_t, int, union sigval);
void psiginfo(const siginfo_t *, const char *);
void psignal(int, const char *);
diff --git a/include/strings.h b/include/strings.h
index db0960b4..b7a5ea08 100644
--- a/include/strings.h
+++ b/include/strings.h
@@ -5,6 +5,7 @@
extern "C" {
#endif
+#include <features.h>
#define __NEED_size_t
#define __NEED_locale_t
diff --git a/include/sys/sysinfo.h b/include/sys/sysinfo.h
index 3b7d0e3c..6a3931e5 100644
--- a/include/sys/sysinfo.h
+++ b/include/sys/sysinfo.h
@@ -8,7 +8,7 @@ extern "C" {
#define SI_LOAD_SHIFT 16
struct sysinfo {
- long uptime;
+ unsigned long uptime;
unsigned long loads[3];
unsigned long totalram;
unsigned long freeram;
diff --git a/include/unistd.h b/include/unistd.h
index 212263a7..0e8149e4 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -425,6 +425,8 @@ pid_t gettid(void);
#define _SC_XOPEN_STREAMS 246
#define _SC_THREAD_ROBUST_PRIO_INHERIT 247
#define _SC_THREAD_ROBUST_PRIO_PROTECT 248
+#define _SC_MINSIGSTKSZ 249
+#define _SC_SIGSTKSZ 250
#define _CS_PATH 0
#define _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS 1
@@ -467,6 +469,8 @@ pid_t gettid(void);
#define _CS_POSIX_V7_LPBIG_OFFBIG_LINTFLAGS 1147
#define _CS_V6_ENV 1148
#define _CS_V7_ENV 1149
+#define _CS_POSIX_V7_THREADS_CFLAGS 1150
+#define _CS_POSIX_V7_THREADS_LDFLAGS 1151
#ifdef __cplusplus
}
diff --git a/ldso/dlstart.c b/ldso/dlstart.c
index 20d50f2c..259f5e18 100644
--- a/ldso/dlstart.c
+++ b/ldso/dlstart.c
@@ -140,6 +140,21 @@ hidden void _dlstart_c(size_t *sp, size_t *dynv)
size_t *rel_addr = (void *)(base + rel[0]);
*rel_addr = base + rel[2];
}
+
+ rel = (void *)(base+dyn[DT_RELR]);
+ rel_size = dyn[DT_RELRSZ];
+ size_t *relr_addr = 0;
+ for (; rel_size; rel++, rel_size-=sizeof(size_t)) {
+ if ((rel[0]&1) == 0) {
+ relr_addr = (void *)(base + rel[0]);
+ *relr_addr++ += base;
+ } else {
+ for (size_t i=0, bitmap=rel[0]; bitmap>>=1; i++)
+ if (bitmap&1)
+ relr_addr[i] += base;
+ relr_addr += 8*sizeof(size_t)-1;
+ }
+ }
#endif
stage2_func dls2;
diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index fd0d38e9..d055bd26 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -32,7 +32,9 @@
#define realloc __libc_realloc
#define free __libc_free
-static void error(const char *, ...);
+static void error_impl(const char *, ...);
+static void error_noop(const char *, ...);
+static void (*error)(const char *, ...) = error_noop;
#define MAXP2(a,b) (-(-(a)&-(b)))
#define ALIGN(x,y) ((x)+(y)-1 & -(y))
@@ -63,6 +65,8 @@ struct dso {
size_t *dynv;
struct dso *next, *prev;
+ int elfmachine;
+ int elfclass;
Phdr *phdr;
int phnum;
size_t phentsize;
@@ -151,6 +155,7 @@ static struct fdpic_loadmap *app_loadmap;
static struct fdpic_dummy_loadmap app_dummy_loadmap;
struct debug *_dl_debug_addr = &debug;
+static void (*exe_dl_debug_state)(void) = 0;
extern hidden int __malloc_replaced;
@@ -211,7 +216,8 @@ static void decode_vec(size_t *v, size_t *a, size_t cnt)
size_t i;
for (i=0; i<cnt; i++) a[i] = 0;
for (; v[0]; v+=2) if (v[0]-1<cnt-1) {
- a[0] |= 1UL<<v[0];
+ if (v[0] < 8*sizeof(long))
+ a[0] |= 1UL<<v[0];
a[v[0]] = v[1];
}
}
@@ -516,6 +522,23 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
}
}
+static void do_relr_relocs(struct dso *dso, size_t *relr, size_t relr_size)
+{
+ unsigned char *base = dso->base;
+ size_t *reloc_addr;
+ for (; relr_size; relr++, relr_size-=sizeof(size_t))
+ if ((relr[0]&1) == 0) {
+ reloc_addr = laddr(dso, relr[0]);
+ *reloc_addr++ += (size_t)base;
+ } else {
+ int i = 0;
+ for (size_t bitmap=relr[0]; (bitmap>>=1); i++)
+ if (bitmap&1)
+ reloc_addr[i] += (size_t)base;
+ reloc_addr += 8*sizeof(size_t)-1;
+ }
+}
+
static void redo_lazy_relocs()
{
struct dso *p = lazy_head, *next;
@@ -623,6 +646,19 @@ static void unmap_library(struct dso *dso)
}
}
+static int verify_elf_magic(const Ehdr* eh) {
+ return eh->e_ident[0] == ELFMAG0 &&
+ eh->e_ident[1] == ELFMAG1 &&
+ eh->e_ident[2] == ELFMAG2 &&
+ eh->e_ident[3] == ELFMAG3;
+}
+
+/* Verifies that an elf header's machine and class match the loader */
+static int verify_elf_arch(const Ehdr* eh) {
+ return eh->e_machine == ldso.elfmachine &&
+ eh->e_ident[EI_CLASS] == ldso.elfclass;
+}
+
static void *map_library(int fd, struct dso *dso)
{
Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)];
@@ -645,6 +681,10 @@ static void *map_library(int fd, struct dso *dso)
if (l<0) return 0;
if (l<sizeof *eh || (eh->e_type != ET_DYN && eh->e_type != ET_EXEC))
goto noexec;
+ if (!verify_elf_magic(eh)) goto noexec;
+ if (!verify_elf_arch(eh)) goto noexec;
+ dso->elfmachine = eh->e_machine;
+ dso->elfclass = eh->e_ident[EI_CLASS];
phsize = eh->e_phentsize * eh->e_phnum;
if (phsize > sizeof buf - sizeof *eh) {
allocated_buf = malloc(phsize);
@@ -809,29 +849,53 @@ error:
return 0;
}
-static int path_open(const char *name, const char *s, char *buf, size_t buf_size)
+static int path_open_library(const char *name, const char *s, char *buf, size_t buf_size)
{
size_t l;
int fd;
+ const char *p;
for (;;) {
s += strspn(s, ":\n");
+ p = s;
l = strcspn(s, ":\n");
if (l-1 >= INT_MAX) return -1;
- if (snprintf(buf, buf_size, "%.*s/%s", (int)l, s, name) < buf_size) {
- if ((fd = open(buf, O_RDONLY|O_CLOEXEC))>=0) return fd;
- switch (errno) {
- case ENOENT:
- case ENOTDIR:
- case EACCES:
- case ENAMETOOLONG:
- break;
- default:
- /* Any negative value but -1 will inhibit
- * futher path search. */
+ s += l;
+ if (snprintf(buf, buf_size, "%.*s/%s", (int)l, p, name) < buf_size) {
+ fd = open(buf, O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ case EACCES:
+ case ENAMETOOLONG:
+ /* Keep searching in path list. */
+ continue;
+ default:
+ /* Any negative value but -1 will
+ * inhibit further path search in
+ * load_library. */
+ return -2;
+ }
+ }
+ Ehdr eh;
+ ssize_t n = pread(fd, &eh, sizeof eh, 0);
+ /* If the elf file is invalid return -2 to inhibit
+ * further path search in load_library. */
+ if (n < 0 ||
+ n != sizeof eh ||
+ !verify_elf_magic(&eh)) {
+ close(fd);
return -2;
}
+ /* If the elf file has a valid header but is for the
+ * wrong architecture ignore it and keep searching the
+ * path list. */
+ if (!verify_elf_arch(&eh)) {
+ close(fd);
+ continue;
+ }
+ return fd;
}
- s += l;
}
}
@@ -869,7 +933,7 @@ static int fixup_rpath(struct dso *p, char *buf, size_t buf_size)
case ENOENT:
case ENOTDIR:
case EACCES:
- break;
+ return 0;
default:
return -1;
}
@@ -1011,7 +1075,7 @@ static struct dso *load_library(const char *name, struct dso *needed_by)
/* Catch and block attempts to reload the implementation itself */
if (name[0]=='l' && name[1]=='i' && name[2]=='b') {
static const char reserved[] =
- "c.pthread.rt.m.dl.util.xnet.";
+ "c.c_musl.pthread.rt.m.dl.util.xnet.";
const char *rp, *next;
for (rp=reserved; *rp; rp=next) {
next = strchr(rp, '.') + 1;
@@ -1055,12 +1119,12 @@ static struct dso *load_library(const char *name, struct dso *needed_by)
}
if (strlen(name) > NAME_MAX) return 0;
fd = -1;
- if (env_path) fd = path_open(name, env_path, buf, sizeof buf);
+ if (env_path) fd = path_open_library(name, env_path, buf, sizeof buf);
for (p=needed_by; fd == -1 && p; p=p->needed_by) {
if (fixup_rpath(p, buf, sizeof buf) < 0)
fd = -2; /* Inhibit further search. */
if (p->rpath)
- fd = path_open(name, p->rpath, buf, sizeof buf);
+ fd = path_open_library(name, p->rpath, buf, sizeof buf);
}
if (fd == -1) {
if (!sys_path) {
@@ -1099,7 +1163,7 @@ static struct dso *load_library(const char *name, struct dso *needed_by)
}
}
if (!sys_path) sys_path = "/lib:/usr/local/lib:/usr/lib";
- fd = path_open(name, sys_path, buf, sizeof buf);
+ fd = path_open_library(name, sys_path, buf, sizeof buf);
}
pathname = buf;
}
@@ -1358,13 +1422,17 @@ static void reloc_all(struct dso *p)
2+(dyn[DT_PLTREL]==DT_RELA));
do_relocs(p, laddr(p, dyn[DT_REL]), dyn[DT_RELSZ], 2);
do_relocs(p, laddr(p, dyn[DT_RELA]), dyn[DT_RELASZ], 3);
-
- if (head != &ldso && p->relro_start != p->relro_end &&
- mprotect(laddr(p, p->relro_start), p->relro_end-p->relro_start, PROT_READ)
- && errno != ENOSYS) {
- error("Error relocating %s: RELRO protection failed: %m",
- p->name);
- if (runtime) longjmp(*rtld_fail, 1);
+ if (!DL_FDPIC)
+ do_relr_relocs(p, laddr(p, dyn[DT_RELR]), dyn[DT_RELRSZ]);
+
+ if (head != &ldso && p->relro_start != p->relro_end) {
+ long ret = __syscall(SYS_mprotect, laddr(p, p->relro_start),
+ p->relro_end-p->relro_start, PROT_READ);
+ if (ret != 0 && ret != -ENOSYS) {
+ error("Error relocating %s: RELRO protection failed: %m",
+ p->name);
+ if (runtime) longjmp(*rtld_fail, 1);
+ }
}
p->relocated = 1;
@@ -1561,6 +1629,8 @@ void __libc_start_init(void)
static void dl_debug_state(void)
{
+ if (exe_dl_debug_state)
+ exe_dl_debug_state();
}
weak_alias(dl_debug_state, _dl_debug_state);
@@ -1667,6 +1737,8 @@ hidden void __dls2(unsigned char *base, size_t *sp)
ldso.phnum = ehdr->e_phnum;
ldso.phdr = laddr(&ldso, ehdr->e_phoff);
ldso.phentsize = ehdr->e_phentsize;
+ ldso.elfmachine = ehdr->e_machine;
+ ldso.elfclass = ehdr->e_ident[EI_CLASS];
kernel_mapped_dso(&ldso);
decode_dyn(&ldso);
@@ -1759,6 +1831,9 @@ void __dls3(size_t *sp, size_t *auxv)
env_preload = getenv("LD_PRELOAD");
}
+ /* Activate error handler function */
+ error = error_impl;
+
/* If the main program was already loaded by the kernel,
* AT_PHDR will point to some location other than the dynamic
* linker's program headers. */
@@ -1983,6 +2058,12 @@ void __dls3(size_t *sp, size_t *auxv)
if (find_sym(head, "aligned_alloc", 1).dso != &ldso)
__aligned_alloc_replaced = 1;
+ /* Determine if another DSO is providing the _dl_debug_state symbol
+ * and forward calls to it. */
+ struct symdef debug_sym = find_sym(head, "_dl_debug_state", 1);
+ if (debug_sym.dso != &ldso)
+ exe_dl_debug_state = (void (*)(void))laddr(debug_sym.dso, debug_sym.sym->st_value);
+
/* Switch to runtime mode: any further failures in the dynamic
* linker are a reportable failure rather than a fatal startup
* error. */
@@ -2348,7 +2429,7 @@ int dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void
return ret;
}
-static void error(const char *fmt, ...)
+static void error_impl(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
@@ -2362,3 +2443,7 @@ static void error(const char *fmt, ...)
__dl_vseterr(fmt, ap);
va_end(ap);
}
+
+static void error_noop(const char *fmt, ...)
+{
+}
diff --git a/sources.bp b/sources.bp
index 0e63694b..dd9fbdab 100644
--- a/sources.bp
+++ b/sources.bp
@@ -1118,7 +1118,6 @@ cc_defaults {
"src/thread/pthread_setschedprio.c",
"src/thread/pthread_setspecific.c",
"src/thread/pthread_sigmask.c",
- "src/thread/pthread_sigqueue.c",
"src/thread/pthread_spin_destroy.c",
"src/thread/pthread_spin_init.c",
"src/thread/pthread_spin_lock.c",
@@ -1353,6 +1352,7 @@ cc_defaults {
"src/math/aarch64/sqrtf.c",
"src/math/aarch64/trunc.c",
"src/math/aarch64/truncf.c",
+ "src/process/aarch64/vfork.s",
"src/setjmp/aarch64/longjmp.s",
"src/setjmp/aarch64/setjmp.s",
"src/signal/aarch64/restore.s",
@@ -1395,6 +1395,7 @@ cc_defaults {
"src/math/sqrtf.c",
"src/math/trunc.c",
"src/math/truncf.c",
+ "src/process/vfork.c",
"src/setjmp/longjmp.c",
"src/setjmp/setjmp.c",
"src/signal/restore.c",
diff --git a/src/conf/confstr.c b/src/conf/confstr.c
index 02cb1aa2..3d417284 100644
--- a/src/conf/confstr.c
+++ b/src/conf/confstr.c
@@ -7,7 +7,7 @@ size_t confstr(int name, char *buf, size_t len)
const char *s = "";
if (!name) {
s = "/bin:/usr/bin";
- } else if ((name&~4U)!=1 && name-_CS_POSIX_V6_ILP32_OFF32_CFLAGS>33U) {
+ } else if ((name&~4U)!=1 && name-_CS_POSIX_V6_ILP32_OFF32_CFLAGS>35U) {
errno = EINVAL;
return 0;
}
diff --git a/src/conf/sysconf.c b/src/conf/sysconf.c
index 3baaed32..60d3e745 100644
--- a/src/conf/sysconf.c
+++ b/src/conf/sysconf.c
@@ -4,6 +4,7 @@
#include <sys/resource.h>
#include <signal.h>
#include <sys/sysinfo.h>
+#include <sys/auxv.h>
#include "syscall.h"
#include "libc.h"
@@ -19,6 +20,8 @@
#define JT_AVPHYS_PAGES JT(9)
#define JT_ZERO JT(10)
#define JT_DELAYTIMER_MAX JT(11)
+#define JT_MINSIGSTKSZ JT(12)
+#define JT_SIGSTKSZ JT(13)
#define RLIM(x) (-32768|(RLIMIT_ ## x))
@@ -165,6 +168,9 @@ long sysconf(int name)
[_SC_XOPEN_STREAMS] = JT_ZERO,
[_SC_THREAD_ROBUST_PRIO_INHERIT] = -1,
[_SC_THREAD_ROBUST_PRIO_PROTECT] = -1,
+
+ [_SC_MINSIGSTKSZ] = JT_MINSIGSTKSZ,
+ [_SC_SIGSTKSZ] = JT_SIGSTKSZ,
};
if (name >= sizeof(values)/sizeof(values[0]) || !values[name]) {
@@ -212,6 +218,13 @@ long sysconf(int name)
mem *= si.mem_unit;
mem /= PAGE_SIZE;
return (mem > LONG_MAX) ? LONG_MAX : mem;
+ case JT_MINSIGSTKSZ & 255:
+ case JT_SIGSTKSZ & 255: ;
+ long val = __getauxval(AT_MINSIGSTKSZ);
+ if (val < MINSIGSTKSZ) val = MINSIGSTKSZ;
+ if (values[name] == JT_SIGSTKSZ)
+ val += SIGSTKSZ - MINSIGSTKSZ;
+ return val;
case JT_ZERO & 255:
return 0;
}
diff --git a/src/include/sys/stat.h b/src/include/sys/stat.h
new file mode 100644
index 00000000..59339bee
--- /dev/null
+++ b/src/include/sys/stat.h
@@ -0,0 +1,9 @@
+#ifndef SYS_STAT_H
+#define SYS_STAT_H
+
+#include "../../../include/sys/stat.h"
+
+hidden int __fstat(int, struct stat *);
+hidden int __fstatat(int, const char *restrict, struct stat *restrict, int);
+
+#endif
diff --git a/src/internal/dynlink.h b/src/internal/dynlink.h
index 51c0639f..830354eb 100644
--- a/src/internal/dynlink.h
+++ b/src/internal/dynlink.h
@@ -93,7 +93,7 @@ struct fdpic_dummy_loadmap {
#endif
#define AUX_CNT 32
-#define DYN_CNT 32
+#define DYN_CNT 37
typedef void (*stage2_func)(unsigned char *, size_t *);
diff --git a/src/internal/syscall.h b/src/internal/syscall.h
index d5f294d4..4a446157 100644
--- a/src/internal/syscall.h
+++ b/src/internal/syscall.h
@@ -58,7 +58,7 @@ hidden long __syscall_ret(unsigned long),
#define __syscall_cp(...) __SYSCALL_DISP(__syscall_cp,__VA_ARGS__)
#define syscall_cp(...) __syscall_ret(__syscall_cp(__VA_ARGS__))
-static inline long __alt_socketcall(int sys, int sock, int cp, long a, long b, long c, long d, long e, long f)
+static inline long __alt_socketcall(int sys, int sock, int cp, syscall_arg_t a, syscall_arg_t b, syscall_arg_t c, syscall_arg_t d, syscall_arg_t e, syscall_arg_t f)
{
long r;
if (cp) r = __syscall_cp(sys, a, b, c, d, e, f);
@@ -71,9 +71,9 @@ static inline long __alt_socketcall(int sys, int sock, int cp, long a, long b, l
return r;
}
#define __socketcall(nm, a, b, c, d, e, f) __alt_socketcall(SYS_##nm, __SC_##nm, 0, \
- (long)(a), (long)(b), (long)(c), (long)(d), (long)(e), (long)(f))
+ __scc(a), __scc(b), __scc(c), __scc(d), __scc(e), __scc(f))
#define __socketcall_cp(nm, a, b, c, d, e, f) __alt_socketcall(SYS_##nm, __SC_##nm, 1, \
- (long)(a), (long)(b), (long)(c), (long)(d), (long)(e), (long)(f))
+ __scc(a), __scc(b), __scc(c), __scc(d), __scc(e), __scc(f))
/* fixup legacy 16-bit junk */
@@ -201,43 +201,43 @@ static inline long __alt_socketcall(int sys, int sock, int cp, long a, long b, l
#define SYS_sendfile SYS_sendfile64
#endif
-#ifndef SYS_timer_settime
+#ifdef SYS_timer_settime32
#define SYS_timer_settime SYS_timer_settime32
#endif
-#ifndef SYS_timer_gettime
+#ifdef SYS_timer_gettime32
#define SYS_timer_gettime SYS_timer_gettime32
#endif
-#ifndef SYS_timerfd_settime
+#ifdef SYS_timerfd_settime32
#define SYS_timerfd_settime SYS_timerfd_settime32
#endif
-#ifndef SYS_timerfd_gettime
+#ifdef SYS_timerfd_gettime32
#define SYS_timerfd_gettime SYS_timerfd_gettime32
#endif
-#ifndef SYS_clock_settime
+#ifdef SYS_clock_settime32
#define SYS_clock_settime SYS_clock_settime32
#endif
-#ifndef SYS_clock_gettime
+#ifdef SYS_clock_gettime32
#define SYS_clock_gettime SYS_clock_gettime32
#endif
-#ifndef SYS_clock_getres
+#ifdef SYS_clock_getres_time32
#define SYS_clock_getres SYS_clock_getres_time32
#endif
-#ifndef SYS_clock_nanosleep
+#ifdef SYS_clock_nanosleep_time32
#define SYS_clock_nanosleep SYS_clock_nanosleep_time32
#endif
-#ifndef SYS_gettimeofday
+#ifdef SYS_gettimeofday_time32
#define SYS_gettimeofday SYS_gettimeofday_time32
#endif
-#ifndef SYS_settimeofday
+#ifdef SYS_settimeofday_time32
#define SYS_settimeofday SYS_settimeofday_time32
#endif
diff --git a/src/linux/epoll.c b/src/linux/epoll.c
index 93baa814..e56e8f4c 100644
--- a/src/linux/epoll.c
+++ b/src/linux/epoll.c
@@ -5,6 +5,7 @@
int epoll_create(int size)
{
+ if (size<=0) return __syscall_ret(-EINVAL);
return epoll_create1(0);
}
diff --git a/src/linux/membarrier.c b/src/linux/membarrier.c
index 343f7360..f64fe7e1 100644
--- a/src/linux/membarrier.c
+++ b/src/linux/membarrier.c
@@ -35,7 +35,7 @@ int __membarrier(int cmd, int flags)
__tl_lock();
sem_init(&barrier_sem, 0, 0);
struct sigaction sa = {
- .sa_flags = SA_RESTART,
+ .sa_flags = SA_RESTART | SA_ONSTACK,
.sa_handler = bcast_barrier
};
memset(&sa.sa_mask, -1, sizeof sa.sa_mask);
diff --git a/src/misc/getopt.c b/src/misc/getopt.c
index c3f66995..f8df7aea 100644
--- a/src/misc/getopt.c
+++ b/src/misc/getopt.c
@@ -25,7 +25,7 @@ void __getopt_msg(const char *a, const char *b, const char *c, size_t l)
FUNLOCK(f);
}
-int getopt(int argc, char * const argv[], const char *optstring)
+int __posix_getopt(int argc, char * const argv[], const char *optstring)
{
int i;
wchar_t c, d;
@@ -101,5 +101,3 @@ int getopt(int argc, char * const argv[], const char *optstring)
}
return c;
}
-
-weak_alias(getopt, __posix_getopt);
diff --git a/src/misc/getopt_long.c b/src/misc/getopt_long.c
index 6949ab1c..117d1a1d 100644
--- a/src/misc/getopt_long.c
+++ b/src/misc/getopt_long.c
@@ -9,6 +9,8 @@
extern int __optpos, __optreset;
+int __posix_getopt(int argc, char * const argv[], const char *optstring);
+
static void permute(char *const *argv, int dest, int src)
{
char **av = (char **)argv;
@@ -134,7 +136,7 @@ static int __getopt_long_core(int argc, char *const *argv, const char *optstring
return '?';
}
}
- return getopt(argc, argv, optstring);
+ return __posix_getopt(argc, argv, optstring);
}
int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx)
@@ -146,3 +148,20 @@ int getopt_long_only(int argc, char *const *argv, const char *optstring, const s
{
return __getopt_long(argc, argv, optstring, longopts, idx, 1);
}
+
+/* ANDROID CHANGE: implement getopt via getopt_long to continue parsing options
+ * after the first non-option argument to match the user visible behavior of
+ * glibc.
+ */
+int getopt(int argc, char * const argv[], const char *optstring)
+{
+ static int posixly_correct = -1;
+
+ if (posixly_correct == -1 || __optreset)
+ posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
+
+ if (posixly_correct)
+ return __posix_getopt(argc, argv, optstring);
+ else
+ return __getopt_long(argc, argv, optstring, NULL, NULL, 0);
+}
diff --git a/src/misc/getrlimit.c b/src/misc/getrlimit.c
index 2ab2f0f4..bf676307 100644
--- a/src/misc/getrlimit.c
+++ b/src/misc/getrlimit.c
@@ -6,12 +6,13 @@
int getrlimit(int resource, struct rlimit *rlim)
{
- unsigned long k_rlim[2];
int ret = syscall(SYS_prlimit64, 0, resource, 0, rlim);
if (!ret) {
FIX(rlim->rlim_cur);
FIX(rlim->rlim_max);
}
+#ifdef SYS_getrlimit
+ unsigned long k_rlim[2];
if (!ret || errno != ENOSYS)
return ret;
if (syscall(SYS_getrlimit, resource, k_rlim) < 0)
@@ -21,6 +22,9 @@ int getrlimit(int resource, struct rlimit *rlim)
FIX(rlim->rlim_cur);
FIX(rlim->rlim_max);
return 0;
+#else
+ return ret;
+#endif
}
weak_alias(getrlimit, getrlimit64);
diff --git a/src/misc/mntent.c b/src/misc/mntent.c
index eabb8200..d404fbe3 100644
--- a/src/misc/mntent.c
+++ b/src/misc/mntent.c
@@ -2,6 +2,7 @@
#include <string.h>
#include <mntent.h>
#include <errno.h>
+#include <limits.h>
static char *internal_buf;
static size_t internal_bufsize;
@@ -21,7 +22,8 @@ int endmntent(FILE *f)
struct mntent *getmntent_r(FILE *f, struct mntent *mnt, char *linebuf, int buflen)
{
- int cnt, n[8], use_internal = (linebuf == SENTINEL);
+ int n[8], use_internal = (linebuf == SENTINEL);
+ size_t len, i;
mnt->mnt_freq = 0;
mnt->mnt_passno = 0;
@@ -39,10 +41,14 @@ struct mntent *getmntent_r(FILE *f, struct mntent *mnt, char *linebuf, int bufle
errno = ERANGE;
return 0;
}
- cnt = sscanf(linebuf, " %n%*s%n %n%*s%n %n%*s%n %n%*s%n %d %d",
+
+ len = strlen(linebuf);
+ if (len > INT_MAX) continue;
+ for (i = 0; i < sizeof n / sizeof *n; i++) n[i] = len;
+ sscanf(linebuf, " %n%*s%n %n%*s%n %n%*s%n %n%*s%n %d %d",
n, n+1, n+2, n+3, n+4, n+5, n+6, n+7,
&mnt->mnt_freq, &mnt->mnt_passno);
- } while (cnt < 2 || linebuf[n[0]] == '#');
+ } while (linebuf[n[0]] == '#' || n[1]==len);
linebuf[n[1]] = 0;
linebuf[n[3]] = 0;
diff --git a/src/misc/setrlimit.c b/src/misc/setrlimit.c
index 8340aee0..5b713cf3 100644
--- a/src/misc/setrlimit.c
+++ b/src/misc/setrlimit.c
@@ -12,12 +12,14 @@ struct ctx {
int err;
};
+#ifdef SYS_setrlimit
static void do_setrlimit(void *p)
{
struct ctx *c = p;
if (c->err>0) return;
c->err = -__syscall(SYS_setrlimit, c->res, c->lim);
}
+#endif
int setrlimit(int resource, const struct rlimit *rlim)
{
@@ -29,6 +31,7 @@ int setrlimit(int resource, const struct rlimit *rlim)
rlim = &tmp;
}
int ret = __syscall(SYS_prlimit64, 0, resource, rlim, 0);
+#ifdef SYS_setrlimit
if (ret != -ENOSYS) return __syscall_ret(ret);
struct ctx c = {
@@ -42,6 +45,9 @@ int setrlimit(int resource, const struct rlimit *rlim)
return -1;
}
return 0;
+#else
+ return __syscall_ret(ret);
+#endif
}
weak_alias(setrlimit, setrlimit64);
diff --git a/src/network/getaddrinfo.c b/src/network/getaddrinfo.c
index efaab306..9df045f6 100644
--- a/src/network/getaddrinfo.c
+++ b/src/network/getaddrinfo.c
@@ -66,9 +66,11 @@ int getaddrinfo(const char *restrict host, const char *restrict serv, const stru
pthread_setcancelstate(
PTHREAD_CANCEL_DISABLE, &cs);
int r = connect(s, ta[i], tl[i]);
+ int saved_errno = errno;
pthread_setcancelstate(cs, 0);
close(s);
if (!r) continue;
+ errno = saved_errno;
}
switch (errno) {
case EADDRNOTAVAIL:
diff --git a/src/network/gethostbyaddr_r.c b/src/network/gethostbyaddr_r.c
index 0f1e61aa..ceaf3935 100644
--- a/src/network/gethostbyaddr_r.c
+++ b/src/network/gethostbyaddr_r.c
@@ -54,10 +54,11 @@ int gethostbyaddr_r(const void *a, socklen_t l, int af,
case EAI_OVERFLOW:
return ERANGE;
default:
- case EAI_MEMORY:
- case EAI_SYSTEM:
case EAI_FAIL:
*err = NO_RECOVERY;
+ return EBADMSG;
+ case EAI_SYSTEM:
+ *err = NO_RECOVERY;
return errno;
case 0:
break;
diff --git a/src/network/gethostbyname2_r.c b/src/network/gethostbyname2_r.c
index fc894877..c9f3acc4 100644
--- a/src/network/gethostbyname2_r.c
+++ b/src/network/gethostbyname2_r.c
@@ -22,7 +22,7 @@ int gethostbyname2_r(const char *name, int af,
if (cnt<0) switch (cnt) {
case EAI_NONAME:
*err = HOST_NOT_FOUND;
- return ENOENT;
+ return 0;
case EAI_AGAIN:
*err = TRY_AGAIN;
return EAGAIN;
@@ -30,7 +30,6 @@ int gethostbyname2_r(const char *name, int af,
case EAI_FAIL:
*err = NO_RECOVERY;
return EBADMSG;
- case EAI_MEMORY:
case EAI_SYSTEM:
*err = NO_RECOVERY;
return errno;
diff --git a/src/network/lookup_name.c b/src/network/lookup_name.c
index aa558c19..bec6ba22 100644
--- a/src/network/lookup_name.c
+++ b/src/network/lookup_name.c
@@ -153,8 +153,11 @@ static int name_from_dns(struct address buf[static MAXADDRS], char canon[static
qlens[nq] = __res_mkquery(0, name, 1, afrr[i].rr,
0, 0, 0, qbuf[nq], sizeof *qbuf);
if (qlens[nq] == -1)
- return EAI_NONAME;
+ return 0;
qbuf[nq][3] = 0; /* don't need AD flag */
+ /* Ensure query IDs are distinct. */
+ if (nq && qbuf[nq][0] == qbuf[0][0])
+ qbuf[nq][0]++;
nq++;
}
}
diff --git a/src/network/netlink.h b/src/network/netlink.h
index 38acb178..873fabe2 100644
--- a/src/network/netlink.h
+++ b/src/network/netlink.h
@@ -86,7 +86,7 @@ struct ifaddrmsg {
#define RTA_DATALEN(rta) ((rta)->rta_len-sizeof(struct rtattr))
#define RTA_DATAEND(rta) ((char*)(rta)+(rta)->rta_len)
#define RTA_NEXT(rta) (struct rtattr*)((char*)(rta)+NETLINK_ALIGN((rta)->rta_len))
-#define RTA_OK(nlh,end) ((char*)(end)-(char*)(rta) >= sizeof(struct rtattr))
+#define RTA_OK(rta,end) ((char*)(end)-(char*)(rta) >= sizeof(struct rtattr))
#define NLMSG_RTA(nlh,len) ((void*)((char*)(nlh)+sizeof(struct nlmsghdr)+NETLINK_ALIGN(len)))
#define NLMSG_RTAOK(rta,nlh) RTA_OK(rta,NLMSG_DATAEND(nlh))
diff --git a/src/network/res_mkquery.c b/src/network/res_mkquery.c
index 33f50cb9..614bf786 100644
--- a/src/network/res_mkquery.c
+++ b/src/network/res_mkquery.c
@@ -13,6 +13,7 @@ int __res_mkquery(int op, const char *dname, int class, int type,
int n;
if (l && dname[l-1]=='.') l--;
+ if (l && dname[l-1]=='.') return -1;
n = 17+l+!!l;
if (l>253 || buflen<n || op>15u || class>255u || type>255u)
return -1;
diff --git a/src/network/res_msend.c b/src/network/res_msend.c
index 3e018009..9adaea13 100644
--- a/src/network/res_msend.c
+++ b/src/network/res_msend.c
@@ -68,14 +68,20 @@ int __res_msend_rc(int nqueries, const unsigned char *const *queries,
}
/* Get local address and open/bind a socket */
- sa.sin.sin_family = family;
fd = socket(family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
/* Handle case where system lacks IPv6 support */
if (fd < 0 && family == AF_INET6 && errno == EAFNOSUPPORT) {
+ for (i=0; i<nns && conf->ns[nns].family == AF_INET6; i++);
+ if (i==nns) {
+ pthread_setcancelstate(cs, 0);
+ return -1;
+ }
fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
family = AF_INET;
+ sl = sizeof sa.sin;
}
+ sa.sin.sin_family = family;
if (fd < 0 || bind(fd, (void *)&sa, sl) < 0) {
if (fd >= 0) close(fd);
pthread_setcancelstate(cs, 0);
diff --git a/src/process/aarch64/vfork.s b/src/process/aarch64/vfork.s
new file mode 100644
index 00000000..429bec8c
--- /dev/null
+++ b/src/process/aarch64/vfork.s
@@ -0,0 +1,9 @@
+.global vfork
+.type vfork,%function
+vfork:
+ mov x8, 220 // SYS_clone
+ mov x0, 0x4111 // SIGCHLD | CLONE_VM | CLONE_VFORK
+ mov x1, 0
+ svc 0
+ .hidden __syscall_ret
+ b __syscall_ret
diff --git a/src/stat/fchmodat.c b/src/stat/fchmodat.c
index 4ee00b0a..bc581050 100644
--- a/src/stat/fchmodat.c
+++ b/src/stat/fchmodat.c
@@ -2,7 +2,6 @@
#include <fcntl.h>
#include <errno.h>
#include "syscall.h"
-#include "kstat.h"
int fchmodat(int fd, const char *path, mode_t mode, int flag)
{
@@ -11,12 +10,12 @@ int fchmodat(int fd, const char *path, mode_t mode, int flag)
if (flag != AT_SYMLINK_NOFOLLOW)
return __syscall_ret(-EINVAL);
- struct kstat st;
+ struct stat st;
int ret, fd2;
char proc[15+3*sizeof(int)];
- if ((ret = __syscall(SYS_fstatat, fd, path, &st, flag)))
- return __syscall_ret(ret);
+ if (fstatat(fd, path, &st, flag))
+ return -1;
if (S_ISLNK(st.st_mode))
return __syscall_ret(-EOPNOTSUPP);
@@ -27,12 +26,12 @@ int fchmodat(int fd, const char *path, mode_t mode, int flag)
}
__procfdname(proc, fd2);
- ret = __syscall(SYS_fstatat, AT_FDCWD, proc, &st, 0);
+ ret = stat(proc, &st);
if (!ret) {
- if (S_ISLNK(st.st_mode)) ret = -EOPNOTSUPP;
- else ret = __syscall(SYS_fchmodat, AT_FDCWD, proc, mode);
+ if (S_ISLNK(st.st_mode)) ret = __syscall_ret(-EOPNOTSUPP);
+ else ret = syscall(SYS_fchmodat, AT_FDCWD, proc, mode);
}
__syscall(SYS_close, fd2);
- return __syscall_ret(ret);
+ return ret;
}
diff --git a/src/stat/fstat.c b/src/stat/fstat.c
index 9bbb46de..27db0ccb 100644
--- a/src/stat/fstat.c
+++ b/src/stat/fstat.c
@@ -4,12 +4,14 @@
#include <fcntl.h>
#include "syscall.h"
-int fstat(int fd, struct stat *st)
+int __fstat(int fd, struct stat *st)
{
if (fd<0) return __syscall_ret(-EBADF);
- return fstatat(fd, "", st, AT_EMPTY_PATH);
+ return __fstatat(fd, "", st, AT_EMPTY_PATH);
}
+weak_alias(__fstat, fstat);
+
#if !_REDIR_TIME64
weak_alias(fstat, fstat64);
#endif
diff --git a/src/stat/fstatat.c b/src/stat/fstatat.c
index de165b5c..74c51cf5 100644
--- a/src/stat/fstatat.c
+++ b/src/stat/fstatat.c
@@ -6,7 +6,6 @@
#include <stdint.h>
#include <sys/sysmacros.h>
#include "syscall.h"
-#include "kstat.h"
struct statx {
uint32_t stx_mask;
@@ -69,6 +68,10 @@ static int fstatat_statx(int fd, const char *restrict path, struct stat *restric
return 0;
}
+#ifdef SYS_fstatat
+
+#include "kstat.h"
+
static int fstatat_kstat(int fd, const char *restrict path, struct stat *restrict st, int flag)
{
int ret;
@@ -130,18 +133,25 @@ static int fstatat_kstat(int fd, const char *restrict path, struct stat *restric
return 0;
}
+#endif
-int fstatat(int fd, const char *restrict path, struct stat *restrict st, int flag)
+int __fstatat(int fd, const char *restrict path, struct stat *restrict st, int flag)
{
int ret;
+#ifdef SYS_fstatat
if (sizeof((struct kstat){0}.st_atime_sec) < sizeof(time_t)) {
ret = fstatat_statx(fd, path, st, flag);
if (ret!=-ENOSYS) return __syscall_ret(ret);
}
ret = fstatat_kstat(fd, path, st, flag);
+#else
+ ret = fstatat_statx(fd, path, st, flag);
+#endif
return __syscall_ret(ret);
}
+weak_alias(__fstatat, fstatat);
+
#if !_REDIR_TIME64
weak_alias(fstatat, fstatat64);
#endif
diff --git a/src/stdio/freopen.c b/src/stdio/freopen.c
index 615d4b47..96bfbb42 100644
--- a/src/stdio/freopen.c
+++ b/src/stdio/freopen.c
@@ -40,6 +40,8 @@ FILE *freopen(const char *restrict filename, const char *restrict mode, FILE *re
fclose(f2);
}
+ f->mode = 0;
+ f->locale = 0;
FUNLOCK(f);
return f;
diff --git a/src/stdio/open_wmemstream.c b/src/stdio/open_wmemstream.c
index ed1b561d..b8ae4a79 100644
--- a/src/stdio/open_wmemstream.c
+++ b/src/stdio/open_wmemstream.c
@@ -40,8 +40,12 @@ fail:
static size_t wms_write(FILE *f, const unsigned char *buf, size_t len)
{
struct cookie *c = f->cookie;
- size_t len2;
+ size_t len2 = f->wpos - f->wbase;
wchar_t *newbuf;
+ if (len2) {
+ f->wpos = f->wbase;
+ if (wms_write(f, f->wbase, len2) < len2) return 0;
+ }
if (len + c->pos >= c->space) {
len2 = 2*c->space+1 | c->pos+len+1;
if (len2 > SSIZE_MAX/4) return 0;
diff --git a/src/stdio/tempnam.c b/src/stdio/tempnam.c
index 565df6b6..0c65b1f0 100644
--- a/src/stdio/tempnam.c
+++ b/src/stdio/tempnam.c
@@ -6,7 +6,6 @@
#include <string.h>
#include <stdlib.h>
#include "syscall.h"
-#include "kstat.h"
#define MAXTRIES 100
@@ -37,11 +36,10 @@ char *tempnam(const char *dir, const char *pfx)
for (try=0; try<MAXTRIES; try++) {
__randname(s+l-6);
-#ifdef SYS_lstat
- r = __syscall(SYS_lstat, s, &(struct kstat){0});
+#ifdef SYS_readlink
+ r = __syscall(SYS_readlink, s, (char[1]){0}, 1);
#else
- r = __syscall(SYS_fstatat, AT_FDCWD, s,
- &(struct kstat){0}, AT_SYMLINK_NOFOLLOW);
+ r = __syscall(SYS_readlinkat, AT_FDCWD, s, (char[1]){0}, 1);
#endif
if (r == -ENOENT) return strdup(s);
}
diff --git a/src/stdio/tmpnam.c b/src/stdio/tmpnam.c
index d667a836..71dc8bb1 100644
--- a/src/stdio/tmpnam.c
+++ b/src/stdio/tmpnam.c
@@ -5,7 +5,6 @@
#include <string.h>
#include <stdlib.h>
#include "syscall.h"
-#include "kstat.h"
#define MAXTRIES 100
@@ -17,11 +16,10 @@ char *tmpnam(char *buf)
int r;
for (try=0; try<MAXTRIES; try++) {
__randname(s+12);
-#ifdef SYS_lstat
- r = __syscall(SYS_lstat, s, &(struct kstat){0});
+#ifdef SYS_readlink
+ r = __syscall(SYS_readlink, s, (char[1]){0}, 1);
#else
- r = __syscall(SYS_fstatat, AT_FDCWD, s,
- &(struct kstat){0}, AT_SYMLINK_NOFOLLOW);
+ r = __syscall(SYS_readlinkat, AT_FDCWD, s, (char[1]){0}, 1);
#endif
if (r == -ENOENT) return strcpy(buf ? buf : internal, s);
}
diff --git a/src/stdlib/qsort_nr.c b/src/stdlib/qsort_nr.c
index efe7ccec..8ffe71d0 100644
--- a/src/stdlib/qsort_nr.c
+++ b/src/stdlib/qsort_nr.c
@@ -10,5 +10,5 @@ static int wrapper_cmp(const void *v1, const void *v2, void *cmp)
void qsort(void *base, size_t nel, size_t width, cmpfun cmp)
{
- __qsort_r(base, nel, width, wrapper_cmp, cmp);
+ __qsort_r(base, nel, width, wrapper_cmp, (void *)cmp);
}
diff --git a/src/temp/__randname.c b/src/temp/__randname.c
index 2bce37a0..e9b970f1 100644
--- a/src/temp/__randname.c
+++ b/src/temp/__randname.c
@@ -1,5 +1,6 @@
#include <time.h>
#include <stdint.h>
+#include "pthread_impl.h"
/* This assumes that a check for the
template size has already been made */
@@ -10,7 +11,7 @@ char *__randname(char *template)
unsigned long r;
__clock_gettime(CLOCK_REALTIME, &ts);
- r = ts.tv_nsec*65537 ^ (uintptr_t)&ts / 16 + (uintptr_t)template;
+ r = ts.tv_sec + ts.tv_nsec + __pthread_self()->tid * 65537UL;
for (i=0; i<6; i++, r>>=5)
template[i] = 'A'+(r&15)+(r&16)*2;
diff --git a/src/thread/pthread_cancel.c b/src/thread/pthread_cancel.c
index 2f9d5e97..2d3a98ea 100644
--- a/src/thread/pthread_cancel.c
+++ b/src/thread/pthread_cancel.c
@@ -77,7 +77,7 @@ void __testcancel()
static void init_cancellation()
{
struct sigaction sa = {
- .sa_flags = SA_SIGINFO | SA_RESTART,
+ .sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK,
.sa_sigaction = cancel_handler
};
memset(&sa.sa_mask, -1, _NSIG/8);
diff --git a/src/thread/pthread_sigqueue.c b/src/thread/pthread_sigqueue.c
deleted file mode 100644
index 9c347933..00000000
--- a/src/thread/pthread_sigqueue.c
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <signal.h>
-#include <string.h>
-#include <unistd.h>
-#include "pthread_impl.h"
-#include "lock.h"
-
-int pthread_sigqueue(pthread_t t, int sig, const union sigval value)
-{
- siginfo_t si;
- int r;
- memset(&si, 0, sizeof si);
- si.si_signo = sig;
- si.si_code = SI_QUEUE;
- si.si_value = value;
- si.si_uid = getuid();
- si.si_pid = getpid();
- LOCK(t->killlock);
- r = t->tid ? -__syscall(SYS_rt_tgsigqueueinfo, si.si_pid, t->tid, sig, &si)
- : (sig+0U >= _NSIG ? EINVAL : 0);
- UNLOCK(t->killlock);
- return r;
-}
diff --git a/src/thread/synccall.c b/src/thread/synccall.c
index d58c851f..a6b177c0 100644
--- a/src/thread/synccall.c
+++ b/src/thread/synccall.c
@@ -45,7 +45,7 @@ void __synccall(void (*func)(void *), void *ctx)
{
sigset_t oldmask;
int cs, i, r;
- struct sigaction sa = { .sa_flags = SA_RESTART, .sa_handler = handler };
+ struct sigaction sa = { .sa_flags = SA_RESTART | SA_ONSTACK, .sa_handler = handler };
pthread_t self = __pthread_self(), td;
int count = 0;
diff --git a/src/time/__map_file.c b/src/time/__map_file.c
index d3cefa82..c2b29fe8 100644
--- a/src/time/__map_file.c
+++ b/src/time/__map_file.c
@@ -2,15 +2,14 @@
#include <fcntl.h>
#include <sys/stat.h>
#include "syscall.h"
-#include "kstat.h"
const char unsigned *__map_file(const char *pathname, size_t *size)
{
- struct kstat st;
+ struct stat st;
const unsigned char *map = MAP_FAILED;
int fd = sys_open(pathname, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0) return 0;
- if (!syscall(SYS_fstat, fd, &st)) {
+ if (!__fstat(fd, &st)) {
map = __mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
*size = st.st_size;
}
diff --git a/src/time/clock_getcpuclockid.c b/src/time/clock_getcpuclockid.c
index 8a0e2d4c..bce1e8ab 100644
--- a/src/time/clock_getcpuclockid.c
+++ b/src/time/clock_getcpuclockid.c
@@ -8,6 +8,7 @@ int clock_getcpuclockid(pid_t pid, clockid_t *clk)
struct timespec ts;
clockid_t id = (-pid-1)*8U + 2;
int ret = __syscall(SYS_clock_getres, id, &ts);
+ if (ret == -EINVAL) ret = -ESRCH;
if (ret) return -ret;
*clk = id;
return 0;
diff --git a/src/time/clock_gettime.c b/src/time/clock_gettime.c
index 3e1d0975..4d2ec22f 100644
--- a/src/time/clock_gettime.c
+++ b/src/time/clock_gettime.c
@@ -42,6 +42,9 @@ static int cgt_init(clockid_t clk, struct timespec *ts)
p = cgt_time32_wrap;
}
}
+#ifdef VDSO_CGT_WORKAROUND
+ if (!__vdsosym(VDSO_CGT32_VER, VDSO_CGT32_SYM)) p = 0;
+#endif
#endif
int (*f)(clockid_t, struct timespec *) =
(int (*)(clockid_t, struct timespec *))p;
@@ -80,10 +83,12 @@ int __clock_gettime(clockid_t clk, struct timespec *ts)
return __syscall_ret(r);
long ts32[2];
r = __syscall(SYS_clock_gettime, clk, ts32);
+#ifdef SYS_gettimeofday
if (r==-ENOSYS && clk==CLOCK_REALTIME) {
r = __syscall(SYS_gettimeofday, ts32, 0);
ts32[1] *= 1000;
}
+#endif
if (!r) {
ts->tv_sec = ts32[0];
ts->tv_nsec = ts32[1];
@@ -92,6 +97,7 @@ int __clock_gettime(clockid_t clk, struct timespec *ts)
return __syscall_ret(r);
#else
r = __syscall(SYS_clock_gettime, clk, ts);
+#ifdef SYS_gettimeofday
if (r == -ENOSYS) {
if (clk == CLOCK_REALTIME) {
__syscall(SYS_gettimeofday, ts, 0);
@@ -100,6 +106,7 @@ int __clock_gettime(clockid_t clk, struct timespec *ts)
}
r = -EINVAL;
}
+#endif
return __syscall_ret(r);
#endif
}
diff --git a/src/time/timer_create.c b/src/time/timer_create.c
index 4bef2390..cd32c945 100644
--- a/src/time/timer_create.c
+++ b/src/time/timer_create.c
@@ -43,6 +43,8 @@ static void *start(void *arg)
union sigval val = args->sev->sigev_value;
pthread_barrier_wait(&args->b);
+ if (self->cancel)
+ return 0;
for (;;) {
siginfo_t si;
while (sigwaitinfo(SIGTIMER_SET, &si) < 0);
@@ -113,8 +115,10 @@ int timer_create(clockid_t clk, struct sigevent *restrict evp, timer_t *restrict
ksev.sigev_signo = SIGTIMER;
ksev.sigev_notify = SIGEV_THREAD_ID;
ksev.sigev_tid = td->tid;
- if (syscall(SYS_timer_create, clk, &ksev, &timerid) < 0)
+ if (syscall(SYS_timer_create, clk, &ksev, &timerid) < 0) {
timerid = -1;
+ td->cancel = 1;
+ }
td->timer_id = timerid;
pthread_barrier_wait(&args.b);
if (timerid < 0) return -1;