6.9. Tips and Tricks

6.9. Tips and Tricks

This next section covers some useful tips and tricks for more advanced debugging. The tips include more advanced ways to attach to a process at a particular point in time, methods to find the address of variables and functions, and a useful technique for viewing data structures fully formatted for an executable and/or library that was not built in debug mode (that is, with -g).

下一節將介紹一些有用的提示和技巧, 以進行更高級的調試。這些提示包括在特定時間點附加到進程的更高級方法、查找變量和函數地址的方法, 以及查看完全格式化爲未內置的可執行文件和/或沒有在調試模式(即使用 -g)的庫的數據結構的有用技術。

6.9.1. Attaching to a Process—Revisited

6.9.1.1. The pause() method

Sometimes you run into a situation where you need to get a process into GDB under very specific conditions. For example, you might be investigating a problem that disappears when you compile the program with -g. In this case, you won’t be able to set a breakpoint at a file name and line of code. Because there is no debug information, you might need/ want to add some special code into the program to set a manual type of breakpoint.

有時你遇到了一種情況, 你需要在非常特殊的條件下, 在 GDB 中得到一個進程。例如, 您可能正在調查與 -g 一起編譯程序時消失的問題。在這種情況下, 您將無法在文件名和代碼行上設置斷點。因爲沒有調試信息, 所以您可能需要/希望在程序中添加一些特殊代碼來設置手動類型的斷點。

There are three good ways to stop a program at a specific point and attach a debugger:

有三種好方法可以在特定點停止程序並附加調試器:

  1. Use the pause function.
  2. Use a for or while loop.
  3. Use the system() function to spawn an xterm.

The pause() function will cause the process to wait until it receives a signal (see the pause(2) man page for more information). Unfortunately, GDB occasionally has difficulty in properly handling situations like this, so a simple trick may be needed to ensure it works properly. Consider the following code:

pause () 函數將導致進程等待, 直到接收到信號 (有關詳細信息, 請參閱pause (2)幫助手冊)。不幸的是, GDB 偶爾會在正確處理這樣的情況時遇到困難, 因此可能需要一個簡單的技巧來確保它能正常工作。請考慮以下代碼:

#include <stdio.h>

#include <unistd.h>

 

int main( void )

{

   int foo = 1;

 

   printf( "my pid = %d\n", getpid() );

 

   pause();

 

   printf( "got signal, resuming!\n" );

 

   return( 0 );

}

 

Compiling and running this program causes it to display its pid then wait for a signal. If we attach GDB to this process and look at the stack, we will see the following:

編譯和運行此程序會導致它顯示其 pid, 然後等待信號。如果我們將 GDB 附加到這個過程中並查看堆棧, 我們將看到以下內容:

(gdb) where

#0  0x400d6f0b in pause () from /lib/i686/libc.so.6

#1  0x080483d4 in main () at mypause.c:11

(gdb)

 

With debuggers on other platforms such as dbx, you could simply type “next” to go to the next instruction, and the debugger would automatically jump to the next instruction for which debug symbols existed, which in this case is in the function main(). We can tell that main() has been compiled with debug symbols because the file and line number are displayed. If we give the next command to GDB, we will see the following:

使用其他平臺 (如 dbx) 上的調試器, 您可以簡單地鍵入 "next" 轉到下一條指令, 調試程序將自動跳轉到帶有調試符號的下一條指令, 在本例中是函數 main ()。因爲文件和行號顯示, 我們可以認爲main () 已使用調試符號編譯。如果我們給 GDB 下一個命令, 我們將看到以下內容:

(gdb) next

Single stepping until exit from function pause,

which has no line number information.

 

At this point, the GDB session will appear to be hung. In actuality, it is sitting in the pause() system call waiting for a symbol. If we use another shell to send the original process a SIGCONT, pause() will terminate, and the process will continue. Unless a breakpoint has been set at some point after the pause() call, the program will continue execution, possibly to completion.

此時, GDB 會話將顯示爲掛起。實際上, 它正坐在pause () 系統調用中等待信號。如果我們使用另一個 shell 發送原始進程 SIGCONT, pause () 將終止, 並且進程將繼續。除非在pause () 調用後某個時刻設置了斷點, 否則程序將繼續執行, 最終會完成。

