2011-12-01 18 views
11

Thực sự kỳ lạ gcc quirk. Check this out:Tại sao GCC trừ giá trị sai cho con trỏ ngăn xếp khi phân bổ một mảng lớn không có các cuộc gọi hàm tiếp theo?

main() { int a[100]; a[0]=1; } 

sản xuất lắp ráp này:

0: 55      push %rbp 
    1: 48 89 e5    mov %rsp,%rbp 
    4: 48 81 ec 18 01 00 00 sub $0x118,%rsp 
    b: c7 85 70 fe ff ff 01 movl $0x1,-0x190(%rbp) 
    12: 00 00 00 
    15: c9      leaveq 
    16: c3      retq 

Phía trên cùng của ngăn xếp rõ ràng là 400, vì một mảng 100 * 4 của nó. Vì vậy, khi nó ghi vào mục đầu tiên, nó làm rbp - 400 (dòng 'b'). Tốt. Nhưng tại sao nó trừ 280 từ con trỏ ngăn xếp (dòng '4')? Không phải là điểm đến giữa mảng?

Nếu chúng ta thêm một cuộc gọi chức năng sau đó, gcc làm điều đúng đắn:

b() {} 
main() { int a[100]; a[0]=1; b(); } 

sản xuất lắp ráp này:

0000000000000000 <b>: 
    0: 55      push %rbp 
    1: 48 89 e5    mov %rsp,%rbp 
    4: c9      leaveq 
    5: c3      retq 

0000000000000006 <main>: 
    6: 55      push %rbp 
    7: 48 89 e5    mov %rsp,%rbp 
    a: 48 81 ec 90 01 00 00 sub $0x190,%rsp 
    11: c7 85 70 fe ff ff 01 movl $0x1,-0x190(%rbp) 
    18: 00 00 00 
    1b: b8 00 00 00 00   mov $0x0,%eax 
    20: e8 00 00 00 00   callq 25 <main+0x1f> 
    25: c9      leaveq 
    26: c3      retq 

Ở đây, nó đúng cách trừ 400 (dòng 'a').

Tại sao thay đổi khi bạn thêm cuộc gọi chức năng? Là gcc chỉ lười biếng, và không làm điều đó đúng bởi vì nó không quan trọng? Chuyện gì vậy? Rõ ràng điều này chỉ xảy ra khi biên dịch cho x86_64, nhưng không phải cho đồng bằng x86. Điều này có một cái gì đó kỳ lạ để làm với "redzone" của x86_64? Điều gì đang xảy ra chính xác?

+0

Tại sao bạn lo lắng về mã không có bất kỳ ảnh hưởng nào, như là điều hiển nhiên từ mã !? Ngoài ra, ví dụ (thứ hai) của bạn nên sử dụng một số quy ước gọi điện thoại trong đó ngăn xếp được tham gia, bởi vì không có tham số đi qua tham gia (trên ngăn xếp) trong ví dụ của bạn. Side-lưu ý: Tôi ghét AT & T lắp ráp :) – 0xC0000022L

+3

Có lẽ vì sự tò mò? Hay đây không phải là lý do hợp lệ trong sách của bạn? BTW, tôi tìm thấy câu trả lời khai sáng – hirschhornsalz

Trả lời

13

Đoán của bạn là chính xác. Nó là một "vùng màu đỏ". Vùng màu đỏ là không gian từ rsp-128 đến rsp, có thể được sử dụng bởi một hàm cho các biến cục bộ và để lưu trữ tạm thời. Không gian này bị ảnh hưởng bởi các trình xử lý ngắt và ngoại lệ. Rõ ràng, vùng màu đỏ bị phá hủy bởi các cuộc gọi hàm, do đó, nếu bất kỳ hàm nào được gọi, thì không có biến cục bộ nào có thể nằm trong vùng màu đỏ.

Vùng màu đỏ chỉ có thể được sử dụng trong 64 bit Linux, BSD và Mac. Nó không có sẵn trong mã kernel.

Nó có thể được sử dụng để tối ưu hóa không gian, vì với vùng màu đỏ, bạn có thể tham chiếu tới 512 byte biến cục bộ với hướng dẫn ngắn, chỉ dựa trên rsp và ebp. Không có vùng màu đỏ chỉ có 384 byte. Tất cả các biến cục bộ ngoài giới hạn này đều được truy cập bằng mã dài hơn hoặc với các thanh ghi bổ sung.

Ví dụ của bạn, sử dụng vùng màu đỏ là không cần thiết, nhưng gcc thích sử dụng nó cho tất cả các chức năng "lá". Nó chỉ là dễ dàng hơn để thực hiện trình biên dịch theo cách này.

+0

Về vùng màu đỏ không có sẵn trong mã hạt nhân: thực ra, hạt nhân Linux không tôn trọng nó. –

5

ABI x86-64 yêu cầu 'vùng màu đỏ' 128 byte ngoài con trỏ ngăn xếp có thể được sử dụng mà không sửa đổi %rsp. Trong ví dụ đầu tiên, main() là một chức năng lá, do đó trình biên dịch tối ưu hóa việc sử dụng không gian ngăn xếp - tức là, không có cuộc gọi chức năng nào, do đó vùng này sẽ không bị ghi đè.

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