💾 Archived View for gemini.strahinja.org › blog › 2022-08 › 20220831.gmi captured on 2023-07-22 at 16:25:25. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-06-16)
-=-=-=-=-=-=-
Радећи на писању mkfile-а за Neomutt, сусрео сам се са необичним проблемом. При покретању, Neomutt је крахирао уз SIGSEGV, а испитивањем у GDB-у установио сам да до грешке долази у фајлу gui/terminal.c, у линијама 62-63:
char *tcaps = tigetstr("tsl"); if (tcaps && (tcaps != (char *) -1) && *tcaps)
Наиме, вредност показивача tcaps је у моменту segmentation fault-а 0xfffffffff75de85c, што није адреса којој програми смеју приступати. Даље испитивање ме је довело до функције tigetstr(3) из NetBSD-curses-а:
const char * ti_getstr(const TERMINAL *term, const char *id) { ssize_t ind; size_t i; TERMUSERDEF *ud; => https://git.sr.ht/~strahinja/galeb-mkfiles/tree/master/item/neomutt/mkfile mkfile _DIAGASSERT(term != NULL); _DIAGASSERT(id != NULL); ind = _ti_strindex(id); if (ind != -1) return term->strs[ind]; // <-- ово се враћа... /* ... */ } char * tigetstr(const char *id) { _DIAGASSERT(id != NULL); if (cur_term != NULL) { char* ret = ti_getstr(cur_term, id); return __UNCONST(ret); // <-- ... и прослеђује // макроу __UNCONST } return (char *)CANCELLED_STRING; }
Адреса показивача term->strs[ind] који се враћа из интерне функције ti_getstr у функцију tigetstr je 0x7ffff75de85c, што је исправна адреса показивача, и у GDB-у се приказује као знаковна ниска. Она се после cast-овања макроом __UNCONST:
#define __UNCONST(a) (void *)(intptr_t)(a)
враћа и додељује већ поменутом показивачу tcaps у функцији mutt_ts_capability у Neomutt-у.
Међутим, ту долази до проблема: 0x7ffff75de85c одједном постаје 0xfffffffff75de85c. Зашто?
Испрва сам помислио да је кривац различита врста кода у односу на позицију (PIC - Position Independent Code), али поновно компајлирање NetBSD-curses-а и Neomutt-а уз -fPIC није ништа променило.
Затим сам покушао да направим мали тест програм, који би иницијализовао NetBSD-curses и позвао проблематичну функцију tigetstr. На моје изненађење, док је Neomutt проузроковао segmentation fault, тест програм је радио без проблема! Додавање још кода из Neomutt-а тест програму није променило ситуацију.
Оно што ми је коначно дало идеју шта је узрок проблема је следећи одговор на питање на StackOverflow-у:
channelGet() is assumed to return int (due to lack of prototype) [истакао СР], so the result is truncated to 0xf7fed1a0 then it is cast to a 64-bit pointer, so gets sign-extended to 0xfffffffff7fed1a0
„Претпоставља се да функција враћа int, због недостатка прототипа, па је резултат скраћен. Онда се cast-ује у 64-битни показивач, коме се продужава знак.“ Па наравно, недостатак прототипа! Функција tigetstr захтева укључивање заглавља term.h, које није било укључено са том путањом, већ као ncurses/term.h. Што се тиче самог прототипа, у овом случају он је сасвим једноставан, без struct-ова, али је његов недостатак ипак проузроковао SIGSEGV!
одговор на питање на StackOverflow-у
————————————————————
Епилог: Neomutt ипак захтева функције које недостају NetBSD-curses-у, па ћу за његово коришћење по свој прилици морати или да их сам напишем, или да пређем на гломазни ncurses, али сам из целе ствари имао прилике да научим нешто ново.