That requires an additional shell and kill -CONT <pid> command, which is a little tedious. An easier way is to have GDB itself send the SIGCONT. Note that a breakpoint is also set at line 13 to stop execution after pause() terminates:

這需要額外的 shell 和 kill –CONT <pid>命令, 這有點繁瑣。更簡單的方法是讓 GDB 自己發送 SIGCONT。請注意, 還會在行13設置斷點, 以在pause () 終止後停止執行:

(gdb) where

#0  0x400d6f0b in pause () from /lib/i686/libc.so.6

#1  0x080483d4 in main () at mypause.c:11

(gdb) break 13

Breakpoint 1 at 0x80483d4: file mypause.c, line 13.

(gdb) signal SIGCONT

Continuing with signal SIGCONT.

 

Breakpoint 1, main () at mypause.c:13

13         printf( "got signal, resuming!\n" );

(gdb)

 

The debugging session can now continue exactly at the location desired.

調試會話現在可以在完全所需的位置繼續。

6.9.1.2. The “for” or “while” Loop Method

The “for” or “while” loop method is similar but does not require sending a signal.

"for" 或 "while" 循環方法類似, 但不需要發送信號。

#include <stdio.h>

#include <unistd.h>

 

int main( void )

{

  int foo = 1;

 

  printf( "my pid = %d\n", getpid() );

 

  while ( foo == 1 ) ;

 

  return( 0 );

}

 

penguin> g++ pause.C -o pause -g

penguin> gdb pause

 

In this case, the program will loop indefinitely unless the variable foo is changed to a different value than 1. You can use the “top” program to find this process, given it will consume a lot of CPU time and will be near the top of the display (see Appendix A for more information on top). Attaching to the process in another window, we will see that the process is stopped on this loop.

在這種情況下, 除非變量 foo 更改爲不同於1的值, 否則程序將無限期循環。您可以使用 "top" 程序來查找這個過程, 因爲它將消耗大量的 CPU 時間, 並且將接近顯示的頂部 (請參閱附錄 a 以獲得有關上面的更多信息)。附加到另一個窗口中的進程, 我們將看到該進程在此循環上停止。

penguin> gdb - 15205

... (unimportant output deleted) ...

10     while ( foo == 1 ) ;

(gdb) set foo=2

(gdb) step

12     printf( "got signal, resuming!\n" );

(gdb)

 

As soon as we set the value of foo to 2, the program can pop out of the loop and continue. We used step to move forward by one line to show that the program is indeed free of this loop.

一旦我們將 foo 的值設置爲 2, 程序就會跳出循環並繼續。我們使用step向前移動一行, 以表明該程序確實跳出這個循環。

6.9.1.3. The xterm method

This method and the pause method work well if the program uses the terminal (the command line display), but what if the program is a daemon (that is, it runs in the background and has no terminal) or has graphical user interface? In this case, you can still get the process ID of the stopped process by spawning an xterm as in the following example:

如果程序使用terminal (命令行顯示), 此方法和pause方法工作正常, 但如果程序是一個守護進程 (即, 它在後臺運行且沒有終端), 或者具有圖形用戶界面, 該怎麼辦?在這種情況下, 您仍然可以通過生成 xterm 來獲取停止進程的進程 ID, 如下面的示例所示:

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

 

int main( void )

{

  char buf[1024] ;

 sprintf( buf, "/usr/bin/xterm -e \"/bin/echo $$ ; error\"\n", getpid() );

 

  system( buf ) ;

 

  return( 0 );

}

 

The command being run will actually cause an error on purpose to ensure that the xterm doesn’t exit after printing the process ID. The error message in the newly created xterm will be xterm: Can’t execvp /bin/echo 15634 ; error: No such file or directory, where 15634 is the process ID (and will be different if you try this method). You can also use the -title switch to xterm to change the title of the xterm to match the process ID of the program that spawned the process.

正在運行的命令實際上會導致錯誤, 以確保在打印進程 ID 後 xterm 不會退出。新創建的 xterm 中的錯誤信息將在xterm中顯示: Can’t execvp /bin/echo 15634 ; error: No such file or directory , 其中15634是進程 ID (如果您嘗試此方法, 進程ID將會有所不同)。您還可以使用 title 開關 xterm 更改 xterm 的標題, 以匹配生成進程的ID。

