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