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