Once you attach to the process ID with GDB, simply exit the xterm to regain control of the process. It will continue as normal from that point on.

在使用 GDB 連接到進程 ID 之後, 只需退出 xterm 即可重新獲得對該進程的控制權。它將從正常點上繼續 。

6.9.2. Finding the Address of Variables and Functions

Processes can have dozens of libraries loaded at any given time with hundreds or thousands of global and static variables each. With this many global variables in the address space at the same time, there is a chance that two or more variables will have the same name. Worse, there is a chance that the wrong variable will be used (the ELF chapter includes more detail on this) or that you will look at the wrong variable using GDB. Fortunately, GDB provides a quick and easy method to find all variables that match a pattern (a regular expression). The following example finds all variables that have the string “environ” in their name:

進程可以在任何給定的時間加載大量的庫, 每一個庫都有成百上千個全局和靜態變量。同時, 在地址空間中有許多全局變量, 有可能兩個或多個變量同名。更糟糕的是, 有可能會使用錯誤的變量 (ELF 章節包含更多細節), 或者您將使用 GDB 查看錯誤的變量。幸運的是, GDB 提供了一種快速簡便的方法來查找與模式匹配的所有變量 (正則表達式)。下面的示例查找在其名稱中具有字符串 "environ" 的所有變量:

(gdb) info variables environ

All variables matching regular expression ".*environ.*":

 

File sh.func.c:

Char **STR_environ;

char **environ;

 

File tw.init.c:

Char **STR_environ;

 

File tw.parse.c:

Char **STR_environ;

 

File sh.c:

Char **STR_environ;

char **environ;

Non-debugging symbols:

0x08096184 __environ@@GLIBC_2.0

0x08096184 environ@@GLIBC_2.0

0x401acb38 __environ

0x401acb38 _environ

0x401b1c34 last_environ

0x400129d8 __environ

0x400129d8 _environ

 

There are several variables that have this string (“environ”) and a few that have exactly the same name (“_environ”). The address of each variable is listed on the left for the variables that do not have debug information.

有幾個變量有這個字符串 ("environ") 和一些具有完全相同的名稱 ("_environ")。每個變量的地址都列在左側, 用於沒有調試信息的變量。

There can also be thousands of functions in the address space, including more than one function with the same name. Using a similar method to that just shown for variables, you can get GDB to display all of the functions that match a certain pattern. Here is an example where we find all of the functions that have the string “main” in them:

地址空間中還有數以千計的函數, 其中包含多個同名的函數。使用類似的方法來顯示變量, 可以讓 GDB 顯示與某種模式匹配的所有函數。下面是一個示例, 我們在其中找到了包含字符串 "main" 的所有函數:

Code View: Scroll / Show All

(gdb) info functions main

All functions matching regular expression ".*main.*":

 

File sh.c:

int main(int, char **);

 

Non-debugging symbols:

0x08049ca0 __libc_start_main

0x400b0400 __libc_start_main

0x400b05c0 __libc_main

0x400bce30 __bindtextdomain

0x400bce30 bindtextdomain

0x400bce60 __bind_textdomain_codeset

0x400bce60 bind_textdomain_codeset

0x400bdf10 _nl_find_domain

0x400be1e0 _nl_init_domain_conv

0x400be490 _nl_free_domain_conv

0x400be4e0 _nl_load_domain

0x400be900 _nl_unload_domain

0x400bf090 __textdomain

0x400bf090 textdomain

0x400fa4a0 _IO_switch_to_main_wget_area

0x40102410 _IO_switch_to_main_get_area

0x40107820 main_trim

0x4015de60 getdomainname

0x4015ded0 setdomainname

0x40170560 arg_trimdomain_list

0x401710a0 _res_hconf_trim_domain

0x40171130 _res_hconf_trim_domains

0x4017b4c0 nrl_domainname

0x40001990 dl_main

0x401c5dc0 xdr_domainname

0x401c6fd0 yp_get_default_domain

0x401cf670 nis_domain_of

0x401cf6b0 nis_domain_of_r

 

