Важност прототипова
31.08.2022Радећи на писању 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; _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 returnint
(due to lack of prototype) [истакао СР], so the result is truncated to0xf7fed1a0
- 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!
Епилог: Neomutt ипак захтева функције које недостају NetBSD-curses‑у, па ћу за његово коришћење по свој прилици морати или да их сам напишем, или да пређем на гломазни ncurses, али сам из целе ствари имао прилике да научим нешто ново.