AVR-GCC 优化了“外部”全局变量,然后出现“未定义引用...”的错误

问题描述 投票:0回答:1

简而言之,.cpp 文件中定义的全局 const 变量(数组)从 .o 文件中消失,导致“未定义的引用...”错误。通过在变量的定义中添加“易失性”可以完美解决该问题。关闭编译器优化或“-flto”是没有用的。

编译器:AVR-GCC 11.10,来自 https://github.com/ZakKemble/avr-gcc-build/releases

环境:PlatformIO。

操作系统:Windows 11 x64。

定义如下:

// file: oled_basic_init_sequence.cpp

#include <Arduino.h>

namespace  oled_basic {

    const uint8_t _init_sequence_for_ssd1316_4_wire_spi[25] PROGMEM = {
        0xae, /*display off*/
        0x00, /*set lower column address*/
        // ...
    };


    const uint8_t _init_sequence_for_ssd1306_2_wire_i2c[23] PROGMEM = {
        0xAE,
        0xD5,
        // ...
    };

}  // namespace oled_basic

有或没有

PROGMEM
并不重要。在.h文件中:

// file: oled_basic_init_sequence.h

#pragma once


#include <stdint.h>

namespace oled_basic {

    extern const uint8_t _init_sequence_for_ssd1316_4_wire_spi[25];

    extern const uint8_t _init_sequence_for_ssd1306_2_wire_i2c[23];

}  // namespace oled_basic

在main.cpp文件中:


#include <Arduino.h>


#include "oled_basic_init_sequence.h"


volatile uint8_t * test = 0;


void setup() {
    *test = oled_basic::_init_sequence_for_ssd1316_4_wire_spi[0];
    
    // this doesn't help
    *test = pgm_read_byte(&(oled_basic::_init_sequence_for_ssd1316_4_wire_spi[0]));
}

void loop() {

}


编译失败:

Building in release mode
Compiling .pio\build\ATmega128\src\oled_basic_init_sequence.cpp.o
Linking .pio\build\ATmega128\firmware.elf
c:/users/xxxxx/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/11.1.0/../../../../avr/bin/ld.exe: C:\Users\xxxxx\AppData\Local\Temp\ccelvOYl.ltrans0.ltrans.o: in function `main':
<artificial>:(.text.startup+0x5a): undefined reference to `_ZN10oled_basic37_init_sequence_for_ssd1316_4_wire_spiE'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\ATmega128\firmware.elf] Error 1
========================================================= [FAILED] Took 3.42 seconds =========================================================

详细构建信息:


avr-g++ -o .pio\build\ATmega128\firmware.elf -mmcu=atmega128 -Os -Wl,--gc-sections -flto -fuse-linker-plugin .pio\build\ATmega128\src\main.cpp.o .pio\build\ATmega128\src\oled_basic_font.cpp.o .pio\build\ATmega128\src\oled_basic_init_sequence.cpp.o -L.pio\build\ATmega128 -Wl,--start-group .pio\build\ATmega128\lib81f\libi2c.a .pio\build\ATmega128\lib685\libSPI.a .pio\build\ATmega128\libe29\libWire.a .pio\build\ATmega128\libFrameworkArduinoVariant.a .pio\build\ATmega128\libFrameworkArduino.a -lm -Wl,--end-group

也许我只是犯了一些低级语法错误,但仍然无法意识到。

c++ gcc avr avr-gcc
1个回答
0
投票

可能的修复

包括

oled_basic_init_sequence.h
中的
oled_basic_init_sequence.cpp

说明

namespace  oled_basic {
    const uint8_t _init_sequence_for_ssd1316_4_wire_spi[25] = {
        0xae, /*display off*/
        0x00, /*set lower column address*/
        // ...
    };
}

如果没有使用

extern
关键字对变量进行前向声明,此代码将使用
内部链接
定义 _init_sequence_for_ssd1316_4_wire_spi:它不能在其他翻译单元中使用。

通过前向声明,变量将通过外部链接来定义。

namespace  oled_basic {
    extern const uint8_t _init_sequence_for_ssd1316_4_wire_spi[25];

    const uint8_t _init_sequence_for_ssd1316_4_wire_spi[25] = {
        0xae, /*display off*/
        0x00, /*set lower column address*/
        // ...
    };
}

我们可以在 compiler explorer 上看到编译器为第二个片段中的变量发出全局符号,但不是第一个片段:

.global oled_basic::_init_sequence_for_ssd1316_4_wire_spi
        .section        .rodata
        .type   oled_basic::_init_sequence_for_ssd1316_4_wire_spi, @object
        .size   oled_basic::_init_sequence_for_ssd1316_4_wire_spi, 25
oled_basic::_init_sequence_for_ssd1316_4_wire_spi:
        .string "\256"
        .string ""
        .zero   22
© www.soinside.com 2019 - 2024. All rights reserved.