If you want to find a function or variable with a specific name, you can use the following regular expression ^<function or variable name>$. The carrot ^ means the beginning of the string, and the dollar sign $ means the end of the string. Here is an example of where we find all functions that have the exact name of main (Note: There is only one function with this name.):

如果要查找具有特定名稱的函數或變量, 可以使用下面的正則表達式 ^<function or="" variable="" name="">$。符號 ^ 表示字符串的開頭, 符號 $ 表示字符串的末尾。下面是一個示例, 其中我們找到了所有具有 main 名稱的函數 (注意: 只有一個具有此名稱的函數):

(gdb) info functions ^main$

All functions matching regular expression "^main$":

 

File sh.c:

int main(int, char **);

6.9.3. Viewing Structures in Executables without Debug Symbols

When debugging, particularly commercial applications, it’s very likely that the binaries have not been compiled with debug symbols (using the -g compile switch). When this happens, a knowledge of assembly, the machine’s calling conventions, and a rough idea of the data structures involved is required to fully understand what the program is doing when viewing it in a debugger. In the case of open source software, the code is readily available, so even if the executables were compiled without debug symbols, one can examine the source along with the assembly to see what is happening. In a commercial software situation where the software is having a problem on the customer’s computer, sending the source code to the customer is simply not an option for a support analyst.

在調試 (尤其是商業應用程序) 時, 很可能是二進制文件沒有用調試符號 (沒有使用 -g 編譯開關) 編譯。當發生這種情況時, 需要了解彙編、機器的調用約定以及所涉及的數據結構, 以便在調試器中查看它時, 能夠完全瞭解該程序正在執行的操作。在開源軟件的情況下, 代碼是現成的, 因此即使在沒有調試符號的情況下編譯可執行文件, 也可以與彙編一起檢查源代碼, 以查看正在發生的情況。在商業軟件的情況下, 軟件在客戶的計算機上有問題, 向客戶發送源代碼根本不是技術支持的選擇。

For cases like these and many others, analyzing structures in raw memory as hex is certainly possible but can be very painstaking and tedious. This section introduces a trick one can use on ELF-based systems to dump data as structures from within a debugger where no debugging symbols are readily available. To demonstrate this, a very small and simple example will be used. The source code for this example is made up of one header file and one source module. The header file’s name is employee.h and contains the following code:

對於像這些和其他許多人這樣的情況, 將原始內存中的結構作爲十六進制分析當然是可能的, 但非常費力和乏味。本節介紹一個可以在基於 ELF 的系統上使用的竅門, 將數據從沒有可用的調試符號的調試器中轉儲到結構。爲了演示這一點, 將使用一個非常小且簡單的示例。此示例的源代碼由一個頭文件和一個源模塊組成。頭文件的名稱爲employee.h, 包含以下代碼:

struct employee_rec

{

   int employee_no;

   int is_ceo;

   char first_name[20];

   char last_name[20];

   int manager_emp_no;

   int department_no;

};

 

The source module’s name is employee.c and contains this code:

源模塊的名稱爲employee.c 幷包含以下代碼:

#include "employee.h"

 

int foo( struct employee_rec *pEmp )

{

   pEmp->manager_emp_no = 10;

   return 0;

}

 

int main( void )

{

   struct employee_rec *pEmployee;

 

  pEmployee = (struct employee_rec*)malloc( sizeof( struct employee_rec

) );

 

   /* Set the record up */

   pEmployee->employee_no = 4416;

   pEmployee->is_ceo = 0;

   strcpy( pEmployee->first_name, "Dan" );

   strcpy( pEmployee->last_name, "Behman" );

   pEmployee->manager_emp_no = 3278;

   pEmployee->department_no = 321;

 

   foo( pEmployee );

 

   return c;

}

 

Let’s compile this source to create an executable called emp:

gcc -o emp employee.c

 

Now let’s assume that our newly created emp executable is a massive complex commercial application. Let’s also assume that there is a functional problem somewhere in this massive program, and we’ve determined that a key to determining the cause is something that is being done in the function, foo. Also assume that we don’t have access to all of the source code, but we do know the structure of each employee record. If we run emp under GDB, we’re limited to working with addresses in memory, translating sizes, and converting hex to more meaningful values because the program was not compiled with debug symbols (the -g compile switch) and the source code is not readily available.

