Problemy z kompilowaniem: Seqfault i Illegal Instruction

Ostatnio reorganizowałam i rekompilowałam wszystkie zewnętrzne zależności Nebula Graph - open-sourcowej rozproszonej grafowej bazy danych. Natknęłam się wtedy na dwie interesujące kwestie, którymi chciałabym się w Wami podzielić.
Segfault we Flex -- Segmentation fault (core dumped)
Segfault wystąpiło przy kompilowaniu Flex:
make[2]: Entering directory '/home/dutor/flex-2.6.4/src'
./stage1flex -o stage1scan.c ./scan.l
make[2]: *** [Makefile:1696: stage1scan.c] Segmentation fault (core dumped)
Sprawdziłam coredump
za pomocą gdb
:
Core was generated by `./stage1flex -o stage1scan.c ./scan.l'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 flexinit (argc=4, argv=0x7ffd25bea718) at main.c:976
976 action_array[0] = '\0';
(gdb) disas
Dump of assembler code for function flexinit:
0x0000556c1b1ae040 <+0>: push %r15
0x0000556c1b1ae042 <+2>: lea 0x140fd(%rip),%rax # 0x556c1b1c2146
...
0x0000556c1b1ae20f <+463>: callq 0x556c1b1af460 <allocate_array> # Allocate buffer
...
=> 0x0000556c1b1ae24f <+527>: movb $0x0,(%rax) # Write to buffer[0], failed due to illegal address
...
(gdb) disas allocate_array
Dump of assembler code for function allocate_array:
0x0000556c1b1af460 <+0>: sub $0x8,%rsp
0x0000556c1b1af464 <+4>: mov %rsi,%rdx
0x0000556c1b1af467 <+7>: xor %eax,%eax
0x0000556c1b1af469 <+9>: movslq %edi,%rsi
0x0000556c1b1af46c <+12>: xor %edi,%edi
0x0000556c1b1af46e <+14>: callq 0x556c1b19a100 <reallocarray@plt> # Allocate buffer
0x0000556c1b1af473 <+19>: test %eax,%eax # Check if the result pointer is NULL
0x0000556c1b1af475 <+21>: je 0x556c1b1af47e <allocate_array+30># Jump to error handler if NULL
0x0000556c1b1af477 <+23>: cltq # Extend eax to rax, truncated
0x0000556c1b1af479 <+25>: add $0x8,%rsp
0x0000556c1b1af47d <+29>: retq
...
End of assembler dump.
Widzimy z powyższego kodu asemblera, że problem został spowodowany przez funkcję alocate_array
. reallocarray
zwrócił wskaźnik, który należy zapisać w 64-bitowym rejestrze rax
. Jednak allocate_array
wywołało reallocarray
i zwróciło 32-bitowy rejestr eax
. W międzyczasie użyło instrukcji cltq
, aby rozszerzyć eax
do rax
.
Powodem może być to, że prototyp reallocarray
z punktu widzenia allocate_array
był inny niż prawdziwy prototyp. Patrząc na log kompilacji, znalazłam takie ostrzeżenie, jak implicit declaration of function reallocarray'
. Problem ten można rozwiązać, dodając CFLAGS = -D_GNU_SOURCE
na etapie konfiguracji. Ten problem nie pojawi się za każdym razem. Jednak właczenie opcji -pie
i parametru kernel.randomize_va_space
sprawia, że dużo łatwiej zreprodukować ten problem.
Czego się nauczyłam:
- Zwracany typ funkcji zadeklarowanej niejawnie to
int
w C. - Zwróć uwagę na ostrzeżenia kompilatora z włączoną opcją
-Wall
i-Wextra
. Lepiej włącz-Werror
w trybie deweloperskim.
GCC Illegal Instruction -- Internal Compiler Error: Illegal Instruction
Jakiś czas temu otrzymałam feedback od użytkowników Nebula Graph, że pojawił się błąd kompilatora: illegal instruction. Zobacz szczegóły w tym pull request.
Oto błąd:
Scanning dependencies of target base_obj_gch
[ 0%] Generating Base.h.gch
In file included from /opt/nebula/gcc/include/c++/8.2.0/chrono:40,
from /opt/nebula/gcc/include/c++/8.2.0/thread:38,
from /home/zkzy/nebula/nebula/src/common/base/Base.h:15:
/opt/nebula/gcc/include/c++/8.2.0/limits:1599:7: internal compiler error: Illegal instruction
min() _GLIBCXX_USE_NOEXCEPT { return FLT_MIN; }
^~~
0xb48c5f crash_signal
../.././gcc/toplev.c:325
Please submit a full bug report,
with preprocessed source if appropriate.
Ponieważ jest to wewnętrzny błąd kompilatora, zakładam, że nielegalna instrukcja została napotkana w samym g++. Aby zlokalizować określony zestaw tych instrukcji i komponent, do którego on należy, musimy zreproduktować błąd.
Na szczęście, poniższy fragment kodu dokonuje cudów:
#include <thread>
int main()
{
return 0;
}
Illegal instruction z pewnością wywoła SIGIL. Ponieważ g++ działa tylko jako wejście kompilatora, prawdziwym kompilatorem jest cc1plus. Możemy użyć gdb do wykonania kompilacji i wyłapania illegal instruction.
$ gdb --args /opt/nebula/gcc/bin/g++ test.cpp
gdb> set follow-fork-mode child
gdb> run
Starting program: /opt/nebula/gcc/bin/g++ test.cpp
[New process 31172]
process 31172 is executing new program: /opt/nebula/gcc/libexec/gcc/x86_64-pc-linux-gnu/8.2.0/cc1plus
Thread 2.1 "cc1plus" received signal SIGILL, Illegal instruction.
[Switching to process 31172]
0x00000000013aa0fb in __gmpn_mul_1 ()
gdb> disas
...
0x00000000013aa086 <+38>: mulx (%rsi),%r10,%r8
Bingo!
mulx
należy do zestawu instrukcji BMI2, a procesor maszyny, w której wystąpił błąd, nie obsługuje tego zestawu instrukcji. Po gruntownym dochodzeniu odkryłam, że to GMP (który jest jedną z zależności GCC) wprowadził ten zestaw instrukcji. Domyślnie GMP wykrywa typ procesora komputera hosta na etapie konfiguracji, aby wykorzystać najnowsze zestawy instrukcji, co poprawia wydajność przy jednoczesnym ograniczeniu przenośności binarki.
Aby rozwiązać ten problem, możesz spróbować nadpisać dwa pliki w drzewie źródłowym GMP, tj. config.guess
i config.sub
odpowiednio z _configfsf.guess_
i configfsf.sub
przed etapem konfiguracji.
Podsumowanie
- GCC domyślnie nie przyjmuje nowego zestawu instrukcji z powodu problemu z kompatybilnością.
- Aby zrównoważyć kompatybilność i wydajność, musisz wykonać dodatkową pracę.
Jeśli chcesz skompilować kod źródłowy Nebula Graph, to zapoznaj się z jego dokumentacją.