Skip to content

Commit

Permalink
[sanitizer] Port sanitizer_common to LoongArch
Browse files Browse the repository at this point in the history
Initial libsanitizer support for LoongArch. It survived all GCC UBSan tests.

Major changes:

1. LoongArch port of Linux kernel only supports `statx` for `stat` and its families.  So we need to add `statx_to_stat` and use it for `stat`-like libcalls.  The logic is "borrowed" from Glibc.
2. `sanitizer_syscall_linux_loongarch64.inc` is mostly duplicated from RISC-V port, as the syscall interface is almost same.

Reviewed By: SixWeining, MaskRay, XiaodongLoong, vitalybuka

Differential Revision: https://reviews.llvm.org/D129371
  • Loading branch information
xry111 authored and MaskRay committed Jul 20, 2022
1 parent f72e0a8 commit dbec35c
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 25 deletions.
3 changes: 2 additions & 1 deletion compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
Expand Up @@ -3,6 +3,7 @@ set(ARM32 arm armhf)
set(HEXAGON hexagon)
set(X86 i386)
set(X86_64 x86_64)
set(LOONGARCH64 loongarch64)
set(MIPS32 mips mipsel)
set(MIPS64 mips64 mips64el)
set(PPC32 powerpc powerpcspe)
Expand All @@ -24,7 +25,7 @@ endif()

set(ALL_SANITIZER_COMMON_SUPPORTED_ARCH ${X86} ${X86_64} ${PPC64} ${RISCV64}
${ARM32} ${ARM64} ${MIPS32} ${MIPS64} ${S390X} ${SPARC} ${SPARCV9}
${HEXAGON})
${HEXAGON} ${LOONGARCH64})
set(ALL_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64}
${MIPS32} ${MIPS64} ${PPC64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON})
set(ALL_DFSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64})
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/sanitizer_common/CMakeLists.txt
Expand Up @@ -190,6 +190,7 @@ set(SANITIZER_IMPL_HEADERS
sanitizer_syscall_linux_arm.inc
sanitizer_syscall_linux_x86_64.inc
sanitizer_syscall_linux_riscv64.inc
sanitizer_syscall_linux_loongarch64.inc
sanitizer_syscalls_netbsd.inc
sanitizer_thread_registry.h
sanitizer_thread_safety.h
Expand Down
96 changes: 74 additions & 22 deletions compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
Expand Up @@ -78,6 +78,10 @@
#include <sys/personality.h>
#endif

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

#if SANITIZER_FREEBSD
#include <sys/exec.h>
#include <sys/procctl.h>
Expand Down Expand Up @@ -188,6 +192,8 @@ ScopedBlockSignals::~ScopedBlockSignals() { SetSigProcMask(&saved_, nullptr); }
# 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
Expand Down Expand Up @@ -290,6 +296,28 @@ static void stat64_to_stat(struct stat64 *in, struct stat *out) {
}
#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
// Undefine compatibility macros from <sys/stat.h>
// so that they would not clash with the kernel_stat
Expand Down Expand Up @@ -341,52 +369,65 @@ static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) {
#endif

uptr internal_stat(const char *path, void *buf) {
#if SANITIZER_FREEBSD
# if SANITIZER_FREEBSD
return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0);
# elif SANITIZER_LINUX
# if (SANITIZER_WORDSIZE == 64 || SANITIZER_X32 || \
# elif SANITIZER_LINUX
# if defined(__loongarch__)
struct statx bufx;
int res = internal_syscall(SYSCALL(statx), AT_FDCWD, (uptr)path,
AT_NO_AUTOMOUNT, STATX_BASIC_STATS, (uptr)&bufx);
statx_to_stat(&bufx, (struct stat *)buf);
return res;
# elif (SANITIZER_WORDSIZE == 64 || SANITIZER_X32 || \
(defined(__mips__) && _MIPS_SIM == _ABIN32)) && \
!SANITIZER_SPARC
!SANITIZER_SPARC
return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf,
0);
# else
# else
struct stat64 buf64;
int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path,
(uptr)&buf64, 0);
stat64_to_stat(&buf64, (struct stat *)buf);
return res;
# endif
# else
# endif
# else
struct stat64 buf64;
int res = internal_syscall(SYSCALL(stat64), path, &buf64);
stat64_to_stat(&buf64, (struct stat *)buf);
return res;
# endif
# endif
}

uptr internal_lstat(const char *path, void *buf) {
#if SANITIZER_FREEBSD
# if SANITIZER_FREEBSD
return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf,
AT_SYMLINK_NOFOLLOW);
# elif SANITIZER_LINUX
# if (defined(_LP64) || SANITIZER_X32 || \
# elif SANITIZER_LINUX
# if defined(__loongarch__)
struct statx bufx;
int res = internal_syscall(SYSCALL(statx), AT_FDCWD, (uptr)path,
AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT,
STATX_BASIC_STATS, (uptr)&bufx);
statx_to_stat(&bufx, (struct stat *)buf);
return res;
# elif (defined(_LP64) || SANITIZER_X32 || \
(defined(__mips__) && _MIPS_SIM == _ABIN32)) && \
!SANITIZER_SPARC
!SANITIZER_SPARC
return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf,
AT_SYMLINK_NOFOLLOW);
# else
# else
struct stat64 buf64;
int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path,
(uptr)&buf64, AT_SYMLINK_NOFOLLOW);
stat64_to_stat(&buf64, (struct stat *)buf);
return res;
# endif
# else
# endif
# else
struct stat64 buf64;
int res = internal_syscall(SYSCALL(lstat64), path, &buf64);
stat64_to_stat(&buf64, (struct stat *)buf);
return res;
# endif
# endif
}

