new file mode 100644
@@ -0,0 +1,10 @@
+TARGET_ARCH=aarch64
+TARGET_BASE_ARCH=arm
+TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/aarch64-pauth.xml gdb-xml/aarch64-mte.xml
+TARGET_HAS_BFLT=y
+CONFIG_SEMIHOSTING=y
+CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
+TARGET_SYSTBL_ABI=common,32,time32,stat64,renameat,rlimit,memfd_secret
+TARGET_SYSTBL=syscall_64.tbl
+TARGET_LONG_BITS=64
+TARGET_ABI32=y
@@ -265,7 +265,7 @@
221 common execve sys_execve compat_sys_execve
222 32 mmap2 sys_mmap2
222 64 mmap sys_mmap
-223 32 fadvise64_64 sys_fadvise64_64 compat_sys_fadvise64_64
+223 32 arm_fadvise64_64 sys_arm_fadvise64_64
223 64 fadvise64 sys_fadvise64_64
224 common swapon sys_swapon
225 common swapoff sys_swapoff
@@ -4,6 +4,7 @@
#define TARGET_PROT_BTI 0x10
#define TARGET_PROT_MTE 0x20
+#ifndef TARGET_ABI32
/*
* arch/arm64/include/asm/processor.h:
*
@@ -16,6 +17,11 @@
/* arch/arm64/include/asm/elf.h */
#define ELF_ET_DYN_BASE TARGET_PAGE_ALIGN((1ull << 48) / 3 * 2)
+#else
+/* aarch64_ilp32 */
+#define TASK_UNMAPPED_BASE (1ull << (30))
+#define ELF_ET_DYN_BASE TARGET_PAGE_ALIGN((1ull << 30) / 3 * 2)
+#endif
#include "../generic/target_mman.h"
@@ -682,7 +682,11 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags)
/* 64 bit ARM definitions */
#define ELF_ARCH EM_AARCH64
+#ifndef TARGET_ABI32
#define ELF_CLASS ELFCLASS64
+#else
+#define ELF_CLASS ELFCLASS32
+#endif
#if TARGET_BIG_ENDIAN
# define ELF_PLATFORM "aarch64_be"
#else
@@ -977,11 +981,13 @@ const char *elf_hwcap2_str(uint32_t bit)
#undef GET_FEATURE_ID
+#ifndef TARGET_ABI32
#if TARGET_BIG_ENDIAN
# define VDSO_HEADER "vdso-be.c.inc"
#else
# define VDSO_HEADER "vdso-le.c.inc"
#endif
+#endif
#endif /* not TARGET_AARCH64 */
@@ -86,7 +86,7 @@ struct vm86_saved_state {
};
#endif
-#if defined(TARGET_ARM) && defined(TARGET_ABI32)
+#if defined(TARGET_ARM) && defined(TARGET_ABI32) && !defined(TARGET_AARCH64)
/* FPU emulator */
#include "nwfpe/fpa11.h"
#endif
@@ -98,7 +98,7 @@ struct emulated_sigtable {
struct TaskState {
pid_t ts_tid; /* tid (or pid) of this task */
-#ifdef TARGET_ARM
+#if defined(TARGET_ARM) && !defined(TARGET_AARCH64)
# ifdef TARGET_ABI32
/* FPA state */
FPA11 fpa;
@@ -6936,7 +6936,7 @@ static inline abi_long copy_to_user_flock(abi_ulong target_flock_addr,
typedef abi_long from_flock64_fn(struct flock *fl, abi_ulong target_addr);
typedef abi_long to_flock64_fn(abi_ulong target_addr, const struct flock *fl);
-#if defined(TARGET_ARM) && TARGET_ABI_BITS == 32
+#if defined(TARGET_ARM) && TARGET_ABI_BITS == 32 && !defined(TARGET_AARCH64)
struct target_oabi_flock64 {
abi_short l_type;
abi_short l_whence;
@@ -7642,7 +7642,7 @@ static inline abi_long host_to_target_stat64(CPUArchState *cpu_env,
abi_ulong target_addr,
struct stat *host_st)
{
-#if defined(TARGET_ARM) && defined(TARGET_ABI32)
+#if defined(TARGET_ARM) && defined(TARGET_ABI32) && !defined(TARGET_AARCH64)
if (cpu_env->eabi) {
struct target_eabi_stat64 *target_st;
@@ -12533,7 +12533,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
from_flock64_fn *copyfrom = copy_from_user_flock64;
to_flock64_fn *copyto = copy_to_user_flock64;
-#ifdef TARGET_ARM
+#if defined(TARGET_ARM) && !defined(TARGET_AARCH64)
if (!cpu_env->eabi) {
copyfrom = copy_from_user_oabi_flock64;
copyto = copy_to_user_oabi_flock64;
@@ -60,7 +60,7 @@
#define TARGET_IOC_TYPEBITS 8
#if (defined(TARGET_I386) && defined(TARGET_ABI32)) \
- || (defined(TARGET_ARM) && defined(TARGET_ABI32)) \
+ || (defined(TARGET_ARM) && defined(TARGET_ABI32) && !defined(TARGET_AARCH64)) \
|| (defined(TARGET_SPARC) && defined(TARGET_ABI32)) \
|| defined(TARGET_M68K) || defined(TARGET_SH4)
/* 16 bit uid wrappers emulation */
@@ -1234,7 +1234,7 @@ struct target_winsize {
#include "target_mman.h"
#if (defined(TARGET_I386) && defined(TARGET_ABI32)) \
- || (defined(TARGET_ARM) && defined(TARGET_ABI32))
+ || (defined(TARGET_ARM) && defined(TARGET_ABI32) && !defined(TARGET_AARCH64))
#define TARGET_STAT_HAVE_NSEC
struct target_stat {
abi_ushort st_dev;
@@ -1905,7 +1905,7 @@ struct target_stat {
abi_long st_blocks;
abi_ulong __unused[3];
};
-#elif defined(TARGET_AARCH64)
+#elif defined(TARGET_AARCH64) && !defined(TARGET_ABI32)
#define TARGET_STAT_HAVE_NSEC
struct target_stat {
abi_ulong st_dev;
@@ -1928,6 +1928,65 @@ struct target_stat {
abi_ulong target_st_ctime_nsec;
abi_uint __unused[2];
};
+#elif defined(TARGET_AARCH64) && defined(TARGET_ABI32)
+#define TARGET_STAT_HAVE_NSEC
+struct target_stat {
+ abi_ulong st_dev;
+ abi_ulong __ilp32_pad1;
+ abi_ulong st_ino;
+ abi_ulong __ilp32_pad2;
+ abi_uint st_mode;
+ abi_uint st_nlink;
+ abi_uint st_uid;
+ abi_uint st_gid;
+ abi_ulong st_rdev;
+ abi_ulong __ilp32_pad3;
+ abi_ulong _pad1;
+ abi_ulong __ilp32_pad4;
+ abi_long st_size;
+ abi_ulong __ilp32_pad5;
+ abi_int st_blksize;
+ abi_int __pad2;
+ abi_long st_blocks;
+ abi_ulong __ilp32_pad6;
+ abi_long target_st_atime;
+ abi_ulong target_st_atime_nsec;
+ abi_long target_st_mtime;
+ abi_ulong target_st_mtime_nsec;
+ abi_long target_st_ctime;
+ abi_ulong target_st_ctime_nsec;
+ abi_uint __unused[2];
+};
+
+#define TARGET_HAS_STRUCT_STAT64
+struct target_stat64 {
+ abi_ulong st_dev;
+ abi_ulong __ilp32_pad1;
+#define TARGET_STAT64_HAS_BROKEN_ST_INO 1
+ abi_ulong __st_ino;
+ abi_ulong __ilp32_pad2;
+ abi_uint st_mode;
+ abi_uint st_nlink;
+ abi_uint st_uid;
+ abi_uint st_gid;
+ abi_ulong st_rdev;
+ abi_ulong __ilp32_pad3;
+ abi_ulong __pad1;
+ abi_ulong __ilp32_pad4;
+ abi_llong st_size;
+ abi_int st_blksize;
+ abi_int _pad2;
+ abi_long st_blocks;
+ abi_ulong __ilp32_pad5;
+ abi_long target_st_atime;
+ abi_ulong target_st_atime_nsec;
+ abi_long target_st_mtime;
+ abi_ulong target_st_mtime_nsec;
+ abi_long target_st_ctime;
+ abi_ulong target_st_ctime_nsec;
+ abi_ullong st_ino;
+} QEMU_PACKED;
+
#elif defined(TARGET_XTENSA)
#define TARGET_STAT_HAVE_NSEC
struct target_stat {
@@ -130,8 +130,10 @@ static inline uint64_t target_offset64(uint64_t word0, uint64_t word1)
void print_termios(void *arg);
+#if (TARGET_ABI_BITS == 32) && defined(TARGET_AARCH64)
+static inline int regpairs_aligned(CPUArchState *cpu_env, int num) { return 1; }
/* ARM EABI and MIPS expect 64bit types aligned even on pairs or registers */
-#ifdef TARGET_ARM
+#elif TARGET_ARM
static inline int regpairs_aligned(CPUArchState *cpu_env, int num)
{
return cpu_env->eabi;
@@ -104,6 +104,10 @@ aarch64_be_magic='\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0
aarch64_be_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
aarch64_be_family=armeb
+aarch64_ilp32_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00'
+aarch64_ilp32_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
+aarch64_ilp32_family=arm
+
hppa_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x0f'
hppa_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
hppa_family=hppa
@@ -159,7 +163,7 @@ qemu_get_family() {
ppc64el|ppc64le)
echo "ppcle"
;;
- arm|armel|armhf|arm64|armv[4-9]*l|aarch64)
+ arm|armel|armhf|arm64|armv[4-9]*l|aarch64|aarch64_ilp32)
echo "arm"
;;
armeb|armv[4-9]*b|aarch64_be)
@@ -8,9 +8,13 @@
#ifndef ARM_CPU_PARAM_H
#define ARM_CPU_PARAM_H
-#ifdef TARGET_AARCH64
+#if defined(TARGET_AARCH64) && !defined(TARGET_ABI32)
# define TARGET_PHYS_ADDR_SPACE_BITS 52
# define TARGET_VIRT_ADDR_SPACE_BITS 52
+#elif defined(TARGET_AARCH64) && defined(TARGET_ABI32)
+# define TARGET_LONG_BITS 64
+# define TARGET_PHYS_ADDR_SPACE_BITS 40
+# define TARGET_VIRT_ADDR_SPACE_BITS 32
#else
# define TARGET_PHYS_ADDR_SPACE_BITS 40
# define TARGET_VIRT_ADDR_SPACE_BITS 32
This patch adds support for the AARCH64 ILP32 ABI [1] to the QEMU linux-user AARCH64 port. The ILP32 ABI was initially developed quite some time ago [2] to facilitate porting legacy code to the new AARCH64 architecture. However, it appears that most legacy code is still used as ARMv7 (ARM 32-bit) binaries, running on ARM 64-bit CPUs through the 32-bit EL0 compatibility feature of those CPUs. As a result, the ILP32 ABI has not been widely adopted. The 32-bit EL0 compatibility feature is optional, and it seems that upcoming ARM 64-bit CPUs will not include it [3]. Therefore, the AARCH64 ILP32 ABI can be revived to support running older legacy code. The ILP32 ABI can also be beneficial on systems with tight memory constraints, as 32-bit code typically consumes less memory, both in terms of operation and code size, compared to the same code using a 64-bit ABI. This indicates that there are still important use cases for the AARCH64 ILP32 ABI. Adding support for such binaries in the QEMU linux-user enables common development scenarios, such as simulating a build-system with AARCH64 ILP32 ABI instead of relying on explicit cross-compilation. The qemu-aarch64_ilp32 target as been tested with tests from the Linux Test Project [4] copiled with the toolchain released once by the Linaro [5]. The results are very similar to qemu-arm and other 32bit linux-user targets. Manual inspection of the failures didn't reveal any failures specific to AARCH64 ILP32 ABI version. [1] https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst [2] https://lore.kernel.org/all/20180516081910.10067-1-ynorov@caviumnetworks.com/ [3] https://developer.arm.com/documentation/109697/2024_12/Feature-descriptions/The-Armv9-0-architecture-extension?lang=en [4] https://github.com/linux-test-project/ltp [5] https://snapshots.linaro.org/components/toolchain/binaries/7.3-2018.04-rc1/aarch64-linux-gnu_ilp32/ Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> --- configs/targets/aarch64_ilp32-linux-user.mak | 10 +++ linux-user/aarch64/syscall_64.tbl | 2 +- linux-user/aarch64/target_mman.h | 6 ++ linux-user/elfload.c | 6 ++ linux-user/qemu.h | 4 +- linux-user/syscall.c | 6 +- linux-user/syscall_defs.h | 65 +++++++++++++++++++- linux-user/user-internals.h | 4 +- scripts/qemu-binfmt-conf.sh | 6 +- target/arm/cpu-param.h | 6 +- 10 files changed, 103 insertions(+), 12 deletions(-) create mode 100644 configs/targets/aarch64_ilp32-linux-user.mak