現在讓我們假設我們新創建的 emp 可執行文件是一個龐大的複雜的商業應用程序。讓我們也假設在這個龐大的程序中有一個功能性問題, 我們已經確定了確定原因的關鍵是在函數foo中。也假設我們不能訪問所有的源代碼, 但我們知道每個員工記錄的結構。如果我們在 GDB 下運行 emp, 我們只限於處理內存中的地址、轉換大小和將十六進制轉換爲更有意義的值, 因爲程序不是用調試符號 (g 編譯開關) 編譯的, 而且源代碼不容易獲得。

Instead of struggling through assembly and raw memory, we can make use of the LD_PRELOAD environment variable, which Linux’s dynamic loader recognizes. When this environment variable is set to a valid library, the loader will load it before any other libraries get loaded. This gives us the opportunity to make our GDB session aware of any structure types we wish. To begin, create structure.c with the following code:

我們可以利用 Linux 的動態加載器識別的 LD_PRELOAD 環境變量, 而不是通過彙編和原始內存進行分析。當此環境變量設置爲有效的庫時, 加載程序將在加載任何其他庫之前加載它。這使我們有機會讓 GDB瞭解我們希望的結構類型。首先, 用以下代碼創建structure.c:

#include "employee.h"

 

struct employee_rec *floating_rec;

 

Compile this source file with the following command (-shared is used to create a shared library, and employee.c contains the simple source code from the preceding script):

使用以下命令編譯此源文件 (-共享用於創建共享庫, 而employee. c 包含前面腳本中的簡單源代碼):

gcc -shared -o libstructure.so -g employee.c

 

This will create a shared library called libstructure.so containing debug symbols. Next, we set the LD_PRELOAD environment variable to point to this file:

這將創建一個名爲 libstructure.so 包含調試符號的共享庫。接下來, 我們將 LD_PRELOAD 環境變量設置爲指向此文件:

export LD_PRELOAD=/home/dbehman/structure.so

 

Now when we re-run emp under a debugger, our employee_rec structure will be known, and we can point our floating_rec pointer to any point in memory where this structure begins and print it out. To demonstrate, first launch emp under GDB and set a breakpoint in the function foo:

現在, 當我們在調試器下重新運行 emp 時, 我們的 employee_rec 結構將被知道, 我們可以將我們的 floating_rec 指針指向內存中的任何點, 從這個結構開始並將其打印出來。爲了演示, 首先在 GDB 下啓動 emp, 並在函數 foo 中設置一個斷點:

penguin> gdb emp

GNU gdb 5.3.92

Copyright 2003 Free Software Foundation, Inc.

gdb is free software, covered by the GNU General Public License, and

you are welcome to change it and/or distribute copies of it under

certain conditions. Type "show copying" to see the conditions.

There is  absolutely no warranty for gdb.  Type "show  warranty" for

details.

This gdb was configured as "i586-suse-linux"...

(gdb) break foo

Breakpoint 1 at 0x804836f

(gdb)

 

Run emp, and when the breakpoint is hit, dump the assembly:

(gdb) run

Starting program: /home/dbehman/testing/emp

 

Breakpoint 1, 0x0804836f in foo ()

(gdb) disas

Dump of assembler code for function foo:

0x0804836c <foo+0>:     push   %ebp

0x0804836d <foo+1>:     mov    %esp,%ebp

0x0804836f <foo+3>:     mov    0x8(%ebp),%eax

0x08048372 <foo+6>:     movl   $0x1,0x4(%eax)

0x08048379 <foo+13>:    mov    $0x0,%eax

0x0804837e <foo+18>:    pop    %ebp

0x0804837f <foo+19>:    ret

End of assembler dump.

(gdb)

 

The first two instructions are standard, used to set up the stack for this function and comprise what is referred to as the function prologue (see Chapter 5 for more information). The third instruction copies an 8-byte value from the stack into the eax register. This is the address of the structure in memory that we want to look at. We can see by the message “Breakpoint 1, 0x0804836f in foo ()” from GDB above that the next instruction to be executed is the copying of our address into eax at 0x0804836f. Have the debugger execute this instruction and then examine the contents of eax.

