1 %{ 2 /* 3 * CDDL HEADER START 4 * 5 * The contents of this file are subject to the terms of the 6 * Common Development and Distribution License (the "License"). 7 * You may not use this file except in compliance with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 * 22 */ 23 /* 24 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 /* 29 * Copyright (c) 2019, Joyent, Inc. All rights reserved. 30 */ 31 32 #include <mdb/mdb_types.h> 33 #include <mdb/mdb_debug.h> 34 #include <mdb/mdb_shell.h> 35 #include <mdb/mdb_string.h> 36 #include <mdb/mdb_frame.h> 37 #include <mdb/mdb_lex.h> 38 #include <mdb/mdb_io.h> 39 #include <mdb/mdb_nv.h> 40 #include <mdb/mdb.h> 41 42 /* 43 * Utility routines to fetch values from the target's virtual address space 44 * and object file, respectively. These are called from the handlers for 45 * the * /.../ and % /.../ code below. 46 */ 47 48 static void 49 vfetch(void *buf, size_t nbytes, uintptr_t addr) 50 { 51 if (mdb_tgt_vread(mdb.m_target, buf, nbytes, addr) != nbytes) 52 yyperror("failed to read from address %p", addr); 53 } 54 55 static void 56 ffetch(void *buf, size_t nbytes, uintptr_t addr) 57 { 58 if (mdb_tgt_fread(mdb.m_target, buf, nbytes, addr) != nbytes) 59 yyperror("failed to read from address %p", addr); 60 } 61 62 /* 63 * Because we define YYMAXDEPTH as zero below, we have to provide a YYEXPAND() 64 * function to expand our yys and yyv variables. For simplicity, we currently 65 * define these structures statically; a more complex solution can be defined if 66 * it is ever needed. If we return 'val', yacc assumes resize has failed. 67 */ 68 static int 69 yyexpand(int val) 70 { 71 return (val ? val : YYMAXDEPTH); 72 } 73 #define YYEXPAND yyexpand 74 75 /* 76 * This will cause the rest of the yacc code to assume that yys and yyv are 77 * pointers, not static arrays. 78 */ 79 #undef YYMAXDEPTH 80 #define YYMAXDEPTH 0 81 %} 82 83 %union { 84 char *l_string; 85 char l_char; 86 uintmax_t l_immediate; 87 mdb_var_t *l_var; 88 mdb_idcmd_t *l_dcmd; 89 } 90 91 %token <l_string> MDB_TOK_SYMBOL 92 %token <l_string> MDB_TOK_STRING 93 %token <l_char> MDB_TOK_CHAR 94 %token <l_immediate> MDB_TOK_IMMEDIATE 95 %token <l_dcmd> MDB_TOK_DCMD 96 %token <l_var> MDB_TOK_VAR_REF 97 %token <l_immediate> MDB_TOK_LEXPR 98 %token <l_immediate> MDB_TOK_REXPR 99 %token <l_immediate> MDB_TOK_COR1_DEREF 100 %token <l_immediate> MDB_TOK_COR2_DEREF 101 %token <l_immediate> MDB_TOK_COR4_DEREF 102 %token <l_immediate> MDB_TOK_COR8_DEREF 103 %token <l_immediate> MDB_TOK_OBJ1_DEREF 104 %token <l_immediate> MDB_TOK_OBJ2_DEREF 105 %token <l_immediate> MDB_TOK_OBJ4_DEREF 106 %token <l_immediate> MDB_TOK_OBJ8_DEREF 107 108 %left '|' 109 %left '^' 110 %left '&' 111 %left MDB_TOK_EQUAL MDB_TOK_NOTEQUAL 112 %left MDB_TOK_LSHIFT MDB_TOK_RSHIFT 113 %left '-' '+' 114 %left '*' '%' '#' 115 %left MDB_TOK_MODULUS 116 117 %right MDB_COR_VALUE 118 %right MDB_OBJ_VALUE 119 %right MDB_INT_NEGATE 120 %right MDB_BIT_COMPLEMENT 121 %right MDB_LOG_NEGATE 122 %right MDB_VAR_REFERENCE 123 124 %type <l_immediate> expression 125 %type <l_dcmd> command 126 127 %% 128 statement_list: /* Empty */ 129 | statement_list statement { return (0); } 130 ; 131 132 terminator: '\n' 133 | ';' 134 135 statement: pipeline shell_pipe terminator { 136 if (!mdb_call(mdb_nv_get_value(mdb.m_dot), 1, 0)) 137 return (0); 138 } 139 140 | expression pipeline shell_pipe terminator { 141 if (!mdb_call($1, 1, DCMD_ADDRSPEC)) 142 return (0); 143 } 144 145 | expression ',' expression pipeline shell_pipe terminator { 146 if (!mdb_call($1, $3, DCMD_ADDRSPEC | DCMD_LOOP)) 147 return (0); 148 } 149 150 | ',' expression pipeline shell_pipe terminator { 151 if (!mdb_call(mdb_nv_get_value(mdb.m_dot), $2, 152 DCMD_LOOP)) 153 return (0); 154 } 155 156 | expression terminator { 157 mdb_frame_t *pfp = mdb_frame_pipe(); 158 /* 159 * The handling of naked expressions is slightly tricky: 160 * in a string context, we want to just set dot to the 161 * expression value. In a pipe context, we also set 162 * dot but need to record the address in the right- 163 * hand command's addrv and update any vcbs that are 164 * active. Otherwise, on the command-line, we have to 165 * support this as an alias for executing the previous 166 * command with the new value of dot. Sigh. 167 */ 168 if (mdb_iob_isastr(mdb.m_in)) { 169 mdb_nv_set_value(mdb.m_dot, $1); 170 mdb.m_incr = 0; 171 } else if (pfp != NULL && pfp->f_pcmd != NULL) { 172 mdb_addrvec_unshift(&pfp->f_pcmd->c_addrv, 173 (uintptr_t)$1); 174 mdb_vcb_update(pfp, (uintptr_t)$1); 175 mdb_nv_set_value(mdb.m_dot, $1); 176 } else { 177 mdb_list_move(&mdb.m_lastc, 178 &mdb.m_frame->f_cmds); 179 if (!mdb_call($1, 1, DCMD_ADDRSPEC)) 180 return (0); 181 } 182 } 183 184 | expression ',' expression shell_pipe terminator { 185 mdb_list_move(&mdb.m_lastc, &mdb.m_frame->f_cmds); 186 if (!mdb_call($1, $3, DCMD_ADDRSPEC | DCMD_LOOP)) 187 return (0); 188 } 189 190 | ',' expression shell_pipe terminator { 191 uintmax_t dot = mdb_dot_incr(","); 192 mdb_list_move(&mdb.m_lastc, &mdb.m_frame->f_cmds); 193 if (!mdb_call(dot, $2, DCMD_LOOP)) 194 return (0); 195 } 196 197 | '!' MDB_TOK_STRING terminator { 198 if (mdb_iob_isapipe(mdb.m_in)) 199 yyerror("syntax error"); 200 mdb_shell_exec($2); 201 } 202 203 | terminator { 204 if ((mdb.m_flags & MDB_FL_REPLAST) && 205 !mdb_iob_isastr(mdb.m_in)) { 206 uintmax_t dot = mdb_dot_incr("\\n"); 207 /* 208 * If a bare terminator is encountered, execute 209 * the previous command if -o repeatlast is set 210 * and stdin is not an mdb_eval() string. 211 */ 212 mdb_list_move(&mdb.m_lastc, 213 &mdb.m_frame->f_cmds); 214 if (!mdb_call(dot, 1, 0)) 215 return (0); 216 } 217 } 218 ; 219 220 pipeline: pipeline '|' command { mdb_cmd_create($3, &mdb.m_frame->f_argvec); } 221 | command { mdb_cmd_create($1, &mdb.m_frame->f_argvec); } 222 ; 223 224 command: '?' format_list { $$ = mdb_dcmd_lookup("?"); } 225 | '/' format_list { $$ = mdb_dcmd_lookup("/"); } 226 | '\\' format_list { $$ = mdb_dcmd_lookup("\\"); } 227 | '@' format_list { $$ = mdb_dcmd_lookup("@"); } 228 | '=' format_list { $$ = mdb_dcmd_lookup("="); } 229 | MDB_TOK_DCMD argument_list { $$ = $1; } 230 | '$' { $$ = mdb_dcmd_lookup("$?"); } 231 ; 232 233 shell_pipe: /* Empty */ 234 | '!' MDB_TOK_STRING { mdb_shell_pipe($2); } 235 ; 236 237 format_list: /* Empty */ 238 | format_list MDB_TOK_LEXPR expression MDB_TOK_REXPR { 239 mdb_arg_t arg; 240 241 arg.a_type = MDB_TYPE_IMMEDIATE; 242 arg.a_un.a_val = $3; 243 244 mdb_argvec_append(&mdb.m_frame->f_argvec, &arg); 245 } 246 247 | format_list MDB_TOK_IMMEDIATE { 248 mdb_arg_t arg; 249 250 arg.a_type = MDB_TYPE_IMMEDIATE; 251 arg.a_un.a_val = $2; 252 253 mdb_argvec_append(&mdb.m_frame->f_argvec, &arg); 254 } 255 256 | format_list MDB_TOK_STRING { 257 mdb_arg_t arg; 258 259 arg.a_type = MDB_TYPE_STRING; 260 arg.a_un.a_str = $2; 261 262 mdb_argvec_append(&mdb.m_frame->f_argvec, &arg); 263 } 264 265 | format_list MDB_TOK_CHAR { 266 mdb_arg_t arg; 267 268 arg.a_type = MDB_TYPE_CHAR; 269 arg.a_un.a_char = $2; 270 271 mdb_argvec_append(&mdb.m_frame->f_argvec, &arg); 272 } 273 ; 274 275 argument_list: /* Empty */ 276 | argument_list MDB_TOK_LEXPR expression MDB_TOK_REXPR { 277 mdb_arg_t arg; 278 279 arg.a_type = MDB_TYPE_IMMEDIATE; 280 arg.a_un.a_val = $3; 281 282 mdb_argvec_append(&mdb.m_frame->f_argvec, &arg); 283 } 284 285 | argument_list MDB_TOK_STRING { 286 mdb_arg_t arg; 287 288 arg.a_type = MDB_TYPE_STRING; 289 arg.a_un.a_str = $2; 290 291 mdb_argvec_append(&mdb.m_frame->f_argvec, &arg); 292 } 293 ; 294 295 expression: expression '+' expression { $$ = $1 + $3; } 296 | expression '-' expression { $$ = $1 - $3; } 297 | expression '*' expression { $$ = $1 * $3; } 298 299 | expression '%' expression { 300 if ($3 == 0UL) 301 yyerror("attempted to divide by zero"); 302 303 /* 304 * Annoyingly, x86 generates a #DE when dividing 305 * LONG_MIN by -1; check for this case explicitly. 306 */ 307 if ($1 == LONG_MIN && $3 == -1L) 308 yyerror("divide overflow"); 309 310 $$ = (intmax_t)$1 / (intmax_t)$3; 311 } 312 313 | expression MDB_TOK_MODULUS expression { 314 315 if ($3 == 0UL) 316 yyerror("attempted to divide by zero"); 317 318 $$ = $1 % $3; 319 } 320 321 322 323 | expression '&' expression { $$ = $1 & $3; } 324 | expression '|' expression { $$ = $1 | $3; } 325 | expression '^' expression { $$ = $1 ^ $3; } 326 327 | expression MDB_TOK_EQUAL expression { $$ = ($1 == $3); } 328 | expression MDB_TOK_NOTEQUAL expression { $$ = ($1 != $3); } 329 330 | expression MDB_TOK_LSHIFT expression { $$ = $1 << $3; } 331 | expression MDB_TOK_RSHIFT expression { $$ = $1 >> $3; } 332 333 | expression '#' expression { 334 if ($3 == 0UL) 335 yyerror("attempted to divide by zero"); 336 337 $$ = ((intptr_t)($1 + ($3 - 1)) / (intptr_t)$3) * $3; 338 } 339 340 | '*' expression %prec MDB_COR_VALUE { 341 uintptr_t value; 342 343 vfetch(&value, sizeof (value), $2); 344 $$ = value; 345 } 346 347 | MDB_TOK_COR1_DEREF expression %prec MDB_COR_VALUE { 348 uint8_t value; 349 350 vfetch(&value, sizeof (value), $2); 351 $$ = value; 352 } 353 354 | MDB_TOK_COR2_DEREF expression %prec MDB_COR_VALUE { 355 uint16_t value; 356 357 vfetch(&value, sizeof (value), $2); 358 $$ = value; 359 } 360 361 | MDB_TOK_COR4_DEREF expression %prec MDB_COR_VALUE { 362 uint32_t value; 363 364 vfetch(&value, sizeof (value), $2); 365 $$ = value; 366 } 367 368 | MDB_TOK_COR8_DEREF expression %prec MDB_COR_VALUE { 369 uint64_t value; 370 371 vfetch(&value, sizeof (value), $2); 372 $$ = value; 373 } 374 375 | '%' expression %prec MDB_OBJ_VALUE { 376 uintptr_t value; 377 378 ffetch(&value, sizeof (value), $2); 379 $$ = value; 380 } 381 382 | MDB_TOK_OBJ1_DEREF expression %prec MDB_OBJ_VALUE { 383 uint8_t value; 384 385 ffetch(&value, sizeof (value), $2); 386 $$ = value; 387 } 388 389 | MDB_TOK_OBJ2_DEREF expression %prec MDB_OBJ_VALUE { 390 uint16_t value; 391 392 ffetch(&value, sizeof (value), $2); 393 $$ = value; 394 } 395 396 | MDB_TOK_OBJ4_DEREF expression %prec MDB_OBJ_VALUE { 397 uint32_t value; 398 399 ffetch(&value, sizeof (value), $2); 400 $$ = value; 401 } 402 403 | MDB_TOK_OBJ8_DEREF expression %prec MDB_OBJ_VALUE { 404 uint64_t value; 405 406 ffetch(&value, sizeof (value), $2); 407 $$ = value; 408 } 409 410 | '-' expression %prec MDB_INT_NEGATE { $$ = -$2; } 411 | '~' expression %prec MDB_BIT_COMPLEMENT { $$ = ~$2; } 412 | '#' expression %prec MDB_LOG_NEGATE { $$ = !$2; } 413 | '(' expression ')' { $$ = $2; } 414 415 | MDB_TOK_VAR_REF %prec MDB_VAR_REFERENCE { 416 $$ = mdb_nv_get_value($1); 417 } 418 419 | MDB_TOK_SYMBOL { 420 if (strcmp($1, ".") == 0) { 421 $$ = mdb_nv_get_value(mdb.m_dot); 422 strfree($1); 423 424 } else { 425 const char *obj = MDB_TGT_OBJ_EVERY, *name = $1; 426 char *s = (char *)$1; 427 GElf_Sym sym; 428 429 if ((s = strrsplit(s, '`')) != NULL) { 430 name = s; 431 obj = $1; 432 } 433 434 if (mdb_tgt_lookup_by_name(mdb.m_target, 435 obj, name, &sym, NULL) == -1) { 436 strfree($1); 437 yyperror("failed to dereference " 438 "symbol"); 439 } 440 441 strfree($1); 442 $$ = (uintmax_t)sym.st_value; 443 } 444 } 445 446 | '+' { $$ = mdb_dot_incr("+"); } 447 | '^' { $$ = mdb_dot_decr("^"); } 448 | '&' { $$ = mdb.m_raddr; } 449 | MDB_TOK_IMMEDIATE 450 ; 451 452 %% 453