簡體   English   中英

編譯后的“ Hello World” C程序如何使用機器語言存儲字符串?

[英]How does a compiled “Hello World” C program store the String using machine language?

所以我今天開始學習機器語言。 我用C語言編寫了一個基本的“ Hello World”程序,其中顯示了“ Hello,world!”。 使用for循環十次。 然后,我使用Gnu Debugger來反匯編main並以機器語言查看代碼(我的計算機具有x86處理器,並且我將gdb設置為使用intel語法):

user@PC:~/Path/To/Code$ gdb -q ./a.out
Reading symbols from ./a.out...done.
(gdb) list
1      #include <stdio.h>
2
3      int main()
4      {
5           int i;
6           for(i = 0; i < 10; i++) {
7                printf("Hello, world!\n");
8           }
9           return 0;
10      } 
(gdb) disassemble main
Dump of assembler code for function main:
  0x0804841d <+0>:     push    ebp
  0x0804841e <+1>:     mov     ebp,esp
  0x08048420 <+3>:     and     esp,0xfffffff0
  0x08048423 <+6>:     sub     esp,0x20
  0x08048426 <+9>:     mov     DWORD PTR [esp+0x1c],0x0
  0x0804842e <+17>:    jmp     0x8048441 <main+36>
  0x08048430 <+19>:    mov     DWORD PTR [esp],0x80484e0
  0x08048437 <+26>:    call    0x80482f0 <puts@plt>
  0x0804843c <+31>:    add     DWORD PTR [esp+0x1c],0x1
  0x08048441 <+36>:    cmp     DWORD PTR [esp+0x1c],0x9
  0x08048446 <+41>:    jle     0x8048430 <main+19>
  0x08048448 <+43>:    mov     eax,0x0
  0x0804844d <+48>:    leave
  0x0804844e <+49>:    ret
End of assembler dump.
(gdb) x/s 0x80484e0
0x80484e0: "Hello, world!"

我了解大多數機器代碼以及每個命令的作用。 如果我正確理解的話,地址“ 0x80484e0”將被加載到esp寄存器中,以便可以使用該地址的內存。 我檢查了地址,毫不奇怪的是它包含了所需的字符串。 現在我的問題是-字符串最初是如何到達那里的? 我在程序中找不到在該位置設置字符串的部分。

我也看不懂其他東西:當我第一次啟動程序時,eip指向,其中變量i初始化為[esp + 0x1c]。 但是,esp指向的地址稍后會在程序中更改(更改為0x80484e0),但更改后[esp + 0x1c]仍用於“ i”。 當地址esp指向更改時,地址[esp + 0x1c]不應該更改嗎?

二進制或程序由機器代碼和數據組成。 在這種情況下,您在源代碼中放入的字符串,編譯器也將這些只是字節的數據,並且由於使用方式而被視為只讀數據,因此取決於編譯器可能位於.rodata或.text中或編譯器可能使用的其他名稱。 Gcc可能將其稱為.rodata。 程序本身是.text。 鏈接器隨即出現,並且在鏈接事物時會找到.text,.data,.bss,.rodata和您可能擁有的任何其他項目的位置,然后將點連接起來。 在調用printf的情況下,鏈接器知道將字符串,字節數組放置在何處,並被告知其名稱(毫無疑問是一些內部臨時名稱),並告訴printf調用該名稱,以便鏈接器在調用printf之前修補指令以獲取地址到格式字符串。

Disassembly of section .text:

0000000000400430 <main>:
  400430:   53                      push   %rbx
  400431:   bb 0a 00 00 00          mov    $0xa,%ebx
  400436:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40043d:   00 00 00 
  400440:   bf e4 05 40 00          mov    $0x4005e4,%edi
  400445:   e8 b6 ff ff ff          callq  400400 <puts@plt>
  40044a:   83 eb 01                sub    $0x1,%ebx
  40044d:   75 f1                   jne    400440 <main+0x10>
  40044f:   31 c0                   xor    %eax,%eax
  400451:   5b                      pop    %rbx
  400452:   c3                      retq   
  400453:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40045a:   00 00 00 
  40045d:   0f 1f 00                nopl   (%rax)



Disassembly of section .rodata:

