2012-05-08 21 views
6

Điều gì có thể gây ra lỗi phân đoạn khi chỉ cần nhập hàm?SIGSEGV khi nhập hàm

Chức năng bước vào trông giống như:

21: void eesu3(Matrix & iQ) 
22: { 

nơi Matrix là một struct. Khi chạy với GDB, backtrace sẽ tạo ra:

(gdb) backtrace 
#0 eesu3 (iQ=...) at /home/.../eesu3.cc:22 
#1 ... 

GDB không nói gì iQ là. ... có nghĩa đen ở đó. Điều gì có thể gây ra điều này?

GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

Chương trình được xây dựng với -O3 -g

Người gọi đi như thế:

Matrix q; 
// do some stuff with q 
eesu3(q); 

Không có gì đặc biệt ở đây

Tôi đọc lại chương trình với valgrind:

valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes <prgname> 

Output:

==2240== Warning: client switching stacks? SP change: 0x7fef7ef68 --> 0x7fe5e3000 
==2240==   to suppress, use: --max-stackframe=10076008 or greater 
==2240== Invalid write of size 8 
==2240== at 0x14C765B: eesu3(Matrix &) (eesu3.cc:22) 
... 
==2240== Address 0x7fe5e3fd8 is on thread 1's stack 
==2240== 
==2240== Can't extend stack to 0x7fe5e2420 during signal delivery for thread 1: 
==2240== no stack segment 
==2240== 
==2240== Process terminating with default action of signal 11 (SIGSEGV) 
==2240== Access not within mapped region at address 0x7FE5E2420 
==2240== at 0x14C765B: eesu3(Matrix&) (eesu3.cc:22) 
==2240== If you believe this happened as a result of a stack 
==2240== overflow in your program's main thread (unlikely but 
==2240== possible), you can try to increase the size of the 
==2240== main thread stack using the --main-stacksize= flag. 
==2240== The main thread stack size used in this run was 8388608. 

Trông giống như một đống bị hỏng của mình.

Dump of assembler code for function eesu3(Matrix &): 
    0x00000000014c7640 <+0>: push %rbp 
    0x00000000014c7641 <+1>: mov %rsp,%rbp 
    0x00000000014c7644 <+4>: push %r15 
    0x00000000014c7646 <+6>: push %r14 
    0x00000000014c7648 <+8>: push %r13 
    0x00000000014c764a <+10>: push %r12 
    0x00000000014c764c <+12>: push %rbx 
    0x00000000014c764d <+13>: and $0xfffffffffffff000,%rsp 
    0x00000000014c7654 <+20>: sub $0x99b000,%rsp 
=> 0x00000000014c765b <+27>: mov %rdi,0xfd8(%rsp) 

Được rồi, để làm rõ: Dữ liệu của ma trận sống trên heap. Về cơ bản nó nắm giữ một con trỏ đến dữ liệu. Cấu trúc nhỏ, 32 byte. (Chỉ cần kiểm tra)

Bây giờ, tôi xây dựng lại chương trình với các tùy chọn tối ưu hóa khác nhau:

-O0: lỗi không hiển thị.

-O1: lỗi hiển thị.

-O3: lỗi hiển thị.

--update

-O3 -fno-inline -fno-inline-functions: lỗi không hiển thị.

Điều đó giải thích điều đó. Quá nhiều nội tuyến vào hàm dẫn đến việc sử dụng chồng quá mức.

Vấn đề là do một stack overflow

+2

Để xem các biến của bạn, v.v. không tối ưu hóa. Biên dịch với '-O0 -g' – RageD

+2

Tham nhũng ngăn xếp? – Benj

+0

Ok, sẽ xây dựng lại bằng '-O0 -g'. Mất một thời gian – ritter

Trả lời

13

Điều gì có thể gây ra lỗi phân đoạn khi chỉ cần nhập hàm?

