新闻资讯
Group news
青岛广盛源肥业有限公司    您的位置: 首页  >  新闻资讯  >  正文

Linux调试器中的处理变问题

2019年10月11日 文章来源:网络整理 热度:143℃ 作者:刘英

变量是偷偷摸摸的。有时,它们会很高兴地呆在寄存器中,但是一转头就会跑到堆栈中。为了优化,编译器可能会完全将它们从窗口中抛出。无论变量在内存中的如何移动,我们都需要一些方法在调试器中跟踪和操作它们。这篇文章将会教你如何处理调试器中的变量,并使用 libelfin 演示一个简单的实现。

系列文章索引

准备环境

断点

寄存器和内存

ELF 和 DWARF

源码和信号

源码级逐步执行

源码级断点

堆栈展开

处理变量

高级话题

在开始之前,请确保你使用的 libelfin 版本是我分支上的 fbreg。这包含了一些 hack 来支持获取当前堆栈帧的基址并评估位置列表,这些都不是由原生的 libelfin 提供的。你可能需要给 GCC 传递 -gdwarf-2 参数使其生成兼容的 DWARF 信息。但是在实现之前,我将详细说明 DWARF 5 最新规范中的位置编码方式。如果你想要了解更多信息,那么你可以从这里获取该标准。

DWARF 位置

某一给定时刻的内存中变量的位置使用 DW_AT_location 属性编码在 DWARF 信息中。位置描述可以是单个位置描述、复合位置描述或位置列表。

简单位置描述:描述了对象的一个连续的部分(通常是所有部分)的位置。简单位置描述可以描述可寻址存储器或寄存器中的位置,或缺少位置(具有或不具有已知值)。比如,DW_OP_fbreg -32: 一个整个存储的变量 - 从堆栈帧基址开始的32个字节。

复合位置描述:根据片段描述对象,每个对象可以包含在寄存器的一部分中或存储在与其他片段无关的存储器位置中。比如, DW_OP_reg3 DW_OP_piece 4 DW_OP_reg10 DW_OP_piece 2:前四个字节位于寄存器 3 中,后两个字节位于寄存器 10 中的一个变量。

位置列表:描述了具有有限生存期或在生存期内更改位置的对象。比如:

[ 0]DW_OP_reg0

[ 1]DW_OP_reg3

[ 2]DW_OP_reg2

根据程序计数器的当前值,位置在寄存器之间移动的变量。

根据位置描述的种类,DW_AT_locaTIon 以三种不同的方式进行编码。exprloc 编码简单和复合的位置描述。它们由一个字节长度组成,后跟一个 DWARF 表达式或位置描述。loclist 和 loclistptr 的编码位置列表,它们在 .debug_loclists 部分中提供索引或偏移量,该部分描述了实际的位置列表。

DWARF 表达式

使用 DWARF 表达式计算变量的实际位置。这包括操作堆栈值的一系列操作。有很多 DWARF 操作可用,所以我不会详细解释它们。相反,我会从每一个表达式中给出一些例子,给你一个可用的东西。另外,不要害怕这些;libelfin将为我们处理所有这些复杂性。

字面编码

DW_OP_lit0、DW_OP_lit1……DW_OP_lit31

将字面量压入堆栈

DW_OP_addr

将地址操作数压入堆栈

DW_OP_constu

将无符号值压入堆栈

寄存器值

DW_OP_fbreg

压入在堆栈帧基址找到的值,偏移给定值

DW_OP_breg0、DW_OP_breg1…… DW_OP_breg31

将给定寄存器的内容加上给定的偏移量压入堆栈

堆栈操作

DW_OP_dup

复制堆栈顶部的值

DW_OP_deref

将堆栈顶部视为内存地址,并将其替换为该地址的内容

算术和逻辑运算

DW_OP_and

弹出堆栈顶部的两个值,并压回它们的逻辑 AND

DW_OP_plus

与 DW_OP_and 相同,但是会添加值

控制流操作

DW_OP_le、DW_OP_eq、DW_OP_gt 等

弹出前两个值,比较它们,并且如果条件为真,则压入 1,否则为 0

DW_OP_bra

条件分支:如果堆栈的顶部不是 0,则通过 offset 在表达式中向后或向后跳过

输入转化

DW_OP_convert

将堆栈顶部的值转换为不同的类型,它由给定偏移量的 DWARF 信息条目描述

特殊操作

DW_OP_nop

什么都不做!

DWARF 类型

DWARF 类型的表示需要足够强大来为调试器用户提供有用的变量表示。用户经常希望能够在应用程序级别进行调试,而不是在机器级别进行调试,并且他们需要了解他们的变量正在做什么。

DWARF 类型与大多数其他调试信息一起编码在 DIE 中。它们可以具有指示其名称、编码、大小、字节等的属性。无数的类型标签可用于表示指针、数组、结构体、typedef 以及 C 或 C++ 程序中可以看到的任何其他内容。

以这个简单的结构体为例:

struct test{int i;float j;int k[42];test* next;};

这个结构体的父 DIE 是这样的:

< 1> DW_TAG_structure_typeDW_AT_name "test"DW_AT_byte_size 0x000000b8DW_AT_decl_file 0x00000001 test.cppDW_AT_decl_line 0x00000001

上面说的是我们有一个叫做 test 的结构体,大小为 0xb8,在 test.cpp 的第 1 行声明。接下来有许多描述成员的子 DIE。

上一篇:Linux输入子系统:怎样使用输入设备编程


下一篇:需要了解linux的内存管理

友情链接
Links