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