【问题标题】:Application crashes when accessing specific data in the .rodata section访问 .rodata 部分中的特定数据时应用程序崩溃
【发布时间】:2019-10-27 17:14:24
【问题描述】:

我不得不完全改写这个问题及其标题,因为我最初的分析结果是错误的(感谢所有提示和建议!)。问题的旧标题是:

GCC -O3 搞乱二进制文件并产生数据垃圾

事实证明它与优化级别无关,我也不确定数据是否垃圾。

无论如何,这些是我正在使用的 SDK 代码的相关 sn-ps。我显然不能在这里发布整个 SDK 代码。

首先我有两个常量:

static const usb_device_controller_interface_struct_t s_UsbDeviceEhciInterface = {
    USB_DeviceEhciInit, USB_DeviceEhciDeinit, USB_DeviceEhciSend,
    USB_DeviceEhciRecv, USB_DeviceEhciCancel, USB_DeviceEhciControl};

static const usb_device_class_map_t s_UsbDeviceClassInterfaceMap[] = {
{USB_DeviceCdcAcmInit, USB_DeviceCdcAcmDeinit, USB_DeviceCdcAcmEvent, kUSB_DeviceClassTypeCdc},
    {(usb_device_class_init_call_t)NULL, (usb_device_class_deinit_call_t)NULL, (usb_device_class_event_callback_t)NULL,
     (usb_device_class_type_t)0},
};

在我的 main.c 中,看似无关,有这个 sn-p:

if (kStatus_USB_Success != error)
{
    usb_echo("kUSB_DeviceCdcEventSetControlLineState error!");
}

现在,如果我使用-O3 编译,应用程序在访问s_UsbDeviceEhciInterface 时崩溃,使用-O0 时应用程序在访问s_UsbDeviceClassInterfaceMap 时崩溃

似乎都涉及到第三个代码 sn-p。

使用-O3,反汇编中的数据对于s_UsbDeviceEhciInterface如下所示:

000066b4 <s_UsbDeviceEhciInterface>:
    66b4:   00000479 0000145d 00001499 000014a5     y...]...........
    66c4:   000014ad 00001685 4253556b 7665445f     ........kUSB_Dev
    66d4:   43656369 76456364 53746e65 6f437465     iceCdcEventSetCo
    66e4:   6f72746e 6e694c6c 61745365 65206574     ntrolLineState e
    66f4:   726f7272 00000021                       rror!...

s_UsbDeviceClassInterfaceMap 似乎根本不存在。

使用-O0,反汇编中的数据对于s_UsbDeviceEhciInterface如下所示:

00009164 <s_UsbDeviceEhciInterface>:
    9164:   000018b5 00001999 00006ebb 00006eed     .........n...n..
    9174:   00001a21 00001c35                       !...5...

对于s_UsbDeviceClassInterfaceMap

0000917c <s_UsbDeviceClassInterfaceMap>:
    917c:   00007eb1 00007f49 00002985 00000002     .~..I....)......
    ...
    919c:   00007071 4253556b 7665445f 43656369     qp..kUSB_DeviceC
    91ac:   76456364 53746e65 6f437465 6f72746e     dcEventSetContro
    91bc:   6e694c6c 61745365 65206574 726f7272     lLineState error
    91cc:   ffff0021                                !...

在映射文件中,我们可以看到两次 main.c 数据跟随 const - 正如反汇编所暗示的那样。对于-O3

 .rodata.s_UsbDeviceEhciInterface
                0x00000000000066b4       0x18 ./usb/device/source/usb_device_dci.o
 .rodata.USB_DeviceCdcVcomCallback.str1.4
                0x00000000000066cc       0x30 ./source/main.o

对于-O0

.rodata.s_UsbDeviceEhciInterface
                    0x0000000000009164       0x18 ./usb/device/source/usb_device_dci.o
.rodata.s_UsbDeviceClassInterfaceMap
                    0x000000000000917c       0x20 ./usb/device/class/usb_device_class.o
.rodata        0x000000000000919c       0x32 ./source/main.o

