blob: 65e5ca2d49360b984ba12caf04e4f9fae31b767d [file] [log] [blame]
Ulrich Drepperb08d5a82005-07-26 05:00:05 +00001/* Standard libdwfl callbacks for debugging the running Linux kernel.
2 Copyright (C) 2005 Red Hat, Inc.
3
4 This program is Open Source software; you can redistribute it and/or
5 modify it under the terms of the Open Software License version 1.0 as
6 published by the Open Source Initiative.
7
8 You should have received a copy of the Open Software License along
9 with this program; if not, you may obtain a copy of the Open Software
10 License version 1.0 from http://www.opensource.org/licenses/osl.php or
11 by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
12 3001 King Ranch Road, Ukiah, CA 95482. */
13
14#include <config.h>
15#undef _FILE_OFFSET_BITS /* Doesn't jibe with fts. */
16
17#include "libdwflP.h"
18#include <inttypes.h>
19#include <errno.h>
20#include <stdio.h>
21#include <stdio_ext.h>
22#include <string.h>
23#include <stdlib.h>
24#include <sys/utsname.h>
25#include <fcntl.h>
26#include <unistd.h>
27#include <fts.h>
28
29
30#define MODULEDIRFMT "/lib/modules/%s"
31
32#define MODULELIST "/proc/modules"
33#define SECADDRFMT "/sys/module/%s/sections/%s"
34
35
36static inline const char *
37kernel_release (void)
38{
39 /* Cache the `uname -r` string we'll use. */
40 static struct utsname utsname;
41 if (utsname.release[0] == '\0' && uname (&utsname) != 0)
42 return NULL;
43 return utsname.release;
44}
45
46/* Find the ELF file for the running kernel and dwfl_report_elf it. */
47int
48dwfl_linux_kernel_report_kernel (Dwfl *dwfl)
49{
50 if (dwfl == NULL)
51 return -1;
52
53 const char *release = kernel_release ();
54 if (release == NULL)
55 return errno;
56
57 char *fname = NULL;
58 asprintf (&fname, "/boot/vmlinux-%s", release);
59 if (fname == NULL)
60 return -1;
61 int fd = open64 (fname, O_RDONLY);
62 if (fd < 0)
63 {
64 free (fname);
65 fname = NULL;
66 asprintf (&fname, "/usr/lib/debug" MODULEDIRFMT "/vmlinux", release);
67 if (fname == NULL)
68 return -1;
69 fd = open64 (fname, O_RDONLY);
70 }
71
72 int result = 0;
73 if (fd < 0)
74 result = errno;
75 else if (INTUSE(dwfl_report_elf) (dwfl, "kernel", fname, fd, 0) == NULL)
76 {
77 close (fd);
78 result = -1;
79 }
80
81 free (fname);
82
83 return result;
84}
85INTDEF (dwfl_linux_kernel_report_kernel)
86
87/* Dwfl_Callbacks.find_elf for the running Linux kernel and its modules. */
88
89int
90dwfl_linux_kernel_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
91 void **userdata __attribute__ ((unused)),
92 const char *module_name,
93 Dwarf_Addr base __attribute__ ((unused)),
94 char **file_name,
95 Elf **elfp __attribute__ ((unused)))
96{
97 const char *release = kernel_release ();
98 if (release == NULL)
99 return -1;
100
101 /* Do "find /lib/modules/`uname -r` -name MODULE_NAME.ko". */
102
103 char *modulesdir[] = { NULL, NULL };
104 asprintf (&modulesdir[0], MODULEDIRFMT "/kernel", release);
105 if (modulesdir[0] == NULL)
106 return -1;
107
108 FTS *fts = fts_open (modulesdir, FTS_LOGICAL | FTS_NOSTAT, NULL);
109 if (fts == NULL)
110 {
111 free (modulesdir[0]);
112 return -1;
113 }
114
115 size_t namelen = strlen (module_name);
116 FTSENT *f;
117 int error = ENOENT;
118 while ((f = fts_read (fts)) != NULL)
119 {
120 error = ENOENT;
121 switch (f->fts_info)
122 {
123 case FTS_F:
124 case FTS_NSOK:
125 /* See if this file name is "MODULE_NAME.ko". */
126 if (f->fts_namelen == namelen + 3
127 && !memcmp (f->fts_name, module_name, namelen)
128 && !memcmp (f->fts_name + namelen, ".ko", 4))
129 {
130 int fd = open64 (f->fts_accpath, O_RDONLY);
131 *file_name = strdup (f->fts_path);
132 fts_close (fts);
133 if (fd < 0)
134 free (*file_name);
135 else if (*file_name == NULL)
136 {
137 close (fd);
138 fd = -1;
139 }
140 return fd;
141 }
142 break;
143
144 case FTS_ERR:
145 case FTS_DNR:
146 case FTS_NS:
147 error = f->fts_errno;
148 break;
149
150 default:
151 break;
152 }
153 }
154
155 errno = error;
156 return -1;
157}
158INTDEF (dwfl_linux_kernel_find_elf)
159
160/* Dwfl_Callbacks.section_address for kernel modules in the running Linux.
161 We read the information from /sys/module directly. */
162
163int
164dwfl_linux_kernel_module_section_address
165(Dwfl_Module *mod __attribute__ ((unused)),
166 void **userdata __attribute__ ((unused)),
167 const char *modname, Dwarf_Addr base __attribute__ ((unused)),
168 const char *secname, Dwarf_Addr *addr)
169{
170 char *sysfile = NULL;
171 asprintf (&sysfile, SECADDRFMT, modname, secname);
172 if (sysfile == NULL)
173 return ENOMEM;
174
175 FILE *f = fopen (sysfile, "r");
176 if (f == NULL)
177 {
178 if (errno == ENOENT)
179 {
180 /* The .modinfo and .data.percpu sections are never kept
181 loaded in the kernel. If the kernel was compiled without
182 CONFIG_MODULE_UNLOAD, the .exit.* sections are not
183 actually loaded at all.
184
185 Just relocate these bogusly to zero. This part of the
186 debug information will not be of any use. */
187
188 if (!strcmp (secname, ".modinfo")
189 || !strcmp (secname, ".data.percpu")
190 || !strncmp (secname, ".exit", 5))
191 {
192 *addr = 0;
193 return DWARF_CB_OK;
194 }
195 }
196
197 return DWARF_CB_ABORT;
198 }
199
200 (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
201
202 int result = (fscanf (f, "%" PRIi64 "\n", addr) == 1 ? 0
203 : ferror_unlocked (f) ? errno : ENOEXEC);
204 fclose (f);
205
206 if (result == 0)
207 return DWARF_CB_OK;
208
209 errno = result;
210 return DWARF_CB_ABORT;
211}
212INTDEF (dwfl_linux_kernel_module_section_address)
213
214int
215dwfl_linux_kernel_report_modules (Dwfl *dwfl)
216{
217 FILE *f = fopen (MODULELIST, "r");
218 if (f == NULL)
219 return errno;
220
221 (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
222
223 int result = 0;
224 Dwarf_Addr modaddr;
225 unsigned long int modsz;
226 char modname[128];
227 while (fscanf (f, "%128s %lu %*s %*s %*s %" PRIi64 "\n",
228 modname, &modsz, &modaddr) == 3)
229 if (INTUSE(dwfl_report_module) (dwfl, modname,
230 modaddr, modaddr + modsz) == NULL)
231 {
232 result = -1;
233 break;
234 }
235
236 if (result == 0)
237 result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
238
239 fclose (f);
240
241 return result;
242}
243INTDEF (dwfl_linux_kernel_report_modules)