static td_err_e check_thread_list (const td_thrhandle_t *th, psaddr_t head, bool *uninit) { td_err_e err; psaddr_t next, ofs; err = DB_GET_FIELD (next, th->th_ta_p, head, list_t, next, 0); if (err == TD_OK) { if (next == 0) { *uninit = true; return TD_NOTHR; } err = DB_GET_FIELD_ADDRESS (ofs, th->th_ta_p, 0, pthread, list, 0); } while (err == TD_OK) { if (next == head) return TD_NOTHR; if (next - (ofs - (psaddr_t) 0) == th->th_unique) return TD_OK; err = DB_GET_FIELD (next, th->th_ta_p, next, list_t, next, 0); } return err; }
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_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_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_thr_tls_get_addr (const td_thrhandle_t *th, psaddr_t map_address, size_t offset, psaddr_t *address) { td_err_e err; psaddr_t modid; /* Get the TLS module ID from the `struct link_map' in the inferior. */ err = DB_GET_FIELD (modid, th->th_ta_p, map_address, link_map, l_tls_modid, 0); if (err == TD_NOCAPAB) return TD_NOAPLIC; if (err == TD_OK) { err = td_thr_tlsbase (th, (uintptr_t) modid, address); if (err == TD_OK) *address += offset; } return err; }
td_err_e td_thr_tsd (const td_thrhandle_t *th, const thread_key_t tk, void **data) { td_err_e err; psaddr_t tk_seq, level1, level2, seq, value; void *copy; uint32_t pthread_key_2ndlevel_size, idx1st, idx2nd; LOG ("td_thr_tsd"); /* Get the key entry. */ err = DB_GET_VALUE (tk_seq, th->th_ta_p, __pthread_keys, tk); if (err == TD_NOAPLIC) return TD_BADKEY; if (err != TD_OK) return err; /* Fail if this key is not at all used. */ if (((uintptr_t) tk_seq & 1) == 0) return TD_BADKEY; /* This makes sure we have the size information on hand. */ err = DB_GET_FIELD_ADDRESS (level2, th->th_ta_p, 0, pthread_key_data_level2, data, 1); if (err != TD_OK) return err; /* Compute the indeces. */ pthread_key_2ndlevel_size = DB_DESC_NELEM (th->th_ta_p->ta_field_pthread_key_data_level2_data); idx1st = tk / pthread_key_2ndlevel_size; idx2nd = tk % pthread_key_2ndlevel_size; /* Now fetch the first level pointer. */ err = DB_GET_FIELD (level1, th->th_ta_p, th->th_unique, pthread, specific, idx1st); if (err == TD_NOAPLIC) return TD_DBERR; if (err != TD_OK) return err; /* Check the pointer to the second level array. */ if (level1 == 0) return TD_NOTSD; /* Locate the element within the second level array. */ err = DB_GET_FIELD_ADDRESS (level2, th->th_ta_p, level1, pthread_key_data_level2, data, idx2nd); if (err == TD_NOAPLIC) return TD_DBERR; if (err != TD_OK) return err; /* Now copy in that whole structure. */ err = DB_GET_STRUCT (copy, th->th_ta_p, level2, pthread_key_data); if (err != TD_OK) return err; /* Check whether the data is valid. */ err = DB_GET_FIELD_LOCAL (seq, th->th_ta_p, copy, pthread_key_data, seq, 0); if (err != TD_OK) return err; if (seq != tk_seq) return TD_NOTSD; /* Finally, fetch the value. */ err = DB_GET_FIELD_LOCAL (value, th->th_ta_p, copy, pthread_key_data, data, 0); if (err == TD_OK) *data = value; return err; }
static td_err_e iterate_thread_list (td_thragent_t *ta, td_thr_iter_f *callback, void *cbdata_p, td_thr_state_e state, int ti_pri, psaddr_t head, bool fake_empty, pid_t match_pid) { td_err_e err; psaddr_t next, ofs; void *copy; /* Test the state. XXX This is incomplete. Normally this test should be in the loop. */ if (state != TD_THR_ANY_STATE) return TD_OK; err = DB_GET_FIELD (next, ta, head, list_t, next, 0); if (err != TD_OK) return err; if (next == 0 && fake_empty) { /* __pthread_initialize_minimal has not run. There is just the main thread to return. We cannot rely on its thread register. They sometimes contain garbage that would confuse us, left by the kernel at exec. So if it looks like initialization is incomplete, we only fake a special descriptor for the initial thread. */ td_thrhandle_t th = { ta, 0 }; return callback (&th, cbdata_p) != 0 ? TD_DBERR : TD_OK; } /* Cache the offset from struct pthread to its list_t member. */ err = DB_GET_FIELD_ADDRESS (ofs, ta, 0, pthread, list, 0); if (err != TD_OK) return err; if (ta->ta_sizeof_pthread == 0) { err = _td_check_sizeof (ta, &ta->ta_sizeof_pthread, SYM_SIZEOF_pthread); if (err != TD_OK) return err; } copy = __alloca (ta->ta_sizeof_pthread); while (next != head) { psaddr_t addr, schedpolicy, schedprio; addr = next - (ofs - (psaddr_t) 0); if (next == 0 || addr == 0) /* Sanity check. */ return TD_DBERR; /* 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. */ if (ps_pdread (ta->ph, addr, copy, ta->ta_sizeof_pthread) != PS_OK) return TD_ERR; /* Verify that this thread's pid field matches the child PID. If its pid field is negative, it's about to do a fork or it is the sole thread in a fork child. */ psaddr_t pid; err = DB_GET_FIELD_LOCAL (pid, ta, copy, pthread, pid, 0); if (err == TD_OK && (pid_t) (uintptr_t) pid < 0) { 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_LOCAL (pid, ta, copy, pthread, tid, 0); } if (err != TD_OK) break; if ((pid_t) (uintptr_t) pid == match_pid) { err = DB_GET_FIELD_LOCAL (schedpolicy, ta, copy, pthread, schedpolicy, 0); if (err != TD_OK) break; err = DB_GET_FIELD_LOCAL (schedprio, ta, copy, pthread, schedparam_sched_priority, 0); if (err != TD_OK) break; /* Now test whether this thread matches the specified conditions. */ /* Only if the priority level is as high or higher. */ int descr_pri = ((uintptr_t) schedpolicy == SCHED_OTHER ? 0 : (uintptr_t) schedprio); if (descr_pri >= ti_pri) { /* Yep, it matches. Call the callback function. */ td_thrhandle_t th; th.th_ta_p = (td_thragent_t *) ta; th.th_unique = addr; if (callback (&th, cbdata_p) != 0) return TD_DBERR; } } /* Get the pointer to the next element. */ err = DB_GET_FIELD_LOCAL (next, ta, copy + (ofs - (psaddr_t) 0), list_t, next, 0); if (err != TD_OK) break; } return err; }