For the last few days I've been troubleshooting a problem with a C++ thread class I created to aid in the static allocation of threads under ChibiOS.

The problem boiled down to the fact that the ChibiOS ARM port (specifically, the ARM7 port, but it's probably true for most ports) isn't properly executing the constructors for statically (globally) allocated class objects. To clean up the other post, I moved the troubleshooting material to this post.

Note: I've discussed this with the author of ChibiOS, and he hopes to resolve the issue in ChibiOS version 2.3.2.

When any object with a vtable pointer is allocated on the stack (i.e. at run time), it works. But when it is allocated as a global variable, the vtable pointer is NULL. Here's the test code and output:

[cpp]
class FooParent
{
public:
FooParent();

virtual void doSomething();

uint32_t mField1;
uint32_t mField2;
};

FooParent::FooParent()
:
mField1(0xBEEFBA55),
mField2(0xDEADFACE)
{
}

void
FooParent::doSomething()
{
}

class
Foo : public FooParent
{
public:
Foo();
virtual void doSomething();

uint32_t mField3;
};

Foo::Foo()
:
mField3(0xFEEDABCD)
{
}

void
Foo::doSomething()
{
}

Foo f;

void
dumpObjects
{
uint32_t* p = reinterpret_cast<uint32_t*> (&f);
sDebug.log("f dump @ 0x%08x: 0x%08x\n", p, *p); p+=1;
sDebug.log("f dump @ 0x%08x: 0x%08x\n", p, *p); p+=1;
sDebug.log("f dump @ 0x%08x: 0x%08x\n", p, *p); p+=1;
sDebug.log("f dump @ 0x%08x: 0x%08x\n", p, *p); p+=1;

Foo g;
p = reinterpret_cast<uint32_t*> (&g);
sDebug.log("g dump @ 0x%08x: 0x%08x\n", p, *p); p+=1;
sDebug.log("g dump @ 0x%08x: 0x%08x\n", p, *p); p+=1;
sDebug.log("g dump @ 0x%08x: 0x%08x\n", p, *p); p+=1;
sDebug.log("g dump @ 0x%08x: 0x%08x\n", p, *p); p+=1;
}
[/cpp]

[cpp]
f dump @ 0x00203090: 0x00000000
f dump @ 0x00203094: 0x00000000
f dump @ 0x00203098: 0x00000000
f dump @ 0x0020309c: 0x00000000

g dump @ 0x00202a60: 0x0010a0a8
g dump @ 0x00202a64: 0xbeefba55
g dump @ 0x00202a68: 0xdeadface
g dump @ 0x00202a6c: 0xfeedabcd
[/cpp]

As you can see, the constructor isn't getting called, (and vtable initialization isn't happening) for the statically-allocated object f, but does get called for object g, allocated on the stack.

This is a GCC issue. It needs to call static ctors (and dtors, theoretically), but it's failing to do so. It could be a mis-configured build of the toolchain, or a bug in the run-time library, or something else along those lines.

Thoughts While Troubleshooting

It currently fails on ARM (on an AT91SAM7X). The symptom is typical of exceeding the allocated stack space: the processor appears to reset (that is, it re-starts execution from the start of code).

There are two failure modes: in the first, it fails in ThreadEntry(), where the the thread context is cast to a pointer to the BaseThread class, and the virtual entry() method is called, executing the CThread subclass' implementation. If I remove the call to entry() and just implement some code directly in ThreadEntry(), it works fine.

The second failure mode has to do with what I put in the modified ThreadEntry(). Although it executes, subsequent execution later in the program fails (by failure, it is meant that the processor appears to reset).

At this point, I'm not sure what is wrong. Code seems well-enough aligned, when the GCC .map file is examined. The self/this pointer in ThreadEntry() has the correct value. I've tried with very large stacks to ensure I'm not overrunning the stack. I've tried a non-template class hierarchy to be sure virtual method dispatch works correctly (it does).

I'm going to try twiddling some outputs on the MCU (instead of calling the stdlib routines I have been). Without JTAG debugging, it's going to be difficult to figure this out.