Đây là tất cả về đăng ký BP / EBP / RBP trên nền tảng Intel. Thanh ghi này mặc định là phân đoạn ngăn xếp (không cần tiền tố đặc biệt để truy cập phân đoạn ngăn xếp).
EBP là lựa chọn đăng ký tốt nhất để truy cập cấu trúc dữ liệu, biến và không gian làm việc được phân bổ động trong ngăn xếp. EBP thường được sử dụng để truy cập các phần tử trên ngăn xếp liên quan đến một điểm cố định trên ngăn xếp hơn là liên quan đến TOS hiện tại. Nó thường xác định địa chỉ cơ sở của khung ngăn xếp hiện tại được thiết lập cho thủ tục hiện tại. Khi EBP được sử dụng làm thanh ghi cơ sở trong phép tính bù, độ lệch được tính toán tự động trong phân đoạn ngăn xếp hiện tại (tức là phân đoạn hiện được chọn bởi SS). Bởi vì SS không phải được chỉ định rõ ràng, mã hóa lệnh trong những trường hợp như vậy hiệu quả hơn. EBP cũng có thể được sử dụng để lập chỉ mục thành các phân đoạn có thể xác định được thông qua các thanh ghi phân đoạn khác.
(nguồn - http://css.csail.mit.edu/6.858/2017/readings/i386/s02_03.htm )
Vì trên hầu hết các nền tảng 32-bit, phân đoạn dữ liệu và phân đoạn ngăn xếp giống nhau, nên sự liên kết giữa EBP / RBP với ngăn xếp này không còn là vấn đề nữa. Trên nền tảng 64-bit cũng vậy: Kiến trúc x86-64, được AMD giới thiệu vào năm 2003, phần lớn đã không hỗ trợ phân đoạn ở chế độ 64-bit: bốn trong số các thanh ghi phân đoạn: CS, SS, DS và ES bị buộc về 0 Những trường hợp này của nền tảng x86 32-bit và 64-bit về cơ bản có nghĩa là thanh ghi EBP / RBP có thể được sử dụng, không có bất kỳ tiền tố nào, trong các lệnh bộ xử lý truy cập bộ nhớ.
Vì vậy, tùy chọn trình biên dịch mà bạn đã viết cho phép BP / EBP / RBP được sử dụng cho các phương tiện khác, ví dụ như để giữ một biến cục bộ.
Bởi "Điều này tránh các hướng dẫn để lưu, thiết lập và khôi phục con trỏ khung" có nghĩa là tránh mã sau trên mục nhập của mỗi chức năng:
push ebp
mov ebp, esp
hoặc enter
hướng dẫn, rất hữu ích trên bộ xử lý Intel 80286 và 80386.
Ngoài ra, trước khi trả về hàm, mã sau được sử dụng:
mov esp, ebp
pop ebp
hoặc leave
hướng dẫn.
Các công cụ gỡ lỗi có thể quét dữ liệu ngăn xếp và sử dụng dữ liệu thanh ghi EBP được đẩy này trong khi định vị call sites
, tức là để hiển thị tên của hàm và các đối số theo thứ tự chúng đã được gọi là phân cấp.
Lập trình viên có thể có câu hỏi về khung ngăn xếp không theo nghĩa rộng (rằng nó là một thực thể duy nhất trong ngăn xếp chỉ phục vụ một lệnh gọi hàm và giữ địa chỉ trả về, đối số và biến cục bộ) nhưng theo nghĩa hẹp - khi thuật ngữ stack frames
được đề cập trong bối cảnh của các tùy chọn trình biên dịch. Từ quan điểm của trình biên dịch, một khung ngăn xếp chỉ là mã vào và ra cho quy trình , đẩy một mỏ neo vào ngăn xếp - cũng có thể được sử dụng để gỡ lỗi và xử lý ngoại lệ. Các công cụ gỡ lỗi có thể quét dữ liệu ngăn xếp và sử dụng các neo này để truy tìm ngược, đồng thời xác định vị trí call sites
trong ngăn xếp, tức là để hiển thị tên của hàm theo thứ tự chúng đã được gọi là phân cấp.
Đó là lý do tại sao điều rất quan trọng đối với lập trình viên là phải hiểu khung ngăn xếp là gì về các tùy chọn trình biên dịch - bởi vì trình biên dịch có thể kiểm soát việc tạo mã này hay không.
Trong một số trường hợp, khung ngăn xếp (mã vào và ra cho quy trình) có thể bị trình biên dịch bỏ qua và các biến sẽ được truy cập trực tiếp thông qua con trỏ ngăn xếp (SP / ESP / RSP) chứ không phải là con trỏ cơ sở thuận tiện (BP / ESP / RSP). Các điều kiện để trình biên dịch bỏ qua các khung ngăn xếp đối với một số hàm có thể khác nhau, ví dụ: (1) hàm là một hàm lá (nghĩa là một thực thể cuối không gọi các hàm khác); (2) không có ngoại lệ nào được sử dụng; (3) không có thói quen nào được gọi với các tham số gửi đi trên ngăn xếp; (4) hàm không có tham số.
Việc bỏ qua các khung ngăn xếp (mã nhập và thoát cho quy trình) có thể làm cho mã nhỏ hơn và nhanh hơn, nhưng cũng có thể ảnh hưởng tiêu cực đến khả năng truy tìm lại dữ liệu trong ngăn xếp và hiển thị cho người lập trình. Đây là các tùy chọn trình biên dịch xác định trong những điều kiện nào mà một hàm phải đáp ứng để trình biên dịch cấp cho nó mã xuất và nhập khung ngăn xếp. Ví dụ, một trình biên dịch có thể có các tùy chọn để thêm mã vào và ra như vậy vào các hàm trong các trường hợp sau: (a) luôn luôn, (b) không bao giờ, (c) khi cần (xác định các điều kiện).
Quay trở lại từ tổng quát đến đặc biệt: nếu bạn sẽ sử dụng -fomit-frame-pointer
tùy chọn trình biên dịch GCC, bạn có thể giành chiến thắng trên cả mã đầu vào và mã thoát cho quy trình và khi có thêm một thanh ghi (trừ khi nó đã được bật theo mặc định hoặc chính nó hoặc ngầm định bởi người khác tùy chọn, trong trường hợp này, bạn đã được hưởng lợi từ việc sử dụng thanh ghi EBP / RBP và sẽ không có thêm lợi ích nào bằng cách chỉ định rõ ràng tùy chọn này nếu nó đã được sử dụng ngầm). Tuy nhiên, xin lưu ý rằng ở chế độ 16 bit và 32 bit, thanh ghi BP không có khả năng truy cập các phần 8 bit của nó như AX có (AL và AH).
Vì tùy chọn này, bên cạnh việc cho phép trình biên dịch sử dụng EBP như một thanh ghi có mục đích chung trong việc tối ưu hóa, còn ngăn chặn việc tạo mã thoát và mã nhập cho khung ngăn xếp làm phức tạp việc gỡ lỗi - đó là lý do tại sao tài liệu GCC tuyên bố rõ ràng (nhấn mạnh bất thường bằng dấu đậm style) bật tùy chọn này làm cho việc gỡ lỗi không thể xảy ra trên một số máy
Cũng xin lưu ý rằng các tùy chọn trình biên dịch khác, liên quan đến gỡ lỗi hoặc tối ưu hóa, có thể hoàn toàn -fomit-frame-pointer
BẬT hoặc TẮT tùy chọn.
Tôi không tìm thấy bất kỳ thông tin chính thức nào tại gcc.gnu.org về cách các tùy chọn khác ảnh hưởng đến -fomit-frame-pointer
nền tảng x86 , https://gcc.gnu.org/onlineocs/gcc-3.4.4/gcc/Optimize-Options.html chỉ nêu những điều sau:
-O cũng bật -fomit-frame-pointer trên các máy mà việc này không ảnh hưởng đến việc gỡ lỗi.
Vì vậy, không rõ ràng từ tài liệu -fomit-frame-pointer
sẽ được bật nếu bạn chỉ biên dịch với một -O
tùy chọn duy nhất trên nền tảng x86. Nó có thể được kiểm tra theo kinh nghiệm, nhưng trong trường hợp này, các nhà phát triển GCC không có cam kết không thay đổi hành vi của tùy chọn này trong tương lai mà không cần thông báo.
Tuy nhiên, Peter Cordes đã chỉ ra trong các bình luận rằng có sự khác biệt đối với cài đặt mặc định của -fomit-frame-pointer
nền tảng x86-16 và nền tảng x86-32 / 64.
Tùy chọn này - -fomit-frame-pointer
- cũng có liên quan đến Trình biên dịch Intel C ++ 15.0 , không chỉ với GCC:
Đối với Trình biên dịch Intel, tùy chọn này có một bí danh /Oy
.
Đây là những gì Intel đã viết về nó:
Các tùy chọn này xác định liệu EBP có được sử dụng như một đăng ký mục đích chung trong việc tối ưu hóa hay không. Tùy chọn -fomit-frame-pointer và / Oy cho phép sử dụng điều này. Tùy chọn -fno-omit-frame-pointer và / Oy- không cho phép nó.
Một số trình gỡ rối mong muốn EBP được sử dụng như một con trỏ khung ngăn xếp và không thể tạo ra dấu vết ngăn xếp trừ khi điều này xảy ra. Tùy chọn -fno-omit-frame-pointer và / Oy- chỉ đạo trình biên dịch tạo mã duy trì và sử dụng EBP làm con trỏ khung ngăn xếp cho tất cả các chức năng để trình gỡ lỗi vẫn có thể tạo ra một ngăn xếp tồn đọng mà không cần làm như sau:
Đối với -fno-omit-frame-pointer: tắt tối ưu hóa với -O0 For / Oy-: tắt tối ưu hóa / O1, / O2 hoặc / O3 Tùy chọn -fno-omit-frame-pointer được đặt khi bạn chỉ định tùy chọn - O0 hoặc tùy chọn -g. Tùy chọn -fomit-frame-pointer được đặt khi bạn chỉ định tùy chọn -O1, -O2 hoặc -O3.
Tùy chọn / Oy được đặt khi bạn chỉ định tùy chọn / O1, / O2 hoặc / O3. Tùy chọn / Oy- được đặt khi bạn chỉ định tùy chọn / Od.
Sử dụng tùy chọn -fno-omit-frame-pointer hoặc / Oy- sẽ giảm số lượng thanh ghi đa năng có sẵn xuống 1 và có thể dẫn đến mã kém hiệu quả hơn một chút.
LƯU Ý Đối với hệ thống Linux *: Hiện đang xảy ra sự cố với việc xử lý ngoại lệ GCC 3.2. Do đó, trình biên dịch Intel bỏ qua tùy chọn này khi GCC 3.2 được cài đặt cho C ++ và xử lý ngoại lệ được bật (mặc định).
Xin lưu ý rằng báo giá trên chỉ liên quan đến trình biên dịch Intel C ++ 15, không liên quan đến GCC.