2012-11-12 24 views
6

Tôi muốn tăng biến TLS trong quá trình lắp ráp nhưng lại đưa ra một lỗi phân đoạn trong mã lắp ráp. Tôi không muốn cho phép trình biên dịch thay đổi bất kỳ đăng ký hoặc bộ nhớ nào khác. Có cách nào để làm điều này mà không sử dụng cú pháp đầu vào và đầu ra gcc?lưu trữ cục bộ luồng trong lắp ráp

__thread unsigned val; 
int main() { 
    val = 0; 
    asm("incl %gs:val"); 
    return 0; 
} 
+0

1) Tại sao bạn không thể viết ''val + = 1;'' thay thế? 2) Viết nó, biên dịch nó bằng '-O2 -S', và kiểm tra đầu ra của assembly; bạn sẽ khám phá ra rằng bạn đã nhầm lẫn về cách truy cập các biến '__thread'. – zwol

+1

@Zack bạn có thể viết câu trả lời về điều đó không? – 0x90

+0

val ++ dịch để movl $ 0x1,% gs: 0xfffffffc, nhưng khi tôi làm asm ("movl $ 1,% gs: val") theo cách thủ công, nó chuyển thành movl $ 0x1,% gs: 0x8049f14. Cách lấy địa chỉ 0xfffffffc trong chương trình của tôi. – Yogi

Trả lời

14

Nếu bạn thực sự thực sự cần phải có khả năng để làm điều này vì một lý do, bạn nên truy cập vào một biến thread-địa phương từ ngôn ngữ lắp ráp bởi gia tải địa chỉ của nó trong C, như thế này:

__thread unsigned val; 
void incval(void) 
{ 
    unsigned *vp = &val; 
    asm ("incl\t%0" : "+m" (*vp)); 
} 

Điều này là do trình tự mã yêu cầu để truy cập biến thread-local khác với mọi hệ điều hành và kết hợp CPU được GCC hỗ trợ, và cũng thay đổi nếu bạn biên dịch cho một thư viện được chia sẻ chứ không phải là thực thi (tức là với -fPIC). Cấu trúc trên cho phép trình biên dịch phát ra chuỗi mã đúng cho bạn. Trong trường hợp có thể truy cập biến thread-local mà không có bất kỳ hướng dẫn bổ sung nào, việc tạo địa chỉ sẽ được xếp vào hoạt động lắp ráp. Bằng cách minh họa, đây là cách gcc 4.7 cho x86/Linux biên dịch nêu trên trong nhiều chế độ khác nhau có thể (tôi đã tước ra một loạt các chỉ thị lắp ráp trong mọi trường hợp, cho rõ ràng) ...

# -S -O2 -m32 -fomit-frame-pointer 
incval: 
     incl %gs:[email protected] 
     ret 

# -S -O2 -m64 
incval: 
     incl %fs:[email protected] 
     ret 

# -S -O2 -m32 -fomit-frame-pointer -fpic 
incval: 
     pushl %ebx 
     call __x86.get_pc_thunk.bx 
     addl $_GLOBAL_OFFSET_TABLE_, %ebx 
     leal [email protected](,%ebx,1), %eax 
     call [email protected] 
     incl (%eax) 
     popl %ebx 
     ret 

# -S -O2 -m64 -fpic 
incval: 
     .byte 0x66 
     leaq [email protected](%rip), %rdi 
     .value 0x6666 
     rex64 
     call [email protected] 
     incl (%rax) 
     ret 

Do nhận ra rằng tất cả bốn ví dụ sẽ khác nếu tôi đã biên dịch cho x86/OSX và khác biệt một lần nữa đối với x86/Windows.

+0

Câu trả lời hay. Các tiền tố bổ sung cho căn chỉnh trong mã cuối cùng? – Jester

+2

@Jester Họ cung cấp cho người liên kết một số không gian bổ sung, để nó có thể thay thế các hướng dẫn bạn thấy bằng một chuỗi hiệu quả hơn (nhưng bao gồm các chỉ dẫn dài hơn) nếu có thể. Xem http://people.redhat.com/drepper/tls.pdf và http://www.x86-64.org/pipermail/discuss/2002-September/002829.html để biết chi tiết đẫm máu. – zwol

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