* Issues with modifying pc in a sigaction handler
@ 2021-04-13 22:03 Devin Hussey
2021-04-14 9:49 ` Peter Maydell
0 siblings, 1 reply; 2+ messages in thread
From: Devin Hussey @ 2021-04-13 22:03 UTC (permalink / raw)
To: qemu-devel
In a toy project I was doing
(https://github.com/easyaspi314/ThumbGolf), I found that qemu will
incorrectly handle modifying pc in a handler.
Specifically, on platforms with instruction alignment requirements
(most notably ARM), if you set the pc to an odd address, QEMU will
start reading unaligned instructions.
Naturally, this is frustrating when dealing with ARM Thumb functions
which have the lowest bit set when referenced, as you must manually
clear the Thumb bit instead of it being implicit on hardware.
The following code exhibits this bug for ARM:
---
#include <signal.h>
#include <ucontext.h>
#include <stdio.h>
static void hello(void)
{
printf("Hello,");
}
static void handler(int signo, siginfo_t *si, void *data)
{
ucontext_t *uc = (ucontext_t *)data;
// Effectively bl hello although we assume thumb state
uc->uc_mcontext.arm_lr = uc->uc_mcontext.arm_pc + 2 | 1;
uc->uc_mcontext.arm_pc = (unsigned long)&hello;
}
int main(void)
{
// Set up the signal handler
struct sigaction sa, osa;
sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_NODEFER | SA_SIGINFO;
sa.sa_sigaction = handler;
sigaction(SIGILL, &sa, &osa);
sigaction(SIGTRAP, &sa, &osa);
// Throw a SIGILL, which we do a runtime patch to call hello().
// Make sure we mark the caller saved registers.
__asm__ ("udf #0" ::: "r0", "r1", "r2", "r3", "r12", "lr", "memory");
printf(" world!\n");
}
---
Compile with:
clang -O2 -march=armv7-a -mthumb file.c -static
(The same should happen with GCC).
On hardware (specifically, a Snapdragon 730g in Termux on Android 11),
the code prints "Hello, world!" and exits normally.
However, qemu-arm will get tripped up by the pc being odd, and execute this:
---
... snip
----------------
IN: main
0x00010288: 4c05 ldr r4, [pc, #0x14]
0x0001028a: de00 udf #0
----------------
IN: handler
0x000102a4: 4804 ldr r0, [pc, #0x10]
0x000102a6: 6dd1 ldr r1, [r2, #0x5c]
0x000102a8: 4478 add r0, pc
0x000102aa: 3102 adds r1, #2
0x000102ac: f041 0101 orr r1, r1, #1
0x000102b0: e9c2 1016 strd r1, r0, [r2, #0x58]
0x000102b4: 4770 bx lr
----------------
IN: __restore_rt
0x0004c36c: e3a070ad mov r7, #0xad
0x0004c370: ef000000 svc #0
----------------
IN: hello
0x000102bd: 7848 ldrb r0, [r1, #1]
0x000102bf: 0644 lsls r4, r0, #0x19
0x000102c1: 5cf0 ldrb r0, [r6, r3]
0x000102c3: 06bb lsls r3, r7, #0x1a
0x000102c5: 053e lsls r6, r7, #0x14
0x000102c7: f000 89b5 beq.w #0x90635
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
---
Note the odd addresses in hello().
It should be interpreted as so, which happens when you manually clear
the Thumb bit:
---
0x000102b8: 4801 ldr r0, [pc, #4]
0x000102ba: 4478 add r0, pc
0x000102bc: f006
bb5c b.w #0x16978 (printf)
---
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: Issues with modifying pc in a sigaction handler
2021-04-13 22:03 Issues with modifying pc in a sigaction handler Devin Hussey
@ 2021-04-14 9:49 ` Peter Maydell
0 siblings, 0 replies; 2+ messages in thread
From: Peter Maydell @ 2021-04-14 9:49 UTC (permalink / raw)
To: Devin Hussey; +Cc: QEMU Developers
On Tue, 13 Apr 2021 at 23:44, Devin Hussey <husseydevin@gmail.com> wrote:
>
> In a toy project I was doing
> (https://github.com/easyaspi314/ThumbGolf), I found that qemu will
> incorrectly handle modifying pc in a handler.
>
> Specifically, on platforms with instruction alignment requirements
> (most notably ARM), if you set the pc to an odd address, QEMU will
> start reading unaligned instructions.
>
> Naturally, this is frustrating when dealing with ARM Thumb functions
> which have the lowest bit set when referenced, as you must manually
> clear the Thumb bit instead of it being implicit on hardware.
>
> The following code exhibits this bug for ARM:
>
> ---
> #include <signal.h>
> #include <ucontext.h>
> #include <stdio.h>
>
> static void hello(void)
> {
> printf("Hello,");
> }
>
> static void handler(int signo, siginfo_t *si, void *data)
> {
> ucontext_t *uc = (ucontext_t *)data;
> // Effectively bl hello although we assume thumb state
> uc->uc_mcontext.arm_lr = uc->uc_mcontext.arm_pc + 2 | 1;
> uc->uc_mcontext.arm_pc = (unsigned long)&hello;
This is setting the arm_pc field to an odd number (because
the compiler/linker for Thumb will treat "take the address
of a thumb mode function" as a request for the value with the
LSB set to indicate Thumb mode. However, the ABI for the
uc_mcontext fields is that the arm_pc field should be the actual
required value of the PC -- if you want to go to somewhere in
Thumb mode you need to set the arm_pc field to the true PC
(with the LSB clear) and also set the T bit in the CPSR field
of the uc_mcontext.
So your code is doing something it shouldn't and has thus
wandered off into a corner case where QEMU behaves differently
from the hardware...
thanks
-- PMM
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2021-04-14 9:51 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-13 22:03 Issues with modifying pc in a sigaction handler Devin Hussey
2021-04-14 9:49 ` Peter Maydell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).