Ảnh hưởng của liên kết tĩnh và động đến địa chỉ bắt đầu


8

Tôi có một chương trình C đơn giản. Tôi chạy:

$ gcc Q1.c -Wall -save-temps -o Q1

Sau đó, tôi kiểm tra thực thi được tạo ra:

$  objdump -f Q1
Q1:     file format elf32-i386
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x080483b0

Sau đó, tôi biên dịch nó với liên kết tĩnh:

$ gcc Q1.c -Wall -save-temps -static -o Q1

và kiểm tra lại tập tin:

$ objdump -f Q1
Q1:     file format elf32-i386
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x08048e08

Liên kết tĩnh và động có ảnh hưởng gì đến địa chỉ bắt đầu của chương trình? Địa chỉ bắt đầu là địa chỉ của main(), phải không?

Câu trả lời:


10

Địa chỉ bắt đầu là địa chỉ của main(), phải không?

Không thực sự: Sự khởi đầu của một chương trình không thực sự main(). Theo mặc định, GCC sẽ tạo ra các tệp thực thi có địa chỉ bắt đầu tương ứng với _startký hiệu. Bạn có thể thấy điều đó bằng cách làm a objdump --disassemble Q1. Đây là đầu ra trên một chương trình đơn giản của tôi chỉ thực hiện return 0;trong main():

0000000000400e30 <_start>:
  400e30:       31 ed                   xor    %ebp,%ebp
  400e32:       49 89 d1                mov    %rdx,%r9
  400e35:       5e                      pop    %rsi
  400e36:       48 89 e2                mov    %rsp,%rdx
  400e39:       48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
  400e3d:       50                      push   %rax
  400e3e:       54                      push   %rsp
  400e3f:       49 c7 c0 a0 15 40 00    mov    $0x4015a0,%r8
  400e46:       48 c7 c1 10 15 40 00    mov    $0x401510,%rcx
  400e4d:       48 c7 c7 40 0f 40 00    mov    $0x400f40,%rdi
  400e54:       e8 f7 00 00 00          callq  400f50 <__libc_start_main>
  400e59:       f4                      hlt    
  400e5a:       66 90                   xchg   %ax,%ax
  400e5c:       0f 1f 40 00             nopl   0x0(%rax)

Như bạn có thể thấy tại địa chỉ 400e54, _start()lần lượt gọi __libc_start_main, trong đó khởi tạo những thứ cần thiết (pthreads, atexit, ...) và cuối cùng gọi main()với các đối số thích hợp (argc, argv và env).

Được rồi, nhưng nó phải làm gì với việc thay đổi địa chỉ?

Khi bạn yêu cầu gccliên kết tĩnh, điều đó có nghĩa là tất cả các khởi tạo mà tôi đã đề cập ở trên phải được thực hiện bằng cách sử dụng các hàm có trong tệp thực thi. Và thực sự nếu bạn nhìn vào kích thước của cả hai tệp thực thi, bạn sẽ thấy rằng phiên bản tĩnh lớn hơn nhiều. Trong thử nghiệm của tôi, phiên bản tĩnh là 800K trong khi phiên bản chia sẻ chỉ là 6K.

Các chức năng bổ sung xảy ra được đặt trước _start(), do đó thay đổi địa chỉ bắt đầu. Đây là cách bố trí của tệp thực thi tĩnh xung quanh start():

000000000049e960 r translit_from_tbl
0000000000400a76 t _i18n_number_rewrite
0000000000400bc0 t fini
0000000000400bd0 t init_cacheinfo
0000000000400e30 T _start
0000000000400e60 t deregister_tm_clones
0000000000400e90 t register_tm_clones
0000000000400ed0 t __do_global_dtors_aux

Và đây là cách bố trí của tệp thực thi được chia sẻ:

00000000004003c0 T _start
00000000004003f0 t deregister_tm_clones
00000000004004b0 T main
00000000004004c0 T __libc_csu_init
00000000006008a0 B _end
0000000000400370 T _init

Kết quả là, tôi nhận được các địa chỉ bắt đầu hơi khác nhau: 0x400e30 trong trường hợp tĩnh và 0x4003c0 trong trường hợp được chia sẻ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.