746 lines
17 KiB
C
746 lines
17 KiB
C
/*
|
|
* This file is part of ltrace.
|
|
* Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
|
|
* Copyright (C) 2010 Zachary T Welch, CodeSourcery
|
|
* Copyright (C) 2010 Joe Damato
|
|
* Copyright (C) 1998,2008,2009 Juan Cespedes
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*/
|
|
|
|
#define _GNU_SOURCE /* For getline. */
|
|
#include "config.h"
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/types.h>
|
|
#include <ctype.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <link.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "backend.h"
|
|
#include "breakpoint.h"
|
|
#include "config.h"
|
|
#include "debug.h"
|
|
#include "events.h"
|
|
#include "library.h"
|
|
#include "ltrace-elf.h"
|
|
#include "proc.h"
|
|
|
|
/* /proc/pid doesn't exist just after the fork, and sometimes `ltrace'
|
|
* couldn't open it to find the executable. So it may be necessary to
|
|
* have a bit delay
|
|
*/
|
|
|
|
#define MAX_DELAY 100000 /* 100000 microseconds = 0.1 seconds */
|
|
|
|
#define PROC_PID_FILE(VAR, FORMAT, PID) \
|
|
char VAR[strlen(FORMAT) + 6]; \
|
|
sprintf(VAR, FORMAT, PID)
|
|
|
|
/*
|
|
* Returns a (malloc'd) file name corresponding to a running pid
|
|
*/
|
|
char *
|
|
pid2name(pid_t pid) {
|
|
if (!kill(pid, 0)) {
|
|
int delay = 0;
|
|
|
|
PROC_PID_FILE(proc_exe, "/proc/%d/exe", pid);
|
|
|
|
while (delay < MAX_DELAY) {
|
|
if (!access(proc_exe, F_OK)) {
|
|
return strdup(proc_exe);
|
|
}
|
|
delay += 1000; /* 1 milisecond */
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static FILE *
|
|
open_status_file(pid_t pid)
|
|
{
|
|
PROC_PID_FILE(fn, "/proc/%d/status", pid);
|
|
/* Don't complain if we fail. This would typically happen
|
|
when the process is about to terminate, and these files are
|
|
not available anymore. This function is called from the
|
|
event loop, and we don't want to clutter the output just
|
|
because the process terminates. */
|
|
return fopen(fn, "r");
|
|
}
|
|
|
|
static char *
|
|
find_line_starting(FILE * file, const char * prefix, size_t len)
|
|
{
|
|
char * line = NULL;
|
|
size_t line_len = 0;
|
|
while (!feof(file)) {
|
|
if (getline(&line, &line_len, file) < 0)
|
|
return NULL;
|
|
if (strncmp(line, prefix, len) == 0)
|
|
return line;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
each_line_starting(FILE *file, const char *prefix,
|
|
enum callback_status (*cb)(const char *line,
|
|
const char *prefix,
|
|
void *data),
|
|
void *data)
|
|
{
|
|
size_t len = strlen(prefix);
|
|
char * line;
|
|
while ((line = find_line_starting(file, prefix, len)) != NULL) {
|
|
enum callback_status st = (*cb)(line, prefix, data);
|
|
free(line);
|
|
if (st == CBS_STOP)
|
|
return;
|
|
}
|
|
}
|
|
|
|
static enum callback_status
|
|
process_leader_cb(const char *line, const char *prefix, void *data)
|
|
{
|
|
pid_t * pidp = data;
|
|
*pidp = atoi(line + strlen(prefix));
|
|
return CBS_STOP;
|
|
}
|
|
|
|
pid_t
|
|
process_leader(pid_t pid)
|
|
{
|
|
pid_t tgid = 0;
|
|
FILE * file = open_status_file(pid);
|
|
if (file != NULL) {
|
|
each_line_starting(file, "Tgid:\t", &process_leader_cb, &tgid);
|
|
fclose(file);
|
|
}
|
|
|
|
return tgid;
|
|
}
|
|
|
|
static enum callback_status
|
|
process_stopped_cb(const char *line, const char *prefix, void *data)
|
|
{
|
|
char c = line[strlen(prefix)];
|
|
// t:tracing stop, T:job control stop
|
|
*(int *)data = (c == 't' || c == 'T');
|
|
return CBS_STOP;
|
|
}
|
|
|
|
int
|
|
process_stopped(pid_t pid)
|
|
{
|
|
int is_stopped = -1;
|
|
FILE * file = open_status_file(pid);
|
|
if (file != NULL) {
|
|
each_line_starting(file, "State:\t", &process_stopped_cb,
|
|
&is_stopped);
|
|
fclose(file);
|
|
}
|
|
return is_stopped;
|
|
}
|
|
|
|
static enum callback_status
|
|
process_status_cb(const char *line, const char *prefix, void *data)
|
|
{
|
|
const char * status = line + strlen(prefix);
|
|
const char c = *status;
|
|
|
|
#define RETURN(C) do { \
|
|
*(enum process_status *)data = C; \
|
|
return CBS_STOP; \
|
|
} while (0)
|
|
|
|
switch (c) {
|
|
case 'Z': RETURN(PS_ZOMBIE);
|
|
case 't': RETURN(PS_TRACING_STOP);
|
|
case 'T':
|
|
/* This can be either "T (stopped)" or, for older
|
|
* kernels, "T (tracing stop)". */
|
|
if (!strcmp(status, "T (stopped)\n"))
|
|
RETURN(PS_STOP);
|
|
else if (!strcmp(status, "T (tracing stop)\n"))
|
|
RETURN(PS_TRACING_STOP);
|
|
else {
|
|
fprintf(stderr, "Unknown process status: %s",
|
|
status);
|
|
RETURN(PS_STOP); /* Some sort of stop
|
|
* anyway. */
|
|
}
|
|
case 'D':
|
|
case 'S': RETURN(PS_SLEEPING);
|
|
}
|
|
|
|
RETURN(PS_OTHER);
|
|
#undef RETURN
|
|
}
|
|
|
|
enum process_status
|
|
process_status(pid_t pid)
|
|
{
|
|
enum process_status ret = PS_INVALID;
|
|
FILE * file = open_status_file(pid);
|
|
if (file != NULL) {
|
|
each_line_starting(file, "State:\t", &process_status_cb, &ret);
|
|
fclose(file);
|
|
if (ret == PS_INVALID)
|
|
fprintf(stderr,
|
|
"Couldn't determine status of process %d: %s\n",
|
|
pid, strerror(errno));
|
|
} else {
|
|
/* If the file is not present, the process presumably
|
|
* exited already. */
|
|
ret = PS_ZOMBIE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
all_digits(const char *str)
|
|
{
|
|
while (isdigit(*str))
|
|
str++;
|
|
return !*str;
|
|
}
|
|
|
|
int
|
|
process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n)
|
|
{
|
|
PROC_PID_FILE(fn, "/proc/%d/task", pid);
|
|
DIR *d = opendir(fn);
|
|
if (d == NULL)
|
|
return -1;
|
|
|
|
pid_t *tasks = NULL;
|
|
size_t n = 0;
|
|
size_t alloc = 0;
|
|
|
|
while (1) {
|
|
struct dirent entry;
|
|
struct dirent *result;
|
|
if (readdir_r(d, &entry, &result) != 0) {
|
|
fail:
|
|
free(tasks);
|
|
closedir(d);
|
|
return -1;
|
|
}
|
|
if (result == NULL)
|
|
break;
|
|
if (result->d_type == DT_DIR && all_digits(result->d_name)) {
|
|
pid_t npid = atoi(result->d_name);
|
|
if (n >= alloc) {
|
|
alloc = alloc > 0 ? (2 * alloc) : 8;
|
|
pid_t *ntasks = realloc(tasks,
|
|
sizeof(*tasks) * alloc);
|
|
if (ntasks == NULL)
|
|
goto fail;
|
|
tasks = ntasks;
|
|
}
|
|
assert(n < alloc);
|
|
tasks[n++] = npid;
|
|
}
|
|
}
|
|
|
|
closedir(d);
|
|
|
|
*ret_tasks = tasks;
|
|
*ret_n = n;
|
|
return 0;
|
|
}
|
|
|
|
/* On native 64-bit system, we need to be careful when handling cross
|
|
* tracing. This select appropriate pointer depending on host and
|
|
* target architectures. XXX Really we should abstract this into the
|
|
* ABI object, as theorized about somewhere on pmachata/revamp
|
|
* branch. */
|
|
static void *
|
|
select_32_64(struct process *proc, void *p32, void *p64)
|
|
{
|
|
if (sizeof(long) == 4 || proc->mask_32bit)
|
|
return p32;
|
|
else
|
|
return p64;
|
|
}
|
|
|
|
static int
|
|
fetch_dyn64(struct process *proc, arch_addr_t *addr, Elf64_Dyn *ret)
|
|
{
|
|
if (umovebytes(proc, *addr, ret, sizeof(*ret)) != sizeof(*ret))
|
|
return -1;
|
|
*addr += sizeof(*ret);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
fetch_dyn32(struct process *proc, arch_addr_t *addr, Elf64_Dyn *ret)
|
|
{
|
|
Elf32_Dyn dyn;
|
|
if (umovebytes(proc, *addr, &dyn, sizeof(dyn)) != sizeof(dyn))
|
|
return -1;
|
|
|
|
*addr += sizeof(dyn);
|
|
ret->d_tag = dyn.d_tag;
|
|
ret->d_un.d_val = dyn.d_un.d_val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int (*
|
|
dyn_fetcher(struct process *proc))(struct process *,
|
|
arch_addr_t *, Elf64_Dyn *)
|
|
{
|
|
return select_32_64(proc, fetch_dyn32, fetch_dyn64);
|
|
}
|
|
|
|
int
|
|
proc_find_dynamic_entry_addr(struct process *proc, arch_addr_t src_addr,
|
|
int d_tag, arch_addr_t *ret)
|
|
{
|
|
debug(DEBUG_FUNCTION, "find_dynamic_entry()");
|
|
|
|
if (ret == NULL || src_addr == 0 || d_tag < 0)
|
|
return -1;
|
|
|
|
int i = 0;
|
|
while (1) {
|
|
Elf64_Dyn entry;
|
|
if (dyn_fetcher(proc)(proc, &src_addr, &entry) < 0
|
|
|| entry.d_tag == DT_NULL
|
|
|| i++ > 100) { /* Arbitrary cut-off so that we
|
|
* don't loop forever if the
|
|
* binary is corrupted. */
|
|
debug(2, "Couldn't find address for dtag!");
|
|
return -1;
|
|
}
|
|
|
|
if (entry.d_tag == d_tag) {
|
|
/* XXX The double cast should be removed when
|
|
* arch_addr_t becomes integral type. */
|
|
*ret = (arch_addr_t)(uintptr_t)entry.d_un.d_val;
|
|
debug(2, "found address: %p in dtag %d", *ret, d_tag);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Our own type for representing 32-bit linkmap. We can't rely on the
|
|
* definition in link.h, because that's only accurate for our host
|
|
* architecture, not for target architecture (where the traced process
|
|
* runs). */
|
|
#define LT_LINK_MAP(BITS) \
|
|
{ \
|
|
Elf##BITS##_Addr l_addr; \
|
|
Elf##BITS##_Addr l_name; \
|
|
Elf##BITS##_Addr l_ld; \
|
|
Elf##BITS##_Addr l_next; \
|
|
Elf##BITS##_Addr l_prev; \
|
|
}
|
|
struct lt_link_map_32 LT_LINK_MAP(32);
|
|
struct lt_link_map_64 LT_LINK_MAP(64);
|
|
|
|
static int
|
|
fetch_lm64(struct process *proc, arch_addr_t addr,
|
|
struct lt_link_map_64 *ret)
|
|
{
|
|
if (umovebytes(proc, addr, ret, sizeof(*ret)) != sizeof(*ret))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
fetch_lm32(struct process *proc, arch_addr_t addr,
|
|
struct lt_link_map_64 *ret)
|
|
{
|
|
struct lt_link_map_32 lm;
|
|
if (umovebytes(proc, addr, &lm, sizeof(lm)) != sizeof(lm))
|
|
return -1;
|
|
|
|
ret->l_addr = lm.l_addr;
|
|
ret->l_name = lm.l_name;
|
|
ret->l_ld = lm.l_ld;
|
|
ret->l_next = lm.l_next;
|
|
ret->l_prev = lm.l_prev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int (*
|
|
lm_fetcher(struct process *proc))(struct process *,
|
|
arch_addr_t, struct lt_link_map_64 *)
|
|
{
|
|
return select_32_64(proc, fetch_lm32, fetch_lm64);
|
|
}
|
|
|
|
/* The same as above holds for struct r_debug. */
|
|
#define LT_R_DEBUG(BITS) \
|
|
{ \
|
|
int r_version; \
|
|
Elf##BITS##_Addr r_map; \
|
|
Elf##BITS##_Addr r_brk; \
|
|
int r_state; \
|
|
Elf##BITS##_Addr r_ldbase; \
|
|
}
|
|
|
|
struct lt_r_debug_32 LT_R_DEBUG(32);
|
|
struct lt_r_debug_64 LT_R_DEBUG(64);
|
|
|
|
static int
|
|
fetch_rd64(struct process *proc, arch_addr_t addr,
|
|
struct lt_r_debug_64 *ret)
|
|
{
|
|
if (umovebytes(proc, addr, ret, sizeof(*ret)) != sizeof(*ret))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
fetch_rd32(struct process *proc, arch_addr_t addr,
|
|
struct lt_r_debug_64 *ret)
|
|
{
|
|
struct lt_r_debug_32 rd;
|
|
if (umovebytes(proc, addr, &rd, sizeof(rd)) != sizeof(rd))
|
|
return -1;
|
|
|
|
ret->r_version = rd.r_version;
|
|
ret->r_map = rd.r_map;
|
|
ret->r_brk = rd.r_brk;
|
|
ret->r_state = rd.r_state;
|
|
ret->r_ldbase = rd.r_ldbase;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int (*
|
|
rdebug_fetcher(struct process *proc))(struct process *,
|
|
arch_addr_t, struct lt_r_debug_64 *)
|
|
{
|
|
return select_32_64(proc, fetch_rd32, fetch_rd64);
|
|
}
|
|
|
|
static int
|
|
fetch_auxv64_entry(int fd, Elf64_auxv_t *ret)
|
|
{
|
|
/* Reaching EOF is as much problem as not reading whole
|
|
* entry. */
|
|
return read(fd, ret, sizeof(*ret)) == sizeof(*ret) ? 0 : -1;
|
|
}
|
|
|
|
static int
|
|
fetch_auxv32_entry(int fd, Elf64_auxv_t *ret)
|
|
{
|
|
Elf32_auxv_t auxv;
|
|
if (read(fd, &auxv, sizeof(auxv)) != sizeof(auxv))
|
|
return -1;
|
|
|
|
ret->a_type = auxv.a_type;
|
|
ret->a_un.a_val = auxv.a_un.a_val;
|
|
return 0;
|
|
}
|
|
|
|
static int (*
|
|
auxv_fetcher(struct process *proc))(int, Elf64_auxv_t *)
|
|
{
|
|
return select_32_64(proc, fetch_auxv32_entry, fetch_auxv64_entry);
|
|
}
|
|
|
|
static void
|
|
crawl_linkmap(struct process *proc, struct lt_r_debug_64 *dbg)
|
|
{
|
|
debug (DEBUG_FUNCTION, "crawl_linkmap()");
|
|
|
|
if (!dbg || !dbg->r_map) {
|
|
debug(2, "Debug structure or it's linkmap are NULL!");
|
|
return;
|
|
}
|
|
|
|
/* XXX The double cast should be removed when
|
|
* arch_addr_t becomes integral type. */
|
|
arch_addr_t addr = (arch_addr_t)(uintptr_t)dbg->r_map;
|
|
|
|
while (addr != 0) {
|
|
struct lt_link_map_64 rlm = {};
|
|
if (lm_fetcher(proc)(proc, addr, &rlm) < 0) {
|
|
debug(2, "Unable to read link map");
|
|
return;
|
|
}
|
|
|
|
arch_addr_t key = addr;
|
|
/* XXX The double cast should be removed when
|
|
* arch_addr_t becomes integral type. */
|
|
addr = (arch_addr_t)(uintptr_t)rlm.l_next;
|
|
if (rlm.l_name == 0) {
|
|
debug(2, "Name of mapped library is NULL");
|
|
return;
|
|
}
|
|
|
|
char lib_name[BUFSIZ];
|
|
/* XXX The double cast should be removed when
|
|
* arch_addr_t becomes integral type. */
|
|
umovebytes(proc, (arch_addr_t)(uintptr_t)rlm.l_name,
|
|
lib_name, sizeof(lib_name));
|
|
|
|
/* Library name can be an empty string, in which case
|
|
* the entry represents either the main binary, or a
|
|
* VDSO. Unfortunately we can't rely on that, as in
|
|
* recent glibc, that entry is initialized to VDSO
|
|
* SONAME.
|
|
*
|
|
* It's not clear how to detect VDSO in this case. We
|
|
* can't assume that l_name of real DSOs will be
|
|
* either absolute or relative (for LD_LIBRARY_PATH=:
|
|
* it will be neither). We can't compare l_addr with
|
|
* AT_SYSINFO_EHDR either, as l_addr is bias (which
|
|
* also means it's not unique, and therefore useless
|
|
* for this). We could load VDSO from process image
|
|
* and at least compare actual SONAMEs. For now, this
|
|
* kludge is about the best that we can do. */
|
|
if (*lib_name == 0
|
|
|| strcmp(lib_name, "linux-vdso.so.1") == 0
|
|
|| strcmp(lib_name, "linux-gate.so.1") == 0
|
|
|| strcmp(lib_name, "linux-vdso32.so.1") == 0
|
|
|| strcmp(lib_name, "linux-vdso64.so.1") == 0)
|
|
continue;
|
|
|
|
/* Do we have that library already? */
|
|
if (proc_each_library(proc, NULL, library_with_key_cb, &key))
|
|
continue;
|
|
|
|
struct library *lib = malloc(sizeof(*lib));
|
|
if (lib == NULL) {
|
|
fail:
|
|
free(lib);
|
|
fprintf(stderr, "Couldn't load ELF object %s: %s\n",
|
|
lib_name, strerror(errno));
|
|
continue;
|
|
}
|
|
|
|
if (library_init(lib, LT_LIBTYPE_DSO) < 0)
|
|
goto fail;
|
|
|
|
if (ltelf_read_library(lib, proc, lib_name, rlm.l_addr) < 0) {
|
|
library_destroy(lib);
|
|
goto fail;
|
|
}
|
|
|
|
lib->key = key;
|
|
proc_add_library(proc, lib);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static int
|
|
load_debug_struct(struct process *proc, struct lt_r_debug_64 *ret)
|
|
{
|
|
debug(DEBUG_FUNCTION, "load_debug_struct");
|
|
|
|
if (rdebug_fetcher(proc)(proc, proc->os.debug_addr, ret) < 0) {
|
|
debug(2, "This process does not have a debug structure!");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
rdebug_bp_on_hit(struct breakpoint *bp, struct process *proc)
|
|
{
|
|
debug(DEBUG_FUNCTION, "arch_check_dbg");
|
|
|
|
struct lt_r_debug_64 rdbg;
|
|
if (load_debug_struct(proc, &rdbg) < 0) {
|
|
debug(2, "Unable to load debug structure!");
|
|
return;
|
|
}
|
|
|
|
if (rdbg.r_state == RT_CONSISTENT) {
|
|
debug(2, "Linkmap is now consistent");
|
|
switch (proc->os.debug_state) {
|
|
case RT_ADD:
|
|
debug(2, "Adding DSO to linkmap");
|
|
crawl_linkmap(proc, &rdbg);
|
|
break;
|
|
case RT_DELETE:
|
|
debug(2, "Removing DSO from linkmap");
|
|
// XXX unload that library
|
|
break;
|
|
default:
|
|
debug(2, "Unexpected debug state!");
|
|
}
|
|
}
|
|
|
|
proc->os.debug_state = rdbg.r_state;
|
|
}
|
|
|
|
#ifndef ARCH_HAVE_FIND_DL_DEBUG
|
|
int
|
|
arch_find_dl_debug(struct process *proc, arch_addr_t dyn_addr,
|
|
arch_addr_t *ret)
|
|
{
|
|
return proc_find_dynamic_entry_addr(proc, dyn_addr, DT_DEBUG, ret);
|
|
}
|
|
#endif
|
|
|
|
int
|
|
linkmap_init(struct process *proc, arch_addr_t dyn_addr)
|
|
{
|
|
debug(DEBUG_FUNCTION, "linkmap_init(%d, dyn_addr=%p)", proc->pid, dyn_addr);
|
|
|
|
if (arch_find_dl_debug(proc, dyn_addr, &proc->os.debug_addr) == -1) {
|
|
debug(2, "Couldn't find debug structure!");
|
|
return -1;
|
|
}
|
|
|
|
int status;
|
|
struct lt_r_debug_64 rdbg;
|
|
if ((status = load_debug_struct(proc, &rdbg)) < 0) {
|
|
debug(2, "No debug structure or no memory to allocate one!");
|
|
return status;
|
|
}
|
|
|
|
crawl_linkmap(proc, &rdbg);
|
|
|
|
/* XXX The double cast should be removed when
|
|
* arch_addr_t becomes integral type. */
|
|
arch_addr_t addr = (arch_addr_t)(uintptr_t)rdbg.r_brk;
|
|
if (arch_translate_address_dyn(proc, addr, &addr) < 0)
|
|
return -1;
|
|
|
|
struct breakpoint *rdebug_bp = insert_breakpoint_at(proc, addr, NULL);
|
|
if (rdebug_bp == NULL) {
|
|
/* This is not fatal, the tracing can continue with
|
|
* reduced functionality. */
|
|
fprintf(stderr,
|
|
"Couldn't insert _r_debug breakpoint to %d: %s.\n"
|
|
"As a result of that, ltrace will not be able to "
|
|
"detect and trace\nnewly-loaded libraries.\n",
|
|
proc->pid, strerror(errno));
|
|
} else {
|
|
static struct bp_callbacks rdebug_callbacks = {
|
|
.on_hit = rdebug_bp_on_hit,
|
|
};
|
|
rdebug_bp->cbs = &rdebug_callbacks;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
task_kill (pid_t pid, int sig)
|
|
{
|
|
// Taken from GDB
|
|
int ret;
|
|
|
|
errno = 0;
|
|
ret = syscall (__NR_tkill, pid, sig);
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
process_removed(struct process *proc)
|
|
{
|
|
delete_events_for(proc);
|
|
}
|
|
|
|
int
|
|
process_get_entry(struct process *proc,
|
|
arch_addr_t *entryp,
|
|
arch_addr_t *interp_biasp)
|
|
{
|
|
PROC_PID_FILE(fn, "/proc/%d/auxv", proc->pid);
|
|
int fd = open(fn, O_RDONLY);
|
|
int ret = 0;
|
|
if (fd == -1) {
|
|
fail:
|
|
fprintf(stderr, "couldn't read %s: %s", fn, strerror(errno));
|
|
ret = -1;
|
|
done:
|
|
if (fd != -1)
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
arch_addr_t at_entry = 0;
|
|
arch_addr_t at_bias = 0;
|
|
while (1) {
|
|
Elf64_auxv_t entry = {};
|
|
if (auxv_fetcher(proc)(fd, &entry) < 0)
|
|
goto fail;
|
|
|
|
switch (entry.a_type) {
|
|
case AT_BASE:
|
|
/* XXX The double cast should be removed when
|
|
* arch_addr_t becomes integral type. */
|
|
at_bias = (arch_addr_t)(uintptr_t)entry.a_un.a_val;
|
|
continue;
|
|
|
|
case AT_ENTRY:
|
|
/* XXX The double cast should be removed when
|
|
* arch_addr_t becomes integral type. */
|
|
at_entry = (arch_addr_t)(uintptr_t)entry.a_un.a_val;
|
|
default:
|
|
continue;
|
|
|
|
case AT_NULL:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (entryp != NULL)
|
|
*entryp = at_entry;
|
|
if (interp_biasp != NULL)
|
|
*interp_biasp = at_bias;
|
|
goto done;
|
|
}
|
|
|
|
int
|
|
os_process_init(struct process *proc)
|
|
{
|
|
proc->os.debug_addr = 0;
|
|
proc->os.debug_state = 0;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
os_process_destroy(struct process *proc)
|
|
{
|
|
}
|
|
|
|
int
|
|
os_process_clone(struct process *retp, struct process *proc)
|
|
{
|
|
retp->os = proc->os;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
os_process_exec(struct process *proc)
|
|
{
|
|
return 0;
|
|
}
|