Saya memiliki jejak tumpukan berikut. Apakah mungkin untuk mengetahui sesuatu yang berguna dari ini untuk debugging?
Program received signal SIGSEGV, Segmentation fault.
0x00000002 in ?? ()
(gdb) bt
#0 0x00000002 in ?? ()
#1 0x00000001 in ?? ()
#2 0xbffff284 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb)
Di mana untuk mulai melihat kode ketika kita mendapatkan Segmentation fault
, dan pelacakan tumpukan tidak begitu berguna?
CATATAN: Jika saya memposting kode, maka pakar SO akan memberi saya jawabannya. Saya ingin mengambil panduan dari SO dan menemukan jawabannya sendiri, jadi saya tidak memposting kodenya di sini. Permintaan maaf.
-fno-omit-frame-pointer
? Juga, untuk kerusakan memori,valgrind
mungkin alat yang lebih tepat, jika itu pilihan untuk Anda.Jawaban:
Alamat palsu tersebut (0x00000002 dan sejenisnya) sebenarnya adalah nilai PC, bukan nilai SP. Sekarang, ketika Anda mendapatkan SEGV semacam ini, dengan alamat PC palsu (sangat kecil), 99% dari waktu itu karena panggilan melalui penunjuk fungsi palsu. Perhatikan bahwa panggilan virtual di C ++ diimplementasikan melalui pointer fungsi, sehingga masalah apa pun dengan panggilan virtual dapat bermanifestasi dengan cara yang sama.
Instruksi panggilan langsung hanya mendorong PC setelah panggilan ke stack dan kemudian menetapkan PC ke nilai target (palsu dalam kasus ini), jadi jika ini adalah apa yang terjadi, Anda dapat dengan mudah membatalkannya secara manual bermunculan PC dari tumpukan . Dalam kode x86 32-bit Anda cukup melakukan:
Dengan kode 64-bit x86 yang Anda butuhkan
Kemudian, Anda harus bisa melakukan
bt
dan mencari tahu di mana kode sebenarnya.1% lainnya dari waktu, kesalahan akan terjadi karena menimpa tumpukan, biasanya dengan melimpahkan array yang disimpan di tumpukan. Dalam kasus ini, Anda mungkin bisa mendapatkan kejelasan lebih lanjut tentang situasi tersebut dengan menggunakan alat seperti valgrind
sumber
gdb executable corefile
akan membuka gdb dengan file yang dapat dieksekusi dan inti, pada titik mana Anda dapat melakukannyabt
(atau perintah di atas diikuti olehbt
) ...sp
, bukanesp
ataursp
, dan instruksi panggilannya menyimpan alamat pengirim dilr
register, bukan di stack. Jadi untuk ARM, yang benar-benar Anda perlukan untuk membatalkan panggilan adalahset $pc = $lr
. Jika$lr
tidak valid, Anda memiliki masalah yang jauh lebih sulit untuk bersantai.Jika situasinya cukup sederhana, jawaban Chris Dodd adalah yang terbaik. Itu terlihat seperti itu melompat melalui pointer NULL.
Namun, mungkin saja program menembak dirinya sendiri di kaki, lutut, leher, dan mata sebelum menabrak — menimpa tumpukan, mengacaukan penunjuk bingkai, dan kejahatan lainnya. Jika demikian, maka mengungkap hash tidak akan menunjukkan kepada Anda kentang dan daging.
Solusi yang lebih efisien akan menjalankan program di bawah debugger, dan melangkahi fungsi hingga program macet. Setelah fungsi crashing diidentifikasi, mulai lagi dan masuk ke fungsi itu dan tentukan fungsi mana yang dipanggil yang menyebabkan crash. Ulangi sampai Anda menemukan satu baris kode yang menyinggung. 75% dari waktu, perbaikannya akan terlihat jelas.
Dalam 25% situasi lainnya, yang disebut baris kode yang menyinggung adalah red herring. Ini akan bereaksi terhadap kondisi (tidak valid) yang mengatur banyak baris sebelumnya — mungkin ribuan baris sebelumnya. Jika demikian, kursus terbaik yang dipilih bergantung pada banyak faktor: sebagian besar pemahaman Anda tentang kode dan pengalaman dengannya:
printf
pada variabel kritis akan menghasilkan A ha!Semoga berhasil!
sumber
Dengan asumsi bahwa penunjuk tumpukan valid ...
Mungkin tidak mungkin untuk mengetahui dengan tepat di mana SEGV terjadi dari backtrace - saya pikir dua frame tumpukan pertama sepenuhnya ditimpa. 0xbffff284 sepertinya alamat yang valid, tetapi dua alamat berikutnya tidak. Untuk melihat tumpukan lebih dekat, Anda dapat mencoba yang berikut ini:
gdb $ x / 32ga $ rsp
atau varian (ganti 32 dengan nomor lain). Itu akan mencetak beberapa kata (32) mulai dari penunjuk tumpukan ukuran raksasa (g), diformat sebagai alamat (a). Ketik 'bantuan x' untuk info lebih lanjut tentang format.
Menginstruksikan kode Anda dengan beberapa sentinel 'printf' mungkin bukan ide yang buruk, dalam kasus ini.
sumber
info symbol
bagaimana melakukan ini di gdb.x/256wa $sp
=)Lihat beberapa register Anda yang lain untuk melihat apakah salah satunya memiliki penunjuk tumpukan yang di-cache di dalamnya. Dari sana, Anda mungkin bisa mengambil tumpukan. Juga, jika ini disematkan, cukup sering tumpukan didefinisikan di alamat yang sangat khusus. Dengan menggunakan itu, terkadang Anda juga bisa mendapatkan tumpukan yang layak. Ini semua mengasumsikan bahwa ketika Anda melompat ke hyperspace, program Anda tidak muntah di seluruh memori sepanjang jalan ...
sumber
Jika itu adalah tumpukan yang ditimpa, nilainya mungkin sesuai dengan sesuatu yang dapat dikenali dari program.
Misalnya, saya baru saja melihat diri saya sendiri di tumpukan
dan
0x342d
13357, yang ternyata menjadi node-id ketika saya memahami log aplikasi untuk itu. Itu segera membantu mempersempit situs kandidat tempat penimpaan tumpukan mungkin terjadi.sumber