Somebody reported this on stackoverflow. Before I spend too much time thinking about how this ought to work, does anybody have the elfload.c intended operation in their head still? Bug description and analysis of what goes wrong below:
https://stackoverflow.com/questions/64956322/alignment-requirements-for-arm64-elf-executables-run-in-qemu-assembled-by-gas Given this trivial asm: ===begin program.s=== // GNU Assembler, ARM64 Linux .bss .lcomm ARRAY, 16 .text .global _start _start: mov x8, 93 // exit sys num mov x0, 0 // success svc 0 ===endit=== clang -nostdlib -fno-integrated-as -target aarch64-linux-gnu -s program.s -o program.out the resulting program.out works fine on a real kernel but makes qemu-aarch64 SEGV: $ ./build/x86/qemu-aarch64 /tmp/program.out Segmentation fault (core dumped) Looking at it with gdb, the segv is from QEMU itself, in zero_bss(): if (host_start < host_map_start) { memset((void *)host_start, 0, host_map_start - host_start); } We try to clear the memory from 'host_start' to 'host_map_start' (the latter being the round-up-to-host-page version of the former), but for this binary we never mmap()ed host_start as writeable, so we segv inside the memset. The guest-binary relevant bits of /proc/maps are: 00400000-00401000 r--p 00000000 08:15 5767333 /tmp/program.out 00401000-01411000 ---p 00000000 00:00 0 The program header is: Program Header: LOAD off 0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**16 filesz 0x00000000000000bc memsz 0x00000000000000bc flags r-x LOAD off 0x00000000000000c0 vaddr 0x00000000004100c0 paddr 0x00000000004100c0 align 2**16 filesz 0x0000000000000000 memsz 0x0000000000000010 flags rw- and in zero_bss() host_start == 0x4100c0, host_map_start == 0x411000, last_bss == 0x411000. The code calls page_set_flags(start = 0x410000, end = 0x411000, flags=PAGE_VALID|PAGE_READ|PAGE_WRITE) with, I assume, the intention that that will make the memset() OK, but page_set_flags() just sets up some data structure bits which will mean that a subsequent SEGV will cause us to do a page_unprotect() that sets the page as actually writeable. Unfortunately the SIGSEGV handler is not installed until quite late in linux-user/main.c, so when the memset() in zero_bss() SEGVs QEMU just dies. Should we try to get the SEGV handler working earlier in initialization (it's pretty hairy machinery so that could be tricky) or should elfload.c be mprotect()ing things appropriately itself? thanks -- PMM