Simple ARM code generation

Steve Freeland caucasatron@yahoo.ca
Mon Aug 21 06:32:00 GMT 2006


> From: Richard Earnshaw <Richard.Earnshaw@buzzard.freeserve.co.uk>
> On Mon, 14 Aug 2006 15:15:46 PDT, Steve Freeland wrote:
> > Hello,
> > 
> > I'm attempting to use gcc (the WinARM build, see -v output below) to compile 
> > the following stub program:
> > 
> > int AEEMod_Load(IShell *pIShell, void *ph, IModule **ppMod)
> > {
> >     return ENOMEMORY; /* defined to "2" */
> > }
> > 
> > The output disassembles to the following:
> > 
> > Disassembly of section .text:
> > 
> > 00000000 <AEEMod_Load>:
> >    0:   e1a0c00d        mov     ip, sp
> >    4:   e92dd800        stmdb   sp!, {fp, ip, lr, pc}
> >    8:   e24cb004        sub     fp, ip, #4      ; 0x4
> >    c:   e24dd00c        sub     sp, sp, #12     ; 0xc
> >   10:   e50b0010        str     r0, [fp, #-16]
> >   14:   e50b1014        str     r1, [fp, #-20]
> >   18:   e50b2018        str     r2, [fp, #-24]
> >   1c:   e3a03002        mov     r3, #2  ; 0x2
> >   20:   e1a00003        mov     r0, r3
> >   24:   e24bd00c        sub     sp, fp, #12     ; 0xc
> >   28:   e89da800        ldmia   sp, {fp, sp, pc}
> > 
> > This code crashes the device I'm working with.
> > 
> > There seems to be a problem with the use of stmdb and ldmia to save and resto
> > re the register context to the stack.  The stmdb instruction saves 4 register
> > s, and the ldmia only restores 3 of them, one of which (sp) isn't in the orig
> > inal 4.  This trick seems to be common, but I don't understand how it works. 
> >  What order are the registers saved and loaded in?  As far as I can tell, at 
> > the end the pc register ends up with either the original fp or sp.
> > 
> > What needs to happen is for the original sp to be restored, and the original 
> > lr to be loaded into pc (the "return").
> > 
> > If I change the two last lines to the following:
> > 
> >   24:   e24bd004        sub     sp, fp, #4      ; 0x4
> >   28:   e12fff1e        bx      lr
> > 
> > The code works correctly.  Can anyone explain this to me?

First of all, thanks for the response!

> Hmm, the code looks correct to me (note that although IP is saved, this is just a copy of SP).

Aaah yes, I missed that.  Although the issue is tangential to my immediate problem, I'd really appreciate it if you could explain how that stmdb/ldmia trick works.  My understanding is that registers are saved in order (r0 first, r15/pc last) and loaded in reverse order.  But that would mean the original value of r15/pc that gets saved onto the stack at 0x4 gets *loaded* right back into r15/pc at 0x28.  Obviously that can't be right...  what am I missing?

Also, this: http://en.wikibooks.org/wiki/ARM/Programmer%27s_Model#Program_Counter claims that r15/pc can't always be manipulated like any other register.  Is that correct?  If so, is using ldmia into r15/pc always ok?  

> Let me take a guess.  You are using something like an ARM920 (or an ARM7TDMI) device, and you are calling
> this function from Thumb code.  If so, then you need to compile your function with -mthumb-interwork, then it
> will generate a return sequence that switches correctly back to Thumb.

You're correct that it's an ARM7TDMI device.  The code which calls my code is in firmware, so I'm not entirely sure whether it's Thumb or not...  If you're right, then presumably the calling procedure puts the appropriate flag in the LSB of r14/lr and expects the procedure being called to use bx and not ldmia to return.  Is it the case, then, that the switch to or from Thumb mode can't be done by modifying pc with ldmia?  If so, that would certainly explain the crash.  I'll try that as soon as I get the chance.

Thanks again, and it'd be great if you could sort me out with respect to the stmdb/ldmia trick.

- Steve






More information about the Gcc-help mailing list