2016-10-28 21 views
5

tôi đang cố gắng để có được một bức tranh rõ ràng về người (gọi hoặc callee) là chịu trách nhiệm nội các sắp xếp đống. Trường hợp để lắp ráp 64-bit là khá rõ ràng, rằng đó là bởi gọi.responsiblity của sự liên kết ngăn xếp trong x86 lắp ráp

Đề cập đến System V AMD64 ABI, phần 3.2.2 Stack Khung:

Sự kết thúc của khu vực tranh luận đầu vào sẽ được xếp trên một 16 (32, nếu __m256 được truyền trên stack) ranh giới byte.

Nói cách khác, nó nên được an toàn giả định, mà cho tất cả các điểm nhập cảnh của gọi là chức năng:

16 | (%rsp + 8)

giữ (thêm tám là vì call implicitely đẩy trở lại địa chỉ trên stack).

Giao diện trong thế giới 32 bit (giả sử cdecl) như thế nào? Tôi nhận thấy rằng gcc nơi sự liên kết bên hàm được gọi với cấu trúc sau:

and esp, -16 

mà dường như chỉ ra, đó là trách nhiệm callee của.

Nói một cách rõ ràng hơn, hãy xem xét đoạn mã sau:

global main 
extern printf 
extern scanf 
section .rodata 
    s_fmt db "%d %d", 0 
    s_res db `%d with remainder %d\n`, 0 
section .text 
main: 
    start 0, 0 
    sub  esp, 8 
    mov  DWORD [ebp-4], 0 ; dividend 
    mov  DWORD [ebp-8], 0 ; divisor 

    lea  eax, [ebp-8] 
    push eax 
    lea  eax, [ebp-4] 
    push eax 
    push s_fmt 
    call scanf 
    add  esp, 12 

    mov  eax, [ebp-4] 
    cdq 
    idiv DWORD [ebp-8] 

    push edx 
    push eax 
    push s_res 
    call printf 

    xor  eax, eax 
    leave 
    ret 

Có cần thiết để sắp xếp ngăn xếp trước khi scanf được gọi là? Nếu vậy, thì điều này sẽ đòi hỏi giảm %esp bốn byte trước khi đẩy hai đối số này để scanf như:

4 bytes (return address) 
4 bytes (%ebp of previous stack frame) 
8 bytes (for two variables) 
12 bytes (three arguments for scanf) 
= 28 
+4

Mã căn chỉnh 16 byte trông giống như 'và esp, 0xfffffff0' thường là thứ bạn sẽ tìm thấy được thêm vào mã mẫu của' main'. Nhưng đối với mọi liên kết chức năng khác được duy trì bởi chức năng gọi. Tôi nên chỉ ra rằng đối với mã 32 bit, bạn nên làm theo [System V i386 ABI] (https://www.uclibc.org/docs/psABI-i386.pdf). Có, bạn cần phải giữ cho xếp chồng 16-byte liên kết ngay trước khi một cuộc gọi chức năng như 'scanf' vv –

+0

x86 ngăn xếp phải được liên kết trên 4 byte chỉ (kích thước đăng ký chung). vì vậy thường không cần thực hiện các nhiệm vụ đặc biệt để sắp xếp xếp chồng. in x64 - đây là trách nhiệm của người gọi cho stack align trên 16 * x trước khi gọi – RbMm

+0

@RbMm: Theo tôi hiểu, yêu cầu căn chỉnh cho x86 stack trên Linux đã tăng lên. Yêu cầu hiện tại là 16 byte (32 nếu một người vượt qua đối số '_m256'). Phần 2.2.2 của ABI bây giờ có chứa cụm từ này: 'Phần cuối của vùng đối số đầu vào phải được căn chỉnh trên một ranh giới byte 16 (32, nếu __m256 được chuyển vào chồng).' –

Trả lời

3

gcc chỉ được tham gia một cách tiếp cận phòng thủ với -m32, bằng cách không giả định rằng main được gọi với một đúng 16B ngăn xếp được sắp xếp.

Hệ thống i386 V ABI đã đảm bảo/yêu cầu trong nhiều năm mà ESP + 4 được liên kết 16B khi nhập vào một hàm. (tức là ESP phải được liên kết 16B trước khi hướng dẫn GỌI, vì vậy args trên ngăn xếp bắt đầu tại ranh giới 16B. Điều này tương tự như đối với hệ thống x86-64 V.)

ABI cũng đảm bảo rằng quá trình -bit bắt đầu với ESP xếp trên một ranh giới 16B (ví dụ tại _start, điểm mấu chốt ELF, nơi ESP điểm tại argc, không phải là một địa chỉ trả lại), và mã glibc CRT duy trì sự liên kết đó.

Theo như quy ước gọi là có liên quan, EBP chỉ là một đăng ký cuộc gọi được bảo tồn. Nhưng có, đầu ra của trình biên dịch với -fno-omit-frame-pointer không quan tâm đến push ebp trước các thanh ghi được giữ nguyên khác (như EBX) và làm như vậy ngay cả khi chức năng không cần sử dụng EBP, vì vậy các giá trị EBP đã lưu sẽ tạo thành một danh sách liên kết.


lẽ gcc là phòng thủ vì một Linux kernel cực kỳ cổ đại (từ trước đó phiên bản cho i386 ABI, khi sự liên kết cần thiết chỉ 4B) có thể vi phạm giả định đó, và nó chỉ có một vài thêm hướng dẫn chạy một lần trong thời gian sống của quá trình (giả sử chương trình không gọi main đệ quy).


Không giống như gcc, clang giả định ngăn xếp được căn chỉnh chính xác khi nhập vào chính. (clang cũng assumes that narrow args have been sign or zero-extended to 32 bits, mặc dù bản sửa đổi ABI hiện tại không chỉ định hành vi đó (chưa) gcc và clang cả hai mã phát ra ở phía người gọi, nhưng chỉ clang phụ thuộc vào nó trong callee. Điều này xảy ra trong 64 bit mã, nhưng tôi đã không kiểm tra 32-bit.)

Nhìn vào đầu ra trình biên dịch trên http://gcc.godbolt.org/ cho chính và chức năng khác với chính nếu bạn tò mò.


Tôi vừa cập nhật liên kết ABI trong thẻ tag vào ngày khác. http://x86-64.org/ vẫn chết và dường như không quay trở lại, vì vậy tôi đã cập nhật liên kết Hệ thống V để trỏ tới tệp PDF của bản sửa đổi hiện tại trong repo github của HJ Lu và his page with links.

Lưu ý rằng last version on SCO's sitekhông bản sửa đổi hiện tại và không bao gồm yêu cầu 16-stack-alignment.

+2

IDK lý do tại sao điều này đã được bình chọn. Cập nhật trong trường hợp nó chỉ trả lời nửa đầu của câu hỏi>. < –

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