Trace-objlist.shar

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	main.c
#	test-prog.c
# This archive created: Thu Nov  2 15:24:45 2000
export PATH; PATH=/bin:$PATH
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
X
XCC=cc
XCFLAGS=-n32 -g -fullwarn
XLDLIBS=
X
XSHELL=/bin/sh
Xall: trace-objlist  test-prog
X
Xtrace-objlist: main.o
X	$(CC) $(CFLAGS) main.o  $(LDLIBS) -o trace-objlist
X
Xmain.o: main.c
X	$(CC) $(CFLAGS) -c main.c 
X
Xtest-prog:test-prog.c
X	$(CC) $(CFLAGS) test-prog.c -o test-prog
X
Xshar:
X	shar -p X Makefile main.c test-prog.c >trace-objlist.shar
X
Xclean clobber:
X	-rm -f *.o
X	-rm -f trace-objlist
X	-rm -f core
X	-rm -f trace-objlist.shar
X	-rm -f test-prog
X
X#$Revision: 1.5 $
SHAR_EOF
fi # end of overwriting check
if test -f 'main.c'
then
	echo shar: will not over-write existing file "'main.c'"
else
sed 's/^X//' << \SHAR_EOF > 'main.c'
X/*
X   Given a process id, print the list of DSOs attached (and the count).
X
X
X   Because the execution and link addresses of an a.out
X   are identical, we can take all addresses as offsets
X   of the basic elf header (from elf_header_base_address,
X   which we find in the program header).
X
X   This is written for n32 apps only, at present.
X
X   For use by anyone.
X   Original version, Oct 18,2000
X   [email protected]
X   Revised and improved by Bruno Stefanizzi, [email protected]
X 
X   This version only handles n32 apps.
X   Handling o32 and 64 apps is straightforward, but
X   is not done ...
X
X   $Revision: 1.17 $
X   $Date: 2000/11/02 23:24:32 $
X*/
X
X
X
X#include <sys/types.h>
X#include <sys/signal.h>
X#include <sys/fault.h>
X#include <sys/syscall.h>
X#include <sys/procfs.h>
X
X#include <stdio.h>
X#include <stdlib.h>
X#include <stddef.h>
X#include <unistd.h>
X#include <fcntl.h>
X#include <elf.h>
X#include <string.h>
X#include <errno.h>
X#include <objlist.h>
X#include <dirent.h>
X
X
Xtypedef unsigned long ADDR;
Xtypedef unsigned long OFFSET;
X
X#define ELF_SYM Elf32_Sym
X#define ELF_DYN Elf32_Dyn
X#define ELF_EHDR Elf32_Ehdr
X#define ELF_PHDR Elf32_Phdr
X#define OBJ_INFO Elf32_Obj_Info
X
Xextern int errno;
X
Xchar *magic_symbol = "__rld_obj_head";
Xint fd;
X
X#define streq(a,b) (strcmp((a),(b)) == 0)
X
Xstatic int find_program_in_proc(char *pid_str, DIR *dirp);
Xstatic void open_proc_dir(DIR **dirp);
XADDR find_symbol(char *name, int in_fd);
Xvoid print_obj_list (char *name,ADDR, int in_fd);
Xvoid read_program_headers(OFFSET offset,int in_fd);
Xvoid read_victim_mem(char * buf,OFFSET offset,int len, int in_fd);
Xchar * read_victim_string(OFFSET  straddr,int in_fd);
Xint get_fd_on_executable(int fd);
Xstatic void print_piocmap(int fd);
X
X
X/* We seek/read in the victim's virtual address directly
X   That is, offset == virtual address
X*/
X#define ADJ_ADDR(a) ((a))
XADDR  elf_header_base_address;
X
Xstruct symbol_list_s {
X       ADDR symbols;
X       ADDR strings;
X       long symcount;
X       long stringsize;
X};
X
Xstruct symbol_list_s symbol_base;
X
Xstatic int print_short_form = 0;
Xstatic int print_piocmap_regions = 0;
Xstatic int print_headers = 1;
X
Xchar *progname = "";
Xint 
Xmain(int argc, char **argv)
X{
X    char pfile[256];
X    char *pid_str;
X    int pid_num;
X    ADDR addr;
X    int localfd;
X    DIR *dirp = NULL;
X    int i;
X
X    progname = argv[0];
X    if(argc < 2) {       
X        printf("Usage: %s [-p] [-s] [-h] process-pid or process-name\n",
X		progname);
X        printf("default: print long form, one line per active DSO\n");
X        printf("-s means short form, one line per active DSO\n");
X        printf("-p means print piocmap regions list\n");
X        printf("-h means suppress column headers\n");
X        exit(1);
X    }
X    
X    for( i = 1; i < argc; ++i) {
X	if(streq(argv[i],"--")) {
X		/* next arg is id/name */
X		++i;
X		break;
X	}
X	if(streq(argv[i],"-p")) {
X	   print_piocmap_regions = 1;
X	   continue;
X	}
X	if(streq(argv[i],"-s")) {
X	   print_short_form = 1;
X	   continue;
X	}
X	if(streq(argv[i],"-h")) {
X	   print_headers = 0;
X	   continue;
X	}
X	/* Done with - options */
X	break;
X    }
X
X    pid_str = argv[i];
X    pid_num = atoi(pid_str);    
X
X    if (pid_num == 0) {
X	open_proc_dir(&dirp);
X	fd = find_program_in_proc(pid_str, dirp);
X    }
X    else { 
X        sprintf(pfile, "/proc/%s", pid_str);
X        fd = open(pfile, O_RDONLY);
X    }
X    if (fd < 0) {
X        fprintf(stderr, "Can't open %s\n", pfile);
X        exit(1);
X    }
X    if(print_piocmap_regions) {
X        print_piocmap(fd);
X    } 
X    localfd = get_fd_on_executable(fd);
X
X    /* This reads using the file id, and sets elf_header_base_address,
X       which we need for  the second read_program_headers
X       call.  For this one it is an elf file, so
X       the offset of the elf header is zero.
X    */
X    read_program_headers(/*base=*/0,localfd);
X
X    /* We do not need or want the *file* fd any more */
X    close(localfd);
X
X    /* 
X       We have the virtual address
X       of the elf header itself now so that we
X       can read the elf header in the process.
X    */
X    read_program_headers(elf_header_base_address,fd);
X    /* Now find the elf symbol address for
X       the magic symbol.
X    */
X    addr = find_symbol(magic_symbol, fd);
X
X    print_obj_list(magic_symbol,addr,fd);
X    
X
X
X    close(fd);
X    return 0;
X}
X
Xstatic void open_proc_dir(DIR **dirp)
X{
X    if (!*dirp) {
X	if ((*dirp = opendir("/proc")) == NULL) {
X	    perror("/proc");
X	    exit(1);
X	}
X    }
X    rewinddir(*dirp);
X}
X
Xstatic int find_program_in_proc(char *proc_name, DIR *dirp)
X{
X    struct dirent *dent;
X    int fd = -1;
X    struct prpsinfo info;
X    char pfile[1024];
X
X    while ((dent = readdir(dirp)) != NULL) {
X        if (strcmp(dent->d_name, ".") != 0 
X		&& strcmp(dent->d_name, "..") != 0
X           	&& strcmp(dent->d_name, "pinfo") != 0) {
X	    sprintf(pfile, "/proc/%s", dent->d_name);
X            fd = open(pfile, O_RDONLY);
X	    if (fd>0) {
X	        ioctl(fd, PIOCPSINFO, &info);
X		if (strcmp(proc_name, info.pr_fname) == 0) {
X		    return fd;
X		}
X		else
X		    close(fd);
X            }
X	}
X    }
X    return fd;
X}
X
Xstatic void
Xprint_piocmap(int fd)
X{
X	int nmaps;
X	struct prmap_sgi maps[2048];
X	register prmap_sgi_t *map;
X	prmap_sgi_arg_t maparg;  
X	int i;
X
X	maparg.pr_vaddr = (caddr_t)maps;
X	maparg.pr_size = sizeof maps;
X
X	nmaps = ioctl(fd, PIOCMAP_SGI,&maparg);
X	if(nmaps == -1) {
X    	   printf("PIOCMAP_SGI ioctl failed\n");
X	}
X	if(print_headers) {
X	  printf("PIOCMAP_SGI output\n");
X	  printf("      vaddr       offset       size      mflags \n");
X	}
X	for (map = maps, i = 0; i < nmaps; ++i,++map) {
X    	   printf("[%d] 0x%08llx 0x%08llx 0x%08llx 0x%08llx\n",
X            	   i,
X            	   (unsigned long long)(map)->pr_vaddr,
X            	   (unsigned long long)(map)->pr_off,
X            	   (unsigned long long)(map)->pr_size,
X            	   (unsigned long long)(map)->pr_mflags);
X	}
X
X}
X
X/*
X       in_fd is open on the process, the zero byte of the
X       open fd should be the process elf header, so
X       follow the elf header to the data.
X
X       The  program header has (using example elfdump output):
X
X        Program header 4  type PT_DYNAMIC (2), offset 0x1c8 456
X        vaddr 0x100001c8 268435912, paddr 0x100001c8 268435912
X        filesz 0x100 256
X        memsz 0x100 256, flags 0x4 PF_R, align 0x4 4
X
X       so we can get the following things from the dynamic section,:
X
X[1]     DT_STRTAB                     0x100002dc                        
X[2]     DT_SYMTAB                     0x10000664                        
X[3]     DT_STRSZ                      0x1c0                     
X[4]     DT_MIPS_SYMTABNO              0x10            
X
X       And then we can search the table (linear search)
X        to find the name.
X
X
X*/
XADDR
Xfind_symbol(char *name, int in_fd)
X{
X
X       int i;
X       
X
X
X       for(i = 0; i <symbol_base.symcount; ++i) {
X               ELF_SYM sym;
X
X               char *dynsym_name;
X
X               
X               ADDR straddr;
X               ADDR symaddr = symbol_base.symbols
X                       + sizeof(ELF_SYM)*i;
X
X               read_victim_mem((char*)&sym,
X                       ADJ_ADDR(symaddr),sizeof(sym),in_fd);
X
X               straddr = sym.st_name + symbol_base.strings;
X
X               dynsym_name = read_victim_string(ADJ_ADDR(straddr),in_fd);
X
X               if(strcmp(name,dynsym_name) == 0) {
X                       /* FOUND! */
X                       return (ADDR)sym.st_value;
X               }
X       }
X
X       printf("Could not find %s\n",name);
X       exit(1);
X       /* NOTREACHED */
X}
X
X
X
X/*ARGSUSED*/
Xvoid
Xprint_obj_list (char *name,ADDR addr, int in_fd)
X{
X    int count = 0;
X    unsigned bufsize = 2000;
X    char *buf = malloc(bufsize);
X
X    ADDR victim_addr;
X
X	int nmaps;
X	struct prmap_sgi maps[2048];
X	register prmap_sgi_t *map;
X	prmap_sgi_arg_t maparg;  
X	int i;
X
X	maparg.pr_vaddr = (caddr_t)maps;
X	maparg.pr_size = sizeof maps;
X
X    {
X	    nmaps = ioctl(in_fd, PIOCMAP_SGI,&maparg);
X	    if(nmaps == -1) {
X    	       printf("PIOCMAP_SGI ioctl failed\n");
X	    }
X	}          
X       
X    if(!buf) {
X     printf("Unable to malloc basic buffer\n");
X     exit(1);
X    }
X    read_victim_mem((char *)&victim_addr,ADJ_ADDR(addr),
X           sizeof(victim_addr),in_fd);
X    if(print_headers) {
X       if(print_short_form) {
X	printf(
X"                                  vaddr     QSaddr size(KBytes)\n");
X       } else {
X        printf(
X"                                                   Kbytes\n");
X	printf(
X"                                  vaddr     QSaddr Phys rsdnt Tot  Priv Shrd\n");
X
X       }
X    }
X    for(; ; ) {
X        OBJ_INFO oi;
X        if(victim_addr == 0) {
X               break;
X        }
X        read_victim_mem((char *)&oi,ADJ_ADDR(victim_addr),
X               sizeof(oi),in_fd);
X
X        if(oi.oi_pathname_len > (bufsize-1)) {
X               bufsize = (unsigned)oi.oi_pathname_len+10;
X               free(buf);
X               buf = malloc(bufsize);
X               if(buf == 0) {
X                 printf("Un able to re-malloc %ld bytes buf\n",
X                       (long)bufsize);
X                 exit(2);
X               }
X        }
X        read_victim_mem((char *)buf,ADJ_ADDR(oi.oi_pathname),
X               (int)oi.oi_pathname_len,
X               in_fd);
X        buf[oi.oi_pathname_len] = 0;
X        {            
X            for (map = maps, i = 0, i = nmaps; i-- > 0; ++map) {       
X		if ((unsigned long long)map->pr_vaddr
X                        == (unsigned long  long)oi.oi_ehdr)
X                {
X			/* Print entries for each
X			   DSO, one per DSO (not one per memory region).
X			*/
X                    unsigned long weightSize, resSize, totalSize, privSize;
X                    unsigned long refCount = map->pr_mflags >> MA_REFCNT_SHIFT;
X                    unsigned long pgsize = getpagesize()/1024;
X                    privSize = ((map->pr_mflags & MA_PHYS) ||
X				(map->pr_mflags & MA_SHMEM)) ?
X				0 : (map->pr_psize * pgsize) / refCount;
X                    weightSize = map->pr_wsize;
X					weightSize *= pgsize;		/* use 1KB resolution */
X					weightSize /= MA_WSIZE_FRAC;
X					weightSize /= refCount;
X                    resSize = (map->pr_vsize * pgsize) / refCount;
X                    totalSize = (map->pr_size + 1023) / 1024;                      
X                    
X		    if(print_short_form) {
X                       printf("[%2d] %-24s 0x%08llx 0x%08llx %6d\n",
X				count,
X				buf, 
X				(unsigned long long)map->pr_vaddr,
X				(unsigned long long)oi.oi_orig_ehdr,
X				totalSize);
X		    } else {
X                       printf("[%2d] %-24s 0x%08llx 0x%08llx "
X			      "%4d %4d %4d %4d %4d\n",
X				count,
X				buf, 
X				(unsigned long long)map->pr_vaddr,
X				(unsigned long long)oi.oi_orig_ehdr,
X				weightSize, 
X				resSize, 
X				totalSize, 
X				privSize, 
X				weightSize-privSize);
X		    }
X                    
X                    break;
X                }
X            }
X        }
X        ++count;
X
X        victim_addr = oi.oi_next;
X    }
X    if(print_headers) {
X      printf("Number of list entries: %d\n",count);
X    }
X
X    return;
X}
Xvoid 
Xread_victim_mem(char * buf,OFFSET offset,int len, int in_fd)
X{
X       int res;
X
X
X       res = lseek(in_fd,offset,SEEK_SET);
X       if(res < 0) {
X               printf("Seek to 0x%llx %lld failed\n",
X                       (unsigned long long)offset,
X                       (long long)offset);
X               exit(2);
X       }
X
X       
X       res = read(in_fd,buf,len);
X       if(res != len) {
X               int myerr = errno;
X               printf("Unable to read %d bytes at offset"
X                       " 0x%llx %lld (%d %s)\n",
X                       len,(unsigned long long)offset,
X                       (long long)offset,
X                       myerr,strerror(myerr));
X               exit(2);
X       }
X       
X}
X
X
X/*
X       Read an entire string (slowly, performance 
X       does not matter).
X       Return pointer to static mem of the string.
X
X       At all times we ensure our static return buffer
X       is NUL terminated.
X       
X*/
Xchar * 
Xread_victim_string(OFFSET  straddr,int in_fd)
X{
X
X#define LBUFSIZ 32000
X       static char buf[LBUFSIZ+1];
X       int i;
X
X       buf[0] = 0;
X       for(i = 0; i < LBUFSIZ; ++i) {
X               buf[i+1] = 0;
X               read_victim_mem(buf+i,straddr+i,1,in_fd);
X               if(buf[i] == 0) {
X                       /* found NUL, have read entire string */
X                       return buf;
X               }
X       }
X       printf("INCOMPLETE string!, addr 0x%llx\n",(long long)straddr);
X       return buf;
X
X       
X#undef LBUFSIZ
X}
X
X/*
X       We are at 0 in the area, having opened but not read.
X       
X*/
Xvoid
Xread_program_headers(OFFSET elf_header_off,int in_fd)
X{
X
X       ELF_EHDR ehdr;
X       OFFSET phdr_offset;
X       int    phdr_count;
X       int    entsize;
X       int i;
X       OFFSET dynamic_offset;
X       int    dynamic_count;
X
X       read_victim_mem((char*)&ehdr,/* offset */elf_header_off,
X                       sizeof(ehdr), in_fd);
X
X       
X       if( ehdr.e_ident[0] != 0x7f ||
X           ehdr.e_ident[1] != 'E' ||
X           ehdr.e_ident[2] != 'L' ||
X           ehdr.e_ident[3] != 'F' ) {
X
X               printf("did not find elf header!\n");
X               exit(2);
X       }
X       /* should also check to see is n32, here */
X       /* FIXME */
X
X       phdr_offset = ehdr.e_phoff;
X       entsize = ehdr.e_phentsize;
X       phdr_count = ehdr.e_phnum;
X
X
X       for(i = 0; i < phdr_count; ++i) {
X          ELF_PHDR phdr;
X          read_victim_mem((char *)&phdr,
X                       elf_header_off+
X                       phdr_offset + i*entsize,
X               sizeof(phdr),in_fd);
X
X          if(phdr.p_offset == 0) {
X               elf_header_base_address = phdr.p_vaddr;
X               break;
X          }
X       }
X       if(i == phdr_count) {
X               printf("Never found base address!\n");
X               exit(2);
X       }
X
X       for(i = 0; i < phdr_count; ++i) {
X          ELF_PHDR phdr;
X          read_victim_mem((char *)&phdr,
X                       elf_header_off +
X                       phdr_offset + i*entsize,
X               sizeof(phdr),in_fd);
X
X          if(phdr.p_type == PT_DYNAMIC) {
X               dynamic_offset =  phdr.p_offset;
X               dynamic_count = (int)(phdr.p_filesz/sizeof(ELF_DYN));
X               break;
X          }
X       }
X       if(i == phdr_count) {
X               printf("Never found dynamic info!\n");
X               exit(2);
X       }
X       
X
X       for(i = 0; i <dynamic_count; ++i) {
X               ELF_DYN dyn;
X
X               read_victim_mem((char *)&dyn,
X                       elf_header_off +
X                       dynamic_offset+i*sizeof(dyn),
X                       sizeof(dyn),in_fd);
X
X               switch(dyn.d_tag) {
X               case DT_STRTAB:
X                       symbol_base.strings = dyn.d_un.d_ptr;
X                       break;
X               case DT_SYMTAB:
X                       symbol_base.symbols = dyn.d_un.d_ptr;
X                       break;
X               case DT_STRSZ:
X                       symbol_base.stringsize = (int)dyn.d_un.d_val;
X                       break;
X               case DT_MIPS_SYMTABNO:
X                       symbol_base.symcount = (int)dyn.d_un.d_val;
X                       break;
X               }
X       }
X
X
X       if(symbol_base.symcount == 0 ||
X               symbol_base.stringsize == 0 ||
X               symbol_base.symbols == 0 ||
X               symbol_base.strings ==0) {
X               printf("Did not find all dyamic entries we need!\n");
X               exit(3);
X       }
X
X
X}
X
X/*
X       Get open fd on the executable *file*.
X*/
Xint
Xget_fd_on_executable(int fd)
X{
X       int res;
X
X       /* Passing 3rd arg 0 means open fd on the executable file. */
X
X       res = ioctl(fd, PIOCOPENM,0);
X       if(res < 0) {
X          printf("Could not open fd on executable!\n");
X       }
X       return res;
X}
SHAR_EOF
fi # end of overwriting check
if test -f 'test-prog.c'
then
	echo shar: will not over-write existing file "'test-prog.c'"
else
sed 's/^X//' << \SHAR_EOF > 'test-prog.c'
X/*
X	$Revision: 1.3 $
X	$Date: 2000/10/18 18:57:41 $
X*/
X#include <unistd.h>
Xint main()
X{
X	sleep(1000);
X	return 0;
X}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0