前兩個指令是標準的, 用於設置此函數的棧, 幷包含稱爲函數的序言 (參見5章以瞭解更多信息)。第三個指令將8字節的值從棧複製到 eax 寄存器中。這是我們要查看的內存中結構的地址。我們可以看到, 從上面的消息 "breakpoint 1, 0x0804836f in foo ()", 將執行的下一個指令是複製我們的地址到在0x0804836f的 eax。讓調試器執行此指令, 然後檢查 eax 的內容。

(gdb) stepi

0x08048372 in foo ()

(gdb) print /x $eax

$1 = 0x8049690

(gdb)

 

Now that we know the address of our structure, we can examine the raw memory contents. By examining the structure in employee.h, we know that the size of our structure is 56 bytes. When examining memory, a count is specified, and because we want to see words of size four bytes each, we use a count of 14 (56 divided by 4).

現在我們知道了結構的地址, 我們可以檢查原始內存內容。通過檢查employee. h中的結構, 我們知道我們的結構的大小是56字節。在檢查內存時, 指定了一個計數, 因爲我們希望看到每個大小爲四字節的字, 我們使用的計數爲 14 (56 除以 4)。

(gdb) x /14wx $eax

0x8049690:     0x00001140     0x00000000     0x006e6144     0x00000000

0x80496a0:     0x00000000     0x00000000     0x00000000     0x6d686542

0x80496b0:     0x00006e61     0x00000000     0x00000000     0x00000000

0x80496c0:     0x00000cce      0x00000141

(gdb)

 

There’s our structure in raw memory form! It is possible to get the information we’re looking for out of this representation, but a lot of conversion is required to get the correct values. Endian-ness must also be considered on x86-based platforms (which this is), so another layer of conversion is required. See the section, “Understanding and Dealing with Endian-ness,” that follows for more information. All of the conversion involved introduces many chances for human error and being off by one digit can put an analyst down the wrong path and potentially cause a loss of hours in debugging time.

在原始的內存中有我們的結構形式!可以從這些數據中獲取我們要查找的信息, 但是需要進行大量的轉換才能獲得正確的值。在 x86 平臺上也必須考慮字節序, 因此需要另一層轉換。有關詳細信息, 請參閱 "瞭解和處理字節序" 一節。所涉及的所有轉換都引入了許多人爲錯誤的機會, 並且被一個數字的丟失會使分析人員走上錯誤的路徑, 並可能導致調試時間數小時的延長。

Fortunately, we’ve preloaded a library that contains debug symbols for the structure we want. All we have to do now is point our dummy structure pointer at this memory, and then we can print it out normally!

幸運的是, 我們已經預裝了一個庫, 其中包含了我們想要的結構的調試符號。我們現在要做的就是把我們的虛擬結構指針指向這個內存, 然後我們就可以正常打印出來了!

(gdb) set variable floating_rec = $eax

(gdb) print *floating_rec

$2 = {employee_no = 4416, is_ceo = 0,

  first_name = "Dan", '\0' <repeats 16 times>,

  last_name = "Behman", '\0' <repeats 13 times>,

  manager_emp_no = 3278, department_no = 321}

(gdb)

 

As you can see, this way it is much easier to see what the values are and what they refer to. Let’s now execute the next machine instruction and dump our structure again.

正如您所看到的, 這種方式更容易看到值, 以及它們所引用的內容。現在, 讓我們執行下一臺機器指令, 再轉儲我們的結構。

(gdb) stepi

0x08048379 in foo ()

(gdb) print *floating_rec

$3 = {employee_no = 4416, is_ceo = 1,

  first_name = "Dan", '\0' <repeats 16 times>,

  last_name = "Behman", '\0' <repeats 13 times>,

  manager_emp_no = 3278, department_no = 321}

(gdb)

 

We can easily see that the is_ceo field was changed from 0 to 1. We now know that the function foo() performs a very significant promotion of a regular employee to a Chief Executive Officer!

This example is scaled down, but it should show how valuable this technique can be, especially when working with large and complex data structures.

我們可以很容易地看到, is_ceo 字段從0改爲1。我們現在知道的函數 foo () 執行一個從普通員工到首席執行官的非常重要的晉升! 此示例已縮小, 但它應該顯示此技術的價值, 尤其是在處理大型複雜數據結構時。

6.9.4. Understanding and Dealing with Endian-ness

