2010-05-04 53 views
7

Tôi cố gắng xây dựng một ứng dụng sử dụng loại pthreads và __m128 SSE. Theo hướng dẫn của GCC, sắp xếp ngăn xếp mặc định là 16 byte. Để sử dụng __m128, yêu cầu là căn chỉnh 16 byte.GCC - Cách sắp xếp lại ngăn xếp?

CPU mục tiêu của tôi hỗ trợ SSE. Tôi sử dụng trình biên dịch GCC không hỗ trợ sắp xếp lại thời gian chạy ngăn xếp (ví dụ: -mstackrealign). Tôi không thể sử dụng bất kỳ phiên bản trình biên dịch GCC nào khác.

ứng dụng thử nghiệm của tôi trông giống như:

#include <xmmintrin.h> 
#include <pthread.h> 
void *f(void *x){ 
    __m128 y; 
    ... 
} 
int main(void){ 
    pthread_t p; 
    pthread_create(&p, NULL, f, NULL); 
} 

Ứng dụng này tạo ra một ngoại lệ và lối thoát hiểm. Sau khi gỡ lỗi đơn giản (printf "% p", & y), tôi thấy rằng biến y không được căn chỉnh 16 byte.

Câu hỏi của tôi là: làm cách nào tôi có thể căn chỉnh lại ngăn xếp đúng cách (16 byte) mà không sử dụng bất kỳ cờ và thuộc tính GCC nào (chúng không giúp ích gì)? Tôi có nên sử dụng GCC inline Assembler trong hàm này f()?

+2

Nếu bạn phải sử dụng một phiên bản gcc Đặc biệt, xin vui lòng bao gồm phiên bản gcc (ví dụ như gcc 4.3.2 i386), và máy chủ/hệ điều hành đích (ví dụ Debian 5.0 (lenny) Linux 2.6.26 i686). Việc biết nên đề xuất các tùy chọn gcc 4.3 so với 3,4 có thể tạo sự khác biệt hay không. – mctylr

Trả lời

0

Tôi đã giải quyết được sự cố này. Đây là giải pháp của tôi:

void another_function(){ 
    __m128 y; 
    ... 
} 
void *f(void *x){ 
asm("pushl %esp"); 
asm("subl $16,%esp"); 
asm("andl $-0x10,%esp"); 
another_function(); 
asm("popl %esp"); 
} 

Đầu tiên, chúng tôi tăng ngăn xếp lên 16 byte. Thứ hai, chúng tôi làm cho ít nhất đáng kể nibble bằng 0x0. Chúng tôi bảo vệ con trỏ ngăn xếp bằng cách sử dụng toán hạng push/pop. Chúng ta gọi một hàm khác, có tất cả các biến cục bộ 16 byte của nó. Tất cả các hàm lồng nhau cũng sẽ có các biến cục bộ 16 byte của chúng.

Và nó hoạt động!

+4

Nghiêm túc. CẬP NHẬT COMPILER CỦA BẠN. Đừng tự hào về bản thân vì đã đặt các thiết bị goldberg rube vào mã của bạn. –

+6

Mã này xuất hiện để lưu ESP trên ngăn xếp, sau đó di chuyển ESP ở một nơi khác, sau đó bật ESP. Điều này sẽ gây ra một giá trị ngẫu nhiên để được popped vào ESP. Điều này không gây ra tai nạn? Hoặc bạn đang sử dụng một quy ước gọi điện thoại nơi ESP được lưu ở một nơi khác, có lẽ vào EBP, và phục hồi vào cuối, làm cho POP thừa? – user9876

+0

1) Tôi không thể cập nhật GCC -> Tôi có một môi trường thời gian chạy cụ thể và một CPU tương thích x86 cụ thể. 2) Không, tại sao nó có thể gây ra sự cố? Tiết kiệm ESP, sau đó khôi phục lại nó không gây ra bất kỳ sự cố hoặc giá trị ngẫu nhiên nào. Tôi đã thử nghiệm mã trên cũng không có pushl/popl và nó cũng là Ok. Không có bất kỳ quy ước gọi điện thoại và ESP không được lưu ở một nơi khác. – psihodelia

