blob: b5336896d04991089362e1f327001cfc3b6068a3 [file] [log] [blame]
#define _GNU_SOURCE /* For getline. */
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <inttypes.h>
#include <link.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <dirent.h>
#include <ctype.h>
#include <errno.h>
#include <sys/syscall.h>
#include <error.h>
#include "common.h"
#include "breakpoint.h"
#include "proc.h"
#include "library.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)
error(0, errno, "process_status %d", pid);
} 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) {
free(tasks);
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) {
free(tasks);
return -1;
}
tasks = ntasks;
}
if (n >= alloc)
abort();
tasks[n++] = npid;
}
}
closedir(d);
*ret_tasks = tasks;
*ret_n = n;
return 0;
}
static int
find_dynamic_entry_addr(Process *proc, void *pvAddr, int d_tag, void **addr) {
fprintf(stderr, "find_dynamic_entry_addr %d %p %d\n",
proc->pid, pvAddr, d_tag);
ElfW(Dyn) entry;
debug(DEBUG_FUNCTION, "find_dynamic_entry()");
if (addr == NULL || pvAddr == NULL || d_tag < 0 || d_tag > DT_NUM) {
return -1;
}
while ((sizeof(entry) == umovebytes(proc, pvAddr, &entry, sizeof(entry))) &&
(entry.d_tag != DT_NULL)) {
fprintf(stderr, " entry %ld %#lx\n", entry.d_tag, entry.d_un.d_val);
if (entry.d_tag == d_tag) {
fprintf(stderr, " hit\n");
*addr = (void *)entry.d_un.d_val;
debug(2, "found address: 0x%p in dtag %d\n", *addr, d_tag);
return 0;
}
pvAddr += sizeof(entry);
}
debug(2, "Couldn't address for dtag!\n");
return -1;
}
enum callback_status
find_library_addr(struct Process *proc, struct library *lib, void *data)
{
target_address_t addr = (target_address_t)*(GElf_Addr *)data;
return lib->base == addr ? CBS_STOP : CBS_CONT;
}
static void
crawl_linkmap(Process *proc, struct r_debug *dbg)
{
struct link_map rlm;
char lib_name[BUFSIZ];
struct link_map *lm = NULL;
debug (DEBUG_FUNCTION, "crawl_linkmap()");
if (!dbg || !dbg->r_map) {
debug(2, "Debug structure or it's linkmap are NULL!");
return;
}
lm = dbg->r_map;
while (lm) {
if (umovebytes(proc, lm, &rlm, sizeof(rlm)) != sizeof(rlm)) {
debug(2, "Unable to read link map\n");
return;
}
lm = rlm.l_next;
if (rlm.l_name == NULL) {
debug(2, "Invalid library name referenced in dynamic linker map\n");
return;
}
umovebytes(proc, rlm.l_name, lib_name, sizeof(lib_name));
debug(2, "Dispatching callback for: %s, "
"Loaded at 0x%" PRI_ELF_ADDR "\n",
lib_name, rlm.l_addr);
fprintf(stderr, "DSO addr=%#lx, name='%s'\n", rlm.l_addr, lib_name);
/* Do we have that library already? */
struct library *lib
= proc_each_library(proc, NULL, find_library_addr,
&rlm.l_addr);
if (lib != NULL)
continue;
if (*lib_name == '\0') {
/* VDSO. No associated file, XXX but we might
* load it from the address space of the
* process. */
continue;
}
lib = ltelf_read_library(proc, lib_name, rlm.l_addr);
if (lib == NULL) {
error(0, errno, "Couldn't load ELF object %s\n",
lib_name);
continue;
}
proc_add_library(proc, lib);
}
return;
}
static struct r_debug *
load_debug_struct(Process *proc) {
struct r_debug *rdbg = NULL;
debug(DEBUG_FUNCTION, "load_debug_struct");
rdbg = malloc(sizeof(*rdbg));
if (!rdbg) {
return NULL;
}
if (umovebytes(proc, proc->debug, rdbg, sizeof(*rdbg)) != sizeof(*rdbg)) {
debug(2, "This process does not have a debug structure!\n");
free(rdbg);
return NULL;
}
return rdbg;
}
static void
rdebug_callback_hit(struct breakpoint *bp, struct Process *proc)
{
fprintf(stderr, "======= HIT\n");
struct r_debug *dbg = NULL;
//struct cb_data data;
debug(DEBUG_FUNCTION, "arch_check_dbg");
if (!(dbg = load_debug_struct(proc))) {
debug(2, "Unable to load debug structure!");
return;
}
if (dbg->r_state == RT_CONSISTENT) {
debug(2, "Linkmap is now consistent");
if (proc->debug_state == RT_ADD) {
debug(2, "Adding DSO to linkmap");
//data.proc = proc;
crawl_linkmap(proc, dbg);
//&data);
} else if (proc->debug_state == RT_DELETE) {
debug(2, "Removing DSO from linkmap");
} else {
debug(2, "Unexpected debug state!");
}
}
proc->debug_state = dbg->r_state;
return;
}
int
linkmap_init(struct Process *proc, target_address_t dyn_addr)
{
void *dbg_addr = NULL;
struct r_debug *rdbg = NULL;
//struct cb_data data;
debug(DEBUG_FUNCTION, "linkmap_init()");
fprintf(stderr, "linkmap_init dyn_addr=%p\n", dyn_addr);
if (find_dynamic_entry_addr(proc, dyn_addr, DT_DEBUG, &dbg_addr) == -1) {
debug(2, "Couldn't find debug structure!");
return -1;
}
proc->debug = dbg_addr;
if (!(rdbg = load_debug_struct(proc))) {
debug(2, "No debug structure or no memory to allocate one!");
return -1;
}
//data.lte = lte;
void *addr;
{
struct library_symbol libsym;
library_symbol_init(&libsym, (target_address_t)rdbg->r_brk,
NULL, 0, LS_TOPLT_NONE);
addr = sym2addr(proc, &libsym);
library_symbol_destroy(&libsym);
}
struct breakpoint *rdebug_bp = insert_breakpoint(proc, addr, NULL);
static struct bp_callbacks rdebug_callbacks = {
.on_hit = rdebug_callback_hit,
};
rdebug_bp->cbs = &rdebug_callbacks;
crawl_linkmap(proc, rdbg);
free(rdbg);
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;
}