__Gxx_personality_v0 dùng để làm gì?


103

Đây là một câu hỏi cũ từ một trang web phát triển hệ điều hành, nhưng nó khiến tôi tò mò vì tôi không thể tìm thấy lời giải thích hợp lý ở đâu.

Khi biên dịch và liên kết một chương trình C ++ tồn tại bằng gcc, đôi khi xảy ra lỗi trình liên kết như thế này:

out/kernel.o:(.eh_frame+0x11): undefined reference to `__gxx_personality_v0'

Điều này rõ ràng là do biểu tượng này được định nghĩa trong libstdc ++, biểu tượng này bị thiếu trong môi trường tự do. Khắc phục sự cố chỉ cần xác định biểu tượng này ở đâu đó:

void *__gxx_personality_v0;

Đó là điều tốt đẹp, nhưng tôi không thích những thứ chỉ hoạt động một cách kỳ diệu ... Vậy câu hỏi đặt ra là, mục đích của biểu tượng này là gì?

Câu trả lời:


93

Nó được sử dụng trong các bảng mở ngăn xếp, ví dụ như bạn có thể thấy trong kết quả lắp ráp của câu trả lời của tôi cho một câu hỏi khác . Như đã đề cập trong câu trả lời đó, việc sử dụng nó được xác định bởi Itanium C ++ ABI , nơi nó được gọi là Quy trình tính cách .

Lý do nó "hoạt động" bằng cách định nghĩa nó như một con trỏ void NULL toàn cục có lẽ là vì không có gì ném ra một ngoại lệ. Khi một thứ gì đó cố gắng tạo ra một ngoại lệ, thì bạn sẽ thấy nó hoạt động sai.

Tất nhiên, nếu không có gì sử dụng ngoại lệ, bạn có thể tắt chúng bằng -fno-exceptions(và nếu không có gì đang sử dụng RTTI, bạn cũng có thể thêm -fno-rtti). Nếu bạn đang sử dụng chúng, bạn phải (như các câu trả lời khác đã được lưu ý) g++thay vì liên kết với gcc, điều này sẽ thêm -lstdc++cho bạn.


2
Cảm ơn cho mẹo về -fno-exceptions. Tôi đã thêm vào CPPFLAGS += -fno-exceptionstệp makefile của mình và điều đó đã giải quyết được lỗi.
Alan Kinnaman

12

Đó là một phần của xử lý ngoại lệ. Cơ chế gcc EH cho phép kết hợp các mô hình EH khác nhau và một quy trình tính cách được gọi để xác định xem có khớp ngoại lệ hay không, kết quả cuối cùng nào cần gọi, v.v. Quy trình tính cách cụ thể này dành cho việc xử lý ngoại lệ C ++ (trái ngược với gcj / Java xử lý ngoại lệ).


11

Xử lý ngoại lệ được bao gồm trong các triển khai thường trực.

Lý do của điều này là bạn có thể sử dụng gccđể biên dịch mã của mình. Nếu bạn biên dịch với tùy chọn, -###bạn sẽ nhận thấy nó thiếu tùy chọn trình liên kết -lstdc++khi nó gọi quy trình trình liên kết. Biên dịch với g++sẽ bao gồm thư viện đó, và do đó các ký hiệu được định nghĩa trong đó.


Tôi luôn nghĩ rằng việc biên dịch với g ++ chỉ cần thiết khi bạn đặc biệt muốn nói với trình biên dịch rằng mã là C ++ (ví dụ: thiếu phần mở rộng). Bây giờ có vẻ như việc biên dịch mã C ++ với gcc bỏ lỡ việc bao gồm các thư viện đi kèm. Ngoài việc thiếu một số thư viện, có một số "tác dụng phụ" khác của việc biên dịch của tôi file.cppvới gccthay vì g++?
Lazer

1
@eSkay theo như tôi biết, liên kết của libstdc++là sự khác biệt duy nhất giữa hai.
Johannes Schaub -

6

Một bản tóm tắt nhanh về libstd++cơ sở mã đã tiết lộ hai cách sử dụng sau đây của __gx_personality_v0:

Trong libsupc ++ / unwind-cxx.h

// GNU C++ personality routine, Version 0.                                      
extern "C" _Unwind_Reason_Code __gxx_personality_v0
     (int, _Unwind_Action, _Unwind_Exception_Class,
      struct _Unwind_Exception *, struct _Unwind_Context *);

Trong libsupc ++ / eh_personality.cc

#define PERSONALITY_FUNCTION    __gxx_personality_v0
extern "C" _Unwind_Reason_Code
PERSONALITY_FUNCTION (int version,
                      _Unwind_Action actions,
                      _Unwind_Exception_Class exception_class,
                      struct _Unwind_Exception *ue_header,
                      struct _Unwind_Context *context)
{
  // ... code to handle exceptions and stuff ...
}

(Lưu ý: nó thực sự phức tạp hơn một chút; có một số biên dịch có điều kiện có thể thay đổi một số chi tiết).

Vì vậy, miễn là mã của bạn không thực sự sử dụng xử lý ngoại lệ, việc xác định ký hiệu void*sẽ không ảnh hưởng đến bất cứ điều gì, nhưng ngay sau khi nó xảy ra, bạn sẽ gặp sự cố - __gxx_personality_v0là một hàm, không phải một số đối tượng toàn cục, vì vậy hãy thử để gọi hàm sẽ chuyển đến địa chỉ 0 và gây ra lỗi mặc định.


Không nhất thiết phải nhảy đến 0; toàn cầu chưa được khởi tạo nên nó thực sự có thể là bất kỳ giá trị nào.
strager

6
strager, globals được không khởi tạo nếu các lập trình viên không khởi tạo chúng
Johannes Schaub - litb

@litb: điều này chỉ đúng nếu kernel thực hiện zeroing phần bss :-P. Nhưng có, chúng nên được khởi tạo bằng 0 vì lợi ích của sự tỉnh táo.
Evan Teran

9
@Evan Teran: Không, việc triển khai C tuân thủ sẽ luôn khởi tạo các hình cầu bằng 0. Xem §5.1.2 và §6.7.8 đoạn 10 của tiêu chuẩn C99.
Adam Rosenfield

6

Tôi đã gặp lỗi này một lần và tôi đã tìm ra nguyên nhân:

Tôi đang sử dụng trình biên dịch gcc và tệp của tôi được gọi CLIENT.Cmặc dù tôi đang làm chương trình C chứ không phải chương trình C ++.

gcc nhận dạng .Cphần mở rộng là chương trình C ++ và .cphần mở rộng là chương trình C (hãy cẩn thận với chữ C nhỏ và C lớn).

Vì vậy, tôi đã đổi tên CLIENT.cchương trình tệp của mình và nó đã hoạt động.


2

Các câu trả lời trên đều đúng: nó được sử dụng trong xử lý ngoại lệ. Các nhãn hiệu cho GCC phiên bản 6 có thêm thông tin (mà không còn hiện diện trong phiên bản 7 bằng tay). Lỗi có thể phát sinh khi liên kết một chức năng bên ngoài - không xác định với GCC - ném các ngoại lệ Java.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.