Re: [rfc/rfa] Use ARM exception tables as GDB unwinder

Ulrich Weigand
2011-03-09T18:43:23Z

Daniel Jacobowitz wrote:
> On Wed, Dec 01, 2010 at 05:45:31PM +0100, Ulrich Weigand wrote:
> > Hmm, looking at DO_CALL a bit more I think this can actually be handled
> > inline just fine:
> >
> > #define DO_CALL(syscall_name, args) \
> > DOARGS_##args; \
> > mov ip, r7; \
> > cfi_register (r7, ip); \
> > ldr r7, =SYS_ify (syscall_name); \
> > swi 0x0; \
> > mov r7, ip; \
> > cfi_restore (r7); \
> > UNDOARGS_##args
> >
> > Note the calls to DOARGS_... / UNDOARGS_..., which actually save and
> > restore registers on the stack anyway (just not r7). Why not simply
> > have (a variant) of [UN]DOARGS_... save r7 to the stack, and then
> > provide both correct CFI and ARM unwind records for it? [ Saving
> > to IP would then be no longer necessary. ]
> >
> > Am I missing something here?
>
> I don't think you are missing anything, except a couple of cycles.
>
> We save r7 in ip because it ought to be faster loading and storing it
> on the stack. But we already take the position that the system call
> overhead is substantial compared to DO_CALL... so the extra two memory
> ops do not seem like a huge loss to me, especially if we can save on
> code size.

I finally got around to continue working on this. The patch below
implements this approach. However, this uncovered one rather fundamental
problem: when implementing the vfork system call, the syscall stub
must *not* put anything on the stack or else things will break due to
the peculiar semantics of vfork:

The system will return to execute solely the child at first, until such
time as the child calls exec; and in the meantime, the child will share
the parent's address space. Now if vfork pushed something on the stack,
the child will pop that value, and go on to overwrite that stack location
with other stuff. Once the parent later gets to run as well, it will
restore a corrupted value into r7 ...

I'm not quite sure if there is a way around this ... Any suggestions
would be appreciated.

> I haven't looked at this code in a while, but isn't
> nptl/sysdep-cancel.h:PSEUDO broken for non-cancellable syscalls with
> many arguments? It calls DOARGS and then DO_CALL, but DO_CALL does
> DOARGS again. For 0-4 arguments it doesn't matter; I don't know if
> any of the 5/6/7 argument calls have a used nocancel variant.

As Andreas notes, DOARGS is called with a 0 argument. However, I'm
wondering if there isn't a bug anyway:

__##syscall_name##_nocancel: \
.cfi_sections .debug_frame; \
cfi_startproc; \
DO_CALL (syscall_name, args); \
PSEUDO_RET; \
cfi_endproc; \
.size __##syscall_name##_nocancel,.-__##syscall_name##_nocancel; \

Shouldn't there be a
cmn r0, $4096;
in between the DO_CALL and the PSEUDO_RET?

Also, some of the code in the linuxthreads/ directories seems to be broken,
but I'm wondering why those are still there in the first place; linuxthreads
support has been removed from glibc a long time ago, hasn't it?

Bye,
Ulrich

diff --git a/sysdeps/unix/sysv/linux/arm/eabi/nptl/sysdep-cancel.h b/sysdeps/unix/sysv/linux/arm/eabi/nptl/sysdep-cancel.h
index 458558b..7819578 100644
--- a/sysdeps/unix/sysv/linux/arm/eabi/nptl/sysdep-cancel.h
+++ b/sysdeps/unix/sysv/linux/arm/eabi/nptl/sysdep-cancel.h
@@ -44,29 +44,22 @@
.size __##syscall_name##_nocancel,.-__##syscall_name##_nocancel; \
ENTRY (name); \
SINGLE_THREAD_P; \
- DOARGS_##args; \
bne .Lpseudo_cancel; \
- cfi_remember_state; \
- DO_CALL (syscall_name, 0); \
- UNDOARGS_##args; \
+ DO_CALL (syscall_name, args); \
cmn r0, $4096; \
PSEUDO_RET; \
- cfi_restore_state; \
.Lpseudo_cancel: \
- .fnstart; \
DOCARGS_##args; /* save syscall args etc. around CENABLE. */ \
CENABLE; \
mov ip, r0; /* put mask in safe place. */ \
UNDOCARGS_##args; /* restore syscall args. */ \
ldr r7, =SYS_ify (syscall_name); \
swi 0x0; /* do the call. */ \
- .fnend; /* Past here we can't easily unwind. */ \
mov r7, r0; /* save syscall return value. */ \
mov r0, ip; /* get mask back. */ \
CDISABLE; \
mov r0, r7; /* retrieve return value. */ \
RESTORE_LR_##args; \
- UNDOARGS_##args; \
cmn r0, $4096

