llvm/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp

//===-- sanitizer_linux.cpp -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries and implements linux-specific functions from
// sanitizer_libc.h.
//===----------------------------------------------------------------------===//

#include "sanitizer_platform.h"

#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
    SANITIZER_SOLARIS

#  include "sanitizer_common.h"
#  include "sanitizer_flags.h"
#  include "sanitizer_getauxval.h"
#  include "sanitizer_internal_defs.h"
#  include "sanitizer_libc.h"
#  include "sanitizer_linux.h"
#  include "sanitizer_mutex.h"
#  include "sanitizer_placement_new.h"
#  include "sanitizer_procmaps.h"

#  if SANITIZER_LINUX && !SANITIZER_GO
#    include <asm/param.h>
#  endif

// For mips64, syscall(__NR_stat) fills the buffer in the 'struct kernel_stat'
// format. Struct kernel_stat is defined as 'struct stat' in asm/stat.h. To
// access stat from asm/stat.h, without conflicting with definition in
// sys/stat.h, we use this trick.  sparc64 is similar, using
// syscall(__NR_stat64) and struct kernel_stat64.
#  if SANITIZER_LINUX && (SANITIZER_MIPS64 || SANITIZER_SPARC64)
#    include <asm/unistd.h>
#    include <sys/types.h>
#define stat
#    if SANITIZER_SPARC64
#define stat64
#    endif
#    if SANITIZER_GO
#      undef st_atime
#      undef st_mtime
#      undef st_ctime
#define st_atime
#define st_mtime
#define st_ctime
#    endif
#    include <asm/stat.h>
#    undef stat
#    undef stat64
#  endif

#  include <dlfcn.h>
#  include <errno.h>
#  include <fcntl.h>
#  include <link.h>
#  include <pthread.h>
#  include <sched.h>
#  include <signal.h>
#  include <sys/mman.h>
#  if !SANITIZER_SOLARIS
#    include <sys/ptrace.h>
#  endif
#  include <sys/resource.h>
#  include <sys/stat.h>
#  include <sys/syscall.h>
#  include <sys/time.h>
#  include <sys/types.h>
#  include <ucontext.h>
#  include <unistd.h>

#  if SANITIZER_LINUX
#    include <sys/utsname.h>
#  endif

#  if SANITIZER_LINUX && !SANITIZER_ANDROID
#    include <sys/personality.h>
#  endif

#  if SANITIZER_LINUX && defined(__loongarch__)
#    include <sys/sysmacros.h>
#  endif

#  if SANITIZER_FREEBSD
#    include <machine/atomic.h>
#    include <sys/exec.h>
#    include <sys/procctl.h>
#    include <sys/sysctl.h>
extern "C" {
// <sys/umtx.h> must be included after <errno.h> and <sys/types.h> on
// FreeBSD 9.2 and 10.0.
#    include <sys/umtx.h>
}
#    include <sys/thr.h>
#  endif  // SANITIZER_FREEBSD

#  if SANITIZER_NETBSD
#    include <limits.h>  // For NAME_MAX
#    include <sys/exec.h>
#    include <sys/sysctl.h>
extern struct ps_strings *__ps_strings;
#  endif  // SANITIZER_NETBSD

#  if SANITIZER_SOLARIS
#    include <stddef.h>
#    include <stdlib.h>
#    include <sys/frame.h>
#    include <thread.h>
#define environ
#  endif

extern char **environ;

#  if SANITIZER_LINUX
// <linux/time.h>
struct kernel_timeval {};

// <linux/futex.h> is broken on some linux distributions.
const int FUTEX_WAIT =;
const int FUTEX_WAKE =;
const int FUTEX_PRIVATE_FLAG =;
const int FUTEX_WAIT_PRIVATE =;
const int FUTEX_WAKE_PRIVATE =;
#  endif  // SANITIZER_LINUX

// Are we using 32-bit or 64-bit Linux syscalls?
// x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32
// but it still needs to use 64-bit syscalls.
#  if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \
                          SANITIZER_WORDSIZE == 64 ||                      \
                          (defined(__mips__) && _MIPS_SIM == _ABIN32))
#define SANITIZER_LINUX_USES_64BIT_SYSCALLS
#  else
#define SANITIZER_LINUX_USES_64BIT_SYSCALLS
#  endif

// Note : FreeBSD implemented both Linux and OpenBSD apis.
#  if SANITIZER_LINUX && defined(__NR_getrandom)
#    if !defined(GRND_NONBLOCK)
#define GRND_NONBLOCK
#    endif
#define SANITIZER_USE_GETRANDOM
#  else
#define SANITIZER_USE_GETRANDOM
#  endif  // SANITIZER_LINUX && defined(__NR_getrandom)

#  if SANITIZER_FREEBSD
#define SANITIZER_USE_GETENTROPY
extern "C" void *__sys_mmap(void *addr, size_t len, int prot, int flags, int fd,
                            off_t offset);
#  endif