The term endian refers to how a particular architecture stores data in memory. Specifically, the order in which the bytes are stored is dictated by the endian-ness of a particular architecture.

術語 "endian" 是指特定體系結構如何將數據存儲在內存中。具體地說, 存儲字節的順序由特定體系結構的字節序決定。

When viewing raw memory in a debugger or a hex dump of some kind, it is very important to know whether the system is big or little endian. If the system is big endian, no conversion is needed, but if the system is little endian, you will have to convert the raw data into the human-preferred form (big endian) when reading it.

當在調試器或某種十六進制轉儲中查看原始內存時, 瞭解系統是大還是小的字節序是非常重要的。如果系統是大的字節序, 不需要轉換, 但如果系統是小的字節序, 您將必須轉換原始的數據轉換爲可讀的形式 (大字節序)。

Consider the following simple program:

int main()

{

  unsigned long long i64 = 0x0123456789abcdef ;

  char  *tmpPtr ;

 

  return 0 ;

}

 

This simple program sets the value of a 64-bit variable i64 to 0x0123456789abcdef. The program also includes a character pointer, tmpPtr, that will be used from within GDB to determine the order of the bytes for the 64-bit value as it is stored in memory. Let’s see the byte order for this 64-bit value from within GDB:

此簡單程序將64位變量 i64 的值設置爲0x0123456789abcdef。該程序還包括一個字符指針, tmpPtr, 將在 GDB 內確定64位值的字節順序, 當它存儲在內存中。讓我們從 GDB 內部查看這個64位值的字節順序:

Code View: Scroll / Show All

penguin> g++ endian.c -o endian -g

penguin> gdb endian

GNU gdb 5.2.1

Copyright 2002 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and

you are  welcome to change it  and/or distribute copies of  it under

certain conditions.

Type "show copying" to see the conditions.

There  is absolutely no warranty  for GDB. Type "show  warranty" for

details.

This GDB was configured as "i586-suse-linux"...

(gdb) break main

Breakpoint 1 at 0x804836c: file endian.c, line 4.

(gdb) run

Starting program: /home/wilding/src/Linuxbook/endian

 

Breakpoint 1, main () at endian.c:4

4     unsigned long long i64 = 0x0123456789abcdef ;

Current language: auto; currently c++

(gdb) next

7     return 0 ;

(gdb) set tmpPtr = &i64

(gdb) printf "%x\n", (unsigned char)tmpPtr[0]

ef

(gdb) printf "%x\n", (unsigned char)tmpPtr[1]

cd

(gdb) printf "%x\n", (unsigned char)tmpPtr[2]

ab

(gdb) printf "%x\n", (unsigned char)tmpPtr[3]

89

(gdb) printf "%x\n", (unsigned char)tmpPtr[4]

67

(gdb) printf "%x\n", (unsigned char)tmpPtr[5]

45

(gdb) printf "%x\n", (unsigned char)tmpPtr[6]

23

(gdb) printf "%x\n", (unsigned char)tmpPtr[7]

   1

 

In the GDB session, we set the character pointer to the address of the 64-bit variable with set tmpPtr = &i64. Because the pointer is a character pointer, we can use it as a character array to find each byte of the 64-bit value, i64. Each index into the character array (for example, tmpPtr[3]) shows another byte, in order, of the 64-bit value as it is stored in memory. From the GDB output here, the 64-bit value of 0x0123456789abcdef is actually stored as 0xefcdab8967452301 in memory. In other words, the byte order is completely reversed.

在 GDB 中, 我們用 set tmpPtr = &i64 將字符指針設置爲64位變量的地址。因爲指針是一個字符指針, 所以我們可以使用它作爲字符數組來查找64位值 i64 的每個字節。字符數組中的每個索引 (例如, tmpPtr [3]) 顯示了在內存中存儲的64位值的每一個字節 (按順序)。從 GDB 的輸出中, 0x0123456789abcdef 的64位值實際上存儲爲內存中的0xefcdab8967452301。換言之, 字節順序完全顛倒。

Note: Byte reversing only need be done when viewing data that is in words two or more bytes in size. Individual bytes are always displayed as expected.

注意: 僅當查看字大小中的兩個或更多字節的數據時, 才需要執行字節反轉。每個字節總是按預期顯示。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章