/usr/share/systemtap/runtime/vma.c is in systemtap-common 2.3-1ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | /* -*- linux-c -*-
* VMA tracking and lookup functions.
*
* Copyright (C) 2005-2013 Red Hat Inc.
* Copyright (C) 2006 Intel Corporation.
*
* This file is part of systemtap, and is free software. You can
* redistribute it and/or modify it under the terms of the GNU General
* Public License (GPL); either version 2, or (at your option) any
* later version.
*/
#ifndef _STP_VMA_C_
#define _STP_VMA_C_
#include "sym.h"
#include "stp_string.c"
#include "task_finder_vma.c"
#include <asm/uaccess.h>
static void _stp_vma_match_vdso(struct task_struct *tsk)
{
/* vdso is arch specific */
#if defined(STAPCONF_MM_CONTEXT_VDSO) || defined(STAPCONF_MM_CONTEXT_VDSO_BASE)
int i, j;
if (tsk->mm)
{
struct _stp_module *found = NULL;
#ifdef STAPCONF_MM_CONTEXT_VDSO
unsigned long vdso_addr = (unsigned long) tsk->mm->context.vdso;
#else
unsigned long vdso_addr = tsk->mm->context.vdso_base;
#endif
dbug_task_vma(1,"tsk: %d vdso: 0x%lx\n", tsk->pid, vdso_addr);
for (i = 0; i < _stp_num_modules && found == NULL; i++) {
struct _stp_module *m = _stp_modules[i];
if (m->path[0] == '/'
&& m->num_sections == 1
&& strncmp(m->name, "vdso", 4) == 0)
{
unsigned long notes_addr;
int all_ok = 1;
notes_addr = vdso_addr + m->build_id_offset;
dbug_task_vma(1,"notes_addr %s: 0x%lx + 0x%lx = 0x%lx (len: %x)\n", m->path,
vdso_addr, m->build_id_offset, notes_addr, m->build_id_len);
for (j = 0; j < m->build_id_len; j++)
{
int rc;
unsigned char b;
/*
* Why check CONFIG_UTRACE here? If we're using real
* in-kernel utrace, we can always just call
* get_user() (since tsk == current).
*
* Since we're only reading here, we can call
* __access_process_vm_noflush(), which only calls
* things that are exported.
*/
#ifdef CONFIG_UTRACE
rc = copy_from_user(&b, (void*)(notes_addr + j), 1);
#else
if (tsk == current)
{
rc = copy_from_user(&b, (void*)(notes_addr + j), 1);
}
else
{
rc = (__access_process_vm_noflush(tsk, (notes_addr + j),
&b, 1, 0) != 1);
}
#endif
if (rc || b != m->build_id_bits[j])
{
dbug_task_vma(1,"darn, not equal (rc=%d) at %d (0x%x != 0x%x)\n",
rc, j, b, m->build_id_bits[j]);
all_ok = 0;
break;
}
}
if (all_ok)
found = m;
}
}
if (found != NULL)
{
stap_add_vma_map_info(tsk, vdso_addr,
vdso_addr + found->sections[0].size,
"vdso", found);
dbug_task_vma(1,"found vdso: %s\n", found->path);
}
}
#endif /* STAPCONF_MM_CONTEXT_VDSO */
}
#ifdef HAVE_TASK_FINDER
/* exec callback, will try to match vdso for new process,
will drop all vma maps for a process that disappears. */
static int _stp_vma_exec_cb(struct stap_task_finder_target *tgt,
struct task_struct *tsk,
int register_p,
int process_p)
{
dbug_task_vma(1,
"tsk %d:%d , register_p: %d, process_p: %d\n",
tsk->pid, tsk->tgid, register_p, process_p);
if (process_p)
{
if (register_p)
_stp_vma_match_vdso(tsk);
else
stap_drop_vma_maps(tsk);
}
return 0;
}
/* mmap callback, will match new vma with _stp_module or register vma name. */
static int _stp_vma_mmap_cb(struct stap_task_finder_target *tgt,
struct task_struct *tsk,
char *path, struct dentry *dentry,
unsigned long addr,
unsigned long length,
unsigned long offset,
unsigned long vm_flags)
{
int i, res;
struct _stp_module *module = NULL;
const char *name = (dentry != NULL) ? dentry->d_name.name : NULL;
if (path == NULL || *path == '\0') /* unknown? */
path = (char *)name; /* we'll copy this soon, in ..._add_vma_... */
dbug_task_vma(1,
"mmap_cb: tsk %d:%d path %s, addr 0x%08lx, length 0x%08lx, offset 0x%lx, flags 0x%lx\n",
tsk->pid, tsk->tgid, path, addr, length, offset, vm_flags);
// We are only interested in the first load of the whole module that
// is executable. We register whether or not we know the module,
// so we can later lookup the name given an address for this task.
if (path != NULL && offset == 0 && (vm_flags & VM_EXEC)
&& stap_find_vma_map_info(tsk, addr, NULL, NULL, NULL, NULL) != 0) {
for (i = 0; i < _stp_num_modules; i++) {
if (strcmp(path, _stp_modules[i]->path) == 0)
{
unsigned long vm_start = 0;
unsigned long vm_end = 0;
dbug_task_vma(1,
"vm_cb: matched path %s to module (sec: %s)\n",
path, _stp_modules[i]->sections[0].name);
module = _stp_modules[i];
/* Make sure we really don't know about this module
yet. If we do know, we might want to extend
the coverage. */
res = stap_find_vma_map_info_user(tsk->group_leader,
module,
&vm_start, &vm_end,
NULL);
if (res == -ESRCH)
res = stap_add_vma_map_info(tsk->group_leader,
addr, addr + length,
path, module);
else if (res == 0 && vm_end + 1 == addr)
res = stap_extend_vma_map_info(tsk->group_leader,
vm_start,
addr + length);
/* VMA entries are allocated dynamically, this is fine,
* since we are in a task_finder callback, which is in
* user context. */
if (res != 0) {
_stp_error ("Couldn't register module '%s' for pid %d (%d)\n", _stp_modules[i]->path, tsk->group_leader->pid, res);
}
return 0;
}
}
/* None of the tracked modules matched, register without,
* to make sure we can lookup the name later. Ignore errors,
* we will just report unknown when asked and tables were
* full. Restrict to target process when given to preserve
* vma_map entry slots. */
if (_stp_target == 0
|| _stp_target == tsk->group_leader->pid)
{
res = stap_add_vma_map_info(tsk->group_leader, addr,
addr + length, path, NULL);
dbug_task_vma(1,
"registered '%s' for %d (res:%d) [%lx-%lx]\n",
path, tsk->group_leader->pid,
res, addr, addr + length);
}
} else if (path != NULL) {
// Once registered, we may want to extend an earlier
// registered region. A segment might be mapped with
// different flags for different offsets. If so we want
// to record the extended range so we can address more
// precisely to module names and symbols.
res = stap_extend_vma_map_info(tsk->group_leader,
addr, addr + length);
dbug_task_vma(1,
"extended '%s' for %d (res:%d) [%lx-%lx]\n",
path, tsk->group_leader->pid,
res, addr, addr + length);
}
return 0;
}
/* munmap callback, removes vma map info. */
static int _stp_vma_munmap_cb(struct stap_task_finder_target *tgt,
struct task_struct *tsk,
unsigned long addr,
unsigned long length)
{
/* Unconditionally remove vm map info, ignore if not present. */
stap_remove_vma_map_info(tsk->group_leader, addr);
return 0;
}
#endif
/* Initializes the vma tracker. */
static int _stp_vma_init(void)
{
int rc = 0;
#ifdef HAVE_TASK_FINDER
static struct stap_task_finder_target vmcb = {
// NB: no .pid, no .procname filters here.
// This means that we get a system-wide mmap monitoring
// widget while the script is running. (The
// system-wideness may be restricted by stap -c or
// -x.) But this seems to be necessary if we want to
// to stack tracebacks through arbitrary shared libraries.
//
// XXX: There may be an optimization opportunity
// for executables (for which the main task-finder
// callback should be sufficient).
.pid = 0,
.procname = NULL,
.purpose = "vma tracking",
.callback = &_stp_vma_exec_cb,
.mmap_callback = &_stp_vma_mmap_cb,
.munmap_callback = &_stp_vma_munmap_cb,
.mprotect_callback = NULL
};
rc = stap_initialize_vma_map ();
if (rc != 0) {
_stp_error("Couldn't initialize vma map: %d\n", rc);
return rc;
}
dbug_task_vma(1,
"registering vmcb (_stap_target: %d)\n", _stp_target);
rc = stap_register_task_finder_target (& vmcb);
if (rc != 0)
_stp_error("Couldn't register task finder target: %d\n", rc);
#endif
return rc;
}
/* Get rid of the vma tracker (memory). */
static void _stp_vma_done(void)
{
#if defined(CONFIG_UTRACE)
stap_destroy_vma_map();
#endif
}
#endif /* _STP_VMA_C_ */
|