xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_grammar.y (revision 7a2abfc9b29ea1ccc055443876ab57455e3e8f71)
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