/* DOARGS pushes four bytes on the stack for five arguments, eight bytes for
@@ -79,6 +72,7 @@
.save {r7, lr}

# define DOCARGS_0 \
+ .fnstart; \
stmfd sp!, {r7, lr}; \
cfi_adjust_cfa_offset (8); \
cfi_rel_offset (r7, 0); \
@@ -89,9 +83,11 @@
ldmfd sp!, {r7, lr}; \
cfi_adjust_cfa_offset (-8); \
cfi_restore (r7); \
- cfi_restore (lr)
+ cfi_restore (lr); \
+ .fnend

# define DOCARGS_1 \
+ .fnstart; \
stmfd sp!, {r0, r1, r7, lr}; \
cfi_adjust_cfa_offset (16); \
cfi_rel_offset (r7, 8); \
@@ -106,6 +102,7 @@
RESTORE_LR_0

# define DOCARGS_2 \
+ .fnstart; \
stmfd sp!, {r0, r1, r7, lr}; \
cfi_adjust_cfa_offset (16); \
cfi_rel_offset (r7, 8); \
@@ -120,6 +117,7 @@
RESTORE_LR_0

# define DOCARGS_3 \
+ .fnstart; \
stmfd sp!, {r0, r1, r2, r3, r7, lr}; \
cfi_adjust_cfa_offset (24); \
cfi_rel_offset (r7, 16); \
@@ -134,6 +132,7 @@
RESTORE_LR_0

# define DOCARGS_4 \
+ .fnstart; \
stmfd sp!, {r0, r1, r2, r3, r7, lr}; \
cfi_adjust_cfa_offset (24); \
cfi_rel_offset (r7, 16); \
@@ -147,47 +146,58 @@
# define RESTORE_LR_4 \
RESTORE_LR_0

-/* r4 is only stmfd'ed for correct stack alignment. */
# define DOCARGS_5 \
- .save {r4}; \
- stmfd sp!, {r0, r1, r2, r3, r4, r7, lr}; \
- cfi_adjust_cfa_offset (28); \
- cfi_rel_offset (r7, 20); \
- cfi_rel_offset (lr, 24); \
- .save {r7, lr}; \
- .pad #20
+ .fnstart; \
+ stmfd sp!, {r0, r1, r2, r3, r4, r5, r7, lr}; \
+ cfi_adjust_cfa_offset (32); \
+ cfi_rel_offset (r4, 16); \
+ cfi_rel_offset (r5, 20); \
+ cfi_rel_offset (r7, 24); \
+ cfi_rel_offset (lr, 28); \
+ .save {r4, r5, r7, lr}; \
+ .pad #16; \
+ ldr r4, [sp, $32]
# define UNDOCARGS_5 \
ldmfd sp!, {r0, r1, r2, r3}; \
cfi_adjust_cfa_offset (-16); \
.fnend; \
.fnstart; \
- .save {r4}; \
- .save {r7, lr}; \
- .pad #4
+ .save {r4, r5, r7, lr};
# define RESTORE_LR_5 \
- ldmfd sp!, {r4, r7, lr}; \
- cfi_adjust_cfa_offset (-12); \
- /* r4 will be marked as restored later. */ \
+ ldmfd sp!, {r4, r5, r7, lr}; \
+ cfi_adjust_cfa_offset (-16); \
+ cfi_restore (r4); \
+ cfi_restore (r5); \
cfi_restore (r7); \
- cfi_restore (lr)
+ cfi_restore (lr); \
+ .fnend

# define DOCARGS_6 \
- .save {r4, r5}; \
- stmfd sp!, {r0, r1, r2, r3, r7, lr}; \
- cfi_adjust_cfa_offset (24); \
+ .fnstart; \
+ mov ip, sp; \
+ stmfd sp!, {r0, r1, r2, r3, r4, r5, r7, lr}; \
+ cfi_adjust_cfa_offset (32); \
+ cfi_rel_offset (r4, 16); \
+ cfi_rel_offset (r5, 20); \
cfi_rel_offset (r7, 16); \
cfi_rel_offset (lr, 20); \
- .save {r7, lr}; \
- .pad #16
+ .save {r4, r5, r7, lr}; \
+ .pad #16; \
+ ldmia ip, {r4, r5}
# define UNDOCARGS_6 \
ldmfd sp!, {r0, r1, r2, r3}; \
cfi_adjust_cfa_offset (-16); \
.fnend; \
.fnstart; \
- .save {r4, r5}; \
- .save {r7, lr}
+ .save {r4, r5, r7, lr};
# define RESTORE_LR_6 \
- RESTORE_LR_0
+ ldmfd sp!, {r4, r5, r7, lr}; \
+ cfi_adjust_cfa_offset (-16); \
+ cfi_restore (r4); \
+ cfi_restore (r5); \
+ cfi_restore (r7); \
+ cfi_restore (lr); \
+ .fnend

