Một chút hiểu thêm về "Hello World" trong C.

Gần đây, gặp một số vấn đề về Loader-Linker giữa môi trường build và môi trường chạy trong Cross-Compiling.

Có thể bất kì chương nào trong Linux cũng vậy. Nhưng chỉ xét một chường trình được build bằng C thì
một chương trình sẽ được chạy 2 cách dưới đây

1. Chương trình hoàn toàn là tĩnh

  • Không có symbol nào cần phải được (resolved) trước khi chạy.
  • Không yêu cầu bất cứ một thư viện run-time nào.
  • Kernel cứ thế load vào, rồi nhảy đến vị trí của Program Entry là chạy.
  • Có kích thước lớn
  • Để build được nó thì các thư viện phụ thuộc khi build cũng phải có phiên bản static.
    Lấy ví dụ HelloWorld
#include  
int main(int argc, char *args[])  
{  
printf("Hello, Ajinomoto \n");  
return 0;  
}  

Khi build tĩnh bằng (hầu như phải có -static)

gcc -o helloworld_static helloworld.c -static  

Kích thước file chạy sẽ như sau:

$ ls -liah helloworld_static  
2910638 -rwxrwxr-x 1 duser duser 717K Dec 9 13:37 helloworld_static  

717KB cho HelloWorld trên Ubuntu 14.04

Kiểm tra bằng objdump:

$ objdump -p helloworld_static

helloworld_static: file format elf32-i386

Program Header:  
LOAD off 0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12  
filesz 0x000a0c77 memsz 0x000a0c77 flags r-x  
LOAD off 0x000a0f40 vaddr 0x080e9f40 paddr 0x080e9f40 align 2**12  
filesz 0x00001040 memsz 0x000023c4 flags rw-  
NOTE off 0x000000f4 vaddr 0x080480f4 paddr 0x080480f4 align 2**2  
filesz 0x00000044 memsz 0x00000044 flags r--  
TLS off 0x000a0f40 vaddr 0x080e9f40 paddr 0x080e9f40 align 2**2  
filesz 0x00000010 memsz 0x00000028 flags r--  
STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4  
filesz 0x00000000 memsz 0x00000000 flags rw-  
RELRO off 0x000a0f40 vaddr 0x080e9f40 paddr 0x080e9f40 align 2**0  
filesz 0x000000c0 memsz 0x000000c0 flags r--  

Nhìn có vẻ khó hiểu, như chỉ để ý thấy chỉ có phần Program Header mà không còn phần nào khác.

2. Chương trình là động

  • Chứa symbol cần được phân giải trước khi chạy.
  • Hầu như các command trên Linux đều là dạng này.
  • Loader-Linker (có đường dẫn tuyệt đối) được ghi bên trong file chạy
    tức là nếu yêu cầu loader-linker /lib/ld-linux.so.2 mà môi trường chạy không có thì sẽ có lỗi rất lạ kiểu Không tìm thấy file, hoặc định dạng lỗi, etc.
  • Tên của thư viện phụ thuộc (không có đường dẫn) được ghi bên trong file chạy.
  • Kích thước file chạy nhỏ.

Tiếp tục thử với Hello World

gcc -o helloworld helloworld.c  

Kiểm tra kích thước file chạy:

$ ls -liah helloworld_dynamic  
2910636 -rwxrwxr-x 1 duser duser 7.2K Dec 9 13:37 helloworld_dynamic  

7.2K cho HelloWorld

Kiểm tra bằng objdump:

$ objdump -p helloworld_dynamic

helloworld_dynamic: file format elf32-i386

