Example #1
0
td_err_e
td_thr_setfpregs (const td_thrhandle_t *th, const prfpregset_t *fpregs)
{
  psaddr_t cancelhandling, tid;
  td_err_e err;

  LOG ("td_thr_setfpregs");

  if (th->th_unique == 0)
    /* Special case for the main thread before initialization.  */
    return ps_lsetfpregs (th->th_ta_p->ph, ps_getpid (th->th_ta_p->ph),
			  fpregs) != PS_OK ? TD_ERR : TD_OK;

  /* We have to get the state and the PID for this thread.  */
  err = DB_GET_FIELD (cancelhandling, th->th_ta_p, th->th_unique, pthread,
		      cancelhandling, 0);
  if (err != TD_OK)
    return err;

  /* Only set the registers if the thread hasn't yet terminated.  */
  if ((((int) (uintptr_t) cancelhandling) & TERMINATED_BITMASK) == 0)
    {
      err = DB_GET_FIELD (tid, th->th_ta_p, th->th_unique, pthread, tid, 0);
      if (err != TD_OK)
	return err;

      if (ps_lsetfpregs (th->th_ta_p->ph, (uintptr_t) tid, fpregs) != PS_OK)
	return TD_ERR;
    }

  return TD_OK;
}
td_err_e
td_thr_getgregs (const td_thrhandle_t *th, prgregset_t gregs)
{
  struct _pthread_descr_struct pds;

  LOG ("td_thr_getgregs");

  /* We have to get the state and the PID for this thread.  */
  if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds,
		 sizeof (struct _pthread_descr_struct)) != PS_OK)
    return TD_ERR;

  /* If the thread already terminated we return all zeroes.  */
  if (pds.p_terminated)
    memset (gregs, '\0', sizeof (prgregset_t));
  /* Otherwise get the register content through the callback.  */
  else
    {
      pid_t pid = pds.p_pid ?: ps_getpid (th->th_ta_p->ph);

      if (ps_lgetregs (th->th_ta_p->ph, pid, gregs) != PS_OK)
	return TD_ERR;
    }

  return TD_OK;
}
td_err_e
td_thr_getfpregs (const td_thrhandle_t *th, prfpregset_t *regset)
{
  psaddr_t cancelhandling, tid;
  td_err_e err;

  LOG ("td_thr_getfpregs");

  if (th->th_unique == 0)
    /* Special case for the main thread before initialization.  */
    return ps_lgetfpregs (th->th_ta_p->ph, ps_getpid (th->th_ta_p->ph),
			  regset) != PS_OK ? TD_ERR : TD_OK;

  /* We have to get the state and the PID for this thread.  */
  err = DB_GET_FIELD (cancelhandling, th->th_ta_p, th->th_unique, pthread,
		      cancelhandling, 0);
  if (err != TD_OK)
    return err;

  /* If the thread already terminated we return all zeroes.  */
  if (((int) (uintptr_t) cancelhandling) & TERMINATED_BITMASK)
    memset (regset, '\0', sizeof (*regset));
  /* Otherwise get the register content through the callback.  */
  else
    {
      err = DB_GET_FIELD (tid, th->th_ta_p, th->th_unique, pthread, tid, 0);
      if (err != TD_OK)
	return err;

      if (ps_lgetfpregs (th->th_ta_p->ph, (uintptr_t) tid, regset) != PS_OK)
	return TD_ERR;
    }

  return TD_OK;
}
td_err_e
td_ta_map_lwp2thr(td_thragent_t const * agent, lwpid_t lwpid,
		  td_thrhandle_t *th)
{
    th->pid = ps_getpid(agent->ph);
    th->tid = lwpid;
    return TD_OK;
}
Example #5
0
td_err_e
td_thr_get_info (const td_thrhandle_t *th, td_thrinfo_t *infop)
{
  struct _pthread_descr_struct pds;

  LOG ("td_thr_get_info");

  /* Handle the case when the thread library is not yet initialized.  */
  if (th->th_unique == NULL)
    {
      memset (&pds, '\0', sizeof (pds));
      pds.p_tid = PTHREAD_THREADS_MAX;
    }
  else
    /* Get the thread descriptor.  */
    if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds,
		   th->th_ta_p->sizeof_descr) != PS_OK)
      return TD_ERR;	/* XXX Other error value?  */

  /* Fill in information.  Clear first to provide reproducable
     results for the fields we do not fill in.  */
  memset (infop, '\0', sizeof (td_thrinfo_t));

  /* We have to handle the manager thread special since the thread
     descriptor in older versions is not fully initialized.  */
  if (pds.p_nr == 1)
    {
      infop->ti_tid = th->th_ta_p->pthread_threads_max * 2 + 1;
      infop->ti_type = TD_THR_SYSTEM;
      infop->ti_state = TD_THR_ACTIVE;
    }
  else
    {
      infop->ti_tid = pds.p_tid;
      infop->ti_tls = (char *) pds.p_specific;
      infop->ti_pri = pds.p_priority;
      infop->ti_type = TD_THR_USER;

      if (! pds.p_terminated)
	/* XXX For now there is no way to get more information.  */
	infop->ti_state = TD_THR_ACTIVE;
      else if (! pds.p_detached)
	infop->ti_state = TD_THR_ZOMBIE;
      else
	infop->ti_state = TD_THR_UNKNOWN;
    }

  /* Initialization which are the same in both cases.  */
  infop->ti_lid = pds.p_pid ?: ps_getpid (th->th_ta_p->ph);
  infop->ti_ta_p = th->th_ta_p;
  infop->ti_startfunc = pds.p_start_args.start_routine;
  memcpy (&infop->ti_events, &pds.p_eventbuf.eventmask,
	  sizeof (td_thr_events_t));
  infop->ti_traceme = pds.p_report_events != 0;

  return TD_OK;
}
td_err_e
td_thr_tlsbase (const td_thrhandle_t *th,
		unsigned long int modid,
		psaddr_t *base)
{
  td_err_e err;
  psaddr_t dtv, dtvslot, dtvptr;

  if (modid < 1)
    return TD_NOTLS;

  psaddr_t pd = th->th_unique;
  if (pd == 0)
    {
      /* This is the fake handle for the main thread before libpthread
	 initialization.  We are using 0 for its th_unique because we can't
	 trust that its thread register has been initialized.  But we need
	 a real pointer to have any TLS access work.  In case of dlopen'd
	 libpthread, initialization might not be for quite some time.  So
	 try looking up the thread register now.  Worst case, it's nonzero
	 uninitialized garbage and we get bogus results for TLS access
	 attempted too early.  Tough.  */

      td_thrhandle_t main_th;
      err = __td_ta_lookup_th_unique (th->th_ta_p, ps_getpid (th->th_ta_p->ph),
				      &main_th);
      if (err == 0)
	pd = main_th.th_unique;
      if (pd == 0)
	return TD_TLSDEFER;
    }

  /* Get the DTV pointer from the thread descriptor.  */
  err = DB_GET_FIELD (dtv, th->th_ta_p, pd, pthread, dtvp, 0);
  if (err != TD_OK)
    return err;

  /* Find the corresponding entry in the DTV.  */
  err = DB_GET_FIELD_ADDRESS (dtvslot, th->th_ta_p, dtv, dtv, dtv, modid);
  if (err != TD_OK)
    return err;

  /* Extract the TLS block address from that DTV slot.  */
  err = DB_GET_FIELD (dtvptr, th->th_ta_p, dtvslot, dtv_t, pointer_val, 0);
  if (err != TD_OK)
    return err;

  /* It could be that the memory for this module is not allocated for
     the given thread.  */
  if ((uintptr_t) dtvptr & 1)
    return TD_TLSDEFER;

  *base = dtvptr;
  return TD_OK;
}
td_err_e
td_thr_validate (const td_thrhandle_t *th)
{
  td_err_e err;
  psaddr_t list;

  LOG ("td_thr_validate");

  /* First check the list with threads using user allocated stacks.  */
  bool uninit = false;
  err = DB_GET_SYMBOL (list, th->th_ta_p, __stack_user);
  if (err == TD_OK)
    err = check_thread_list (th, list, &uninit);

  /* If our thread is not on this list search the list with stack
     using implementation allocated stacks.  */
  if (err == TD_NOTHR)
    {
      err = DB_GET_SYMBOL (list, th->th_ta_p, stack_used);
      if (err == TD_OK)
	err = check_thread_list (th, list, &uninit);

      if (err == TD_NOTHR && uninit && th->th_unique == 0)
	/* __pthread_initialize_minimal has not run yet.
	   There is only the special case thread handle.  */
	err = TD_OK;
    }

  if (err == TD_OK)
    {
      /* Verify that this is not a stale element in a fork child.  */
      pid_t match_pid = ps_getpid (th->th_ta_p->ph);
      psaddr_t pid;
      err = DB_GET_FIELD (pid, th->th_ta_p, th->th_unique, pthread, pid, 0);
      if (err == TD_OK && (pid_t) (uintptr_t) pid < 0)
	{
	  /* This was a thread that was about to fork, or it is the new sole
	     thread in a fork child.  In the latter case, its tid was stored
	     via CLONE_CHILD_SETTID and so is already the proper child PID.  */
	  if (-(pid_t) (uintptr_t) pid == match_pid)
	    /* It is about to do a fork, but is really still the parent PID.  */
	    pid = (psaddr_t) (uintptr_t) match_pid;
	  else
	    /* It must be a fork child, whose new PID is in the tid field.  */
	    err = DB_GET_FIELD (pid, th->th_ta_p, th->th_unique,
				pthread, tid, 0);
	}
      if (err == TD_OK && (pid_t) (uintptr_t) pid != match_pid)
	err = TD_NOTHR;
    }

  return err;
}
td_err_e
td_ta_new(struct ps_prochandle * proc_handle, td_thragent_t ** agent_out)
{
    td_thragent_t * agent;

    agent = (td_thragent_t *)malloc(sizeof(td_thragent_t));
    if (!agent) {
        return TD_MALLOC;
    }

    agent->pid = ps_getpid(proc_handle);
    agent->ph = proc_handle;
    *agent_out = agent;

    return TD_OK;
}
Example #9
0
td_err_e
td_ta_thr_iter (const td_thragent_t *ta_arg, td_thr_iter_f *callback,
		void *cbdata_p, td_thr_state_e state, int ti_pri,
		sigset_t *ti_sigmask_p, unsigned int ti_user_flags)
{
  td_thragent_t *const ta = (td_thragent_t *) ta_arg;
  td_err_e err;
  psaddr_t list = 0;

  LOG ("td_ta_thr_iter");

  /* Test whether the TA parameter is ok.  */
  if (! ta_ok (ta))
    return TD_BADTA;

  /* The thread library keeps two lists for the running threads.  One
     list contains the thread which are using user-provided stacks
     (this includes the main thread) and the other includes the
     threads for which the thread library allocated the stacks.  We
     have to iterate over both lists separately.  We start with the
     list of threads with user-defined stacks.  */

  pid_t pid = ps_getpid (ta->ph);
  err = DB_GET_SYMBOL (list, ta, __stack_user);
  if (err == TD_OK)
    err = iterate_thread_list (ta, callback, cbdata_p, state, ti_pri,
			       list, true, pid);

  /* And the threads with stacks allocated by the implementation.  */
  if (err == TD_OK)
    err = DB_GET_SYMBOL (list, ta, stack_used);
  if (err == TD_OK)
    err = iterate_thread_list (ta, callback, cbdata_p, state, ti_pri,
			       list, false, pid);

  return err;
}
Example #10
0
td_err_e
td_thr_get_info (const td_thrhandle_t *th, td_thrinfo_t *infop)
{
  td_err_e err;
  void *copy;
  psaddr_t tls, schedpolicy, schedprio, cancelhandling, tid, report_events;

  LOG ("td_thr_get_info");

  if (th->th_unique == 0)
    {
      /* Special case for the main thread before initialization.  */
      copy = NULL;
      tls = 0;
      cancelhandling = 0;
      schedprio = 0;
      tid = 0;
      err = DB_GET_VALUE (report_events, th->th_ta_p,
			  __nptl_initial_report_events, 0);
    }
  else
    {
      /* Copy the whole descriptor in once so we can access the several
	 fields locally.  Excess copying in one go is much better than
	 multiple ps_pdread calls.  */
      err = DB_GET_STRUCT (copy, th->th_ta_p, th->th_unique, pthread);
      if (err != TD_OK)
	return err;

      err = DB_GET_FIELD_ADDRESS (tls, th->th_ta_p, th->th_unique,
				  pthread, specific, 0);
      if (err != TD_OK)
	return err;

      err = DB_GET_FIELD_LOCAL (schedpolicy, th->th_ta_p, copy, pthread,
				schedpolicy, 0);
      if (err != TD_OK)
	return err;
      err = DB_GET_FIELD_LOCAL (schedprio, th->th_ta_p, copy, pthread,
				schedparam_sched_priority, 0);
      if (err != TD_OK)
	return err;
      err = DB_GET_FIELD_LOCAL (tid, th->th_ta_p, copy, pthread, tid, 0);
      if (err != TD_OK)
	return err;
      err = DB_GET_FIELD_LOCAL (cancelhandling, th->th_ta_p, copy, pthread,
				cancelhandling, 0);
      if (err != TD_OK)
	return err;
      err = DB_GET_FIELD_LOCAL (report_events, th->th_ta_p, copy, pthread,
				report_events, 0);
    }
  if (err != TD_OK)
    return err;

  /* Fill in information.  Clear first to provide reproducable
     results for the fields we do not fill in.  */
  memset (infop, '\0', sizeof (td_thrinfo_t));

  infop->ti_tid = (thread_t) th->th_unique;
  infop->ti_tls = (char *) tls;
  infop->ti_pri = ((uintptr_t) schedpolicy == SCHED_OTHER
		   ? 0 : (uintptr_t) schedprio);
  infop->ti_type = TD_THR_USER;

  if ((((int) (uintptr_t) cancelhandling) & EXITING_BITMASK) == 0)
    /* XXX For now there is no way to get more information.  */
    infop->ti_state = TD_THR_ACTIVE;
  else if ((((int) (uintptr_t) cancelhandling) & TERMINATED_BITMASK) == 0)
    infop->ti_state = TD_THR_ZOMBIE;
  else
    infop->ti_state = TD_THR_UNKNOWN;

  /* Initialization which are the same in both cases.  */
  infop->ti_ta_p = th->th_ta_p;
  infop->ti_lid = tid == 0 ? ps_getpid (th->th_ta_p->ph) : (uintptr_t) tid;
  infop->ti_traceme = report_events != 0;

  if (copy != NULL)
    err = DB_GET_FIELD_LOCAL (infop->ti_startfunc, th->th_ta_p, copy, pthread,
			      start_routine, 0);
  if (copy != NULL && err == TD_OK)
    {
      uint32_t idx;
      for (idx = 0; idx < TD_EVENTSIZE; ++idx)
	{
	  psaddr_t word;
	  err = DB_GET_FIELD_LOCAL (word, th->th_ta_p, copy, pthread,
				    eventbuf_eventmask_event_bits, idx);
	  if (err != TD_OK)
	    break;
	  infop->ti_events.event_bits[idx] = (uintptr_t) word;
	}
      if (err == TD_NOAPLIC)
	memset (&infop->ti_events.event_bits[idx], 0,
		(TD_EVENTSIZE - idx) * sizeof infop->ti_events.event_bits[0]);
    }

  return err;
}
Example #11
0
td_err_e
td_ta_new(struct ps_prochandle * proc_handle, td_thragent_t ** agent_out)
{
    td_thragent_t * agent;

    /* Platforms before Android 2.3 contain a system bug that prevents
     * gdbserver to attach to all threads in a target process when
     * it is run as the same userID than the target (works fine if
     * run as root).
     *
     * Due to the way gdbserver is coded, this makes gdbserver exit()
     * immediately (see linux_attach_lwp in linux-low.c). Even if we
     * modify the source code to not exit(), then signals will not
     * be properly rerouted to gdbserver, preventing breakpoints from
     * working correctly.
     *
     * The following code is here to test for this problematic condition.
     * If it is detected, we return TD_NOLIBTHREAD to indicate that there
     * are no threads to attach to (gdbserver will attach to the main thread
     * though).
     */
    do {
        char  path[64];
        DIR*  dir;
        struct dirent *entry;
        pid_t     my_pid = getpid();
        int       target_pid = ps_getpid(proc_handle);
        uint64_t  my_caps, tid_caps;

        D("Probing system for platform bug.\n");

        /* nothing to do if we run as root */
        if (geteuid() == 0) {
            D("Running as root, nothing to do.\n");
            break;
        }

        /* First, get our own permitted capabilities */
        if (_get_task_permitted_caps(my_pid, my_pid, &my_caps) < 0) {
            /* something is really fishy here */
            D("Could not get gdbserver permitted caps!\n");
            return TD_NOLIBTHREAD;
        }

        /* Now, for each thread in the target process, compare the
         * permitted capabilities set to our own. If they differ,
         * the thread attach will fail. Booo...
         */
        snprintf(path, sizeof path, "/proc/%d/task", target_pid);
        dir = opendir(path);
        if (!dir) {
            D("Could not open %s: %s\n", path, strerror(errno));
            break;
        }
        while ((entry = readdir(dir)) != NULL) {
            int  tid;

            if (entry->d_name[0] == '.')   /* skip . and .. */
                continue;

            tid = atoi(entry->d_name);
            if (tid == 0)  /* should not happen - be safe */
                continue;

            if (_get_task_permitted_caps(target_pid, tid, &tid_caps) < 0) {
                /* again, something is fishy */
                D("Could not get permitted caps for thread %d\n", tid);
                closedir(dir);
                return TD_NOLIBTHREAD;
            }

            if (tid_caps != my_caps) {
                /* AAAARGH !! The permitted capabilities set differ. */
                D("AAAAAH, Can't debug threads!\n");
                closedir(dir);
                return TD_NOLIBTHREAD;
            }
        }
        closedir(dir);
        D("Victory: We can debug threads!\n");
    } while (0);

    /* We now return to our regularly scheduled program */

    agent = (td_thragent_t *)malloc(sizeof(td_thragent_t));
    if (!agent) {
        return TD_MALLOC;
    }

    agent->pid = ps_getpid(proc_handle);
    agent->ph = proc_handle;
    *agent_out = agent;

    return TD_OK;
}