namespace __sanitizer {

void SetSigProcMask(__sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) {}

// Block asynchronous signals
void BlockSignals(__sanitizer_sigset_t *oldset) {}

ScopedBlockSignals::ScopedBlockSignals(__sanitizer_sigset_t *copy) {}

ScopedBlockSignals::~ScopedBlockSignals() {}

#  if SANITIZER_LINUX && defined(__x86_64__)
#    include "sanitizer_syscall_linux_x86_64.inc"
#  elif SANITIZER_LINUX && SANITIZER_RISCV64
#    include "sanitizer_syscall_linux_riscv64.inc"
#  elif SANITIZER_LINUX && defined(__aarch64__)
#    include "sanitizer_syscall_linux_aarch64.inc"
#  elif SANITIZER_LINUX && defined(__arm__)
#    include "sanitizer_syscall_linux_arm.inc"
#  elif SANITIZER_LINUX && defined(__hexagon__)
#    include "sanitizer_syscall_linux_hexagon.inc"
#  elif SANITIZER_LINUX && SANITIZER_LOONGARCH64
#    include "sanitizer_syscall_linux_loongarch64.inc"
#  else
#    include "sanitizer_syscall_generic.inc"
#  endif

// --------------- sanitizer_libc.h
#  if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
#    if !SANITIZER_S390
uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
                   u64 offset) {}
#    endif  // !SANITIZER_S390

uptr internal_munmap(void *addr, uptr length) {}

#    if SANITIZER_LINUX
uptr internal_mremap(void *old_address, uptr old_size, uptr new_size, int flags,
                     void *new_address) {}
#    endif

int internal_mprotect(void *addr, uptr length, int prot) {}

int internal_madvise(uptr addr, uptr length, int advice) {}

uptr internal_close(fd_t fd) {}

uptr internal_open(const char *filename, int flags) {}

uptr internal_open(const char *filename, int flags, u32 mode) {}

uptr internal_read(fd_t fd, void *buf, uptr count) {}

uptr internal_write(fd_t fd, const void *buf, uptr count) {}

uptr internal_ftruncate(fd_t fd, uptr size) {}

#    if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && SANITIZER_LINUX
static void stat64_to_stat(struct stat64 *in, struct stat *out) {
  internal_memset(out, 0, sizeof(*out));
  out->st_dev = in->st_dev;
  out->st_ino = in->st_ino;
  out->st_mode = in->st_mode;
  out->st_nlink = in->st_nlink;
  out->st_uid = in->st_uid;
  out->st_gid = in->st_gid;
  out->st_rdev = in->st_rdev;
  out->st_size = in->st_size;
  out->st_blksize = in->st_blksize;
  out->st_blocks = in->st_blocks;
  out->st_atime = in->st_atime;
  out->st_mtime = in->st_mtime;
  out->st_ctime = in->st_ctime;
}
#    endif

#    if SANITIZER_LINUX && defined(__loongarch__)
static void statx_to_stat(struct statx *in, struct stat *out) {
  internal_memset(out, 0, sizeof(*out));
  out->st_dev = makedev(in->stx_dev_major, in->stx_dev_minor);
  out->st_ino = in->stx_ino;
  out->st_mode = in->stx_mode;
  out->st_nlink = in->stx_nlink;
  out->st_uid = in->stx_uid;
  out->st_gid = in->stx_gid;
  out->st_rdev = makedev(in->stx_rdev_major, in->stx_rdev_minor);
  out->st_size = in->stx_size;
  out->st_blksize = in->stx_blksize;
  out->st_blocks = in->stx_blocks;
  out->st_atime = in->stx_atime.tv_sec;
  out->st_atim.tv_nsec = in->stx_atime.tv_nsec;
  out->st_mtime = in->stx_mtime.tv_sec;
  out->st_mtim.tv_nsec = in->stx_mtime.tv_nsec;
  out->st_ctime = in->stx_ctime.tv_sec;
  out->st_ctim.tv_nsec = in->stx_ctime.tv_nsec;
}
#    endif

#    if SANITIZER_MIPS64 || SANITIZER_SPARC64
#      if SANITIZER_MIPS64
typedef struct kernel_stat kstat_t;
#      else
typedef struct kernel_stat64 kstat_t;
#      endif
// Undefine compatibility macros from <sys/stat.h>
// so that they would not clash with the kernel_stat
// st_[a|m|c]time fields
#      if !SANITIZER_GO
#        undef st_atime
#        undef st_mtime
#        undef st_ctime
#      endif
#      if defined(SANITIZER_ANDROID)
// Bionic sys/stat.h defines additional macros
// for compatibility with the old NDKs and
// they clash with the kernel_stat structure
// st_[a|m|c]time_nsec fields.
#        undef st_atime_nsec
#        undef st_mtime_nsec
#        undef st_ctime_nsec
#      endif
static void kernel_stat_to_stat(kstat_t *in, struct stat *out) {
  internal_memset(out, 0, sizeof(*out));
  out->st_dev = in->st_dev;
  out->st_ino = in->st_ino;
  out->st_mode = in->st_mode;
  out->st_nlink = in->st_nlink;
  out->st_uid = in->st_uid;
  out->st_gid = in->st_gid;
  out->st_rdev = in->st_rdev;
  out->st_size = in->st_size;
  out->st_blksize = in->st_blksize;
  out->st_blocks = in->st_blocks;
#      if defined(__USE_MISC) || defined(__USE_XOPEN2K8) || \
          defined(SANITIZER_ANDROID)
  out->st_atim.tv_sec = in->st_atime;
  out->st_atim.tv_nsec = in->st_atime_nsec;
  out->st_mtim.tv_sec = in->st_mtime;
  out->st_mtim.tv_nsec = in->st_mtime_nsec;
  out->st_ctim.tv_sec = in->st_ctime;
  out->st_ctim.tv_nsec = in->st_ctime_nsec;
#      else
  out->st_atime = in->st_atime;
  out->st_atimensec = in->st_atime_nsec;
  out->st_mtime = in->st_mtime;
  out->st_mtimensec = in->st_mtime_nsec;
  out->st_ctime = in->st_ctime;
  out->st_atimensec = in->st_ctime_nsec;
#      endif
}
#    endif