Nguyên nhân thường gặp nhất là tình trạng cạn kiệt ngăn xếp. Làm (gdb) disas tại điểm cố định. Nếu lệnh bị hỏng là lần đọc đầu tiên hoặc ghi vào một vị trí ngăn xếp sau khi %rsp bị giảm đi, thì tình trạng cạn kiệt ngăn xếp gần như chắc chắn là nguyên nhân.

Giải pháp thường liên quan đến việc tạo chuỗi với ngăn xếp lớn hơn, di chuyển một số biến lớn từ ngăn xếp sang đống hoặc cả hai.

Một nguyên nhân có thể: nếu Matrix chứa mảng rất lớn, bạn không thể đặt nó trên stack: hạt nhân sẽ không mở rộng đống ngoài hiện bởi hơn 128K (hoặc lâu hơn, tôi không nhớ chính xác giá trị) . Nếu Matrix lớn hơn giới hạn đó, bạn không thể đặt nó trên ngăn xếp.

Cập nhật:

0x00000000014c7654 <+20>: sub $0x99b000,%rsp 
=> 0x00000000014c765b <+27>: mov %rdi,0xfd8(%rsp) 

tháo Điều này khẳng định chẩn đoán.

Ngoài ra, bạn đang đặt 0x99b000 byte trên ngăn xếp (gần 10MB). Phải có một số đối tượng khổng lồ mà bạn đang cố định vị trí trên ngăn xếp trong thường lệ eesu3. Đừng làm thế.

Ý anh là gì bởi "hạt nhân sẽ không mở rộng đống ngoài hiện tại hơn"

Khi bạn mở rộng ngăn xếp (sụt lần %rsp) bằng cách ví dụ 1MB, và sau đó cố gắng chạm vào vị trí ngăn xếp đó, bộ nhớ sẽ không thể truy cập được (hạt nhân tăng stack theo yêu cầu). Điều này sẽ tạo ra một cái bẫy phần cứng, và chuyển điều khiển cho hạt nhân. Khi hạt nhân quyết định phải làm gì, nó nhìn vào

  1. hiện %rsp
  2. Meemory vị trí mà ứng dụng cố gắng truy cập
  3. hạn Stack cho thread hiện hành

Nếu đứt gãy địa chỉ dưới hiện tại %rsp, nhưng trong phạm vi 128K (hoặc một số hằng số khác có độ lớn tương tự), hạt nhân chỉ mở rộng ngăn xếp (miễn là phần mở rộng đó sẽ không vượt quá giới hạn ngăn xếp).

Nếu địa chỉ lỗi lớn hơn 128K bên dưới hiện tại %rsp (như trường hợp ở đây), bạn nhận được SIGSEGV.

Tất cả đều hoạt động tốt cho hầu hết các chương trình: ngay cả khi chúng sử dụng nhiều ngăn xếp trong quy trình đệ quy, chúng thường mở rộng ngăn xếp theo các đoạn nhỏ. Nhưng một chương trình tương đương đã cố gắng để dự trữ tất cả các ngăn xếp trong một thói quen duy nhất sẽ bị rơi.

Dù sao, hãy làm (gdb) info locals tại điểm cố định và xem những gì người dân địa phương có thể yêu cầu 10MB ngăn xếp. Sau đó di chuyển chúng đến đống.

Cập nhật 2:

Không người dân địa phương

Ah, chương trình đã có thể không làm cho nó đủ xa vào eesu3 cho có được người dân địa phương.

khi xây dựng bằng -O0 lỗi biến mất. Lỗi GCC?

Nó có thể là một lỗi GCC, nhưng nhiều khả năng nó chỉ là GCC được nội tuyến có rất nhiều thói quen khác vào eesu3, và mỗi người trong số các thói quen inlined cần N KBs riêng của chồng. Sự cố có biến mất nếu bạn tạo nguồn có chứa eesu3 với -fno-inline không?

