A summary of the causes and debugging methods of segment errors in Linux environment

  • 2020-04-01 21:27:06
  • OfStack

Recently, I have been working on a C language project under Linux environment. Due to the secondary development on the basis of an original project and the huge and complex project, there are many problems, among which the famous Segmentation Fault is the most encountered and takes the longest time. Took this opportunity to learn the system, here on the Linux environment to make a summary of the segment error, for the convenience of similar problems in the future troubleshooting and solution.

1. What are paragraph errors

In a nutshell, a segment error is an instance of accessing more memory than the system assigned to the program, such as a memory address that does not exist, a memory address that is protected by the system, a memory address that is read-only, and so on. Here's an accurate definition of "paragraph error" (see Answers.com) :

A segmentation fault (, shortened to segfault) under these conditions is A particular error condition that can occur during the operation of computer software. In short, A segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed (e.g., Attempts to write to a read-only location, or to overwrite part of the operating system). Systems based on processors like the Motorola 68000 tend to refer to these events as Address or Bus errors.

Segmentation is one approach to memory management and protection in the operating system. It has been superseded by paging for most purposes, But much of the terminology of segmentation is still used, "Segmentation fault" being an example. Some operating systems still have segmentation at Some logical level although paging is used as the main memory management policy.

On Unix - like operating systems, a process that accesses invalid memory receives the SIGSEGV signal. On Microsoft Windows, A process that accesses invalid memory receives the STATUS_ACCESS_VIOLATION exception.

2. Reasons for paragraph errors
2.1 access to a memory address that does not exist
 
#include<stdio.h> 
#include<stdlib.h> 
void main() 
{ 
int *ptr = NULL; 
*ptr = 0; 
} 

2.2 access the memory address protected by the system
 
#include<stdio.h> 
#include<stdlib.h> 
void main() 
{ 
int *ptr = (int *)0; 
*ptr = 100; 
} 

2.3 access to read-only memory addresses
 
#include<stdio.h> 
#include<stdlib.h> 
#include<string.h> 
void main() 
{ 
char *ptr = "test"; 
strcpy(ptr, "TEST"); 
} 

2.4 stack overflow
 
#include<stdio.h> 
#include<stdlib.h> 
void main() 
{ 
main(); 
} 

And so on.

3. Section error information acquisition
When a segment error occurs in the program, the prompt message is very few, there are several ways to view the occurrence of segment error information.

3.1 dmesg
Dmesg displays the information stored in the kernel when the application crashes. As shown below, the dmesg command allows you to view the program name that caused the segment error, the memory address that caused the segment error, the instruction pointer address, the stack pointer address, the error code, the reason for the error, and so on. Take procedure 2.3 as an example:

Panfeng @ ubuntu: ~ / $dmesg segfault
[2329.479037] segfault3[2700]: segfault at 80484e0 IP 00d2906a sp bfbbec3c error 7 in libc-2.10.1.so[cb4000+13e000]
3.2 g
When compiling the source code of a program using GCC, add the -g parameter, which adds useful information for GDB debugging to the generated binaries. Take procedure 2.3 as an example:

Panfeng @ubuntu:~/segfault$gcc-g-o segfault3 segfault3.c
3.3 nm
Use the nm command to list the symbol table in the binary file, including the symbol address, symbol type, symbol name, and so on, to help locate where the segment error occurred. Take procedure 2.3 as an example:
 
panfeng@ubuntu:~/segfault$ nm segfault3 
08049f20 d _DYNAMIC 
08049ff4 d _GLOBAL_OFFSET_TABLE_ 
080484dc R _IO_stdin_used 
w _Jv_RegisterClasses 
08049f10 d __CTOR_END__ 
08049f0c d __CTOR_LIST__ 
08049f18 D __DTOR_END__ 
08049f14 d __DTOR_LIST__ 
080484ec r __FRAME_END__ 
08049f1c d __JCR_END__ 
08049f1c d __JCR_LIST__ 
0804a014 A __bss_start 
0804a00c D __data_start 
08048490 t __do_global_ctors_aux 
08048360 t __do_global_dtors_aux 
0804a010 D __dso_handle 
w __gmon_start__ 
0804848a T __i686.get_pc_thunk.bx 
08049f0c d __init_array_end 
08049f0c d __init_array_start 
08048420 T __libc_csu_fini 
08048430 T __libc_csu_init 
U __libc_start_main@@GLIBC_2.0 
0804a014 A _edata 
0804a01c A _end 
080484bc T _fini 
080484d8 R _fp_hw 
080482bc T _init 
08048330 T _start 
0804a014 b completed.6990 
0804a00c W data_start 
0804a018 b dtor_idx.6992 
080483c0 t frame_dummy 
080483e4 T main 
U memcpy@@GLIBC_2.0 