uptr internal_stat(const char *path, void *buf) {}

uptr internal_lstat(const char *path, void *buf) {}

uptr internal_fstat(fd_t fd, void *buf) {}

uptr internal_filesize(fd_t fd) {}

uptr internal_dup(int oldfd) {}

uptr internal_dup2(int oldfd, int newfd) {}

uptr internal_readlink(const char *path, char *buf, uptr bufsize) {}

uptr internal_unlink(const char *path) {}

uptr internal_rename(const char *oldpath, const char *newpath) {}

uptr internal_sched_yield() {}

void internal_usleep(u64 useconds) {}

uptr internal_execve(const char *filename, char *const argv[],
                     char *const envp[]) {}
#  endif  // !SANITIZER_SOLARIS && !SANITIZER_NETBSD

#  if !SANITIZER_NETBSD
void internal__exit(int exitcode) {}
#  endif  // !SANITIZER_NETBSD

// ----------------- sanitizer_common.h
bool FileExists(const char *filename) {}

bool DirExists(const char *path) {}

#  if !SANITIZER_NETBSD
tid_t GetTid() {}

int TgKill(pid_t pid, tid_t tid, int sig) {}
#  endif

#  if SANITIZER_GLIBC
u64 NanoTime() {}
// Used by real_clock_gettime.
uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {}
#  elif !SANITIZER_SOLARIS && !SANITIZER_NETBSD
u64 NanoTime() {
  struct timespec ts;
  clock_gettime(CLOCK_REALTIME, &ts);
  return (u64)ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
}
#  endif

// Like getenv, but reads env directly from /proc (on Linux) or parses the
// 'environ' array (on some others) and does not use libc. This function
// should be called first inside __asan_init.
const char *GetEnv(const char *name) {}

#  if !SANITIZER_FREEBSD && !SANITIZER_NETBSD && !SANITIZER_GO
extern "C" {
SANITIZER_WEAK_ATTRIBUTE extern void *__libc_stack_end;
}
#  endif

#  if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
static void ReadNullSepFileToArray(const char *path, char ***arr,
                                   int arr_size) {}
#  endif

static void GetArgsAndEnv(char ***argv, char ***envp) {}

char **GetArgv() {}

char **GetEnviron() {}

#  if !SANITIZER_SOLARIS
void FutexWait(atomic_uint32_t *p, u32 cmp) {}

void FutexWake(atomic_uint32_t *p, u32 count) {}

#  endif  // !SANITIZER_SOLARIS

// ----------------- sanitizer_linux.h
// The actual size of this structure is specified by d_reclen.
// Note that getdents64 uses a different structure format. We only provide the
// 32-bit syscall here.
#  if SANITIZER_NETBSD
// Not used
#  else
struct linux_dirent {};
#  endif

#  if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
// Syscall wrappers.
uptr internal_ptrace(int request, int pid, void *addr, void *data) {}

uptr internal_waitpid(int pid, int *status, int options) {}

uptr internal_getpid() {}

uptr internal_getppid() {}

int internal_dlinfo(void *handle, int request, void *p) {}

uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) {}

uptr internal_lseek(fd_t fd, OFF_T offset, int whence) {}

#    if SANITIZER_LINUX
uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {}
#      if defined(__x86_64__)
#        include <asm/unistd_64.h>
// Currently internal_arch_prctl() is only needed on x86_64.
uptr internal_arch_prctl(int option, uptr arg2) {}
#      endif
#    endif

uptr internal_sigaltstack(const void *ss, void *oss) {}

extern "C" pid_t __fork(void);

int internal_fork() {}

#    if SANITIZER_FREEBSD
int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
                    uptr *oldlenp, const void *newp, uptr newlen) {
  return internal_syscall(SYSCALL(__sysctl), name, namelen, oldp,
                          (size_t *)oldlenp, newp, (size_t)newlen);
}

