/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* MDB Target Layer
*
* The *target* is the program being inspected by the debugger. The MDB target
* layer provides a set of functions that insulate common debugger code,
* including the MDB Module API, from the implementation details of how the
* debugger accesses information from a given target. Each target exports a
* standard set of properties, including one or more address spaces, one or
* more symbol tables, a set of load objects, and a set of threads that can be
* examined using the interfaces in <mdb/mdb_target.h>. This technique has
* been employed successfully in other debuggers, including [1], primarily
* to improve portability, although the term "target" often refers to the
* encapsulation of architectural or operating system-specific details. The
* target abstraction is useful for MDB because it allows us to easily extend
* the debugger to examine a variety of different program forms. Primarily,
* the target functions validate input arguments and then call an appropriate
* function in the target ops vector, defined in <mdb/mdb_target_impl.h>.
* However, this interface layer provides a very high level of flexibility for
* separating the debugger interface from instrumentation details. Experience
* has shown this kind of design can facilitate separating out debugger
* instrumentation into an external agent [2] and enable the development of
* advanced instrumentation frameworks [3]. We want MDB to be an ideal
* extensible framework for the development of such applications.
*
* Aside from a set of wrapper functions, the target layer also provides event
* management for targets that represent live executing programs. Our model of
* events is also extensible, and is based upon work in [3] and [4]. We define
* a *software event* as a state transition in the target program (for example,
* the transition of the program counter to a location of interest) that is
* observed by the debugger or its agent. A *software event specifier* is a
* description of a class of software events that is used by the debugger to
* instrument the target so that the corresponding software events can be
* observed. In MDB, software event specifiers are represented by the
* mdb_sespec_t structure, defined in <mdb/mdb_target_impl.h>. As the user,
* the internal debugger code, and MDB modules may all wish to observe software
* events and receive appropriate notification and callbacks, we do not expose
* software event specifiers directly as part of the user interface. Instead,
* clients of the target layer request that events be observed by creating
* new *virtual event specifiers*. Each virtual specifier is named by a unique
* non-zero integer (the VID), and is represented by a mdb_vespec_t structure.
* One or more virtual specifiers are then associated with each underlying
* software event specifier. This design enforces the constraint that the
* target must only insert one set of instrumentation, regardless of how many
* times the target layer was asked to trace a given event. For example, if
* multiple clients request a breakpoint at a particular address, the virtual
* specifiers will map to the same sespec, ensuring that only one breakpoint
* trap instruction is actually planted at the given target address. When no
* virtual specifiers refer to an sespec, it is no longer needed and can be
* removed, along with the corresponding instrumentation.
*
* The following state transition diagram illustrates the life cycle of a
* software event specifier and example transitions:
*
* cont/
* +--------+ delete +--------+ stop +-------+
* (|( DEAD )|) <------- ( ACTIVE ) <------> ( ARMED )
* +--------+ +--------+ +-------+
* delete | object / \ reset | failure
* | v v |
* | +--------+ +-------+ |
* +---- ( IDLE ) ( ERR ) <----+
* | +--------+ +-------+
* | |
* +------------------------------+
*
* The MDB execution control model is based upon the synchronous debugging
* model exported by Solaris proc(4). A target program is set running or the
* debugger is attached to a running target. On ISTOP (stop on event of
* interest), one target thread is selected as the representative. The
* algorithm for selecting the representative is target-specific, but we assume
* that if an observed software event has occurred, the target will select the
* thread that triggered the state transition of interest. The other threads
* are stopped in sympathy with the representative as soon as possible. Prior
* to continuing the target, we plant our instrumentation, transitioning event
* specifiers from the ACTIVE to the ARMED state, and then back again when the
* target stops. We then query each active event specifier to learn which ones
* are matched, and then invoke the callbacks associated with their vespecs.
* If an OS error occurs while attempting to arm or disarm a specifier, the
* specifier is transitioned to the ERROR state; we will attempt to arm it
* again at the next continue. If no target process is under our control or
* if an event is not currently applicable (e.g. a deferred breakpoint on an
* object that is not yet loaded), it remains in the IDLE state. The target
* implementation should intercept object load events and then transition the
* specifier to the ACTIVE state when the corresponding object is loaded.
*
* To simplify the debugger implementation and allow targets to easily provide
* new types of observable events, most of the event specifier management is
* done by the target layer. Each software event specifier provides an ops
* vector of subroutines that the target layer can call to perform the
* various state transitions described above. The target maintains two lists
* of mdb_sespec_t's: the t_idle list (IDLE state) and the t_active list
* (ACTIVE, ARMED, and ERROR states). Each mdb_sespec_t maintains a list of
* associated mdb_vespec_t's. If an sespec is IDLE or ERROR, its se_errno
* field will have an errno value specifying the reason for its inactivity.
* The vespec stores the client's callback function and private data, and the
* arguments used to construct the sespec. All objects are reference counted
* so we can destroy an object when it is no longer needed. The mdb_sespec_t
* invariants for the respective states are as follows:
*
* IDLE: on t_idle list, se_data == NULL, se_errno != 0, se_ctor not called
* ACTIVE: on t_active list, se_data valid, se_errno == 0, se_ctor called
* ARMED: on t_active list, se_data valid, se_errno == 0, se_ctor called
* ERROR: on t_active list, se_data valid, se_errno != 0, se_ctor called
*
* Additional commentary on specific state transitions and issues involving
* event management can be found below near the target layer functions.
*
* References
*
* [1] John Gilmore, "Working in GDB", Technical Report, Cygnus Support,
* 1.84 edition, 1994.
*
* [2] David R. Hanson and Mukund Raghavachari, "A Machine-Independent
* Debugger", Software--Practice and Experience, 26(11), 1277-1299(1996).
*
* [3] Michael W. Shapiro, "RDB: A System for Incremental Replay Debugging",
* Technical Report CS-97-12, Department of Computer Science,
* Brown University.
*
* [4] Daniel B. Price, "New Techniques for Replay Debugging", Technical
* Report CS-98-05, Department of Computer Science, Brown University.
*/
#include <mdb/mdb_target_impl.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_callb.h>
#include <mdb/mdb_gelf.h>
#include <mdb/mdb_io_impl.h>
#include <mdb/mdb_string.h>
#include <mdb/mdb_signal.h>
#include <mdb/mdb_frame.h>
#include <strings.h>
#include <stdlib.h>
#include <errno.h>
/*
* Define convenience macros for referencing the set of vespec flag bits that
* are preserved by the target implementation, and the set of bits that
* determine automatic ve_hits == ve_limit behavior.
*/
#define T_IMPL_BITS \
#define T_AUTO_BITS \
/*
* Define convenience macro for referencing target flag pending continue bits.
*/
#define T_CONT_BITS \
{
mdb_tgt_t *t;
if (flags & ~MDB_TGT_F_ALL) {
return (NULL);
}
t->t_vepos = 1;
t->t_veneg = 1;
break;
}
}
return (NULL);
}
return (t);
}
int
{
return (t->t_flags);
}
int
{
if (flags & ~MDB_TGT_F_ALL)
}
int
{
}
/*ARGSUSED*/
static int
{
(void) mdb_tgt_vespec_delete(t, vid);
return (0);
}
void
{
t->t_ops->t_deactivate(t);
}
}
}
void
{
}
/*
* If the root was explicitly set with -R and contains %V,
* expand it like a path. If the resulting directory is
* not present, then replace %V with "latest" and re-evaluate.
*/
if (v != NULL) {
const char **p;
#ifndef _KMDB
struct stat s;
#endif
mdb_path_free(p, len);
#ifndef _KMDB
mdb_path_free(p, len);
}
#endif
}
/*
* Re-evaluate the macro and dmod paths now that we have the
* new target set and m_root figured out.
*/
}
t->t_ops->t_activate(t);
}
}
void
{
t->t_ops->t_periodic(t);
}
const char *
{
}
const char *
{
}
const char *
{
return (t->t_ops->t_platform(t));
}
int
{
}
int
{
}
int
{
}
{
if (t->t_flags & MDB_TGT_F_ASIO)
case (uintptr_t)MDB_TGT_AS_VIRT:
case (uintptr_t)MDB_TGT_AS_PHYS:
case (uintptr_t)MDB_TGT_AS_FILE:
case (uintptr_t)MDB_TGT_AS_IO:
}
}
{
if (!(t->t_flags & MDB_TGT_F_RDWR))
return (set_errno(EMDB_TGTRDONLY));
if (t->t_flags & MDB_TGT_F_ASIO)
case (uintptr_t)MDB_TGT_AS_VIRT:
case (uintptr_t)MDB_TGT_AS_PHYS:
case (uintptr_t)MDB_TGT_AS_FILE:
case (uintptr_t)MDB_TGT_AS_IO:
}
}
{
}
{
if (t->t_flags & MDB_TGT_F_RDWR)
return (set_errno(EMDB_TGTRDONLY));
}
{
}
{
if (t->t_flags & MDB_TGT_F_RDWR)
return (set_errno(EMDB_TGTRDONLY));
}
{
}
{
if (t->t_flags & MDB_TGT_F_RDWR)
return (set_errno(EMDB_TGTRDONLY));
}
{
}
{
if (t->t_flags & MDB_TGT_F_RDWR)
return (set_errno(EMDB_TGTRDONLY));
}
int
{
}
{
char *p;
if (nread >= 0) {
goto done;
}
nread = 0;
p = &buf[0];
if (*p == '\0')
return (nread);
nread++;
addr++;
p++;
}
if (nread == 0 && n == -1)
return (-1); /* If we can't even read a byte, return -1 */
done:
if (nbytes != 0)
return (nread);
}
{
}
int
{
if (obj == MDB_TGT_OBJ_EVERY &&
goto found;
}
goto found;
return (-1);
return (0);
}
int
{
if (t == NULL)
return (0);
}
return (-1);
}
/*
* The mdb_tgt_lookup_by_scope function is a convenience routine for code that
* wants to look up a scoped symbol name such as "object`symbol". It is
* implemented as a simple wrapper around mdb_tgt_lookup_by_name. Note that
* we split on the *last* occurrence of "`", so the object name itself may
* contain additional scopes whose evaluation is left to the target. This
* allows targets to implement additional scopes, such as source files,
* function names, link map identifiers, etc.
*/
int
{
const char *name = s;
if (t == NULL)
name = s;
if (*object == '\0')
return (set_errno(EMDB_NOOBJ));
if (*name == '\0')
return (set_errno(EMDB_NOSYM));
}
}
}
int
{
}
{
return (-1);
}
{
return (-1);
}
int
{
}
int
{
}
const mdb_map_t *
{
}
const mdb_map_t *
{
}
struct ctf_file *
{
}
struct ctf_file *
{
}
/*
* Return the latest target status. We just copy out our cached copy. The
* status only needs to change when the target is run, stepped, or continued.
*/
int
{
/*
* If we're called with the address of the target's internal status,
* then call down to update it; otherwise copy out the saved status.
*/
return (-1); /* errno is set for us */
/*
* Assert that our state is valid before returning it. The state must
* be valid, and DSTOP and ISTOP cannot be set simultaneously. ISTOP
* is only valid when stopped. DSTOP is only valid when running or
* stopped. If any test fails, abort the debugger.
*/
if (state > MDB_TGT_LOST)
fail("target has ISTOP and DSTOP set simultaneously\n");
return (0);
}
/*
* For the given sespec, scan its list of vespecs for ones that are marked
* temporary and delete them. We use the same method as vespec_delete below.
*/
/*ARGSUSED*/
void
{
mdb_tgt_vespec_rele(t, vep);
}
}
}
/*
* Prune each sespec on the active list of temporary vespecs. This function
* is called, for example, after the target finishes a continue operation.
*/
void
{
}
}
/*
* Transition the given sespec to the IDLE state. We invoke the destructor,
* and then move the sespec from the active list to the idle list.
*/
void
{
}
/*
* Transition each sespec on the active list to the IDLE state. This function
* is called, for example, after the target terminates execution.
*/
void
{
}
mdb_tgt_sespec_rele(t, sep);
}
}
}
/*
* Attempt to transition the given sespec from the IDLE to ACTIVE state. We
* do this by invoking se_ctor -- if this fails, we save the reason in se_errno
* and return -1 with errno set. One strange case we need to deal with here is
* the possibility that a given vespec is sitting on the idle list with its
* corresponding sespec, but it is actually a duplicate of another sespec on the
* active list. This can happen if the sespec is associated with a
* MDB_TGT_SPEC_DISABLED vespec that was just enabled, and is now ready to be
* activated. A more interesting reason this situation might arise is the case
* where a virtual address breakpoint is set at an address just mmap'ed by
* dlmopen. Since no symbol table information is available for this mapping
* yet, a pre-existing deferred symbolic breakpoint may already exist for this
* address, but it is on the idle list. When the symbol table is ready and the
* DLACTIVITY event occurs, we now discover that the virtual address obtained by
* evaluating the symbolic breakpoint matches the explicit virtual address of
* the active virtual breakpoint. To resolve this conflict in either case, we
* destroy the idle sespec, and attach its list of vespecs to the existing
* active sespec.
*/
int
{
return (0); /* cannot be activated while disabled bit set */
/*
* First search the active list for an existing, duplicate sespec to
* handle the special case described above.
*/
break;
}
}
/*
* If a duplicate is found, destroy the existing, idle sespec, and
* attach all of its vespecs to the duplicate sespec.
*/
mdb_tgt_sespec_rele(t, sep);
mdb_tgt_sespec_hold(t, dup);
}
return (0);
}
/*
* If no duplicate is found, call the sespec's constructor. If this
* is successful, move the sespec to the active list.
*/
return (-1);
}
}
return (0);
}
/*
* Transition each sespec on the idle list to the ACTIVE state. This function
* is called, for example, after the target's t_run() function returns. If
* the se_ctor() function fails, the specifier is not yet applicable; it will
* remain on the idle list and can be activated later.
*
* Returns 1 if there weren't any unexpected activation failures; 0 if there
* were.
*/
int
{
if (mdb_tgt_sespec_activate_one(t, sep) < 0 &&
rc = 0;
}
return (rc);
}
/*
* Transition the given sespec to the ARMED state. Note that we attempt to
* re-arm sespecs previously in the ERROR state. If se_arm() fails the sespec
* transitions to the ERROR state but stays on the active list.
*/
void
{
return; /* do not arm sespecs more than once */
} else {
}
}
/*
* Transition each sespec on the active list (except matched specs) to the
* ARMED state. This function is called prior to continuing the target.
*/
void
{
}
}
/*
* Transition each sespec on the active list that is in the ARMED state to
* the ACTIVE state. If se_disarm() fails, the sespec is transitioned to
* the ERROR state instead, but left on the active list.
*/
static void
{
continue; /* do not disarm if in ERROR state */
} else {
}
}
}
/*
* Determine if the software event that triggered the most recent stop matches
* any of the active event specifiers. If 'all' is TRUE, we consider all
* sespecs in our search. If 'all' is FALSE, we only consider ARMED sespecs.
* If we successfully match an event, we add it to the t_matched list and
* place an additional hold on it.
*/
static mdb_sespec_t *
{
continue; /* restrict search to ARMED sespecs */
mdb_tgt_sespec_hold(t, sep);
}
}
return (t->t_matched);
}
/*
* This function provides the low-level target continue algorithm. We proceed
* in three phases: (1) we arm the active sespecs, except the specs matched at
* the time we last stopped, (2) we call se_cont() on any matched sespecs to
* step over these event transitions, and then arm the corresponding sespecs,
* and (3) we call the appropriate low-level continue routine. Once the
* target stops again, we determine which sespecs were matched, and invoke the
* appropriate vespec callbacks and perform other vespec maintenance.
*/
static int
{
int error = 0;
uint_t n = 0; /* # of callbacks */
/*
* If the target is undead, dead, or lost, we no longer allow continue.
* This effectively forces the user to use ::kill or ::run after death.
*/
return (set_errno(EMDB_TGTZOMB));
return (set_errno(EMDB_TGTCORE));
return (set_errno(EMDB_TGTLOST));
/*
* If any of single-step, step-over, or step-out is pending, it takes
* precedence over an explicit or pending continue, because these are
* all different specialized forms of continue.
*/
if (t->t_flags & MDB_TGT_F_STEP)
else if (t->t_flags & MDB_TGT_F_NEXT)
else if (t->t_flags & MDB_TGT_F_STEP_BRANCH)
else if (t->t_flags & MDB_TGT_F_STEP_OUT)
/*
* To handle step-over, we ask the target to find the address past the
* next control transfer instruction. If an address is found, we plant
* a temporary breakpoint there and continue; otherwise just step.
*/
} else
}
/*
* To handle step-out, we ask the target to find the return address of
* the current frame, plant a temporary breakpoint there, and continue.
*/
if (t->t_flags & MDB_TGT_F_STEP_OUT) {
return (-1); /* errno is set for us */
return (-1); /* errno is set for us */
}
/*
* To handle step-branch, we ask the target to enable it for the coming
* continue. Step-branch is incompatible with step, so don't enable it
* if we're going to be stepping.
*/
return (-1); /* errno is set for us */
}
(void) mdb_signal_block(SIGHUP);
(void) mdb_signal_block(SIGTERM);
t->t_flags &= ~T_CONT_BITS;
t->t_flags |= MDB_TGT_F_BUSY;
/*
* Iterate over the matched sespec list, performing autostop processing
* and clearing the matched bit for each associated vespec. We then
* invoke each sespec's se_cont callback in order to continue past
* the corresponding event. If the matched list has more than one
* sespec, we assume that the se_cont callbacks are non-interfering.
*/
}
break;
}
break;
}
}
/*
* Clear the se_matched field for each matched sespec, and drop the
* reference count since the sespec is no longer on the matched list.
*/
mdb_tgt_sespec_rele(t, sep);
}
/*
* If the matched list was non-empty, see if we hit another event while
* performing se_cont() processing. If so, don't bother continuing any
* further. If not, arm the sespecs on the old matched list by calling
* mdb_tgt_sespec_arm_all() again and then continue by calling t_cont.
*/
goto out; /* abort now if se_cont() failed */
goto out;
}
}
}
if (t->t_flags & MDB_TGT_F_UNLOAD)
out:
(void) mdb_signal_unblock(SIGTERM);
(void) mdb_signal_unblock(SIGHUP);
/*
* When we invoke a ve_callback, it may in turn request that the
* target continue immediately after callback processing is
* complete. We only allow this to occur if *all* callbacks
* agree to continue. To implement this behavior, we keep a
* count (ncont) of such requests, and only apply the cumulative
* continue bits (cbits) to the target if ncont is equal to the
* total number of callbacks that are invoked (n).
*/
/*
* Place an extra hold on the current vespec and pick
* up the next pointer before invoking the callback: we
* must be prepared for the vespec to be deleted or
* moved to a different list by the callback.
*/
mdb_tgt_vespec_hold(t, vep);
t->t_flags &= ~T_CONT_BITS;
(void) mdb_tgt_vespec_delete(t,
(void) mdb_tgt_vespec_disable(t,
}
(void) mdb_tgt_continue(t, NULL);
}
mdb_tgt_vespec_rele(t, vep);
}
}
t->t_flags &= ~MDB_TGT_F_BUSY;
if (error != 0)
return (0);
}
/*
* This function is the common glue that connects the high-level target layer
* continue functions (e.g. step and cont below) with the low-level
* tgt_continue() function above. Since vespec callbacks may perform any
* actions, including attempting to continue the target itself, we must be
* prepared to be called while the target is still marked F_BUSY. In this
* case, we just set a pending bit and return. When we return from the call
* to tgt_continue() that made us busy into the tgt_request_continue() call
* that is still on the stack, we will loop around and call tgt_continue()
* again. This allows vespecs to continue the target without recursion.
*/
static int
{
int status;
if (t->t_flags & MDB_TGT_F_BUSY) {
return (0);
}
do {
if (status == 0) {
continue;
}
}
}
t->t_flags &= ~T_CONT_BITS;
return (status);
}
/*
* Restart target execution: we rely upon the underlying target implementation
* to do most of the work for us. In particular, we assume it will properly
* preserve the state of our event lists if the run fails for some reason,
* and that it will reset all events to the IDLE state if the run succeeds.
* If it is successful, we attempt to activate all of the idle sespecs. The
* t_run() operation is defined to leave the target stopped at the earliest
* possible point in execution, and then return control to the debugger,
* awaiting a step or continue operation to set it running again.
*/
int
{
int i;
for (i = 0; i < argc; i++) {
}
return (-1); /* errno is set for us */
t->t_flags &= ~T_CONT_BITS;
(void) mdb_tgt_sespec_activate_all(t);
return (0);
}
int
{
}
int
{
}
int
{
}
int
{
}
int
{
}
int
{
}
void *
{
(void) set_errno(EMDB_NOSESPEC);
return (NULL);
}
}
/*
* Return a structured description and comment string for the given vespec.
* We fill in the common information from the vespec, and then call down to
* the underlying sespec to provide the comment string and modify any
* event type-specific information.
*/
char *
{
(void) set_errno(EMDB_NOSESPEC);
return (NULL);
}
}
/*
* Qsort callback for sorting vespecs by VID, used below.
*/
static int
{
}
/*
* Iterate over all vespecs and call the specified callback function with the
* corresponding VID and caller data pointer. We want the callback function
* to see a consistent, sorted snapshot of the vespecs, and allow the callback
* to take actions such as deleting the vespec itself, so we cannot simply
* iterate over the lists. Instead, we pre-allocate an array of vespec
* pointers, fill it in and place an additional hold on each vespec, and then
* sort it. After the callback has been executed on each vespec in the
* sorted array, we remove our hold and free the temporary array.
*/
int
{
mdb_tgt_vespec_hold(t, vep);
}
}
mdb_tgt_vespec_hold(t, vep);
}
}
fail("target has %u vespecs on list but vecnt shows %u\n",
}
(int (*)(const void *, const void *))tgt_vespec_compare);
break;
}
mdb_tgt_vespec_rele(t, *vepp);
return (0);
}
/*
* Reset the vespec flags, match limit, and callback data to the specified
* values. We silently correct invalid parameters, except for the VID.
* The caller is required to query the existing properties and pass back
* the existing values for any properties that should not be modified.
* If the callback data is modified, the caller is responsible for cleaning
* up any state associated with the previous value.
*/
int
{
return (set_errno(EMDB_NOSESPEC));
/*
* If the value of the MDB_TGT_SPEC_DISABLED bit is changing, call the
*/
if ((flags & MDB_TGT_SPEC_DISABLED) !=
if (flags & MDB_TGT_SPEC_DISABLED)
(void) mdb_tgt_vespec_disable(t, id);
else
(void) mdb_tgt_vespec_enable(t, id);
}
/*
* Make that only one MDB_TGT_SPEC_AUTO* bit is set in the new flags
* value: extra bits are cleared according to order of precedence.
*/
if (flags & MDB_TGT_SPEC_AUTOSTOP)
else if (flags & MDB_TGT_SPEC_AUTODEL)
/*
* The TEMPORARY property always takes precedence over STICKY.
*/
if (flags & MDB_TGT_SPEC_TEMPORARY)
flags &= ~MDB_TGT_SPEC_STICKY;
/*
* If any MDB_TGT_SPEC_AUTO* bits are changing, reset the hit count
* back to zero and clear all of the old auto bits.
*/
}
/*
* If any MDB_TGT_SPEC_AUTO* flags are set, make sure the limit is at
* least one. If none are set, reset it back to zero.
*/
else
/*
* As a convenience, we allow the caller to specify SPEC_DELETED in
* the flags field as indication that the event should be deleted.
*/
if (flags & MDB_TGT_SPEC_DELETED)
(void) mdb_tgt_vespec_delete(t, id);
return (0);
}
/*
* Remove the user disabled bit from the specified vespec, and attempt to
* activate the underlying sespec and move it to the active list if possible.
*/
int
{
return (set_errno(EMDB_NOSESPEC));
return (-1); /* errno is set for us */
}
return (0);
}
/*
* Set the user disabled bit on the specified vespec, and move it to the idle
* list. If the vespec is not alone with its sespec or if it is a currently
* matched event, we must always create a new idle sespec and move the vespec
* there. If the vespec was alone and active, we can simply idle the sespec.
*/
int
{
return (set_errno(EMDB_NOSESPEC));
return (0); /* already disabled */
mdb_tgt_sespec_hold(t, sep);
return (0);
}
/*
* Delete the given vespec. We use the MDB_TGT_SPEC_DELETED flag to ensure that
* multiple calls to mdb_tgt_vespec_delete to not attempt to decrement the
* reference count on the vespec more than once. This is because the vespec
* may remain referenced if it is currently held by another routine (e.g.
* vespec_iter), and so the user could attempt to delete it more than once
* since it reference count will be >= 2 prior to the first delete call.
*/
int
{
return (set_errno(EMDB_NOSESPEC));
mdb_tgt_vespec_rele(t, vep);
return (0);
}
int
{
}
int
{
}
int
{
return (0);
}
(void) set_errno(EMDB_WPRANGE);
return (0);
}
}
int
{
return (0);
}
(void) set_errno(EMDB_WPRANGE);
return (0);
}
}
int
{
return (0);
}
(void) set_errno(EMDB_WPRANGE);
return (0);
}
}
int
{
}
int
{
}
int
{
}
int
{
}
int
{
}
int
const char *rname, mdb_tgt_reg_t r)
{
}
int
mdb_tgt_stack_f *cb, void *p)
{
}
int
{
break;
}
return (0);
}
{
}
}
long
{
return (set_errno(EMDB_TGTNOTSUP));
}
void *
{
(void) set_errno(EMDB_TGTNOTSUP);
return (NULL);
}
long
{
return (0L);
}
int
{
return (set_errno(EMDB_XDEXISTS));
}
return (0);
}
int
{
return (0);
}
}
}
int
{
#error "STT_NUM has grown. update mdb_tgt_sym_match()"
#endif
/*
* In case you haven't already guessed, this relies on the bitmask
* used by <mdb/mdb_target.h> and <libproc.h> for encoding symbol
* type and binding matching the order of STB and STT constants
* compatibility, so I think this is reasonably fair game.
*/
}
return (0); /* Unknown binding or type; fail to match */
}
void
{
GElf_Xword d = 0, t = 0;
GElf_Addr b = 0, e = 0;
uint32_t m = 0;
mdb_var_t *v;
/*
* Reset legacy adb variables based on the specified ELF object file
* provided by the target. We define these variables:
*
* b - the address of the data segment (first writeable Phdr)
* d - the size of the data segment
* e - the address of the entry point
* m - the magic number identifying the file
* t - the address of the text segment (first executable Phdr)
*/
size_t i;
}
}
}
mdb_nv_set_value(v, b);
mdb_nv_set_value(v, d);
mdb_nv_set_value(v, e);
mdb_nv_set_value(v, m);
mdb_nv_set_value(v, t);
}
/*ARGSUSED*/
void
{
}
void
{
} else
}
}
{
else
return (sep);
}
{
break;
}
return (sep);
}
{
break;
}
return (sep);
}
/*ARGSUSED*/
void
{
}
void
{
/*
* Remove this vespec from the sespec's velist and decrement
* the reference count on the sespec.
*/
/*
* If we are deleting the most recently assigned VID, reset
* t_vepos or t_veneg as appropriate to re-use that number.
* This could be enhanced to re-use any free number by
* maintaining a bitmap or hash of the allocated IDs.
*/
/*
* Call the destructor to clean up ve_args, and then free
* the actual vespec structure.
*/
t->t_vecnt--;
}
}
int
{
/*
* Make that only one MDB_TGT_SPEC_AUTO* bit is set in the new flags
* value: extra bits are cleared according to order of precedence.
*/
if (flags & MDB_TGT_SPEC_AUTOSTOP)
else if (flags & MDB_TGT_SPEC_AUTODEL)
/*
* The TEMPORARY property always takes precedence over STICKY.
*/
if (flags & MDB_TGT_SPEC_TEMPORARY)
flags &= ~MDB_TGT_SPEC_STICKY;
/*
* Find a matching sespec or create a new one on the appropriate list.
* We always create a new sespec if the vespec is created disabled.
*/
if (flags & MDB_TGT_SPEC_DISABLED)
/*
* Generate a new ID for the vespec. Increasing positive integers are
* assigned to visible vespecs; decreasing negative integers are
* assigned to hidden vespecs. The target saves our most recent choice.
*/
if (flags & MDB_TGT_SPEC_INTERNAL) {
mult = -1;
} else {
mult = 1;
}
mdb_tgt_sespec_hold(t, sep);
mdb_tgt_vespec_hold(t, vep);
t->t_vecnt++;
/*
* If this vespec is the first reference to the sespec and it's active,
* then it is newly created and we should attempt to initialize it.
* If se_ctor fails, then move the sespec back to the idle list.
*/
}
/*
* If the sespec is active and the target is currently running (because
* we grabbed it using PGRAB_NOSTOP), then go ahead and attempt to arm
* the sespec so it will take effect immediately.
*/
}
/*
* Search the target's active, idle, and disabled lists for the vespec matching
* the specified VID, and return a pointer to it, or NULL if no match is found.
*/
{
if (vid == 0)
return (NULL); /* 0 is never a valid VID */
return (vep);
}
}
return (vep);
}
}
return (NULL);
}
/*ARGSUSED*/
void
{
/* default destructor does nothing */
}
/*ARGSUSED*/
void
{
/* default callback does nothing */
}
/*ARGSUSED*/
void
{
/* default destructor does nothing */
}
/*ARGSUSED*/
int
{
}
/*ARGSUSED*/
int
{
}
/*ARGSUSED*/
int
{
return (0); /* return success */
}
/*ARGSUSED*/
int
{
return (0); /* return success */
}
/*ARGSUSED*/
int
{
return (0); /* return success */
}
int
{
int fail = 0;
fail++;
}
}
return (fail > 0 ? -1 : 0);
}
int
{
int fail = 0;
fail++;
}
}
return (fail > 0 ? -1 : 0);
}
void
{
continue; /* Don't export register as a variable */
flags |= MDB_NV_RDONLY;
}
}