%{
/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#include <mdb/mdb_types.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_shell.h>
#include <mdb/mdb_string.h>
#include <mdb/mdb_frame.h>
#include <mdb/mdb_lex.h>
#include <mdb/mdb_io.h>
#include <mdb/mdb_nv.h>
#include <mdb/mdb.h>
/*
* Utility routines to fetch values from the target's virtual address space
* and object file, respectively. These are called from the handlers for
* the * /.../ and % /.../ code below.
*/
static void
vfetch(void *buf, size_t nbytes, uintptr_t addr)
{
if (mdb_tgt_vread(mdb.m_target, buf, nbytes, addr) != nbytes)
yyperror("failed to read from address %p", addr);
}
static void
ffetch(void *buf, size_t nbytes, uintptr_t addr)
{
if (mdb_tgt_fread(mdb.m_target, buf, nbytes, addr) != nbytes)
yyperror("failed to read from address %p", addr);
}
/*
* Because we define YYMAXDEPTH as zero below, we have to provide a YYEXPAND()
* function to expand our yys and yyv variables. For simplicity, we currently
* define these structures statically; a more complex solution can be defined if
* it is ever needed. If we return 'val', yacc assumes resize has failed.
*/
static int
yyexpand(int val)
{
return (val ? val : YYMAXDEPTH);
}
#define YYEXPAND yyexpand
/*
* This will cause the rest of the yacc code to assume that yys and yyv are
* pointers, not static arrays.
*/
#undef YYMAXDEPTH
#define YYMAXDEPTH 0
%}
%union {
char *l_string;
char l_char;
uintmax_t l_immediate;
mdb_var_t *l_var;
mdb_idcmd_t *l_dcmd;
}
%token <l_string> MDB_TOK_SYMBOL
%token <l_string> MDB_TOK_STRING
%token <l_char> MDB_TOK_CHAR
%token <l_immediate> MDB_TOK_IMMEDIATE
%token <l_dcmd> MDB_TOK_DCMD
%token <l_var> MDB_TOK_VAR_REF
%token <l_immediate> MDB_TOK_LEXPR
%token <l_immediate> MDB_TOK_REXPR
%token <l_immediate> MDB_TOK_COR1_DEREF
%token <l_immediate> MDB_TOK_COR2_DEREF
%token <l_immediate> MDB_TOK_COR4_DEREF
%token <l_immediate> MDB_TOK_COR8_DEREF
%token <l_immediate> MDB_TOK_OBJ1_DEREF
%token <l_immediate> MDB_TOK_OBJ2_DEREF
%token <l_immediate> MDB_TOK_OBJ4_DEREF
%token <l_immediate> MDB_TOK_OBJ8_DEREF
%left '|'
%left '^'
%left '&'
%left MDB_TOK_EQUAL MDB_TOK_NOTEQUAL
%left MDB_TOK_LSHIFT MDB_TOK_RSHIFT
%left '-' '+'
%left '*' '%' '#'
%right MDB_COR_VALUE
%right MDB_OBJ_VALUE
%right MDB_INT_NEGATE
%right MDB_BIT_COMPLEMENT
%right MDB_LOG_NEGATE
%right MDB_VAR_REFERENCE
%type <l_immediate> expression
%type <l_dcmd> command
%%
statement_list: /* Empty */
| statement_list statement { return (0); }
;
terminator: '\n'
| ';'
statement: pipeline shell_pipe terminator {
if (!mdb_call(mdb_nv_get_value(mdb.m_dot), 1, 0))
return (0);
}
| expression pipeline shell_pipe terminator {
if (!mdb_call($1, 1, DCMD_ADDRSPEC))
return (0);
}
| expression ',' expression pipeline shell_pipe terminator {
if (!mdb_call($1, $3, DCMD_ADDRSPEC | DCMD_LOOP))
return (0);
}
| ',' expression pipeline shell_pipe terminator {
if (!mdb_call(mdb_nv_get_value(mdb.m_dot), $2,
DCMD_LOOP))
return (0);
}
| expression terminator {
mdb_frame_t *pfp = mdb_frame_pipe();
/*
* The handling of naked expressions is slightly tricky:
* in a string context, we want to just set dot to the
* expression value. In a pipe context, we also set
* dot but need to record the address in the right-
* hand command's addrv and update any vcbs that are
* active. Otherwise, on the command-line, we have to
* support this as an alias for executing the previous
* command with the new value of dot. Sigh.
*/
if (mdb_iob_isastr(mdb.m_in)) {
mdb_nv_set_value(mdb.m_dot, $1);
mdb.m_incr = 0;
} else if (pfp != NULL && pfp->f_pcmd != NULL) {
mdb_addrvec_unshift(&pfp->f_pcmd->c_addrv,
(uintptr_t)$1);
mdb_vcb_update(pfp, (uintptr_t)$1);
mdb_nv_set_value(mdb.m_dot, $1);
} else {
mdb_list_move(&mdb.m_lastc,
&mdb.m_frame->f_cmds);
if (!mdb_call($1, 1, DCMD_ADDRSPEC))
return (0);
}
}
| expression ',' expression shell_pipe terminator {
mdb_list_move(&mdb.m_lastc, &mdb.m_frame->f_cmds);
if (!mdb_call($1, $3, DCMD_ADDRSPEC | DCMD_LOOP))
return (0);
}
| ',' expression shell_pipe terminator {
uintmax_t dot = mdb_dot_incr(",");
mdb_list_move(&mdb.m_lastc, &mdb.m_frame->f_cmds);
if (!mdb_call(dot, $2, DCMD_LOOP))
return (0);
}
| '!' MDB_TOK_STRING terminator {
if (mdb_iob_isapipe(mdb.m_in))
yyerror("syntax error");
mdb_shell_exec($2);
}
| terminator {
if ((mdb.m_flags & MDB_FL_REPLAST) &&
!mdb_iob_isastr(mdb.m_in)) {
uintmax_t dot = mdb_dot_incr("\\n");
/*
* If a bare terminator is encountered, execute
* the previous command if -o repeatlast is set
* and stdin is not an mdb_eval() string.
*/
mdb_list_move(&mdb.m_lastc,
&mdb.m_frame->f_cmds);
if (!mdb_call(dot, 1, 0))
return (0);
}
}
;
pipeline: pipeline '|' command { mdb_cmd_create($3, &mdb.m_frame->f_argvec); }
| command { mdb_cmd_create($1, &mdb.m_frame->f_argvec); }
;
command: '?' format_list { $$ = mdb_dcmd_lookup("?"); }
| '/' format_list { $$ = mdb_dcmd_lookup("/"); }
| '\\' format_list { $$ = mdb_dcmd_lookup("\\"); }
| '@' format_list { $$ = mdb_dcmd_lookup("@"); }
| '=' format_list { $$ = mdb_dcmd_lookup("="); }
| MDB_TOK_DCMD argument_list { $$ = $1; }
| '$' { $$ = mdb_dcmd_lookup("$?"); }
;
shell_pipe: /* Empty */
| '!' MDB_TOK_STRING { mdb_shell_pipe($2); }
;
format_list: /* Empty */
| format_list MDB_TOK_LEXPR expression MDB_TOK_REXPR {
mdb_arg_t arg;
arg.a_type = MDB_TYPE_IMMEDIATE;
arg.a_un.a_val = $3;
mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
}
| format_list MDB_TOK_IMMEDIATE {
mdb_arg_t arg;
arg.a_type = MDB_TYPE_IMMEDIATE;
arg.a_un.a_val = $2;
mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
}
| format_list MDB_TOK_STRING {
mdb_arg_t arg;
arg.a_type = MDB_TYPE_STRING;
arg.a_un.a_str = $2;
mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
}
| format_list MDB_TOK_CHAR {
mdb_arg_t arg;
arg.a_type = MDB_TYPE_CHAR;
arg.a_un.a_char = $2;
mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
}
;
argument_list: /* Empty */
| argument_list MDB_TOK_LEXPR expression MDB_TOK_REXPR {
mdb_arg_t arg;
arg.a_type = MDB_TYPE_IMMEDIATE;
arg.a_un.a_val = $3;
mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
}
| argument_list MDB_TOK_STRING {
mdb_arg_t arg;
arg.a_type = MDB_TYPE_STRING;
arg.a_un.a_str = $2;
mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
}
;
expression: expression '+' expression { $$ = $1 + $3; }
| expression '-' expression { $$ = $1 - $3; }
| expression '*' expression { $$ = $1 * $3; }
| expression '%' expression {
if ($3 == 0UL)
yyerror("attempted to divide by zero");
/*
* Annoyingly, x86 generates a #DE when dividing
* LONG_MIN by -1; check for this case explicitly.
*/
if ($1 == LONG_MIN && $3 == -1L)
yyerror("divide overflow");
$$ = (intmax_t)$1 / (intmax_t)$3;
}
| expression '&' expression { $$ = $1 & $3; }
| expression '|' expression { $$ = $1 | $3; }
| expression '^' expression { $$ = $1 ^ $3; }
| expression MDB_TOK_EQUAL expression { $$ = ($1 == $3); }
| expression MDB_TOK_NOTEQUAL expression { $$ = ($1 != $3); }
| expression MDB_TOK_LSHIFT expression { $$ = $1 << $3; }
| expression MDB_TOK_RSHIFT expression { $$ = $1 >> $3; }
| expression '#' expression {
if ($3 == 0UL)
yyerror("attempted to divide by zero");
$$ = ((intptr_t)($1 + ($3 - 1)) / (intptr_t)$3) * $3;
}
| '*' expression %prec MDB_COR_VALUE {
uintptr_t value;
vfetch(&value, sizeof (value), $2);
$$ = value;
}
| MDB_TOK_COR1_DEREF expression %prec MDB_COR_VALUE {
uint8_t value;
vfetch(&value, sizeof (value), $2);
$$ = value;
}
| MDB_TOK_COR2_DEREF expression %prec MDB_COR_VALUE {
uint16_t value;
vfetch(&value, sizeof (value), $2);
$$ = value;
}
| MDB_TOK_COR4_DEREF expression %prec MDB_COR_VALUE {
uint32_t value;
vfetch(&value, sizeof (value), $2);
$$ = value;
}
| MDB_TOK_COR8_DEREF expression %prec MDB_COR_VALUE {
uint64_t value;
vfetch(&value, sizeof (value), $2);
$$ = value;
}
| '%' expression %prec MDB_OBJ_VALUE {
uintptr_t value;
ffetch(&value, sizeof (value), $2);
$$ = value;
}
| MDB_TOK_OBJ1_DEREF expression %prec MDB_OBJ_VALUE {
uint8_t value;
ffetch(&value, sizeof (value), $2);
$$ = value;
}
| MDB_TOK_OBJ2_DEREF expression %prec MDB_OBJ_VALUE {
uint16_t value;
ffetch(&value, sizeof (value), $2);
$$ = value;
}
| MDB_TOK_OBJ4_DEREF expression %prec MDB_OBJ_VALUE {
uint32_t value;
ffetch(&value, sizeof (value), $2);
$$ = value;
}
| MDB_TOK_OBJ8_DEREF expression %prec MDB_OBJ_VALUE {
uint64_t value;
ffetch(&value, sizeof (value), $2);
$$ = value;
}
| '-' expression %prec MDB_INT_NEGATE { $$ = -$2; }
| '~' expression %prec MDB_BIT_COMPLEMENT { $$ = ~$2; }
| '#' expression %prec MDB_LOG_NEGATE { $$ = !$2; }
| '(' expression ')' { $$ = $2; }
| MDB_TOK_VAR_REF %prec MDB_VAR_REFERENCE {
$$ = mdb_nv_get_value($1);
}
| MDB_TOK_SYMBOL {
if (strcmp($1, ".") == 0) {
$$ = mdb_nv_get_value(mdb.m_dot);
strfree($1);
} else {
const char *obj = MDB_TGT_OBJ_EVERY, *name = $1;
char *s = (char *)$1;
GElf_Sym sym;
if ((s = strrsplit(s, '`')) != NULL) {
name = s;
obj = $1;
}
if (mdb_tgt_lookup_by_name(mdb.m_target,
obj, name, &sym, NULL) == -1) {
strfree($1);
yyperror("failed to dereference "
"symbol");
}
strfree($1);
$$ = (uintmax_t)sym.st_value;
}
}
| '+' { $$ = mdb_dot_incr("+"); }
| '^' { $$ = mdb_dot_decr("^"); }
| '&' { $$ = mdb.m_raddr; }
| MDB_TOK_IMMEDIATE
;
%%