uptr internal_fstat(fd_t fd, void *buf) {
Expand All @@ -397,9 +438,15 @@ uptr internal_fstat(fd_t fd, void *buf) {
int res = internal_syscall(SYSCALL(fstat), fd, &kbuf);
kernel_stat_to_stat(&kbuf, (struct stat *)buf);
return res;
# else
# elif SANITIZER_LINUX && defined(__loongarch__)
struct statx bufx;
int res = internal_syscall(SYSCALL(statx), fd, 0, AT_EMPTY_PATH,
STATX_BASIC_STATS, (uptr)&bufx);
statx_to_stat(&bufx, (struct stat *)buf);
return res;
# else
return internal_syscall(SYSCALL(fstat), fd, (uptr)buf);
# endif
# endif
#else
struct stat64 buf64;
int res = internal_syscall(SYSCALL(fstat64), fd, &buf64);
Expand Down Expand Up @@ -445,15 +492,15 @@ uptr internal_unlink(const char *path) {
}

uptr internal_rename(const char *oldpath, const char *newpath) {
#if defined(__riscv) && defined(__linux__)
# if (defined(__riscv) || defined(__loongarch__)) && defined(__linux__)
return internal_syscall(SYSCALL(renameat2), AT_FDCWD, (uptr)oldpath, AT_FDCWD,
(uptr)newpath, 0);
# elif SANITIZER_LINUX
# elif SANITIZER_LINUX
return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD,
(uptr)newpath);
# else
# else
return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath);
# endif
# endif
}

uptr internal_sched_yield() {
Expand Down Expand Up @@ -2176,6 +2223,11 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
*pc = ucontext->uc_mcontext.pc;
*bp = ucontext->uc_mcontext.r30;
*sp = ucontext->uc_mcontext.r29;
# elif defined(__loongarch__)
ucontext_t *ucontext = (ucontext_t *)context;
*pc = ucontext->uc_mcontext.__pc;
*bp = ucontext->uc_mcontext.__gregs[22];
*sp = ucontext->uc_mcontext.__gregs[3];
# else
# error "Unsupported arch"
# endif
Expand Down
6 changes: 6 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_platform.h
Expand Up @@ -272,6 +272,12 @@
# define SANITIZER_RISCV64 0
#endif

#if defined(__loongarch_lp64)
# define SANITIZER_LOONGARCH64 1
#else
# define SANITIZER_LOONGARCH64 0
#endif

// By default we allow to use SizeClassAllocator64 on 64-bit platform.
// But in some cases (e.g. AArch64's 39-bit address space) SizeClassAllocator64
// does not work well and we need to fallback to SizeClassAllocator32.
Expand Down
Expand Up @@ -59,7 +59,8 @@ using namespace __sanitizer;

# if !defined(__powerpc64__) && !defined(__x86_64__) && \
!defined(__aarch64__) && !defined(__mips__) && !defined(__s390__) && \
!defined(__sparc__) && !defined(__riscv) && !defined(__hexagon__)
!defined(__sparc__) && !defined(__riscv) && !defined(__hexagon__) && \
!defined(__loongarch__)
COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat));
#endif

Expand Down
Expand Up @@ -271,6 +271,10 @@ namespace __sanitizer {
defined(__powerpc__) || defined(__s390__) || defined(__sparc__) || \
defined(__hexagon__)
# define SIZEOF_STRUCT_USTAT 20
# elif defined(__loongarch__)
// Not used. The minimum Glibc version available for LoongArch is 2.36
// so ustat() wrapper is already gone.
# define SIZEOF_STRUCT_USTAT 0
# else
# error Unknown size of struct ustat
# endif
Expand Down
Expand Up @@ -122,6 +122,9 @@ const unsigned struct_kernel_stat64_sz = 0; // RISCV64 does not use stat64
# elif defined(__hexagon__)
const unsigned struct_kernel_stat_sz = 128;
const unsigned struct_kernel_stat64_sz = 0;
# elif defined(__loongarch__)
const unsigned struct_kernel_stat_sz = 128;
const unsigned struct_kernel_stat64_sz = 0;
# endif
struct __sanitizer_perf_event_attr {
unsigned type;
Expand All @@ -142,7 +145,7 @@ const unsigned struct_kexec_segment_sz = 4 * sizeof(unsigned long);

#if SANITIZER_LINUX

#if defined(__powerpc64__) || defined(__s390__)
#if defined(__powerpc64__) || defined(__s390__) || defined(__loongarch__)
const unsigned struct___old_kernel_stat_sz = 0;
#elif !defined(__sparc__)
const unsigned struct___old_kernel_stat_sz = 32;
Expand Down

0 comments on commit dbec35c

Please sign in to comment.