Triển Khai Hàm _Alloca
Trong ngôn ngữ C tồn tại một hàm đặc biệt có tên là alloca
, cho phép cấp phát vùng nhớ trên ngăn xếp (stack). Khi hàm chứa lời gọi này kết thúc, vùng nhớ sẽ tự động được giải phóng nhờ cơ chế điều chỉnh con trỏ ngăn xếp của hệ thống.
Nguyên mẫu của hàm alloca
như sau:
|
|
Ngày nay, do chứa nhiều lỗ hổng an toàn tiềm ẩn, việc sử dụng hàm này đã không còn được khuyến khích trong hầu hết tài liệu lập trình hiện đại. Tuy nhiên, trong thư viện CRT (C Runtime), vẫn tồn tại một phiên bản nội bộ có tên _alloca
được các trình biên dịch sử dụng để triển khai các tính năng đặc biệt. Chẳng hạn, trong tiêu chuẩn C99 cho phép khai báo mảng độ dài thay đổi (VLA - Variable Length Array) trực tiếp trên ngăn xếp, GCC thực chất đã sử dụng _alloca
để thực hiện việc cấp phát này.
Một trường hợp đặc biệt khác là khi bạn khai báo một mảng cục bộ với kích thước quá lớn, GCC cũng sẽ tự động chèn lời gọi _alloca
. Điều này có liên quan đến việc CRT triển khai alloca
với cơ chế kiểm tra tràn ngăn xếp được tích hợp sẵn.
Vào cuối tuần vừa rồi, tôi đã thử nghiệm viết một phiên bản _alloca
thủ công để thay thế triển khai gốc trong CRT (xin miễn bàn lý do :) ). Ban đầu gặp khá nhiều khó khăn do chưa nắm rõ các quy ước nội bộ của trình biên dịch.
Khác với alloca
thông thường, _alloca
là một hàm nội bộ của GCC với giao thức truyền tham số đặc biệt. Thay vì truyền qua ngăn xếp, kích thước cấp phát được truyền trực tiếp qua thanh ghi eax
. Giá trị trả về cũng được đặt trong eax
, đồng thời hàm sẽ trực tiếp điều chỉnh con trỏ ngăn xếp esp
.
Sau nhiều lần chương trình sụp đổ và phân tích từng dòng lệnh bằng GDB, tôi nhận ra một số chi tiết thú vị khác:
- Khi gọi
_alloca
, người gọi không cần bảo vệ dữ liệu trong các thanh ghi khác ngoàieax
vàesp
. Nói cách khác, hàm_alloca
phải tự chịu trách nhiệm bảo vệ các thanh ghi khác (tuy nhiên trong thực tế, việc phá hủy thanh ghiecx
có thể chấp nhận được, nhưng nếu làm thay đổiedx
thì chắc chắn gây crash chương trình). - Khi bật chế độ tối ưu hóa
-O2
(với GCC 3.4.5), trình biên dịch có thể dự đoán hành vi của_alloca
để sắp xếp phân bố biến cục bộ trên ngăn xếp. Điều này đòi hỏi_alloca
phải cấp phát chính xác kích thước đã căn chỉnh 4 byte (DWORD) dựa trên giá trị trongeax
. Việc cấp phát thiếu hay thừa đều có thể phá vỡ logic của mã máy được sinh ra.
Dưới đây là phiên bản _alloca
do tôi triển khai:
|
|
P/s: Bài viết này chắc chắn sẽ trở thành “tài liệu tham khảo” cho nhiều bạn sinh viên trong tương lai. Xin phép khẳng định lại: Mã nguồn được chia sẻ hoàn toàn miễn phí, nhưng nếu gặp lỗi xin đừng liên hệ tôi hỗ trợ. Tôi không nhận gia sư hay làm bài tập hộ!