00000000004005e0 <_IO_stdin_used>:
  4005e0:   01 00                   add    %eax,(%rax)
  4005e2:   02 00                   add    (%rax),%al
  4005e4:   48                      rex.W
  4005e5:   65 6c                   gs insb (%dx),%es:(%rdi)
  4005e7:   6c                      insb   (%dx),%es:(%rdi)
  4005e8:   6f                      outsl  %ds:(%rsi),(%dx)
  4005e9:   2c 20                   sub    $0x20,%al
  4005eb:   77 6f                   ja     40065c <__GNU_EH_FRAME_HDR+0x68>
  4005ed:   72 6c                   jb     40065b <__GNU_EH_FRAME_HDR+0x67>
  4005ef:   64 21 00                and    %eax,%fs:(%rax)

編譯器將對該指令進行編碼,但可能會將地址保留為零或某些填充

  400440:   bf e4 05 40 00          mov    $0x4005e4,%edi

以便鏈接器稍后可以填充它。 gnu反匯編程序試圖反匯編沒有意義的.rodata(和.data等)塊,因此請忽略它試圖解釋以地址0x4005e4開始的字符串的指令。

鏈接對象的反匯編之前,顯示了兩個部分.text和.rodata

Disassembly of section .text.startup:

0000000000000000 <main>:
   0:   53                      push   %rbx
   1:   bb 0a 00 00 00          mov    $0xa,%ebx
   6:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
   d:   00 00 00 
  10:   bf 00 00 00 00          mov    $0x0,%edi
  15:   e8 00 00 00 00          callq  1a <main+0x1a>
  1a:   83 eb 01                sub    $0x1,%ebx
  1d:   75 f1                   jne    10 <main+0x10>
  1f:   31 c0                   xor    %eax,%eax
  21:   5b                      pop    %rbx
  22:   c3                      retq   

0000000000000000 <.rodata.str1.1>:
   0:   48                      rex.W
   1:   65 6c                   gs insb (%dx),%es:(%rdi)
   3:   6c                      insb   (%dx),%es:(%rdi)
   4:   6f                      outsl  %ds:(%rsi),(%dx)
   5:   2c 20                   sub    $0x20,%al
   7:   77 6f                   ja     78 <main+0x78>
   9:   72 6c                   jb     77 <main+0x77>
   b:   64 21 00                and    %eax,%fs:(%rax)

取消鏈接,它只需填充此地址/偏移量,以便鏈接器稍后填充。

  10:   bf 00 00 00 00          mov    $0x0,%edi

還請注意,該對象僅包含.rodata中的字符串。 與庫和其他項目鏈接以使其成為一個完整的程序顯然會添加更多.rodata,但鏈接程序將對所有這些進行管理。

也許更容易看這個例子

void more_fun ( unsigned int, unsigned int, unsigned int );

unsigned int a;
unsigned int b=5;
const unsigned int c=7;

void fun ( void )
{
    more_fun(a,b,c);
}

作為對象分解

Disassembly of section .text:

0000000000000000 <fun>:
   0:   8b 35 00 00 00 00       mov    0x0(%rip),%esi        # 6 <fun+0x6>
   6:   8b 3d 00 00 00 00       mov    0x0(%rip),%edi        # c <fun+0xc>
   c:   ba 07 00 00 00          mov    $0x7,%edx
  11:   e9 00 00 00 00          jmpq   16 <fun+0x16>

Disassembly of section .data:

0000000000000000 <b>:
   0:   05                      .byte 0x5
   1:   00 00                   add    %al,(%rax)
    ...

Disassembly of section .rodata:

0000000000000000 <c>:
   0:   07                      (bad)  
   1:   00 00                   add    %al,(%rax)
    ...

出於任何原因,您都必須將其鏈接以查看.bss部分。 該示例的要點是該函數的機器代碼位於.text中,未初始化的全局變量位於.bss中,已初始化的全局變量為.data,而const初始化的全局變量為.rodata。 編譯器足夠聰明,可以知道即使全局常量也不會更改const,因此它可以將該值硬編碼到數學中,而無需從ram讀取,但是它必須從ram讀取的其他兩個變量因此生成指令鏈接器在鏈接時填充地址零。

在您的情況下,您的只讀/常量數據是字節的集合,並且不是數學運算,因此將源文件中定義的字節放置在內存中,因此可以將它們作為printf的第一個參數指向。

二進制文件不僅僅是機器代碼。 而且,編譯器和鏈接器可以將內容放置在內存中,以便機器代碼獲得,機器代碼本身不必編寫將由其余機器代碼使用的每個值。

編譯器將字符串“硬連接”到目標代碼中,然后鏈接程序將其“硬連接”到機器代碼中。

並不是說字符串是嵌入到代碼中的,也不是存儲在數據區域中的,這意味着如果您使用了指向該字符串的指針並嘗試對其進行更改,則會出現異常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM