Blame elf/sotruss-lib.c

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