Add support for loongarch64
Bug: https://crbug.com/linux-syscall-support/64
Change-Id: I820c339d2746c8487f0942e86d0dc245ee0e1e50
Reviewed-on: https://chromium-review.googlesource.com/c/linux-syscall-support/+/3824867
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/README.md b/README.md
index b0cd129..e25abdd 100644
--- a/README.md
+++ b/README.md
@@ -39,6 +39,7 @@
* MIPS 32-bit o32 ABI
* MIPS 32-bit n32 ABI
* MIPS 64-bit n64 ABI
+* LOONGARCH 64-bit ABI
## API
diff --git a/linux_syscall_support.h b/linux_syscall_support.h
index 5ea1295..2664b72 100644
--- a/linux_syscall_support.h
+++ b/linux_syscall_support.h
@@ -89,7 +89,7 @@
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \
defined(__mips__) || defined(__PPC__) || defined(__ARM_EABI__) || \
defined(__aarch64__) || defined(__s390__) || defined(__e2k__) || \
- (defined(__riscv) && __riscv_xlen == 64)) \
+ (defined(__riscv) && __riscv_xlen == 64) || defined(__loongarch_lp64)) \
&& (defined(__linux) || defined(__ANDROID__))
#ifndef SYS_CPLUSPLUS
@@ -309,8 +309,8 @@
} __attribute__((packed,aligned(4)));
#elif (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32)
#define kernel_old_sigaction kernel_sigaction
-#elif defined(__aarch64__) || defined(__riscv)
- // No kernel_old_sigaction defined for arm64 or riscv.
+#elif defined(__aarch64__) || defined(__riscv) || defined(__loongarch_lp64)
+ // No kernel_old_sigaction defined for arm64 riscv and loongarch64.
#endif
/* Some kernel functions (e.g. sigaction() in 2.6.23) require that the
@@ -349,7 +349,7 @@
void (*sa_sigaction_)(int, siginfo_t *, void *);
};
unsigned long sa_flags;
-#ifndef __riscv
+#if !defined(__riscv) && !defined(__loongarch_lp64)
void (*sa_restorer)(void);
#endif
struct kernel_sigset_t sa_mask;
@@ -551,7 +551,7 @@
int st_blocks;
int st_pad4[14];
};
-#elif defined(__aarch64__) || defined(__riscv)
+#elif defined(__aarch64__) || defined(__riscv) || defined(__loongarch_lp64)
struct kernel_stat {
unsigned long st_dev;
unsigned long st_ino;
@@ -752,6 +752,37 @@
};
#endif
+struct kernel_statx_timestamp {
+ int64_t tv_sec;
+ uint32_t tv_nsec;
+ int32_t __reserved;
+};
+
+struct kernel_statx {
+ uint32_t stx_mask;
+ uint32_t stx_blksize;
+ uint64_t stx_attributes;
+ uint32_t stx_nlink;
+ uint32_t stx_uid;
+ uint32_t stx_gid;
+ uint16_t stx_mode;
+ uint16_t __spare0[1];
+ uint64_t stx_ino;
+ uint64_t stx_size;
+ uint64_t stx_blocks;
+ uint64_t stx_attributes_mask;
+ struct kernel_statx_timestamp stx_atime;
+ struct kernel_statx_timestamp stx_btime;
+ struct kernel_statx_timestamp stx_ctime;
+ struct kernel_statx_timestamp stx_mtime;
+ uint32_t stx_rdev_major;
+ uint32_t stx_rdev_minor;
+ uint32_t stx_dev_major;
+ uint32_t stx_dev_minor;
+ uint64_t stx_mnt_id;
+ uint64_t __spare2;
+ uint64_t __spare3[12];
+};
/* Definitions missing from the standard header files */
#ifndef O_DIRECTORY
@@ -788,6 +819,18 @@
#ifndef AT_REMOVEDIR
#define AT_REMOVEDIR 0x200
#endif
+#ifndef AT_NO_AUTOMOUNT
+#define AT_NO_AUTOMOUNT 0x800
+#endif
+#ifndef AT_EMPTY_PATH
+#define AT_EMPTY_PATH 0x1000
+#endif
+#ifndef STATX_BASIC_STATS
+#define STATX_BASIC_STATS 0x000007ffU
+#endif
+#ifndef AT_STATX_SYNC_AS_STAT
+#define AT_STATX_SYNC_AS_STAT 0x0000
+#endif
#ifndef MREMAP_FIXED
#define MREMAP_FIXED 2
#endif
@@ -1119,7 +1162,7 @@
#define __NR_getrandom (__NR_SYSCALL_BASE + 384)
#endif
/* End of ARM 3/EABI definitions */
-#elif defined(__aarch64__) || defined(__riscv)
+#elif defined(__aarch64__) || defined(__riscv) || defined(__loongarch_lp64)
#ifndef __NR_setxattr
#define __NR_setxattr 5
#endif
@@ -1175,9 +1218,11 @@
#ifndef __NR_readlinkat
#define __NR_readlinkat 78
#endif
+#if !defined(__loongarch_lp64)
#ifndef __NR_newfstatat
#define __NR_newfstatat 79
#endif
+#endif
#ifndef __NR_set_tid_address
#define __NR_set_tid_address 96
#endif
@@ -1218,7 +1263,9 @@
#ifndef __NR_getrandom
#define __NR_getrandom 278
#endif
-/* End of aarch64 definitions */
+#ifndef __NR_statx
+#define __NR_statx 291
+#endif
#elif defined(__x86_64__)
#ifndef __NR_pread64
#define __NR_pread64 17
@@ -1935,7 +1982,7 @@
#undef LSS_RETURN
#if defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) \
|| defined(__ARM_EABI__) || defined(__aarch64__) || defined(__s390__) \
- || defined(__e2k__) || defined(__riscv)
+ || defined(__e2k__) || defined(__riscv) || defined(__loongarch_lp64)
/* Failing system calls return a negative result in the range of
* -1..-4095. These are "errno" values with the sign inverted.
*/
@@ -3796,6 +3843,134 @@
"b[4]", "b[5]", "pred0");
LSS_RETURN(int, __res);
}
+ #elif defined(__loongarch_lp64)
+ /* Most definitions of _syscallX() neglect to mark "memory" as being
+ * clobbered. This causes problems with compilers, that do a better job
+ * at optimizing across __asm__ calls.
+ * So, we just have to redefine all of the _syscallX() macros.
+ */
+ #undef LSS_REG
+ #define LSS_REG(ar,a) register int64_t __r##ar __asm__("a"#ar) = (int64_t)a
+ /* syscall is like subroutine calls, all caller-saved registers may be
+ * clobbered, we should add them to the |Clobbers| list.
+ * a0 is not included because it's in the output list.
+ */
+ #define LSS_SYSCALL_CLOBBERS "t0", "t1", "t2", "t3", "t4", "t5", "t6", \
+ "t7", "t8", "memory"
+ #undef LSS_BODY
+ #define LSS_BODY(type,name,args...) \
+ register int64_t __res_a0 __asm__("a0"); \
+ int64_t __res; \
+ __asm__ __volatile__ ("li.d $a7, %1\n" \
+ "syscall 0x0\n" \
+ : "=r"(__res_a0) \
+ : "i"(__NR_##name) , ## args \
+ : LSS_SYSCALL_CLOBBERS); \
+ __res = __res_a0; \
+ LSS_RETURN(type, __res)
+ #undef _syscall0
+ #define _syscall0(type, name) \
+ type LSS_NAME(name)(void) { \
+ LSS_BODY(type, name); \
+ }
+ #undef _syscall1
+ #define _syscall1(type, name, type1, arg1) \
+ type LSS_NAME(name)(type1 arg1) { \
+ LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \
+ }
+ #undef _syscall2
+ #define _syscall2(type, name, type1, arg1, type2, arg2) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2) { \
+ LSS_REG(0, arg1); LSS_REG(1, arg2); \
+ LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \
+ }
+ #undef _syscall3
+ #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \
+ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
+ LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \
+ }
+ #undef _syscall4
+ #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
+ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
+ LSS_REG(3, arg4); \
+ LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \
+ }
+ #undef _syscall5
+ #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5) { \
+ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
+ LSS_REG(3, arg4); LSS_REG(4, arg5); \
+ LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \
+ "r"(__r4)); \
+ }
+ #undef _syscall6
+ #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5,type6,arg6) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5, type6 arg6) { \
+ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
+ LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \
+ LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \
+ "r"(__r4), "r"(__r5)); \
+ }
+
+ LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
+ int flags, void *arg, int *parent_tidptr,
+ void *newtls, int *child_tidptr) {
+ int64_t __res;
+ {
+ register int64_t __res_a0 __asm__("a0");
+ register uint64_t __flags __asm__("a0") = flags;
+ register void *__stack __asm__("a1") = child_stack;
+ register void *__ptid __asm__("a2") = parent_tidptr;
+ register void *__tls __asm__("a3") = newtls;
+ register int *__ctid __asm__("a4") = child_tidptr;
+ __asm__ __volatile__(/* Push "arg" and "fn" onto the stack that will be
+ * used by the child.
+ */
+ "addi.d %2, %2, -16\n"
+ "st.d %1, %2, 8\n"
+ "st.d %4, %2, 0\n"
+
+ /* %a0 = syscall(%a0 = flags,
+ * %a1 = child_stack,
+ * %a2 = parent_tidptr,
+ * %a3 = newtls,
+ * %a4 = child_tidptr)
+ */
+ "li.d $a7, %8\n"
+ "syscall 0x0\n"
+
+ /* if (%a0 != 0)
+ * return %a0;
+ */
+ "bnez $a0, 1f\n"
+
+ /* In the child, now. Call "fn(arg)".
+ */
+ "ld.d $a0, $sp, 0\n"
+ "ld.d $a1, $sp, 8\n"
+ "addi.d $sp, $sp, 16\n"
+ "jirl $ra, $a1, 0\n"
+
+ /* Call _exit(%a0).
+ */
+ "li.d $a7, %9\n"
+ "syscall 0x0\n"
+ "1:\n"
+ : "=r" (__res_a0)
+ : "r"(fn), "r"(__stack), "r"(__flags), "r"(arg),
+ "r"(__ptid), "r"(__tls), "r"(__ctid),
+ "i"(__NR_clone), "i"(__NR_exit)
+ : LSS_SYSCALL_CLOBBERS);
+ __res = __res_a0;
+ }
+ LSS_RETURN(int, __res);
+ }
#endif
#define __NR__exit __NR_exit
@@ -3827,8 +4002,10 @@
// fork is polyfilled below when not available.
LSS_INLINE _syscall0(pid_t, fork)
#endif
+ #if defined(__NR_fstat)
LSS_INLINE _syscall2(int, fstat, int, f,
struct kernel_stat*, b)
+ #endif
LSS_INLINE _syscall2(int, fstatfs, int, f,
struct kernel_statfs*, b)
#if defined(__x86_64__)
@@ -3863,10 +4040,10 @@
gid_t *, e, gid_t *, s)
LSS_INLINE _syscall3(int, getresuid, uid_t *, r,
uid_t *, e, uid_t *, s)
-#if !defined(__ARM_EABI__)
+ #if defined(__NR_getrlimit)
LSS_INLINE _syscall2(int, getrlimit, int, r,
struct kernel_rlimit*, l)
-#endif
+ #endif
LSS_INLINE _syscall1(pid_t, getsid, pid_t, p)
LSS_INLINE _syscall0(pid_t, _gettid)
LSS_INLINE _syscall2(pid_t, gettimeofday, struct kernel_timeval*, t,
@@ -3984,8 +4161,10 @@
gid_t, e, gid_t, s)
LSS_INLINE _syscall3(int, setresuid, uid_t, r,
uid_t, e, uid_t, s)
+ #if defined(__NR_setrlimit)
LSS_INLINE _syscall2(int, setrlimit, int, r,
const struct kernel_rlimit*, l)
+ #endif
LSS_INLINE _syscall0(pid_t, setsid)
LSS_INLINE _syscall2(int, sigaltstack, const stack_t*, s,
const stack_t*, o)
@@ -4019,23 +4198,6 @@
LSS_INLINE _syscall3(long, getcpu, unsigned *, cpu,
unsigned *, node, void *, unused)
#endif
- #if defined(__x86_64__) || defined(__e2k__) || \
- (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32)
- LSS_INLINE _syscall3(int, recvmsg, int, s,
- struct kernel_msghdr*, m, int, f)
- LSS_INLINE _syscall3(int, sendmsg, int, s,
- const struct kernel_msghdr*, m, int, f)
- LSS_INLINE _syscall6(int, sendto, int, s,
- const void*, m, size_t, l,
- int, f,
- const struct kernel_sockaddr*, a, int, t)
- LSS_INLINE _syscall2(int, shutdown, int, s,
- int, h)
- LSS_INLINE _syscall3(int, socket, int, d,
- int, t, int, p)
- LSS_INLINE _syscall4(int, socketpair, int, d,
- int, t, int, p, int*, s)
- #endif
#if defined(__NR_fadvise64)
#if defined(__x86_64__)
/* Need to make sure loff_t isn't truncated to 32-bits under x32. */
@@ -4118,6 +4280,12 @@
const char *, p,
struct kernel_stat*, b, int, f)
#endif
+ #if defined(__NR_statx)
+ LSS_INLINE _syscall5(int, statx, int, d,
+ const char *, p,
+ int, f, int, m,
+ struct kernel_statx*, b)
+ #endif
#if defined(__x86_64__) || defined(__s390x__)
LSS_INLINE int LSS_NAME(getresgid32)(gid_t *rgid,
gid_t *egid,
@@ -4614,23 +4782,31 @@
LSS_SC_BODY(4, int, 8, d, type, protocol, sv);
}
#endif
- #if defined(__ARM_EABI__) || defined (__aarch64__) || defined(__riscv)
+ #if defined(__NR_recvmsg)
LSS_INLINE _syscall3(ssize_t, recvmsg, int, s, struct kernel_msghdr*, msg,
int, flags)
+ #endif
+ #if defined(__NR_sendmsg)
LSS_INLINE _syscall3(ssize_t, sendmsg, int, s, const struct kernel_msghdr*,
msg, int, flags)
+ #endif
+ #if defined(__NR_sendto)
LSS_INLINE _syscall6(ssize_t, sendto, int, s, const void*, buf, size_t,len,
int, flags, const struct kernel_sockaddr*, to,
unsigned int, tolen)
+ #endif
+ #if defined(__NR_shutdown)
LSS_INLINE _syscall2(int, shutdown, int, s, int, how)
+ #endif
+ #if defined(__NR_socket)
LSS_INLINE _syscall3(int, socket, int, domain, int, type, int, protocol)
+ #endif
+ #if defined(__NR_socketpair)
LSS_INLINE _syscall4(int, socketpair, int, d, int, type, int, protocol,
int*, sv)
#endif
- #if defined(__i386__) || defined(__ARM_ARCH_3__) || \
- (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \
- defined(__s390__)
- #define __NR__socketcall __NR_socketcall
+
+ #if defined(__NR_socketcall)
LSS_INLINE _syscall2(int, _socketcall, int, c,
va_list, a)
LSS_INLINE int LSS_NAME(socketcall)(int op, ...) {
@@ -4642,36 +4818,43 @@
return rc;
}
+ # if !defined(__NR_recvmsg)
LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg,
int flags){
return (ssize_t)LSS_NAME(socketcall)(17, s, msg, flags);
}
-
+ # endif
+ # if !defined(__NR_sendmsg)
LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s,
const struct kernel_msghdr *msg,
int flags) {
return (ssize_t)LSS_NAME(socketcall)(16, s, msg, flags);
}
-
+ # endif
+ # if !defined(__NR_sendto)
LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len,
int flags,
const struct kernel_sockaddr *to,
unsigned int tolen) {
return (ssize_t)LSS_NAME(socketcall)(11, s, buf, len, flags, to, tolen);
}
-
+ # endif
+ # if !defined(__NR_shutdown)
LSS_INLINE int LSS_NAME(shutdown)(int s, int how) {
return LSS_NAME(socketcall)(13, s, how);
}
-
+ # endif
+ # if !defined(__NR_socket)
LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) {
return LSS_NAME(socketcall)(1, domain, type, protocol);
}
-
+ # endif
+ # if !defined(__NR_socketpair)
LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol,
int sv[2]) {
return LSS_NAME(socketcall)(8, d, type, protocol, sv);
}
+ # endif
#endif
#if defined(__NR_fstatat64)
LSS_INLINE _syscall4(int, fstatat64, int, d,
@@ -4920,16 +5103,79 @@
}
#endif
+#if defined(__NR_statx)
+ /* copy the contents of kernel_statx to the kernel_stat structure. */
+ LSS_INLINE void LSS_NAME(cp_stat_statx)(struct kernel_stat *to,
+ struct kernel_statx *from) {
+ memset(to, 0, sizeof(struct kernel_stat));
+ to->st_dev = ((from->stx_dev_minor & 0xff) |
+ ((from->stx_dev_major & 0xfff) << 8) |
+ ((from->stx_dev_minor & ~0xff) << 12));
+ to->st_rdev = ((from->stx_rdev_minor & 0xff) |
+ ((from->stx_rdev_major & 0xfff) << 8) |
+ ((from->stx_rdev_minor & ~0xff) << 12));
+ to->st_ino = from->stx_ino;
+ to->st_mode = from->stx_mode;
+ to->st_nlink = from->stx_nlink;
+ to->st_uid = from->stx_uid;
+ to->st_gid = from->stx_gid;
+ to->st_atime_ = from->stx_atime.tv_sec;
+ to->st_atime_nsec_ = from->stx_atime.tv_nsec;
+ to->st_mtime_ = from->stx_mtime.tv_sec;
+ to->st_mtime_nsec_ = from->stx_mtime.tv_nsec;
+ to->st_ctime_ = from->stx_ctime.tv_sec;
+ to->st_ctime_nsec_ = from->stx_ctime.tv_nsec;
+ to->st_size = from->stx_size;
+ to->st_blocks = from->stx_blocks;
+ to->st_blksize = from->stx_blksize;
+ }
+#endif
+
+#if !defined(__NR_fstat)
+ LSS_INLINE int LSS_NAME(fstat)(int fd,
+ struct kernel_stat *buf) {
+ #if defined(__NR_newfstatat)
+ return LSS_NAME(newfstatat)(fd, "", buf, AT_EMPTY_PATH);
+ #elif defined(__NR_statx)
+ struct kernel_statx stx;
+ int flags = AT_NO_AUTOMOUNT | AT_EMPTY_PATH;
+ int mask = STATX_BASIC_STATS;
+ int res = LSS_NAME(statx)(fd, "", flags, mask, &stx);
+ LSS_NAME(cp_stat_statx)(buf, &stx);
+ return res;
+ #endif
+ }
+#endif
+
#if !defined(__NR_stat)
LSS_INLINE int LSS_NAME(stat)(const char *pathname,
struct kernel_stat *buf) {
- return LSS_NAME(newfstatat)(AT_FDCWD, pathname, buf, 0);
+ #if defined(__NR_newfstatat)
+ return LSS_NAME(newfstatat)(AT_FDCWD, pathname, buf, 0);
+ #elif defined(__NR_statx)
+ struct kernel_statx stx;
+ int flags = AT_NO_AUTOMOUNT | AT_STATX_SYNC_AS_STAT;
+ int mask = STATX_BASIC_STATS;
+ int res = LSS_NAME(statx)(AT_FDCWD, pathname, flags, mask, &stx);
+ LSS_NAME(cp_stat_statx)(buf, &stx);
+ return res;
+ #endif
}
#endif
+
#if !defined(__NR_lstat)
LSS_INLINE int LSS_NAME(lstat)(const char *pathname,
struct kernel_stat *buf) {
- return LSS_NAME(newfstatat)(AT_FDCWD, pathname, buf, AT_SYMLINK_NOFOLLOW);
+ #if defined(__NR_newfstatat)
+ return LSS_NAME(newfstatat)(AT_FDCWD, pathname, buf, AT_SYMLINK_NOFOLLOW);
+ #elif defined(__NR_statx)
+ struct kernel_statx stx;
+ int flags = AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW;
+ int mask = STATX_BASIC_STATS;
+ int res = LSS_NAME(statx)(AT_FDCWD, pathname, flags, mask, &stx);
+ LSS_NAME(cp_stat_statx)(buf, &stx);
+ return res;
+ #endif
}
#endif
@@ -4943,7 +5189,7 @@
// TODO: define this in an arch-independant way instead of inlining the clone
// syscall body.
-# if defined(__aarch64__) || defined(__riscv)
+# if defined(__aarch64__) || defined(__riscv) || defined(__loongarch_lp64)
LSS_INLINE pid_t LSS_NAME(fork)(void) {
// No fork syscall on aarch64 - implement by means of the clone syscall.
// Note that this does not reset glibc's cached view of the PID/TID, so
diff --git a/tests/Makefile b/tests/Makefile
index 7b18ed2..b54e0f5 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -116,6 +116,7 @@
"mips64-unknown-linux-gnu-gcc -mabi=n32" \
"s390-ibm-linux-gnu-gcc" \
"s390x-ibm-linux-gnu-gcc" \
+ "loongarch64-unknown-linux-gnu-gcc" \
; do \
cxx=`echo "$$cc" | sed 's:-gcc:-g++:'`; \
$(MAKE) --no-print-directory CC="$$cc" CXX="$$cxx" cross_compile; \