返回顶部
首页 > 资讯 > 操作系统 >Linux如何处理变量
  • 512
分享到

Linux如何处理变量

2023-06-16 12:06:26 512人浏览 独家记忆
摘要

这篇文章主要介绍了linux如何处理变量,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。DWARF 位置某一给定时刻的内存中变量的位置使用 DW_AT_location 属性编

这篇文章主要介绍了linux如何处理变量,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

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]<lowpc=0x2e00><highpc=0x2e19>DW_OP_reg0

    • [ 1]<lowpc=0x2e19><highpc=0x2e3f>DW_OP_reg3

    • [ 2]<lowpc=0x2ec4><highpc=0x2ec7>DW_OP_reg2

    • <loclist with 3 entries follows>

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

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

DWARF 表达式

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

  • 字面编码

    • 将无符号值压入堆栈

    • 将地址操作数压入堆栈

    • 将字面量压入堆栈

    • DW_OP_lit0、DW_OP_lit1&hellip;&hellip;DW_OP_lit31

    • DW_OP_addr <addr>

    • DW_OP_constu <unsigned>

  • 寄存器值

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

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

    • DW_OP_fbreg <offset>

    • DW_OP_breg0、DW_OP_breg1&hellip;&hellip; DW_OP_breg31 <offset>

  • 堆栈操作

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

    • 复制堆栈顶部的值

    • DW_OP_dup

    • DW_OP_deref

  • 算术和逻辑运算

    • 与 DW_OP_and 相同,但是会添加值

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

    • DW_OP_and

    • DW_OP_plus

  • 控制流操作

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

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

    • DW_OP_le、DW_OP_eq、DW_OP_gt 等

    • DW_OP_bra <offset>

  • 输入转化

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

    • DW_OP_convert <DIE offset>

  • 特殊操作

    • 什么都不做!

    • DW_OP_nop

DWARF 类型

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

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

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

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

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

< 1><0x0000002a>    DW_TAG_structure_type                       DW_AT_name                  "test"                       DW_AT_byte_size             0x000000b8                       DW_AT_decl_file             0x00000001 test.cpp                       DW_AT_decl_line             0x00000001

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

< 2><0x00000032>      DW_TAG_member                         DW_AT_name                  "i"                         DW_AT_type                  <0x00000063>                         DW_AT_decl_file             0x00000001 test.cpp                         DW_AT_decl_line             0x00000002                         DW_AT_data_member_location  0 < 2><0x0000003e>      DW_TAG_member                         DW_AT_name                  "j"                         DW_AT_type                  <0x0000006a>                         DW_AT_decl_file             0x00000001 test.cpp                         DW_AT_decl_line             0x00000003                         DW_AT_data_member_location  4 < 2><0x0000004a>      DW_TAG_member                         DW_AT_name                  "k"                         DW_AT_type                  <0x00000071>                         DW_AT_decl_file             0x00000001 test.cpp                         DW_AT_decl_line             0x00000004                         DW_AT_data_member_location  8 < 2><0x00000056>      DW_TAG_member                         DW_AT_name                  "next"                         DW_AT_type                  <0x00000084>                         DW_AT_decl_file             0x00000001 test.cpp                         DW_AT_decl_line             0x00000005                         DW_AT_data_member_location  176(as signed = -80)

每个成员都有一个名称、一个类型(它是一个 DIE 偏移量)、一个声明文件和行,以及一个指向其成员所在的结构体的字节偏移。其类型指向如下。

< 1><0x00000063>    DW_TAG_base_type                       DW_AT_name                  "int"                       DW_AT_encoding              DW_ATE_signed                       DW_AT_byte_size             0x00000004 < 1><0x0000006a>    DW_TAG_base_type                       DW_AT_name                  "float"                       DW_AT_encoding              DW_ATE_float                       DW_AT_byte_size             0x00000004 < 1><0x00000071>    DW_TAG_array_type                       DW_AT_type                  <0x00000063> < 2><0x00000076>      DW_TAG_subrange_type                         DW_AT_type                  <0x0000007d>                         DW_AT_count                 0x0000002a < 1><0x0000007d>    DW_TAG_base_type                       DW_AT_name                  "sizetype"                       DW_AT_byte_size             0x00000008                       DW_AT_encoding              DW_ATE_unsigned < 1><0x00000084>    DW_TAG_pointer_type                       DW_AT_type                  <0x0000002a>

