Nios® V/II Embedded Design Suite (EDS)
Support for Embedded Development Tools, Processors (SoCs and Nios® V/II processor), Embedded Development Suites (EDSs), Boot and Configuration, Operating Systems, C and C++

Gcc4 Frame Pointer Issue

Altera_Forum
Honored Contributor II
1,850 Views

I've been trying to write some code in an MPU exception handler to trap and dump the call stack when an intermittent exception occurs in our system and am pretty sure I've found a bug in the latest Gcc4 compiler that ships with Quartus 10.0. I haven't tried it on 10.1 as yet. 

 

It seems under gcc4 the frame pointer is not calcuated correctly in all cases. 

Here's the generated asm code from gcc3 (10.0) 

addi sp,sp,-120 

stw ra,116(sp) 

stw fp,112(sp) 

stw r16,108(sp) 

stw r17,104(sp) 

stw r18,100(sp) 

stw r19,96(sp) 

stw r20,92(sp) 

addi fp,sp,112 

 

And the corresponding code from Gcc4 

addi sp,sp,-116  

stw ra,112(sp)  

stw fp,108(sp)  

stw r19,104(sp)  

stw r18,100(sp)  

stw r17,96(sp)  

stw r16,92(sp)  

addi fp,sp,92  

 

Under Gcc4 the frame pointer is not pointing to the correct location as documented in the Nios II Processor Reference Handbook Section II-7 Stacks. You can see the difference in the addi fp,sp,xx instruction. Gcc3 does seem to do it right. 

 

Also I've found that the __builtin_return_address() function does not work for Gcc4. Any value greater than >0 under Gcc4 returns 0 for the address. 

Rebuild under Gcc3 and it works fine. 

 

Can any one shed some light on this. If it's not a bug how should the call stack be walked now? The following code works under gcc3, fails under gcc4. 

 

struct stack_frame { 

struct stack_frame* next; 

void* ret; 

}; 

 

struct stack_frame* fp; 

__asm ("mov %0, fp" : "=r" (fp) ); 

alt_u32 cnt = 0; 

while(fp && (cnt < 10)) { 

isr_printf("%x: fp(%x) pfp(%x) ra(%x)\n", cnt, fp, fp->next, fp->ret); 

fp = fp->next; 

cnt++; 

} ; 

 

:cry:
0 Kudos
18 Replies
Altera_Forum
Honored Contributor II
714 Views

That is a bug, the frame pointers should point to each other.

0 Kudos
Altera_Forum
Honored Contributor II
714 Views

I am facing the same issue -- the compiler bug is preventing me from implementing a backtrace function for our system. I believe we're using gcc-4.1.2. Any resolution or workarounds for this issue? 

 

Thanks for any help.
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

Possibly dumb question: is it a bug or is it a result of frame pointer optimization? 

Would compiling with -fno-omit-frame-pointer help?
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

It appears to be a real bug. I've already chased -fno-omit-frame-pointer quite a bit. Also turns out that -O1, or just about anything other than -O0, will also turn off frame pointers. I think I have that part nailed down, but it still seems that half the functions have the correct prologue, while others don't. I'm getting the exact same results as the first post in this thread.

0 Kudos
Altera_Forum
Honored Contributor II
714 Views

You won't get a backtrace without frame pointers (well not trivially). 

There are some techniques that do work without having to look at the dwarf (or whatever the flavour of the month is) stack offset data. 

If you aren't in a function that doesn't return, it is a SMOP to follow instructions forwards keeping track of %sp until you hit a return instruction.
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

Sigh. 

2yrs later and with Quartus 12.1 seems to still have the problem that I report in Jan 2011.  

We've been running with gcc3 for an extended time period but need to update and the Quartus 12.1 installation seems to have removed gcc3 support. 

Anyone got a work around to the problem? 

 

jcn
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

Build gcc from the sources. 

 

Or you could just do what the linux kernel does for tracebacks... 

Just print every word from the stack that points into the code space. 

(You could look at the instruction at the call site to supress some incorrect entries.) 

 

You might also find that it is actually a simple bug in the gcc code for nios - possibly 

a change that should have been done for gcc4, but wasn't. 

The gcc4 and gcc3 nios support files are almost identical (well, were last time I looked). 

Trouble is, fixing gcc either involves a lot of knowledge, or some blind faith!
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

G'day dsl, 

 

Build gcc from source is last on my list :) 

The idea to print every word seems like a good workaround.
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

The build instructions on the wiki make it easy (fixing it is more difficult). 

More useful if you don't want you C compilations be tied to the Altera IDE.
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

Any news concerning this frame pointer issue? 

 

I am trying to store/dump a backtrace in case of an exception in a NiosII system (without MPU) with ACDS 13.01 sp1 (gcc version 4.5.3). My observation matches exactly the OPs description. The compiler seems to calculate the frame pointer correctly as long as the called functions do not hold a lot of local variables. But with a more complex chain of function calls, the fp points somewhere near the stack pointer but NOT to the actual back link. 

 

As for the __builtin_return_address(): still the same in gcc 4.5.3. 

 

Does anyone know if this behaviour is still the same with the new compiler in Quartus/Nios 13.1?
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

We ended up walking the stack with our own code and anything examined everything that looked like an address in the code space was examined. 

