对于具有虚函数的类,gdb打印非字符串值的静态const数组的无效地址

Jac*_*mok 15 c++ gdb

编辑:请向下滚动至问题末尾的“编辑”部分,以获取更多最新信息。我不会编辑本文的其余部分以保留评论的历史记录。

我在头文件中定义了一个类,如下所示:

class TestClass
{
public:
  TestClass() { }
  ~TestClass() { }
  void Test();
private:
  static const char * const carr[];
  static const int iarr[];
};
Run Code Online (Sandbox Code Playgroud)

TestClass::Test()函数只是确保两个数组都被使用,所以它们没有被优化-将它们打印到日志中。为了清楚起见,我不会在这里发布它。数组在.cpp文件中初始化。

大写情况很好,当创建此类的实例时,地址如下所示:

t   TestClass * 0x20000268  
    carr    const char * const[]    0x8002490 <TestClass::carr> 
    iarr    const int []    0x800249c <TestClass::iarr>
Run Code Online (Sandbox Code Playgroud)

开头的内存地址0x20...属于RAM区域,而0x80...属于ROM / Flash。如预期的那样,两个阵列都放置在ROM中。

但是,如果我将virtual限定符添加到类中的任何函数,例如其析构函数,如下所示:

class TestClass
{
public:
  TestClass() { }
  virtual ~TestClass() { }
  void Test();
private:
  static const char * const carr[];
  static const int iarr[];
};
Run Code Online (Sandbox Code Playgroud)

那么结果是这样的:

t   TestClass * 0x20000268  
    carr    const char * const[3]   0x80024b4 <TestClass::carr> 
    iarr    const int [1000]    0x20000270
Run Code Online (Sandbox Code Playgroud)

特别是- iarr放在RAM中,这完全不是我期望的。

该文件的编译方式如下:

arm-none-eabi-g++ -mcpu=cortex-m7 -mthumb -mfloat-abi=soft -O0 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -ffreestanding -fno-move-loop-invariants -Wall -Wextra -g3 -DDEBUG -DUSE_FULL_ASSERT -DTRACE -DOS_USE_TRACE_ITM -DSTM32F767xx -DUSE_HAL_DRIVER -DHSE_VALUE=24000000 -I../include -I../system/include -I../system/include/cmsis -I../system/include/stm32f7-hal -std=gnu++11 -fabi-version=0 -fno-exceptions -fno-rtti -fno-use-cxa-atexit -fno-threadsafe-statics -c -o "src\\main.o" "..\\src\\main.cpp" 
Run Code Online (Sandbox Code Playgroud)

和链接部分:

arm-none-eabi-g++ -mcpu=cortex-m7 -mthumb -mfloat-abi=soft -O0 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -ffreestanding -fno-move-loop-invariants -Wall -Wextra  -g3 -T mem.ld -T libs.ld -T sections.ld -nostartfiles -Xlinker --gc-sections -L"../ldscripts" -Wl,-Map,"VirtualClassTestF7.map" --specs=nano.specs -o "VirtualClassTestF7.elf" "@objs.rsp"  
Run Code Online (Sandbox Code Playgroud)

此项目中内置了更多与硬件初始化有关的文件。我不包括那些使职位简短的人。

是否有任何开关可以控制此行为?我已经尝试了一些显而易见的部分,这些部分可能与该问题有丝毫联系:

  • 优化级别:O0,O1,O2,O3,Os,Ofast
  • 移除-ffunction-sections-fdata-sections
  • 新增中 -fno-common
  • 使数组更大以超过某个阈值(如果有的话)。我已经将其大小设为10k元素(乘以sizeof(uint32_t)),并且它仍在RAM中
  • 尝试三种不同版本的工具链

工具链是arm-none-eabi。试用版本(的输出arm-none-eabi-gcc --version):

  • arm-none-eabi-gcc.exe(用于ARM嵌入式处理器的GNU工具)4.9.3 20150529(发行版)[ARM / embedded-4_9-branch版本224288]
  • arm-none-eabi-gcc.exe(出血边缘工具链)7.2.0
  • arm-none-eabi-gcc.exe(出血边缘工具链)8.3.0
  • Cygwin(GCC(GCC)7.4.0)

第一个来自ARM官方网站:https : //developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads。后两个来自http://www.freddiechopin.info/en/download/category/11-bleeding-edge-toolchain,因为ARM正式不发布64位版本,并且我们的项目规模扩大到可以打破32位的水平版。

为什么这是一个问题,为什么我要专门研究编译器开关:可能还有另一种方法,可以通过将它们写入不同的方式来将这些值强制进入ROM。这不是一种选择-我们最近在一个较大的项目中遇到了这个问题,该项目跨越成千上万个文件,并且在各个地方都大量使用类继承。捕获所有可能出现的此类数组(有些是使用宏创建的,有些是使用外部工具生成的),然后重新组织所有这些代码是不可能的。因此,我正在寻找编译器以这种精确方式运行的原因以及不涉及触摸源文件的可能解决方案是什么。

编辑: gdb似乎存在某种问题,它如何检索该变量的地址,或者我丢失了一些东西。我继续在PC(Cygwin gcc 7.4.0)上创建了相同的示例:

#include <stdio.h>

class TestClass
{
public:
  TestClass() { }
  virtual ~TestClass() { }
  static const char * const carr[];
  static const int iarr[];
};

const char * const TestClass::carr[] = {
    "test1", "test2", "test3"
};

const int TestClass::iarr[] = {
    1,2,3,4,5,6,7,8,9,0
};

int main() {
  TestClass instance;
  printf("instance: %p, carr: %p, iarr: %p\n", &instance, instance.carr, instance.iarr);
  fflush(stdout);
  while(1);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

该程序的输出是这样的:

instance: 0xffffcba8, carr: 0x100403020, iarr: 0x100403040
Run Code Online (Sandbox Code Playgroud)

地图文件也确认了这一点。相关部分:

 .rdata         0x0000000100403000       0xa0 ./src/main.o
                0x0000000100403020                TestClass::carr
                0x0000000100403040                TestClass::iarr
Run Code Online (Sandbox Code Playgroud)

但是gdb显示了这一点:

p instance.iarr
$2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
p &instance.iarr
[New Thread 57872.0x4f28]
$3 = (const int (*)[10]) 0x60003b8a0
p &instance.iarr
$4 = (const int (*)[10]) 0x60003b8d0
Run Code Online (Sandbox Code Playgroud)

更有趣的是,每次我尝试使用gdb打印该地址时,该地址都会更改。这是什么原因?

问题标题和标签已调整。

use*_*083 1

gdb 将数组复制到 RAM 中,您甚至不需要它的实例,带有 vtable 的类就足够了:

(gdb) p TestClass::iarr
$1 = {1, 2, 3, 4, 5, 6}
(gdb) p (int*)TestClass::iarr
$2 = (int *) 0x7ffff7a8b780
(gdb) p *(int *) 0x7ffff7a8b780 @ 100
$3 = {1, 2, 3, 4, 5, 6, 0 <repeats 94 times>}
(gdb) p (int*)TestClass::iarr
$4 = (int *) 0x7ffff7a8b7a0
(gdb) p (int*)TestClass::iarr
$5 = (int *) 0x7ffff7a8b7c0
(gdb) p *(int *) 0x7ffff7a8b780 @ 100
$6 = {1, 2, 3, 4, 5, 6, 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 1, 2, 3, 4, 5, 6, 0 <repeats 78 times>}
Run Code Online (Sandbox Code Playgroud)

我想这可以归结为 gdb 对“C”的解释。如果您需要 gdb 中的真实地址,则需要一个返回它的函数。