0

I am experiencing a problem with a C++ project for an STM32 ARM micro. I created a class that implement a state machine. This class has some methods that are not called directly by other methods, but just by function pointers:

// Foo.h
class Foo
{
public:
    typedef void(Foo::*State)(void);
    State state;

    void init();
    virtual void poll();

    void state1();
    void state2();

    Foo();
    virtual ~Foo() {}
};

// Foo.cpp
#include "Foo.h"

#define ASSIGN_STATE(X,Y) \
X = &Foo::state ## Y;

void Foo::init()
{
    ASSIGN_STATE(state, 1);
}

void Foo::state1()
{
    ASSIGN_STATE(state, 2);
}

void Foo::state2()
{
    ;
}

void Foo::poll()
{
    (this->*state)();
}

Foo::Foo()
{
    this->init();
}

I instanciate an object of this class with global scope:

Foo foo;
int main()
{
    foo.init();
    while(1)
        foo.poll();
    return(1);
}

If I compile the project the methods that are not directly called by other methods (state1() and state2()) are not linked and the firmware crashes when it calls them.

If instead I instantiate them inside main(), the methods are linked and everything works correctly.

int main()
{
    Foo foo;
    foo.init();
    while(1)
        foo.poll();
    return(1);
}

Compiler and linker flags:

COMPILER_FLAGS = $(DEBUG_FLAGS) -mcpu=cortex-m3 -mthumb -Wall -mlittle-endian -MD -MP -MF $(DEPS)/$(@F:%.o=%.d) -fno-strict-aliasing -fsigned-char -ffunction-sections -fdata-sections $(DEFINES) $(INCLUDES)
CPPCOMPILER_FLAGS =  $(DEBUG_FLAGS) -mcpu=cortex-m3 -mthumb -Wall -mlittle-endian -MD -MP -MF $(DEPS)/$(@F:%.o=%.d) -fno-strict-aliasing -fsigned-char -ffunction-sections -fdata-sections -fno-rtti -fno-exceptions $(DEFINES) $(INCLUDES)
LINKER_FLAGS = -mcpu=cortex-m3 -mthumb -specs=nano.specs -lnosys -L$(LINKERSCRIPT_DIR) -T$(LINK_SCRIPT) -Wl,--gc-sections $(DEFINES)

The project is C/C++ mixed. I user the GCC ARM TOOLS toolchain to compile it, under the Windows 7 OS.

I tried with different compiler flags and different optimization options, but nothing changed. Any idea on why I get this behavior, or how I could investigate its causes?

Ryan Kohn
  • 13,079
  • 14
  • 56
  • 81
  • Can you please complete the code according to http://stackoverflow.com/help/mcve. For example, the implementation `foo.poll()` is missing and how the functions are called. – sfrehse Oct 28 '14 at 09:34
  • Basically, please add some internals of the transition function. – sfrehse Oct 28 '14 at 09:35
  • 1
    Please further add some compiler flags you used and build environment (including OS). – sfrehse Oct 28 '14 at 09:42
  • Possible duplicate of [What is an undefined reference/unresolved external symbol error and how do I fix it?](http://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix) – πάντα ῥεῖ Oct 28 '14 at 09:49
  • Thanks for the hints. I modified the sources adding a complite .cpp and .h example and correcting some errors. I compiled the exemple and reproduced the error. I found no help in the page suggested by πάντα ῥεῖ – Luca Giancristofaro Oct 28 '14 at 10:21
  • The only wrong thing I see is forgetting the required `int` in `int main()`. I am unable to make g++ 4.9.1 warn about that, though. Still it might help with other things to specify `--std=` your language standard, and `-pedantic-errors` and `-Wextra`. – Cheers and hth. - Alf Oct 28 '14 at 10:53

1 Answers1

0

What's happening is that part of your code is getting garbage-collected by the linker because it thinks it's not referenced anywhere. I've seen this happen with code only accessed from an ISR because gcc couldn't see any reference between the main code and the ISR.

When you specify -ffunction-sections and -fdata-sections an automatically named section is created for every function and data item in your project. Then, when it gets to the linker you've got -Wl,--gc-sections. This causes the linker to drop all unreferenced sections from the output. On an MCU this is a Very Good Thing because it minimises your program memory usage. Unfortunately, as I've described, the compiler is not infallible in its detection of unused code.

In the past I've hacked around this by declaring a pointer to the missing object inside code that's definitely OK and ensuring that I initialise it in the main code. The compiler sees the outgoing reference and the function/data doesn't get garbage collected.

You could probably also solve it by moving the data/function that's going missing to a section that never gets garbage collected using the gcc section attribute. For example, to move a function you could use __attribute__((section(".text"))) on it.

Andy Brown
  • 11,766
  • 2
  • 42
  • 61
  • Thanks for the hint, I think this is the problem I have. Anyway, I tried with adding the __attribute__((section(".text"))) on a method, but it doesn't work. I could I tell the compiler not to garbage-collect .text? – Luca Giancristofaro Oct 29 '14 at 10:26
  • I do not get what you exactly mean with the hack with pointer declaration to the object, could you please post a sample code? I am not able to test the compilation removing -Wl,--gc-sections, because I do not have enough program memory. I will try removing some code. – Luca Giancristofaro Oct 29 '14 at 10:28
  • With the class instance at global scope you may be able to prevent the methods from disappearing by declaring some Foo::State fn pointer variables inside main() and assigning state1 and state2 to them. You can also use the `-Wl,--print-gc-sections` linker option to monitor what it's gc'd. – Andy Brown Oct 29 '14 at 14:57
  • I tried your method pointer hack, but it doesn't work. I understood how my functions are discarded byt the linker, but still do not know how to avoid it – Luca Giancristofaro Oct 30 '14 at 11:16
  • Have you tried confirming what exactly is removed? Compile with `-g` to get source attached. Link with `-Wl,--print-gc-sections` so you can see sections removed. Then cross-ref removed sections with `objdump -xS Foo.o`. – Andy Brown Oct 30 '14 at 14:47
  • I made what you said. linking with -Wl,--print-gc-sections produces a huge amount of output lines and do not why I am not able to redirect them to a file. I do not understand how -g would be usefull in my case. How should I give an interpretation to the objdump output? – Luca Giancristofaro Nov 06 '14 at 21:25
  • from objdump I see that state1 and state2 are not linked – Luca Giancristofaro Nov 06 '14 at 21:59