If the address followed a call op code we printed the address. It was a bit of effort but works with gcc3 / gcc4.  

We are building with the with 13.1 now but I never bothered looking at whether this was fixed as our code still worked correctly.
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

I've just reread the recent posts and noticed that Altera are no longer including gcc3. 

Have they fixed their gcc4 build so that it can generate 'pure code'? 

The earlier gcc4 builds would always place switch statement jump tables directly in the code segment. Something that you definitely don't want if you are using tightly coupled instruction and data memories. 

If they haven't, anyone who is generating anything small will need to rebuild gcc anyway.
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

Update: Quartus 13.1 with GCC 4.7.3 has exactly the same issue. 

It seems that the error occurs whenever the callee saved registers (r16-r23) get pushed onto the stack upon a function call. Then the frame pointer misses the correct position by <numOfCalleeRegs>*4 Bytes. 

 

Simple code example: 

class MyClass { public: MyClass() {}; virtual ~MyClass() {}; }; void FramePointerIssue( void ){ MyClass* foo = new MyClass(); delete foo; } int main(void){ FramePointerIssue(); return -1; } 

 

Question: Is there an easy way to find out whether an address lies within the code space or not? I know that the information can be found in the *.objdump file, but is there also a programmatic way of accessing this info.
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

Just a dumb thought on this one: Can you rename the gcc3 executable to gcc4 and get the IDE to invoke the renamed gcc3? Or are the compilers that incompatible? 

 

BillA
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

The linker script could well add global symbols at the ends of the code space. 

It should then be possible to define C symbols with the same names and take their addresses. 

 

Actually it probably isn't that difficult to fix gcc to output the correct prologue and epilogue. 

It is also worth checking the epilogue. There was a bug in gcc for arm that caused it to adjust the stack pointer and then load the return value from an address below the sp - which an interrupt could have overwritten.
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

To those who are interested in Alteras official statement: 

 

 

--- Quote Start ---  

... 

the GCC is not setting the frame pointer as documented in the ABI. Both the text and diagrams in the ABI document say the FP points at the saved FP on the stack. Instead, GCC is pointing it at the last saved register (i.e., at the low end of the frame), and consistently applying the wrong constant offset to all fp-based stack references. This bug was observed on the GCC 4.7. 

... 

We have already filed this issue to Mentor (tracking number 16009) and the fixes is planned to be included in our Nios II GNU toolchain (upgrade to GCC4.8) in a future version of the Quartus II software. 

--- Quote End ---  

 

 

... So until this future becomes present, we do have to live with workarounds.
0 Kudos
Altera_Forum
Honored Contributor II
714 Views

Unless Mentor are will to post the change somewhere and allow you to rebuild the older gcc with that specific fix applied.

0 Kudos
Altera_Forum
Honored Contributor II
714 Views

If anyone's interested we use the following function to dump the stack of a UCOS thread, should be fairly straght forward to modify for none UCOS systems: 

 

 

// these variables generate by linker, using standard linker script. extern char _start; extern char __etext; extern char __ram_exceptions_start; extern char __ram_exceptions_end; void DumpStackTcb(OS_TCB* a_pTCB) { void* sp = 0; //lint -e{522} __asm ("mov %0, sp" : "=r" (sp) ); void* pTextStart = &_start; void* pTextEnd = &__etext; void* pExceptStart = &__ram_exceptions_start; void* pExceptEnd = &__ram_exceptions_end; OS_STK* pStackBottom = a_pTCB->OSTCBStkBottom; OS_STK* pStackTop = a_pTCB->OSTCBStkBottom + a_pTCB->OSTCBStkSize ; // OSTCBStkSize is num of elements, not bytes OS_STK* pStackCur = a_pTCB->OSTCBStkPtr; isr_printf("TS(0x%x) TE(0x%x) ES(0x%x) EE(0x%x)\n", (unsigned)pTextStart, (unsigned)pTextEnd, (unsigned)pExceptStart, (unsigned)pExceptEnd); isr_printf("id(0x%x) sb(0x%x) st(0x%x) ss(0x%x) sp(0x%x) sp(0x%x)\n" , a_pTCB->OSTCBPrio , (unsigned)pStackBottom , (unsigned)pStackTop , a_pTCB->OSTCBStkSize * sizeof(OS_STK) , (unsigned)pStackCur , (unsigned)sp ); u32 Cnt = 0; for(OS_STK* pStack = pStackCur; (pStack >= pStackBottom) && (pStack < pStackTop) ; pStack++) { void* pPossibleAddr = (void*)*pStack; if(((((unsigned)pPossibleAddr) & 0x3) == 0) && (((pPossibleAddr > pTextStart) && (pPossibleAddr <= pTextEnd)) || ((pPossibleAddr > pExceptStart) && (pPossibleAddr <= pExceptEnd)))) { u32 PrevInstr = *((OS_STK*)pPossibleAddr-1); // check if instruction before poss. return address is a call if( ((PrevInstr & 0x3f) == 0) || ((PrevInstr & 0x07ffffff) == 0x003ee83a) ) { // check call / callr isr_printf("0x%x: ra(0x%x) pi(0x%x)\n", Cnt, (unsigned)pPossibleAddr, PrevInstr); Cnt++; } } } }
0 Kudos
Reply