21.04.20204 min

Jamie Liu

Problemy z kompilowaniem: Seqfault i Illegal Instruction

Zobacz jak debugguje się błędy kompilacji w C i do jakich wniosków może to doprowadzić, a to wszystko na przykładzie dwóch błędów: segfault i illegal instruction.

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ą.

<p>Loading...</p>

Powiązane artykuły

Dziel się wiedzą ze 160 tysiącami naszych czytelników

Zostań autorem Readme

Ubezpieczeniowy Fundusz Gwarancyjny

Specjalista ds. testów

medium

Znamy widełki

Kontrakt B2BUmowa o pracę

Warszawa

Ważna do 27.02.2022

Dobrze
JIRASoapUIOracle Database
Początkujący
FitNesseJMeter

Sii Polska

DevOps Engineer

medium

17 000 - 24 000 PLN

Kontrakt B2B

Warszawa

Ważna do 27.02.2022

Dobrze
AWSGCPGitHub
Bardzo dobrze
Linux

Accenture Polska

Agile Project Manager

medium

Brak widełek

Kontrakt B2BUmowa o pracę

Warszawa

Praca zdalna 100%

Ważna do 27.02.2022

Bardzo dobrze
Agile Methodologies (Scrum, Kanban, Kaizen)
Dobrze
Team-building & people management
Początkujący
cloud fundamentals

DB Schenker Technology Center Warsaw

Business Analyst

medium

Brak widełek

Kontrakt B2BUmowa o pracę

Warszawa

Ważna do 27.02.2022

Accenture Polska

PHP/Magento Developer

medium

Brak widełek

Kontrakt B2BUmowa o pracę

Praca zdalna 100%

Ważna do 27.02.2022

Bardzo dobrze
PHPGIT
Dobrze
Magento 2SOAP/REST HTML/CSS
Początkujący
JSONJavaScript jQuery

T-Mobile Polska S. A.

Inżynier Sieciowy

medium

Brak widełek

Kontrakt B2B

Warszawa

Ważna do 27.02.2022

Asseco Poland S.A.

Młodszy Analityk Finansowy

junior

Brak widełek

Umowa o pracę

Rzeszów

Ważna do 27.02.2022

Netguru

Junior iOS Developer

junior

4 200 - 6 000 PLN

Kontrakt B2BUmowa o pracę

Praca zdalna 100%

Ważna do 27.02.2022

Dobrze
iOS

7N

Data Engineer

medium

15 100 - 18 100 PLN

Kontrakt B2B

Praca zdalna 100%

Ważna do 27.02.2022

Dobrze
MS SQLETLSpark

7N

Senior Business Analyst

senior

16 800 - 21 800 PLN

Kontrakt B2B

Praca zdalna 100%

Ważna do 27.02.2022