Trong khi cố gắng để làm cho một số mã cũ làm việc trở lại (https://github.com/chaos4ever/chaos/blob/master/libraries/system/system_calls.h#L387, FWIW) Tôi phát hiện ra rằng một số ngữ nghĩa của gcc
dường như đã thay đổi theo một cách khá tinh tế nhưng vẫn nguy hiểm trong 10-15 năm gần đây nhất ...: PGCC/x86 nội tuyến asm: Làm thế nào để bạn nói với gcc rằng phần lắp ráp nội tuyến sẽ sửa đổi% esp?
Mã được sử dụng để hoạt động tốt với các phiên bản cũ hơn của gcc
, như 2,95. Dù sao, đây là mã:
static inline return_type system_call_service_get(const char *protocol_name, service_parameter_type *service_parameter,
tag_type *identification)
{
return_type return_value;
asm volatile("pushl %2\n"
"pushl %3\n"
"pushl %4\n"
"lcall %5, $0"
: "=a" (return_value),
"=g" (*service_parameter)
: "g" (identification),
"g" (service_parameter),
"g" (protocol_name),
"n" (SYSTEM_CALL_SERVICE_GET << 3));
return return_value;
}
Vấn đề với đoạn code trên được rằng gcc
(4.7 trong trường hợp của tôi) sẽ biên dịch này để mã asm sau (AT & cú pháp T):
# 392 "../system/system_calls.h" 1
pushl 68(%esp) # This pointer (%esp + 0x68) is valid when the inline asm is entered.
pushl %eax
pushl 48(%esp) # ...but this one is not (%esp + 0x48), since two dwords have now been pushed onto the stack, so %esp is not what the compiler expects it to be
lcall $456, $0
# Restoration of %esp at this point is done in the called method (i.e. lret $12)
Vấn đề: Các biến (identification
và protocol_name
) nằm trong ngăn xếp trong ngữ cảnh gọi. Vì vậy, gcc
(với tối ưu hóa hóa ra, không chắc chắn nếu nó quan trọng) sẽ chỉ nhận được các giá trị từ đó và đưa nó cho phần nội tuyến asm. Nhưng vì tôi đang đẩy nội dung trên ngăn xếp, các bù trừ rằng tính toán gcc
sẽ bị tắt bởi 8 trong cuộc gọi thứ ba (pushl 48(%esp)
). :)
Điều này đã cho tôi một thời gian dài để tìm ra, nó không phải là tất cả rõ ràng với tôi lúc đầu.
Cách dễ nhất xung quanh điều này là tất nhiên để sử dụng ràng buộc đầu vào r
, để đảm bảo rằng giá trị trong sổ đăng ký thay thế. Nhưng có cách nào khác tốt hơn không? Dĩ nhiên, một cách rõ ràng là viết lại toàn bộ giao diện gọi hệ thống để không đẩy nội dung vào ngăn xếp ở vị trí đầu tiên (và sử dụng thanh ghi thay thế, ví dụ như Linux), nhưng đó không phải là tái cấu trúc mà tôi cảm thấy thích làm tối nay ...
Có cách nào để báo cho gcc
nội tuyến asm rằng "ngăn xếp dễ bay hơi" không? Các bạn đã xử lý những thứ như thế này trong quá khứ?
Cập nhật sau cùng buổi tối: Tôi đã tìm thấy một liên quan gcc
ML ren (https://gcc.gnu.org/ml/gcc-help/2011-06/msg00206.html) nhưng nó không có vẻ để giúp đỡ. Dường như việc chỉ định %esp
trong danh sách clobber nên làm cho nó bù trừ từ %ebp
thay vào đó, nhưng nó không hoạt động và tôi nghi ngờ -O2 -fomit-frame-pointer
có hiệu lực ở đây. Tôi đã bật cả hai cờ này.
Iirc thêm "cc" và/hoặc "bộ nhớ" vào danh sách clobber thực hiện điều này. Đôi khi thêm biến động vào asm() sẽ ngăn trình biên dịch tối ưu hóa quá mức. – technosaurus
Làm cách nào để thực hiện 'đẩy 8 +% 4'? –
Nhân tiện: [% rsp trong danh sách clobber bị bỏ qua một cách thầm lặng] (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52813). –