]> sourceware.org Git - systemtap.git/blame - runtime/vma.c
Deal with kernels pre-6.12 kernel that relocated unaligned.h
[systemtap.git] / runtime / vma.c
CommitLineData
7d8aa2ee
MW
1/* -*- linux-c -*-
2 * VMA tracking and lookup functions.
3 *
8e1a57de 4 * Copyright (C) 2005-2019 Red Hat Inc.
7d8aa2ee
MW
5 * Copyright (C) 2006 Intel Corporation.
6 *
7 * This file is part of systemtap, and is free software. You can
8 * redistribute it and/or modify it under the terms of the GNU General
9 * Public License (GPL); either version 2, or (at your option) any
10 * later version.
11 */
12
13#ifndef _STP_VMA_C_
14#define _STP_VMA_C_
15
16#include "sym.h"
2466bf70 17#include "stp_string.c"
7d8aa2ee
MW
18#include "task_finder_vma.c"
19
18da5887
MW
20#include <asm/uaccess.h>
21
22static void _stp_vma_match_vdso(struct task_struct *tsk)
23{
24/* vdso is arch specific */
3b62d74b 25#if defined(STAPCONF_MM_CONTEXT_VDSO) || defined(STAPCONF_MM_CONTEXT_VDSO_BASE)
18da5887
MW
26 int i, j;
27 if (tsk->mm)
28 {
29 struct _stp_module *found = NULL;
3b62d74b
MW
30
31#ifdef STAPCONF_MM_CONTEXT_VDSO
18da5887 32 unsigned long vdso_addr = (unsigned long) tsk->mm->context.vdso;
3b62d74b
MW
33#else
34 unsigned long vdso_addr = tsk->mm->context.vdso_base;
35#endif
36
a2b0b5c8 37 dbug_task_vma(1,"tsk: %d vdso: 0x%lx\n", tsk->pid, vdso_addr);
3b62d74b 38
18da5887
MW
39 for (i = 0; i < _stp_num_modules && found == NULL; i++) {
40 struct _stp_module *m = _stp_modules[i];
6ec3b7c0
YZ
41 if (m->path
42 && m->path[0] == '/'
b3131710 43 && m->num_sections == 1)
18da5887
MW
44 {
45 unsigned long notes_addr;
46 int all_ok = 1;
b3131710
DS
47
48 /* Assume that if the path's basename starts with 'vdso'
49 * and ends with '.so', it is the vdso.
50 *
51 * Note that this logic should match up with the logic in
52 * the find_vdso() function in translate.cxx. */
53 const char *name = strrchr(m->path, '/');
54 if (name)
55 {
56 const char *ext;
57
58 name++;
59 ext = strrchr(name, '.');
60 if (!ext
61 || strncmp("vdso", name, 4) != 0
62 || strcmp(".so", ext) != 0)
63 continue;
64 }
65
31d50137 66 notes_addr = vdso_addr + m->build_id_offset;
8ca8aa7d 67 dbug_task_vma(1,"notes_addr %s: 0x%lx + 0x%lx = 0x%lx (len: %x)\n", m->path,
31d50137 68 vdso_addr, m->build_id_offset, notes_addr, m->build_id_len);
18da5887
MW
69 for (j = 0; j < m->build_id_len; j++)
70 {
71 int rc;
72 unsigned char b;
414b89b8
DS
73
74 /*
f1410a8a
DS
75 * Since we're only reading here, we can call
76 * __access_process_vm_noflush(), which only calls
77 * things that are exported.
414b89b8 78 */
c0456b6f
DS
79 if (tsk == current)
80 {
81 rc = copy_from_user(&b, (void*)(notes_addr + j), 1);
82 }
83 else
84 {
f1410a8a
DS
85 rc = (__access_process_vm_noflush(tsk, (notes_addr + j),
86 &b, 1, 0) != 1);
c0456b6f 87 }
18da5887
MW
88 if (rc || b != m->build_id_bits[j])
89 {
a2b0b5c8 90 dbug_task_vma(1,"darn, not equal (rc=%d) at %d (0x%x != 0x%x)\n",
18da5887 91 rc, j, b, m->build_id_bits[j]);
18da5887
MW
92 all_ok = 0;
93 break;
94 }
95 }
96 if (all_ok)
97 found = m;
98 }
99 }
100 if (found != NULL)
101 {
102 stap_add_vma_map_info(tsk, vdso_addr,
d1b55487 103 vdso_addr + found->sections[0].size, 0,
e4d6479f 104 "vdso", found);
a2b0b5c8 105 dbug_task_vma(1,"found vdso: %s\n", found->path);
18da5887
MW
106 }
107 }
108#endif /* STAPCONF_MM_CONTEXT_VDSO */
109}
110
c3c8589d 111#ifdef HAVE_TASK_FINDER
18da5887
MW
112/* exec callback, will try to match vdso for new process,
113 will drop all vma maps for a process that disappears. */
7d8aa2ee
MW
114static int _stp_vma_exec_cb(struct stap_task_finder_target *tgt,
115 struct task_struct *tsk,
116 int register_p,
117 int process_p)
118{
a2b0b5c8 119 dbug_task_vma(1,
7d8aa2ee
MW
120 "tsk %d:%d , register_p: %d, process_p: %d\n",
121 tsk->pid, tsk->tgid, register_p, process_p);
18da5887
MW
122 if (process_p)
123 {
124 if (register_p)
125 _stp_vma_match_vdso(tsk);
126 else
127 stap_drop_vma_maps(tsk);
128 }
7d8aa2ee
MW
129
130 return 0;
131}
132
133/* mmap callback, will match new vma with _stp_module or register vma name. */
134static int _stp_vma_mmap_cb(struct stap_task_finder_target *tgt,
135 struct task_struct *tsk,
136 char *path, struct dentry *dentry,
137 unsigned long addr,
138 unsigned long length,
139 unsigned long offset,
140 unsigned long vm_flags)
141{
142 int i, res;
143 struct _stp_module *module = NULL;
2eb7fdfd 144 const char *ori_path = NULL;
295de711
DS
145 const char *name = ((dentry != NULL) ? (char *)dentry->d_name.name
146 : NULL);
35f71b69
FCE
147
148 if (path == NULL || *path == '\0') /* unknown? */
149 path = (char *)name; /* we'll copy this soon, in ..._add_vma_... */
7d8aa2ee 150
a2b0b5c8 151 dbug_task_vma(1,
7d8aa2ee
MW
152 "mmap_cb: tsk %d:%d path %s, addr 0x%08lx, length 0x%08lx, offset 0x%lx, flags 0x%lx\n",
153 tsk->pid, tsk->tgid, path, addr, length, offset, vm_flags);
4ae4592f
FCE
154
155 // We used to be only interested in the first load of the whole module that
156 // is executable. But with modern enough gcc/ld.so, executables are mapped
d1b55487
SA
157 // in more small pieces (r--p,r-xp,rw-p, instead of r-xp, rw-p). NB:
158 // the first section might not be executable, so there can be an offset.
4ae4592f
FCE
159 //
160 // We register whether or not we know the module,
7d8aa2ee 161 // so we can later lookup the name given an address for this task.
d1b55487 162 if (path != NULL &&
2eb7fdfd
JL
163 (stap_find_vma_map_info(tsk->group_leader, addr, NULL, NULL, NULL, &ori_path, NULL) != 0 ||
164 strcmp(ori_path ?: "", path) != 0)) {
7d8aa2ee 165 for (i = 0; i < _stp_num_modules; i++) {
641e6d92
FCE
166 // PR20433: papering over possibility of NULL pointers
167 if (strcmp(path ?: "", _stp_modules[i]->path ?: "") == 0)
7d8aa2ee 168 {
76783a24
MW
169 unsigned long vm_start = 0;
170 unsigned long vm_end = 0;
a2b0b5c8 171 dbug_task_vma(1,
7d8aa2ee
MW
172 "vm_cb: matched path %s to module (sec: %s)\n",
173 path, _stp_modules[i]->sections[0].name);
7d8aa2ee 174 module = _stp_modules[i];
dd0dee22
SA
175 /* Make sure we really don't know about this module
176 yet. If we do know, we might want to extend
177 the coverage. */
178 res = stap_find_vma_map_info_user(tsk->group_leader,
179 module,
180 &vm_start, &vm_end,
181 NULL);
4a3ad353 182 if (res == -ESRCH)
dd0dee22 183 res = stap_add_vma_map_info(tsk->group_leader,
4a3ad353
JL
184 addr, addr + length,
185 0, path, module);
186 else
187 res = stap_add_vma_map_info(tsk->group_leader,
188 addr, addr + length,
189 addr - vm_start, path, module);
190
c2537ee6
MW
191 /* VMA entries are allocated dynamically, this is fine,
192 * since we are in a task_finder callback, which is in
193 * user context. */
ea7c27bb 194 if (res != 0 && res != -EEXIST) {
c2537ee6 195 _stp_error ("Couldn't register module '%s' for pid %d (%d)\n", _stp_modules[i]->path, tsk->group_leader->pid, res);
a6af96f5 196 }
7d8aa2ee
MW
197 return 0;
198 }
199 }
200
201 /* None of the tracked modules matched, register without,
202 * to make sure we can lookup the name later. Ignore errors,
203 * we will just report unknown when asked and tables were
204 * full. Restrict to target process when given to preserve
205 * vma_map entry slots. */
206 if (_stp_target == 0
207 || _stp_target == tsk->group_leader->pid)
208 {
209 res = stap_add_vma_map_info(tsk->group_leader, addr,
d1b55487
SA
210 addr + length, offset, path,
211 NULL);
a2b0b5c8 212 dbug_task_vma(1,
b100897f 213 "registered '%s' for %d (res:%d) [%lx-%lx]\n",
35f71b69 214 path, tsk->group_leader->pid,
b100897f 215 res, addr, addr + length);
7d8aa2ee
MW
216 }
217
b100897f
MW
218 } else if (path != NULL) {
219 // Once registered, we may want to extend an earlier
220 // registered region. A segment might be mapped with
221 // different flags for different offsets. If so we want
222 // to record the extended range so we can address more
223 // precisely to module names and symbols.
224 res = stap_extend_vma_map_info(tsk->group_leader,
225 addr, addr + length);
a2b0b5c8 226 dbug_task_vma(1,
b100897f
MW
227 "extended '%s' for %d (res:%d) [%lx-%lx]\n",
228 path, tsk->group_leader->pid,
229 res, addr, addr + length);
7d8aa2ee
MW
230 }
231 return 0;
232}
233
234/* munmap callback, removes vma map info. */
235static int _stp_vma_munmap_cb(struct stap_task_finder_target *tgt,
236 struct task_struct *tsk,
237 unsigned long addr,
238 unsigned long length)
239{
240 /* Unconditionally remove vm map info, ignore if not present. */
241 stap_remove_vma_map_info(tsk->group_leader, addr);
242 return 0;
243}
244
c3a9ae38
SM
245#endif
246
7d8aa2ee
MW
247/* Initializes the vma tracker. */
248static int _stp_vma_init(void)
249{
250 int rc = 0;
a915ecd6 251#ifdef HAVE_TASK_FINDER
7d8aa2ee
MW
252 static struct stap_task_finder_target vmcb = {
253 // NB: no .pid, no .procname filters here.
254 // This means that we get a system-wide mmap monitoring
255 // widget while the script is running. (The
256 // system-wideness may be restricted by stap -c or
257 // -x.) But this seems to be necessary if we want to
258 // to stack tracebacks through arbitrary shared libraries.
259 //
260 // XXX: There may be an optimization opportunity
261 // for executables (for which the main task-finder
262 // callback should be sufficient).
263 .pid = 0,
264 .procname = NULL,
7a23ba3a 265 .build_id_len = 0,
1af100fc 266 .purpose = "vma tracking",
7d8aa2ee
MW
267 .callback = &_stp_vma_exec_cb,
268 .mmap_callback = &_stp_vma_mmap_cb,
269 .munmap_callback = &_stp_vma_munmap_cb,
270 .mprotect_callback = NULL
271 };
14fc9001
MW
272 rc = stap_initialize_vma_map ();
273 if (rc != 0) {
274 _stp_error("Couldn't initialize vma map: %d\n", rc);
275 return rc;
276 }
a2b0b5c8 277 dbug_task_vma(1,
7d8aa2ee 278 "registering vmcb (_stap_target: %d)\n", _stp_target);
7d8aa2ee 279 rc = stap_register_task_finder_target (& vmcb);
81b2ee28
YZ
280 /* NB: we don't need to call stap_cleanup_task_finder_target() to
281 * free up any dynamically-allocated procname buffers since .procname
282 * is always NULL (see above). */
7d8aa2ee
MW
283 if (rc != 0)
284 _stp_error("Couldn't register task finder target: %d\n", rc);
285#endif
286 return rc;
287}
288
14fc9001
MW
289/* Get rid of the vma tracker (memory). */
290static void _stp_vma_done(void)
291{
0064e701 292/* See runtime/linux/runtime.h for more details. See also PR26123 */
fb59e8c7 293#ifdef HAVE_TASK_FINDER
14fc9001
MW
294 stap_destroy_vma_map();
295#endif
296}
297
7d8aa2ee 298#endif /* _STP_VMA_C_ */
This page took 0.209239 seconds and 6 git commands to generate.