int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
                          const void *newp, uptr newlen) {
  // Note: this function can be called during startup, so we need to avoid
  // calling any interceptable functions. On FreeBSD >= 1300045 sysctlbyname()
  // is a real syscall, but for older versions it calls sysctlnametomib()
  // followed by sysctl(). To avoid calling the intercepted version and
  // asserting if this happens during startup, call the real sysctlnametomib()
  // followed by internal_sysctl() if the syscall is not available.
#      ifdef SYS___sysctlbyname
  return internal_syscall(SYSCALL(__sysctlbyname), sname,
                          internal_strlen(sname), oldp, (size_t *)oldlenp, newp,
                          (size_t)newlen);
#      else
  static decltype(sysctlnametomib) *real_sysctlnametomib = nullptr;
  if (!real_sysctlnametomib)
    real_sysctlnametomib =
        (decltype(sysctlnametomib) *)dlsym(RTLD_NEXT, "sysctlnametomib");
  CHECK(real_sysctlnametomib);

  int oid[CTL_MAXNAME];
  size_t len = CTL_MAXNAME;
  if (real_sysctlnametomib(sname, oid, &len) == -1)
    return (-1);
  return internal_sysctl(oid, len, oldp, oldlenp, newp, newlen);
#      endif
}
#    endif

#    if SANITIZER_LINUX
#define SA_RESTORER
// Doesn't set sa_restorer if the caller did not set it, so use with caution
//(see below).
int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {}
#    endif  // SANITIZER_LINUX

uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
                          __sanitizer_sigset_t *oldset) {}

void internal_sigfillset(__sanitizer_sigset_t *set) {}

void internal_sigemptyset(__sanitizer_sigset_t *set) {}

#    if SANITIZER_LINUX
void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {}

bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {}
#    elif SANITIZER_FREEBSD
uptr internal_procctl(int type, int id, int cmd, void *data) {
  return internal_syscall(SYSCALL(procctl), type, id, cmd, data);
}

void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {
  sigset_t *rset = reinterpret_cast<sigset_t *>(set);
  sigdelset(rset, signum);
}

bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
  sigset_t *rset = reinterpret_cast<sigset_t *>(set);
  return sigismember(rset, signum);
}
#    endif
#  endif  // !SANITIZER_SOLARIS

#  if !SANITIZER_NETBSD
// ThreadLister implementation.
ThreadLister::ThreadLister(pid_t pid) :{}

ThreadLister::Result ThreadLister::ListThreads(
    InternalMmapVector<tid_t> *threads) {}

bool ThreadLister::IsAlive(int tid) {}

ThreadLister::~ThreadLister() {}
#  endif

#  if SANITIZER_WORDSIZE == 32
// Take care of unusable kernel area in top gigabyte.
static uptr GetKernelAreaSize() {
#    if SANITIZER_LINUX && !SANITIZER_X32
  const uptr gbyte = 1UL << 30;

  // Firstly check if there are writable segments
  // mapped to top gigabyte (e.g. stack).
  MemoryMappingLayout proc_maps(/*cache_enabled*/ true);
  if (proc_maps.Error())
    return 0;
  MemoryMappedSegment segment;
  while (proc_maps.Next(&segment)) {
    if ((segment.end >= 3 * gbyte) && segment.IsWritable())
      return 0;
  }

#      if !SANITIZER_ANDROID
  // Even if nothing is mapped, top Gb may still be accessible
  // if we are running on 64-bit kernel.
  // Uname may report misleading results if personality type
  // is modified (e.g. under schroot) so check this as well.
  struct utsname uname_info;
  int pers = personality(0xffffffffUL);
  if (!(pers & PER_MASK) && internal_uname(&uname_info) == 0 &&
      internal_strstr(uname_info.machine, "64"))
    return 0;
#      endif  // SANITIZER_ANDROID

  // Top gigabyte is reserved for kernel.
  return gbyte;
#    else
  return 0;
#    endif  // SANITIZER_LINUX && !SANITIZER_X32
}
#  endif  // SANITIZER_WORDSIZE == 32

uptr GetMaxVirtualAddress() {}

uptr GetMaxUserVirtualAddress() {}

#  if !SANITIZER_ANDROID || defined(__aarch64__)
uptr GetPageSize() {}
#  endif

uptr ReadBinaryName(/*out*/ char *buf, uptr buf_len) {}

uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {}

// Match full names of the form /path/to/base_name{-,.}*
bool LibraryNameIs(const char *full_name, const char *base_name) {}

#  if !SANITIZER_ANDROID
// Call cb for each region mapped by map.
void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) {}
#  endif

#  if SANITIZER_LINUX
#    if defined(__x86_64__)
// We cannot use glibc's clone wrapper, because it messes with the child
// task's TLS. It writes the PID and TID of the child task to its thread
// descriptor, but in our case the child task shares the thread descriptor with
// the parent (because we don't know how to allocate a new thread
// descriptor to keep glibc happy). So the stock version of clone(), when
// used with CLONE_VM, would end up corrupting the parent's thread descriptor.
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
                    int *parent_tidptr, void *newtls, int *child_tidptr) {}
