2009-03-04 25 views
41

Nếu tôi hiểu chính xác, phần .bss trong các tệp ELF được sử dụng để phân bổ không gian cho các biến không được khởi tạo. Chuỗi công cụ của chúng tôi tạo ra các tệp ELF, do đó câu hỏi của tôi: phần .bss có thực sự chứa tất cả các số không? Nó có vẻ như một sự lãng phí khủng khiếp của không gian mà khi, nói, tôi phân bổ một mảng mười megabyte toàn cầu, nó kết quả trong mười megabyte của zero trong tập tin ELF. Tôi thấy gì ở đây?Do .bss phần zero khởi tạo biến chiếm không gian trong tập tin elf?

+0

cách nhanh để trả lời nó: làm cho một thế giới hello với một 'int là [1000000] 'và khác mà không có, biên dịch và xem các kích thước biên soạn :-) Sau đó, để thực sự hiểu, dịch ngược nó với binutils, hoặc biên dịch thành mã assembly với '-S'. –

Trả lời

63

Đã một thời gian kể từ khi tôi làm việc với ELF. Nhưng tôi nghĩ rằng tôi vẫn còn nhớ công cụ này. Không, nó không thể chất chứa những số không. Nếu bạn nhìn vào tiêu đề chương trình tập tin ELF, thì bạn sẽ thấy mỗi tiêu đề có hai số: Một là kích thước trong tệp. Và khác là kích thước như phần đã khi phân bổ trong bộ nhớ ảo (readelf -l ./a.out):

Program Headers: 
    Type   Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align 
    PHDR   0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4 
    INTERP   0x000114 0x08048114 0x08048114 0x00013 0x00013 R 0x1 
     [Requesting program interpreter: /lib/ld-linux.so.2] 
    LOAD   0x000000 0x08048000 0x08048000 0x00454 0x00454 R E 0x1000 
    LOAD   0x000454 0x08049454 0x08049454 0x00104 0x61bac RW 0x1000 
    DYNAMIC  0x000468 0x08049468 0x08049468 0x000d0 0x000d0 RW 0x4 
    NOTE   0x000128 0x08048128 0x08048128 0x00020 0x00020 R 0x4 
    GNU_STACK  0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4 

Headers loại LOAD là một trong đó sẽ được sao chép vào bộ nhớ ảo khi tập tin được nạp để thực hiện. Các tiêu đề khác chứa thông tin khác, như các thư viện được chia sẻ cần thiết. Như bạn thấy, các FileSizeMemSiz khác biệt đáng kể cho tiêu đề chứa bss phần (thứ hai LOAD một):

0x00104 (file-size) 0x61bac (mem-size) 

Đối với mã ví dụ này:

int a[100000]; 
int main() { } 

Các đặc điểm kỹ thuật ELF nói rằng một phần của phân đoạn mà kích thước mem lớn hơn kích thước tệp chỉ được điền bằng số không trong bộ nhớ ảo. Phân đoạn để ánh xạ phần của tiêu đề thứ hai LOAD giống như sau:

03  .ctors .dtors .jcr .dynamic .got .got.plt .data .bss 

Vì vậy, cũng có một số phần khác trong đó. Đối với constructor/destructor C++. Điều tương tự đối với Java. Sau đó, nó chứa một bản sao của phần .dynamic và các nội dung khác hữu ích cho liên kết động (tôi tin rằng đây là nơi chứa các thư viện được chia sẻ cần thiết trong số các nội dung khác). Sau đó, phần .data chứa các hình cầu được tạo ban đầu và biến tĩnh cục bộ. Cuối cùng, phần .bss xuất hiện, được lấp đầy bởi số không tại thời điểm tải vì kích thước tệp không bao gồm nó.

Nhân tiện, bạn có thể thấy phần đầu ra nào sẽ đặt một biểu tượng cụ thể bằng cách sử dụng tùy chọn liên kết -M. Đối với gcc, bạn sử dụng -Wl,-M để đặt tùy chọn thông qua trình liên kết. Ví dụ trên cho thấy rằng a được phân bổ trong phạm vi .bss. Nó có thể giúp bạn xác minh rằng đối tượng chưa được khởi tạo của bạn thực sự kết thúc trong .bss và không ở một nơi khác:

.bss   0x08049560 0x61aa0 
[many input .o files...] 
*(COMMON) 
*fill*   0x08049568  0x18 00 
COMMON   0x08049580 0x61a80 /tmp/cc2GT6nS.o 
       0x08049580    a 
       0x080ab000    . = ALIGN ((. != 0x0)?0x4:0x1) 
       0x080ab000    . = ALIGN (0x4) 
       0x080ab000    . = ALIGN (0x4) 
       0x080ab000    _end = . 

