diff options
author | Juan Cespedes <cespedes@debian.org> | 1998-03-08 22:31:44 +0100 |
---|---|---|
committer | Juan Cespedes <cespedes@debian.org> | 1998-03-08 22:31:44 +0100 |
commit | 5e01f654d83a95f2acffa86df57a4c2db9b0cae9 (patch) | |
tree | 52440efff51fc1473c436939f4505225ee7edaeb | |
parent | c40e64afa6a897bb7eb6fd4dc21f622632ae215a (diff) | |
download | ltrace-5e01f654d83a95f2acffa86df57a4c2db9b0cae9.tar.gz |
Version 0.2.0
* First Debian unstable release
* Complete re-structured all the code to be able to add support for
different architectures (but only i386 arch is supported in this
version)
* Log also return values
* Log arguments (and return values) for syscalls
* Added preliminary support for various simultaneous processes
* getopt-like options
* New option: -a (alignment column)
* New option: -L (don't display library calls)
* New option: -s (maximum # of chars in strings)
* Now it reads config files with function names and parameter types
* Programs using clone() should work ok now
* debian/rules: gzipped only big files in /usr/doc/ltrace
* Debian: New Standards-Version: 2.4.0.0
* beginning to work on sparc port (not yet done)
-rw-r--r-- | BUGS | 7 | ||||
-rw-r--r-- | COPYING | 5 | ||||
-rw-r--r-- | KK | 79 | ||||
-rw-r--r-- | Makefile | 26 | ||||
-rw-r--r-- | README | 12 | ||||
-rw-r--r-- | THOUGHTS | 25 | ||||
-rw-r--r-- | TODO | 49 | ||||
-rw-r--r-- | all.h | 135 | ||||
-rw-r--r-- | breakpoints.c | 35 | ||||
-rw-r--r-- | config_file.c | 163 | ||||
-rw-r--r-- | config_file.h | 2 | ||||
-rw-r--r-- | debian/changelog | 22 | ||||
-rw-r--r-- | debian/conffiles | 2 | ||||
-rw-r--r-- | debian/control | 11 | ||||
-rw-r--r-- | debian/copyright | 2 | ||||
-rwxr-xr-x | debian/rules | 11 | ||||
-rw-r--r-- | defs.h | 13 | ||||
-rw-r--r-- | display_args.c | 130 | ||||
-rw-r--r-- | elf.c | 45 | ||||
-rw-r--r-- | elf.h | 3 | ||||
-rw-r--r-- | etc/ltrace.conf | 87 | ||||
-rw-r--r-- | etc/ltrace.rc | 37 | ||||
-rw-r--r-- | execute_program.c | 37 | ||||
-rw-r--r-- | functions.c | 182 | ||||
-rw-r--r-- | functions.h | 33 | ||||
-rw-r--r-- | i386.c | 144 | ||||
-rw-r--r-- | i386.h | 35 | ||||
-rw-r--r-- | ltrace.1 | 27 | ||||
-rw-r--r-- | ltrace.c | 129 | ||||
-rw-r--r-- | ltrace.h | 136 | ||||
-rw-r--r-- | options.c | 118 | ||||
-rw-r--r-- | options.h | 19 | ||||
-rw-r--r-- | output.c | 169 | ||||
-rw-r--r-- | output.h | 12 | ||||
-rw-r--r-- | process.c | 216 | ||||
-rw-r--r-- | process.h | 39 | ||||
-rw-r--r-- | process_event.c | 165 | ||||
-rw-r--r-- | signal.c | 35 | ||||
-rw-r--r-- | signal.h | 1 | ||||
-rw-r--r-- | symbols.c | 33 | ||||
-rw-r--r-- | symbols.h | 18 | ||||
-rw-r--r-- | syscall.c | 165 | ||||
-rw-r--r-- | syscall.h | 2 | ||||
-rw-r--r-- | sysdeps/Linux/Makefile | 31 | ||||
-rw-r--r-- | sysdeps/Linux/i386/Makefile | 10 | ||||
-rw-r--r-- | sysdeps/Linux/i386/arch.h | 5 | ||||
-rw-r--r-- | sysdeps/Linux/i386/breakpoint.c | 23 | ||||
-rw-r--r-- | sysdeps/Linux/i386/regs.c | 18 | ||||
-rw-r--r-- | sysdeps/Linux/i386/signalent.h | 32 | ||||
-rw-r--r-- | sysdeps/Linux/i386/syscallent.h | 164 | ||||
-rw-r--r-- | sysdeps/Linux/i386/trace.c | 85 | ||||
-rw-r--r-- | sysdeps/Linux/sparc/Makefile | 9 | ||||
-rw-r--r-- | sysdeps/Linux/sparc/arch.h | 6 | ||||
-rw-r--r-- | sysdeps/Linux/sparc/breakpoint.c | 17 | ||||
-rw-r--r-- | sysdeps/Linux/sparc/regs.c | 18 | ||||
-rw-r--r-- | sysdeps/Linux/sparc/signalent.h | 0 | ||||
-rw-r--r-- | sysdeps/Linux/sparc/syscallent.h | 0 | ||||
-rw-r--r-- | sysdeps/Linux/sparc/trace.c | 66 | ||||
-rw-r--r-- | sysdeps/Linux/trace.c | 48 | ||||
-rwxr-xr-x | tests/execl | bin | 4213 -> 0 bytes | |||
-rw-r--r-- | tests/execl.c | 4 | ||||
-rwxr-xr-x | tests/fork | bin | 4332 -> 0 bytes | |||
-rw-r--r-- | tests/fork.c | 6 | ||||
-rwxr-xr-x | tests/hello | bin | 4178 -> 0 bytes | |||
-rw-r--r-- | tests/hello.c | 4 | ||||
-rwxr-xr-x | tests/int | bin | 4005 -> 0 bytes | |||
-rw-r--r-- | tests/int.c | 4 | ||||
-rwxr-xr-x | tests/kill | bin | 4095 -> 0 bytes | |||
-rw-r--r-- | tests/kill.c | 6 | ||||
-rw-r--r-- | tests/kill.s | 21 | ||||
-rwxr-xr-x | tests/kill2 | bin | 4111 -> 0 bytes | |||
-rw-r--r-- | tests/kill2.o | bin | 820 -> 0 bytes | |||
-rw-r--r-- | tests/kill2.s | 27 | ||||
-rw-r--r-- | tests/kill3.o | bin | 812 -> 0 bytes | |||
-rw-r--r-- | tests/kill3.s | 29 | ||||
-rwxr-xr-x | tests/trap | bin | 4004 -> 0 bytes | |||
-rw-r--r-- | tests/trap.c | 4 | ||||
-rw-r--r-- | wait_for_something.c | 96 |
78 files changed, 1934 insertions, 1415 deletions
@@ -1,2 +1,5 @@ -Too many... :) - +* options -p and -f don't work yet +* Manual page is not accurate (config files...) +* elf.c only supports elf32 binaries +* netscape sometimes dies with SIGSEGV (is this still true?) +* Only Linux/i386 is supported @@ -2,7 +2,7 @@ Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 675 Mass Ave, Cambridge, MA 02139, USA + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -305,7 +305,8 @@ the "copyright" line and a pointer to where the full notice is found. 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., 675 Mass Ave, Cambridge, MA 02139, USA. + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Also add information on how to contact you by electronic and paper mail. @@ -0,0 +1,79 @@ +atexit(0x40006150) = 0 +__libc_init_first(2, 0xbffffb6e, 0xbffffb7d, 0, 0xbffffb82) = 0x40006150 +atexit(0x0804cc20) = 0 +socket(2, 2, 0, 0x0804a0d5, 0x4000b070) = 5 +socket(4, 2, 0, 2, 2) = -1 +socket(3, 2, 0, 4, 2) = -1 +socket(5, 2, 0, 0x0804a0d5, 0x4000b070) = -1 +strncpy(0xbffff9b4, "eth0", 16) = 0xbffff9b4 +memset(0xbffff828, '\000', 340) = 0xbffff828 +strcpy(0xbffff828, "eth0") = 0xbffff828 +strcpy(0xbffff7e8, "eth0") = 0xbffff7e8 +ioctl(5, 35091, 0xbffff7e8, 0xbffff7e8, 0xbffff9b4) = 0 +strcpy(0xbffff7e8, "eth0") = 0xbffff7e8 +ioctl(5, 35111, 0xbffff7e8, 0xbffff7e8, 0xbffff9b4) = 0 +strcpy(0xbffff7e8, "eth0") = 0xbffff7e8 +ioctl(5, 35101, 0xbffff7e8, 0xbffff7e8, 0xbffff9b4) = 0 +strcpy(0xbffff7e8, "eth0") = 0xbffff7e8 +ioctl(5, 35105, 0xbffff7e8, 0xbffff7e8, 0xbffff9b4) = 0 +strcpy(0xbffff7e8, "eth0") = 0xbffff7e8 +ioctl(5, 35184, 0xbffff7e8, 0xbffff7e8, 0xbffff9b4) = 0 +strcpy(0xbffff7e8, "eth0") = 0xbffff7e8 +ioctl(5, 35184, 0xbffff7e8, 0xbffff7e8, 0xbffff9b4) = 0 +strcpy(0xbffff7e8, "eth0") = 0xbffff7e8 +ioctl(5, 35095, 0xbffff7e8, 0xbffff9b4, 0xbffff828) = 0 +strcpy(0xbffff7e8, "eth0") = 0xbffff7e8 +ioctl(5, 35097, 0xbffff7e8, 0xbffff9b4, 0xbffff828) = 0 +strcpy(0xbffff7e8, "eth0") = 0xbffff7e8 +ioctl(5, 35099, 0xbffff7e8, 0xbffff9b4, 0xbffff828) = 0 +strcpy(0xbffff7e8, "eth0") = 0xbffff7e8 +ioctl(5, 35093, 0xbffff7e8, 0xbffff9b4, 0xbffff828) = 0 +strcpy(0xbffff7e8, "eth0") = 0xbffff7e8 +strcpy(0xbffff7e8, "eth0") = 0xbffff7e8 +fopen(0x0804cfe0, 0x0804cfde, 0xbffff7e8, 0xbffff828, 0xbffff9b4) = 0x0804f828 +fgets("Inter-| Receive "..., 255, 0x0804f828) = 0xbffff6d0 +fgets(" face |packets errs drop fifo fr"..., 255, 0x0804f828) = 0xbffff6d0 +strstr(" face |packets errs drop fifo fr"..., "bytes") = NULL +fgets(" lo: 26 0 0 0 "..., 255, 0x0804f828) = 0xbffff6d0 +strncmp("lo: 26 0 0 0 0 "..., "eth0", 4) = 7 +fgets(" lo:0: 0 0 0 0 "..., 255, 0x0804f828) = 0xbffff6d0 +strncmp("lo:0: 0 0 0 0 0"..., "eth0", 4) = 7 +fgets(" eth0: 0 0 0 0 "..., 255, 0x0804f828) = 0xbffff6d0 +strncmp("eth0: 0 0 0 0 0"..., "eth0", 4) = 0 +strchr(0xbffff6d2, 58, 0xbffff7e8, 0xbffff828, 0xbffff9b4) = 0xbffff6d6 +strchr(0xbffff6d7, 58, 0xbffff7e8, 0xbffff828, 0xbffff9b4) = 0 +strncmp("0 0 0 0 0 0 "..., "No", 2) = -30 +sscanf(0xbffff6dd, 0x0804d02b, 0xbffff924, 0xbffff934, 0xbffff93c) = 11 +fclose(0x0804f828) = 0 +strncmp("eth0", "lo", 2) = -7 +printf("%-8.8s Link encap:%s ") = 31 +sprintf(0x0804f240, 0x0804d740, 0, 80, 78) = 17 +printf("HWaddr %s ") = 26 +printf("\n") = 1 +inet_ntoa(0x010010ac, 0x0804ed64, 0xbffff828, 0x0804ef2c, 1) = 0x0804f828 +strcpy(0x0804f5d4, "172.16.0.1") = 0x0804f5d4 +printf(" %s addr:%s") = 30 +inet_ntoa(0xff0010ac, 0x0804ed64, 0xbffff828, 0x0804ef2c, 1) = 0x0804f828 +strcpy(0x0804f5d4, "172.16.0.255") = 0x0804f5d4 +printf(" Bcast:%s ") = 22 +inet_ntoa(0x00ffffff, 0x0804ed64, 0xbffff828, 0x0804ef2c, 1) = 0x0804f828 +strcpy(0x0804f5d4, "255.255.255.0") = 0x0804f5d4 +printf("Mask:%s\n") = 19 +printf(" ") = 10 +printf("UP ") = 3 +printf("BROADCAST ") = 10 +printf("RUNNING ") = 8 +printf("MULTICAST ") = 10 +printf(" MTU:%d Metric:%d\n") = 20 +printf(" ") = 10 +printf("RX packets:%lu error:%lu dropped"...) = 46 +printf(" ") = 10 +printf("TX packets:%lu error:%lu dropped"...) = 58 +printf(" ") = 10 +printf("Interrupt:%d ") = 12 +printf("Base address:0x%x ") = 19 +printf("\n") = 1 +printf("\n") = 1 +close(5) = 0 +exit(0) ++++ exited (status 0) +++ @@ -1,13 +1,23 @@ +##ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) +OS := $(shell uname -s) + +TOPDIR = $(shell pwd) + CC = gcc -CFLAGS = -O2 -g -Wall +CFLAGS = -O2 -g -Wall -I$(TOPDIR) -I$(TOPDIR)/sysdeps/$(OS) -I- #-I$(TOPDIR)/sysdeps/$(ARCH) -OBJ = ltrace.o functions.o elf.o i386.o symbols.o process.o syscall.o output.o signal.o +OBJ = ltrace.o options.o elf.o output.o config_file.o \ + execute_program.o wait_for_something.o process_event.o \ + display_args.o breakpoints.o -all: ltrace +all: dummy + $(MAKE) -C sysdeps/$(OS) + $(MAKE) ltrace -ltrace: ltrace.o $(OBJ) +ltrace: sysdeps/sysdep.o $(OBJ) clean: + $(MAKE) -C sysdeps/$(OS) clean rm -f ltrace $(OBJ) dist: clean @@ -15,6 +25,12 @@ dist: clean install: ltrace install -d $(DESTDIR)/usr/bin $(DESTDIR)/usr/doc/ltrace $(DESTDIR)/usr/man/man1 + install -d $(DESTDIR)/etc install -s ltrace $(DESTDIR)/usr/bin - install -m 644 README $(DESTDIR)/usr/doc/ltrace + install -m 644 etc/ltrace.conf $(DESTDIR)/etc + install -m 644 COPYING README TODO BUGS $(DESTDIR)/usr/doc/ltrace install -m 644 ltrace.1 $(DESTDIR)/usr/man/man1 + +dummy: + +.EXPORT_ALL_VARIABLES: @@ -2,7 +2,7 @@ A Dynamic Library Tracer - Copyright 1997 Juan Cespedes <cespedes@debian.org> + Copyright 1997,1998 Juan Cespedes <cespedes@debian.org> Contents @@ -22,10 +22,12 @@ calls instead of system calls. 2. Where can I find it ---------------------- -At the moment, it's only available as a Debian package, but it should -work at least with any other i386 ELF Linux. It's in: - * ftp://ftp.etsit.upm.es/pub/Linux/local/ltrace_* - * ftp://ftp.debian.org/debian/project/experimental/ltrace_* +At the moment, it's only available as a Debian package. Please let +me know if you distribute it any other way. + +You may find it at: + * ftp://ftp.debian.org/debian/dists/unstable/main/source/utils/ltrace_* + Alternatively, you may find it in any Debian mirror. For more info, see ftp://ftp.debian.org/debian/README.mirrors diff --git a/THOUGHTS b/THOUGHTS deleted file mode 100644 index 42ffb0f..0000000 --- a/THOUGHTS +++ /dev/null @@ -1,25 +0,0 @@ -process_options(); -open_program(); -enable_all_breakpoints(); -execute_program(); -forever { - wait_for_something(); - switch(what happened) { - signal: - log_it(); - exit: - log_it(); - if (no_more_processes()) { exit(0); } - syscall: - log_it(); - sysret: - log_it(); - breakpoint: - libcall: - enable_brk_on_libret(); - log_it(); - libret: - log_it(); - singlestep: - } -} @@ -1,37 +1,12 @@ -* {enable,disable}_all_breakpoints should be process-dependent - -* All wait4's should be wait_for_child(): - + A process may stop because: - - It execve()'d (needs breakpoints to be added) (DONE) - - It received a signal (DONE) - - It breakpointed at a library call (DONE) - - It breakpointed at a library return - - It breakpointed at a syscall - - It breakpointed at a syscall return - - It breakpointed after a SINGLESTEP - -* ``struct_process'' should also have: - + The state of the process: - - uninitialized (DONE) - - running - - within a given library call - - within a given syscall (DONE) - + All the symbols it has breakpointed, and, for each of them: - - its addr - - the original value in the addr, if appropiate - + Whether a return from a libcall is pending, and if so: - - its return addr - - the original value in the return addr - -* I should parse a config file (/etc/ltrace.conf & ~/.ltracerc) to - handle different arguments to the library calls - -* ltrace should accept `-p' option. (hey, it's not as difficult as it - seems) - -* return values should be logged. That implies: - + Breakpointing each return value - + Disabling breakpoints and displaying ``???'' as return value when - a new function is called - -* All architecture dependent stuff should be moved to ``arch.h'' +* Option -p (trace running process) +* Option -f (trace children) +* SYSCALLS: + + execve() <- trace new program +* Display different argument types: + + format + + stringN should not display `...' when limit of bytes is reached +* Update /etc/ltrace.conf +* SPARC: + + almost all... +* netscape: + + Why does it shows so many `breakpointed at:' messages? @@ -1,135 +0,0 @@ -/* elf.c: */ -extern int read_elf(const char *); - - -/* - * Lista de types: - */ - -#define _T_INT 1 -#define _T_ADDR 6 - -#define _T_UNKNOWN -1 -#define _T_VOID 0 -#define _T_INT 1 -#define _T_UINT 2 -#define _T_OCTAL 3 -#define _T_CHAR 4 -#define _T_STRING 5 -#define _T_ADDR 6 -#define _T_FILE 7 -#define _T_HEX 8 -#define _T_FORMAT 9 /* printf-like format */ - -#define _T_OUTPUT 0x80 /* OR'ed if arg is an OUTPUT value */ - -struct function { - const char * function_name; - int return_type; - int num_params; - int params_type[10]; - struct function * next; -}; - -extern struct function * list_of_functions; - -extern void print_function(const char *, int pid, int esp); -#ifndef _LTRACE_I386_H -#define _LTRACE_I386_H - -#define BREAKPOINT {0xcc} -#define BREAKPOINT_LENGTH 1 - -struct breakpoint { - unsigned long addr; - unsigned char value[BREAKPOINT_LENGTH]; -}; - -void insert_breakpoint(int pid, struct breakpoint * sbp); -void delete_breakpoint(int pid, struct breakpoint * sbp); -unsigned long get_eip(int pid); -unsigned long get_esp(int pid); -unsigned long get_orig_eax(int pid); -unsigned long get_return(int pid, unsigned long esp); -unsigned long get_arg(int pid, unsigned long esp, int arg_num); -int is_there_a_breakpoint(int pid, unsigned long eip); -void continue_after_breakpoint(int pid, struct breakpoint * sbp, int delete_it); -void continue_process(int pid, int signal); -void trace_me(void); -void untrace_pid(int pid); - -#include "process.h" -#define PROC_BREAKPOINT 1 -#define PROC_SYSCALL 2 -#define PROC_SYSRET 3 -int type_of_stop(struct process * proc, int *what); - -#endif -#include <stdio.h> - -extern FILE * output; -extern int opt_d; -extern int opt_i; -extern int opt_S; -void send_left(const char * fmt, ...); -void send_right(const char * fmt, ...); -void send_line(const char * fmt, ...); -#ifndef _LTRACE_PROCESS_H -#define _LTRACE_PROCESS_H - -#include "i386.h" - -/* not ready yet */ -#if 0 -struct symbols_from_filename { - char * filename; - struct library_symbol * list_of_symbols; -} - -struct library_symbol { - char * name; - unsigned long addr; - unsigned long return_addr; - unsigned char old_value[BREAKPOINT_LENGTH]; - struct library_symbol * next; -}; -#endif - -struct process { - char * filename; /* from execve() (TODO) */ - int pid; - int breakpoints_enabled; - struct breakpoint return_value; /* if within a function */ - int syscall_number; /* outside syscall => -1 */ - struct process * next; -}; - -extern struct process * list_of_processes; - -unsigned int instruction_pointer; - -int execute_process(const char * file, char * const argv[]); -void wait_for_child(void); - -#endif -extern char * signal_name[]; -#ifndef _LTRACE_SYMBOLS_H -#define _LTRACE_SYMBOLS_H - -#include "i386.h" - -struct library_symbol { - char * name; - struct breakpoint sbp; - unsigned long return_addr; - struct library_symbol * next; -}; - -extern struct library_symbol * library_symbols; - -void enable_all_breakpoints(int pid); -void disable_all_breakpoints(int pid); - -#endif - -extern char * syscall_list[]; diff --git a/breakpoints.c b/breakpoints.c new file mode 100644 index 0000000..ded819d --- /dev/null +++ b/breakpoints.c @@ -0,0 +1,35 @@ +#include "ltrace.h" +#include "options.h" +#include "output.h" + +void enable_all_breakpoints(struct process * proc) +{ + if (proc->breakpoints_enabled <= 0) { + struct library_symbol * tmp = proc->list_of_symbols; + + if (opt_d>0) { + output_line(0, "Enabling breakpoints for pid %u...", proc->pid); + } + while(tmp) { + insert_breakpoint(proc->pid, &tmp->brk); + tmp = tmp->next; + } + } + proc->breakpoints_enabled = 1; +} + +void disable_all_breakpoints(struct process * proc) +{ + if (proc->breakpoints_enabled) { + struct library_symbol * tmp = proc->list_of_symbols; + + if (opt_d>0) { + output_line(0, "Disabling breakpoints for pid %u...", proc->pid); + } + while(tmp) { + delete_breakpoint(proc->pid, &tmp->brk); + tmp = tmp->next; + } + } + proc->breakpoints_enabled = 0; +} diff --git a/config_file.c b/config_file.c new file mode 100644 index 0000000..71e7d3a --- /dev/null +++ b/config_file.c @@ -0,0 +1,163 @@ +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +#include "ltrace.h" +#include "config_file.h" +#include "options.h" +#include "output.h" + +/* + * "void" LT_PT_VOID + * "int" LT_PT_INT + * "uint" LT_PT_UINT + * "octal" LT_PT_OCTAL + * "char" LT_PT_CHAR + * "string" LT_PT_STRING + * "format" LT_PT_FORMAT + * "addr" LT_PT_ADDR + */ + +struct function * list_of_functions = NULL; + +static struct list_of_pt_t { + char * name; + enum param_type pt; +} list_of_pt[] = { + { "void", LT_PT_VOID }, + { "int", LT_PT_INT }, + { "uint", LT_PT_UINT }, + { "octal", LT_PT_OCTAL }, + { "char", LT_PT_CHAR }, + { "addr", LT_PT_ADDR }, + { "file", LT_PT_FILE }, + { "format", LT_PT_FORMAT }, + { "string", LT_PT_STRING }, + { "string0", LT_PT_STRING0 }, + { "string1", LT_PT_STRING1 }, + { "string2", LT_PT_STRING2 }, + { "string3", LT_PT_STRING3 }, + { NULL, LT_PT_UNKNOWN } /* Must finish with NULL */ +}; + +static enum param_type str2type(char ** str) +{ + struct list_of_pt_t * tmp = &list_of_pt[0]; + + while(tmp->name) { + if (!strncmp(*str, tmp->name, strlen(tmp->name)) + && index(" ,)#", *(*str+strlen(tmp->name)))) { + *str += strlen(tmp->name); + return tmp->pt; + } + tmp++; + } + return LT_PT_UNKNOWN; +} + +static void eat_spaces(char ** str) +{ + while(**str==' ') { + (*str)++; + } +} + +static int line_no; +static char * filename; + +struct function * process_line (char * buf) { + struct function fun; + struct function * fun_p; + char * str = buf; + char * tmp; + int i; + + line_no++; + if (opt_d>1) { + output_line(0, "Reading line %d of `%s'", line_no, filename); + } + eat_spaces(&str); + fun.return_type = str2type(&str); + if (fun.return_type==LT_PT_UNKNOWN) { + if (opt_d>1) { + output_line(0, " Skipping line %d", line_no); + } + return NULL; + } + if (opt_d>2) { + output_line(0, " return_type = %d", fun.return_type); + } + eat_spaces(&str); + tmp = strpbrk(str, " ("); + if (!tmp) { + output_line(0, "Syntax error in `%s', line %d", filename, line_no); + return NULL; + } + *tmp = '\0'; + fun.name = strdup(str); + str = tmp+1; + if (opt_d>2) { + output_line(0, " name = %s", fun.name); + } + fun.params_right = 0; + for(i=0; i<MAX_ARGS; i++) { + eat_spaces(&str); + if (*str == ')') { + break; + } + if (str[0]=='+') { + fun.params_right++; + str++; + } else if (fun.params_right) { + fun.params_right++; + } + fun.param_types[i] = str2type(&str); + if (fun.return_type==LT_PT_UNKNOWN) { + output_line(0, "Syntax error in `%s', line %d", filename, line_no); + return NULL; + } + eat_spaces(&str); + if (*str==',') { + str++; + continue; + } else if (*str==')') { + continue; + } else { + output_line(0, "Syntax error in `%s', line %d", filename, line_no); + return NULL; + } + } + fun.num_params = i; + fun_p = malloc(sizeof(struct function)); + memcpy(fun_p, &fun, sizeof(struct function)); + return fun_p; +} + +void read_config_file(char * file) +{ + FILE * stream; + char buf[1024]; + + filename = file; + + if (opt_d) { + output_line(0, "Reading config file `%s'...", filename); + } + + stream = fopen(filename, "r"); + if (!stream) { + return; + } + line_no=0; + while (fgets(buf, 1024, stream)) { + struct function * tmp = process_line(buf); + + if (tmp) { + if (opt_d) { + output_line(0, "New function: `%s'", tmp->name); + } + tmp->next = list_of_functions; + list_of_functions = tmp; + } + } +} diff --git a/config_file.h b/config_file.h new file mode 100644 index 0000000..9230c0a --- /dev/null +++ b/config_file.h @@ -0,0 +1,2 @@ +extern void read_config_file(char*); + diff --git a/debian/changelog b/debian/changelog index c06caad..340ca18 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,27 @@ +ltrace (0.2.0) unstable; urgency=low + + * First `unstable' release + * Complete re-structured all the code to be able to add support for + different architectures (but only i386 arch is supported in this + version) + * Log also return values + * Log arguments (and return values) for syscalls + * Added preliminary support for various simultaneous processes + * getopt-like options + * New option: -a (alignment column) + * New option: -L (don't display library calls) + * New option: -s (maximum # of chars in strings) + * Now it reads config files with function names and parameter types + * Programs using clone() should work ok now + * debian/rules: gzipped only big files in /usr/doc/ltrace + * New Standards-Version: 2.4.0.0 + * beginning to work on sparc port (not yet done) + + -- Juan Cespedes <cespedes@debian.org> Sun, 8 Mar 1998 22:27:30 +0100 + ltrace (0.1.7) experimental; urgency=low + * Internal release. * New Standards-Version (2.3.0.1) * Clean up structures a bit * Trying to log return values... diff --git a/debian/conffiles b/debian/conffiles index 0b4e677..1c10cee 100644 --- a/debian/conffiles +++ b/debian/conffiles @@ -1 +1 @@ -/etc/ltrace.rc +/etc/ltrace.conf diff --git a/debian/control b/debian/control index 396ce14..8c54af0 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: ltrace Section: utils Priority: optional Maintainer: Juan Cespedes <cespedes@debian.org> -Standards-Version: 2.3.0.0 +Standards-Version: 2.4.0.0 Package: ltrace Architecture: i386 @@ -10,5 +10,12 @@ Depends: ${shlibs:Depends} Description: A library call tracer ltrace is a library call tracer, i.e. a debugging tool which prints out a trace of all the dynamic library calls made by another process/program. + . + It also displays system calls, as well as `strace', but strace still + does a better job displaying arguments to system calls. + . The program to be traced need not be recompiled for this, so you can - use it on binaries for which you don't have source. + use it on binaries for which you don't have the source handy. + . + This is still a work in progress, so some things may fail or don't work + as expected. diff --git a/debian/copyright b/debian/copyright index 106ee71..12fad64 100644 --- a/debian/copyright +++ b/debian/copyright @@ -4,7 +4,7 @@ Dynamic Library Tracer ``ltrace''. Copyrights ---------- -Copyright (C) 1997 Juan Cespedes <cespedes@debian.org> +Copyright (C) 1997,1998 Juan Cespedes <cespedes@debian.org> License diff --git a/debian/rules b/debian/rules index 35be3a5..23bb348 100755 --- a/debian/rules +++ b/debian/rules @@ -1,6 +1,6 @@ #! /usr/bin/make -f # -# Copyright (C) 1997 Juan Cespedes <cespedes@debian.org> +# Copyright (C) 1997,1998 Juan Cespedes <cespedes@debian.org> build: $(MAKE) @@ -15,16 +15,17 @@ binary-arch: build $(RM) -rf debian/tmp install -d debian/tmp/DEBIAN + cp debian/conffiles debian/tmp/DEBIAN $(MAKE) install DESTDIR=`pwd`/debian/tmp - cp -p README debian/tmp/usr/doc/ltrace - cp -p BUGS debian/tmp/usr/doc/ltrace - cp -p TODO debian/tmp/usr/doc/ltrace + $(RM) debian/tmp/usr/doc/ltrace/COPYING cp -p debian/changelog debian/tmp/usr/doc/ltrace - gzip -9fv debian/tmp/usr/doc/ltrace/* + gzip -9f debian/tmp/usr/doc/ltrace/README debian/tmp/usr/doc/ltrace/changelog cp -p debian/copyright debian/tmp/usr/doc/ltrace gzip -9f debian/tmp/usr/man/man1/* dpkg-shlibdeps debian/tmp/usr/bin/ltrace dpkg-gencontrol + chown -R root.root debian/tmp + chmod -R u+rw,go-w debian/tmp dpkg --build debian/tmp .. clean: @@ -0,0 +1,13 @@ + +#ifndef DEFAULT_ACOLUMN +#define DEFAULT_ACOLUMN 50 /* default alignment column for results */ +#endif /* (-a switch) */ + +#ifndef MAX_ARGS +#define MAX_ARGS 32 /* maximum number of args to a syscall */ +#endif + +#ifndef DEFAULT_STRLEN +#define DEFAULT_STRLEN 32 /* default maximum # of bytes printed in */ +#endif /* strings (-s switch) */ + diff --git a/display_args.c b/display_args.c new file mode 100644 index 0000000..e5ca197 --- /dev/null +++ b/display_args.c @@ -0,0 +1,130 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "ltrace.h" +#include "options.h" + +static int display_char(char what); +static int display_string(enum tof type, struct process * proc, int arg_num); +static int display_stringN(int arg2, enum tof type, struct process * proc, int arg_num); +static int display_unknown(enum tof type, struct process * proc, int arg_num); + +int display_arg(enum tof type, struct process * proc, int arg_num, enum param_type rt) +{ + int tmp; + long arg; + + switch(rt) { + case LT_PT_VOID: + return 0; + case LT_PT_INT: + return fprintf(output, "%d", (int)gimme_arg(type, proc, arg_num)); + case LT_PT_UINT: + return fprintf(output, "%u", (unsigned)gimme_arg(type, proc, arg_num)); + case LT_PT_OCTAL: + return fprintf(output, "0%o", (unsigned)gimme_arg(type, proc, arg_num)); + case LT_PT_CHAR: + tmp = fprintf(output, "'"); + tmp += display_char((int)gimme_arg(type, proc, arg_num)); + tmp += fprintf(output, "'"); + return tmp; + case LT_PT_ADDR: + arg = gimme_arg(type, proc, arg_num); + if (!arg) { + return fprintf(output, "NULL"); + } else { + return fprintf(output, "0x%08x", (unsigned)arg); + } + case LT_PT_FORMAT: + case LT_PT_STRING: + return display_string(type, proc, arg_num); + case LT_PT_STRING0: + return display_stringN(0, type, proc, arg_num); + case LT_PT_STRING1: + return display_stringN(1, type, proc, arg_num); + case LT_PT_STRING2: + return display_stringN(2, type, proc, arg_num); + case LT_PT_STRING3: + return display_stringN(3, type, proc, arg_num); + case LT_PT_UNKNOWN: + default: + return display_unknown(type, proc, arg_num); + } + return fprintf(output, "?"); +} + +static int display_char(char what) +{ + switch(what) { + case -1: return fprintf(output, "EOF"); + case '\r': return fprintf(output, "\\r"); + case '\n': return fprintf(output, "\\n"); + case '\t': return fprintf(output, "\\t"); + case '\\': return fprintf(output, "\\"); + default: + if ((what<32) || (what>126)) { + return fprintf(output, "\\%03o", what); + } else { + return fprintf(output, "%c", what); + } + } +} + +static int display_string(enum tof type, struct process * proc, int arg_num) +{ + void * addr; + char * str1; + int i; + int len=0; + + addr = (void *)gimme_arg(type, proc, arg_num); + if (!addr) { + return fprintf(output, "NULL"); + } + + str1 = malloc(opt_s+3); + if (!str1) { + return fprintf(output, "???"); + } + umovestr(proc, addr, opt_s+1, str1); + len = fprintf(output, "\""); + for(i=0; len<opt_s+1; i++) { + if (str1[i]) { + len += display_char(str1[i]); + } else { + break; + } + } + len += fprintf(output, "\""); + if (str1[i]) { + len += fprintf(output, "..."); + } + free(str1); + return len; +} + +#define MIN(a,b) (((a)<(b)) ? (a) : (b)) + +static int display_stringN(int arg2, enum tof type, struct process * proc, int arg_num) +{ + int a,b; + a = gimme_arg(type, proc, arg2-1); + b = opt_s; + opt_s = MIN(opt_s, a); + a = display_string(type, proc, arg_num); + opt_s = b; + return a; +} + +static int display_unknown(enum tof type, struct process * proc, int arg_num) +{ + long tmp; + + tmp = gimme_arg(type, proc, arg_num); + + if (tmp<1000000 && tmp>-1000000) { + return fprintf(output, "%ld", tmp); + } else { + return fprintf(output, "0x%08lx", tmp); + } +} @@ -8,29 +8,31 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> -#include <linux/elf.h> +#include <elf.h> #include <sys/mman.h> #include <string.h> +#include <unistd.h> -#include "elf.h" #include "ltrace.h" -#include "symbols.h" +#include "elf.h" +#include "options.h" #include "output.h" -int read_elf(const char *filename) +struct library_symbol * read_elf(const char *filename) { + struct library_symbol * library_symbols = NULL; struct stat sbuf; int fd; void * addr; - struct elf32_hdr * hdr; + Elf32_Ehdr * hdr; Elf32_Shdr * shdr; - struct elf32_sym * symtab = NULL; + Elf32_Sym * symtab = NULL; int i; char * strtab = NULL; u_long symtab_len = 0; if (opt_d>0) { - send_line("Reading symbol table from %s...", filename); + output_line(0, "Reading symbol table from %s...", filename); } fd = open(filename, O_RDONLY); @@ -42,8 +44,8 @@ int read_elf(const char *filename) fprintf(stderr, "Can't stat \"%s\": %s\n", filename, sys_errlist[errno]); exit(1); } - if (sbuf.st_size < sizeof(struct elf32_hdr)) { - fprintf(stderr, "\"%s\" is not an ELF object\n", filename); + if (sbuf.st_size < sizeof(Elf32_Ehdr)) { + fprintf(stderr, "\"%s\" is not an ELF binary object\n", filename); exit(1); } addr = mmap(NULL, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); @@ -53,7 +55,7 @@ int read_elf(const char *filename) } hdr = addr; if (strncmp(hdr->e_ident, ELFMAG, SELFMAG)) { - fprintf(stderr, "\"%s\" is not an ELF object\n", filename); + fprintf(stderr, "\"%s\" is not an ELF binary object\n", filename); exit(1); } for(i=0; i<hdr->e_shnum; i++) { @@ -61,9 +63,9 @@ int read_elf(const char *filename) if (shdr->sh_type == SHT_DYNSYM) { if (!symtab) { #if 0 - symtab = (struct elf32_sym *)shdr->sh_addr; + symtab = (Elf32_Sym *)shdr->sh_addr; #else - symtab = (struct elf32_sym *)(addr + shdr->sh_offset); + symtab = (Elf32_Sym *)(addr + shdr->sh_offset); #endif symtab_len = shdr->sh_size; } @@ -75,14 +77,15 @@ int read_elf(const char *filename) } } if (opt_d>1) { - send_line("symtab: 0x%08x", (unsigned)symtab); - send_line("symtab_len: %lu", symtab_len); - send_line("strtab: 0x%08x", (unsigned)strtab); + output_line(0, "symtab: 0x%08x", (unsigned)symtab); + output_line(0, "symtab_len: %lu", symtab_len); + output_line(0, "strtab: 0x%08x", (unsigned)strtab); } if (!symtab) { - return 0; + close(fd); + return NULL; } - for(i=0; i<symtab_len/sizeof(struct elf32_sym); i++) { + for(i=0; i<symtab_len/sizeof(Elf32_Sym); i++) { if (!((symtab+i)->st_shndx) && (symtab+i)->st_value) { struct library_symbol * tmp = library_symbols; @@ -91,16 +94,18 @@ int read_elf(const char *filename) perror("malloc"); exit(1); } - library_symbols->sbp.addr = ((symtab+i)->st_value); + library_symbols->brk.addr = (void *)((symtab+i)->st_value); + library_symbols->brk.enabled = 0; library_symbols->name = strtab+(symtab+i)->st_name; library_symbols->next = tmp; if (opt_d>1) { - send_line("addr: 0x%08x, symbol: \"%s\"", + output_line(0, "addr: 0x%08x, symbol: \"%s\"", (unsigned)((symtab+i)->st_value), (strtab+(symtab+i)->st_name)); } } } - return 1; + close(fd); + return library_symbols; } @@ -1,3 +1,4 @@ +#include "ltrace.h" -extern int read_elf(const char *); +extern struct library_symbol * read_elf(const char *); diff --git a/etc/ltrace.conf b/etc/ltrace.conf new file mode 100644 index 0000000..1ce3e6c --- /dev/null +++ b/etc/ltrace.conf @@ -0,0 +1,87 @@ +; ltrace.conf + +; Argument types: +; + == May vary (ie, is a returned value) (prefix) +; void +; int +; uint == (unsigned int) +; octal == (unsigned) [written in octal] +; char +; addr == (void *) [unsigned, written in hexa] +; file == (FILE *) [TODO] +; format == ((const char *), ...) [printf() like] [TODO] +; string == (char *) +; stringN == (char *) [N>=0] [show only up to (arg N) bytes] + +; errno.h +addr __errno_location(void); + +; fcntl.h +int open(string,int,octal); ; WARNING: 3rd argument may not be there + +; getopt.h +int getopt_long(int,addr,string,addr,addr); + +; libintl.h +string bindtextdomain(string, string); +string textdomain(string); + +; libio.h +int _IO_putc(char,file); + +; locale.h +string setlocale(int, string); + +; stdio.h +int fclose(file); +addr fgets(+string, uint, file); +int fprintf(file,format); +int fputs(string,file); +int printf(format); + +; unistd.h +int close(int); +int fork(void); +int geteuid(void); +int gethostname(+string2,int); +int mkdir(string,octal); +int read(int, +string0, uint); +int sethostname(+string2,int); +uint sleep(uint); +int sync(void); +int write(int, string3, uint); + +; stdlib.h +int atexit(addr); +void exit(int); +void free(addr); +addr malloc(int); + +; string.h +addr memset(addr,char,int); +string rindex(string,char); +int strcmp(string,string); +addr strcpy(addr,string); +addr strdup(string); +int strncmp(string,string,int); +addr strncpy(addr,string3,uint); +string strrchr(string,char); +string strstr(string,string); + +; time.h +int time(addr); + +; SYSCALLS +addr SYS_brk(addr); +int SYS_close(int); +int SYS_execve(string,addr,addr); +void SYS_exit(int); +int SYS_fork(void); +int SYS_getpid(void); +;addr SYS_mmap(addr,int,int,int,int,int); +int SYS_munmap(addr,uint); +int SYS_open(string,int,octal); +int SYS_personality(uint); +int SYS_read(int,+string0,uint); +int SYS_write(int,string3,uint); +int SYS_sync(void); diff --git a/etc/ltrace.rc b/etc/ltrace.rc deleted file mode 100644 index 548c39d..0000000 --- a/etc/ltrace.rc +++ /dev/null @@ -1,37 +0,0 @@ -; + == May vary (ie, is a returned value) -; int -; addr == (void *) [int, written in hexa] -; file == (FILE *) -; format == ((char *), ...) [printf() like] -; octal == int [written in octal] -; string == (char *) - -int atexit(addr); -int close(int); -int exit(int); -int fclose(file); -int fprintf(file,format); -int free(addr); -int gethostname(+string,int); -int getopt_long(int,addr,string,addr,addr); -addr malloc(int); -addr memset(addr,char,int); -int mkdir(string,octal); -int open(string,int,octal); <- OJO -int printf(format); -string rindex(string,char); -int strcmp(string,string); -int strncmp(string,string,int); -int time(addr); - -; #define _T_UNKNOWN -1 -; #define _T_VOID 0 -; #define _T_INT 1 -; #define _T_UINT 2 -; #define _T_OCTAL 3 -; #define _T_CHAR 4 -; #define _T_STRING 5 -; #define _T_ADDR 6 -; #define _T_FILE 7 -; #define _T_HEX 8 -; #define _T_FORMAT 9 /* printf-like format */ diff --git a/execute_program.c b/execute_program.c new file mode 100644 index 0000000..02d6471 --- /dev/null +++ b/execute_program.c @@ -0,0 +1,37 @@ +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#include "ltrace.h" +#include "options.h" +#include "output.h" +#include "sysdep.h" + +void execute_program(struct process * sp, char **argv) +{ + int pid; + + if (opt_d) { + output_line(0, "Executing `%s'...", sp->filename); + } + + pid = fork(); + if (pid<0) { + perror("fork"); + exit(1); + } else if (!pid) { /* child */ + trace_me(); + execvp(sp->filename, argv); + fprintf(stderr, "Can't execute `%s': %s\n", sp->filename, strerror(errno)); + exit(1); + } + + if (opt_d) { + output_line(0, "PID=%d", pid); + } + + sp->pid = pid; + + return; +} diff --git a/functions.c b/functions.c deleted file mode 100644 index 7070ecc..0000000 --- a/functions.c +++ /dev/null @@ -1,182 +0,0 @@ -#include <stdio.h> -#include <string.h> -#include <sys/ptrace.h> -#include <stdlib.h> -#include <ctype.h> - -#include "functions.h" -#include "output.h" - -static int current_pid; - -struct function * list_of_functions = NULL; - -struct function functions_list[] = { - {"atexit", _T_INT, 1, {_T_ADDR}}, - {"close", _T_INT, 1, {_T_INT}}, - {"exit", _T_INT, 1, {_T_INT}}, - {"fclose", _T_INT, 1, {_T_FILE}}, - {"fprintf", _T_INT, 2, {_T_FILE, _T_FORMAT}}, - {"free", _T_INT, 1, {_T_ADDR}}, - {"gethostname", _T_INT, 2, {_T_STRING, _T_INT}}, - {"getopt_long", _T_INT, 5, {_T_INT, _T_ADDR, _T_STRING, _T_ADDR, _T_ADDR}}, - {"malloc", _T_ADDR, 1, {_T_UINT}}, - {"memset", _T_ADDR, 3, {_T_ADDR, _T_CHAR, _T_UINT}}, - {"mkdir", _T_INT, 2, {_T_STRING, _T_OCTAL}}, - {"open", _T_INT, 3, {_T_STRING, _T_INT, _T_INT}}, - {"printf", _T_INT, 1, {_T_FORMAT}}, - {"rindex", _T_STRING, 2, {_T_STRING, _T_CHAR}}, - {"strcmp", _T_INT, 2, {_T_STRING, _T_STRING}}, - {"strncmp", _T_INT, 3, {_T_STRING, _T_STRING, _T_INT}}, - {"time", _T_UINT, 1, {_T_ADDR}}, - {NULL, _T_UNKNOWN, 5, {_T_UNKNOWN, _T_UNKNOWN, _T_UNKNOWN, _T_UNKNOWN, _T_UNKNOWN}}, -}; - -static char * process_string(unsigned char * str) -{ - static char tmp[256]; - - tmp[0] = '\0'; - while(*str) { - switch(*str) { - case '\r': strcat(tmp,"\\r"); break; - case '\n': strcat(tmp,"\\n"); break; - case '\t': strcat(tmp,"\\t"); break; - case '\\': strcat(tmp,"\\"); break; - default: - if ((*str<32) || (*str>126)) { - sprintf(tmp,"%s\\%03o", tmp, *str); - } else { - sprintf(tmp, "%s%c", tmp, *str); - } - } - str++; - } - return tmp; -} - -static char * print_string(int addr) -{ - static char tmp[256]; - int a; - int i=0; - - tmp[0] = '\0'; - while(1) { - a = ptrace(PTRACE_PEEKTEXT, current_pid, addr+i, 0); - *(int *)&tmp[i] = a; - if (!tmp[i] || !tmp[i+1] || !tmp[i+2] || !tmp[i+3] || i>100) { - break; - } - i += 4; - } - return process_string(tmp); -} - -static char * print_param(int type, int esp) -{ - static char tmp[256]; - int a; - - a = ptrace(PTRACE_PEEKTEXT, current_pid, esp, 0); - - switch(type) { - case _T_STRING: - case _T_FORMAT: - sprintf(tmp,"\"%s\"",print_string(a)); - break; - default: - if (a<1000000 && a>-1000000) { - sprintf(tmp, "%d", a); - } else { - sprintf(tmp, "0x%08x", a); - } - } - return tmp; -} - -void print_function(const char *name, int pid, int esp) -{ - struct function * tmp; - char message[1024]; - int i; - - current_pid = pid; - - tmp = list_of_functions; - while(tmp) { - if (!strcmp(name, tmp->function_name)) { - break; - } - } - if (!tmp) { - tmp = &functions_list[0]; - while(tmp->function_name) { - if (!strcmp(name, tmp->function_name)) { - break; - } - tmp++; - } - } - sprintf(message, "%s(", name); - if (tmp->num_params>0) { - sprintf(message, "%s%s", message, print_param(tmp->params_type[0], esp+4)); - } - for(i=1; i<tmp->num_params; i++) { - sprintf(message, "%s,%s", message, print_param(tmp->params_type[i], esp+4*(i+1))); - } - send_left("%s", message); - send_right(") = ???"); -} - -static int func_type(char ** buf) -{ - int returned_value = 0; - - if (**buf == '+') { - returned_value |= _T_OUTPUT; - (*buf)++; - } - if (!strcmp(*buf, "int")) { - returned_value |= _T_INT; - *buf += 3; - } else if (!strcmp(*buf, "addr")) { - returned_value |= _T_ADDR; - *buf += 4; - } else { - return -1; - } - return returned_value; -} - -static int fill_fields(struct function * fun, char * buf) -{ - char * tmp = buf; - - for(; (*tmp==' '); tmp++); - if ((fun->return_type = func_type(&buf)) == -1) { - return 0; - } - for(; (*tmp==' '); tmp++); - - return 0; -} - -void read_config_file(const char * filename) -{ - char buf[1024]; - FILE * stream; - struct function tmp; - - stream = fopen(filename, "r"); - if (!stream) { - return; - } - while (fgets(buf, 1024, stream)) { - if (fill_fields(&tmp, buf)) { - tmp.next = list_of_functions; - list_of_functions = malloc(sizeof(struct function)); - bcopy(&tmp, list_of_functions, sizeof(struct function)); - } - } -} diff --git a/functions.h b/functions.h deleted file mode 100644 index 2df9247..0000000 --- a/functions.h +++ /dev/null @@ -1,33 +0,0 @@ - -/* - * Lista de types: - */ - -#define _T_INT 1 -#define _T_ADDR 6 - -#define _T_UNKNOWN -1 -#define _T_VOID 0 -#define _T_INT 1 -#define _T_UINT 2 -#define _T_OCTAL 3 -#define _T_CHAR 4 -#define _T_STRING 5 -#define _T_ADDR 6 -#define _T_FILE 7 -#define _T_HEX 8 -#define _T_FORMAT 9 /* printf-like format */ - -#define _T_OUTPUT 0x80 /* OR'ed if arg is an OUTPUT value */ - -struct function { - const char * function_name; - int return_type; - int num_params; - int params_type[10]; - struct function * next; -}; - -extern struct function * list_of_functions; - -extern void print_function(const char *, int pid, int esp); @@ -1,144 +0,0 @@ -#include <sys/ptrace.h> -#include <sys/types.h> -#include <sys/resource.h> -#include <sys/wait.h> -#include <errno.h> -#include <stdio.h> - -#include "i386.h" -#include "ltrace.h" - -void insert_breakpoint(int pid, struct breakpoint * sbp) -{ - int a; - - a = ptrace(PTRACE_PEEKTEXT, pid, sbp->addr, 0); - sbp->value[0] = a & 0xFF; - a &= 0xFFFFFF00; - a |= 0xCC; - ptrace(PTRACE_POKETEXT, pid, sbp->addr, a); -} - -void delete_breakpoint(int pid, struct breakpoint * sbp) -{ - int a; - - a = ptrace(PTRACE_PEEKTEXT, pid, sbp->addr, 0); - a &= 0xFFFFFF00; - a |= sbp->value[0]; - ptrace(PTRACE_POKETEXT, pid, sbp->addr, a); -} - -unsigned long get_eip(int pid) -{ - unsigned long eip; - - eip = ptrace(PTRACE_PEEKUSER, pid, 4*EIP, 0); - - return eip-1; /* Length of breakpoint (0xCC) is 1 byte */ -} - -unsigned long get_esp(int pid) -{ - unsigned long esp; - - esp = ptrace(PTRACE_PEEKUSER, pid, 4*UESP, 0); - - return esp; -} - -unsigned long get_orig_eax(int pid) -{ - return ptrace(PTRACE_PEEKUSER, pid, 4*ORIG_EAX); -} - -unsigned long get_return(int pid, unsigned long esp) -{ - return ptrace(PTRACE_PEEKTEXT, pid, esp, 0); -} - -unsigned long get_arg(int pid, unsigned long esp, int arg_num) -{ - return ptrace(PTRACE_PEEKTEXT, pid, esp+4*arg_num); -} - -int is_there_a_breakpoint(int pid, unsigned long eip) -{ - int a; - a = ptrace(PTRACE_PEEKTEXT, pid, eip, 0); - if ((a & 0xFF) == 0xCC) { - return 1; - } else { - return 0; - } -} - -void continue_process(int pid, int signal) -{ - ptrace(PTRACE_SYSCALL, pid, 1, signal); -} - -void continue_after_breakpoint(int pid, struct breakpoint * sbp, int delete_it) -{ - delete_breakpoint(pid, sbp); - ptrace(PTRACE_POKEUSER, pid, 4*EIP, sbp->addr); - if (delete_it) { - continue_process(pid, 0); - } else { - int status; - - ptrace(PTRACE_SINGLESTEP, pid, 0, 0); - - pid = wait4(pid, &status, 0, NULL); - if (pid==-1) { - perror("wait4"); - exit(1); - } - insert_breakpoint(pid, sbp); - continue_process(pid, 0); - } -} - -void trace_me(void) -{ - if (ptrace(PTRACE_TRACEME, 0, 1, 0)<0) { - perror("PTRACE_TRACEME"); - exit(1); - } -} - -void untrace_pid(int pid) -{ - if (ptrace(PTRACE_DETACH, pid, 0, 0)<0) { - perror("PTRACE_DETACH"); - exit(1); - } -} - -/* - * Return values: - * PROC_BREAKPOINT - Breakpoint - * PROC_SYSCALL - Syscall entry - * PROC_SYSRET - Syscall return - */ -int type_of_stop(int pid, struct proc_arch * proc_arch, int *what) -{ - *what = get_orig_eax(pid); - - if (*what!=-1) { - if (proc_arch->syscall_number != *what) { - proc_arch->syscall_number = *what; - return PROC_SYSCALL; - } else { - proc_arch->syscall_number = -1; - return PROC_SYSRET; - } - } - - return PROC_BREAKPOINT; -} - -void proc_arch_init(struct proc_arch *proc_arch) -{ - proc_arch->syscall_number=-1; -} @@ -1,35 +0,0 @@ -#ifndef _LTRACE_I386_H -#define _LTRACE_I386_H - -#define BREAKPOINT {0xcc} -#define BREAKPOINT_LENGTH 1 - -struct breakpoint { - unsigned long addr; - unsigned char value[BREAKPOINT_LENGTH]; -}; - -void insert_breakpoint(int pid, struct breakpoint * sbp); -void delete_breakpoint(int pid, struct breakpoint * sbp); -unsigned long get_eip(int pid); -unsigned long get_esp(int pid); -unsigned long get_orig_eax(int pid); -unsigned long get_return(int pid, unsigned long esp); -unsigned long get_arg(int pid, unsigned long esp, int arg_num); -int is_there_a_breakpoint(int pid, unsigned long eip); -void continue_after_breakpoint(int pid, struct breakpoint * sbp, int delete_it); -void continue_process(int pid, int signal); -void trace_me(void); -void untrace_pid(int pid); - -struct proc_arch { - int syscall_number; /* outside syscall => -1 */ -}; - -#define PROC_BREAKPOINT 1 -#define PROC_SYSCALL 2 -#define PROC_SYSRET 3 -int type_of_stop(int pid, struct proc_arch *proc_arch, int *what); -void proc_arch_init(struct proc_arch *proc_arch); - -#endif @@ -6,7 +6,7 @@ ltrace \- A library call tracer .SH SYNOPSIS .B ltrace -.I "[-d] [-o filename] command [arg ...]" +.I "[-diLS] [-a column] [-s strsize] [-o filename] command [arg ...]" .SH DESCRIPTION .B ltrace @@ -21,12 +21,28 @@ Its use is very similar to .SH OPTIONS .TP +.I \-a column +Align return values in a secific column (default column 50). .I \-d Increase the debugging level. .TP +.I \-f +Trace child processes as they are created by +currently traced processes as a result of the fork(2) +or clone(2) system calls. +The new process is attached as soon as its pid is known. +.TP .I \-i Print the instruction pointer at the time of the library call. .TP +.I \-s +Specify the maximum string size to print (the default is 32). +.TP +.I \-L +DON'T display library calls (use it with the +.I \-S +option). +.TP .I \-S Display system calls as well as library calls .TP @@ -42,6 +58,15 @@ If you like to report a bug, send a notice to the author, or use the .BR bug(1) program if you are under Debian GNU/Linux. +.SH FILES +.TP +.I /etc/ltrace.conf +System configuration file +.TP +.I ~/.ltrace.conf +Personal config file, overrides +.I /etc/ltrace.conf + .SH AUTHOR Juan Cespedes <cespedes@debian.org> @@ -1,113 +1,60 @@ #include <stdio.h> #include <stdlib.h> -#include <sys/param.h> -#include <errno.h> #include <unistd.h> #include <string.h> +#include <errno.h> +#include <sys/param.h> +#include "ltrace.h" #include "elf.h" -#include "process.h" #include "output.h" +#include "config_file.h" +#include "options.h" -extern void read_config_file(const char *); +char * command; +struct process * list_of_processes = NULL; -FILE * output = stderr; -int opt_d = 0; /* debug */ -int opt_i = 0; /* instruction pointer */ -int opt_S = 0; /* syscalls */ - -static void usage(void) -{ - fprintf(stderr,"Usage: ltrace [-d] [-i] [-S] [-o filename] command [arg ...]\n\n"); -} +static struct process * open_program(void); -static char * search_for_command(char * filename) +int main(int argc, char **argv) { - static char pathname[MAXPATHLEN]; - char *path; - int m, n; - - if (strchr(filename, '/')) { - return filename; + argv = process_options(argc, argv); + if (opt_p || opt_f) { + fprintf(stderr, "ERROR: Options -p and -f don't work yet\n"); + exit(1); } - for (path = getenv("PATH"); path && *path; path += m) { - if (strchr(path, ':')) { - n = strchr(path, ':') - path; - m = n + 1; - } else { - m = n = strlen(path); - } - strncpy(pathname, path, n); - if (n && pathname[n - 1] != '/') { - pathname[n++] = '/'; - } - strcpy(pathname + n, filename); - if (!access(pathname, X_OK)) { - break; - } + read_config_file("/etc/ltrace.conf"); + if (getenv("HOME")) { + char path[PATH_MAX]; + sprintf(path, getenv("HOME")); /* FIXME: buffer overrun */ + strcat(path, "/.ltrace.conf"); + read_config_file(path); } - if (access(pathname, X_OK)) { - return NULL; - } else { - return pathname; + execute_program(open_program(), argv); + while(1) { + process_event(wait_for_something()); } } -int main(int argc, char **argv) +static struct process * open_program(void) { - int pid; - char * command; - - while ((argc>2) && (argv[1][0] == '-') && (argv[1][2] == '\0')) { - switch(argv[1][1]) { - case 'd': opt_d++; - break; - case 'o': output = fopen(argv[2], "w"); - if (!output) { - fprintf(stderr, "Can't open %s for output: %s\n", argv[2], sys_errlist[errno]); - exit(1); - } - argc--; argv++; - break; - case 'i': opt_i++; - break; - case 'S': opt_S++; - break; - default: fprintf(stderr, "Unknown option '%c'\n", argv[1][1]); - usage(); - exit(1); - } - argc--; argv++; - } - - if (argc<2) { - usage(); - exit(1); - } - command = search_for_command(argv[1]); - if (!command) { - fprintf(stderr, "%s: command not found\n", argv[1]); + list_of_processes = malloc(sizeof(struct process)); + if (!list_of_processes) { + perror("malloc"); exit(1); } - if (!read_elf(command)) { - fprintf(stderr, "%s: Not dynamically linked\n", command); - exit(1); - } - - if (opt_d>0) { - send_line("Reading config file(s)..."); - } - read_config_file("/etc/ltrace.cfg"); - read_config_file(".ltracerc"); - - pid = execute_process(command, argv+1); - if (opt_d>0) { - send_line("pid %u launched", pid); - } - - while(1) { - wait_for_child(); + list_of_processes->filename = command; + list_of_processes->pid = 0; + list_of_processes->breakpoints_enabled = -1; + list_of_processes->current_syscall = -1; + list_of_processes->current_symbol = NULL; + list_of_processes->breakpoint_being_enabled = NULL; + list_of_processes->next = NULL; + if (opt_L) { + list_of_processes->list_of_symbols = read_elf(command); + } else { + list_of_processes->list_of_symbols = NULL; } - exit(0); + return list_of_processes; } @@ -1,7 +1,135 @@ +#ifndef _HCK_LTRACE_H +#define _HCK_LTRACE_H + +#include <sys/types.h> #include <stdio.h> -extern FILE * output; +#include "defs.h" + +/* BREAKPOINT_LENGTH is defined in "sysdep.h" */ +#include "sysdep.h" + +extern char * command; + +struct breakpoint { + void * addr; + unsigned char orig_value[BREAKPOINT_LENGTH]; + int enabled; +}; + +enum param_type { + LT_PT_UNKNOWN=-1, + LT_PT_VOID, + LT_PT_INT, + LT_PT_UINT, + LT_PT_OCTAL, + LT_PT_CHAR, + LT_PT_ADDR, + LT_PT_FILE, + LT_PT_FORMAT, /* printf-like format */ + LT_PT_STRING, + LT_PT_STRING0, /* stringN: string up to (arg N) bytes */ + LT_PT_STRING1, + LT_PT_STRING2, + LT_PT_STRING3 +}; + +enum tof { + LT_TOF_NONE, + LT_TOF_FUNCTION, /* A real library function */ + LT_TOF_SYSCALL /* A syscall */ +}; + +struct function { + const char * name; + enum param_type return_type; + int num_params; + enum param_type param_types[MAX_ARGS]; + int params_right; + struct function * next; +}; + +extern struct function * list_of_functions; + +struct library_symbol { + char * name; + struct breakpoint brk; + + struct library_symbol * next; +}; + +struct process { + char * filename; + pid_t pid; + int breakpoints_enabled; /* -1:not enabled yet, 0:disabled, 1:enabled */ + + int current_syscall; /* -1 for none */ + struct library_symbol * current_symbol; /* NULL for none */ + + struct breakpoint return_value; + struct library_symbol * list_of_symbols; + + /* Arch-dependent: */ + void * instruction_pointer; + void * stack_pointer; /* To get return addr, args... */ + void * return_addr; + struct breakpoint * breakpoint_being_enabled; + + /* output: */ + enum tof type_being_displayed; + + struct process * next; +}; + +struct event { + struct process *proc; + enum { + LT_EV_UNKNOWN, + LT_EV_NONE, + LT_EV_SIGNAL, + LT_EV_EXIT, + LT_EV_EXIT_SIGNAL, + LT_EV_SYSCALL, + LT_EV_SYSRET, + LT_EV_BREAKPOINT + } thing; + union { + int ret_val; /* _EV_EXIT */ + int signum; /* _EV_SIGNAL, _EV_EXIT_SIGNAL */ + int sysnum; /* _EV_SYSCALL, _EV_SYSRET */ + void * brk_addr; /* _EV_BREAKPOINT */ + } e_un; +}; + +extern struct process * list_of_processes; + +extern void * instruction_pointer; + +struct event * wait_for_something(void); +void process_event(struct event * event); +void execute_program(struct process *, char **); +int display_arg(enum tof type, struct process * proc, int arg_num, enum param_type rt); +void enable_all_breakpoints(struct process * proc); +void disable_all_breakpoints(struct process * proc); + +/* Arch-dependent stuff: */ +extern void trace_me(void); +extern void * get_instruction_pointer(int pid); +extern void * get_stack_pointer(int pid); +extern void * get_return_addr(int pid, void * stack_pointer); +extern void insert_breakpoint(int pid, struct breakpoint * sbp); +extern void delete_breakpoint(int pid, struct breakpoint * sbp); +extern int child_p(int sysnum); +extern int syscall_p(pid_t pid, int status); +extern void continue_process(pid_t pid); +extern void continue_after_signal(pid_t pid, int signum); +extern void continue_after_breakpoint(struct process * proc, struct breakpoint * sbp, int delete_it); +extern void continue_enabling_breakpoint(pid_t pid, struct breakpoint * sbp); +extern long gimme_arg(enum tof type, struct process * proc, int arg_num); +extern int umovestr(struct process * proc, void * addr, int len, void * laddr); +#if 0 /* not yet */ +extern int umoven(struct process * proc, void * addr, int len, void * laddr); +#endif + -extern int opt_d; -extern int opt_i; -extern int opt_S; +#endif diff --git a/options.c b/options.c new file mode 100644 index 0000000..f5b8874 --- /dev/null +++ b/options.c @@ -0,0 +1,118 @@ +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <limits.h> + +#include "ltrace.h" +#include "options.h" +#include "defs.h" + +FILE * output; +int opt_a = DEFAULT_ACOLUMN; /* default alignment column for results */ +int opt_d = 0; /* debug */ +int opt_i = 0; /* instruction pointer */ +int opt_s = DEFAULT_STRLEN; /* default maximum # of bytes printed in strings */ +int opt_S = 0; /* display syscalls */ +int opt_L = 1; /* display library calls */ +int opt_f = 0; /* trace child processes as they are created */ + +/* List of pids given to option -p: */ +struct opt_p_t * opt_p = NULL; /* attach to process with a given pid */ + +static void usage(void) +{ + fprintf(stderr, "Usage: ltrace [-dfiLS] [-a column] [-s strlen] [-o filename]\n" + " [-p pid] ... [command [arg ...]]\n\n"); +} + +static char * search_for_command(char * filename) +{ + static char pathname[PATH_MAX]; + char *path; + int m, n; + + if (strchr(filename, '/')) { + return filename; + } + for (path = getenv("PATH"); path && *path; path += m) { + if (strchr(path, ':')) { + n = strchr(path, ':') - path; + m = n + 1; + } else { + m = n = strlen(path); + } + strncpy(pathname, path, n); + if (n && pathname[n - 1] != '/') { + pathname[n++] = '/'; + } + strcpy(pathname + n, filename); + if (!access(pathname, X_OK)) { + return pathname; + } + } + return filename; +} + +char ** process_options(int argc, char **argv) +{ + char *nextchar = NULL; + + output = stderr; + + while(1) { + if (!nextchar || !(*nextchar)) { + if (!argv[1] || argv[1][0] != '-' || !argv[1][1]) { + break; + } + nextchar = &argv[1][1]; + argc--; argv++; + } + switch (*nextchar++) { + case 'a': opt_a = atoi(argv[1]); + argc--; argv++; + break; + case 'd': opt_d++; + break; + case 'o': output = fopen(argv[1], "w"); + if (!output) { + fprintf(stderr, "Can't open %s for output: %s\n", argv[1], strerror(errno)); + exit(1); + } + argc--; argv++; + break; + case 'i': opt_i++; + break; + case 's': opt_s = atoi(argv[1]); + argc--; argv++; + break; + case 'L': opt_L = 0; + break; + case 'S': opt_S = 1; + break; + case 'f': opt_f = 1; + break; + case 'p': + { + struct opt_p_t * tmp = malloc(sizeof(struct opt_p_t)); + if (!tmp) { + perror("malloc"); + exit(1); + } + tmp->pid = atoi(argv[1]); + argc--; argv++; + break; + } + default: fprintf(stderr, "Unknown option '%c'\n", *(nextchar-1)); + usage(); + exit(1); + } + } + + if (argc<2) { + usage(); + exit(1); + } + command = search_for_command(argv[1]); + return &argv[1]; +} diff --git a/options.h b/options.h new file mode 100644 index 0000000..311a13b --- /dev/null +++ b/options.h @@ -0,0 +1,19 @@ +#include <stdio.h> + +extern FILE * output; +extern int opt_a; /* default alignment column for results */ +extern int opt_d; /* debug */ +extern int opt_i; /* instruction pointer */ +extern int opt_s; /* default maximum # of bytes printed in strings */ +extern int opt_L; /* display library calls */ +extern int opt_S; /* display system calls */ +extern int opt_f; /* trace child processes */ + +struct opt_p_t { + pid_t pid; + struct opt_p_t * next; +}; + +extern struct opt_p_t * opt_p; /* attach to process with a given pid */ + +extern char ** process_options(int argc, char **argv); @@ -2,56 +2,165 @@ #include <stdarg.h> #include "ltrace.h" -#include "process.h" +#include "options.h" +#include "output.h" -static int new_line=1; +static pid_t current_pid = 0; +static int current_column = 0; -void send_left(const char * fmt, ...) +static void begin_of_line(enum tof type, struct process * proc) { - va_list args; - - va_start(args, fmt); + current_column = 0; + if (!proc) { + return; + } + if (list_of_processes && list_of_processes->next) { + current_column += fprintf(output, "[pid %d] ", proc->pid); + } if (opt_i) { - fprintf(output, "[%08x] ", instruction_pointer); + if (type==LT_TOF_FUNCTION) { + current_column += fprintf(output, "[%08x] ", + (unsigned)proc->return_addr); + } else { + current_column += fprintf(output, "[%08x] ", + (unsigned)proc->instruction_pointer); + } } - vfprintf(output, fmt, args); - va_end(args); - new_line=0; } -void send_right(const char * fmt, ...) +static struct function * name2func(char * name) { - va_list args; + struct function * tmp; - if (new_line==0) { - va_start(args, fmt); - vfprintf(output, fmt, args); - fprintf(output, "\n"); - va_end(args); + tmp = list_of_functions; + while(tmp) { + if (!strcmp(tmp->name, name)) { + return tmp; + } + tmp = tmp->next; } - new_line=1; + return NULL; } -void send_line(const char * fmt, ...) +void output_line(struct process * proc, char *fmt, ...) { va_list args; - va_start(args, fmt); - if (opt_i) { - fprintf(output, "[%08x] ", instruction_pointer); + if (current_pid) { + fprintf(output, " <unfinished ...>\n"); + } + begin_of_line(LT_TOF_NONE, proc); + + va_start(args, fmt); + vfprintf(output, fmt, args); + fprintf(output, "\n"); + va_end(args); + current_pid=0; + current_column=0; +} + +static void tabto(int col) +{ + if (current_column < col) { + fprintf(output, "%*s", col-current_column, ""); } - vfprintf(output, fmt, args); - fprintf(output, "\n"); - va_end(args); - new_line=1; } -void print_libcall(const char name, int pid, int esp) +void output_left(enum tof type, struct process * proc, char * function_name) { - fprintf(output, "libcall: %s\n", name); + struct function * func; + + if (current_pid) { +#if 1 /* ugly hack :) */ + if (current_pid == proc->pid + && proc->type_being_displayed == LT_TOF_FUNCTION + && proc->type_being_displayed == type) { + tabto(opt_a); + fprintf(output, "= ???\n"); + } else +#endif + fprintf(output, " <unfinished ...>\n"); + current_pid=0; + current_column=0; + } + current_pid=proc->pid; + proc->type_being_displayed = type; + begin_of_line(type, proc); + current_column += fprintf(output, "%s(", function_name); + + func = name2func(function_name); + if (!func) { + int i; + for(i=0; i<4; i++) { + current_column += display_arg(type, proc, i, LT_PT_UNKNOWN); + current_column += fprintf(output, ", "); + } + current_column += display_arg(type, proc, 4, LT_PT_UNKNOWN); + return; + } else { + int i; + for(i=0; i< func->num_params - func->params_right - 1; i++) { + current_column += display_arg(type, proc, i, func->param_types[i]); + current_column += fprintf(output, ", "); + } + if (func->num_params>func->params_right) { + current_column += display_arg(type, proc, i, func->param_types[i]); + if (func->params_right) { + current_column += fprintf(output, ", "); + } + } + if (!func->params_right && func->return_type == LT_PT_VOID) { + current_column += fprintf(output, ") "); + tabto(opt_a); + fprintf(output, "= <void>\n"); + current_pid = 0; + current_column = 0; + } + } } -void print_libret(const char name, int pid, int esp) +void output_right(enum tof type, struct process * proc, char * function_name) { - fprintf(output, "libret: %s\n", name); + struct function * func = name2func(function_name); + + if (func && func->params_right==0 && func->return_type == LT_PT_VOID) { + return; + } + + if (current_pid && current_pid!=proc->pid) { + fprintf(output, " <unfinished ...>\n"); + begin_of_line(type, proc); + current_column += fprintf(output, "<... %s resumed> ", function_name); + } else if (!current_pid) { + begin_of_line(type, proc); + current_column += fprintf(output, "<... %s resumed> ", function_name); + } + + if (!func) { + current_column += fprintf(output, ") "); + tabto(opt_a); + fprintf(output, "= "); + display_arg(type, proc, -1, LT_PT_UNKNOWN); + fprintf(output, "\n"); + } else { + int i; + for(i=func->num_params-func->params_right; i<func->num_params-1; i++) { + current_column += display_arg(type, proc, i, func->param_types[i]); + current_column += fprintf(output, ", "); + } + if (func->params_right) { + current_column += display_arg(type, proc, i, func->param_types[i]); + } + current_column += fprintf(output, ") "); + tabto(opt_a); + fprintf(output, "= "); + if (func->return_type == LT_PT_VOID) { + fprintf(output, "<void>"); + } else { + display_arg(type, proc, -1, func->return_type); + } + fprintf(output, "\n"); + } + current_pid=0; + current_column=0; } @@ -1,3 +1,9 @@ -void send_left(const char * fmt, ...); -void send_right(const char * fmt, ...); -void send_line(const char * fmt, ...); +#include <sys/types.h> + +#include "ltrace.h" + +void output_line(struct process * proc, char *fmt, ...); + +void output_left(enum tof type, struct process * proc, char * function_name); +void output_right(enum tof type, struct process * proc, char * function_name); + diff --git a/process.c b/process.c deleted file mode 100644 index 2852310..0000000 --- a/process.c +++ /dev/null @@ -1,216 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> -#include <signal.h> -#include <string.h> -#include <sys/types.h> -#include <sys/resource.h> -#include <sys/wait.h> - -#include <asm/unistd.h> - -#include "i386.h" -#include "ltrace.h" -#include "process.h" -#include "symbols.h" -#include "functions.h" -#include "syscall.h" -#include "signal.h" -#include "output.h" - -struct process * list_of_processes = NULL; - -unsigned int instruction_pointer; - -static void detach_process(int pid); -static struct process * pid2proc(int pid); -static void process_child(struct process * current_process); - -int execute_process(const char * file, char * const argv[]) -{ - struct process * tmp; - int pid = fork(); - - if (pid<0) { - perror("fork"); - exit(1); - } else if (!pid) { - trace_me(); - execvp(file, argv); - fprintf(stderr, "Can't execute \"%s\": %s\n", argv[1], sys_errlist[errno]); - exit(1); - } - - tmp = (struct process *)malloc(sizeof(struct process)); - tmp->pid = pid; - tmp->breakpoints_enabled = 0; - proc_arch_init(&tmp->proc_arch); - tmp->within_function = 0; - tmp->next = list_of_processes; - list_of_processes = tmp; - - return pid; -} - -static void detach_process(int pid) -{ - struct process *tmp, *tmp2; - - if (list_of_processes && (pid == list_of_processes->pid)) { - tmp2 = list_of_processes->next; - free(list_of_processes); - list_of_processes = tmp2; - } else { - tmp = list_of_processes; - while(tmp && tmp->next) { - if (pid == tmp->next->pid) { - tmp2 = tmp->next->next; - free(tmp->next); - tmp->next = tmp2; - } - tmp = tmp->next; - } - } - if (!kill(pid,0)) { - disable_all_breakpoints(pid); - untrace_pid(pid); - } -} - -static struct process * pid2proc(int pid) -{ - struct process * tmp; - - tmp = list_of_processes; - while(tmp) { - if (pid == tmp->pid) { - return tmp; - } - tmp = tmp->next; - } - return NULL; -} - -void wait_for_child(void) -{ - int pid; - int status; - struct process * current_process; - - pid = wait4(-1, &status, 0, NULL); - if (pid==-1) { - if (errno == ECHILD) { - if (opt_d>0) { - send_line("No more children"); - } - exit(0); - } - perror("wait4"); - exit(1); - } - current_process = pid2proc(pid); - if (!current_process) { - fprintf(stderr, "wrong pid %d ???\n", pid); - exit(1); - } - if (!current_process->breakpoints_enabled) { - if (opt_d>0) { - send_line("Enabling breakpoints for pid %d...", pid); - } - enable_all_breakpoints(pid); - current_process->breakpoints_enabled=1; - } - if (WIFEXITED(status)) { - send_line("pid %u exited", pid); - detach_process(pid); - return; - } - if (WIFSIGNALED(status)) { - send_line("--- %s (%s) ---", signal_name[WSTOPSIG(status)], strsignal(WSTOPSIG(status))); - send_line("+++ killed by %s +++", signal_name[WSTOPSIG(status)]); - detach_process(pid); - return; - } - if (!WIFSTOPPED(status)) { - send_line("pid %u ???", pid); - exit(1); - } - if (WSTOPSIG(status) != SIGTRAP) { - send_line("--- %s (%s) ---", signal_name[WSTOPSIG(status)], strsignal(WSTOPSIG(status))); - continue_process(pid, WSTOPSIG(status)); - return; - } - process_child(current_process); -} - -static void process_child(struct process * current_process) -{ - int pid; - unsigned long eip; - int esp; - int function_seen; - int status; - struct library_symbol * tmp = NULL; - - pid = current_process->pid; - eip = get_eip(pid); - instruction_pointer = eip; - - switch (type_of_stop(current_process->pid, ¤t_process->proc_arch, &status)) { - case PROC_SYSCALL: - if (status==__NR_fork) { - disable_all_breakpoints(pid); - } - if (opt_S) { - send_line("SYSCALL: %s()", syscall_list[status]); - } - continue_process(pid, 0); - return; - case PROC_SYSRET: - if (status==__NR_fork) { - enable_all_breakpoints(pid); - } - if (opt_S && (opt_d>0)) { - send_line("SYSRET: %u", status); - } - continue_process(pid, 0); - return; - case PROC_BREAKPOINT: - default: - } - /* pid is breakpointed... */ - /* TODO: I could be here after a PTRACE_SINGLESTEP ... */ - esp = get_esp(pid); - instruction_pointer = get_return(pid, esp); - tmp = library_symbols; - function_seen = 0; - if (eip == current_process->return_value.addr) { - function_seen = 1; -#if 0 - send_line("return"); - print_libret(tmp->name, pid, esp); -#endif - continue_after_breakpoint(pid, ¤t_process->return_value, 1); - } else while(tmp) { - if (eip == tmp->sbp.addr) { - function_seen = 1; - if (current_process->within_function) { - delete_breakpoint(pid, ¤t_process->return_value); - } - current_process->return_value.addr = instruction_pointer; - insert_breakpoint(pid, ¤t_process->return_value); - current_process->within_function=1; -#if 0 - print_libcall(tmp->name, pid, esp); -#endif - continue_after_breakpoint(pid, &tmp->sbp, 0); - break; - } - tmp = tmp->next; - } - if (!function_seen) { - send_line("pid %u stopped; continuing it...", pid); - continue_process(pid, 0); - } -} diff --git a/process.h b/process.h deleted file mode 100644 index 568f61d..0000000 --- a/process.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef _LTRACE_PROCESS_H -#define _LTRACE_PROCESS_H - -#include "i386.h" - -/* not ready yet */ -#if 0 -struct symbols_from_filename { - char * filename; - struct library_symbol * list_of_symbols; -} - -struct library_symbol { - char * name; - unsigned long addr; - unsigned long return_addr; - unsigned char old_value[BREAKPOINT_LENGTH]; - struct library_symbol * next; -}; -#endif - -struct process { - char * filename; /* from execve() (TODO) */ - int pid; - int breakpoints_enabled; - int within_function; - struct breakpoint return_value; /* if within a function */ - struct proc_arch proc_arch; - struct process * next; -}; - -extern struct process * list_of_processes; - -extern unsigned int instruction_pointer; - -int execute_process(const char * file, char * const argv[]); -void wait_for_child(void); - -#endif diff --git a/process_event.c b/process_event.c new file mode 100644 index 0000000..481f1bc --- /dev/null +++ b/process_event.c @@ -0,0 +1,165 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> + +#include "ltrace.h" +#include "output.h" +#include "options.h" + +static void process_signal(struct event * event); +static void process_exit(struct event * event); +static void process_exit_signal(struct event * event); +static void process_syscall(struct event * event); +static void process_sysret(struct event * event); +static void process_breakpoint(struct event * event); + +void process_event(struct event * event) +{ + switch (event->thing) { + case LT_EV_NONE: + return; + case LT_EV_SIGNAL: + process_signal(event); + return; + case LT_EV_EXIT: + process_exit(event); + return; + case LT_EV_EXIT_SIGNAL: + process_exit_signal(event); + return; + case LT_EV_SYSCALL: + process_syscall(event); + return; + case LT_EV_SYSRET: + process_sysret(event); + return; + case LT_EV_BREAKPOINT: + process_breakpoint(event); + return; + default: + fprintf(stderr, "Error! unknown event?\n"); + exit(1); + } +} + +static char * shortsignal(int signum) +{ + static char * signalent0[] = { + #include "signalent.h" + }; + int nsignals0 = sizeof signalent0 / sizeof signalent0[0]; + + if (signum<0 || signum>nsignals0) { + return "UNKNOWN_SIGNAL"; + } else { + return signalent0[signum]; + } +} + +static char * sysname(int sysnum) +{ + static char result[128]; + static char * syscalent0[] = { + #include "syscallent.h" + }; + int nsyscals0 = sizeof syscalent0 / sizeof syscalent0[0]; + + if (sysnum<0 || sysnum>nsyscals0) { + sprintf(result, "SYS_%d", sysnum); + return result; + } else { + sprintf(result, "SYS_%s", syscalent0[sysnum]); + return result; + } +} + +static void process_signal(struct event * event) +{ + output_line(event->proc, "--- %s (%s) ---", + shortsignal(event->e_un.signum), strsignal(event->e_un.signum)); + continue_after_signal(event->proc->pid, event->e_un.signum); +} + +static void process_exit(struct event * event) +{ + output_line(event->proc, "+++ exited (status %d) +++", + event->e_un.ret_val); +} + +static void process_exit_signal(struct event * event) +{ + output_line(event->proc, "+++ killed by %s +++", + shortsignal(event->e_un.signum)); +} + +static void process_syscall(struct event * event) +{ + event->proc->current_syscall = event->e_un.sysnum; + if (opt_S) { + output_left(LT_TOF_SYSCALL, event->proc, sysname(event->e_un.sysnum)); + } + if (child_p(event->e_un.sysnum)) { + disable_all_breakpoints(event->proc); + if (event->proc->current_symbol) { + delete_breakpoint(event->proc->pid, &event->proc->return_value); + } + } + continue_process(event->proc->pid); +} + +static void process_sysret(struct event * event) +{ + if (opt_S) { + output_right(LT_TOF_SYSCALL, event->proc, sysname(event->e_un.sysnum)); + } + if (child_p(event->e_un.sysnum)) { + enable_all_breakpoints(event->proc); + if (event->proc->current_symbol) { + insert_breakpoint(event->proc->pid, &event->proc->return_value); + } + if (opt_f) { + fprintf(stderr, "ERROR: Option `-f' doesn't work yet\n"); + exit(1); + } + } + event->proc->current_syscall = -1; + continue_process(event->proc->pid); +} + +static void process_breakpoint(struct event * event) +{ + struct library_symbol * tmp; + + if (event->proc->breakpoint_being_enabled) { + continue_enabling_breakpoint(event->proc->pid, event->proc->breakpoint_being_enabled); + event->proc->breakpoint_being_enabled = NULL; + return; + } + if (event->proc->current_symbol && event->e_un.brk_addr == event->proc->return_value.addr) { + output_right(LT_TOF_FUNCTION, event->proc, event->proc->current_symbol->name); + continue_after_breakpoint(event->proc, &event->proc->return_value, 1); + event->proc->current_symbol = NULL; + return; + } + + tmp = event->proc->list_of_symbols; + while(tmp) { + if (event->e_un.brk_addr == tmp->brk.addr) { + if (event->proc->current_symbol) { + delete_breakpoint(event->proc->pid, &event->proc->return_value); + } + event->proc->current_symbol = tmp; + event->proc->stack_pointer = get_stack_pointer(event->proc->pid); + event->proc->return_addr = get_return_addr(event->proc->pid, event->proc->stack_pointer); + output_left(LT_TOF_FUNCTION, event->proc, tmp->name); + event->proc->return_value.addr = event->proc->return_addr; + insert_breakpoint(event->proc->pid, &event->proc->return_value); + continue_after_breakpoint(event->proc, &tmp->brk, 0); + return; + } + tmp = tmp->next; + } + output_line(event->proc, "breakpointed at 0x%08x (?)", + (unsigned)event->e_un.brk_addr); + continue_process(event->proc->pid); +} diff --git a/signal.c b/signal.c deleted file mode 100644 index 24b7c8b..0000000 --- a/signal.c +++ /dev/null @@ -1,35 +0,0 @@ -char * signal_name[] = { "SIG_0", - "SIGHUP", - "SIGINT", - "SIGQUIT", - "SIGKILL", - "SIGTRAP", - "SIGABRT", - "SIGBUS", - "SIGFPE", - "SIGKILL", - - "SIGUSR1", - "SIGSEGV", - "SIGUSR2", - "SIGPIPE", - "SIGALRM", - "SIGTERM", - "SIGSTKFLT", - "SIGCHLD", - "SIGCONT", - "SIGSTOP", - "SIGTSTP", - "SIGTTIN", - "SIGTTOU", - "SIGURG", - "SIGXCPU", - "SIGXFSZ", - "SIGVTALRM", - "SIGPROF", - "SIGWINCH", - "SIGIO", - "SIGPWR", - "SIGUNUSED" -}; - diff --git a/signal.h b/signal.h deleted file mode 100644 index 0e4a269..0000000 --- a/signal.h +++ /dev/null @@ -1 +0,0 @@ -extern char * signal_name[]; diff --git a/symbols.c b/symbols.c deleted file mode 100644 index be109cb..0000000 --- a/symbols.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file contains functions related to the library_symbol struct - */ - -#include <stdio.h> - -#include "symbols.h" -#include "i386.h" - -struct library_symbol * library_symbols = NULL; - -void enable_all_breakpoints(int pid) -{ - struct library_symbol * tmp = NULL; - - tmp = library_symbols; - while(tmp) { - insert_breakpoint(pid, &tmp->sbp); - tmp = tmp->next; - } -} - -void disable_all_breakpoints(int pid) -{ - struct library_symbol * tmp = NULL; - - tmp = library_symbols; - while(tmp) { - delete_breakpoint(pid, &tmp->sbp); - tmp = tmp->next; - } -} - diff --git a/symbols.h b/symbols.h deleted file mode 100644 index 55747fa..0000000 --- a/symbols.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _LTRACE_SYMBOLS_H -#define _LTRACE_SYMBOLS_H - -#include "i386.h" - -struct library_symbol { - char * name; - struct breakpoint sbp; - unsigned long return_addr; - struct library_symbol * next; -}; - -extern struct library_symbol * library_symbols; - -void enable_all_breakpoints(int pid); -void disable_all_breakpoints(int pid); - -#endif diff --git a/syscall.c b/syscall.c deleted file mode 100644 index aca9623..0000000 --- a/syscall.c +++ /dev/null @@ -1,165 +0,0 @@ -char * syscall_list[] = { "setup", /* 0 */ - "exit", - "fork", - "read", - "write", - "open", - "close", - "waitpid", - "creat", - "link", - "unlink", /* 10 */ - "execve", - "chdir", - "time", - "mknod", - "chmod", - "chown", - "break", - "oldstat", - "lseek", - "getpid", /* 20 */ - "mount", - "umount", - "setuid", - "getuid", - "stime", - "ptrace", - "alarm", - "oldfstat", - "pause", - "utime", /* 30 */ - "stty", - "gtty", - "access", - "nice", - "ftime", - "sync", - "kill", - "rename", - "mkdir", - "rmdir", /* 40 */ - "dup", - "pipe", - "times", - "prof", - "brk", - "setgid", - "getgid", - "signal", - "geteuid", - "getegid", /* 50 */ - "acct", - "phys", - "lock", - "ioctl", - "fcntl", - "mpx", - "setpgid", - "ulimit", - "oldolduname", - "umask", /* 60 */ - "chroot", - "ustat", - "dup2", - "getppid", - "getpgrp", - "setsid", - "sigaction", - "sgetmask", - "ssetmask", - "setreuid", /* 70 */ - "setregid", - "sigsuspend", - "sigpending", - "sethostname", - "setrlimit", - "getrlimit", - "getrusage", - "gettimeofday", - "settimeofday", - "getgroups", /* 80 */ - "setgroups", - "select", - "symlink", - "oldlstat", - "readlink", - "uselib", - "swapon", - "reboot", - "readdir", - "mmap", /* 90 */ - "munmap", - "truncate", - "ftruncate", - "fchmod", - "fchown", - "getpriority", - "setpriority", - "profil", - "statfs", - "fstatfs", /* 100 */ - "ioperm", - "socketcall", - "syslog", - "setitimer", - "getitimer", - "stat", - "lstat", - "fstat", - "olduname", - "iopl", /* 110 */ - "vhangup", - "idle", - "vm86", - "wait4", - "swapoff", - "sysinfo", - "ipc", - "fsync", - "sigreturn", - "clone", /* 120 */ - "setdomainname", - "uname", - "modify_ldt", - "adjtimex", - "mprotect", - "sigprocmask", - "create_module", - "init_module", - "delete_module", - "get_kernel_syms", /* 130 */ - "quotactl", - "getpgid", - "fchdir", - "bdflush", - "sysfs", - "personality", - "afs_syscall", - "setfsuid", - "setfsgid", - "_llseek", /* 140 */ - "getdents", - "_newselect", - "flock", - "msync", - "readv", - "writev", - "getsid", - "fdatasync", - "_sysctl", - "mlock", /* 150 */ - "munlock", - "mlockall", - "munlockall", - "sched_setparam", - "sched_getparam", - "sched_setscheduler", - "sched_getscheduler", - "sched_yield", - "sched_get_priority_max", - "sched_get_priority_min", /* 160 */ - "sched_rr_get_interval", - "nanosleep", - "mremap", -}; diff --git a/syscall.h b/syscall.h deleted file mode 100644 index 3ca9072..0000000 --- a/syscall.h +++ /dev/null @@ -1,2 +0,0 @@ - -extern char * syscall_list[]; diff --git a/sysdeps/Linux/Makefile b/sysdeps/Linux/Makefile new file mode 100644 index 0000000..2cfb878 --- /dev/null +++ b/sysdeps/Linux/Makefile @@ -0,0 +1,31 @@ +ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) + +CFLAGS += -I$(TOPDIR)/sysdeps/Linux/$(ARCH) + +OBJ = trace.o + +all: sysdep.h signalent.h syscallent.h ../sysdep.o + +sysdep.h: $(ARCH)/arch.h + cat $(ARCH)/arch.h > sysdep.h + +signalent.h: + cp $(ARCH)/signalent.h signalent.h + +syscallent.h: + cp $(ARCH)/syscallent.h syscallent.h + +../sysdep.o: os.o $(ARCH)/arch.o + $(LD) -r -o ../sysdep.o os.o $(ARCH)/arch.o + +os.o: $(OBJ) + $(LD) -r -o os.o $(OBJ) + +$(ARCH)/arch.o: dummy + $(MAKE) -C $(ARCH) + +clean: + $(MAKE) -C $(ARCH) clean + rm -f $(OBJ) sysdep.h signalent.h syscallent.h os.o sysdep.o ../sysdep.o + +dummy: diff --git a/sysdeps/Linux/i386/Makefile b/sysdeps/Linux/i386/Makefile new file mode 100644 index 0000000..2af3c25 --- /dev/null +++ b/sysdeps/Linux/i386/Makefile @@ -0,0 +1,10 @@ +OBJ = breakpoint.o trace.o regs.o + +all: arch.o + +arch.o: $(OBJ) + $(LD) -r -o arch.o $(OBJ) + +clean: + $(RM) $(OBJ) arch.o + diff --git a/sysdeps/Linux/i386/arch.h b/sysdeps/Linux/i386/arch.h new file mode 100644 index 0000000..adfee31 --- /dev/null +++ b/sysdeps/Linux/i386/arch.h @@ -0,0 +1,5 @@ +#include <sys/types.h> + +#define BREAKPOINT_VALUE {0xcc} +#define BREAKPOINT_LENGTH 1 +#define DECR_PC_AFTER_BREAK 1 diff --git a/sysdeps/Linux/i386/breakpoint.c b/sysdeps/Linux/i386/breakpoint.c new file mode 100644 index 0000000..dfde2ad --- /dev/null +++ b/sysdeps/Linux/i386/breakpoint.c @@ -0,0 +1,23 @@ +#include <sys/ptrace.h> +#include "ltrace.h" + +void insert_breakpoint(int pid, struct breakpoint * sbp) +{ + int a; + + a = ptrace(PTRACE_PEEKTEXT, pid, sbp->addr, 0); + sbp->orig_value[0] = a & 0xFF; + a &= 0xFFFFFF00; + a |= 0xCC; + ptrace(PTRACE_POKETEXT, pid, sbp->addr, a); +} + +void delete_breakpoint(int pid, struct breakpoint * sbp) +{ + int a; + + a = ptrace(PTRACE_PEEKTEXT, pid, sbp->addr, 0); + a &= 0xFFFFFF00; + a |= sbp->orig_value[0]; + ptrace(PTRACE_POKETEXT, pid, sbp->addr, a); +} diff --git a/sysdeps/Linux/i386/regs.c b/sysdeps/Linux/i386/regs.c new file mode 100644 index 0000000..fb737d1 --- /dev/null +++ b/sysdeps/Linux/i386/regs.c @@ -0,0 +1,18 @@ +#include <sys/types.h> +#include <sys/ptrace.h> + +int get_instruction_pointer(pid_t pid) +{ + return ptrace(PTRACE_PEEKUSER, pid, 4*EIP, 0); +} + +int get_stack_pointer(pid_t pid) +{ + return ptrace(PTRACE_PEEKUSER, pid, 4*UESP, 0); +} + +int get_return_addr(pid_t pid, void * stack_pointer) +{ + return ptrace(PTRACE_PEEKTEXT, pid, stack_pointer, 0); +} + diff --git a/sysdeps/Linux/i386/signalent.h b/sysdeps/Linux/i386/signalent.h new file mode 100644 index 0000000..e2c1337 --- /dev/null +++ b/sysdeps/Linux/i386/signalent.h @@ -0,0 +1,32 @@ + "SIG_0", /* 0 */ + "SIGHUP", /* 1 */ + "SIGINT", /* 2 */ + "SIGQUIT", /* 3 */ + "SIGILL", /* 4 */ + "SIGTRAP", /* 5 */ + "SIGABRT", /* 6 */ + "SIGBUS", /* 7 */ + "SIGFPE", /* 8 */ + "SIGKILL", /* 9 */ + "SIGUSR1", /* 10 */ + "SIGSEGV", /* 11 */ + "SIGUSR2", /* 12 */ + "SIGPIPE", /* 13 */ + "SIGALRM", /* 14 */ + "SIGTERM", /* 15 */ + "SIGSTKFLT", /* 16 */ + "SIGCHLD", /* 17 */ + "SIGCONT", /* 18 */ + "SIGSTOP", /* 19 */ + "SIGTSTP", /* 20 */ + "SIGTTIN", /* 21 */ + "SIGTTOU", /* 22 */ + "SIGURG", /* 23 */ + "SIGXCPU", /* 24 */ + "SIGXFSZ", /* 25 */ + "SIGVTALRM", /* 26 */ + "SIGPROF", /* 27 */ + "SIGWINCH", /* 28 */ + "SIGIO", /* 29 */ + "SIGPWR", /* 30 */ + "SIGUNUSED", /* 31 */ diff --git a/sysdeps/Linux/i386/syscallent.h b/sysdeps/Linux/i386/syscallent.h new file mode 100644 index 0000000..36b9021 --- /dev/null +++ b/sysdeps/Linux/i386/syscallent.h @@ -0,0 +1,164 @@ + "setup", /* 0 */ + "exit", /* 1 */ + "fork", /* 2 */ + "read", /* 3 */ + "write", /* 4 */ + "open", /* 5 */ + "close", /* 6 */ + "waitpid", /* 7 */ + "creat", /* 8 */ + "link", /* 9 */ + "unlink", /* 10 */ + "execve", /* 11 */ + "chdir", /* 12 */ + "time", /* 13 */ + "mknod", /* 14 */ + "chmod", /* 15 */ + "chown", /* 16 */ + "break", /* 17 */ + "oldstat", /* 18 */ + "lseek", /* 19 */ + "getpid", /* 20 */ + "mount", /* 21 */ + "umount", /* 22 */ + "setuid", /* 23 */ + "getuid", /* 24 */ + "stime", /* 25 */ + "ptrace", /* 26 */ + "alarm", /* 27 */ + "oldfstat", /* 28 */ + "pause", /* 29 */ + "utime", /* 30 */ + "stty", /* 31 */ + "gtty", /* 32 */ + "access", /* 33 */ + "nice", /* 34 */ + "ftime", /* 35 */ + "sync", /* 36 */ + "kill", /* 37 */ + "rename", /* 38 */ + "mkdir", /* 39 */ + "rmdir", /* 40 */ + "dup", /* 41 */ + "pipe", /* 42 */ + "times", /* 43 */ + "prof", /* 44 */ + "brk", /* 45 */ + "setgid", /* 46 */ + "getgid", /* 47 */ + "signal", /* 48 */ + "geteuid", /* 49 */ + "getegid", /* 50 */ + "acct", /* 51 */ + "phys", /* 52 */ + "lock", /* 53 */ + "ioctl", /* 54 */ + "fcntl", /* 55 */ + "mpx", /* 56 */ + "setpgid", /* 57 */ + "ulimit", /* 58 */ + "oldolduname", /* 59 */ + "umask", /* 60 */ + "chroot", /* 61 */ + "ustat", /* 62 */ + "dup2", /* 63 */ + "getppid", /* 64 */ + "getpgrp", /* 65 */ + "setsid", /* 66 */ + "sigaction", /* 67 */ + "sgetmask", /* 68 */ + "ssetmask", /* 69 */ + "setreuid", /* 70 */ + "setregid", /* 71 */ + "sigsuspend", /* 72 */ + "sigpending", /* 73 */ + "sethostname", /* 74 */ + "setrlimit", /* 75 */ + "getrlimit", /* 76 */ + "getrusage", /* 77 */ + "gettimeofday", /* 78 */ + "settimeofday", /* 79 */ + "getgroups", /* 80 */ + "setgroups", /* 81 */ + "select", /* 82 */ + "symlink", /* 83 */ + "oldlstat", /* 84 */ + "readlink", /* 85 */ + "uselib", /* 86 */ + "swapon", /* 87 */ + "reboot", /* 88 */ + "readdir", /* 89 */ + "mmap", /* 90 */ + "munmap", /* 91 */ + "truncate", /* 92 */ + "ftruncate", /* 93 */ + "fchmod", /* 94 */ + "fchown", /* 95 */ + "getpriority", /* 96 */ + "setpriority", /* 97 */ + "profil", /* 98 */ + "statfs", /* 99 */ + "fstatfs", /* 100 */ + "ioperm", /* 101 */ + "socketcall", /* 102 */ + "syslog", /* 103 */ + "setitimer", /* 104 */ + "getitimer", /* 105 */ + "stat", /* 106 */ + "lstat", /* 107 */ + "fstat", /* 108 */ + "olduname", /* 109 */ + "iopl", /* 110 */ + "vhangup", /* 111 */ + "idle", /* 112 */ + "vm86", /* 113 */ + "wait4", /* 114 */ + "swapoff", /* 115 */ + "sysinfo", /* 116 */ + "ipc", /* 117 */ + "fsync", /* 118 */ + "sigreturn", /* 119 */ + "clone", /* 120 */ + "setdomainname", /* 121 */ + "uname", /* 122 */ + "modify_ldt", /* 123 */ + "adjtimex", /* 124 */ + "mprotect", /* 125 */ + "sigprocmask", /* 126 */ + "create_module", /* 127 */ + "init_module", /* 128 */ + "delete_module", /* 129 */ + "get_kernel_syms", /* 130 */ + "quotactl", /* 131 */ + "getpgid", /* 132 */ + "fchdir", /* 133 */ + "bdflush", /* 134 */ + "sysfs", /* 135 */ + "personality", /* 136 */ + "afs_syscall", /* 137 */ + "setfsuid", /* 138 */ + "setfsgid", /* 139 */ + "_llseek", /* 140 */ + "getdents", /* 141 */ + "_newselect", /* 142 */ + "flock", /* 143 */ + "msync", /* 144 */ + "readv", /* 145 */ + "writev", /* 146 */ + "getsid", /* 147 */ + "fdatasync", /* 148 */ + "_sysctl", /* 149 */ + "mlock", /* 150 */ + "munlock", /* 151 */ + "mlockall", /* 152 */ + "munlockall", /* 153 */ + "sched_setparam", /* 154 */ + "sched_getparam", /* 155 */ + "sched_setscheduler", /* 156 */ + "sched_getscheduler", /* 157 */ + "sched_yield", /* 158 */ + "sched_get_priority_max",/* 159 */ + "sched_get_priority_min",/* 160 */ + "sched_rr_get_interval",/* 161 */ + "nanosleep", /* 162 */ + "mremap", /* 163 */ diff --git a/sysdeps/Linux/i386/trace.c b/sysdeps/Linux/i386/trace.c new file mode 100644 index 0000000..b08ae44 --- /dev/null +++ b/sysdeps/Linux/i386/trace.c @@ -0,0 +1,85 @@ +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <sys/ptrace.h> + +#include "ltrace.h" + +/* Returns syscall number if `pid' stopped because of a syscall. + * Returns -1 otherwise + */ +int syscall_p(pid_t pid, int status) +{ + if (WIFSTOPPED(status) && WSTOPSIG(status)==SIGTRAP) { + int tmp = ptrace(PTRACE_PEEKUSER, pid, 4*ORIG_EAX); + if (tmp>=0) { + return tmp; + } + } + return -1; +} + +void continue_after_breakpoint(struct process *proc, struct breakpoint * sbp, int delete_it) +{ + delete_breakpoint(proc->pid, sbp); + ptrace(PTRACE_POKEUSER, proc->pid, 4*EIP, sbp->addr); + if (delete_it) { + continue_process(proc->pid); + } else { + proc->breakpoint_being_enabled = sbp; + ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0); + } +} + +long gimme_arg(enum tof type, struct process * proc, int arg_num) +{ + if (arg_num==-1) { /* return value */ + return ptrace(PTRACE_PEEKUSER, proc->pid, 4*EAX); + } + + if (type==LT_TOF_FUNCTION) { + return ptrace(PTRACE_PEEKTEXT, proc->pid, proc->stack_pointer+4*(arg_num+1)); + } else if (type==LT_TOF_SYSCALL) { +#if 0 + switch(arg_num) { + case 0: return ptrace(PTRACE_PEEKUSER, proc->pid, 4*EBX); + case 1: return ptrace(PTRACE_PEEKUSER, proc->pid, 4*ECX); + case 2: return ptrace(PTRACE_PEEKUSER, proc->pid, 4*EDX); + case 3: return ptrace(PTRACE_PEEKUSER, proc->pid, 4*ESI); + case 4: return ptrace(PTRACE_PEEKUSER, proc->pid, 4*EDI); + default: + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(2); + } +#else + return ptrace(PTRACE_PEEKUSER, proc->pid, 4*arg_num); +#endif + } else { + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(1); + } + + return 0; +} + +int umovestr(struct process * proc, void * addr, int len, void * laddr) +{ + long a; + int i; + int offset=0; + + while(offset<len) { + a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr+offset, 0); + for(i=0; i<sizeof(long); i++) { + if (((char*)&a)[i] && offset+i < len) { + *(char *)(laddr+offset+i) = ((char*)&a)[i]; + } else { + *(char *)(laddr+offset+i) = '\0'; + return 0; + } + } + offset += sizeof(long); + } + *(char *)(laddr+offset) = '\0'; + return 0; +} diff --git a/sysdeps/Linux/sparc/Makefile b/sysdeps/Linux/sparc/Makefile new file mode 100644 index 0000000..b71ce1f --- /dev/null +++ b/sysdeps/Linux/sparc/Makefile @@ -0,0 +1,9 @@ +OBJ = breakpoint.o regs.o trace.o + +all: arch.o + +arch.o: $(OBJ) + $(LD) -r -o arch.o $(OBJ) + +clean: + $(RM) $(OBJ) arch.o diff --git a/sysdeps/Linux/sparc/arch.h b/sysdeps/Linux/sparc/arch.h new file mode 100644 index 0000000..83b437d --- /dev/null +++ b/sysdeps/Linux/sparc/arch.h @@ -0,0 +1,6 @@ +#include <sys/types.h> + +#define BREAKPOINT_VALUE {0x91, 0xd0, 0x20, 0x01} +#define BREAKPOINT_LENGTH 4 +#define DECR_PC_AFTER_BREAK 0 + diff --git a/sysdeps/Linux/sparc/breakpoint.c b/sysdeps/Linux/sparc/breakpoint.c new file mode 100644 index 0000000..c0a700b --- /dev/null +++ b/sysdeps/Linux/sparc/breakpoint.c @@ -0,0 +1,17 @@ +#include <sys/ptrace.h> +#include "ltrace.h" + +void insert_breakpoint(int pid, struct breakpoint * sbp) +{ + unsigned long a; + + a = ptrace(PTRACE_PEEKTEXT, pid, sbp->addr, 0); + *(unsigned long *)sbp->orig_value = a; + a = ((0x91 * 256 + 0xd0) * 256 + 0x20) * 256 + 0x01; + ptrace(PTRACE_POKETEXT, pid, sbp->addr, a); +} + +void delete_breakpoint(int pid, struct breakpoint * sbp) +{ + ptrace(PTRACE_POKETEXT, pid, sbp->addr, *(long *)sbp->orig_value); +} diff --git a/sysdeps/Linux/sparc/regs.c b/sysdeps/Linux/sparc/regs.c new file mode 100644 index 0000000..73e9e42 --- /dev/null +++ b/sysdeps/Linux/sparc/regs.c @@ -0,0 +1,18 @@ +#include <sys/types.h> +#include <sys/ptrace.h> + +int get_instruction_pointer(pid_t pid) +{ + return ptrace(PTRACE_PEEKUSER, pid, PT_PC, 0); +} + +int get_stack_pointer(pid_t pid) +{ + return -1; +} + +int get_return_addr(pid_t pid, void * stack_pointer) +{ + return -1; +} + diff --git a/sysdeps/Linux/sparc/signalent.h b/sysdeps/Linux/sparc/signalent.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/sysdeps/Linux/sparc/signalent.h diff --git a/sysdeps/Linux/sparc/syscallent.h b/sysdeps/Linux/sparc/syscallent.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/sysdeps/Linux/sparc/syscallent.h diff --git a/sysdeps/Linux/sparc/trace.c b/sysdeps/Linux/sparc/trace.c new file mode 100644 index 0000000..4671877 --- /dev/null +++ b/sysdeps/Linux/sparc/trace.c @@ -0,0 +1,66 @@ +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <sys/ptrace.h> + +#include "ltrace.h" + +/* Returns syscall number if `pid' stopped because of a syscall. + * Returns -1 otherwise + */ +int syscall_p(pid_t pid, int status) +{ +#if 0 + if (WIFSTOPPED(status) && WSTOPSIG(status)==SIGTRAP) { + int tmp = ptrace(PTRACE_PEEKUSER, pid, 4*ORIG_EAX); + if (tmp>=0) { + return tmp; + } + } +#endif + return -1; +} + +void continue_after_breakpoint(struct process *proc, struct breakpoint * sbp, int delete_it) +{ + delete_breakpoint(proc->pid, sbp); + ptrace(PTRACE_POKEUSER, proc->pid, PT_PC, sbp->addr); + if (delete_it) { + continue_process(proc->pid); + } else { + proc->breakpoint_being_enabled = sbp; + ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0); + } +} + +long gimme_arg(enum tof type, struct process * proc, int arg_num) +{ +#if 0 + if (arg_num==-1) { /* return value */ + return ptrace(PTRACE_PEEKUSER, proc->pid, 4*EAX); + } + + if (type==LT_TOF_FUNCTION) { + return ptrace(PTRACE_PEEKTEXT, proc->pid, proc->stack_pointer+4*(arg_num+1)); + } else if (type==LT_TOF_SYSCALL) { +#if 0 + switch(arg_num) { + case 0: return ptrace(PTRACE_PEEKUSER, proc->pid, 4*EBX); + case 1: return ptrace(PTRACE_PEEKUSER, proc->pid, 4*ECX); + case 2: return ptrace(PTRACE_PEEKUSER, proc->pid, 4*EDX); + case 3: return ptrace(PTRACE_PEEKUSER, proc->pid, 4*ESI); + case 4: return ptrace(PTRACE_PEEKUSER, proc->pid, 4*EDI); + default: + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(2); + } +#else + return ptrace(PTRACE_PEEKUSER, proc->pid, 4*arg_num); +#endif + } else { + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(1); + } +#endif + return 0; +} diff --git a/sysdeps/Linux/trace.c b/sysdeps/Linux/trace.c new file mode 100644 index 0000000..33bf244 --- /dev/null +++ b/sysdeps/Linux/trace.c @@ -0,0 +1,48 @@ +#include <stdio.h> +#include <sys/types.h> +#include <sys/ptrace.h> +#include <asm/unistd.h> + +#include "ltrace.h" +#include "options.h" + +/* Returns 1 if a new child is about to be created + (ie, with fork() or clone()) + Returns 0 otherwise. */ +int child_p(int sysnum) +{ + return (sysnum == __NR_fork || sysnum == __NR_clone); +} + +void trace_me(void) +{ + if (ptrace(PTRACE_TRACEME, 0, 1, 0)<0) { + perror("PTRACE_TRACEME"); + exit(1); + } +} + +void continue_after_signal(pid_t pid, int signum) +{ + /* We should always trace syscalls to be able to control fork(), clone(), execve()... */ +#if 0 + if (opt_S) { + ptrace(PTRACE_SYSCALL, pid, 1, signum); + } else { + ptrace(PTRACE_CONT, pid, 1, signum); + } +#else + ptrace(PTRACE_SYSCALL, pid, 1, signum); +#endif +} + +void continue_process(pid_t pid) +{ + continue_after_signal(pid, 0); +} + +void continue_enabling_breakpoint(pid_t pid, struct breakpoint * sbp) +{ + insert_breakpoint(pid, sbp); + continue_process(pid); +} diff --git a/tests/execl b/tests/execl Binary files differdeleted file mode 100755 index e39f5c6..0000000 --- a/tests/execl +++ /dev/null diff --git a/tests/execl.c b/tests/execl.c deleted file mode 100644 index 5ed7e19..0000000 --- a/tests/execl.c +++ /dev/null @@ -1,4 +0,0 @@ -main() -{ - execl("/home/cespedes/debian/hck/ltrace-0.1.2/tests/hello",0); -} diff --git a/tests/fork b/tests/fork Binary files differdeleted file mode 100755 index 9a19d31..0000000 --- a/tests/fork +++ /dev/null diff --git a/tests/fork.c b/tests/fork.c deleted file mode 100644 index 61b792a..0000000 --- a/tests/fork.c +++ /dev/null @@ -1,6 +0,0 @@ -main() -{ -fork(); -printf("hola\n"); -sleep(1); -} diff --git a/tests/hello b/tests/hello Binary files differdeleted file mode 100755 index a3ead2e..0000000 --- a/tests/hello +++ /dev/null diff --git a/tests/hello.c b/tests/hello.c deleted file mode 100644 index 499df67..0000000 --- a/tests/hello.c +++ /dev/null @@ -1,4 +0,0 @@ -main() -{ -printf("Hello, world!\n"); -} diff --git a/tests/int b/tests/int Binary files differdeleted file mode 100755 index 0802f1f..0000000 --- a/tests/int +++ /dev/null diff --git a/tests/int.c b/tests/int.c deleted file mode 100644 index 8e8e3a0..0000000 --- a/tests/int.c +++ /dev/null @@ -1,4 +0,0 @@ -main() -{ -__asm__("int $3"); -} diff --git a/tests/kill b/tests/kill Binary files differdeleted file mode 100755 index 85ca59f..0000000 --- a/tests/kill +++ /dev/null diff --git a/tests/kill.c b/tests/kill.c deleted file mode 100644 index 05b57d3..0000000 --- a/tests/kill.c +++ /dev/null @@ -1,6 +0,0 @@ -#include <signal.h> - -main() -{ -kill(0,SIGSTOP); -} diff --git a/tests/kill.s b/tests/kill.s deleted file mode 100644 index a0cc8c8..0000000 --- a/tests/kill.s +++ /dev/null @@ -1,21 +0,0 @@ - .file "kill.c" - .version "01.01" -gcc2_compiled.: -.text - .align 16 -.globl main - .type main,@function -main: - pushl %ebp - movl %esp,%ebp - pushl $19 - pushl $0 - call kill - addl $8,%esp -.L4: - movl %ebp,%esp - popl %ebp - ret -.Lfe1: - .size main,.Lfe1-main - .ident "GCC: (GNU) 2.7.2.2" diff --git a/tests/kill2 b/tests/kill2 Binary files differdeleted file mode 100755 index 73f1378..0000000 --- a/tests/kill2 +++ /dev/null diff --git a/tests/kill2.o b/tests/kill2.o Binary files differdeleted file mode 100644 index 1818eea..0000000 --- a/tests/kill2.o +++ /dev/null diff --git a/tests/kill2.s b/tests/kill2.s deleted file mode 100644 index 445f170..0000000 --- a/tests/kill2.s +++ /dev/null @@ -1,27 +0,0 @@ - .file "kill.c" - .version "01.01" -gcc2_compiled.: -.text - .align 16 -.globl main - .type main,@function -main: - movl $37,%eax - movl $0,%ebx - movl $19,%ecx - int $0x80 - ret - - pushl %ebp - movl %esp,%ebp - pushl $19 - pushl $0 - call kill - addl $8,%esp -.L4: - movl %ebp,%esp - popl %ebp - ret -.Lfe1: - .size main,.Lfe1-main - .ident "GCC: (GNU) 2.7.2.2" diff --git a/tests/kill3.o b/tests/kill3.o Binary files differdeleted file mode 100644 index 640af58..0000000 --- a/tests/kill3.o +++ /dev/null diff --git a/tests/kill3.s b/tests/kill3.s deleted file mode 100644 index 3f780c4..0000000 --- a/tests/kill3.s +++ /dev/null @@ -1,29 +0,0 @@ - .file "kill.c" - .version "01.01" -gcc2_compiled.: -.text - .align 16 -.globl main - .type main,@function -main: - xorl %eax,%eax - movb $37,%al - xorl %ebx,%ebx - xorl %ecx,%ecx - movb $19,%ecx - int $0x80 - ret - - pushl %ebp - movl %esp,%ebp - pushl $19 - pushl $0 - call kill - addl $8,%esp -.L4: - movl %ebp,%esp - popl %ebp - ret -.Lfe1: - .size main,.Lfe1-main - .ident "GCC: (GNU) 2.7.2.2" diff --git a/tests/trap b/tests/trap Binary files differdeleted file mode 100755 index 5530e99..0000000 --- a/tests/trap +++ /dev/null diff --git a/tests/trap.c b/tests/trap.c deleted file mode 100644 index 8e8e3a0..0000000 --- a/tests/trap.c +++ /dev/null @@ -1,4 +0,0 @@ -main() -{ -__asm__("int $3"); -} diff --git a/wait_for_something.c b/wait_for_something.c new file mode 100644 index 0000000..8b0fe1f --- /dev/null +++ b/wait_for_something.c @@ -0,0 +1,96 @@ +#define _GNU_SOURCE 1 +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <signal.h> +#include <string.h> + +#include "ltrace.h" +#include "options.h" +#include "output.h" + +static struct event event; + +/* This should also update `current_process' */ + +static struct process * pid2proc(int pid); + +struct event * wait_for_something(void) +{ + pid_t pid; + int status; + int tmp; + + pid = wait(&status); + if (pid==-1) { + if (errno==ECHILD) { + if (opt_d) { + output_line(0, "No more children"); + } + exit(0); + } + perror("wait"); + exit(1); + } + event.proc = pid2proc(pid); + if (!event.proc) { + fprintf(stderr, "signal from wrong pid %u ?!?\n", pid); + exit(1); + } + event.proc->instruction_pointer = get_instruction_pointer(pid); + if (opt_d>1) { + output_line(0,"signal from pid %u", pid); + } + if (event.proc->breakpoints_enabled == -1) { + if (opt_d>0) { + output_line(0,"Enabling breakpoints for pid %u...", pid); + } + enable_all_breakpoints(event.proc); + event.thing = LT_EV_NONE; + continue_process(event.proc->pid); + return &event; + } + tmp = syscall_p(pid, status); + if (tmp>=0) { + event.thing = (event.proc->current_syscall >= 0) ? LT_EV_SYSRET : LT_EV_SYSCALL; + event.e_un.sysnum = tmp; + return &event; + } + if (WIFEXITED(status)) { + event.thing = LT_EV_EXIT; + event.e_un.ret_val = WEXITSTATUS(status); + return &event; + } + if (WIFSIGNALED(status)) { + event.thing = LT_EV_EXIT_SIGNAL; + event.e_un.signum = WTERMSIG(status); + return &event; + } + if (!WIFSTOPPED(status)) { + event.thing = LT_EV_UNKNOWN; + return &event; + } + if (WSTOPSIG(status) != SIGTRAP) { + event.thing = LT_EV_SIGNAL; + event.e_un.signum = WSTOPSIG(status); + return &event; + } + event.thing = LT_EV_BREAKPOINT; + event.e_un.brk_addr = event.proc->instruction_pointer - DECR_PC_AFTER_BREAK; + return &event; +} + +static struct process * pid2proc(int pid) +{ + struct process * tmp; + + tmp = list_of_processes; + while(tmp) { + if (pid == tmp->pid) { + return tmp; + } + tmp = tmp->next; + } + return NULL; +} + |