Untuk apa __gxx_personality_v0?

103

Ini adalah pertanyaan bekas dari situs pengembangan OS, tetapi membuat saya penasaran karena saya tidak dapat menemukan penjelasan yang layak di mana pun.

Saat mengompilasi dan menautkan program C ++ yang berdiri sendiri menggunakan gcc, terkadang kesalahan penaut seperti ini terjadi:

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

Ini tampaknya karena simbol ini didefinisikan di libstdc ++, yang tidak ada di lingkungan yang berdiri sendiri. Memperbaiki masalah hanya perlu mendefinisikan simbol ini di suatu tempat:

void *__gxx_personality_v0;

Mana yang bagus, tapi saya tidak suka hal-hal yang bekerja secara ajaib ... Jadi pertanyaannya adalah, apa tujuan dari simbol ini?

Bruce Johnston
sumber

Jawaban:

93

Ini digunakan dalam tabel stack unwiding, yang dapat Anda lihat misalnya dalam output assembly dari jawaban saya untuk pertanyaan lain . Seperti yang disebutkan pada jawaban tersebut, penggunaannya ditentukan oleh Itanium C ++ ABI , yang disebut Personality Routine .

Alasan "bekerja" dengan mendefinisikannya sebagai penunjuk kosong NULL global mungkin karena tidak ada yang memberikan pengecualian. Ketika sesuatu mencoba untuk membuat pengecualian, maka Anda akan melihatnya berperilaku tidak baik.

Tentu saja, jika tidak ada yang menggunakan pengecualian, Anda dapat menonaktifkannya dengan -fno-exceptions(dan jika tidak ada yang menggunakan RTTI, Anda juga dapat menambahkan -fno-rtti). Jika Anda menggunakannya, Anda harus (seperti jawaban lain yang telah dicatat) menautkan g++alih - alih gcc, yang akan menambahkan -lstdc++untuk Anda.

CesarB
sumber
2
Terima kasih atas tipnya -fno-exceptions. Saya menambahkan CPPFLAGS += -fno-exceptionske makefile saya, dan itu menyelesaikan kesalahan.
Alan Kinnaman
12

Itu bagian dari penanganan pengecualian. Mekanisme gcc EH memungkinkan untuk mencampur berbagai model EH, dan rutinitas kepribadian dipanggil untuk menentukan apakah pengecualian cocok, finalisasi apa yang akan dipanggil, dll. Rutinitas kepribadian khusus ini untuk penanganan pengecualian C ++ (sebagai lawan, katakanlah, gcj / Java penanganan pengecualian).

Martin v. Löwis
sumber
11

Penanganan pengecualian termasuk dalam implementasi berdiri bebas.

Alasannya adalah bahwa Anda mungkin menggunakan gccuntuk mengkompilasi kode Anda. Jika Anda mengkompilasi dengan opsi, -###Anda akan melihat opsi linker hilang -lstdc++saat menjalankan proses linker. Mengompilasi dengan g++akan menyertakan pustaka itu, dan dengan demikian simbol yang ditentukan di dalamnya.

Johannes Schaub - litb
sumber
Saya selalu berpikir kompilasi dengan g ++ hanya diperlukan ketika Anda secara khusus ingin memberi tahu kompiler bahwa kodenya adalah C ++ (misalnya - ekstensi hilang). Sekarang tampaknya mengompilasi kode C ++ dengan gcc melewatkan penyertaan pustaka come. Selain beberapa pustaka yang hilang, apakah ada "efek samping" lain dari kompilasi my file.cppwith, gccbukan g++?
Lazer
1
@eSkay sejauh yang saya tahu, penautan libstdc++adalah satu-satunya perbedaan antara keduanya.
Johannes Schaub - litb
6

Sebuah grep cepat dari libstd++basis kode mengungkapkan dua penggunaan berikut __gx_personality_v0:

Di 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 *);

Di 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 ...
}

(Catatan: ini sebenarnya sedikit lebih rumit dari itu; ada beberapa kompilasi bersyarat yang dapat mengubah beberapa detail).

Jadi, selama kode Anda tidak benar-benar menggunakan penanganan pengecualian, mendefinisikan simbol sebagai void*tidak akan memengaruhi apa pun, tetapi begitu itu terjadi, Anda akan macet - __gxx_personality_v0adalah sebuah fungsi, bukan objek global, jadi cobalah memanggil fungsi tersebut akan melompat ke alamat 0 dan menyebabkan segfault.

Adam Rosenfield
sumber
Tidak harus melompat ke 0; global tidak diinisialisasi sehingga bisa menjadi nilai apa pun, sungguh.
strager
6
strager, global adalah nol diinisialisasi jika programmer tidak menginisialisasi mereka
Johannes Schaub - litb
@ litb: ini hanya berlaku jika kernel mengimplementasikan zeroing bagian bss :-P. Tapi ya, mereka harus diinisialisasi 0 demi kewarasan.
Evan Teran
9
@ Evan Teran: Tidak, implementasi C yang sesuai akan selalu menginisialisasi global ke 0. Lihat §5.1.2 dan §6.7.8 paragraf 10 dari standar C99.
Adam Rosenfield
6

Saya mengalami kesalahan ini sekali dan saya menemukan asalnya:

Saya menggunakan kompiler gcc dan file saya dipanggil CLIENT.Cmeskipun saya menggunakan program C dan bukan program C ++.

gcc mengenali .Cekstensi sebagai program C ++ dan .cekstensi sebagai program C (hati-hatilah dengan c kecil dan C besar).

Jadi saya mengganti nama CLIENT.cprogram file saya dan itu berhasil.

jlguenego.dll
sumber
2

Jawaban di atas benar: ini digunakan dalam penanganan pengecualian. The pengguna untuk GCC versi 6 memiliki informasi lebih lanjut (yang tidak lagi hadir dalam versi 7 manual). Kesalahan dapat muncul saat menautkan fungsi eksternal yang - tidak diketahui GCC - memunculkan pengecualian Java.

sagitta
sumber