2009-07-18 31 views
15

hi Tôi đã tháo rời một số chương trình (linux) tôi đã viết để hiểu rõ hơn cách thức hoạt động, và tôi nhận thấy rằng các chức năng chính luôn bắt đầu bằng:cố gắng để hiểu được tháo dỡ hướng dẫn chính đầu tiên

lea ecx,[esp+0x4] ; I assume this is for getting the adress of the first argument of the main...why ? 
and esp,0xfffffff0 ; ??? is the compiler trying to align the stack pointer on 16 bytes ??? 
push DWORD PTR [ecx-0x4] ; I understand the assembler is pushing the return adress....why ? 
push ebp     
mov ebp,esp 
push ecx ;why is ecx pushed too ?? 

vì vậy câu hỏi của tôi là: tại sao tất cả công việc này được thực hiện? Tôi chỉ hiểu việc sử dụng:

push ebp     
mov ebp,esp 

phần còn lại dường như vô ích cho tôi ...

+1

Trình biên dịch nào bạn đang sử dụng và bạn có thể cung cấp chương trình con chính hoàn chỉnh chưa? – Inshallah

Trả lời

25

Tôi đã có một đi vào nó:

;# As you have already noticed, the compiler wants to align the stack 
;# pointer on a 16 byte boundary before it pushes anything. That's 
;# because certain instructions' memory access needs to be aligned 
;# that way. 
;# So in order to first save the original offset of esp (+4), it 
;# executes the first instruction: 
lea ecx,[esp+0x4] 

;# Now alignment can happen. Without the previous insn the next one 
;# would have made the original esp unrecoverable: 
and esp,0xfffffff0 

;# Next it pushes the return addresss and creates a stack frame. I 
;# assume it now wants to make the stack look like a normal 
;# subroutine call: 
push DWORD PTR [ecx-0x4] 
push ebp 
mov ebp,esp 

;# Remember that ecx is still the only value that can restore the 
;# original esp. Since ecx may be garbled by any subroutine calls, 
;# it has to save it somewhere: 
push ecx 
+1

+1, đây là những gì đang xảy ra + một lời giải thích tốt. Ranh giới 16 byte là lý tưởng vì chúng được yêu cầu cho việc sử dụng các hướng dẫn SIMD (MMX/SSE/SSE2, v.v.). Nếu bạn cố gắng sử dụng lệnh SIMD liên kết trên giá trị liên kết không phải 16 byte trên ngăn xếp, bạn sẽ phân đoạn. – Falaina

+0

@Falaina: Cảm ơn! Đã chỉnh sửa để phản ánh ý kiến ​​của bạn. – Inshallah

5

này được thực hiện để giữ ngăn xếp liên kết với ranh giới 16 byte. Một số hướng dẫn yêu cầu một số loại dữ liệu nhất định phải được căn chỉnh với phạm vi 16 byte. Để đáp ứng yêu cầu này, GCC đảm bảo rằng ngăn xếp ban đầu được sắp xếp 16 byte và phân bổ không gian ngăn xếp theo bội số của 16 byte. Điều này có thể được kiểm soát bằng cách sử dụng tùy chọn -mpreferred-stack-boundary=num. Nếu bạn sử dụng -mpreferred-stack-boundary = 2 (cho một 2 = liên kết 4 byte), mã căn chỉnh này sẽ không được tạo vì ngăn xếp luôn được căn chỉnh ít nhất 4 byte. Tuy nhiên, bạn có thể gặp sự cố nếu chương trình của bạn sử dụng bất kỳ loại dữ liệu nào yêu cầu căn chỉnh mạnh hơn.

Theo hướng dẫn gcc:

On Pentium và Pentium Pro, đôi và dài giá trị tăng gấp đôi nên được sắp xếp để một byte ranh giới 8 (xem -malign-kép) hoặc bị phạt hiệu suất thời gian chạy đáng kể. Trên Pentium III, kiểu dữ liệu mở rộng SIMD mở rộng (SSE) loại __m128 có thể không hoạt động đúng nếu nó không được liên kết 16 byte.

Để đảm bảo căn chỉnh đúng các giá trị này trên ngăn xếp, ranh giới ngăn xếp phải được căn chỉnh theo yêu cầu của bất kỳ giá trị nào được lưu trữ trên ngăn xếp. Hơn nữa, mọi chức năng phải được tạo ra sao cho nó giữ cho chồng xếp hàng. Do đó, việc gọi một hàm được biên dịch với một ranh giới ngăn xếp ưu tiên cao hơn từ một hàm được biên dịch với một ranh giới ngăn xếp được ưu tiên thấp hơn sẽ rất có khả năng làm sai lệch ngăn xếp. Chúng tôi khuyên rằng các thư viện sử dụng gọi lại luôn sử dụng cài đặt mặc định.

Căn chỉnh bổ sung này tiêu thụ thêm không gian ngăn xếp và thường tăng kích thước mã. Mã nhạy cảm với việc sử dụng không gian ngăn xếp, chẳng hạn như hệ thống nhúng và nhân hệ điều hành, có thể muốn giảm sự liên kết ưu tiên thành -mpreferred-stack-boundary = 2.

Các lea tải con trỏ ngăn xếp ban đầu (từ trước khi cuộc gọi đến main) vào ecx, kể từ khi con trỏ ngăn xếp sắp sửa đổi. Này được sử dụng cho hai mục đích:

  1. để truy cập các đối số cho main chức năng, vì chúng là tương đối so với chồng gốc con trỏ
  2. để khôi phục lại con trỏ ngăn xếp với giá trị ban đầu của nó khi trở về từ main
4
lea ecx,[esp+0x4] ; I assume this is for getting the adress of the first argument of  the main...why ? 
and esp,0xfffffff0 ; ??? is the compiler trying to align the stack pointer on 16 bytes ??? 
push DWORD PTR [ecx-0x4] ; I understand the assembler is pushing the return adress....why ? 
push ebp     
mov ebp,esp 
push ecx ;why is ecx pushed too ?? 

Thậm chí nếu mọi lệnh đều hoạt động hoàn hảo mà không bị phạt tốc độ bất chấp các toán hạng được tùy chỉnh, căn chỉnh sẽ vẫn tăng hiệu suất. Hãy tưởng tượng một vòng lặp tham chiếu đến một số lượng 16 byte mà chỉ chồng chéo hai dòng bộ nhớ cache.Bây giờ, để tải wchar nhỏ đó vào bộ nhớ cache, hai dòng bộ nhớ cache toàn bộ phải được gỡ bỏ, và những gì nếu bạn cần chúng trong cùng một vòng lặp? Bộ nhớ cache nhanh hơn rất nhiều so với bộ nhớ RAM mà hiệu suất bộ nhớ cache luôn quan trọng.

Ngoài ra, thường có một hình phạt tốc độ để chuyển các toán hạng không được căn lề vào sổ đăng ký. Cho rằng ngăn xếp đang được sắp xếp lại, chúng ta tự nhiên phải lưu căn chỉnh cũ để đi qua các khung ngăn xếp cho các tham số và trở về.

ecx là một thanh ghi tạm thời để nó phải được lưu. Ngoài ra, tùy thuộc vào mức tối ưu hóa, một số các khung liên kết khung mà dường như không cần thiết để chạy chương trình cũng có thể là quan trọng để thiết lập một chuỗi sẵn sàng theo dõi các khung.

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