diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 01:01:29 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 01:01:29 +0000 |
commit | 531ce5205104086bc5a9644c9ff4af56e48e99a4 (patch) | |
tree | 28523674f3e6fad32dbec47e22b3ba1d223b80eb | |
parent | 0790ccba6a82147e2fadcc4b94996475512d817a (diff) | |
parent | 23e8e5196456b787d5e78c4744b76946a8535732 (diff) | |
download | musl-android14-mainline-wifi-release.tar.gz |
Snap for 10447354 from 23e8e5196456b787d5e78c4744b76946a8535732 to mainline-wifi-releaseaml_wif_341610000aml_wif_341510000aml_wif_341410080aml_wif_341310010aml_wif_341110010aml_wif_341011010aml_wif_340913010android14-mainline-wifi-release
Change-Id: I19ce127f5adb3e5f155a3e42408b1a1fd97e2edb
50 files changed, 1357 insertions, 279 deletions
@@ -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 @@ -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, ...) +{ +} @@ -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; |