#    elif defined(__mips__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
                    int *parent_tidptr, void *newtls, int *child_tidptr) {
  long long res;
  if (!fn || !child_stack)
    return -EINVAL;
  CHECK_EQ(0, (uptr)child_stack % 16);
  child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
  ((unsigned long long *)child_stack)[0] = (uptr)fn;
  ((unsigned long long *)child_stack)[1] = (uptr)arg;
  register void *a3 __asm__("$7") = newtls;
  register int *a4 __asm__("$8") = child_tidptr;
  // We don't have proper CFI directives here because it requires alot of code
  // for very marginal benefits.
  __asm__ __volatile__(
      /* $v0 = syscall($v0 = __NR_clone,
       * $a0 = flags,
       * $a1 = child_stack,
       * $a2 = parent_tidptr,
       * $a3 = new_tls,
       * $a4 = child_tidptr)
       */
      ".cprestore 16;\n"
      "move $4,%1;\n"
      "move $5,%2;\n"
      "move $6,%3;\n"
      "move $7,%4;\n"
  /* Store the fifth argument on stack
   * if we are using 32-bit abi.
   */
#      if SANITIZER_WORDSIZE == 32
      "lw %5,16($29);\n"
#      else
      "move $8,%5;\n"
#      endif
      "li $2,%6;\n"
      "syscall;\n"

      /* if ($v0 != 0)
       * return;
       */
      "bnez $2,1f;\n"

  /* Call "fn(arg)". */
#      if SANITIZER_WORDSIZE == 32
#        ifdef __BIG_ENDIAN__
      "lw $25,4($29);\n"
      "lw $4,12($29);\n"
#        else
      "lw $25,0($29);\n"
      "lw $4,8($29);\n"
#        endif
#      else
      "ld $25,0($29);\n"
      "ld $4,8($29);\n"
#      endif
      "jal $25;\n"

      /* Call _exit($v0). */
      "move $4,$2;\n"
      "li $2,%7;\n"
      "syscall;\n"

      /* Return to parent. */
      "1:\n"
      : "=r"(res)
      : "r"(flags), "r"(child_stack), "r"(parent_tidptr), "r"(a3), "r"(a4),
        "i"(__NR_clone), "i"(__NR_exit)
      : "memory", "$29");
  return res;
}
#    elif SANITIZER_RISCV64
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
                    int *parent_tidptr, void *newtls, int *child_tidptr) {
  if (!fn || !child_stack)
    return -EINVAL;

  CHECK_EQ(0, (uptr)child_stack % 16);

  register int res __asm__("a0");
  register int __flags __asm__("a0") = flags;
  register void *__stack __asm__("a1") = child_stack;
  register int *__ptid __asm__("a2") = parent_tidptr;
  register void *__tls __asm__("a3") = newtls;
  register int *__ctid __asm__("a4") = child_tidptr;
  register int (*__fn)(void *) __asm__("a5") = fn;
  register void *__arg __asm__("a6") = arg;
  register int nr_clone __asm__("a7") = __NR_clone;

  __asm__ __volatile__(
      "ecall\n"

      /* if (a0 != 0)
       *   return a0;
       */
      "bnez a0, 1f\n"

      // In the child, now. Call "fn(arg)".
      "mv a0, a6\n"
      "jalr a5\n"

      // Call _exit(a0).
      "addi a7, zero, %9\n"
      "ecall\n"
      "1:\n"

      : "=r"(res)
      : "0"(__flags), "r"(__stack), "r"(__ptid), "r"(__tls), "r"(__ctid),
        "r"(__fn), "r"(__arg), "r"(nr_clone), "i"(__NR_exit)
      : "memory");
  return res;
}
#    elif defined(__aarch64__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
                    int *parent_tidptr, void *newtls, int *child_tidptr) {
  register long long res __asm__("x0");
  if (!fn || !child_stack)
    return -EINVAL;
  CHECK_EQ(0, (uptr)child_stack % 16);
  child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
  ((unsigned long long *)child_stack)[0] = (uptr)fn;
  ((unsigned long long *)child_stack)[1] = (uptr)arg;

  register int (*__fn)(void *) __asm__("x0") = fn;
  register void *__stack __asm__("x1") = child_stack;
  register int __flags __asm__("x2") = flags;
  register void *__arg __asm__("x3") = arg;
  register int *__ptid __asm__("x4") = parent_tidptr;
  register void *__tls __asm__("x5") = newtls;
  register int *__ctid __asm__("x6") = child_tidptr;

  __asm__ __volatile__(
      "mov x0,x2\n" /* flags  */
      "mov x2,x4\n" /* ptid  */
      "mov x3,x5\n" /* tls  */
      "mov x4,x6\n" /* ctid  */
      "mov x8,%9\n" /* clone  */

      "svc 0x0\n"

      /* if (%r0 != 0)
       *   return %r0;
       */
      "cmp x0, #0\n"
      "bne 1f\n"

      /* In the child, now. Call "fn(arg)". */
      "ldp x1, x0, [sp], #16\n"
      "blr x1\n"

      /* Call _exit(%r0).  */
      "mov x8, %10\n"
      "svc 0x0\n"
      "1:\n"

      : "=r"(res)
      : "i"(-EINVAL), "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg),
        "r"(__ptid), "r"(__tls), "r"(__ctid), "i"(__NR_clone), "i"(__NR_exit)
      : "x30", "memory");
  return res;
}
#    elif SANITIZER_LOONGARCH64
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
                    int *parent_tidptr, void *newtls, int *child_tidptr) {
  if (!fn || !child_stack)
    return -EINVAL;

  CHECK_EQ(0, (uptr)child_stack % 16);

  register int res __asm__("$a0");
  register int __flags __asm__("$a0") = flags;
  register void *__stack __asm__("$a1") = child_stack;
  register int *__ptid __asm__("$a2") = parent_tidptr;
  register int *__ctid __asm__("$a3") = child_tidptr;
  register void *__tls __asm__("$a4") = newtls;
  register int (*__fn)(void *) __asm__("$a5") = fn;
  register void *__arg __asm__("$a6") = arg;
  register int nr_clone __asm__("$a7") = __NR_clone;

  __asm__ __volatile__(
      "syscall 0\n"

      // if ($a0 != 0)
      //   return $a0;
      "bnez $a0, 1f\n"

      // In the child, now. Call "fn(arg)".
      "move $a0, $a6\n"
      "jirl $ra, $a5, 0\n"

      // Call _exit($a0).
      "addi.d $a7, $zero, %9\n"
      "syscall 0\n"

      "1:\n"

      : "=r"(res)
      : "0"(__flags), "r"(__stack), "r"(__ptid), "r"(__ctid), "r"(__tls),
        "r"(__fn), "r"(__arg), "r"(nr_clone), "i"(__NR_exit)
      : "memory", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
        "$t8");
  return res;
}
#    elif defined(__powerpc64__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
                    int *parent_tidptr, void *newtls, int *child_tidptr) {
  long long res;
// Stack frame structure.
#      if SANITIZER_PPC64V1
  //   Back chain == 0        (SP + 112)
  // Frame (112 bytes):
  //   Parameter save area    (SP + 48), 8 doublewords
  //   TOC save area          (SP + 40)
  //   Link editor doubleword (SP + 32)
  //   Compiler doubleword    (SP + 24)
  //   LR save area           (SP + 16)
  //   CR save area           (SP + 8)
  //   Back chain             (SP + 0)
#define FRAME_SIZE
#define FRAME_TOC_SAVE_OFFSET
#      elif SANITIZER_PPC64V2
  //   Back chain == 0        (SP + 32)
  // Frame (32 bytes):
  //   TOC save area          (SP + 24)
  //   LR save area           (SP + 16)
  //   CR save area           (SP + 8)
  //   Back chain             (SP + 0)
#define FRAME_SIZE
#define FRAME_TOC_SAVE_OFFSET
#      else
#        error "Unsupported PPC64 ABI"
#      endif
  if (!fn || !child_stack)
    return -EINVAL;
  CHECK_EQ(0, (uptr)child_stack % 16);

  register int (*__fn)(void *) __asm__("r3") = fn;
  register void *__cstack __asm__("r4") = child_stack;
  register int __flags __asm__("r5") = flags;
  register void *__arg __asm__("r6") = arg;
  register int *__ptidptr __asm__("r7") = parent_tidptr;
  register void *__newtls __asm__("r8") = newtls;
  register int *__ctidptr __asm__("r9") = child_tidptr;

  __asm__ __volatile__(
      /* fn and arg are saved across the syscall */
      "mr 28, %5\n\t"
      "mr 27, %8\n\t"

      /* syscall
        r0 == __NR_clone
        r3 == flags
        r4 == child_stack
        r5 == parent_tidptr
        r6 == newtls
        r7 == child_tidptr */
      "mr 3, %7\n\t"
      "mr 5, %9\n\t"
      "mr 6, %10\n\t"
      "mr 7, %11\n\t"
      "li 0, %3\n\t"
      "sc\n\t"

      /* Test if syscall was successful */
      "cmpdi  cr1, 3, 0\n\t"
      "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t"
      "bne-   cr1, 1f\n\t"

      /* Set up stack frame */
      "li    29, 0\n\t"
      "stdu  29, -8(1)\n\t"
      "stdu  1, -%12(1)\n\t"
      /* Do the function call */
      "std   2, %13(1)\n\t"
#      if SANITIZER_PPC64V1
      "ld    0, 0(28)\n\t"
      "ld    2, 8(28)\n\t"
      "mtctr 0\n\t"
#      elif SANITIZER_PPC64V2
      "mr    12, 28\n\t"
      "mtctr 12\n\t"
#      else
#        error "Unsupported PPC64 ABI"
#      endif
      "mr    3, 27\n\t"
      "bctrl\n\t"
      "ld    2, %13(1)\n\t"

      /* Call _exit(r3) */
      "li 0, %4\n\t"
      "sc\n\t"

      /* Return to parent */
      "1:\n\t"
      "mr %0, 3\n\t"
      : "=r"(res)
      : "0"(-1), "i"(EINVAL), "i"(__NR_clone), "i"(__NR_exit), "r"(__fn),
        "r"(__cstack), "r"(__flags), "r"(__arg), "r"(__ptidptr), "r"(__newtls),
        "r"(__ctidptr), "i"(FRAME_SIZE), "i"(FRAME_TOC_SAVE_OFFSET)
      : "cr0", "cr1", "memory", "ctr", "r0", "r27", "r28", "r29");
  return res;
}
#    elif defined(__i386__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
                    int *parent_tidptr, void *newtls, int *child_tidptr) {
  int res;
  if (!fn || !child_stack)
    return -EINVAL;
  CHECK_EQ(0, (uptr)child_stack % 16);
  child_stack = (char *)child_stack - 7 * sizeof(unsigned int);
  ((unsigned int *)child_stack)[0] = (uptr)flags;
  ((unsigned int *)child_stack)[1] = (uptr)0;
  ((unsigned int *)child_stack)[2] = (uptr)fn;
  ((unsigned int *)child_stack)[3] = (uptr)arg;
  __asm__ __volatile__(
      /* %eax = syscall(%eax = SYSCALL(clone),
       *                %ebx = flags,
       *                %ecx = child_stack,
       *                %edx = parent_tidptr,
       *                %esi  = new_tls,
       *                %edi = child_tidptr)
       */

      /* Obtain flags */
      "movl    (%%ecx), %%ebx\n"
      /* Do the system call */
      "pushl   %%ebx\n"
      "pushl   %%esi\n"
      "pushl   %%edi\n"
      /* Remember the flag value.  */
      "movl    %%ebx, (%%ecx)\n"
      "int     $0x80\n"
      "popl    %%edi\n"
      "popl    %%esi\n"
      "popl    %%ebx\n"

      /* if (%eax != 0)
       *   return;
       */

      "test    %%eax,%%eax\n"
      "jnz    1f\n"

      /* terminate the stack frame */
      "xorl   %%ebp,%%ebp\n"
      /* Call FN. */
      "call    *%%ebx\n"
#      ifdef PIC
      "call    here\n"
      "here:\n"
      "popl    %%ebx\n"
      "addl    $_GLOBAL_OFFSET_TABLE_+[.-here], %%ebx\n"
#      endif
      /* Call exit */
      "movl    %%eax, %%ebx\n"
      "movl    %2, %%eax\n"
      "int     $0x80\n"
      "1:\n"
      : "=a"(res)
      : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)), "c"(child_stack),
        "d"(parent_tidptr), "S"(newtls), "D"(child_tidptr)
      : "memory");
  return res;
}
#    elif defined(__arm__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
                    int *parent_tidptr, void *newtls, int *child_tidptr) {
  unsigned int res;
  if (!fn || !child_stack)
    return -EINVAL;
  child_stack = (char *)child_stack - 2 * sizeof(unsigned int);
  ((unsigned int *)child_stack)[0] = (uptr)fn;
  ((unsigned int *)child_stack)[1] = (uptr)arg;
  register int r0 __asm__("r0") = flags;
  register void *r1 __asm__("r1") = child_stack;
  register int *r2 __asm__("r2") = parent_tidptr;
  register void *r3 __asm__("r3") = newtls;
  register int *r4 __asm__("r4") = child_tidptr;
  register int r7 __asm__("r7") = __NR_clone;

#      if __ARM_ARCH > 4 || defined(__ARM_ARCH_4T__)
#define ARCH_HAS_BX
#      endif
#      if __ARM_ARCH > 4
#define ARCH_HAS_BLX
#      endif

#      ifdef ARCH_HAS_BX
#        ifdef ARCH_HAS_BLX
#define BLX
#        else
#define BLX
#        endif
#      else
#define BLX
#      endif

  __asm__ __volatile__(
      /* %r0 = syscall(%r7 = SYSCALL(clone),
       *               %r0 = flags,
       *               %r1 = child_stack,
       *               %r2 = parent_tidptr,
       *               %r3  = new_tls,
       *               %r4 = child_tidptr)
       */

      /* Do the system call */
      "swi 0x0\n"

      /* if (%r0 != 0)
       *   return %r0;
       */
      "cmp r0, #0\n"
      "bne 1f\n"

      /* In the child, now. Call "fn(arg)". */
      "ldr r0, [sp, #4]\n"
      "ldr ip, [sp], #8\n" BLX(ip)
      /* Call _exit(%r0). */
      "mov r7, %7\n"
      "swi 0x0\n"
      "1:\n"
      "mov %0, r0\n"
      : "=r"(res)
      : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7), "i"(__NR_exit)
      : "memory");
  return res;
}
#    endif
#  endif  // SANITIZER_LINUX

