Support building the 'sim' with optimization levels higher than -O0. The higher optimization levels emit useful warnings that are masked at -O0.
The basic problem with the implementation of 'os_arch_task_stack_init()' is that we are switching stacks from underneath the compiler without any ability to influence how the compiler uses the stack. Thus when setjmp() returns the compiler doesn't know that the stack frame it allocated for local variables doesn't exist anymore on the new stack. This happens to work with the -O0 optimization level because the compiler addresses local variables using the frame pointer (%ebp). Higher optimization levels use offsets from the stack pointer and immediately expose the issue mentioned above. Fix this by switching stacks in 'os_arch_frame_init()' which is implemented in assembly so we can control the use of stack after setjmp() returns. Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/commit/55afa1c6 Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/tree/55afa1c6 Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/diff/55afa1c6 Branch: refs/heads/master Commit: 55afa1c6035d9841413924b44bb6225333102083 Parents: 9717c79 Author: Neel Natu <n...@nahannisys.com> Authored: Fri Mar 4 18:13:55 2016 -0800 Committer: wes3 <w...@micosa.io> Committed: Sun Mar 6 10:53:54 2016 -0800 ---------------------------------------------------------------------- compiler/sim/pkg.yml | 7 ++- libs/os/src/arch/sim/os_arch_sim.c | 84 ++++++++++++------------- libs/os/src/arch/sim/os_arch_stack_frame.s | 68 ++++++++++++++++++++ libs/util/include/util/util.h | 2 + 4 files changed, 113 insertions(+), 48 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/55afa1c6/compiler/sim/pkg.yml ---------------------------------------------------------------------- diff --git a/compiler/sim/pkg.yml b/compiler/sim/pkg.yml index de62ed9..c9ace8f 100644 --- a/compiler/sim/pkg.yml +++ b/compiler/sim/pkg.yml @@ -29,15 +29,16 @@ pkg.keywords: - gcc compiler.path.cc: "/usr/local/bin/gcc-5" +compiler.path.as: "/usr/local/bin/gcc-5 -x assembler-with-cpp" compiler.path.archive: "ar" compiler.path.objdump: "gobjdump" compiler.path.objsize: "objsize" compiler.path.objcopy: "gobjcopy" compiler.flags.base: > - -m32 -Wall -Werror -ggdb -O0 -DMN_OSX + -m32 -Wall -Werror -ggdb -DMN_OSX -compiler.flags.default: [compiler.flags.base] -compiler.flags.debug: [compiler.flags.base, -ggdb -O0] +compiler.flags.default: [compiler.flags.base, -O1] +compiler.flags.debug: [compiler.flags.base, -O0] compiler.ld.mapfile: false http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/55afa1c6/libs/os/src/arch/sim/os_arch_sim.c ---------------------------------------------------------------------- diff --git a/libs/os/src/arch/sim/os_arch_sim.c b/libs/os/src/arch/sim/os_arch_sim.c index d22f8a2..03975ce 100644 --- a/libs/os/src/arch/sim/os_arch_sim.c +++ b/libs/os/src/arch/sim/os_arch_sim.c @@ -31,19 +31,24 @@ #include <signal.h> #include <sys/time.h> #include <assert.h> +#include <util/util.h> -/* XXX: - * Stack must be 16-byte aligned, any changes to this structure - * must keep that alignment. - */ struct stack_frame { + int sf_mainsp; /* stack on which main() is executing */ jmp_buf sf_jb; int sf_sigsblocked; - os_task_func_t sf_func; - void *sf_arg; - int _pad[3]; + struct os_task *sf_task; }; +/* + * Assert that 'sf_mainsp' and 'sf_jb' are at the specific offsets where + * os_arch_frame_init() expects them to be. + */ +CTASSERT(offsetof(struct stack_frame, sf_mainsp) == 0); +CTASSERT(offsetof(struct stack_frame, sf_jb) == 4); + +extern void os_arch_frame_init(struct stack_frame *sf); + #define CTX_SWITCH_ISR (1) #define CTX_SWITCH_TASK (2) @@ -86,54 +91,43 @@ sigs_block(void) sigprocmask(SIG_BLOCK, &g_sigset, NULL); } -os_stack_t * -os_arch_task_stack_init(struct os_task *t, os_stack_t *stack_top, int size) +/* + * Called from 'os_arch_frame_init()' when setjmp returns indirectly via + * longjmp. The return value of setjmp is passed to this function as 'rc'. + */ +void +os_arch_task_start(struct stack_frame *sf, int rc) { - struct os_task *cur_t; - struct stack_frame *sf; - os_stack_t *s; - void *s_cur; - volatile int block_isr_off; - int rc; + struct os_task *task; - s = (os_stack_t *) ((uint8_t *) stack_top - sizeof(*sf)); - sf = (struct stack_frame *) s; - sf->sf_sigsblocked = 0; - sf->sf_func = t->t_func; - sf->sf_arg = t->t_arg; + task = sf->sf_task; - block_isr_off = g_block_isr_off; + isr_state(&g_block_isr_off, NULL); - /* Save the current stack pointer, and then trick setjmp() - * to store the stackos stack pointer. - */ - asm("movl %%esp, %0" : "=r" (s_cur)); - asm("movl %0,%%esp" : /* no output */ : "r" (s)); + if (rc == CTX_SWITCH_ISR) { + sigs_unblock(); + } - isr_state(&g_block_isr_on, NULL); + task->t_func(task->t_arg); - rc = sim_setjmp(sf->sf_jb); - if (rc != 0) { - cur_t = os_sched_get_current_task(); - - isr_state(&block_isr_off, NULL); + /* This should never return */ + assert(0); +} - if (rc == CTX_SWITCH_ISR) { - sigs_unblock(); - } +os_stack_t * +os_arch_task_stack_init(struct os_task *t, os_stack_t *stack_top, int size) +{ + struct stack_frame *sf; - sf = (struct stack_frame *) cur_t->t_stackptr; - sf->sf_func(sf->sf_arg); + sf = (struct stack_frame *) ((uint8_t *) stack_top - sizeof(*sf)); + sf->sf_sigsblocked = 0; + sf->sf_task = t; - /* This should never return */ - assert(0); - } else { - /* restore current stack pointer */ - isr_state(&g_block_isr_off, NULL); - asm("movl %0, %%esp" : /* no output */ : "r" (s_cur)); - } + isr_state(&g_block_isr_on, NULL); + os_arch_frame_init(sf); + isr_state(&g_block_isr_off, NULL); - return (s); + return ((os_stack_t *)sf); } void http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/55afa1c6/libs/os/src/arch/sim/os_arch_stack_frame.s ---------------------------------------------------------------------- diff --git a/libs/os/src/arch/sim/os_arch_stack_frame.s b/libs/os/src/arch/sim/os_arch_stack_frame.s new file mode 100644 index 0000000..91c9338 --- /dev/null +++ b/libs/os/src/arch/sim/os_arch_stack_frame.s @@ -0,0 +1,68 @@ + .text + .code32 + .p2align 4, 0x90 /* align on 16-byte boundary and fill with NOPs */ + + .globl _os_arch_frame_init + /* + * void os_arch_frame_init(struct stack_frame *sf) + */ +_os_arch_frame_init: + push %ebp /* function prologue for backtrace */ + mov %esp,%ebp + push %esi /* save %esi before using it as a tmpreg */ + + /* + * At this point we are executing on the main() stack: + * ---------------- + * stack_frame ptr 0xc(%esp) + * ---------------- + * return address 0x8(%esp) + * ---------------- + * saved ebp 0x4(%esp) + * ---------------- + * saved esi 0x0(%esp) + * ---------------- + */ + movl 0xc(%esp),%esi /* %esi = 'sf' */ + movl %esp,0x0(%esi) /* sf->mainsp = %esp */ + + /* + * Switch the stack so the stack pointer stored in 'sf->sf_jb' points + * to the task stack. This is slightly complicated because OS X wants + * the incoming stack pointer to be 16-byte aligned. + * + * ---------------- + * sf (other fields) + * ---------------- + * sf (sf_jb) 0x4(%esi) + * ---------------- + * sf (sf_mainsp) 0x0(%esi) + * ---------------- + * alignment padding variable (0 to 12 bytes) + * ---------------- + * pointer to sf_jb %esp + * ---------------- + */ + movl %esi,%esp + subl $0x4,%esp /* make room for setjmp() argument */ + andl $0xfffffff0,%esp /* align %esp on 16-byte boundary */ + leal 0x4(%esi),%eax /* %eax = &sf->sf_jb */ + movl %eax,0x0(%esp) + call __setjmp /* _setjmp(sf->sf_jb) */ + test %eax,%eax + jne 1f + movl 0x0(%esi),%esp /* switch back to the main() stack */ + pop %esi + pop %ebp + ret /* return to os_arch_task_stack_init() */ +1: + lea 2f,%ecx + push %ecx /* retaddr */ + push $0 /* frame pointer */ + movl %esp,%ebp /* handcrafted prologue for backtrace */ + push %eax /* rc */ + push %esi /* sf */ + call _os_arch_task_start /* os_arch_task_start(sf, rc) */ + /* never returns */ +2: + nop http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/55afa1c6/libs/util/include/util/util.h ---------------------------------------------------------------------- diff --git a/libs/util/include/util/util.h b/libs/util/include/util/util.h index 6b3dd8b..1dcdab4 100644 --- a/libs/util/include/util/util.h +++ b/libs/util/include/util/util.h @@ -19,4 +19,6 @@ #ifndef __UTIL_H__ #define __UTIL_H__ +#define CTASSERT(x) typedef int __ctasssert ## __LINE__[(x) ? 1 : -1] + #endif /* __UTIL_H__ */