# ifdef IS_IN_libpthread
# define CENABLE bl PLTJMP(__pthread_enable_asynccancel)
diff --git a/sysdeps/unix/sysv/linux/arm/eabi/sysdep.h b/sysdeps/unix/sysv/linux/arm/eabi/sysdep.h
index b7815ba..70bbfa6 100644
--- a/sysdeps/unix/sysv/linux/arm/eabi/sysdep.h
+++ b/sysdeps/unix/sysv/linux/arm/eabi/sysdep.h
@@ -106,12 +106,95 @@
#undef DO_CALL
#define DO_CALL(syscall_name, args) \
DOARGS_##args; \
- mov ip, r7; \
- cfi_register (r7, ip); \
ldr r7, =SYS_ify (syscall_name); \
swi 0x0; \
- mov r7, ip; \
- cfi_restore (r7); \
UNDOARGS_##args

+#undef DOARGS_0
+#define DOARGS_0 \
+ .fnstart; \
+ str r7, [sp, $-4]!; \
+ cfi_adjust_cfa_offset (4); \
+ cfi_rel_offset (r7, 0); \
+ .save { r7 }
+#undef DOARGS_1
+#define DOARGS_1 DOARGS_0
+#undef DOARGS_2
+#define DOARGS_2 DOARGS_0
+#undef DOARGS_3
+#define DOARGS_3 DOARGS_0
+#undef DOARGS_4
+#define DOARGS_4 DOARGS_0
+#undef DOARGS_5
+#define DOARGS_5 \
+ .fnstart; \
+ stmfd sp!, {r4, r7}; \
+ cfi_adjust_cfa_offset (8); \
+ cfi_rel_offset (r4, 0); \
+ cfi_rel_offset (r7, 4); \
+ .save { r4, r7 }; \
+ ldr r4, [sp, $8]
+#undef DOARGS_6
+#define DOARGS_6 \
+ .fnstart; \
+ mov ip, sp; \
+ stmfd sp!, {r4, r5, r7}; \
+ cfi_adjust_cfa_offset (12); \
+ cfi_rel_offset (r4, 0); \
+ cfi_rel_offset (r5, 4); \
+ cfi_rel_offset (r7, 8); \
+ .save { r4, r5, r7 }; \
+ ldmia ip, {r4, r5}
+#undef DOARGS_7
+#define DOARGS_7 \
+ .fnstart; \
+ mov ip, sp; \
+ stmfd sp!, {r4, r5, r6, r7}; \
+ cfi_adjust_cfa_offset (16); \
+ cfi_rel_offset (r4, 0); \
+ cfi_rel_offset (r5, 4); \
+ cfi_rel_offset (r6, 8); \
+ cfi_rel_offset (r7, 12); \
+ .save { r4, r5, r6, r7 }; \
+ ldmia ip, {r4, r5, r6}
+
+#undef UNDOARGS_0
+#define UNDOARGS_0 \
+ ldr r7, [sp], $4; \
+ cfi_adjust_cfa_offset (-4); \
+ cfi_restore (r7); \
+ .fnend
+#undef UNDOARGS_1
+#define UNDOARGS_1 UNDOARGS_0
+#undef UNDOARGS_2
+#define UNDOARGS_2 UNDOARGS_0
+#undef UNDOARGS_3
+#define UNDOARGS_3 UNDOARGS_0
+#undef UNDOARGS_4
+#define UNDOARGS_4 UNDOARGS_0
+#undef UNDOARGS_5
+#define UNDOARGS_5 \
+ ldmfd sp!, {r4, r7}; \
+ cfi_adjust_cfa_offset (-8); \
+ cfi_restore (r4); \
+ cfi_restore (r7); \
+ .fnend
+#undef UNDOARGS_6
+#define UNDOARGS_6 \
+ ldmfd sp!, {r4, r5, r7}; \
+ cfi_adjust_cfa_offset (-12); \
+ cfi_restore (r4); \
+ cfi_restore (r5); \
+ cfi_restore (r7); \
+ .fnend
+#undef UNDOARGS_7
+#define UNDOARGS_7 \
+ ldmfd sp!, {r4, r5, r6, r7}; \
+ cfi_adjust_cfa_offset (-16); \
+ cfi_restore (r4); \
+ cfi_restore (r5); \
+ cfi_restore (r6); \
+ cfi_restore (r7); \
+ .fnend
+
#endif /* _LINUX_ARM_EABI_SYSDEP_H */