elfedit.c revision d29b2c4438482eb00488be49a1f5d6835f455546
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdarg.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <dirent.h>
#include <libelf.h>
#include <gelf.h>
#include <conv.h>
#include <dlfcn.h>
#include <link.h>
#include <stdarg.h>
#include <libgen.h>
#include <libintl.h>
#include <locale.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>
#include <strings.h>
#include <sgs.h>
#include "msg.h"
#include "_elfedit.h"
#include <debug.h> /* liblddb */
/*
* Column at which elfedit_format_command_usage() will wrap the
* generated usage string if the wrap argument is True (1).
*/
#define USAGE_WRAP_COL 55
/*
* Type used to represent a string buffer that can grow as needed
* to hold strings of arbitrary length. The user should declare
* variables of this type sa static. The strbuf_ensure_size() function
* is used to ensure that it has a minimum desired size.
*/
typedef struct {
char *buf; /* String buffer */
size_t n; /* Size of buffer */
} STRBUF;
/*
* Types used by tokenize_user_cmd() to represent the result of
* spliting a user command into individual tokens.
*/
typedef struct {
char *tok_str; /* Token string */
} TOK_ELT;
typedef struct {
/* newline or NULL termination chars */
/* tokens, including terminating NULL */
} TOK_STATE;
/* State block used by gettok_init() and gettok() */
typedef struct {
const char *gtok_buf; /* Addr of buffer containing string */
char *gtok_cur_buf; /* Addr withing buffer for next token */
int gtok_inc_null_final; /* True if final NULL token used */
int gtok_null_seen; /* True when NULL byte seen */
} GETTOK_STATE;
/*
* The elfedit_cpl_*() functions are used for command line completion.
* Currently this uses the tecla library, but to allow for changing the
* library used, we hide all tecla interfaces from our modules. Instead,
* cmd_match_fcn() builds an ELFEDIT_CPL_STATE struct, and we pass the
* address of that struct as an opaque handle to the modules. Since the
* pointer is opaque, the contents of ELFEDIT_CPL_STATE are free to change
* as necessary.
*/
typedef struct {
const char *ecpl_line; /* raw input line */
int ecpl_word_start; /* start offset within line */
int ecpl_word_end; /* offset just past token */
/*
* ecpl_add_mod_colon is a secret handshake between
* elfedit_cpl_command() and elfedit_cpl_add_match(). It adds
* ':' to end of matched modules.
*/
int ecpl_add_mod_colon;
const char *ecpl_token_str; /* token being completed */
/* This structure maintains elfedit global state */
/*
* Define a pair of static global variables that contain the
* ISA strings that correspond to %i and %I tokens in module search
* paths.
*
* isa_i_str - The ISA string for the currently running program
* isa_I_str - For 64-bit programs, the same as isa_i_str. For
* 32-bit programs, an empty string.
*/
#ifdef __sparc
#ifdef __sparcv9
#else
#endif
#endif
#ifdef __i386
#endif
#ifdef __amd64
#endif
/* Forward declarations */
static void free_user_cmds(void);
static void elfedit_pager_cleanup(void);
/*
* We supply this function for the msg module
*/
const char *
{
}
/*
* Copy at most min(cpsize, dstsize-1) bytes from src into dst,
* truncating src if necessary. The result is always null-terminated.
*
* entry:
* dst - Destination buffer
* src - Source string
* dstsize - sizeof(dst)
*
* note:
* This is similar to strncpy(), but with two modifications:
* 1) You specify the number of characters to copy, not just
* the size of the destination. Hence, you can copy non-NULL
* terminated strings.
* 2) The destination is guaranteed to be NULL terminated. strncpy()
* does not terminate a completely full buffer.
*/
static void
{
if (cpsize > 0)
}
/*
* Calls exit() on behalf of elfedit.
*/
void
elfedit_exit(int status)
{
/* Exiting with unflushed changes pending? Issue debug notice */
/*
* If the edit file is marked for unlink on exit, then
* take care of it here.
*/
}
}
}
/*
* Standard message function for elfedit. All user visible
* output, for error or informational reasons, should go through
* this function.
*
* entry:
* type - Type of message. One of the ELFEDIT_MSG_* values.
* format, ... - As per the printf() family
*
* exit:
* The desired message has been output. For informational
* messages, control returns to the caller. For errors,
* this routine will terminate execution or strip the execution
* stack and return control directly to the outer control loop.
* In either case, the caller will not receive control.
*/
/*PRINTFLIKE2*/
void
{
typedef enum { /* What to do after finished */
DISP_RET = 0, /* Return to caller */
} DISP;
int do_output = 1;
int need_prefix = 1;
switch (type) {
case ELFEDIT_MSG_ERR:
case ELFEDIT_MSG_CMDUSAGE:
break;
case ELFEDIT_MSG_FATAL:
break;
case ELFEDIT_MSG_USAGE:
need_prefix = 0;
break;
case ELFEDIT_MSG_DEBUG:
return;
break;
case ELFEDIT_MSG_QUIET:
do_output = 0;
break;
}
/*
* If there is a pager process running, we are returning to the
* caller, and the output is going to stdout, then let the
* pager handle it instead of writing it directly from this process.
* That way, the output gets paged along with everything else.
*
* If there is a pager process running, and we are not returning
* to the caller, then end the pager process now, before we generate
* any new output. This allows for any text buffered in the pager
* pipe to be output before the new stuff.
*/
} else {
}
}
/*
* If this message is coming from within the libtecla command
* completion code, call gl_normal_io() to give the library notice.
* That function sets the tty back to cooked mode and advances
* the cursor to the beginning of the next line so that our output
* will appear properly. When we return to the command completion code,
* tecla will re-enter raw mode and redraw the current command line.
*/
if (do_output) {
if (need_prefix)
}
/*
* If this is an error, then we do not return to the caller.
* The action taken depends on whether the outer loop has registered
* a jump buffer for us or not.
*/
/* Free the user command list */
/* Clean up to reflect effect of non-local goto */
/* Jump to the outer loop to resume */
} else {
elfedit_exit(1);
}
}
}
/*
* Wrapper on elfedit_msg() that issues an error that results from
* a call to libelf.
*
* entry:
* file - Name of ELF object
* libelf_rtn_name - Name of routine that was called
*
* exit:
* An error has been issued that shows the routine called
* and the libelf error string for it from elf_errmsg().
* This routine does not return to the caller.
*/
void
{
}
/*
* Start an output pager process for elfedit_printf()/elfedit_write() to use.
*
* note:
* If this elfedit session is not interactive, then no pager is
* started. Paging is only intended for interactive use. The caller
* is not supposed to worry about this point, but simply to use
* this function to flag situations in which paging might be needed.
*/
void
elfedit_pager_init(void)
{
const char *errstr;
const char *cmd;
int err;
/*
* If there is no pager process running, start one.
* Only do this for interactive sessions --- elfedit_pager()
* won't use a pager in batch mode.
*/
/*
* If the user has the PAGER environment variable set,
* then we will use that program. Otherwise we default
*/
/*
* The popen() manpage says that on failure, it "may set errno",
* which is somewhat ambiguous. We explicitly zero it here, and
* assume that any change is due to popen() failing.
*/
errno = 0;
}
}
}
/*
* If there is a pager process present, close it out.
*
* note:
* This function is called from within elfedit_msg(), and as
* such, must not use elfedit_msg() to report errors. Furthermore,
* any such errors are not a sufficient reason to terminate the process
* or to longjmp(). This is a rare case where errors are written
* directly to stderr.
*/
static void
elfedit_pager_cleanup(void)
{
}
}
/*
* Print general formtted text for the user, using printf()-style
* formatting. Uses the pager process if one has been started, or
* stdout otherwise.
*/
void
elfedit_printf(const char *format, ...)
{
int err;
int pager;
int broken_pipe = 0;
/*
* If there is a pager process, then use it. Otherwise write
* directly to stdout.
*/
errno = 0;
/* Did we fail because a child pager process has exited? */
/*
* On error, we simply issue the error without cleaning up
* the pager process. The message code handles that as a standard
* part of error processing.
*
* We handle failure due to an exited pager process differently
* than a normal error, because it is usually due to the user
* intentionally telling it to.
*/
if (err < 0) {
if (broken_pipe)
else
}
}
/*
* Some our modules use liblddb routines to format ELF output.
* In order to ensure that such output is sent to the pager pipe
* when there is one, and stdout otherwise, we redefine the dbg_print()
* function here.
*
* This item should be defined NODIRECT.
*/
/* PRINTFLIKE2 */
void
{
int err;
int pager;
int broken_pipe = 0;
#if defined(lint)
/*
* The lml argument is only meaningful for diagnostics sent to ld.so.1.
* Supress the lint error by making a dummy assignment.
*/
lml = 0;
#endif
/*
* If there is a pager process, then use it. Otherwise write
* directly to stdout.
*/
errno = 0;
if (err >= 0)
/* Did we fail because a child pager process has exited? */
/*
* On error, we simply issue the error without cleaning up
* the pager process. The message code handles that as a standard
* part of error processing.
*
* We handle failure due to an exited pager process differently
* than a normal error, because it is usually due to the user
* intentionally telling it to.
*/
if (err < 0) {
if (broken_pipe)
else
}
}
/*
* Write raw bytes of text in a manner similar to fwrite().
* Uses the pager process if one has been started, or
* stdout otherwise.
*/
void
{
int err;
/*
* If there is a pager process, then use it. Otherwise write
* directly to stdout.
*/
}
}
/*
* Wrappers on malloc() and realloc() that check the result for success
* and issue an error if not. The caller can use the result of these
* functions without checking for a NULL pointer, as we do not return to
* the caller in the failure case.
*/
void *
{
void *m;
if (m == NULL) {
}
return (m);
}
void *
{
void *m;
if (m == NULL) {
}
return (m);
}
/*
* Ensure that the given buffer has room for n bytes of data.
*/
static void
{
#define INITIAL_STR_ALLOC 128
size_t n;
while (size > n) /* Double buffer until string fits */
n *= 2;
if (n != str->n) { /* Alloc new string buffer if needed */
str->n = n;
}
}
/*
* by optarg, and advance the pointer to the next item.
*
* entry:
* optarg - Address of pointer to argument or option array
* item - Struct to be filled in.
*
* exit:
* The item block has been filled in with the information for
* the next item in the optarg array. *optarg has been advanced
* to the next item.
*/
void
{
/*
* than the corresponding ELFEDIT_STDOA_ value.
*/
static const elfedit_optarg_item_t stdoa[] = {
/* ELFEDIT_STDOA_O */
/* MSG_INTL(MSG_STDOA_OPTDESC_O) */
/* ELFEDIT_STDOA_AND */
/* MSG_INTL(MSG_STDOA_OPTDESC_AND) */
/* ELFEDIT_STDOA_CMP */
/* MSG_INTL(MSG_STDOA_OPTDESC_CMP) */
/* ELFEDIT_STDOA_OR */
/* MSG_INTL(MSG_STDOA_OPTDESC_OR) */
};
/* Grab first item, advance the callers pointer over it */
/* Values are pre-chewed in the stdoa array above */
/*
* Set the inherited flag so that elfedit_optarg_helpstr()
* can tell who is responsible for translating the help string.
*/
} else { /* Non-inherited item */
/* Advance users pointer past value element */
(*optarg)++;
} else {
}
}
/*
* The module determines the idmask and excmask fields whether
* or not inheritance is in play.
*/
}
/*
* by elfedit_next_optarg(). This routine handles the details of
* knowing whether the string is provided by elfedit itself (inherited),
* or needs to be translated by the module.
*/
const char *
{
/*
* The help string from an inherited item comes right out
* of the main elfedit string table.
*/
/*
* If the string is defined by the module, then we need to
* have the module translate it for us.
*/
}
/*
* Used by usage_optarg() to insert a character into the output buffer,
* advancing the buffer pointer and current column, and reducing the
* amount of remaining space.
*/
static void
{
**cur = '\0';
(*n)--;
(*cur_col)++;
}
/*
* Used by usage_optarg() to insert a string into the output
* buffer, advancing the buffer pointer and current column, and reducing
* the amount of remaining space.
*/
static void
const char *format, ...)
{
*n -= len;
}
/*
* Used by usage_optarg() to insert an optarg item string into the output
* buffer, advancing the buffer pointer and current column, and reducing
* the amount of remaining space.
*/
static void
{
} else {
}
*n -= len;
}
/*
*
* entry:
* main_buf_n - Size of main buffer from which buf and buf_n are
* allocated.
* buf - Address of pointer to where next item is to be placed.
* buf_n - Address of count of remaining bytes in buffer
* buf_cur_col - Address of current output column for current line
* of generated string.
* optarg - Options list
* isopt - True if these are options, false for arguments.
* wrap_str - String to indent wrapped lines. If NULL, lines
* are not wrapped
*/
static void
{
/*
* An option can be combined into a simple format if it lacks
* these flags and is only one character in length.
*/
static const elfedit_cmd_oa_flag_t exflags =
/*
* A static buffer, which is grown as needed to accomodate
* the maximum usage string seen.
*/
static STRBUF simple_str;
int len;
int use_simple = 0;
int use_bkt;
/*
* If processing options, pull the 1-character ones that don't have
* an associated value and don't have any mutual exclusion issues into
* a single combination string to go at the beginning of the usage.
*/
if (isopt) {
char *s;
/*
* The simple string is guaranteed to fit in the same
* amount of space reserved for the main buffer.
*/
s = simple_str.buf;
*s++ = ' ';
*s++ = '[';
*s++ = '-';
(item.oai_excmask == 0)) {
}
}
/*
* If we found more than one, then finish the string and
* add it. Don't do this for a single option, because
* it looks better in that case if the option shows up
* in alphabetical order rather than being hoisted.
*/
if (use_simple) {
*s++ = ']';
*s++ = '\0';
} else {
/* Not using it, so reset the cumulative options mask */
optmask = 0;
}
}
if (isopt) {
/*
* If this is an option that was pulled into the
* combination string above, then skip over it.
*/
(item.oai_excmask == 0))
continue;
/*
* If this is a mutual exclusion option that was
* picked up out of order by a previous iteration
* of this loop, then skip over it.
*/
continue;
/* Add this item to the accumulating options mask */
}
/* Wrap line, or insert blank separator */
wrap_str);
n -= len;
} else {
}
if (use_bkt)
/* Add the item to the buffer */
/*
* If this item has a non-zero mutual exclusion mask,
* then look for the other items and display them all
* together with alternation (|). Note that plain arguments
* cannot have a non-0 exclusion mask, so this is
* effectively options-only (isopt != 0).
*/
if (item.oai_excmask != 0) {
/*
* When showing alternation, elipses for multiple
* copies need to appear inside the [] brackets.
*/
0)
continue;
/*
* Add it to the mask of seen options.
* This will keep us from showing it twice.
*/
}
}
if (use_bkt)
/*
* If alternation was not shown above (non-zero exclusion mask)
* then the elipses for multiple copies are shown outside
* any [] brackets.
*/
if ((item.oai_excmask == 0) &&
}
*buf_n = n;
*buf_cur_col = cur_col;
}
/*
* Format the usage string for a command into a static buffer and
* return the pointer to the user. The resultant string is valid
* until the next call to this routine, and which point it
* will be overwritten or the memory is freed.
*
* entry:
* mod, cmd - Module and command definitions for command to be described
* wrap_str - NULL, or string to be used to indent when
* lines are wrapped. If NULL, no wrapping is done, and
* all output is on a single line.
* cur_col - Starting column at which the string will be displayed.
* Ignored if wrap_str is NULL.
*/
const char *
{
/*
* A static buffer, which is grown as needed to accomodate
* the maximum usage string seen.
*/
char *cur;
/*
* Estimate a worst case size for the usage string:
* - module name
* - lengths of the strings
* - every option or argument is enclosed in brackets
* - space in between each item, with an alternation (" | ")
* - elipses will be displayed with each option and argument
*/
}
}
n++; /* Null termination */
/*
* If wrapping lines, we insert a newline and then wrap_str
* every USAGE_WRAP_COL characters.
*/
n += ((n + USAGE_WRAP_COL) / USAGE_WRAP_COL) *
strbuf_ensure_size(&str, n);
/* Command name */
n = str.n;
else
n -= len;
1, wrap_str);
0, wrap_str);
}
/*
* Wrapper on elfedit_msg() that issues an ELFEDIT_MSG_USAGE
* error giving usage information for the command currently
* referenced by state.cur_cmd.
*/
void
elfedit_command_usage(void)
{
}
/*
* This function allows the loadable modules to get the command line
* flags.
*/
elfedit_flags(void)
{
}
/*
* This function is used to register a per-command invocation output style
* that will momentarily override the global output style for the duration
* of the current command. This function must only be called by an
* active command.
*
* entry:
* str - One of the valid strings for the output style
*/
void
elfedit_set_cmd_outstyle(const char *str)
{
}
}
/*
* This function allows the loadable modules to get the output style.
*/
elfedit_outstyle(void)
{
/*
* If there is an active per-command output style,
* return it.
*/
}
/*
* Return the command descriptor of the currently executing command.
* For use only by the modules or code called by the modules.
*/
elfedit_curcmd(void)
{
}
/*
* Build a dynamically allocated elfedit_obj_state_t struct that
* contains a cache of the ELF file contents. This pre-chewed form
* is fed to each command, reducing the amount of ELF boilerplate
* code each command needs to contain.
*
* entry:
* file - Name of file to process
*
* exit:
* Fills state.elf with the necessary information for the open file.
*
* note: The resulting elfedit_obj_state_t is allocated from a single
* piece of memory, such that a single call to free() suffices
* to release it as well as any memory it references.
*/
static void
init_obj_state(const char *file)
{
int fd;
int open_flag;
/*
* In readonly mode, we open the file readonly so that it is
* impossible to modify the file by accident. This also allows
* us to access readonly files, perhaps in a case where we don't
* intend to change it.
*
* We always use ELF_C_RDWR with elf_begin(), even in a readonly
* session. This allows us to modify the in-memory image, which
* can be useful when examining a file, even though we don't intend
* to modify the on-disk data. The file is not writable in
* this case, and we don't call elf_update(), so it is safe to do so.
*/
}
(void) elf_version(EV_CURRENT);
/*NOTREACHED*/
}
/* We only handle standalone ELF files */
case ELF_K_AR:
break;
case ELF_K_ELF:
break;
default:
file);
break;
}
/*
* Tell libelf that we take responsibility for object layout.
* Otherwise, it will compute "proper" values for layout and
* alignment fields, and these values can overwrite the values
* set in the elfedit session. We are modifying existing
* objects --- the layout concerns have already been dealt
* with when the object was built.
*/
/* Fill in state.elf.obj_state */
case ELFCLASS32:
break;
case ELFCLASS64:
break;
default:
file);
break;
}
}
#if 0
/*
* Debug routine. Dump the module list to stdout.
*/
static void
dbg_module_list(char *title)
{
MODLIST_T *m;
}
printf("<END OF MODULE LIST>\n");
}
#endif
/*
* Search the module list for the named module.
*
* entry:
* name - Name of module to find
* insdef - Address of variable to receive address of predecessor
* node to the desired one.
*
* exit:
* If the module is it is found, this routine returns the pointer to
* its MODLIST_T structure. *insdef references the predecessor node, or
* is NULL if the found item is at the head of the list.
*
* If the module is not found, NULL is returned. *insdef references
* the predecessor node of the position where an entry for this module
* would be placed, or NULL if it would go at the beginning.
*/
static MODLIST_T *
{
int cmp;
if (cmp == 0) { /* Desired module is first in list */
return (moddef);
cmp = -1;
name);
if (cmp == 0)
return (moddef);
if (cmp < 0) {
}
}
}
}
return (NULL);
}
/*
* Determine if a file is a sharable object based on its file path.
* If path ends in a .so, followed optionally by a period and 1 or more
* digits, we say that it is and return a pointer to the first character
* of the suffix. Otherwise NULL is returned.
*/
static const char *
path_is_so(const char *path)
{
int dotso_len;
const char *tail;
if (len == 0)
return (NULL);
tail--;
return (NULL);
}
return (NULL);
return (tail);
return (NULL);
}
/*
* Locate the start of the unsuffixed file name within path. Returns pointer
* to first character of that name in path.
*
* entry:
* path - Path to be examined.
* tail - NULL, or pointer to position at tail of path from which
* the search for '/' characters should start. If NULL,
* strlen() is used to locate the end of the string.
* buf - NULL, or buffer to receive a copy of the characters that
* lie between the start of the filename and tail.
* bufsize - sizeof(buf)
*
* exit:
* The pointer to the first character of the unsuffixed file name
* within path is returned. If buf is non-NULL, the characters
* lying between that point and tail (or the end of path if tail
* is NULL) are copied into buf.
*/
static const char *
{
const char *s;
s = tail;
s--;
return (s);
}
/*
* Issue an error on behalf of load_module(), taking care to release
* resources that routine may have aquired:
*
* entry:
* moddef - NULL, or a module definition to be released via free()
* dl_hdl - NULL, or a handle to a sharable object to release via
* dlclose().
* dl_path - If dl_hdl is non-NULL, the path to the sharable object
* file that was loaded.
* format - A format string to pass to elfedit_msg(), containing
* no more than (3) %s format codes, and no other format codes.
* [s1-s4] - Strings to pass to elfedit_msg() to satisfy the four
* allowed %s codes in format. Should be set to NULL if the
* format string does not need them.
*
* note:
* This routine makes a copy of the s1-s4 strings before freeing any
* memory or unmapping the sharable library. It is therefore safe to
* use strings from moddef, or from the sharable library (which will
* be unmapped) to satisfy the other arguments s1-s4.
*/
static void
const char *s4)
{
char s1_buf[SCRBUFSIZE];
char s2_buf[SCRBUFSIZE];
char s3_buf[SCRBUFSIZE];
char s4_buf[SCRBUFSIZE];
/*
* The caller may provide strings for s1-s3 that are from
* moddef. If we free moddef, the printf() will die on access
* to free memory. We could push back on the user and force
* each call to carefully make copies of such data. However, this
* is an easy case to miss. Furthermore, this is an error case,
* and machine efficiency is not the main issue. We therefore make
* copies of the s1-s3 strings here into auto variables, and then
* use those copies. The user is freed from worrying about it.
*
* We use oversized stack based buffers instead of malloc() to
* reduce the number of ways that things can go wrong while
* reporting the error.
*/
}
/*
* Load a module sharable object for load_module().
*
* entry:
* path - Path of file to open
* moddef - If this function issues a non-returning error, it will
* first return the memory referenced by moddef. This argument
* is not used otherwise.
* must_exist - If True, we consider it to be an error if the file given
* by path does not exist. If False, no error is issued
* and a NULL value is quietly returned.
*
* exit:
* Returns a handle to the loaded object on success, or NULL if no
* file was loaded.
*/
static void *
{
int fd;
void *hdl;
/*
* If the file is not required to exist, and it doesn't, then
* we want to quietly return without an error.
*/
if (!must_exist) {
if (fd >= 0) {
return (NULL);
}
}
return (hdl);
}
/*
* Sanity check option arguments to prevent common errors. The rest of
* elfedit assumes these tests have been done, and does not check
* again.
*/
static void
{
/*
* If ELFEDIT_CMDOA_F_INHERIT is set:
* - oa_name must be a value in the range of
* known ELFEDIT_STDOA_ values.
* - oa_help must be NULL
* - ELFEDIT_CMDOA_F_INHERIT must be the only flag set
*/
/*
* Can't use FAIL --- oa_name is not a valid
* string, and load_module_err() looks at args.
*/
continue;
}
if (isopt) {
/*
* Option name must start with a '-', and must
* have at one following character.
*/
/* MSG_INTL(MSG_ERR_OPT_MODPRE) */
}
/* MSG_INTL(MSG_ERR_OPT_MODLEN) */
}
/*
* oa_idmask must be 0, or it must have a single
* bit set (a power of 2).oa_excmask must be 0
* if oa_idmask is 0
*/
if (optarg->oa_excmask != 0) {
/* MSG_INTL(MSG_ERR_OPT_EXCMASKN0) */
}
} else {
/* MSG_INTL(MSG_ERR_OPT_IDMASKPOW2) */
}
/* Non-zero idmask must be unique */
/* MSG_INTL(MSG_ERR_OPT_IDMASKUNIQ) */
}
/* Add this one to the overall mask */
}
} else {
/*
* Argument name cannot start with a'-', and must
* not be a null string.
*/
/* MSG_INTL(MSG_ERR_ARG_MODPRE) */
}
/* MSG_INTL(MSG_ERR_ARG_MODLEN) */
}
/* oa_idmask and oa_excmask must both be 0 */
(optarg->oa_excmask != 0)) {
/* MSG_INTL(MSG_ERR_ARG_MASKNOT0) */
}
}
/*
* If it takes a value, make sure that we are
* processing options, because CMDOA_F_VALUE is not
* allowed for plain arguments. Then check the following
* item in the list:
* - There must be a following item.
* - oa_name must be non-NULL. This is the only field
* that is used by elfedit.
* - oa_help, oa_flags, oa_idmask, and oa_excmask
* must be 0.
*/
if (!isopt) {
/* MSG_INTL(MSG_ERR_ARG_CMDOA_VAL) */
}
/* MSG_INTL(MSG_ERR_BADMODOPTVAL) */
}
/* MSG_INTL(MSG_ERR_CMDOA_VALNAM) */
}
/* MSG_INTL(MSG_ERR_CMDOA_VALNOT0) */
}
optarg++;
}
}
return;
fail:
}
/*
* Look up the specified module, loading the module if necessary,
* and return its definition, or NULL on failure.
*
* entry:
* name - Name of module to load. If name contains a '/' character or has
* a ".so" suffix, then it is taken to be an absolute file path,
* and is used directly as is. If name does not contain a '/'
* character, then we look for it against the locations in
* the module path, addint the '.so' suffix, and taking the first
* one we find.
* must_exist - If True, we consider it to be an error if we are unable
* to locate a file to load and the module does not already exist.
* If False, NULL is returned quietly in this case.
* allow_abs - True if absolute paths are allowed. False to disallow
* them.
*
* note:
* If the path is absolute, then we load the file and take the module
* name from the data returned by its elfedit_init() function. If a
* module of that name is already loaded, it is unloaded and replaced
* with the new one.
*
* If the path is non absolute, then we check to see if the module has
* already been loaded, and if so, we return that module definition.
* In this case, nothing new is loaded. If the module has not been loaded,
* we search the path for it and load it. If the module name provided
* by the elfedit_init() function does not match the name of the file,
* an error results.
*/
{
const char *path;
void *hdl;
size_t i;
int is_abs_path;
/*
* If the name includes a .so suffix, or has any '/' characters,
* then it is an absolute path that we use as is to load the named
* file. Otherwise, we iterate over the path, adding the .so suffix
* and load the first file that matches.
*/
if (is_abs_path && !allow_abs)
/*
* If this is a non-absolute path, search for the module already
* having been loaded, and return it if so.
*/
if (!is_abs_path) {
/*
* As a result of module_loaded(), insdef now contains the
* immediate predecessor node for the new one, or NULL if
* it goes at the front. In the absolute-path case, we take
* care of this below, after the sharable object is loaded.
*/
}
/*
* malloc() a module definition block before trying to dlopen().
* Doing things in the other order can cause the dlopen()'d object
* to leak: If elfedit_malloc() fails, it can cause a jump to the
* outer command loop without returning to the caller. Hence,
* there will be no opportunity to clean up. Allocaing the module
* first allows us to free it if necessary.
*/
if (is_abs_path) {
} else {
}
}
return (NULL);
}
init_func = (elfedit_init_func_t *)
} else {
init_func = (elfedit_init_func_t *)
}
/*
* Note that the init function will be passing us an
* elfedit[32|64]_module_t pointer, which we cast to the
* generic module pointer type in order to be able to manage
* either type with one set of code.
*/
/*
* Enforce some rules, to help module developers:
* - The primary name of a command must not be
* the empty string ("").
* - Options must start with a '-' followed by at least
* one character.
* - Arguments and options must be well formed.
*/
}
/*
* Check the name the module provides. How we handle this depends
* on whether the path is absolute or the result of a path search.
*/
if (is_abs_path) {
/*
* Be sure we don't unload builtin modules!
* These have a NULL dl_hdl field.
*/
/* Unload existing */
PATH_MAX + 1);
return (old_moddef->ml_mod);
}
/*
* insdef now contains the insertion point for the absolute
* path case.
*/
} else {
/* If the names don't match, then error */
}
/*
* Link module into the module list. If insdef is NULL,
* it goes at the head. If insdef is non-NULL, it goes immediately
* after
*/
} else {
}
}
/*
* Unload the specified module
*/
void
elfedit_unload_module(const char *name)
{
return;
/* Built in modules cannot be unloaded. They have a NULL dl_hdl field */
/*
* When we unload it, the name string goes with it. So
* announce it while we still can without having to make a copy.
*/
/*
* Close it before going further. On failure, we'll jump, and the
* record will remain in the module list. On success,
* we'll retain control, and can safely remove it.
*/
/* Unlink the record from the module list */
else
/* Release the memory */
}
/*
* Load all sharable objects found in the specified directory.
*
* entry:
* dirpath - Path of directory to process.
* must_exist - If True, it is an error if diropen() fails to open
* the given directory. Of False, we quietly ignore it and return.
* abs_path - If True, files are loaded using their literal paths.
* If False, their module name is extracted from the dirpath
* and a path based search is used to locate it.
*/
void
{
const char *tail;
return;
/*NOTREACHED*/
}
if (abs_path) {
} else {
}
}
}
}
/*
* Follow the module load path, and load the first module found for each
* given name.
*/
void
elfedit_load_modpath(void)
{
size_t i;
}
/*
* Given a module definition, look for the specified command.
* Returns the command if found, and NULL otherwise.
*/
static elfeditGC_cmd_t *
{
const char **cmd_name;
return (cmd);
}
return (NULL);
}
/*
* Given a command name, return its command definition.
*
* entry:
* name - Command to be looked up
* must_exist - If True, we consider it to be an error if the command
* does not exist. If False, NULL is returned quietly in
* this case.
* mod_ret - NULL, or address of a variable to receive the
* module definition block of the module containing
* the command.
*
* exit:
* On success, returns a pointer to the command definition, and
* if mod_ret is non-NULL, *mod_ret receives a pointer to the
* module definition. On failure, must_exist determines the
* action taken: If must_exist is True, an error is issued and
* control does not return to the caller. If must_exist is False,
* NULL is quietly returned.
*
* note:
* A ':' in name is used to delimit the module and command names.
* If it is omitted, or if it is the first non-whitespace character
* in the name, then the built in sys: module is implied.
*/
{
const char *mod_str;
const char *cmd_str;
size_t n;
cmd_str++; /* Skip the colon */
} else { /* Have both module and command */
if (n >= sizeof (mod_buf)) {
if (must_exist)
return (NULL);
}
cmd_str++;
}
return (NULL);
/* Locate the command */
if (must_exist) {
/*
* Catch empty command in order to provide
* a better error message.
*/
if (*cmd_str == '\0') {
} else {
}
}
} else {
}
return (cmd);
}
/*
* Release all user command blocks found on state.ucmd
*/
static void
free_user_cmds(void)
{
}
}
/*
* Process all user command blocks found on state.ucmd, and then
* remove them from the list.
*/
static void
{
if (ucmd) {
/* Do them, in order */
/*
* The cmd_func field is the generic definition.
* We need to cast it to the type that matches
* the proper ELFCLASS before calling it.
*/
} else {
}
/* If a pager was started, wrap it up */
switch (cmd_ret) {
case ELFEDIT_CMDRET_MOD:
/*
* Command modified the output ELF image,
* mark the file as needing a flush to disk.
*/
break;
case ELFEDIT_CMDRET_FLUSH:
/*
* Command flushed the output file,
* clear the dirty bit.
*/
}
}
}
}
/*
* Prepare a GETTOK_STATE struct for gettok().
*
* entry:
* gettok_state - gettok state block to use
* str - Writable buffer to tokenize. Note that gettok()
* is allowed to change the contents of this buffer.
* inc_null_final - If the line ends in whitespace instead of
* immediately hitting a NULL, and inc_null_final is TRUE,
* then a null final token is generated. Otherwise trailing
* whitespace is ignored.
*/
static void
{
gettok_state->gtok_null_seen = 0;
}
/*
* Locate the next token from the buffer.
*
* entry:
* gettok_state - State of gettok() operation. Initialized
* by gettok_init(), and passed to gettok().
*
* exit:
* If a token is found, gettok_state->gtok_last_token is filled in
* with the details and True (1) is returned. If no token is found,
* False (1) is returned, and the contents of
* gettok_state->gtok_last_token are undefined.
*
* note:
* - The token returned references the memory in gettok_state->gtok_buf.
* The caller should not modify the buffer until all such
* pointers have been discarded.
* - This routine will modify the contents of gettok_state->gtok_buf
* as necessary to remove quotes and eliminate escape
* (\)characters.
*/
static int
{
char *look;
int quote_ch = '\0';
/* Skip leading whitespace */
str++;
if (*str == '\0') {
/*
* If user requested it, and there was whitespace at the
* end, then generate one last null token.
*/
if (gettok_state->gtok_inc_null_final &&
return (1);
}
return (0);
}
/*
* Read token: The standard delimiter is whitespace, but
* we honor either single or double quotes. Also, we honor
* backslash escapes.
*/
quote_ch = '\0';
continue;
}
continue;
}
break;
}
if (*look == '\\') {
look++;
break;
}
str++;
}
if (!gettok_state->gtok_null_seen)
look++;
*str = '\0';
#if 0
printf("GETTOK >%s< len(%d) offset(%d)\n",
#endif
return (1);
}
/*
* Tokenize the user command string, and return a pointer to the
* TOK_STATE buffer maintained by this function. That buffer contains
* the tokenized strings.
*
* entry:
* user_cmd_str - String to tokenize
* len - # of characters in user_cmd_str to examine. If
* (len < 0), then the complete string is processed
* stopping with the NULL termination. Otherwise,
* processing stops after len characters, and any
* remaining characters are ignored.
* inc_null_final - If True, and if user_cmd_str has whitespace
* at the end following the last non-null token, then
* a final null token will be included. If False, null
* tokens are ignored.
*
* note:
* This routine returns pointers to internally allocated memory.
* The caller must not alter anything contained in the TOK_STATE
* buffer returned. Furthermore, the the contents of TOK_STATE
* are only valid until the next call to tokenize_user_cmd().
*/
static TOK_STATE *
{
#define INITIAL_TOK_ALLOC 5
/*
* As we parse the user command, we need temporary space to
* hold the tokens. We do this by dynamically allocating a string
* buffer and a token array, and doubling them as necessary. This
* is a single threaded application, so static variables suffice.
*/
size_t n;
/*
* Make a copy we can modify. If (len == 0), take the entire
* string. Otherwise limit it to the specified length.
*/
/* Trim off any newline character that might be present */
}
/* Tokenize the user command string into tok struct */
/* If we need more room, expand the token buffer */
n = (tokst.tokst_bufsize == 0) ?
tokst.tokst_bufsize = n;
}
}
/* fold the command token to lowercase */
char *s;
if (isupper(*s))
*s = tolower(*s);
}
return (&tokst);
}
/*
* Parse the user command string, and put an entry for it at the end
* of state.ucmd.
*/
static void
parse_user_cmd(const char *user_cmd_str)
{
char *s;
size_t n;
/*
* Break it into tokens. If there are none, then it is
* an empty command and is ignored.
*/
return;
/* Find the command. Won't return on error */
/*
* If there is no ELF file being edited, then only commands
* from the sys: module are allowed.
*/
/* Allocate, fill in, and insert a USER_CMD_T block */
n = S_DROUND(sizeof (USER_CMD_T));
/*LINTED E_BAD_PTR_CAST_ALIGN*/
ucmd->ucmd_ostyle_set = 0;
s += len;
}
} else {
}
}
/*
* Copy infile to a new file with the name given by outfile.
*/
static void
{
int statloc;
switch (pid) {
case -1: /* Unable to create process */
{
}
/*NOTREACHED*/
return;
case 0:
/*
* exec() only returns on error. This is the child process,
* so we want to stay away from the usual error mechanism
* and handle things directly.
*/
{
}
exit(1);
/*NOTREACHED*/
}
/* This is the parent: Wait for the child to terminate */
}
/*
* If the child failed, then terminate the process. There is no
* need for an error message, because the child will have taken
* care of that.
*/
exit(1);
/* Make sure the copy allows user write access */
}
/* Only keep permission bits, and add user write */
}
}
}
/*
* Given a module path string, determine how long the resulting path will
* be when all % tokens have been expanded.
*
* entry:
* path - Path for which expanded length is desired
* origin_root - Root of $ORIGIN tree containing running elfedit program
*
* exit:
* Returns the value strlen() will give for the expanded path.
*/
static size_t
{
const char *s;
s = path;
len = 0;
for (s = path; *s != '\0'; s++) {
if (*s == '%') {
s++;
switch (*s) {
case 'i': /* ISA of running elfedit */
break;
case 'I': /* "" for 32-bit, same as %i for 64 */
break;
case 'o': /* Insert default path */
len +=
break;
case 'r': /* root of tree with running elfedit */
break;
case '%': /* %% is reduced to just '%' */
len++;
break;
default: /* All other % codes are reserved */
MSG_INTL(MSG_ERR_BADPATHCODE), *s);
/*NOTREACHED*/
break;
}
} else { /* Non-% character passes straight through */
len++;
}
}
return (len);
}
/*
* Given a module path string, and a buffer large enough to hold the results,
* fill the buffer with the expanded path.
*
* entry:
* path - Path for which expanded length is desired
* origin_root - Root of tree containing running elfedit program
* buf - Buffer to receive the result. buf must as large or larger
* than the value given by modpath_strlen().
*
* exit:
* Returns pointer to location following the last character
* written to buf. A NULL byte is written to that address.
*/
static char *
{
const char *cp_str;
if (*path == '%') {
path++;
switch (*path) {
case 'i': /* ISA of running elfedit */
break;
case 'I': /* "" for 32-bit, same as %i for 64 */
break;
case 'o': /* Insert default path */
origin_root, buf);
break;
case 'r':
break;
case '%': /* %% is reduced to just '%' */
break;
default: /* All other % codes are reserved */
/*NOTREACHED*/
break;
}
}
} else { /* Non-% character passes straight through */
}
}
*buf = '\0';
return (buf);
}
/*
* Establish the module search path: state.modpath
*
* The path used comes from the following sources, taking the first
* one that has a value, and ignoring any others:
*
* - ELFEDIT_PATH environment variable
* - -L command line argument
* - Default value
*
* entry:
* path - NULL, or the value of the -L command line argument
*
* exit:
* state.modpath has been filled in
*/
static void
establish_modpath(const char *cmdline_path)
{
const char *path; /* Initial path */
char *expath; /* Expanded path */
path = cmdline_path;
/*
* Root of tree containing running for running program. 32-bit elfedit
* in an ISA-specific subdirectory. So, we find the root by
* getting the $ORGIN of the current running program, and trimming
* off the last 2 (32-bit) or 3 (64-bit) directories.
*
* On a standard system, this will simply yield '/'. However,
* doing it this way allows us to run elfedit from a proto area,
* and pick up modules from the same proto area instead of those
* installed on the system.
*/
len--;
src--;
}
*src = '\0';
/*
* Calculate space needed to hold expanded path. Note that
* this assumes that MSG_STR_MODPATH will never contain a '%o'
* code, and so, the expansion is not recursive. The codes allowed
* are:
* %i - ISA of running elfedit (sparc, sparcv9, etc)
* %I - 64-bit ISA: Same as %i for 64-bit versions of elfedit,
* but yields empty string for 32-bit ISAs.
* %o - The original (default) path.
* %r - Root of tree holding elfedit program.
* %% - A single %
*
* A % followed by anything else is an error. This allows us to
* add new codes in the future without backward compatability issues.
*/
/*
* Count path segments, eliminate extra '/', and replace ':'
* with NULL.
*/
if (*src == '/') {
switch (*(src + 1)) {
case '/':
case ':':
case '\0':
continue;
}
}
if (*src == ':') {
*dst = '\0';
}
dst++;
}
*dst = '\0';
if (*src == '\0') {
src++;
} else {
}
}
}
/*
* When interactive (reading commands from a tty), we catch
* SIGINT in order to restart the outer command loop.
*/
/*ARGSUSED*/
static void
{
/* Jump to the outer loop to resume */
}
}
static void
{
if (full) {
}
elfedit_exit(2);
}
/*
* In order to complete commands, we need to know about them,
* which means that we need to force all the modules to be
* loaded. This is a relatively expensive operation, so we use
* this function, which avoids doing it more than once in a session.
*/
static void
elfedit_cpl_load_modules(void)
{
static int loaded;
if (!loaded) {
}
}
/*
* Compare the token to the given string, and if they share a common
* initial sequence, add the tail of string to the tecla command completion
* buffer:
*
* entry:
* cpldata - Current completion state
* str - String to match against token
* casefold - True to allow case insensitive completion, False
* if case must match exactly.
*/
void
{
const char *cont_suffix;
const char *type_suffix;
/*
* Reasons to return immediately:
* - NULL strings have no completion value
* - The string is shorter than the existing item being completed
*/
((cstate->ecpl_token_len != 0) &&
return;
/* If the string does not share the existing prefix, don't use it */
if (casefold) {
cstate->ecpl_token_len) != 0)
return;
} else {
cstate->ecpl_token_len) != 0)
return;
}
if (cstate->ecpl_add_mod_colon) {
} else {
type_suffix = NULL;
}
}
/*
* Compare the token to the names of the commands from the given module,
* and if they share a common initial sequence, add the tail of string
* to the tecla command completion buffer:
*
* entry:
* tok_buf - Token user has entered
* tok_len - strlen(tok_buf)
* mod - Module definition from which commands should be matched
* cpl, line, word_start, word_end, cont_suffix - As documented
* for gl_get_line() and cpl_add_completion.
*/
static void
{
const char **cmd_name;
}
/*
* Compare the token to the known module names, and add those that
* match to the list of alternatives via elfedit_cpl_match().
*
* entry:
* load_all_modules - If True, causes all modules to be loaded
* before processing is done. If False, only the modules
* currently seen will be used.
*/
void
{
if (load_all_modules)
}
}
/*
* Compare the token to all the known commands, and add those that
* match to the list of alternatives.
*
* note:
* This routine will force modules to be loaded as necessary to
* obtain the names it needs to match.
*/
void
elfedit_cpl_command(void *cpldata)
{
const char *colon_pos;
char buf[128];
/*
* Is there a colon in the command? If so, locate its offset within
* the raw input line.
*/
;
/*
* If no colon was seen, then we are completing a module name,
* or one of the commands from 'sys:'
*/
if (*colon_pos == '\0') {
/*
* Setting cstate->add_mod_colon tells elfedit_cpl_match()
* to add an implicit ':' to the names it matches. We use it
* here so the user doesn't have to enter the ':' manually.
* Hiding this in the opaque state instead of making it
* an argument to that function gives us the ability to
* change it later without breaking the published interface.
*/
cstate->ecpl_add_mod_colon = 0;
/* Add bare (no sys: prefix) commands from the sys: module */
return;
}
/*
* A colon was seen, so we have a module name. Extract the name,
* substituting 'sys' for the case where the given name is empty.
*/
if (colon_pos == 0)
else
/*
* Locate the module. If it isn't already loaded, make an explicit
* attempt to load it and try again. If a module definition is
* obtained, process the commands it supplies.
*/
(void) elfedit_load_module(buf, 0, 0);
}
/*
* Make a copy of the cstate, and adjust the line and
* token so that the new one starts just past the colon
* character. We know that the colon exists because
* of the preceeding test that found it. Therefore, we do
* not need to test against running off the end of the
* string here.
*/
colon_state = *cstate;
':')
}
/* Skip past the ':' character */
}
}
/*
* Command completion function for use with libtacla.
*/
/*ARGSUSED1*/
static int
{
const char *argv[ELFEDIT_MAXCPLARGS];
int ndx;
int i;
int num_opt;
int opt_term_seen;
int skip_one;
int ostyle_ndx = -1;
/*
* For debugging, enable the following block. It tells the tecla
* library that the program using is going to write to stdout.
* It will put the tty back into normal mode, and it will cause
* tecla to redraw the current input line when it gets control back.
*/
#if 0
#endif
/*
* Tokenize the line up through word_end. The last token in
* the list is the one requiring completion.
*/
return (0);
/* Set up the cstate block, containing the completion state */
cstate.ecpl_add_mod_colon = 0;
/*
* If there is only one token, then we are completing the
* command itself.
*/
if (ndx == 0) {
return (0);
}
/*
* There is more than one token. Use the first one to
* locate the definition for the command. If we don't have
* a definition for the command, then there's nothing more
* we can do.
*/
return (0);
/*
* Since we know the command, give them a quick usage message.
* It may be that they just need a quick reminder about the form
* of the command and the options.
*/
/*
* We have a generous setting for ELFEDIT_MAXCPLARGS, so there
* should always be plenty of room. If there's not room, we
* can't proceed.
*/
if (ndx >= ELFEDIT_MAXCPLARGS)
return (0);
/*
* Put pointers to the tokens into argv, and determine how
* many of the tokens are optional arguments.
*
* We consider the final optional argument to be the rightmost
* argument that starts with a '-'. If a '--' is seen, then
* we stop there, and any argument that follows is a plain argument
* (even if it starts with '-').
*
* We look for an inherited '-o' option, because we are willing
* to supply command completion for these values.
*/
num_opt = 0;
opt_term_seen = 0;
skip_one = 0;
for (i = 0; i < ndx; i++) {
if (opt_term_seen || skip_one) {
skip_one = 0;
continue;
}
skip_one = 0;
ostyle_ndx = -1;
(*argv[i] != '-')) {
opt_term_seen = 1;
continue;
}
num_opt = i + 1;
/*
* If it is a recognised ELFEDIT_CMDOA_F_VALUE option,
* then the item following it is the associated value.
* Check for this and skip the value.
*
* At the same time, look for STDOA_OPT_O inherited
* options. We want to identify the index of any such
* item. Although the option is simply "-o", we are willing
* to treat any option that starts with "-o" as a potential
* STDOA_OPT_O. This lets us to command completion for things
* like "-onum", and is otherwise harmless, the only cost
* being a few additional strcmps by the cpl code.
*/
continue;
int is_ostyle_optarg =
ostyle_ndx = i + 1;
num_opt = i + 2;
skip_one = 1;
break;
}
/*
* If it didn't match "-o" exactly, but it is
* ostyle_ndx, then it is a potential combined
* STDOA_OPT_O, as discussed above. It counts
* as a single argument.
*/
if (ostyle_ndx == ndx)
break;
}
}
}
#if 0
#endif
if (ostyle_ndx != -1) {
/*
* If ostyle_ndx is one less than ndx, and ndx is
* the same as num_opt, then we have a definitive
* STDOA_OPT_O inherited outstyle option. We supply
* the value strings, and are done.
*/
return (0);
}
/*
* If ostyle is the same as ndx, then we have an option
* staring with "-o" that may end up being a STDOA_OPT_O,
* and we are still inside that token. In this case, we
* supply completion strings that include the leading
* "-o" followed by the values, without a space
* (i.e. "-onum"). We then fall through, allowing any
* other options starting with "-o" to be added
* below. elfedit_cpl_match() will throw out the incorrect
* options, so it is harmless to add these extra items in
* the worst case, and useful otherwise.
*/
if (ostyle_ndx == ndx)
}
/*
* If (ndx <= num_opt), then the token needing completion
* is an option. If the leading '-' is there, then we should fill
* in all of the option alternatives. If anything follows the '-'
* though, we assume that the user has already figured out what
* option to use, and we leave well enough alone.
*
* Note that we are intentionally ignoring a related case
* where supplying option strings would be legal: In the case
* where we are one past the last option (ndx == (num_opt + 1)),
* and the current option is an empty string, the argument can
* be either a plain argument or an option --- the user needs to
* enter the next character before we can tell. It would be
* OK to enter the option strings in this case. However, consider
* what happens when the first plain argument to the command does
* not provide any command completion (e.g. it is a plain integer).
* In this case, tecla will see that all the alternatives start
* with '-', and will insert a '-' into the input. If the user
* intends the next argument to be plain, they will have to delete
* this '-', which is annoying. Worse than that, they may be confused
* by it, and think that the plain argument is not allowed there.
* The best solution is to not supply option strings unless the
* user first enters the '-'.
*/
}
}
return (0);
}
/*
* At this point we know that ndx and num_opt are not equal.
* If num_opt is larger than ndx, then we have an ELFEDIT_CMDOA_F_VALUE
* argument at the end, and the following value has not been entered.
*
* If ndx is greater than num_opt, it means that we are looking
* at a plain argument (or in the case where (ndx == (num_opt + 1)),
* a *potential* plain argument.
*
* If the command has a completion function registered, then we
* hand off the remaining work to it. The cmd_cplfunc field is
* the generic definition. We need to cast it to the type that matches
* the proper ELFCLASS before calling it.
*/
if (cmdcpl_func != NULL)
} else {
if (cmdcpl_func != NULL)
}
return (0);
}
/*
* Read a line of input from stdin, and return pointer to it.
*
* This routine uses a private buffer, so the contents of the returned
* string are only good until the next call.
*/
static const char *
read_cmd(void)
{
char *s;
/*
* gl_get_line() returns NULL for EOF or for error. EOF is fine,
* but we need to catch and report anything else. Since
* reading from stdin is critical to our operation, an
* error implies that we cannot recover and must exit.
*/
if ((s == NULL) &&
}
} else {
/*
* This should be a dynamically sized buffer, but for now,
* I'm going to take a simpler path.
*/
}
/* Return user string, or 'quit' on EOF */
return (s ? s : MSG_ORIG(MSG_SYS_CMD_QUIT));
}
int
{
/*
* Note: This function can use setjmp()/longjmp() which does
* variables that need their values preserved across a jump must
*
* Volatile can be messy, because it requires explictly casting
* away the attribute when passing it to functions, or declaring
* those functions with the attribute as well. In a single threaded
* program like this one, an easier approach is to make things
* static. That can be done here, or by putting things in the
* 'state' structure.
*/
int c, i;
int num_batch = 0;
char **batch_list = NULL;
/*
* Always have liblddb display unclipped section names.
* This global is exported by liblddb, and declared in debug.h.
*/
opterr = 0;
switch (c) {
case 'a':
break;
case 'd':
break;
case 'e':
/*
* Delay parsing the -e options until after the call to
* conv_check_native() so that we won't bother loading
* modules of the wrong class.
*/
if (batch_list == NULL)
break;
case 'L':
break;
case 'o':
usage(1);
break;
case 'r':
break;
case '?':
usage(1);
}
}
/*
* We allow 0, 1, or 2 files:
*
* The no-file case is an extremely limited mode, in which the
* only commands allowed to execute come from the sys: module.
* This mode exists primarily to allow easy access to the help
* facility.
*
* To get full access to elfedit's capablities, there must
* be an input file. If this is not a readonly
* session, then an optional second output file is allowed.
*
* In the case where two files are given and the session is
* readonly, use a full usage message, because the simple
* one isn't enough for the user to understand their error.
* Otherwise, the simple usage message suffices.
*/
usage(1);
if (argc > 2)
usage(0);
/*
* If we have a file to edit, and unless told otherwise by the
* caller, we try to run the 64-bit version of this program
* when the system is capable of it. If that fails, then we
* continue on with the currently running version.
*
* To force 32-bit execution on a 64-bit host, set the
* LD_NOEXEC_64 environment variable to a non-empty value.
*
* There is no reason to bother with this if in "no file" mode.
*/
(sizeof (char *) == 8) ? 64 : 32);
/*
* Put a module definition for the builtin system module on the
* module list. We know it starts out empty, so we do not have
* to go through a more general insertion process than this.
*/
/* Establish the search path for loadable modules */
/*
* Now that we are running the final version of this program,
*/
/*
* This is arbitrary --- we simply need to be able to
* load modules so that we can access their help strings
* and command completion functions. Without a file, we
* will refuse to call commands from any module other
* than sys. Those commands have been written to be aware
* of the case where there is no input file, and are
* therefore safe to run.
*/
} else {
if (argc == 1) {
else
} else {
/*
* We are editing a copy of the original file that we
* just created. If we should exit before the edits are
* updated, then we want to unlink this copy so that we
* don't leave junk lying around. Once an update
* succeeds however, we'll leave it in place even
* if an error occurs afterwards.
*/
optind++; /* Edit copy instead of the original */
}
}
/*
* Process commands.
*
* If any -e options were used, then do them and
* immediately exit. On error, exit immediately without
* updating the target ELF file. On success, the 'write'
* and 'quit' commands are implicit in this mode.
*
* If no -e options are used, read commands from stdin.
* quit must be explicitly used. Exit is implicit on EOF.
* If stdin is a tty, then errors do not cause the editor
* to terminate. Rather, the error message is printed, and the
* user prompted to continue.
*/
/* Compile the commands */
for (i = 0; i < num_batch; i++)
parse_user_cmd(batch_list[i]);
/*
* 'write' and 'quit' are implicit in this mode.
* Add them as well.
*/
/* And run them. This won't return, thanks to the 'quit' */
} else {
}
/*
* If pager process exits before we are done
* writing, we can see SIGPIPE. Prevent it
* from killing the process.
*/
/* Open tecla handle for command line editing */
/* Register our command completion function */
/*
* Make autoprint the default for interactive
* sessions.
*/
}
for (;;) {
/*
* If this is an interactive session, then use
* sigsetjmp()/siglongjmp() to recover from bad
* commands and keep going. A non-0 return from
* sigsetjmp() means that an error just occurred.
* In that case, we simply restart this loop.
*/
continue;
}
}
/*
* Force all output out before each command.
* This is a no-OP when a tty is in use, but
* in a pipeline, it ensures that the block
* mode buffering doesn't delay output past
* the completion of each command.
*
* If we didn't do this, the output would eventually
* arrive at its destination, but the lag can be
* annoying when you pipe the output into a tool
* that displays the results in real time.
*/
}
}
/*NOTREACHED*/
return (0);
}