Cómo depurar los controladores de red de Linux
Tomamos prestado el programa hola mundo en el módulo Controlador de dispositivo Linux Parte 2: construcción y ejecución para demostrar la situación de error. El programa hola mundo que contiene el código de error es el siguiente. sigue:
1
2
3
4
5
6
7
8
9
10
11
12 p>
13
14
15
16
17
18
19
p> #include
#include
MODULE_LICENSE("BSD dual /GPL");
static int hello_init( void)
{
char *p = NULL;
memcpy(p, "prueba", 4);
printk(KERN_ALERT "Hola, mundo\n");
devuelve 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Adiós, mundo cruel\n");
}
module_init(hello_init);
module_exit(hello_exit);
El Makefile es el siguiente:
1
2
3
4
5
6
7
8
9
10
11
ifneq ($(KERNELRELEASE),)
obj-m := holamundo.o
else
KERNELDIR ? = /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
predeterminado:
$(MAKE ) -C $(KERNELDIR) M=$(PWD) módulos
endif
clean:
rm - rf *.o *~ core .depend . *.cmd *.ko *.mod.c .tmp_versions module.order Module.symvers
Obviamente, la línea 8 del código anterior es un error de puntero nulo.
insmod generará el siguiente mensaje OOPS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[ 459.516490] CR2: 000000000000 CR3: 0000000038760000 CR4: 00000000003407f0
[ 459.516522] DR0: 000000000000 DR1.00000 0000000000 DR2: 0000000000000000
[ 459.516524] DR3: 000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[ 459.516524] Pila:
[ 459.516537] ffff880038fa3db8 ff810021 44 000000000001 000000000001
[ 459.516540] 0000000000000001 ffff880028ab5040 0000000000000001 ffff880038fa3da0 p>
[ 459.516541] ffffffff8119d0b2 ffffffffc0616018 00000000bd1141ac ffffffffffc0616018
[ 459.516543] rastro. 516543] Seguimiento de llamadas:
[ 459.516548] [
[ 459.516550] [
[ 459.516554] [
[ 459.516557] [
[ 459.516576] CR2: 000000000000
[ 459.516578] ----[ end trace 7c52cc8624b7ea60 ]----
El siguiente es el ups Un breve análisis del contenido del mensaje.
ERROR: no se puede manejar la desreferencia del puntero NULL del kernel en (nulo) muestra que la causa del error es el uso de un puntero nulo. El seguimiento de llamadas enumera información de llamadas a funciones. La información en rojo es la más útil y se puede utilizar para encontrar las líneas de código específicas que causan OOPS. A continuación se explica cómo localizar la línea de código específica donde se produce el problema.
En el primer paso, necesitamos usar objdump para desensamblar el archivo bin generado por el compilador, es decir, helloworld.o. Use el siguiente comando para guardar la información de desensamblado en el archivo err.txt:
1
objdump helloworld.o -D > err.txt
El contenido de Objdump Helloworld.txt es el siguiente:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
helloworld.o: Formato de archivo elf64-x86-64
Desmontaje de la sección. texto:
< span style="color:#ff0000;">000000000000
0: e8 00 00 00 00 callq 5 5: 55 push %rbp 6: 48 C7 C7 00 00 00 00 mov $0x0,%rdi D: C7 04 25 00 00 00 movl $0x74736574,0x0 14: 74 65 73 74 18: 31 c0 xor %eax,%eax 1a: 48 89 e5 mov % rsp,%rbp 1d: e8 00 00 00 callq 22 22:31 c0 xor %eax,%eax > 24: 5d pop %rbp 25: c3 retq 26: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) p> 2d: 00 00 000000000030 30: e8 00 00 00 callq 35 35: 55 push %rbp 36: 48 c7 c7 00 00 00 mov $0x0,%rdi 3d: 31 c0 xor %eax,%eax 3f: 48 89 e5 mov %rsp,%rbp 42: e8 00 00 00 callq 47 47: 5d pop %rbp 48: c3 retq Desmontaje part.rodata.str1.1: 0000000000000000 <.rodata.str1.1>: 0: 01 31 añadir %esi,(%rcx) 2: 48 rex. 3: 65 gs 4: 6c insb (%dx),% es :(%rdi) 5: 6c insb (%dx),%es:(%rdi) 6: 6f outsl %ds:(%rsi),(% dx ) 7: 2c 20 sub $0x20,%al< 9: 77 6f ja 7a B: 72 6c jb 79 D: 64 0a 00 o %fs:(%rax),%al 10: 01 31 agregar %esi,(%rcx) p > 12: 47 6f rex.RXB fuera de %ds:(%rsi),(%dx) 14: 6f fuera de %ds:(%rsi),(%dx) p > 15:64 fs 16:62 (malo) 17:79 65 jns 7e 19:2c 20 sub $0x20,%al 1b: 63 72 75 movslq 0x75(%rdx),%esi 1e: 65 GS 1F: 6c insb ( %dx),%es:(%rdi) 20: 20 77 6f y %dh,0x6f(%rdi) 23: 72 6c jb 91 25:64 0a 00 o %fs:(%rax),%al Desmontaje de la sección .modinfo: 000000000000 <__UNIQUE_ID_license0>: p> 0:6c insb (%dx),%es:(%rdi) 1:69 63 65 6e 73 65 3d imul $0x3d65736e,0x 65(%rbx),%esp 8: 44 75 61 rex.R jne 6c b: 6c insb (%dx),%es:( %rdi) c: 20 42 53 y %al,0x53(%rdx) f: 44 2f rex.R (malo) 11: 47 50 rex.RXB push %r8 13: 4c rex.WR ... Desmontaje sección .comentario: 0000000000000000 <.comment>: 0: 00 47 43 agregar %al,0x43(%rdi) 3: 43 3a 20 rex.RXB empujar %r8 11: 47 50 rex.RXB empujar %r8 13: 4c rex.WR ...cmp (%r8),%spl 6: 28 55 62 sub %dl,0x62(%rbp) 9: 75 6e jne 79 B: 74 75 y 82 D: 20 34 2e y %dh,(%rsi,%rbp,1) 10:38 2e cmp %ch,(%rsi) 12: 32 2d 31 39 75 62 xor 0x62753931(%rip),%ch # 62753949 < módulo_limpieza+0x62753919> 18: 75 6e jne 88 1a: 74 75 je 91 1c.31 29 xor %ebp,(%rcx) 1e: 20 34 2e y %dh,(%rsi ,%rbp,1) 21: 38 2e cmp %ch,(%rsi) 23: 32 00 xor (%rax),%al Desensamblar la parte __mcount_loc: 0000000000000000 <__mcount_loc>: Podemos saber por la información de Ups que el error ocurrió en el desplazamiento 0xd de la dirección hello_init, y podemos saberlo por la información de volcado, la dirección de hello_init es 0xd. La dirección de hello_init es la dirección de init_module, porque hello_init es el punto de entrada de inicialización del módulo. Si ocurre un error en otras funciones, la información de volcado tendrá la dirección del símbolo correspondiente. De esto obtenemos que la dirección de error es 0xd. A continuación podemos usar addr2line para localizar la línea de código específica: addr2line -C -f -e helloworld.o d Este comando. Obtendrá el número de línea. Lo anterior es para encontrar el número de línea del accidente del conductor a través de información de Ups. Otras herramientas de depuración Lo anterior es para obtener las líneas de código específicas que causaron el bloqueo a través de información de ups. Esto se usa cuando errores más graves hacen que el kernel se cuelgue. más comúnmente utilizado. La mejor herramienta de depuración es utilizar printk para imprimir información. Cabe señalar que con respecto al nivel de impresión, los detalles del controlador del dispositivo Linux se presentaron en la Parte 2: Construcción y operación del módulo. Además, debe tenerse en cuenta que el uso extensivo de printk ralentizará seriamente el sistema. así que use También preste atención durante el proceso. Las dos herramientas de depuración anteriores son las más utilizadas en mi trabajo. También existen otras herramientas de depuración, como el uso del sistema de archivos /proc, el uso de programas de espacio de usuario (como trace) y el uso de gdb. , kgdb, etc. Estas herramientas generalmente no son muy útiles ni cómodas de usar, por lo que no las presentaré una por una aquí.