#  if SANITIZER_LINUX
int internal_uname(struct utsname *buf) {}
#  endif

#  if SANITIZER_ANDROID
#    if __ANDROID_API__ < 21
extern "C" __attribute__((weak)) int dl_iterate_phdr(
    int (*)(struct dl_phdr_info *, size_t, void *), void *);
#    endif

static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size,
                                   void *data) {
  // Any name starting with "lib" indicates a bug in L where library base names
  // are returned instead of paths.
  if (info->dlpi_name && info->dlpi_name[0] == 'l' &&
      info->dlpi_name[1] == 'i' && info->dlpi_name[2] == 'b') {
    *(bool *)data = true;
    return 1;
  }
  return 0;
}

static atomic_uint32_t android_api_level;

static AndroidApiLevel AndroidDetectApiLevelStatic() {
#    if __ANDROID_API__ <= 19
  return ANDROID_KITKAT;
#    elif __ANDROID_API__ <= 22
  return ANDROID_LOLLIPOP_MR1;
#    else
  return ANDROID_POST_LOLLIPOP;
#    endif
}

static AndroidApiLevel AndroidDetectApiLevel() {
  if (!&dl_iterate_phdr)
    return ANDROID_KITKAT;  // K or lower
  bool base_name_seen = false;
  dl_iterate_phdr(dl_iterate_phdr_test_cb, &base_name_seen);
  if (base_name_seen)
    return ANDROID_LOLLIPOP_MR1;  // L MR1
  return ANDROID_POST_LOLLIPOP;   // post-L
  // Plain L (API level 21) is completely broken wrt ASan and not very
  // interesting to detect.
}

