gpt4 book ai didi

c++ - gdb 为具有虚函数的类打印非字符串值的静态 const 数组的无效地址

转载 作者:IT老高 更新时间:2023-10-28 22:22:39 27 4
gpt4 key购买 nike

编辑:请向下滚动到问题末尾的“编辑”部分,了解最新的详细信息。我不会编辑这篇文章的其余部分来保留评论的历史记录。

我在头文件中定义了一个类:

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

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

上面的例子工作正常,当创建这个类的一个实例时,地址看起来像这样:

t   TestClass * 0x20000268  
carr const char * const[] 0x8002490 <TestClass::carr>
iarr const int [] 0x800249c <TestClass::iarr>

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[];
};

那么结果是这样的:

t   TestClass * 0x20000268  
carr const char * const[3] 0x80024b4 <TestClass::carr>
iarr const int [1000] 0x20000270

特别是 - 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" 

还有链接部分:

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"  

这个项目里面有更多的文件,和硬件初始化有关。我不包括那些以保持帖子简短。

是否有任何开关可以控制这种行为?我已经尝试了我能想出的显而易见的部分,这可能与这个问题有一点关系:

  • 优化级别: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;
}

程序的输出是这样的:

instance: 0xffffcba8, carr: 0x100403020, iarr: 0x100403040

map 文件也证实了这一点。相关部分:

 .rdata         0x0000000100403000       0xa0 ./src/main.o
0x0000000100403020 TestClass::carr
0x0000000100403040 TestClass::iarr

但是 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

更有趣的是,每次我尝试用 gdb 打印时,这个地址都会发生变化。这是什么原因?

问题标题和标签已调整。

最佳答案

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>}

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

关于c++ - gdb 为具有虚函数的类打印非字符串值的静态 const 数组的无效地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55183894/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com