3.4 LDD
Use the LDD command to view the binaries' Shared link library dependencies, including the library's name and starting address, to determine whether the segment error occurred in your own program or in a dependent Shared library. Take procedure 2.3 as an example:
 
panfeng@ubuntu:~/segfault$ ldd ./segfault3 
linux-gate.so.1 => (0x00e08000) 
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00675000) 
/lib/ld-linux.so.2 (0x00482000) 

4. Section error debugging method
4.1 print the information using printf
This is the simplest but often most effective way of debugging, and probably the most used by programmers. In simple terms, you add output like printf near important code in your program, so you can track and print out where a segment of error might be in your code.

To facilitate this approach, the printf function can be wrapped with the conditional compilation instructions #ifdef DEBUG and #endif. In this way, when the program is compiled, if you add the -ddebug parameter, you can see the debugging information; Otherwise, debug information will not be displayed without this parameter.

4.2 use GCC and GDB
4.2.1 debugging steps
1. In order to be able to use GDB debugger, add -g parameter in the compilation stage, take program 2.3 as an example:

Panfeng @ubuntu:~/segfault$gcc-g-o segfault3 segfault3.c
2. Use GDB command to debug the program:
 
panfeng@ubuntu:~/segfault$ gdb ./segfault3 
GNU gdb (GDB) 7.0-ubuntu 
Copyright (C) 2009 Free Software Foundation, Inc. 
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> 
This is free software: you are free to change and redistribute it. 
There is NO WARRANTY, to the extent permitted by law. Type "show copying" 
and "show warranty" for details. 
This GDB was configured as "i486-linux-gnu". 
For bug reporting instructions, please see: 
<http://www.gnu.org/software/gdb/bugs/>... 
Reading symbols from /home/panfeng/segfault/segfault3...done. 
(gdb) 

3. After entering GDB, run the program:
 
(gdb) run 
Starting program: /home/panfeng/segfault/segfault3 

Program received signal SIGSEGV, Segmentation fault. 
0x001a306a in memcpy () from /lib/tls/i686/cmov/libc.so.6 
(gdb) 

See from the output of the program 2.3 received a SIGSEGV signal, trigger errors, and hint at address 0 x001a306a, call memcpy fault, located in the/lib/TLS/i686 / cmov/libc. So. 6 in the library.

4. After debugging, enter the quit command to exit GDB:
 
(gdb) quit 
A debugging session is active. 

Inferior 1 [process 3207] will be killed. 

Quit anyway? (y or n) y 

4.2.2 applicable scenarios
1. Only if it is certain that segment errors will occur.

2, when the source of the program is available, use the -g parameter to compile the program.

3. Generally used in the test phase, GDB will have side effects in the production environment: slow down the program, unstable operation, etc.

4. Even in the test phase, if the program is too complex, GDB cannot handle it.

4.3 use core files and GDB
In section 4.2, it is mentioned that segment error will trigger SIGSEGV signal. Through man 7 signal, it can be seen that the default handler of SIGSEGV will print segment error information and generate a core file. Therefore, we can use GDB tool to debug segment error in the program by means of debugging information in the core file generated when the program exits in an exception.

4.3.1 debugging steps
1. In some Linux versions, core files are not generated by default. First, you can check the size limit of system core files:

Panfeng @ ubuntu: ~ / segfault $ulimit -c
0
2. It can be seen that under the default setting, when segment error occurs in the native Linux environment, the core file will not be automatically generated.

Panfeng @ ubuntu: ~ / segfault $ulimit -c 1024
Panfeng @ ubuntu: ~ / segfault $ulimit -c
1024
3. Run program 2.3 and generate core file by segment error:

Panfeng @ ubuntu: ~ / segfault $. / segfault3
Core dumped
4. Load the core file and use the GDB tool for debugging:
 
panfeng@ubuntu:~/segfault$ gdb ./segfault3 ./core 
GNU gdb (GDB) 7.0-ubuntu 
Copyright (C) 2009 Free Software Foundation, Inc. 
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> 
This is free software: you are free to change and redistribute it. 
There is NO WARRANTY, to the extent permitted by law. Type "show copying" 
and "show warranty" for details. 
This GDB was configured as "i486-linux-gnu". 
For bug reporting instructions, please see: 
<http://www.gnu.org/software/gdb/bugs/>... 
Reading symbols from /home/panfeng/segfault/segfault3...done. 

warning: Can't read pathname for load map:  The input / The output error . 
Reading symbols from /lib/tls/i686/cmov/libc.so.6...(no debugging symbols found)...done. 
Loaded symbols for /lib/tls/i686/cmov/libc.so.6 
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done. 
Loaded symbols for /lib/ld-linux.so.2 
Core was generated by `./segfault3'. 
Program terminated with signal 11, Segmentation fault. 
#0 0x0018506a in memcpy () from /lib/tls/i686/cmov/libc.6 

The output shows the same segment error message as in 4.2.1.

5. After debugging, enter the quit command to exit GDB:

The quit (GDB)
4.3.2 applicable scenarios
1, suitable for debugging the program in the actual build environment segment error (that is, without the recurrence of the segment error).

2. This method is not available when the program is complex and the core file is large.

4.4 using objdump
4.4.1 debugging steps
1. Use the dmesg command to find the recently occurred segment error output information:

Panfeng @ ubuntu: ~ / $dmesg segfault
. .
[17257.502808] segfault3[3320]: segfault at 80484e0 IP 0018506a sp bfc1cd6c error 7 in libc-2.10.1.so[110000+13e000]
Among them, useful for our next debugging process is the address where the segment error occurred: 80484e0 and the instruction pointer address: 0018506a.

2. Use objdump to generate binary information and redirect to the file:

Panfeng @ubuntu:~/segfault$objdump -d./segfault3 > segfault3Dump
The generated segfault3Dump file contains the assembly code for the binary file segfault3.

3. Find the address of the segment fault in the segfault3Dump file:
 
panfeng@ubuntu:~/segfault$ grep -n -A 10 -B 10 "80484e0" ./segfault3Dump 
121- 80483df: ff d0 call *%eax 
122- 80483e1: c9 leave 
123- 80483e2: c3 ret 
124- 80483e3: 90 nop 
125- 
126-080483e4 <main>: 
127- 80483e4: 55 push %ebp 
128- 80483e5: 89 e5 mov %esp,%ebp 
129- 80483e7: 83 e4 f0 and $0xfffffff0,%esp 
130- 80483ea: 83 ec 20 sub $0x20,%esp 
131: 80483ed: c7 44 24 1c e0 84 04 movl $0x80484e0,0x1c(%esp) 
132- 80483f4: 08 
133- 80483f5: b8 e5 84 04 08 mov $0x80484e5,%eax 
134- 80483fa: c7 44 24 08 05 00 00 movl $0x5,0x8(%esp) 
135- 8048401: 00 
136- 8048402: 89 44 24 04 mov %eax,0x4(%esp) 
137- 8048406: 8b 44 24 1c mov 0x1c(%esp),%eax 
138- 804840a: 89 04 24 mov %eax,(%esp) 
139- 804840d: e8 0a ff ff ff call 804831c <memcpy@plt> 
140- 8048412: c9 leave 
141- 8048413: c3 ret 

Through the above assembly code analysis, it is known that segment error occurred main function, the corresponding assembly instruction is movl $0x80484e0,0x1c(%esp), next open the source code of the program, find the source code corresponding to the assembly instruction, also locate the segment error.

4.4.2 applicable scenarios
1, do not need the -g parameter compilation, do not need to rely on the core file, but need to have a certain assembly language foundation.

2. If GCC is used to compile optimization parameters (-o1, -o2, -o3), the generated assembly instructions will be optimized, making the debugging process difficult.

4.5 use catchsegv
The catchsegv command is specifically designed to catch segment errors by loading a prewritten library (/lib/ libsegfault.so) through the PRELOAD mechanism of the dynamic loader (ld-linux.so) to catch the error message of the broken fault.
 
panfeng@ubuntu:~/segfault$ catchsegv ./segfault3 
Segmentation fault (core dumped) 
*** Segmentation fault 
Register dump: 

EAX: 00000000 EBX: 00fb3ff4 ECX: 00000002 EDX: 00000000 
ESI: 080484e5 EDI: 080484e0 EBP: bfb7ad38 ESP: bfb7ad0c 

EIP: 00ee806a EFLAGS: 00010203 

CS: 0073 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b 

Trap: 0000000e Error: 00000007 OldMask: 00000000 
ESP/signal: bfb7ad0c CR2: 080484e0 

Backtrace: 
/lib/libSegFault.so[0x3b606f] 
??:0(??)[0xc76400] 
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xe89b56] 
/build/buildd/eglibc-2.10.1/csu/../sysdeps/i386/elf/start.S:122(_start)[0x8048351] 

Memory map: 

00258000-00273000 r-xp 00000000 08:01 157 /lib/ld-2.10.1.so 
00273000-00274000 r--p 0001a000 08:01 157 /lib/ld-2.10.1.so 
00274000-00275000 rw-p 0001b000 08:01 157 /lib/ld-2.10.1.so 
003b4000-003b7000 r-xp 00000000 08:01 13105 /lib/libSegFault.so 
003b7000-003b8000 r--p 00002000 08:01 13105 /lib/libSegFault.so 
003b8000-003b9000 rw-p 00003000 08:01 13105 /lib/libSegFault.so 
00c76000-00c77000 r-xp 00000000 00:00 0 [vdso] 
00e0d000-00e29000 r-xp 00000000 08:01 4817 /lib/libgcc_s.so.1 
00e29000-00e2a000 r--p 0001b000 08:01 4817 /lib/libgcc_s.so.1 
00e2a000-00e2b000 rw-p 0001c000 08:01 4817 /lib/libgcc_s.so.1 
00e73000-00fb1000 r-xp 00000000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so 
00fb1000-00fb2000 ---p 0013e000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so 
00fb2000-00fb4000 r--p 0013e000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so 
00fb4000-00fb5000 rw-p 00140000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so 
00fb5000-00fb8000 rw-p 00000000 00:00 0 
08048000-08049000 r-xp 00000000 08:01 303895 /home/panfeng/segfault/segfault3 
08049000-0804a000 r--p 00000000 08:01 303895 /home/panfeng/segfault/segfault3 
0804a000-0804b000 rw-p 00001000 08:01 303895 /home/panfeng/segfault/segfault3 
09432000-09457000 rw-p 00000000 00:00 0 [heap] 
b78cf000-b78d1000 rw-p 00000000 00:00 0 
b78df000-b78e1000 rw-p 00000000 00:00 0 
bfb67000-bfb7c000 rw-p 00000000 00:00 0 [stack] 

5. Some DOS and don 'ts
1. When segment error occurs, the definition of segment error should be considered first and the cause of the error should be considered from it.

2, when using the pointer, remember to initialize the pointer after defining the pointer, remember to judge whether it is NULL when using the pointer.

3. When using an array, pay attention to whether the array is initialized, whether the subscript of the array is out of bounds, whether the array elements exist, etc.

4. When accessing variables, notice whether the address space occupied by variables has been freed by the program.

5. When dealing with variables, pay attention to whether the format control of variables is reasonable, etc.

6. List of resources
1, http://www.docin.com/p-105923877.html

2, http://blog.chinaunix.net/space.php? Uid = 317451 & do = blog&id = 92412

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Author: the great circle things (formerly climbing peak @taobao)
Technical points: architecture design, database, distributed computing and storage, Hadoop, C, Erlang, design patterns
Email:ypf412@163.com
MSN:ypf412@hotmail.com
Blog: http://www.cnblogs.com/panfeng412/
Weibo: http://weibo.com/moudayuan/

Related articles: