7

I'm developing a mixed C/C++ program for an ARM STM32F4, but I have problems in accessing global variables defined in the C part. Here is a simple test code to reproduce the problem.

test.h:

#ifndef TEST_H_
#define TEST_H_

#ifdef __cplusplus
extern "C" {
#endif

extern const char* strings[];

#ifdef __cplusplus
}
#endif

#endif /* TEST_H_ */

test.c:

#include <test.h>
const char* strings[] = {"string a", "string b", "string c" };

main.hpp

#ifndef MAIN_HPP_
#define MAIN_HPP_

#define STM32F4

#include <test.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>

#endif /* MAIN_HPP_ */

main.cpp:

#include <main.hpp>

int main(void)
{
    char s2[3][9];

    rcc_periph_clock_enable(RCC_GPIOD);
    gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,
    GPIO12);

    while (1) {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 9; j++) {
                s2[i][j] = strings[i][j];
                if (s2[i][j] == 'i') {
                    gpio_toggle(GPIOD, GPIO12);
                }

                for (int k = 0; k < 1000000; k++) {
                    __asm__("nop");
                }
            }
        }
    }
}

However, when I run it in the debugger I can see that the memory where strings[0] (for example) is pointing is completely zeroed.

Note: the part in the while loop is not relevant, I've just added it to have some feedback and to avoid that the compiler strips the unused values of strings.

So what am I doing wrong here?

EDIT

I'm working with Eclipse under Linux, gnu-arm-none-eabi.

complier and linker command lines and output:

arm-none-eabi-g++ -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O0 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -fno-move-loop-invariants -Wunused -Wuninitialized -Wall -Wextra -Wmissing-declarations -Wconversion -Wpointer-arith -Wpadded -Wshadow -Wlogical-op -Waggregate-return -Wfloat-equal  -g3 -I"/home/andrea/ownCloud/src/arm/libopencm3/include" -I"/home/andrea/ownCloud/src/arm/testt/src" -std=gnu++11 -fabi-version=0 -fno-exceptions -fno-rtti -fno-use-cxa-atexit -fno-threadsafe-statics -Wabi -Wctor-dtor-privacy -Wnoexcept -Wnon-virtual-dtor -Wstrict-null-sentinel -Wsign-promo -MMD -MP -MF"src/main.d" -MT"src/main.o" -c -o "src/main.o" "../src/main.cpp"
In file included from /home/andrea/ownCloud/src/arm/libopencm3/include/libopencm3/stm32/rcc.h:32:0,
                 from /home/andrea/ownCloud/src/arm/testt/src/main.hpp:14,
                 from ../src/main.cpp:20:
/home/andrea/ownCloud/src/arm/libopencm3/include/libopencm3/stm32/f4/rcc.h:640:11: warning: padding struct to align 'rcc_clock_scale::plln' [-Wpadded]
  uint16_t plln;
           ^
/home/andrea/ownCloud/src/arm/libopencm3/include/libopencm3/stm32/f4/rcc.h:644:11: warning: padding struct to align 'rcc_clock_scale::flash_config' [-Wpadded]
  uint32_t flash_config;
           ^
Finished building: ../src/main.cpp

Building file: ../src/test.c
Invoking: Cross ARM C Compiler
arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O0 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -fno-move-loop-invariants -Wunused -Wuninitialized -Wall -Wextra -Wmissing-declarations -Wconversion -Wpointer-arith -Wpadded -Wshadow -Wlogical-op -Waggregate-return -Wfloat-equal  -g3 -I"/home/andrea/ownCloud/src/arm/libopencm3/include" -I"/home/andrea/ownCloud/src/arm/testt/src" -std=gnu11 -Wmissing-prototypes -Wstrict-prototypes -Wbad-function-cast -MMD -MP -MF"src/test.d" -MT"src/test.o" -c -o "src/test.o" "../src/test.c"
Finished building: ../src/test.c

Building target: testt.elf
Invoking: Cross ARM C++ Linker
arm-none-eabi-g++ -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O0 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -fno-move-loop-invariants -Wunused -Wuninitialized -Wall -Wextra -Wmissing-declarations -Wconversion -Wpointer-arith -Wpadded -Wshadow -Wlogical-op -Waggregate-return -Wfloat-equal  -g3 -T "/home/andrea/ownCloud/src/arm/testt/src/stm32f407g-discovery.ld" -T "/home/andrea/ownCloud/src/arm/testt/src/libopencm3_stm32f4.ld" -nostartfiles -Xlinker --gc-sections -L"/home/andrea/ownCloud/src/arm/libopencm3/lib" -Wl,-Map,"testt.map" --specs=nano.specs -o "testt.elf"  ./src/main.o ./src/test.o   -lopencm3_stm32f4
Finished building target: testt.elf

Linker scripts (not the cleanest one, I did some testing with it).