extern "C" __attribute__((weak)) void *_DYNAMIC;

AndroidApiLevel AndroidGetApiLevel() {
  AndroidApiLevel level =
      (AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed);
  if (level)
    return level;
  level = &_DYNAMIC == nullptr ? AndroidDetectApiLevelStatic()
                               : AndroidDetectApiLevel();
  atomic_store(&android_api_level, level, memory_order_relaxed);
  return level;
}

#  endif

static HandleSignalMode GetHandleSignalModeImpl(int signum) {}

HandleSignalMode GetHandleSignalMode(int signum) {}

#  if !SANITIZER_GO
void *internal_start_thread(void *(*func)(void *arg), void *arg) {}

void internal_join_thread(void *th) {}
#  else
void *internal_start_thread(void *(*func)(void *), void *arg) { return 0; }

void internal_join_thread(void *th) {}
#  endif

#  if SANITIZER_LINUX && defined(__aarch64__)
// Android headers in the older NDK releases miss this definition.
struct __sanitizer_esr_context {
  struct _aarch64_ctx head;
  uint64_t esr;
};

static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) {
  static const u32 kEsrMagic = 0x45535201;
  u8 *aux = reinterpret_cast<u8 *>(ucontext->uc_mcontext.__reserved);
  while (true) {
    _aarch64_ctx *ctx = (_aarch64_ctx *)aux;
    if (ctx->size == 0)
      break;
    if (ctx->magic == kEsrMagic) {
      *esr = ((__sanitizer_esr_context *)ctx)->esr;
      return true;
    }
    aux += ctx->size;
  }
  return false;
}
#  elif SANITIZER_FREEBSD && defined(__aarch64__)
// FreeBSD doesn't provide ESR in the ucontext.
static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) { return false; }
#  endif

