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-yno...@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.szyprow...@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

diff --git a/configs/targets/aarch64_ilp32-linux-user.mak 
b/configs/targets/aarch64_ilp32-linux-user.mak
new file mode 100644
index 0000000000..e70fd98eef
--- /dev/null
+++ b/configs/targets/aarch64_ilp32-linux-user.mak
@@ -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
diff --git a/linux-user/aarch64/syscall_64.tbl 
b/linux-user/aarch64/syscall_64.tbl
index 845e24eb37..33507d823b 100644
--- a/linux-user/aarch64/syscall_64.tbl
+++ b/linux-user/aarch64/syscall_64.tbl
@@ -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
diff --git a/linux-user/aarch64/target_mman.h b/linux-user/aarch64/target_mman.h
index 69ec5d5739..7356235a40 100644
--- a/linux-user/aarch64/target_mman.h
+++ b/linux-user/aarch64/target_mman.h
@@ -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"
 
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index a2c152e5ad..70d2913915 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -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 */
 
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 5f00750151..1acf50b2b7 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -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;
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 02ea4221c9..1012686227 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -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;
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index 86d773add7..292939575c 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -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 {
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index b9b05c1d11..b13ca36577 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -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;
diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh
index 6ef9f118d9..695d15e6ea 100755
--- a/scripts/qemu-binfmt-conf.sh
+++ b/scripts/qemu-binfmt-conf.sh
@@ -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)
diff --git a/target/arm/cpu-param.h b/target/arm/cpu-param.h
index 896b35bd6d..151d512e91 100644
--- a/target/arm/cpu-param.h
+++ b/target/arm/cpu-param.h
@@ -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
-- 
2.34.1


Reply via email to