I'm having a problem with virtual functions. It seems as though the
linker is not connecting them properly (at least thats what I think is
happening).
Code time! (I'm going to try to include everything that is relevant, but
I might miss some stuff, so just yell and I'll go find it)
struct BaoUiPg {
private:
...
protected:
...
public:
virtual void appSetup(int nID);
...
};
struct BaoUiPgOutPower : public BaoUiPg {
private:
...
public:
virtual void appSetup(int nID);
};
the InPower, BattStats etc are of similar make-up to BaoUiPgOutPower
static BaoUiPgOutPower s_PageOutPower;
static BaoUiPgInPower s_PageInPower;
static BaoUiPgBattStats s_PageBatteryStats;
static BaoUiPgScavenger s_PageScavenger;
static BaoUiPgInPriority s_PageInputPriority;
static BaoUiPgChrgCtrl s_PageChargeControl;
static BaoUiPgFcPwrLvl s_PageFcPwrLvl;
static BaoUiPgFcOpMode s_PageFcOpMode;
static BaoUiPgVersion s_PageVersion;
static BaoUiPg * s_pPageList[] =
{
&s_PageOutPower,
&s_PageInPower,
&s_PageBatteryStats,
&s_PageScavenger,
&s_PageInputPriority,
&s_PageChargeControl,
&s_PageFcPwrLvl,
&s_PageFcOpMode,
&s_PageVersion,
};
When this code is executed
s_pPageList[0]->appSetup(0);
the program hangs. I went into the GDB insight debugger and this jumps
to a random point in memory and says <undefined>.
My makefile stuff:
# Flags for C and C++ (arm-elf-gcc/arm-elf-g++)
CFLAGS = -g$(DEBUG)
CFLAGS += $(CDEFS) $(CINCS)
CFLAGS += -O$(OPT)
CFLAGS += -Wall -Wcast-align -Wimplicit
CFLAGS += -Wpointer-arith -Wswitch
CFLAGS += -Wredundant-decls -Wreturn-type -Wshadow -Wunused
CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
# flags only for C
CONLYFLAGS += -Wnested-externs
CONLYFLAGS += $(CSTANDARD)
# flags only for C++ (arm-elf-g++)
CPPFLAGS = -g$(DEBUG)
CPPFLAGS += $(CDEFS) $(CINCS)
CPPFLAGS += -O$(OPT)
CPPFLAGS += -Wall -Wno-cast-align -Wimplicit -Wno-non-virtual-dtor
CPPFLAGS += -Wpointer-arith -Wswitch
CPPFLAGS += -Wredundant-decls -Wreturn-type -Wshadow -Wunused
CPPFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
# --cref: add cross reference to map file
LDFLAGS = -nostartfiles -Wl,-Map=$(OBJDIR)/$(OUTTARGET).map,--cref
LDFLAGS += -lc
LDFLAGS += $(NEWLIBLPC) $(MATH_LIB)
LDFLAGS += -lc -lgcc
LDFLAGS += $(CPLUSPLUS_LIB)
# Link: create ELF output file from object files.
.SECONDARY : $(OBJDIR)/$(OUTTARGET).elf
.PRECIOUS : $(AOBJARM) $(AOBJ) $(COBJARM) $(COBJ) $(CPPOBJ) $(CPPOBJARM)
%.elf: $(AOBJARM) $(AOBJ) $(COBJARM) $(COBJ) $(CPPOBJ) $(CPPOBJARM)
@echo
@echo $(MSG_LINKING) $@
# $(CC) $(THUMB) $(ALL_CPPFLAGS) $(AOBJARM) $(AOBJ) $(COBJARM) $(COBJ)
$(CPPOBJ) $(CPPOBJARM) --output $@ $(LDFLAGS)
$(CPP) $(THUMB) $(ALL_CPPFLAGS) $(AOBJARM) $(AOBJ) $(COBJARM) $(COBJ)
$(CPPOBJ) $(CPPOBJARM) --output $@ $(LDFLAGS)
# Compile: create object files from C source files. ARM/Thumb
$(COBJ) : $(OBJDIR)/%.o : %.c
@echo
@echo $(MSG_COMPILING) $<
$(CC) -c $(THUMB) $(ALL_CFLAGS) $(CONLYFLAGS) $< -o $(OBJDIR)/$(@F)
# Compile: create object files from C source files. ARM-only
$(COBJARM) : $(OBJDIR)/%.o : %.c
@echo
@echo $(MSG_COMPILING_ARM) $<
$(CC) -c $(ALL_CFLAGS) $(CONLYFLAGS) $< -o $(OBJDIR)/$(@F)
# Compile: create object files from C++ source files. ARM/Thumb
$(CPPOBJ) : $(OBJDIR)/%.o : %.cpp
@echo
@echo $(MSG_COMPILINGCPP) $<
# $(CC) -c $(THUMB) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $< -o $(OBJDIR)/$(@F)
$(CPP) -c $(THUMB) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $< -o $(OBJDIR)/$(@F)
# Compile: create object files from C++ source files. ARM-only
$(CPPOBJARM) : $(OBJDIR)/%.o : %.cpp
@echo
@echo $(MSG_COMPILINGCPP_ARM) $<
# $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $< -o $(OBJDIR)/$(@F)
$(CPP) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $< -o $(OBJDIR)/$(@F)
# Assemble: create object files from assembler source files. ARM/Thumb
$(AOBJ) : $(OBJDIR)/%.o : %.S
@echo
@echo $(MSG_ASSEMBLING) $<
$(CC) -c $(THUMB) $(ALL_ASFLAGS) $< -o $(OBJDIR)/$(@F)
# Assemble: create object files from assembler source files. ARM-only
$(AOBJARM) : $(OBJDIR)/%.o : %.S
@echo
@echo $(MSG_ASSEMBLING_ARM) $<
$(CC) -c $(ALL_ASFLAGS) $< -o $(OBJDIR)/$(@F)
Note: If I build this for a Win32 platform it works correctly.
If anyone has any suggestions I'm all ears. I was playing around with
some linker flags, but the GCC doc seems to be lacking when it comes to
documenting all the flags, so there could very well be a simple flag
that I'm missing which I just couldn't find. Thanks in advance
everyone!
Jim Kaz wrote:
> I'm having a problem with virtual functions. It seems as though the> linker is not connecting them properly (at least thats what I think is> happening).
So - how about the linker-script? For C++ some extra entries are needed
compared to C (has been discussed before, try a "manual seach" thru the
topic-pages). Are the entries included in the used script? Maybe the
default linker-script (arm-elf-ld --verbose) or one of my examples has
same useful information.
Example:
http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/#lpc_cpp1
It might be too simple but at least virtual functions are used and I
have tested it successfully with an LPC2129.
Martin Thomas
Jim Kaz wrote:
> ...I was playing around with> some linker flags, but the GCC doc seems to be lacking when it comes to> documenting all the flags, so there could very well be a simple flag> that I'm missing which I just couldn't find.>
For linker flags you should perhaps consult the linker documentation
rather than the compiler documentation! ;-)
http://www.gnu.org/software/binutils/manual/ld-2.9.1/ld.html However, I
don't thing linker flags are going to help much.
Is it only this code that fails or any virtual function calls? If the
latter, post a simpler example, it would be easier at least to discount
your code as the problem if that is the case. If it is only this code, I
would suggest that the problem may not be where you think it is, and may
be in your code - perhaps even an unrelated memory error corrupting. In
these cases posting just fragments may be inadequate.
When you call s_pPageList[0]->appSetup(0); in the debugger, and you
checked s_pPageList[0] at that point to determine if it is a valid
object?
Clifford
Clifford Slocombe wrote:
> Is it only this code that fails or any virtual function calls? If the> latter, post a simpler example, it would be easier at least to discount> your code as the problem if that is the case. If it is only this code, I> would suggest that the problem may not be where you think it is, and may> be in your code - perhaps even an unrelated memory error corrupting. In> these cases posting just fragments may be inadequate.>> When you call s_pPageList[0]->appSetup(0); in the debugger, and you> checked s_pPageList[0] at that point to determine if it is a valid> object?
I'm not sure if other virtual functions fail. This was something I
started to investigate but then got side tracked by the whole linker
script thing. I do know that calling other virtual functions does not
cause the system to crash, but I don't know if those functions are
actually being jumped to. I ran the GDB insight debugger and attempted
to step into those functions, but it never seemed to work, so I don't
know if its because if the code is not actually jumping to them, or the
debugger decided to not step into the function, which I've noticed it
does some time. Putting a break point inside one of those functions
also didn't cause the debugger to halt...
I want to say that s_pPageList[0] is a valid object, but I can't
remember for certain and won't be able to check again until Monday.
I know this code works using the compiler that comes with Visual Studios
8 (we have simulator code and the like), but if the VS compiler is
anything like Internet Explorer, it probably does not conform to
regulations, so things that shouldn't work do work.
I'll try those changes on Monday, thanks Martin! Also, in the LPC code,
there is.. uhh ENTRY(_boot) I think was the name, at the top of the ROM
script and _startup at the top of the RAM script. Are those important
to have? Should they be in the Assembly file? I modded my assembly
file a bit to include these, but the SAM7A file looked very different
from the LPC file (don't know if this is because different chip types,
or just different coding styles), so I likely placed _boot and _startup
in the incorrect places.
Thanks for all the help guys!
Ok.. still trying to get these damn virtual functions to work...
Martin, at the top of your LPC ROM linker script there is an
ENTRY(_boot) and at the top the RAM one there is ENTRY(_start). These
appear in the assembly file as well. These two thinks are the only
things missing from my linker scripts. However, the SAM7 assembly files
are vastly different than the LPC ones.
As far as I can tell, your SAM7S examples are more or less identical to
my SAM7A3 file. However, those files aren't designed to handle virtual
functions. Is there anything I need to add to my assembly file? Do I
need to add _start and _boot functions somewhere (and then add them to
the top of my linker scripts as well)?
Any further help you can contribute would be greatly appreciated.
/*----------------------------------------------------------------------
--------
//*- ATMEL Microcontroller Software Support - ROUSSET -
//*---------------------------------------------------------------------
---------
//* The software is delivered "AS IS" without warranty or condition of
any
//* kind, either express, implied or statutory. This includes without
//* limitation any warranty or condition with respect to merchantability
or
//* fitness for any particular purpose, or against the infringements of
//* intellectual property rights of others.
//*---------------------------------------------------------------------
--------
//*- File source : Cstartup.s
//*- Object : Generic CStartup for KEIL and GCC No Use
REMAP
//*- Compilation flag : None
//*-
//*- 1.0 10/Mar/05 JPP : Creation
//*- 1.1 01/Apr/05 JPP : save SPSR
//*---------------------------------------------------------------------
--------*/
.equ IRQ_Stack_Size, 0x00000060
/* #include "AT91SAM7A3_inc.h" */
.equ AIC_IVR, (256)
.equ AIC_FVR, (260)
.equ AIC_EOICR, (304)
.equ AT91C_BASE_AIC, (0xFFFFF000)
#;----------------------------------------------------------------------
--------
#;- Section Definition
#;-----------------
#;- Section
#;- .internal_ram_top Top_Stack: used by the cstartup for vector
initalisation
#;- management defined by ld and affect from ldscript
#;----------------------------------------------------------------------
--------
.section .internal_ram_top
.code 32
.align 0
.global Top_Stack
Top_Stack:
/*----------------------------------------------------------------------
--------
*- Area Definition
*-----------------------------------------------------------------------
-------
* .text is used instead of .section .text so it works with arm-aout too.
*/
.section .reset
.text
.global _startup
.func _startup
_startup:
reset:
/*----------------------------------------------------------------------
--------
//*- Exception vectors
//*--------------------
//*- These vectors can be read at address 0 or at RAM address
//*- They ABSOLUTELY requires to be in relative addresssing mode in
order to
//*- guarantee a valid jump. For the moment, all are just looping.
//*- If an exception occurs before remap, this would result in an
infinite loop.
//*- To ensure if a exeption occurs before start application to infinite
loop.
//*---------------------------------------------------------------------
---------*/
B InitReset /* 0x00 Reset handler */
undefvec:
B undefvec /* 0x04 Undefined
Instruction */
swivec:
B swivec /* 0x08 Software
Interrupt */
pabtvec:
B pabtvec /* 0x0C Prefetch Abort
*/
dabtvec:
B dabtvec /* 0x10 Data Abort */
rsvdvec:
B rsvdvec /* 0x14 reserved */
irqvec:
B IRQ_Handler_Entry /* 0x18 IRQ */
fiqvec: /* 0x1c FIQ */
/*----------------------------------------------------------------------
--------
//*- Function : FIQ_Handler_Entry
//*- Treatments : FIQ Controller Interrupt Handler.
//*- Called Functions : AIC_FVR[interrupt]
//*---------------------------------------------------------------------
---------*/
FIQ_Handler_Entry:
/*- Switch in SVC/User Mode to allow User Stack access for C code */
/* because the FIQ is not yet acknowledged*/
/*- Save and r0 in FIQ_Register */
mov r9,r0
ldr r0 , [r8, #AIC_FVR]
msr CPSR_c,#I_BIT | F_BIT | ARM_MODE_SVC
/*- Save scratch/used registers and LR in User Stack */
stmfd sp!, { r1-r3, r12, lr}
/*- Branch to the routine pointed by the AIC_FVR */
mov r14, pc
bx r0
/*- Restore scratch/used registers and LR from User Stack */
ldmia sp!, { r1-r3, r12, lr}
/*- Leave Interrupts disabled and switch back in FIQ mode */
msr CPSR_c, #I_BIT | F_BIT | ARM_MODE_FIQ
/*- Restore the R0 ARM_MODE_SVC register */
mov r0,r9
/*- Restore the Program Counter using the LR_fiq directly in the PC */
subs pc,lr,#4
.align 0
.RAM_TOP:
.word Top_Stack
InitReset:
/*----------------------------------------------------------------------
--------
/*- Low level Init (PMC, AIC, ? ....) by C function AT91F_LowLevelInit
/*----------------------------------------------------------------------
--------*/
.extern AT91F_LowLevelInit
/*- minumum C initialization */
/*- call AT91F_LowLevelInit( void) */
ldr r13,.RAM_TOP /* temporary stack in
internal RAM */
/*--Call Low level init function in ABSOLUTE through the Interworking
*/
ldr r0,=AT91F_LowLevelInit
mov lr, pc
bx r0
/*----------------------------------------------------------------------
--------
//*- Stack Sizes Definition
//*------------------------
//*- Interrupt Stack requires 2 words x 8 priority level x 4 bytes when
using
//*- the vectoring. This assume that the IRQ management.
//*- The Interrupt Stack must be adjusted depending on the interrupt
handlers.
//*- Fast Interrupt not requires stack If in your application it
required you must
//*- be definehere.
//*- The System stack size is not defined and is limited by the free
internal
//*- SRAM.
//*---------------------------------------------------------------------
---------*/
/*----------------------------------------------------------------------
--------
//*- Top of Stack Definition
//*-------------------------
//*- Interrupt and Supervisor Stack are located at the top of internal
memory in
//*- order to speed the exception handling context saving and restoring.
//*- ARM_MODE_SVC (Application, C) Stack is located at the top of the
external memory.
//*---------------------------------------------------------------------
---------*/
.EQU IRQ_STACK_SIZE, (3*8*4)
.EQU ARM_MODE_FIQ, 0x11
.EQU ARM_MODE_IRQ, 0x12
.EQU ARM_MODE_SVC, 0x13
.EQU I_BIT, 0x80
.EQU F_BIT, 0x40
/*----------------------------------------------------------------------
--------
//*- Setup the stack for each mode
//*-------------------------------*/
mov r0,r13
/*- Set up Fast Interrupt Mode and set FIQ Mode Stack*/
msr CPSR_c, #ARM_MODE_FIQ | I_BIT | F_BIT
/*- Init the FIQ register*/
ldr r8, =AT91C_BASE_AIC
/*- Set up Interrupt Mode and set IRQ Mode Stack*/
msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT
mov r13, r0 /* Init stack IRQ */
sub r0, r0, #IRQ_Stack_Size
/*- Set up Supervisor Mode and set Supervisor Mode Stack*/
msr CPSR_c, #ARM_MODE_SVC
mov r13, r0 /* Init stack Sup */
/*- Enable interrupt & Set up Supervisor Mode and set Supervisor Mode
Stack*/
# Relocate .data section (Copy from ROM to RAM)
LDR R1, =_etext
LDR R2, =_data
LDR R3, =_edata
LoopRel: CMP R2, R3
LDRLO R0, [R1], #4
STRLO R0, [R2], #4
BLO LoopRel
# Clear .bss section (Zero init)
MOV R0, #0
LDR R1, =__bss_start__
LDR R2, =__bss_end__
LoopZI: CMP R1, R2
STRLO R0, [R1], #4
BLO LoopZI
ldr lr,=exit
ldr r0,=main
bx r0
.size _startup, . - _startup
.endfunc
/* "exit" dummy added by mthomas to avoid sbrk write read etc. needed
by the newlib default "exit" */
.global exit, abort
.func exit, abort
exit:
abort:
b .
.size exit, . - exit
.endfunc
/*----------------------------------------------------------------------
--------
//*- Manage exception
//*---------------
//*- This module The exception must be ensure in ARM mode
//*---------------------------------------------------------------------
---------
//*---------------------------------------------------------------------
---------
//*- Function : IRQ_Handler_Entry
//*- Treatments : IRQ Controller Interrupt Handler.
//*- Called Functions : AIC_IVR[interrupt]
//*---------------------------------------------------------------------
---------*/
.global IRQ_Handler_Entry
.func IRQ_Handler_Entry
IRQ_Handler_Entry:
/*- Manage Exception Entry */
/*- Adjust and save LR_irq in IRQ stack */
sub lr, lr, #4
stmfd sp!, {lr}
/*- Save SPSR need to be saved for nested interrupt */
mrs r14, SPSR
stmfd sp!, {r14}
/*- Save and r0 in IRQ stack */
stmfd sp!, {r0}
/*- Write in the IVR to support Protect Mode */
/*- No effect in Normal Mode */
/*- De-assert the NIRQ and clear the source in Protect Mode */
ldr r14, =AT91C_BASE_AIC
ldr r0 , [r14, #AIC_IVR]
str r14, [r14, #AIC_IVR]
/*- Enable Interrupt and Switch in Supervisor Mode */
msr CPSR_c, #ARM_MODE_SVC
/*- Save scratch/used registers and LR in User Stack */
stmfd sp!, { r1-r3, r12, r14}
/*- Branch to the routine pointed by the AIC_IVR */
mov r14, pc
bx r0
/*- Restore scratch/used registers and LR from User Stack*/
ldmia sp!, { r1-r3, r12, r14}
/*- Disable Interrupt and switch back in IRQ mode */
msr CPSR_c, #I_BIT | ARM_MODE_IRQ
/*- Mark the End of Interrupt on the AIC */
ldr r14, =AT91C_BASE_AIC
str r14, [r14, #AIC_EOICR]
/*- Restore SPSR_irq and r0 from IRQ stack */
ldmia sp!, {r0}
/*- Restore SPSR_irq and r0 from IRQ stack */
ldmia sp!, {r14}
msr SPSR_cxsf, r14
/*- Restore adjusted LR_irq from IRQ stack directly in the PC */
ldmia sp!, {pc}^
.size IRQ_Handler_Entry, . - IRQ_Handler_Entry
.endfunc
/*---------------------------------------------------------------
//* ?EXEPTION_VECTOR
//* This module is only linked if needed for closing files.
//*---------------------------------------------------------------*/
.global AT91F_Default_FIQ_handler
.func AT91F_Default_FIQ_handler
AT91F_Default_FIQ_handler:
b AT91F_Default_FIQ_handler
.size AT91F_Default_FIQ_handler, . - AT91F_Default_FIQ_handler
.endfunc
.global AT91F_Default_IRQ_handler
.func AT91F_Default_IRQ_handler
AT91F_Default_IRQ_handler:
b AT91F_Default_IRQ_handler
.size AT91F_Default_IRQ_handler, . - AT91F_Default_IRQ_handler
.endfunc
.global AT91F_Spurious_handler
.func AT91F_Spurious_handler
AT91F_Spurious_handler:
b AT91F_Spurious_handler
.size AT91F_Spurious_handler, . - AT91F_Spurious_handler
.endfunc
.end
I found what is likely the missing piece in my assembly file:
LDR r0, =__ctors_start__
LDR r1, =__ctors_end__
ctor_loop:
CMP r0, r1
BEQ ctor_end
LDR r2, [r0], #4
STMFD sp!, {r0-r1}
MOV lr, pc
----> MOV pc, r2
LDMFD sp!, {r0-r1}
BLO ctor_loop
ctor_end:
Sadly, the marked line causes much devastation to occur in my program
(I.E. it never makes it to main()). I'm thinking that R2 is loaded with
a bogus value so the PC gets loaded with a bogus value, and then there
is much death. I have looked at ARM assembly very very very very
little, but thankfully I do have a handy dandy ARM architecture book :)
Jim Kaz wrote:
> I found what is likely the missing piece in my assembly file:>> LDR r0, =__ctors_start__> LDR r1, =__ctors_end__> ctor_loop:> CMP r0, r1> BEQ ctor_end> LDR r2, [r0], #4> STMFD sp!, {r0-r1}> MOV lr, pc> ----> MOV pc, r2> LDMFD sp!, {r0-r1}> BLO ctor_loop> ctor_end:>>> Sadly, the marked line causes much devastation to occur in my program> (I.E. it never makes it to main()). I'm thinking that R2 is loaded with> a bogus value so the PC gets loaded with a bogus value, and then there> is much death. I have looked at ARM assembly very very very very> little, but thankfully I do have a handy dandy ARM architecture book :)
The loop calls the constructors of static objects. I have not tested
this myself and I am not sure but it may be a "thumb-address-problem".
For a first test rebuild the complete project without thumb/thumb-iw. It
if works try to replace the MOV pc,r2 with BX r2 and rebuild with
thumb/thumb-iw activated. If it still does not work create a minimal
example (linker-script, makefile, startup-code, minimal application
source-code), place it somewhere on a web-space and send the link or
attach it to an e-mail to mthomas AT rhrk DOT uni-kl. DOT de and I will
place it on the "WinARM-server". If I have some time left I will try to
create a C++-example for the SAM7S.
Martin Thomas
Well, I made a minimally sized project and tried out some virtual
functions. I discovered that local virtual functions work, global
virtual functions did not.
After I changed the MOV pc,r2 to BX r2, tada! It worked.
http://users.wpi.edu/~jimmykaz/Arm/Sam7A3_Virt_Fnct.rar
Sadly though, the actual application I've been writing still fails, even
after making this change in its respective assembly file. Back to the
drawing board... yar matie, Yar.
Thanks for all the help Martin! Your name has been given its due place
of importance in the linker scripts and assembly file. Well, at the
very least the linker scripts, I'll have to double check on the assembly
file...
Jim Kaz wrote:
> Sadly though, the actual application I've been writing still fails, even> after making this change in its respective assembly file. Back to the> drawing board... yar matie, Yar.
So! Fun tid bits of information.
1) Virtual functions like being called from inside static functions.
There is much failure to be had otherwise (at least with how my
architecture is set up).
2) GDB Insight will hate you for life if you try to step into a virtual
function. It will either ignore you, and just pass right over it
(although it does actually execute the function, it will never ever show
you), or it will jump to an undefined vector and crash. I've noticed
that on certain virtual functions, if I hit step into, it will crash, if
I hit step over, it keeps on chugging. Go figure.
All the way back to my original post, I had s_pPageList[0]->appSetup(0);
Well, it was really inside a for loop that went up to 9. Well, I got up
to 3 working! After the third one the inheritances of the classes
changes and there is much breaking to be had by all. But I'm a hell of
a lot closer than I used to be, thank you every for the help!