如你所见,我笔记本电脑上的 int 是一个 4 字节的有符号整数类型,float是一个 4 字节的浮点数。整数数组类型通过指向 int  类型作为其元素类型,sizetype(可以认为是 size_t)作为索引类型,它具有 2a 个元素。 test * 类型是  DW_TAG_pointer_type,它引用 test DIE。

实现简单的变量读取器

如上所述,libelfin  将为我们处理大部分复杂性。但是,它并没有实现用于表示可变位置的所有方法,并且在我们的代码中处理这些将变得非常复杂。因此,我现在选择只支持  exprloc。请根据需要添加对更多类型表达式的支持。如果你真的有勇气,请提交补丁到 libelfin 中来帮助完成必要的支持!

处理变量主要是将不同部分定位在存储器或寄存器中,读取或写入与之前一样。为了简单起见,我只会告诉你如何实现读取。

首先我们需要告诉 libelfin 如何从我们的进程中读取寄存器。我们创建一个继承自 expr_context 的类并使用 ptrace  来处理所有内容:

class ptrace_expr_context : public dwarf::expr_context { public:     ptrace_expr_context (pid_t pid) : m_pid{pid} {}     dwarf::taddr reg (unsigned regnum) override {         return get_reGISter_value_from_dwarf_register(m_pid, regnum);     }     dwarf::taddr pc() override {         struct user_regs_struct regs;         ptrace(PTRACE_GETREGS, m_pid, nullptr, &regs);         return regs.rip;     }     dwarf::taddr deref_size (dwarf::taddr address, unsigned size) override {         //TODO take into account size         return ptrace(PTRACE_PEEKDATA, m_pid, address, nullptr);     } private:     pid_t m_pid; };

读取将由我们 debugger 类中的 read_variables 函数处理:

void debugger::read_variables() {     using namespace dwarf;     auto func = get_function_from_pc(get_pc());     //... }

我们上面做的***件事是找到我们目前进入的函数,然后我们需要循环访问该函数中的条目来寻找变量:

for (const auto& die : func) {     if (die.tag == DW_TAG::variable) {         //...     } }

我们通过查找 DIE 中的 DW_AT_location 条目获取位置信息:

auto loc_val = die[DW_AT::location];

接着我们确保它是一个 exprloc,并请求 libelfin 来评估我们的表达式:

if (loc_val.get_type() == value::type::exprloc) {     ptrace_expr_context context {m_pid};     auto result = loc_val.as_exprloc().evaluate(&context);

现在我们已经评估了表达式,我们需要读取变量的内容。它可以在内存或寄存器中,因此我们将处理这两种情况:

switch (result.location_type) {                 case expr_result::type::address:                 {                     auto value = read_memory(result.value);                     std::cout << at_name(die) << " (0x" << std::hex << result.value << ") = "                               << value << std::endl;                     break;                 }                 case expr_result::type::reg:                 {                     auto value = get_register_value_from_dwarf_register(m_pid, result.value);                     std::cout << at_name(die) << " (reg " << result.value << ") = "                               << value << std::endl;                     break;                 }                 default:                     throw std::runtime_error{"Unhandled variable location"};                 }

你可以看到,我根据变量的类型,打印输出了值而没有解释。希望通过这个代码,你可以看到如何支持编写变量,或者用给定的名字搜索变量。

***我们可以将它添加到我们的命令解析器中:

else if(is_prefix(command, "variables")) {     read_variables(); }

感谢你能够认真阅读完这篇文章,希望小编分享的“Linux如何处理变量”这篇文章对大家有帮助,同时也希望大家多多支持编程网,关注编程网操作系统频道,更多相关知识等着你来学习!

--结束END--

本文标题: Linux如何处理变量

本文链接: https://lsjlt.com/news/283650.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Linux如何处理变量
    这篇文章主要介绍了Linux如何处理变量,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。DWARF 位置某一给定时刻的内存中变量的位置使用 DW_AT_location 属性编...
    99+
    2023-06-16
  • 如何处理变量冲突
    这篇文章主要介绍“如何处理变量冲突”,在日常操作中,相信很多人在如何处理变量冲突问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何处理变量冲突”的疑惑有所帮助!接下来,请跟着...
    99+
    2024-04-02
  • 如何实现Linux Shell脚本变量和环境变量
    这篇文章主要讲解了“如何实现Linux Shell脚本变量和环境变量”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何实现Linux Shell脚本变量和环境变量”吧!一、玩转变量和环境变量...
    99+
    2023-06-09
  • Linux中如何使用Shell变量
    Linux中如何使用Shell变量,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。一、本地变量的使用       ...
    99+
    2023-06-17
  • Linux如何实现条件变量
    这篇文章给大家分享的是有关Linux如何实现条件变量的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥...
    99+
    2023-06-15
  • linux环境变量如何设置
    在Linux环境中,可以通过以下几种方式来设置环境变量:1. 临时设置环境变量:可以在命令行中使用 `export` 命令来临时设置...
    99+
    2023-08-31
    linux
  • linux如何删除环境变量
    本篇内容主要讲解“linux如何删除环境变量”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“linux如何删除环境变量”吧!在linux中,可以利用unset命令删除环境变量,该命令用于删除变量或...
    99+
    2023-06-29
  • linux系统变量如何设置
    在Linux系统中,可以通过以下几种方式来设置系统变量: 通过命令行设置:可以使用export命令来设置系统变量。例如,要设置一...
    99+
    2023-10-25
    linux
  • 如何处理 PHP 函数中未定义的变量?
    在 php 中,处理未定义的函数变量主要有以下方法:1. 条件检查(isset() 或 empty());2. 默认值;3. 异常处理。这些方法确保未定义的变量不会中断程序执行。 如何...
    99+
    2024-05-03
    php 未定义变量
  • java 变量该如何理解
    这篇文章的内容主要围绕java 变量该如何理解进行讲述,文章内容清晰易懂,条理清晰,非常适合新手学习,值得大家去阅读。感兴趣的朋友可以跟随小编一起阅读吧。希望大家通过这篇文章有所收获!变量是Java 程序的一个基本存储单元。变量由一个标识符...
    99+
    2023-06-03
  • Linux中如何定义变量脚本
    这篇文章主要介绍Linux中如何定义变量脚本,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!具体内容如下现有两段基本一样的代码,只是变量进行改变,其他都没有变化,但是执行过程中出现了不一样的结果代码一:vi ...
    99+
    2023-06-09
  • 如何修改linux的环境变量
    这篇文章给大家分享的是有关如何修改linux的环境变量的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。方法:1、执行“export PATH=$PATH:/usr/local/MATLAB/R2013b/bin”命...
    99+
    2023-06-22
  • linux如何修改path环境变量
    小编给大家分享一下linux如何修改path环境变量,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!linux修改path环境变量的方法:1、通过“export P...
    99+
    2023-06-22
  • Linux下如何查看环境变量
    这篇文章给大家分享的是有关Linux下如何查看环境变量的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。环境变量相当于“快捷键”,指定一个目录,运行软件的时候,相关的程序将会按照该目录寻找相关文件1. Linux查看...
    99+
    2023-06-28
  • Linux中如何配置环境变量
    这篇文章主要介绍Linux中如何配置环境变量,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!下面所有例子的环境说明如下:系统:Ubuntu 14.0用户名:uusama需要配置MySQL环境变量路径:/hom...
    99+
    2023-06-27
  • Linux PATH环境变量如何查看
    这篇文章主要介绍“Linux PATH环境变量如何查看”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Linux PATH环境变量如何查看”文章能帮助大家解决问题。 ...
    99+
    2023-02-07
    linux path
  • linux如何配置java环境变量
    要配置Java环境变量,可以按照以下步骤进行操作:1. 下载并安装Java Development Kit(JDK):- 前往Ora...
    99+
    2023-08-24
    linux java
  • Linux如何配置jre环境变量
    要在Linux系统中配置JRE环境变量,可以按照以下步骤进行操作: 首先,确保已经安装了JRE(Java Runtime Envi...
    99+
    2024-04-09
    linux
  • 如何处理Shell脚本中带有空格的变量
    小编给大家分享一下如何处理Shell脚本中带有空格的变量,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!在批量处理音频文件时候使用下面的脚本发现含有空格的文件名并不...
    99+
    2023-06-09
  • 如何理解Python的 __name__ 变量
    本篇内容介绍了“如何理解Python的 __name__ 变量”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!大家应该已经在很多 Python...
    99+
    2023-06-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作