GCC giữ globals uninitialized trong một phần CHUNG theo mặc định, để tương thích với các trình biên dịch cũ, cho phép đã globals định nghĩa hai lần trong một chương trình không có nhiều lỗi định nghĩa. Sử dụng -fno-common để làm cho GCC sử dụng các phần .bss cho các tệp đối tượng (không tạo sự khác biệt cho tệp thực thi được liên kết cuối cùng), vì bạn thấy nó sẽ đi vào phần đầu ra .bss anyway.Điều này được điều khiển bởi mã liên kết .Hiển thị nó với ld -verbose). Nhưng điều đó không làm bạn sợ, nó chỉ là một chi tiết bên trong. Xem manpage của gcc.

+0

+1. Làm tốt. Đẹp và kỹ lưỡng. – Eddie

+0

Tôi cho rằng loại phần NOBITS phải được đặt để cho phép điều này? –

+0

Wouter. hmm tôi không bao giờ sử dụng lá cờ đó. tệp tiêu đề máy của tôi cho gcc trông giống như #define BSS_SECTION_ASM_OP "\ t.section \ t.bss, \" aw \ "" –

2

Phần .bss không được lưu trữ trong tệp thi hành. Trong số các mục phổ biến nhất (.text, .data, .bss), chỉ .text (mã thực tế) và .data (dữ liệu khởi tạo) có trong tệp ELF.

+1

Đó không phải là những gì đọc trên một tập tin thực thi tùy ý cho tôi biết. Có một phần tải lại các phần trong tệp, bao gồm phần .bss. –

+0

Nó không phụ thuộc vào chính ELF mà trên chuỗi biên dịch của bạn (ngôn ngữ, công cụ, tùy chọn như gỡ rối, ...). Bạn cũng có thể có các phần tùy chỉnh của riêng bạn. – mouviciel

+0

Phần '.bss' được lưu trữ trong tệp thi hành cho ít nhất ELF. Nhưng nội dung của nó không được lưu trữ, do đó kích thước của '.bss' trong tệp là một hằng số nhỏ. Trên các hệ điều hành có bảo vệ bộ nhớ, phần '.ss' cần phải được lưu trữ theo cách nào đó để bộ nạp có thể sắp xếp bộ nhớ ghi tại vị trí đó. Tất nhiên nó sẽ được nghĩ rằng tất cả những gì còn lại của '.bss' trong một số định dạng là một đóng góp cho một lĩnh vực kích thước được phân bổ nhưng không sao chép. – textshell

20

Phần .bss trong tệp ELF được sử dụng cho dữ liệu tĩnh là không được khởi tạo theo chương trình nhưng đảm bảo được đặt thành 0 khi chạy. Đây là một ví dụ nhỏ sẽ giải thích sự khác biệt.

int main() { 
    static int bss_test1[100]; 
    static int bss_test2[100] = {0}; 
    return 0; 
} 

Trong trường hợp này bss_test1 được đặt vào .bss vì nó chưa được khởi tạo. Tuy nhiên, bss_test2 được đặt vào phân đoạn .data cùng với một loạt số không. Trình tải thời gian cơ bản phân bổ số lượng không gian dành riêng cho .bss và xóa nó trước khi bất kỳ mã người dùng nào bắt đầu thực thi.

Bạn có thể thấy sự khác biệt bằng objdump, nm, hay các tiện ích tương tự:

moozletoots$ objdump -t a.out | grep bss_test 
08049780 l  O .bss 00000190    bss_test1.3 
080494c0 l  O .data 00000190    bss_test2.4 

này thường là một trong những đầu tiên ngạc nhiên các nhà phát triển nhúng chạy vào ... không bao giờ khởi tĩnh để không rõ ràng. Trình tải thời gian chạy (thường) sẽ xử lý điều đó. Ngay sau khi bạn khởi tạo bất kỳ điều gì một cách rõ ràng, bạn đang yêu cầu trình biên dịch/trình liên kết bao gồm dữ liệu trong hình ảnh thực thi.

+0

trên gcc nền tảng của tôi đặt bss_test2 vào phần .bss. bạn có thể đã đề cập đến tùy chọn biên dịch -fno-zero-initialized-in-bss để điều khiển điều này. – tristan

+0

Từ hướng dẫn sử dụng: "Nếu mục tiêu hỗ trợ phần BSS, GCC theo mặc định sẽ đặt các biến được khởi tạo thành 0 vào BSS". – OrangeDog

1

Đó là chính xác, .bss không hiện diện trên thực tế trong tệp, thay vì chỉ thông tin về kích thước của nó có mặt cho trình tải động để phân bổ phần .bss cho chương trình ứng dụng. Khi quy tắc ngón tay cái chỉ LOAD, TLS Segment lấy bộ nhớ cho chương trình ứng dụng, phần còn lại được sử dụng cho trình tải động.

Về tập tin thực thi tĩnh, bss phần cũng được đưa ra không gian trong việc áp dụng

Embedded execuatble nơi không có bộ nạp này là phổ biến.

Suman

+0

bạn nói, TLS cũng được tải, như PT_LOAD? Tôi thấy rằng PT_TLS được bao gồm trong PT_LOAD – osgx

Các vấn đề liên quan