3

này không nên xảy ra ở nơi đầu tiên, nhưng để làm việc xung quanh vấn đề bạn có thể thử:

void *f(void *x) 
{ 
    __m128 y __attribute__ ((aligned (16))); 
    ... 
} 
+0

Không, nó không giúp được gì. Vấn đề giống nhau. – psihodelia

+0

Đoán của tôi là bạn đang làm điều này trên Windows chứ không phải là một hệ điều hành thích hợp? Có một số thông tin tốt ở đây về làm việc xung quanh vấn đề này: http://www.sourceware.org/ml/pthreads-win32/2008/msg00056.html –

+0

Không, tôi làm việc trên Linux – psihodelia

7

Phân bổ trên stack một mảng đó là 15-byte lớn hơn sizeof(__m128), và sử dụng địa chỉ được căn chỉnh đầu tiên trong mảng đó. Nếu bạn cần nhiều, hãy phân bổ chúng trong một mảng với một biên độ 15 byte đơn cho căn chỉnh.

Tôi không nhớ nếu phân bổ một mảng unsigned char giúp bạn an toàn với việc tối ưu hóa bí danh nghiêm ngặt của trình biên dịch hoặc nếu nó chỉ hoạt động theo cách khác.

#include <stdint.h> 

void *f(void *x) 
{ 
    unsigned char y[sizeof(__m128)+15]; 
    __m128 *py = (__m128*) (((uintptr_t)&y) + 15) & ~(uintptr_t)15); 
    ... 
} 
+0

Bạn cũng có thể muốn kiểm tra xem ngăn xếp luồng tổng thể có được phân bổ với căn chỉnh 16 byte hay không. –

+0

Cảm ơn, nhưng ptr_t là gì và tại sao bạn sử dụng & ~ 15? – psihodelia

+5

Thật không may điều này buộc các biến được trên stack bất kể tối ưu hóa trình biên dịch tiềm năng (như giữ nó trong một đăng ký). –

1

Một giải pháp khác là sử dụng chức năng đệm, đầu tiên căn chỉnh ngăn xếp và sau đó gọi f. Vì vậy, thay vì gọi trực tiếp số f, bạn gọi số pad, ngăn xếp ngăn xếp trước và sau đó gọi số foo với ngăn xếp được căn chỉnh.

Mã này sẽ trông như thế này:

#include <xmmintrin.h> 
#include <pthread.h> 

#define ALIGNMENT 16 

void *f(void *x) { 
    __m128 y; 
    // other stuff 
} 

void * pad(void *val) { 
    unsigned int x; // to get the current address from the stack 
    unsigned char pad[ALIGNMENT - ((unsigned int) &x) % ALIGNMENT]; 
    return f(val); 
} 

int main(void){ 
    pthread_t p; 
    pthread_create(&p, NULL, pad, NULL); 
} 
0

Xin lỗi vì đã hồi sinh một chủ đề cũ ...

Đối với những người có một trình biên dịch mới hơn OP, OP đề cập đến một tùy chọn -mstackrealign, mà đưa tôi đến với __attribute__((force_align_arg_pointer)). Nếu chức năng của bạn đang được tối ưu hóa để sử dụng SSE, nhưng %ebp là không đúng, điều này sẽ thực hiện các bản sửa lỗi thời gian chạy nếu cần thiết cho bạn, minh bạch. Tôi cũng phát hiện ra rằng đây chỉ là vấn đề trên i386. x86_64 ABI đảm bảo các đối số được căn chỉnh đến 16 byte.

__attribute__((force_align_arg_pointer)) void i_crash_when_not_aligned_to_16_bytes() { ... }

mát bài viết dành cho những người có thể muốn tìm hiểu thêm: http://wiki.osdev.org/System_V_ABI

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