Thật không may, việc phân loại hành vi như vậy và tìm ra cách giải quyết thích hợp, hoặc sửa GCC, đòi hỏi chuyên môn về trình biên dịch. Bạn có thể bắt đầu bằng cách biên dịch với -fdump-tree-all và xem các tệp <source>.*t.* được tạo. Chúng chứa các vùng văn bản của biểu diễn nội bộ GCC ở các giai đoạn khác nhau của quá trình biên dịch. Bạn có thể có thể hiểu đủ về nó để tiến bộ thêm.

+0

Payload of Matrix đã có trên heap. Nó thực sự chỉ sở hữu một con trỏ đến dữ liệu. Nó không phải là kích thước của struct Matrix (8 hoặc 16 byte). Xem đầu ra ở trên của 'disas' – ritter

+0

@Employed tiếng Nga, ý bạn là gì bởi" hạt nhân sẽ không mở rộng ngăn xếp vượt ra ngoài hiện tại bằng nhiều hơn "bạn có thể viết chi tiết hơn không? – azat

+0

'địa phương thông tin': Không có người dân địa phương. Thật ki quặc. Có 30 vars cục bộ được xác định, mỗi 32byte. Họ đã đi đâu? Hiệu ứng có thể có của '-O3'? Như trong câu hỏi được cập nhật khi xây dựng với '-O0' lỗi sẽ biến mất. Lỗi GCC? – ritter

0

Nếu nó một ma trận, kiểm tra các chỉ số bạn đang cố gắng truy cập. Có lẽ bạn đang truy cập vào các yếu tố vượt quá kích thước của đối tượng Matrix?

4

Đó là tràn ngăn xếp.

eesu3 cố gắng bố trí một cái gì đó rất lớn trên stack, mà có thể được nhìn thấy trong mã lắp ráp của nó:

sub $0x99b000,%rsp 

Điều này có nghĩa nhiều hơn 10MB ngăn xếp không gian được tiêu thụ.

Sự cố có thể ở trong eesu3 hoặc một hàm mà hàm gọi và trình biên dịch chọn nội dòng.

đoán của tôi là vấn đề là trong một cuộc gọi chức năng eesu3, nhưng không phải trong trường hợp thử nghiệm bạn (một chức năng gỡ lỗi?)
Tôi đoán này vì nó không xảy ra mà không cần tối ưu hóa - với tối ưu hóa, chức năng được gạch chân vào eesu3, vì vậy eesu3 sử dụng nhiều ngăn xếp. Không có nó, hàm này không phải là nội tuyến, do đó bạn sẽ gặp vấn đề chỉ khi nó thực sự được gọi.

+0

Có, khả năng của nó là tràn ngăn xếp. Tôi hy vọng nó sẽ ổn cho bạn nếu @Emploed tiếng Nga được điểm. Ông là người đầu tiên chỉ ra rằng ra – ritter

+0

Vấn đề là không có điểm trong việc chỉ ra các điểm ra một lần nữa. – hochl

0

Bạn có thể có một số biến được khởi tạo trong chức năng

void eesu3(Matrix & iQ) 

allthough trình gỡ lỗi có thể bước qua khai báo biến, họ có thể khởi tạo với sự bắt đầu của phạm vi (có nghĩa là chức năng của bạn). Nếu bạn khai báo một bộ đệm rất lớn như vậy:

char * buffer[268435456]; 

Bạn có thể bị tràn ngăn xếp. Có thể tốt hơn là phân bổ một số bộ nhớ như

void * pvBuffer = malloc(268435456); 

Bạn đã khai báo bộ đệm lớn? Đó là quá lớn để đưa vào ngăn xếp? Nó có thể có nghĩa là các kiến ​​trúc khác nhau dẫn đến các kích thước tối đa có thể khác nhau cho các bộ đệm (hệ điều hành 64 bit và 32 bit)? Các hạt nhân khác nhau? Như bạn đã nói rằng chương trình chạy tốt trên một máy nhưng không chạy trên một máy khác.

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