c - gdb 错误地解析堆栈变量位置
问题描述
在调试我正在编写的有趣/学习经验的小内核时,我遇到了一个有点令人费解的 gdb 问题,它显然没有正确解析堆栈上的局部变量地址。到目前为止,我的调查表明调试符号是正确的,但不知何故 gdb 在显示该变量的内容时仍然从错误的内存位置读取。
有问题的相关C代码是:
typedef union
{
uint16_t packed;
struct __attribute__((packed))
{
uint8_t PhysicalLimit;
uint8_t LinearLimit;
} limits;
} MemAddrLimits;
void KernelMain32()
{
ClearScreen();
SimplePrint("kernelMain32");
MemAddrLimits memAddr;
memAddr.packed = GetMemoryAddressLimits();
for (;;) {}
}
where返回指令以 2 字节整数形式GetMemoryAddressLimits()
提供的内存地址宽度(目前用于我的测试)。但是,当使用 gdb 单步执行此函数以打印 的值时,不会显示正确的结果:cpuid
0x3028
memAddr
gdb> p memAddr
$1 = {packed = 0, limits = {PhysicalLimit = 0 '\000', LinearLimit = 0 '\000'}}
gdb> info locals
memAddr = {packed = 0, limits = {PhysicalLimit = 0 '\000', LinearLimit = 0 '\000'}}
gdb> info addr memAddr
打印Symbol "memAddr" is a variable at frame base reg $ebp offset 8+-18.
ie,memAddr
位于ebp-10
并且实际上,检查该地址会显示预期的内容:
gdb> x/hx $ebp-10
0x8ffee: 0x3028
相比之下gdb> p &memAddr
,给出了(MemAddrLimits *) 0x7f6
内存在哪个位置归零的值。
当声明memAddr
为 auint16_t
而不是我的联合类型时,不会发生这些问题。在这种情况下,我们得到
gdb> info addr memAddr
Symbol "memAddr" is multi-location:
Range 0x8b95-0x8b97: a variable in $eax
.
但是,结果仍然(也)写入ebp-10
,即函数的反汇编是相同的 - 唯一的区别在于调试符号。
我是否在这里遗漏了什么,或者有人对这种情况下可能出现的问题有一个好主意?
更多细节
程序版本和构建标志
使用gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0
和GNU gdb (Ubuntu 9.1-0ubuntu1) 9.1
。
使用标志编译
-ffreestanding -m32 -fcf-protection=none -fno-pie -fno-pic -O0 -gdwarf-2 -fvar-tracking -fvar-tracking-assignments
并与
-m elf_i386 -nodefaultlibs -nostartfiles -Ttext 0x7c00 -e start -g
链接阶段生成kernel.elf
我后处理以提取原始可执行二进制文件以及要加载到 gdb 中的符号文件。到目前为止,这对我来说效果很好。
二进制文件中涉及的代码显然比我展示的要多,其中大部分是用汇编编写的,在这里不应该相关。
编译文件
gcc 生成以下代码(片段来自objdump -d kernel.elf
):
00008b74 <KernelMain32>:
8b74: 55 push ebp
8b75: 89 e5 mov ebp,esp
8b77: 83 ec 18 sub esp,0x18
8b7a: e8 f0 fe ff ff call 8a6f <ClearScreen>
8b7f: 68 41 8c 00 00 push 0x8c41
8b84: e8 7a ff ff ff call 8b03 <SimplePrint>
8b89: 83 c4 04 add esp,0x4
8b8c: e8 0f 00 00 00 call 8ba0 <GetMemoryAddressLimits>
8b91: 66 89 45 f6 mov WORD PTR [ebp-0xa],ax
8b95: eb fe jmp 8b95 <KernelMain32+0x21>
从那里我们可以看到它memAddr
确实位于ebp-10
堆栈上,与我们gdb> info addr memAddr
告诉我们的一致。
矮人信息(objdump --dwarf kernel.elf
):
<1><4ff>: Abbrev Number: 20 (DW_TAG_subprogram)
<500> DW_AT_external : 1
<501> DW_AT_name : (indirect string, offset: 0x23c): KernelMain32
<505> DW_AT_decl_file : 2
<506> DW_AT_decl_line : 79
<507> DW_AT_decl_column : 6
<508> DW_AT_low_pc : 0x8b74
<50c> DW_AT_high_pc : 0x8b97
<510> DW_AT_frame_base : 0x20 (location list)
<514> DW_AT_GNU_all_call_sites: 1
<515> DW_AT_sibling : <0x544>
<2><519>: Abbrev Number: 21 (DW_TAG_variable)
<51a> DW_AT_name : (indirect string, offset: 0x2d6): memAddr
<51e> DW_AT_decl_file : 2
<51f> DW_AT_decl_line : 86
<520> DW_AT_decl_column : 19
<521> DW_AT_type : <0x4f3>
<525> DW_AT_location : 2 byte block: 91 6e (DW_OP_fbreg: -18)
和相关片段来自objdump --dwarf=loc kernel.elf
:
Offset Begin End Expression
00000000 <End of list>
objdump: Warning: There is an overlap [0x8 - 0x0] in .debug_loc section.
00000000 <End of list>
objdump: Warning: There is a hole [0x8 - 0x20] in .debug_loc section.
00000020 00008b74 00008b75 (DW_OP_breg4 (esp): 4)
0000002c 00008b75 00008b77 (DW_OP_breg4 (esp): 8)
00000038 00008b77 00008b97 (DW_OP_breg5 (ebp): 8)
00000044 <End of list>
[...]
这些似乎都是我所期望的。(不过,我不确定最后一个警告是否有意义)。
附加说明
如果我将编译标志更改-gdwarf-2
为-g
我得到
gdb> p &memAddr
$1 = (MemAddrLimits *) 0x8ffde
gdb> info addr memAddr
Symbol "memAddr" is a complex DWARF expression:
0: DW_OP_fbreg -18
.
gdb> p memAddr
$2 = {packed = 0, limits = {PhysicalLimit = 0 '\000', LinearLimit = 0 '\000'}}
gdb> p/x $ebp-10
$3 = 0x8ffee
所以memAddr
仍然没有正确解决,但p &memAddr
至少在堆栈框架中,而不是完全不同的地方。不过,info addr memAddr
现在好像有问题了……
解决方案
经过进一步调查,我发现这是由于在 x86-64 qemu 仿真系统上远程调试 32 位代码(我的内核尚未切换到长模式)所致。如果我调试相同的代码,qemu-system-i386
一切正常。
推荐阅读
- javascript - 为什么我的 json.dumps 没有将报价值转换为我的浏览器?
- regex - 在谷歌表格中运行用于条件格式的自定义公式仅格式化“应用于范围”中的第一个单元格
- unity3d - UNITY2D:在具有相同标签和相同图层的两个对象之间进行选择
- ios - 在 iOS 上通过 Spotify App Remote 的 SPTSession 进行身份验证时获取一个奇怪的刷新令牌
- elasticsearch - Elasticsearch / Kibana - 如何通过特定标识符获取日志的时间点快照
- css - 即使在正文中设置了绝对大小,移动 Firefox 上的字体大小与移动 Chrome 不同
- react-native - 如何在 React Native 中获取设备令牌?
- git - Android-studio 更改列表功能如何在 git 的幕后工作
- c# - 我可以使用 C# SemaphoreSlim 来触发某些东西吗
- oracle-apex - 从不同帐户在 Oracle Apex 上发送电子邮件