MEMORY
{
    rom (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
    ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}

_stack_size = 0x400;
/* Include the common ld script. */
INCLUDE libopencm3_stm32f4.ld

libopencm3_stm32f4.ld:

/* Enforce emmition of the vector table. */
EXTERN (vector_table)

/* Define the entry point of the output file. */
ENTRY(reset_handler)

/* Define sections. */
SECTIONS
{
    .text : {
        *(.vectors) /* Vector table */
        *(.text*)   /* Program code */
        . = ALIGN(4);
        *(.rodata*) /* Read-only data */
        . = ALIGN(4);
    } >rom

    /* C++ Static constructors/destructors, also used for __attribute__
     * ((constructor)) and the likes */
    .preinit_array : {
        . = ALIGN(4);
        __preinit_array_start = .;
        KEEP (*(.preinit_array))
        __preinit_array_end = .;
    } >rom
    .init_array : {
        . = ALIGN(4);
        __init_array_start = .;
        KEEP (*(SORT(.init_array.*)))
        KEEP (*(.init_array))
        __init_array_end = .;
    } >rom
    .fini_array : {
        . = ALIGN(4);
        __fini_array_start = .;
        KEEP (*(.fini_array))
        KEEP (*(SORT(.fini_array.*)))
        __fini_array_end = .;
    } >rom

    /*
     * Another section used by C++ stuff, appears when using newlib with
     * 64bit (long long) printf support
     */
    .ARM.extab : {
        *(.ARM.extab*)
    } >rom
    .ARM.exidx : {
        __exidx_start = .;
        *(.ARM.exidx*)
        __exidx_end = .;
    } >rom

    . = ALIGN(4);
    _etext = .;

    .data : {
        _data = .;
        *(.data*)   /* Read-write initialized data */
        . = ALIGN(4);
        _edata = .;
    } >ram AT >rom
    _data_loadaddr = LOADADDR(.data);

    .bss : {
        *(.bss*)    /* Read-write zero initialized data */
        *(COMMON)
        . = ALIGN(4);
        _ebss = .;
    } >ram
    . = ALIGN(4);

    _end_bss = .;
    end = .;
    _end = .;
    _heap_bottom = .;
    _heap_top = ORIGIN(ram)+LENGTH(ram)-_stack_size;

    _stack_bottom =_heap_top;
    _stack_top = ORIGIN(ram) + LENGTH(ram);
    /*
     * The .eh_frame section appears to be used for C++ exception handling.
     * You may need to fix this if you're using C++.
     */
    /DISCARD/ : { *(.eh_frame) }
}

PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram));

EDIT

I'm looking into the problem but I'm a bit puzzled.

The startup code includes the following:

for (src = &_data_loadaddr, dest = &_data;
    dest < &_edata;
    src++, dest++) {
    *dest = *src;
}

So it seems ok to me.

The .map file gives the following infos:

.data           0x0000000020000000        0xc load address 0x000000000800038c
                0x0000000020000000                _data = .
 *(.data*)
 .data.strings  0x0000000020000000        0xc ./src/test.o
                0x0000000020000000                strings
                0x000000002000000c                . = ALIGN (0x4)
                0x000000002000000c                _edata = .
                0x000000002000000c                _data = .
 *(.data*)
                0x000000002000000c                . = ALIGN (0x4)
                0x000000002000000c                _edata = .
                0x000000000800038c                _data_loadaddr = LOADADDR (.data)

.igot.plt       0x000000002000000c        0x0 load address 0x0000000008000398

Now, when I run the debugger I see that right from the start &_data==&_edata==0x2000000c , and I notice also that _data is present two times in the .map file.

So, is there an error in the linker script?

Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
apalazzi
  • 73
  • 6
  • 1
    Unrelated to your current problem, but in the loops in `main` you go out of bounds of `strings[i]`. That alone leads to *undefined behavior*. – Some programmer dude Jan 24 '17 at 12:15
  • 3
    More related to your problem, how do you build your program? What commands (or what IDE) are you using to compile and link your program? – Some programmer dude Jan 24 '17 at 12:17
  • 2
    How do you build/link ? – Jarod42 Jan 24 '17 at 12:17
  • 1
    Do you really need to use C ? Most clean modern C code can be compiled with a C++ compiler with no or with minor changements. – Jabberwocky Jan 24 '17 at 12:18
  • @MichaelWalz - *"clean modern C"*. And now when someone brings counter examples of idiomatic C (such as not casting the return value of `malloc`) it's going to be arbitrarily deemed "not clean"? – StoryTeller - Unslander Monica Jan 24 '17 at 12:28
  • @StoryTeller that's why I write "minor changes". – Jabberwocky Jan 24 '17 at 12:33
  • @MichaelWalz - Edited that in, you mean. But to address *that* point, and to use the same example of `malloc`: A typical program will have dozens or even hundreds of dynamic allocations. To trace each one just to bend it to the will of a C++ compiler is not "minor". But we can agree to disagree if you feel it is. – StoryTeller - Unslander Monica Jan 24 '17 at 12:39
  • @StoryTeller it depends on the OP's code. It could be a suitable solution or it could not. – Jabberwocky Jan 24 '17 at 12:47
  • If the OP is calling functions from C++ that are in a third-party library written in C, the source may not be available at all, let alone be modifiable. – Peter Jan 24 '17 at 12:54
  • 1
    Note: If `strings` is not to be changed, you should make the table `const`, too to place it in Flash instead of RAM as it will be placed now. – too honest for this site Jan 24 '17 at 13:04

2 Answers2

5

As Olaf said in a comment, you did not declare your string table as constant. So it is considered by the compiler/linker as initialized read/write data, instead of read only data.

Maybe your initialization code (executed before the main entry point) does not properly copy the initialized data from flash to RAM.

As a quick fix, try to make your string table as constant:

char const * const strings[] = {"string a", "string b", "string c" };

If it works, you could then investigate memory initialization issues... Have a look to the -nostartfiles argument given to the linker, which may probably disable the startup code (to be confirmed)...

greydet
  • 5,509
  • 3
  • 31
  • 51
0

The problem finally was with the project configuration in Eclipse: I specified both the .ld files as scripts to be included, but the first already had an include directive for the second file; this caused the double _data specification and the wrong behaviour of the startup code.

apalazzi
  • 73
  • 6