Blame elf/sotruss-lib.c

Packit 6c4009
/* Trace calls through PLTs and show caller, callee, and parameters.
Packit 6c4009
   Copyright (C) 2011-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <error.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
#include <sys/uio.h>
Packit 6c4009
Packit 6c4009
#include <ldsodefs.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
extern const char *__progname;
Packit 6c4009
extern const char *__progname_full;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* List of objects to trace calls from.  */
Packit 6c4009
static const char *fromlist;
Packit 6c4009
/* List of objects to trace calls to.  */
Packit 6c4009
static const char *tolist;
Packit 6c4009
Packit 6c4009
/* If non-zero, also trace returns of the calls.  */
Packit 6c4009
static int do_exit;
Packit 6c4009
/* If non-zero print PID for each line.  */
Packit 6c4009
static int print_pid;
Packit 6c4009
Packit 6c4009
/* The output stream to use.  */
Packit 6c4009
static FILE *out_file;
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
match_pid (pid_t pid, const char *which)
Packit 6c4009
{
Packit 6c4009
  if (which == NULL || which[0] == '\0')
Packit 6c4009
    {
Packit 6c4009
      print_pid = 1;
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  char *endp;
Packit 6c4009
  unsigned long n = strtoul (which, &endp, 0);
Packit 6c4009
  return *endp == '\0' && n == pid;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
init (void)
Packit 6c4009
{
Packit 6c4009
  fromlist = getenv ("SOTRUSS_FROMLIST");
Packit 6c4009
  if (fromlist != NULL && fromlist[0] == '\0')
Packit 6c4009
    fromlist = NULL;
Packit 6c4009
  tolist = getenv ("SOTRUSS_TOLIST");
Packit 6c4009
  if (tolist != NULL && tolist[0] == '\0')
Packit 6c4009
    tolist = NULL;
Packit 6c4009
  do_exit = (getenv ("SOTRUSS_EXIT") ?: "")[0] != '\0';
Packit 6c4009
Packit 6c4009
  /* Determine whether this process is supposed to be traced and if
Packit 6c4009
     yes, whether we should print into a file.  */
Packit 6c4009
  const char *which_process = getenv ("SOTRUSS_WHICH");
Packit 6c4009
  pid_t pid = getpid ();
Packit 6c4009
  int out_fd = -1;
Packit 6c4009
  if (match_pid (pid, which_process))
Packit 6c4009
    {
Packit 6c4009
      const char *out_filename = getenv ("SOTRUSS_OUTNAME");
Packit 6c4009
Packit 6c4009
      if (out_filename != NULL && out_filename[0] != 0)
Packit 6c4009
	{
Packit 6c4009
	  size_t out_filename_len = strlen (out_filename) + 13;
Packit 6c4009
	  char fullname[out_filename_len];
Packit 6c4009
	  char *endp = stpcpy (fullname, out_filename);
Packit 6c4009
	  if (which_process == NULL || which_process[0] == '\0')
Packit 6c4009
	    snprintf (endp, 13, ".%ld", (long int) pid);
Packit 6c4009
Packit 6c4009
	  out_fd = open (fullname, O_RDWR | O_CREAT | O_TRUNC, 0666);
Packit 6c4009
	  if (out_fd != -1)
Packit 6c4009
	    print_pid = 0;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If we do not write into a file write to stderr.  Duplicate the
Packit 6c4009
     descriptor so that we can keep printing in case the program
Packit 6c4009
     closes stderr.  Try first to allocate a descriptor with a value
Packit 6c4009
     usually not used as to minimize interference with the
Packit 6c4009
     program.  */
Packit 6c4009
  if (out_fd == -1)
Packit 6c4009
    {
Packit 6c4009
      out_fd = fcntl (STDERR_FILENO, F_DUPFD, 1000);
Packit 6c4009
      if (out_fd == -1)
Packit 6c4009
	out_fd = dup (STDERR_FILENO);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (out_fd != -1)
Packit 6c4009
    {
Packit 6c4009
      /* Convert file descriptor into a stream.  */
Packit 6c4009
      out_file = fdopen (out_fd, "w");
Packit 6c4009
      if (out_file != NULL)
Packit 6c4009
	setlinebuf (out_file);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Audit interface verification.  We also initialize everything if
Packit 6c4009
   everything checks out OK.  */
Packit 6c4009
unsigned int
Packit 6c4009
la_version (unsigned int v)
Packit 6c4009
{
Packit 6c4009
  if (v != LAV_CURRENT)
Packit 6c4009
    error (1, 0, "cannot handle interface version %u", v);
Packit 6c4009
Packit 6c4009
  init ();
Packit 6c4009
Packit 6c4009
  return v;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Check whether a file name is on the colon-separated list of file
Packit 6c4009
   names.  */
Packit 6c4009
static unsigned int
Packit 6c4009
match_file (const char *list, const char *name, size_t name_len,
Packit 6c4009
	    unsigned int mask)
Packit 6c4009
{
Packit 6c4009
  if (list[0] == '\0')
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  const char *cp = list;
Packit 6c4009
  while (1)
Packit 6c4009
    {
Packit 6c4009
      if (strncmp (cp, name, name_len) == 0
Packit 6c4009
	  && (cp[name_len] == ':' || cp[name_len] == '\0'))
Packit 6c4009
	return mask;
Packit 6c4009
Packit 6c4009
      cp = strchr (cp, ':');
Packit 6c4009
      if (cp == NULL)
Packit 6c4009
	return 0;
Packit 6c4009
      ++cp;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
unsigned int
Packit 6c4009
la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
Packit 6c4009
{
Packit 6c4009
  if (out_file == NULL)
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  const char *full_name = map->l_name ?: "";
Packit 6c4009
  if (full_name[0] == '\0')
Packit 6c4009
    full_name = __progname_full;
Packit 6c4009
  size_t full_name_len = strlen (full_name);
Packit 6c4009
  const char *base_name = basename (full_name);
Packit 6c4009
  if (base_name[0] == '\0')
Packit 6c4009
    base_name = __progname;
Packit 6c4009
  size_t base_name_len = strlen (base_name);
Packit 6c4009
Packit 6c4009
  int result = 0;
Packit 6c4009
  const char *print_name = NULL;
Packit 6c4009
  for (struct libname_list *l = map->l_libname; l != NULL; l = l->next)
Packit 6c4009
    {
Packit 6c4009
      if (print_name == NULL || (print_name[0] == '/' && l->name[0] != '/'))
Packit 6c4009
	print_name = l->name;
Packit 6c4009
Packit 6c4009
      if (fromlist != NULL)
Packit 6c4009
	result |= match_file (fromlist, l->name, strlen (l->name),
Packit 6c4009
			      LA_FLG_BINDFROM);
Packit 6c4009
Packit 6c4009
      if (tolist != NULL)
Packit 6c4009
	result |= match_file (tolist, l->name, strlen (l->name),LA_FLG_BINDTO);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (print_name == NULL)
Packit 6c4009
    print_name = base_name;
Packit 6c4009
  if (print_name[0] == '\0')
Packit 6c4009
    print_name = __progname;
Packit 6c4009
Packit 6c4009
  /* We cannot easily get to the object name in the PLT handling
Packit 6c4009
     functions.  Use the cookie to get the string pointer passed back
Packit 6c4009
     to us.  */
Packit 6c4009
  *cookie = (uintptr_t) print_name;
Packit 6c4009
Packit 6c4009
  /* The object name has to be on the list of objects to trace calls
Packit 6c4009
     from or that list must be empty.  In the latter case we trace
Packit 6c4009
     only calls from the main binary.  */
Packit 6c4009
  if (fromlist == NULL)
Packit 6c4009
    result |= map->l_name[0] == '\0' ? LA_FLG_BINDFROM : 0;
Packit 6c4009
  else
Packit 6c4009
    result |= (match_file (fromlist, full_name, full_name_len,
Packit 6c4009
			   LA_FLG_BINDFROM)
Packit 6c4009
	       | match_file (fromlist, base_name, base_name_len,
Packit 6c4009
			     LA_FLG_BINDFROM));
Packit 6c4009
Packit 6c4009
  /* The object name has to be on the list of objects to trace calls
Packit 6c4009
     to or that list must be empty.  In the latter case we trace
Packit 6c4009
     calls toall objects.  */
Packit 6c4009
  if (tolist == NULL)
Packit 6c4009
    result |= LA_FLG_BINDTO;
Packit 6c4009
  else
Packit 6c4009
    result |= (match_file (tolist, full_name, full_name_len, LA_FLG_BINDTO)
Packit 6c4009
	       | match_file (tolist, base_name, base_name_len, LA_FLG_BINDTO));
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
#if __ELF_NATIVE_CLASS == 32
Packit 6c4009
# define la_symbind la_symbind32
Packit 6c4009
typedef Elf32_Sym Elf_Sym;
Packit 6c4009
#else
Packit 6c4009
# define la_symbind la_symbind64
Packit 6c4009
typedef Elf64_Sym Elf_Sym;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
uintptr_t
Packit 6c4009
la_symbind (Elf_Sym *sym, unsigned int ndx, uintptr_t *refcook,
Packit 6c4009
	    uintptr_t *defcook, unsigned int *flags, const char *symname)
Packit 6c4009
{
Packit 6c4009
  if (!do_exit)
Packit 6c4009
    *flags = LA_SYMB_NOPLTEXIT;
Packit 6c4009
Packit 6c4009
  return sym->st_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
print_enter (uintptr_t *refcook, uintptr_t *defcook, const char *symname,
Packit 6c4009
	     unsigned long int reg1, unsigned long int reg2,
Packit 6c4009
	     unsigned long int reg3, unsigned int flags)
Packit 6c4009
{
Packit 6c4009
  char buf[3 * sizeof (pid_t) + 3];
Packit 6c4009
  buf[0] = '\0';
Packit 6c4009
  if (print_pid)
Packit 6c4009
    snprintf (buf, sizeof (buf), "%5ld: ", (long int) getpid ());
Packit 6c4009
Packit 6c4009
  fprintf (out_file, "%s%15s -> %-15s:%s%s(0x%lx, 0x%lx, 0x%lx)\n",
Packit 6c4009
	   buf, (char *) *refcook, (char *) *defcook,
Packit 6c4009
	   (flags & LA_SYMB_NOPLTEXIT) ? "*" : " ", symname, reg1, reg2, reg3);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
#ifdef __i386__
Packit 6c4009
Elf32_Addr
Packit 6c4009
la_i86_gnu_pltenter (Elf32_Sym *sym __attribute__ ((unused)),
Packit 6c4009
		     unsigned int ndx __attribute__ ((unused)),
Packit 6c4009
		     uintptr_t *refcook, uintptr_t *defcook,
Packit 6c4009
		     La_i86_regs *regs, unsigned int *flags,
Packit 6c4009
		     const char *symname, long int *framesizep)
Packit 6c4009
{
Packit 6c4009
  unsigned long int *sp = (unsigned long int *) regs->lr_esp;
Packit 6c4009
Packit 6c4009
  print_enter (refcook, defcook, symname, sp[1], sp[2], sp[3], *flags);
Packit 6c4009
Packit 6c4009
  /* No need to copy anything, we will not need the parameters in any case.  */
Packit 6c4009
  *framesizep = 0;
Packit 6c4009
Packit 6c4009
  return sym->st_value;
Packit 6c4009
}
Packit 6c4009
#elif defined __x86_64__
Packit 6c4009
Elf64_Addr
Packit 6c4009
la_x86_64_gnu_pltenter (Elf64_Sym *sym __attribute__ ((unused)),
Packit 6c4009
			unsigned int ndx __attribute__ ((unused)),
Packit 6c4009
			uintptr_t *refcook, uintptr_t *defcook,
Packit 6c4009
			La_x86_64_regs *regs, unsigned int *flags,
Packit 6c4009
			const char *symname, long int *framesizep)
Packit 6c4009
{
Packit 6c4009
  print_enter (refcook, defcook, symname,
Packit 6c4009
	       regs->lr_rdi, regs->lr_rsi, regs->lr_rdx, *flags);
Packit 6c4009
Packit 6c4009
  /* No need to copy anything, we will not need the parameters in any case.  */
Packit 6c4009
  *framesizep = 0;
Packit 6c4009
Packit 6c4009
  return sym->st_value;
Packit 6c4009
}
Packit 6c4009
#elif defined __sparc__ && !defined __arch64__
Packit 6c4009
Elf32_Addr
Packit 6c4009
la_sparc32_gnu_pltenter (Elf32_Sym *sym __attribute__ ((unused)),
Packit 6c4009
			 unsigned int ndx __attribute__ ((unused)),
Packit 6c4009
			 uintptr_t *refcook, uintptr_t *defcook,
Packit 6c4009
			 La_sparc32_regs *regs, unsigned int *flags,
Packit 6c4009
			 const char *symname, long int *framesizep)
Packit 6c4009
{
Packit 6c4009
  print_enter (refcook, defcook, symname,
Packit 6c4009
	       regs->lr_reg[0], regs->lr_reg[1], regs->lr_reg[2],
Packit 6c4009
	       *flags);
Packit 6c4009
Packit 6c4009
  /* No need to copy anything, we will not need the parameters in any case.  */
Packit 6c4009
  *framesizep = 0;
Packit 6c4009
Packit 6c4009
  return sym->st_value;
Packit 6c4009
}
Packit 6c4009
#elif defined __sparc__ && defined __arch64__
Packit 6c4009
Elf64_Addr
Packit 6c4009
la_sparc64_gnu_pltenter (Elf64_Sym *sym __attribute__ ((unused)),
Packit 6c4009
			 unsigned int ndx __attribute__ ((unused)),
Packit 6c4009
			 uintptr_t *refcook, uintptr_t *defcook,
Packit 6c4009
			 La_sparc64_regs *regs, unsigned int *flags,
Packit 6c4009
			 const char *symname, long int *framesizep)
Packit 6c4009
{
Packit 6c4009
  print_enter (refcook, defcook, symname,
Packit 6c4009
	       regs->lr_reg[0], regs->lr_reg[1], regs->lr_reg[2],
Packit 6c4009
	       *flags);
Packit 6c4009
Packit 6c4009
  /* No need to copy anything, we will not need the parameters in any case.  */
Packit 6c4009
  *framesizep = 0;
Packit 6c4009
Packit 6c4009
  return sym->st_value;
Packit 6c4009
}
Packit 6c4009
#elif !defined HAVE_ARCH_PLTENTER
Packit 6c4009
# warning "pltenter for architecture not supported"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
print_exit (uintptr_t *refcook, uintptr_t *defcook, const char *symname,
Packit 6c4009
	    unsigned long int reg)
Packit 6c4009
{
Packit 6c4009
  char buf[3 * sizeof (pid_t) + 3];
Packit 6c4009
  buf[0] = '\0';
Packit 6c4009
  if (print_pid)
Packit 6c4009
    snprintf (buf, sizeof (buf), "%5ld: ", (long int) getpid ());
Packit 6c4009
Packit 6c4009
  fprintf (out_file, "%s%15s -> %-15s:%s%s - 0x%lx\n",
Packit 6c4009
	   buf, (char *) *refcook, (char *) *defcook, " ", symname, reg);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
#ifdef __i386__
Packit 6c4009
unsigned int
Packit 6c4009
la_i86_gnu_pltexit (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
Packit 6c4009
		    uintptr_t *defcook, const struct La_i86_regs *inregs,
Packit 6c4009
		    struct La_i86_retval *outregs, const char *symname)
Packit 6c4009
{
Packit 6c4009
  print_exit (refcook, defcook, symname, outregs->lrv_eax);
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
#elif defined __x86_64__
Packit 6c4009
unsigned int
Packit 6c4009
la_x86_64_gnu_pltexit (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
Packit 6c4009
		       uintptr_t *defcook, const struct La_x86_64_regs *inregs,
Packit 6c4009
		       struct La_x86_64_retval *outregs, const char *symname)
Packit 6c4009
{
Packit 6c4009
  print_exit (refcook, defcook, symname, outregs->lrv_rax);
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
#elif defined __sparc__ && !defined __arch64__
Packit 6c4009
unsigned int
Packit 6c4009
la_sparc32_gnu_pltexit (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
Packit 6c4009
			uintptr_t *defcook, const struct La_sparc32_regs *inregs,
Packit 6c4009
			struct La_sparc32_retval *outregs, const char *symname)
Packit 6c4009
{
Packit 6c4009
  print_exit (refcook, defcook, symname, outregs->lrv_reg[0]);
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
#elif defined __sparc__ && defined __arch64__
Packit 6c4009
unsigned int
Packit 6c4009
la_sparc64_gnu_pltexit (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
Packit 6c4009
			uintptr_t *defcook, const struct La_sparc64_regs *inregs,
Packit 6c4009
			struct La_sparc64_retval *outregs, const char *symname)
Packit 6c4009
{
Packit 6c4009
  print_exit (refcook, defcook, symname, outregs->lrv_reg[0]);
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
#elif !defined HAVE_ARCH_PLTEXIT
Packit 6c4009
# warning "pltexit for architecture not supported"
Packit 6c4009
#endif