Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How Linux handles variables

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces Linux how to deal with variables, has a certain reference value, interested friends can refer to, I hope you can learn a lot after reading this article, the following let the editor take you to understand it.

DWARF location

The location of the variable in memory at a given time is encoded in the DWARF information using the DW_AT_location attribute. A location description can be a single location description, a composite location description, or a location list.

Simple location description: describes the location of a contiguous part of an object (usually all parts). A simple location description can describe a location in an addressable memory or register, or a missing location (with or without a known value). For example, DW_OP_fbreg-32: an entire stored variable-32 bytes starting from the stack frame base address.

Compound location description: depending on the fragment description object, each object can be contained in a portion of the register or stored in a memory location independent of other fragments. For example, DW_OP_reg3 DW_OP_piece 4 DW_OP_reg10 DW_OP_piece 2: the first four bytes are in register 3 and the last two bytes are in a variable in register 10.

Location list: describes objects that have a limited lifetime or change location during the lifetime. For example:

[0] DW_OP_reg0

[1] DW_OP_reg3

[2] DW_OP_reg2

A variable whose position moves between registers according to the current value of the program counter.

Depending on the type of location description, DW_AT_location is encoded in three different ways. Exprloc encodes a simple and composite location description. They consist of a byte length followed by an DWARF expression or location description. A list of encoded locations for loclist and loclistptr, which provide indexes or offsets in the .debug _ loclists section, which describes the actual list of locations.

DWARF expression

Use the DWARF expression to calculate the actual location of the variable. This includes a series of operations that manipulate stack values. There are many DWARF operations available, so I won't explain them in detail. Instead, I will give you some examples from each expression to give you something available. Also, don't be afraid of this; libelfin will handle all these complexities for us.

Literal coding

Push unsigned values onto the stack

Push the address Operand onto the stack

Press the literal amount into the stack

DW_OP_lit0, DW_OP_lit1. DW_OP_lit31

DW_OP_addr

DW_OP_constu

Register value

Press the contents of a given register with a given offset into the stack

Press the value found at the stack frame base address to offset the given value

DW_OP_fbreg

DW_OP_breg0, DW_OP_breg1. DW_OP_breg31

Stack operation

Treat the top of the stack as a memory address and replace it with the contents of that address

Copy the value at the top of the stack

DW_OP_dup

DW_OP_deref

Arithmetic and logical operation

Same as DW_OP_and, but with values added

Pop up the two values at the top of the stack and press back into their logical AND

DW_OP_and

DW_OP_plus

Control flow operation

Conditional branch: if the top of the stack is not 0, skip backwards or backwards in the expression through offset

Pop up the first two values, compare them, and press 1 if the condition is true, otherwise 0

DW_OP_le, DW_OP_eq, DW_OP_gt, etc.

DW_OP_bra

Input conversion

Converts the value at the top of the stack to a different type, which is described by the DWARF information entry for a given offset

DW_OP_convert

Special operation

Do nothing!

DW_OP_nop

DWARF Typ

The representation of the DWARF type needs to be strong enough to provide useful variable representations for debugger users. Users often want to be able to debug at the application level rather than at the machine level, and they need to know what their variables are doing.

The DWARF type is encoded in DIE along with most other debugging information. They can have properties that indicate their name, encoding, size, bytes, and so on. Countless type tags can be used to represent pointers, arrays, structures, typedef, and anything else you can see in a C or C++ program.

Take this simple structure as an example:

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

The parent DIE of this structure looks like this:

< 1>

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

What it says above is that we have a structure called test, the size of which is 0xb8, declared on line 1 of test.cpp. Then there are many child DIE that describe the member.

< 2>

DW_TAG_member DW_AT_name "I" DW_AT_type DW_AT_decl_file 0x00000001 test.cpp DW_AT_decl_line 0x00000002 DW_AT_data_member_location 0

< 2>

DW_TAG_member DW_AT_name "j" DW_AT_type DW_AT_decl_file 0x00000001 test.cpp DW_AT_decl_line 0x00000003 DW_AT_data_member_location 4

< 2>

DW_TAG_member DW_AT_name "k" DW_AT_type DW_AT_decl_file 0x00000001 test.cpp DW_AT_decl_line 0x00000004 DW_AT_data_member_location 8

< 2>

DW_TAG_member DW_AT_name "next" DW_AT_type DW_AT_decl_file 0x00000001 test.cpp DW_AT_decl_line 0x00000005 DW_AT_data_member_location 176 (as signed =-80)

Each member has a name, a type (which is an DIE offset), a declaration file and line, and a byte offset that points to the structure in which its member is located. Its type points to the following.

< 1>

DW_TAG_base_type DW_AT_name "int" DW_AT_encoding DW_ATE_signed DW_AT_byte_size 0x00000004

< 1>

DW_TAG_base_type DW_AT_name "float" DW_AT_encoding DW_ATE_float DW_AT_byte_size 0x00000004

< 1>

DW_TAG_array_type DW_AT_type

< 2>

DW_TAG_subrange_type DW_AT_type DW_AT_count 0x0000002a

< 1>

DW_TAG_base_type DW_AT_name "sizetype" DW_AT_byte_size 0x00000008 DW_AT_encoding DW_ATE_unsigned

< 1>

DW_TAG_pointer_type DW_AT_type

As you can see, int on my laptop is a 4-byte signed integer type, and float is a 4-byte floating-point number. The integer array type points to the int type as its element type, and sizetype (which can be thought of as size_t) as the index type, which has 2a elements. The test * type is DW_TAG_pointer_type, which refers to test DIE.

Implement a simple variable reader

As mentioned above, libelfin will handle most of the complexity for us. However, it does not implement all the methods for representing variable locations, and it will be very complicated to deal with them in our code. Therefore, I now choose to support only exprloc. Please add support for more types of expressions as needed. If you really have the courage, please submit a patch to libelfin to help complete the necessary support!

Processing variables is mainly to locate different parts in memory or registers, reading or writing the same as before. For simplicity, I will only show you how to implement reading.

First we need to tell libelfin how to read the registers from our process. We create a class that inherits from expr_context and use ptrace to handle everything:

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 masks id;}

Reading will be handled by the read_variables function in our debugger class:

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

The only thing we do above is to find the function we are currently entering, and then we need to iterate through the entries in the function to find variables:

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

We get the location information by looking for the DW_AT_location entry in DIE:

Auto loc_val = De [DW _ AT::location]

Then we make sure it is an exprloc and ask libelfin to evaluate our expression:

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

Now that we have evaluated the expression, we need to read the contents of the variable. It can be in memory or in registers, so we will deal with these two cases:

Switch (result.location_type) {case expr_result::type::address: {auto value = read_memory (result.value); std::cout

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report