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
vfetch(void * buf,size_t nbytes,uintptr_t addr)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
ffetch(void * buf,size_t nbytes,uintptr_t addr)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
yyexpand(int val)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