Program Header:  
PHDR off 0x00000034 vaddr 0x08048034 paddr 0x08048034 align 2**2  
filesz 0x00000120 memsz 0x00000120 flags r-x  
INTERP off 0x00000154 vaddr 0x08048154 paddr 0x08048154 align 2**0  
filesz 0x00000013 memsz 0x00000013 flags r--  
LOAD off 0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12  
filesz 0x000005c0 memsz 0x000005c0 flags r-x  
LOAD off 0x00000f08 vaddr 0x08049f08 paddr 0x08049f08 align 2**12  
filesz 0x00000118 memsz 0x0000011c flags rw-  
DYNAMIC off 0x00000f14 vaddr 0x08049f14 paddr 0x08049f14 align 2**2  
filesz 0x000000e8 memsz 0x000000e8 flags rw-  
NOTE off 0x00000168 vaddr 0x08048168 paddr 0x08048168 align 2**2  
filesz 0x00000044 memsz 0x00000044 flags r--  
EH_FRAME off 0x000004e4 vaddr 0x080484e4 paddr 0x080484e4 align 2**2  
filesz 0x0000002c memsz 0x0000002c flags r--  
STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4  
filesz 0x00000000 memsz 0x00000000 flags rw-  
RELRO off 0x00000f08 vaddr 0x08049f08 paddr 0x08049f08 align 2**0  
filesz 0x000000f8 memsz 0x000000f8 flags r--

Dynamic Section:  
NEEDED libc.so.6  
INIT 0x080482b0  
FINI 0x080484b4  
INIT_ARRAY 0x08049f08  
INIT_ARRAYSZ 0x00000004  
FINI_ARRAY 0x08049f0c  
FINI_ARRAYSZ 0x00000004  
GNU_HASH 0x080481ac  
STRTAB 0x0804821c  
SYMTAB 0x080481cc  
STRSZ 0x0000004a  
SYMENT 0x00000010  
DEBUG 0x00000000  
PLTGOT 0x0804a000  
PLTRELSZ 0x00000018  
PLTREL 0x00000011  
JMPREL 0x08048298  
REL 0x08048290  
RELSZ 0x00000008  
RELENT 0x00000008  
VERNEED 0x08048270  
VERNEEDNUM 0x00000001  
VERSYM 0x08048266

Version References:  
required from libc.so.6:  
0x0d696910 0x00 02 GLIBC_2.0  

3. Hiển thị thông về loader-linker

Khi chương trình động được chạy, một loader-linker sẽ được kernel gọi lên để load và link các thư viện động.
Gọi là loader-linker.
Thường sẽ có nằm ở /lib/ld.ABC.so hoặc tương tự như thế.
Loader-linker này sẽ tìm các thư viện được yêu cầu từ Search Path, sau đó link lại để tạo ra chương trình chạy.
Search Path này được cấu thành từ nhiều loại như: từ các path được đặt bên trong /etc/ld.conf.so, từ biến LD_LIBRARY_PATH, etc. Xem thêm tại: ld.so

Một cách để xem các thông tin debug của loader-linker này:
Giả sử với HelloWorld ở trên:

$ LD_DEBUG=help ./helloworld_dynamic  
Valid options for the LD_DEBUG environment variable are:

libs display library search paths  
reloc display relocation processing  
files display progress for input file  
symbols display symbol table processing  
bindings display information about symbol binding  
versions display version dependencies  
scopes display scope information  
all all previous options combined  
statistics display relocation statistics  
unused determined unused DSOs  
help display this help message and exit

To direct the debugging output into a file instead of standard output  
a filename can be specified using the LD_DEBUG_OUTPUT environment variable.  

Trên là Help, ta thử với libs sẽ có kết quả như sau:

$ LD_DEBUG=libs ./helloworld_dynamic  
7066: find library=libc.so.6 [0]; searching  
7066: search cache=/etc/ld.so.cache  
7066: trying file=/lib/i386-linux-gnu/libc.so.6  
7066:  
7066:  
7066: calling init: /lib/i386-linux-gnu/libc.so.6  
7066:  
7066:  
7066: initialize program: ./helloworld_dynamic  
7066:  
7066:  
7066: transferring control: ./helloworld_dynamic  
7066:  
Hello, Ajinomoto  
7066:  
7066: calling fini: ./helloworld_dynamic [0]  
7066:  

doime 09-12-2016