Context;

SignalContext::WriteFlag SignalContext::GetWriteFlag() const {}

bool SignalContext::IsTrueFaultingAddress() const {}

UNUSED
static const char *RegNumToRegName(int reg) {}

#  if ((SANITIZER_LINUX && SANITIZER_GLIBC) || SANITIZER_NETBSD) && \
      (defined(__arm__) || defined(__aarch64__))
static uptr GetArmRegister(ucontext_t *ctx, int RegNum) {
  switch (RegNum) {
#    if defined(__arm__) && !SANITIZER_NETBSD
#      ifdef MAKE_CASE
#        undef MAKE_CASE
#      endif
#define MAKE_CASE
    MAKE_CASE(0);
    MAKE_CASE(1);
    MAKE_CASE(2);
    MAKE_CASE(3);
    MAKE_CASE(4);
    MAKE_CASE(5);
    MAKE_CASE(6);
    MAKE_CASE(7);
    MAKE_CASE(8);
    MAKE_CASE(9);
    MAKE_CASE(10);
    case REG_R11:
      return ctx->uc_mcontext.arm_fp;
    case REG_R12:
      return ctx->uc_mcontext.arm_ip;
    case REG_R13:
      return ctx->uc_mcontext.arm_sp;
    case REG_R14:
      return ctx->uc_mcontext.arm_lr;
    case REG_R15:
      return ctx->uc_mcontext.arm_pc;
#    elif defined(__aarch64__)
#      if SANITIZER_LINUX
    case 0 ... 30:
      return ctx->uc_mcontext.regs[RegNum];
    case 31:
      return ctx->uc_mcontext.sp;
#      elif SANITIZER_NETBSD
    case 0 ... 31:
      return ctx->uc_mcontext.__gregs[RegNum];
#      endif
#    endif
    default:
      return 0;
  }
  return 0;
}
#  endif  // SANITIZER_LINUX && SANITIZER_GLIBC && (defined(__arm__) ||
          // defined(__aarch64__))

UNUSED
static void DumpSingleReg(ucontext_t *ctx, int RegNum) {}

void SignalContext::DumpAllRegisters(void *context) {}

static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {}

void SignalContext::InitPcSpBp() {}

void InitializePlatformEarly() {}

void CheckASLR() {}

void CheckMPROTECT() {}

void CheckNoDeepBind(const char *filename, int flag) {}

uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
                              uptr *largest_gap_found,
                              uptr *max_occupied_addr) {}

bool GetRandom(void *buffer, uptr length, bool blocking) {}

}  // namespace __sanitizer

#endif