回顾一下:从 main.c 访问常量字符串之前的常量时,应用程序总是崩溃

我尝试启动编译器警告,但在 SDK 代码中只得到一些未使用的参数


为了完整起见,这是原始问题:


过时(见上文)

所以,我有这个代码:

static const usb_device_controller_interface_struct_t s_UsbDeviceEhciInterface = {
    USB_DeviceEhciInit, USB_DeviceEhciDeinit, USB_DeviceEhciSend,
    USB_DeviceEhciRecv, USB_DeviceEhciCancel, USB_DeviceEhciControl};

在其他地方,在另一个看似无关的文件中,我有这个 sn-p:

if (kStatus_USB_Success != error)
{
    usb_echo("kUSB_DeviceCdcEventSetControlLineState error!");
}

现在,访问s_UsbDeviceEhciInterface 会使应用程序崩溃。查看反汇编文件,我发现以下内容:

00006674 <s_UsbDeviceEhciInterface>:
    6674:   00000479 0000145d 00001499 000014a5     y...]...........
    6684:   000014ad 00001685 4253556b 7665445f     ........kUSB_Dev
    6694:   43656369 76456364 53746e65 6f437465     iceCdcEventSetCo
    66a4:   6f72746e 6e694c6c 61745365 65206574     ntrolLineState e
    66b4:   726f7272 00000021                       rror!...

这似乎是两个不相关的代码部分的组合!

如果我删除第二个代码 sn-p 访问 s_UsbDeviceEhciInterface 再次工作。然后反汇编看起来像这样,这更有意义:

000042b4 <s_UsbDeviceEhciInterface>:
    42b4:   00000479 00000b0d 00000b49 00000b55     y.......I...U...
    42c4:   00000b5d 00000d35                       ]...5...

这仅在我使用 -O3 优化时发生。当我切换到-O0 时,一切似乎又好了。

这是怎么发生的?有没有办法在仍然使用-O3 的同时防止这种情况发生?

编辑:我刚刚意识到-O0 也会发生这种情况,只是在数据的另一部分。也许我搞砸了我的链接器脚本?

【问题讨论】:

  • 请尝试获取代码的反汇编输出,而不是查看原始的十六进制转储。
  • 无法帮助您,因为您只提供了一小部分代码。毫无疑问,您有一个代码错误,它不显示为 -O0,而只显示为 -O3。贴一个完整的代码示例...
  • 打开编译器警告。 GCC 可能正在消除死代码路径。
  • 我刚刚意识到它也发生在 -O0 上,只是在不同的地方。我会尝试打开我的编译器警告,谢谢@JL2210
  • @Someprogrammerdude :“原始十六进制转储”是数据对象,而不是代码。

标签: c gcc arm embedded


【解决方案1】:

注意:这不是对您的崩溃的解释,而是试图解释假定的数据垃圾。

您的反汇编似乎是由最终的可执行文件制成的。在此文件中,所有常量都被收集并附加到 rodata 部分。静态和常量结构 s_UsbDeviceEhciInterface 显然是只读数据,就像您在它后面找到的字符串文字一样。

两者的区别在于前者有一个用户定义的名称s_UsbDeviceEhciInterface,而后者没有。这就是为什么反汇编将名称显示为起始标签,但不显示字符串的原因。

您可以生成一个映射文件(链接器的选项-Map)并查看几个对象及其位置。

所以,-O3 不会产生数据垃圾。

由于您发现-O0 也会导致崩溃,您需要从另一个方向进行调查。想想节溢出、悬空指针、未初始化的变量等等。将所有警告级别提高到最大!

【讨论】:

  • 感谢您的解释,我会查看地图文件并提高所有警告级别。我发现有趣的是,-O0 会发生完全相同的事情,只是在另一个常量上会发生。不过,您是对的,对我来说它可能看起来像垃圾,因为其中一个数据结构没有用户定义的名称。无论如何,我会继续调查。也许是某种溢出。
猜你喜欢
  • 2017-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多