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 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
|
/*
* Oracle Linux DTrace.
* Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
#ifndef _PCONTROL_H
#define _PCONTROL_H
/*
* Implemention-specific include file for libproc process management.
* This is not to be seen by the clients of libproc.
*/
#include <stdio.h>
#include <gelf.h>
#include <rtld_db.h>
#include <libproc.h>
#include <limits.h>
#include <dtrace.h>
#include <dt_list.h>
#include <setjmp.h>
#include <sys/ptrace.h>
#include <sys/auxv.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* The unwinder pad function. Used by code throughout libproc, so extern.
*/
extern libproc_unwinder_pad_fun *libproc_unwinder_pad;
/*
* Definitions of the process control structures, internal to libproc.
* These may change without affecting clients of libproc.
*/
/*
* sym_tbl_t contains a primary and an (optional) auxiliary symbol table, which
* we wish to treat as a single logical symbol table. In this logical table,
* the data from the auxiliary table precedes that from the primary. Symbol
* indices start at [0], which is the first item in the auxiliary table
* if there is one. The sole purpose for this is so that we can treat the
* combination of .ldynsym and .dynsym sections as a logically single
* entity without having to violate the public interface to libelf.
*
* Both tables must share the same string table section.
*
* The symtab_getsym() function serves as a gelf_getsym() replacement
* that is aware of the two tables and makes them look like a single table
* to the caller.
*
*/
typedef struct sym_tbl { /* symbol table */
Elf_Data *sym_data_pri; /* primary table */
Elf_Data *sym_data_aux; /* auxiliary table */
size_t sym_symn_aux; /* number of entries in auxiliary table */
size_t sym_symn; /* total number of entries in both tables */
char *sym_strs; /* ptr to strings */
size_t sym_strsz; /* size of string table */
GElf_Shdr sym_hdr_pri; /* primary symbol table section header */
GElf_Shdr sym_hdr_aux; /* auxiliary symbol table section header */
GElf_Shdr sym_strhdr; /* string table section header */
uint_t *sym_byname; /* symbols sorted by name */
uint_t *sym_byaddr; /* symbols sorted by addr */
size_t sym_count; /* number of symbols in each sorted list */
} sym_tbl_t;
/*
* This structure persists even across shared library loads and unloads: it is
* reference-counted by file_ref and deallocated only when this reaches zero.
*
* However, file_lo and file_symsearch have different lifetimes: as with the
* map_info, the latter is discarded whenever !ps_prochandle_t.info_valid, and
* the former is malloced only once but is overwritten whenever a map_iter() is
* carried out (generally right after info_valid becomes 1 again). Within the
* file_lo, rl_scope is also dynamically allocated, but is freed and reallocated
* whenever map_iter() is run.
*/
struct map_info;
typedef struct file_info { /* symbol information for a mapped file */
dt_list_t file_list; /* linked list */
ssize_t file_map; /* primary (text) mapping idx, or -1 if none */
char *file_pname; /* name from prmap_file_t */
dev_t file_dev; /* device number of file */
ino_t file_inum; /* inode number of file */
int file_ref; /* references from map_info_t structures */
int file_init; /* 0: initialization yet to be performed */
GElf_Half file_etype; /* ELF e_type from ehdr */
rd_loadobj_t *file_lo; /* load object structure from rtld_db */
char *file_lname; /* load object name from rtld_db */
char *file_lbase; /* pointer to basename of file_lname */
Elf *file_elf; /* ELF handle */
struct file_info **file_symsearch; /* Symbol search path */
unsigned int file_nsymsearch; /* number of items therein */
sym_tbl_t file_symtab; /* symbol table */
sym_tbl_t file_dynsym; /* dynamic symbol table */
uintptr_t file_dyn_base; /* load address for ET_DYN files */
char *file_shstrs; /* section header string table */
size_t file_shstrsz; /* section header string table size */
} file_info_t;
/*
* The mappings are stored in ps_prochandle_t.mappings; the hash of mapping
* filenames are stored in ps_prochandle_t.map_files.
*
* There is no ownership relationship between the prmap_file_t and the prmap_t:
* they just point to each other.
*/
typedef struct map_info { /* description of an address space mapping */
prmap_t *map_pmap; /* /proc description of this mapping */
file_info_t *map_file; /* pointer into list of mapped files */
} map_info_t;
/*
* Number of buckets in our hashes of process -> mapping name and
* address->breakpoint. (We expect quite a lot of mappings, and
* very few breakpoints.)
*/
#define MAP_HASH_BUCKETS 277
#define BKPT_HASH_BUCKETS 17
/*
* Handler for breakpoints. (Notifiers use the same data structure, but cast
* the handler to return void.)
*/
typedef struct bkpt_handler {
dt_list_t notifier_list; /* linked list (notifiers only) */
/*
* Return nonzero if the process should remain stopped at this
* breakpoint, or zero to continue.
*/
int (*bkpt_handler)(uintptr_t addr, void *data);
/*
* Clean up any breakpoint state on Prelease() or breakpoint delete.
*/
void (*bkpt_cleanup)(void *data);
void *bkpt_data;
} bkpt_handler_t;
/*
* An active breakpoint.
*/
typedef struct bkpt {
struct bkpt *bkpt_next; /* next in hash chain */
struct bkpt *bkpt_singlestep; /* if non-NULL, this is a temporary
breakpoint used for singlestepping
past this other breakpoint. */
uintptr_t bkpt_addr; /* breakpoint address */
unsigned long orig_insn; /* original instruction word */
bkpt_handler_t bkpt_handler; /* handler for this breakpoint. */
dt_list_t bkpt_notifiers; /* notifier chain */
int after_singlestep; /* call handler before or after
singlestepping? */
int in_handler; /* in the handler now */
int pending_removal; /* some handler has called Punbkpt() */
} bkpt_t;
/*
* librtld_db agent state.
*/
struct rd_agent {
struct ps_prochandle *P; /* pointer back to our ps_prochandle */
int maps_ready; /* 1 if the link maps are ready */
int released; /* 1 if released */
size_t l_searchlist_offset; /* Offset of the l_searchlist in the
link map structure. */
int r_version; /* the version of the r_debug interface */
uintptr_t r_brk_addr; /* if nonzero, the address of r_brk */
uintptr_t rtld_global_addr; /* if nonzero, the address of
_rtld_global */
size_t dl_nns_offset; /* Offset of the dl_nns from rtld_global. */
size_t dl_load_lock_offset; /* Offset of the dl_load_lock from
* rtld_global. */
size_t g_debug_offset; /* Offset of the g_debug element from
* its expected value, G_DEBUG. */
size_t link_namespaces_size; /* Apparent size of "struct link
namespaces" in this glibc. */
int rd_monitoring; /* 1 whenever rtld_db has a breakpoint
set on the dynamic linker. */
int rd_monitor_suppressed; /* 1 if rd monitoring is off forever */
rd_event_fun rd_event_fun; /* function to call on rtld event */
void *rd_event_data; /* state passed to rtld_event_fun */
/*
* Transition to an inconsistent state is barred. (If multiple such
* prohibitions are in force, this is >1). If we are actively waiting
* for a transition, ic_transitioned > 0 indicates that we have hit one:
* otherwise, it is not meaningful. If stop_on_consistent is 1, stop on
* transition to a consistent state, not an inconsistent one.
*/
int no_inconsistent;
int stop_on_consistent;
int ic_transitioned;
int lmid_halted; /* if nonzero, the process was halted by
rd_ldso_nonzero_lmid_consistent_begin(). */
int lmid_bkpted; /* halted on bkpt by rlnlcb(). */
int lmid_incompatible_glibc; /* glibc data structure change. */
};
/*
* Previous states of the process.
*/
typedef struct prev_states {
dt_list_t states_list; /* linked list (stack, pushpopped at head) */
int state; /* previous state */
} prev_states_t;
/*
* A process under management.
*/
struct ps_prochandle {
pid_t pid; /* process ID */
int state; /* state of the process, see "libproc.h" */
int released; /* true if released but not yet freed */
int ptraced; /* true if ptrace-attached */
int noninvasive; /* true if this is a noninvasive grab */
int ptrace_count; /* count of Ptrace() calls */
dt_list_t ptrace_states; /* states of higher Ptrace() levels */
int ptrace_halted; /* true if halted by Ptrace() call */
int pending_stops; /* number of SIGSTOPs Ptrace() has sent that
have yet to be consumed */
int awaiting_pending_stops; /* if 1, a pending stop is being waited
for: all blocking Pwait()s when
pending_stops == 0 are converted
to nonblocking */
int group_stopped; /* if 1, in group-stop */
int listening; /* if 1, in PTRACE_LISTEN */
int detach; /* whether to detach when !ptraced and !bkpts */
int no_dyn; /* true if this is probably statically linked */
int memfd; /* /proc/<pid>/mem filedescriptor */
int mapfilefd; /* /proc/<pid>/map_files directory fd */
int info_valid; /* if zero, map and file info need updating */
int lmids_valid; /* 0 if we haven't yet scanned the link map */
int elf64; /* if nonzero, this is a 64-bit process */
int elf_machine; /* the e_machine of this process */
map_info_t *mappings; /* process mappings, sorted by address */
size_t num_mappings; /* number of mappings */
prmap_file_t **map_files; /* hash of mappings by filename */
prmap_file_t **map_inum; /* hash of mappings by (dev, inode) */
uint_t num_files; /* number of file elements in file_list */
dt_list_t file_list; /* list of mapped files w/ symbol table info */
auxv_t *auxv; /* the process's aux vector */
int nauxv; /* number of aux vector entries */
bkpt_t **bkpts; /* hash of active breakpoints by address */
uint_t num_bkpts; /* number of active breakpoints */
uintptr_t tracing_bkpt; /* address of breakpoint we are single-stepping
past, if any */
int bkpt_halted; /* halted at breakpoint by handler */
int bkpt_consume; /* Ask Pwait() to consume any breakpoint traps */
uintptr_t r_debug_addr; /* address of r_debug in the child */
rd_agent_t *rap; /* rtld_db state */
ssize_t map_exec; /* the index of the executable mapping */
ssize_t map_ldso; /* the index of the ld.so mapping */
ptrace_fun *ptrace_wrap; /* ptrace() wrapper */
pwait_fun *pwait_wrap; /* pwait() wrapper */
void *wrap_arg; /* args for hooks and wrappers */
};
/*
* Implementation functions in the process control library.
* These are not exported to clients of the library.
*/
extern int Psym_init(struct ps_prochandle *);
extern void Psym_free(struct ps_prochandle *);
extern int Pread_isa_info(struct ps_prochandle *P, const char *procname);
extern void Preadauxvec(struct ps_prochandle *P);
extern uintptr_t Pget_bkpt_ip(struct ps_prochandle *P, int expect_esrch);
extern long Preset_bkpt_ip(struct ps_prochandle *P, uintptr_t addr);
extern char * Pget_proc_status(pid_t pid, const char *field);
extern int Pmapfilefd(struct ps_prochandle *P);
#ifdef NEED_SOFTWARE_SINGLESTEP
extern uintptr_t Pget_next_ip(struct ps_prochandle *P);
#endif
extern uintptr_t r_debug(struct ps_prochandle *P);
extern char procfs_path[PATH_MAX];
/*
* The wrapper functions are somewhat inconsistently named, because we can
* rename the guts of Pwait() to Pwait_internal() while retaining the helpful
* external name but have no such luxury with ptrace(). hooked_ptrace() is for
* our internal use, to dispatch on to the wrapper.
*/
extern long wrapped_ptrace(struct ps_prochandle *P, enum __ptrace_request request,
pid_t pid, ...);
/*
* Poison all calls to ptrace() from everywhere but the ptrace hook.
*/
#ifndef DO_NOT_POISON_PTRACE
#pragma GCC poison ptrace
#endif
/*
* Routine to print debug messages.
*/
_dt_printflike_(1,2)
extern void _dprintf(const char *, ...);
/*
* Simple convenience.
*/
#define TRUE 1
#define FALSE 0
#ifdef __cplusplus
}
#endif
#endif /* _PCONTROL_H */
|