GDB (API)
|
00001 /* Memory attributes support, for GDB. 00002 00003 Copyright (C) 2001-2013 Free Software Foundation, Inc. 00004 00005 This file is part of GDB. 00006 00007 This program is free software; you can redistribute it and/or modify 00008 it under the terms of the GNU General Public License as published by 00009 the Free Software Foundation; either version 3 of the License, or 00010 (at your option) any later version. 00011 00012 This program is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 GNU General Public License for more details. 00016 00017 You should have received a copy of the GNU General Public License 00018 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 00019 00020 #include "defs.h" 00021 #include "command.h" 00022 #include "gdbcmd.h" 00023 #include "memattr.h" 00024 #include "target.h" 00025 #include "value.h" 00026 #include "language.h" 00027 #include "vec.h" 00028 #include "gdb_string.h" 00029 #include "breakpoint.h" 00030 #include "cli/cli-utils.h" 00031 00032 const struct mem_attrib default_mem_attrib = 00033 { 00034 MEM_RW, /* mode */ 00035 MEM_WIDTH_UNSPECIFIED, 00036 0, /* hwbreak */ 00037 0, /* cache */ 00038 0, /* verify */ 00039 -1 /* Flash blocksize not specified. */ 00040 }; 00041 00042 const struct mem_attrib unknown_mem_attrib = 00043 { 00044 MEM_NONE, /* mode */ 00045 MEM_WIDTH_UNSPECIFIED, 00046 0, /* hwbreak */ 00047 0, /* cache */ 00048 0, /* verify */ 00049 -1 /* Flash blocksize not specified. */ 00050 }; 00051 00052 00053 VEC(mem_region_s) *mem_region_list, *target_mem_region_list; 00054 static int mem_number = 0; 00055 00056 /* If this flag is set, the memory region list should be automatically 00057 updated from the target. If it is clear, the list is user-controlled 00058 and should be left alone. */ 00059 static int mem_use_target = 1; 00060 00061 /* If this flag is set, we have tried to fetch the target memory regions 00062 since the last time it was invalidated. If that list is still 00063 empty, then the target can't supply memory regions. */ 00064 static int target_mem_regions_valid; 00065 00066 /* If this flag is set, gdb will assume that memory ranges not 00067 specified by the memory map have type MEM_NONE, and will 00068 emit errors on all accesses to that memory. */ 00069 static int inaccessible_by_default = 1; 00070 00071 static void 00072 show_inaccessible_by_default (struct ui_file *file, int from_tty, 00073 struct cmd_list_element *c, 00074 const char *value) 00075 { 00076 if (inaccessible_by_default) 00077 fprintf_filtered (file, _("Unknown memory addresses will " 00078 "be treated as inaccessible.\n")); 00079 else 00080 fprintf_filtered (file, _("Unknown memory addresses " 00081 "will be treated as RAM.\n")); 00082 } 00083 00084 00085 /* Predicate function which returns true if LHS should sort before RHS 00086 in a list of memory regions, useful for VEC_lower_bound. */ 00087 00088 static int 00089 mem_region_lessthan (const struct mem_region *lhs, 00090 const struct mem_region *rhs) 00091 { 00092 return lhs->lo < rhs->lo; 00093 } 00094 00095 /* A helper function suitable for qsort, used to sort a 00096 VEC(mem_region_s) by starting address. */ 00097 00098 int 00099 mem_region_cmp (const void *untyped_lhs, const void *untyped_rhs) 00100 { 00101 const struct mem_region *lhs = untyped_lhs; 00102 const struct mem_region *rhs = untyped_rhs; 00103 00104 if (lhs->lo < rhs->lo) 00105 return -1; 00106 else if (lhs->lo == rhs->lo) 00107 return 0; 00108 else 00109 return 1; 00110 } 00111 00112 /* Allocate a new memory region, with default settings. */ 00113 00114 void 00115 mem_region_init (struct mem_region *new) 00116 { 00117 memset (new, 0, sizeof (struct mem_region)); 00118 new->enabled_p = 1; 00119 new->attrib = default_mem_attrib; 00120 } 00121 00122 /* This function should be called before any command which would 00123 modify the memory region list. It will handle switching from 00124 a target-provided list to a local list, if necessary. */ 00125 00126 static void 00127 require_user_regions (int from_tty) 00128 { 00129 struct mem_region *m; 00130 int ix, length; 00131 00132 /* If we're already using a user-provided list, nothing to do. */ 00133 if (!mem_use_target) 00134 return; 00135 00136 /* Switch to a user-provided list (possibly a copy of the current 00137 one). */ 00138 mem_use_target = 0; 00139 00140 /* If we don't have a target-provided region list yet, then 00141 no need to warn. */ 00142 if (mem_region_list == NULL) 00143 return; 00144 00145 /* Otherwise, let the user know how to get back. */ 00146 if (from_tty) 00147 warning (_("Switching to manual control of memory regions; use " 00148 "\"mem auto\" to fetch regions from the target again.")); 00149 00150 /* And create a new list for the user to modify. */ 00151 length = VEC_length (mem_region_s, target_mem_region_list); 00152 mem_region_list = VEC_alloc (mem_region_s, length); 00153 for (ix = 0; VEC_iterate (mem_region_s, target_mem_region_list, ix, m); ix++) 00154 VEC_quick_push (mem_region_s, mem_region_list, m); 00155 } 00156 00157 /* This function should be called before any command which would 00158 read the memory region list, other than those which call 00159 require_user_regions. It will handle fetching the 00160 target-provided list, if necessary. */ 00161 00162 static void 00163 require_target_regions (void) 00164 { 00165 if (mem_use_target && !target_mem_regions_valid) 00166 { 00167 target_mem_regions_valid = 1; 00168 target_mem_region_list = target_memory_map (); 00169 mem_region_list = target_mem_region_list; 00170 } 00171 } 00172 00173 static void 00174 create_mem_region (CORE_ADDR lo, CORE_ADDR hi, 00175 const struct mem_attrib *attrib) 00176 { 00177 struct mem_region new; 00178 int i, ix; 00179 00180 /* lo == hi is a useless empty region. */ 00181 if (lo >= hi && hi != 0) 00182 { 00183 printf_unfiltered (_("invalid memory region: low >= high\n")); 00184 return; 00185 } 00186 00187 mem_region_init (&new); 00188 new.lo = lo; 00189 new.hi = hi; 00190 00191 ix = VEC_lower_bound (mem_region_s, mem_region_list, &new, 00192 mem_region_lessthan); 00193 00194 /* Check for an overlapping memory region. We only need to check 00195 in the vicinity - at most one before and one after the 00196 insertion point. */ 00197 for (i = ix - 1; i < ix + 1; i++) 00198 { 00199 struct mem_region *n; 00200 00201 if (i < 0) 00202 continue; 00203 if (i >= VEC_length (mem_region_s, mem_region_list)) 00204 continue; 00205 00206 n = VEC_index (mem_region_s, mem_region_list, i); 00207 00208 if ((lo >= n->lo && (lo < n->hi || n->hi == 0)) 00209 || (hi > n->lo && (hi <= n->hi || n->hi == 0)) 00210 || (lo <= n->lo && ((hi >= n->hi && n->hi != 0) || hi == 0))) 00211 { 00212 printf_unfiltered (_("overlapping memory region\n")); 00213 return; 00214 } 00215 } 00216 00217 new.number = ++mem_number; 00218 new.attrib = *attrib; 00219 VEC_safe_insert (mem_region_s, mem_region_list, ix, &new); 00220 } 00221 00222 /* 00223 * Look up the memory region cooresponding to ADDR. 00224 */ 00225 struct mem_region * 00226 lookup_mem_region (CORE_ADDR addr) 00227 { 00228 static struct mem_region region; 00229 struct mem_region *m; 00230 CORE_ADDR lo; 00231 CORE_ADDR hi; 00232 int ix; 00233 00234 require_target_regions (); 00235 00236 /* First we initialize LO and HI so that they describe the entire 00237 memory space. As we process the memory region chain, they are 00238 redefined to describe the minimal region containing ADDR. LO 00239 and HI are used in the case where no memory region is defined 00240 that contains ADDR. If a memory region is disabled, it is 00241 treated as if it does not exist. The initial values for LO 00242 and HI represent the bottom and top of memory. */ 00243 00244 lo = 0; 00245 hi = 0; 00246 00247 /* Either find memory range containing ADDRESS, or set LO and HI 00248 to the nearest boundaries of an existing memory range. 00249 00250 If we ever want to support a huge list of memory regions, this 00251 check should be replaced with a binary search (probably using 00252 VEC_lower_bound). */ 00253 for (ix = 0; VEC_iterate (mem_region_s, mem_region_list, ix, m); ix++) 00254 { 00255 if (m->enabled_p == 1) 00256 { 00257 /* If the address is in the memory region, return that 00258 memory range. */ 00259 if (addr >= m->lo && (addr < m->hi || m->hi == 0)) 00260 return m; 00261 00262 /* This (correctly) won't match if m->hi == 0, representing 00263 the top of the address space, because CORE_ADDR is unsigned; 00264 no value of LO is less than zero. */ 00265 if (addr >= m->hi && lo < m->hi) 00266 lo = m->hi; 00267 00268 /* This will never set HI to zero; if we're here and ADDR 00269 is at or below M, and the region starts at zero, then ADDR 00270 would have been in the region. */ 00271 if (addr <= m->lo && (hi == 0 || hi > m->lo)) 00272 hi = m->lo; 00273 } 00274 } 00275 00276 /* Because no region was found, we must cons up one based on what 00277 was learned above. */ 00278 region.lo = lo; 00279 region.hi = hi; 00280 00281 /* When no memory map is defined at all, we always return 00282 'default_mem_attrib', so that we do not make all memory 00283 inaccessible for targets that don't provide a memory map. */ 00284 if (inaccessible_by_default && !VEC_empty (mem_region_s, mem_region_list)) 00285 region.attrib = unknown_mem_attrib; 00286 else 00287 region.attrib = default_mem_attrib; 00288 00289 return ®ion; 00290 } 00291 00292 /* Invalidate any memory regions fetched from the target. */ 00293 00294 void 00295 invalidate_target_mem_regions (void) 00296 { 00297 if (!target_mem_regions_valid) 00298 return; 00299 00300 target_mem_regions_valid = 0; 00301 VEC_free (mem_region_s, target_mem_region_list); 00302 if (mem_use_target) 00303 mem_region_list = NULL; 00304 } 00305 00306 /* Clear memory region list. */ 00307 00308 static void 00309 mem_clear (void) 00310 { 00311 VEC_free (mem_region_s, mem_region_list); 00312 } 00313 00314 00315 static void 00316 mem_command (char *args, int from_tty) 00317 { 00318 CORE_ADDR lo, hi; 00319 char *tok; 00320 struct mem_attrib attrib; 00321 00322 if (!args) 00323 error_no_arg (_("No mem")); 00324 00325 /* For "mem auto", switch back to using a target provided list. */ 00326 if (strcmp (args, "auto") == 0) 00327 { 00328 if (mem_use_target) 00329 return; 00330 00331 if (mem_region_list != target_mem_region_list) 00332 { 00333 mem_clear (); 00334 mem_region_list = target_mem_region_list; 00335 } 00336 00337 mem_use_target = 1; 00338 return; 00339 } 00340 00341 require_user_regions (from_tty); 00342 00343 tok = strtok (args, " \t"); 00344 if (!tok) 00345 error (_("no lo address")); 00346 lo = parse_and_eval_address (tok); 00347 00348 tok = strtok (NULL, " \t"); 00349 if (!tok) 00350 error (_("no hi address")); 00351 hi = parse_and_eval_address (tok); 00352 00353 attrib = default_mem_attrib; 00354 while ((tok = strtok (NULL, " \t")) != NULL) 00355 { 00356 if (strcmp (tok, "rw") == 0) 00357 attrib.mode = MEM_RW; 00358 else if (strcmp (tok, "ro") == 0) 00359 attrib.mode = MEM_RO; 00360 else if (strcmp (tok, "wo") == 0) 00361 attrib.mode = MEM_WO; 00362 00363 else if (strcmp (tok, "8") == 0) 00364 attrib.width = MEM_WIDTH_8; 00365 else if (strcmp (tok, "16") == 0) 00366 { 00367 if ((lo % 2 != 0) || (hi % 2 != 0)) 00368 error (_("region bounds not 16 bit aligned")); 00369 attrib.width = MEM_WIDTH_16; 00370 } 00371 else if (strcmp (tok, "32") == 0) 00372 { 00373 if ((lo % 4 != 0) || (hi % 4 != 0)) 00374 error (_("region bounds not 32 bit aligned")); 00375 attrib.width = MEM_WIDTH_32; 00376 } 00377 else if (strcmp (tok, "64") == 0) 00378 { 00379 if ((lo % 8 != 0) || (hi % 8 != 0)) 00380 error (_("region bounds not 64 bit aligned")); 00381 attrib.width = MEM_WIDTH_64; 00382 } 00383 00384 #if 0 00385 else if (strcmp (tok, "hwbreak") == 0) 00386 attrib.hwbreak = 1; 00387 else if (strcmp (tok, "swbreak") == 0) 00388 attrib.hwbreak = 0; 00389 #endif 00390 00391 else if (strcmp (tok, "cache") == 0) 00392 attrib.cache = 1; 00393 else if (strcmp (tok, "nocache") == 0) 00394 attrib.cache = 0; 00395 00396 #if 0 00397 else if (strcmp (tok, "verify") == 0) 00398 attrib.verify = 1; 00399 else if (strcmp (tok, "noverify") == 0) 00400 attrib.verify = 0; 00401 #endif 00402 00403 else 00404 error (_("unknown attribute: %s"), tok); 00405 } 00406 00407 create_mem_region (lo, hi, &attrib); 00408 } 00409 00410 00411 static void 00412 mem_info_command (char *args, int from_tty) 00413 { 00414 struct mem_region *m; 00415 struct mem_attrib *attrib; 00416 int ix; 00417 00418 if (mem_use_target) 00419 printf_filtered (_("Using memory regions provided by the target.\n")); 00420 else 00421 printf_filtered (_("Using user-defined memory regions.\n")); 00422 00423 require_target_regions (); 00424 00425 if (!mem_region_list) 00426 { 00427 printf_unfiltered (_("There are no memory regions defined.\n")); 00428 return; 00429 } 00430 00431 printf_filtered ("Num "); 00432 printf_filtered ("Enb "); 00433 printf_filtered ("Low Addr "); 00434 if (gdbarch_addr_bit (target_gdbarch ()) > 32) 00435 printf_filtered (" "); 00436 printf_filtered ("High Addr "); 00437 if (gdbarch_addr_bit (target_gdbarch ()) > 32) 00438 printf_filtered (" "); 00439 printf_filtered ("Attrs "); 00440 printf_filtered ("\n"); 00441 00442 for (ix = 0; VEC_iterate (mem_region_s, mem_region_list, ix, m); ix++) 00443 { 00444 char *tmp; 00445 00446 printf_filtered ("%-3d %-3c\t", 00447 m->number, 00448 m->enabled_p ? 'y' : 'n'); 00449 if (gdbarch_addr_bit (target_gdbarch ()) <= 32) 00450 tmp = hex_string_custom ((unsigned long) m->lo, 8); 00451 else 00452 tmp = hex_string_custom ((unsigned long) m->lo, 16); 00453 00454 printf_filtered ("%s ", tmp); 00455 00456 if (gdbarch_addr_bit (target_gdbarch ()) <= 32) 00457 { 00458 if (m->hi == 0) 00459 tmp = "0x100000000"; 00460 else 00461 tmp = hex_string_custom ((unsigned long) m->hi, 8); 00462 } 00463 else 00464 { 00465 if (m->hi == 0) 00466 tmp = "0x10000000000000000"; 00467 else 00468 tmp = hex_string_custom ((unsigned long) m->hi, 16); 00469 } 00470 00471 printf_filtered ("%s ", tmp); 00472 00473 /* Print a token for each attribute. 00474 00475 * FIXME: Should we output a comma after each token? It may 00476 * make it easier for users to read, but we'd lose the ability 00477 * to cut-and-paste the list of attributes when defining a new 00478 * region. Perhaps that is not important. 00479 * 00480 * FIXME: If more attributes are added to GDB, the output may 00481 * become cluttered and difficult for users to read. At that 00482 * time, we may want to consider printing tokens only if they 00483 * are different from the default attribute. */ 00484 00485 attrib = &m->attrib; 00486 switch (attrib->mode) 00487 { 00488 case MEM_RW: 00489 printf_filtered ("rw "); 00490 break; 00491 case MEM_RO: 00492 printf_filtered ("ro "); 00493 break; 00494 case MEM_WO: 00495 printf_filtered ("wo "); 00496 break; 00497 case MEM_FLASH: 00498 printf_filtered ("flash blocksize 0x%x ", attrib->blocksize); 00499 break; 00500 } 00501 00502 switch (attrib->width) 00503 { 00504 case MEM_WIDTH_8: 00505 printf_filtered ("8 "); 00506 break; 00507 case MEM_WIDTH_16: 00508 printf_filtered ("16 "); 00509 break; 00510 case MEM_WIDTH_32: 00511 printf_filtered ("32 "); 00512 break; 00513 case MEM_WIDTH_64: 00514 printf_filtered ("64 "); 00515 break; 00516 case MEM_WIDTH_UNSPECIFIED: 00517 break; 00518 } 00519 00520 #if 0 00521 if (attrib->hwbreak) 00522 printf_filtered ("hwbreak"); 00523 else 00524 printf_filtered ("swbreak"); 00525 #endif 00526 00527 if (attrib->cache) 00528 printf_filtered ("cache "); 00529 else 00530 printf_filtered ("nocache "); 00531 00532 #if 0 00533 if (attrib->verify) 00534 printf_filtered ("verify "); 00535 else 00536 printf_filtered ("noverify "); 00537 #endif 00538 00539 printf_filtered ("\n"); 00540 00541 gdb_flush (gdb_stdout); 00542 } 00543 } 00544 00545 00546 /* Enable the memory region number NUM. */ 00547 00548 static void 00549 mem_enable (int num) 00550 { 00551 struct mem_region *m; 00552 int ix; 00553 00554 for (ix = 0; VEC_iterate (mem_region_s, mem_region_list, ix, m); ix++) 00555 if (m->number == num) 00556 { 00557 m->enabled_p = 1; 00558 return; 00559 } 00560 printf_unfiltered (_("No memory region number %d.\n"), num); 00561 } 00562 00563 static void 00564 mem_enable_command (char *args, int from_tty) 00565 { 00566 int num; 00567 struct mem_region *m; 00568 int ix; 00569 00570 require_user_regions (from_tty); 00571 00572 target_dcache_invalidate (); 00573 00574 if (args == NULL || *args == '\0') 00575 { /* Enable all mem regions. */ 00576 for (ix = 0; VEC_iterate (mem_region_s, mem_region_list, ix, m); ix++) 00577 m->enabled_p = 1; 00578 } 00579 else 00580 { 00581 struct get_number_or_range_state state; 00582 00583 init_number_or_range (&state, args); 00584 while (!state.finished) 00585 { 00586 num = get_number_or_range (&state); 00587 mem_enable (num); 00588 } 00589 } 00590 } 00591 00592 00593 /* Disable the memory region number NUM. */ 00594 00595 static void 00596 mem_disable (int num) 00597 { 00598 struct mem_region *m; 00599 int ix; 00600 00601 for (ix = 0; VEC_iterate (mem_region_s, mem_region_list, ix, m); ix++) 00602 if (m->number == num) 00603 { 00604 m->enabled_p = 0; 00605 return; 00606 } 00607 printf_unfiltered (_("No memory region number %d.\n"), num); 00608 } 00609 00610 static void 00611 mem_disable_command (char *args, int from_tty) 00612 { 00613 int num; 00614 struct mem_region *m; 00615 int ix; 00616 00617 require_user_regions (from_tty); 00618 00619 target_dcache_invalidate (); 00620 00621 if (args == NULL || *args == '\0') 00622 { 00623 for (ix = 0; VEC_iterate (mem_region_s, mem_region_list, ix, m); ix++) 00624 m->enabled_p = 0; 00625 } 00626 else 00627 { 00628 struct get_number_or_range_state state; 00629 00630 init_number_or_range (&state, args); 00631 while (!state.finished) 00632 { 00633 num = get_number_or_range (&state); 00634 mem_disable (num); 00635 } 00636 } 00637 } 00638 00639 /* Delete the memory region number NUM. */ 00640 00641 static void 00642 mem_delete (int num) 00643 { 00644 struct mem_region *m; 00645 int ix; 00646 00647 if (!mem_region_list) 00648 { 00649 printf_unfiltered (_("No memory region number %d.\n"), num); 00650 return; 00651 } 00652 00653 for (ix = 0; VEC_iterate (mem_region_s, mem_region_list, ix, m); ix++) 00654 if (m->number == num) 00655 break; 00656 00657 if (m == NULL) 00658 { 00659 printf_unfiltered (_("No memory region number %d.\n"), num); 00660 return; 00661 } 00662 00663 VEC_ordered_remove (mem_region_s, mem_region_list, ix); 00664 } 00665 00666 static void 00667 mem_delete_command (char *args, int from_tty) 00668 { 00669 int num; 00670 struct get_number_or_range_state state; 00671 00672 require_user_regions (from_tty); 00673 00674 target_dcache_invalidate (); 00675 00676 if (args == NULL || *args == '\0') 00677 { 00678 if (query (_("Delete all memory regions? "))) 00679 mem_clear (); 00680 dont_repeat (); 00681 return; 00682 } 00683 00684 init_number_or_range (&state, args); 00685 while (!state.finished) 00686 { 00687 num = get_number_or_range (&state); 00688 mem_delete (num); 00689 } 00690 00691 dont_repeat (); 00692 } 00693 00694 static void 00695 dummy_cmd (char *args, int from_tty) 00696 { 00697 } 00698 00699 extern initialize_file_ftype _initialize_mem; /* -Wmissing-prototype */ 00700 00701 static struct cmd_list_element *mem_set_cmdlist; 00702 static struct cmd_list_element *mem_show_cmdlist; 00703 00704 void 00705 _initialize_mem (void) 00706 { 00707 add_com ("mem", class_vars, mem_command, _("\ 00708 Define attributes for memory region or reset memory region handling to\n\ 00709 target-based.\n\ 00710 Usage: mem auto\n\ 00711 mem <lo addr> <hi addr> [<mode> <width> <cache>],\n\ 00712 where <mode> may be rw (read/write), ro (read-only) or wo (write-only),\n\ 00713 <width> may be 8, 16, 32, or 64, and\n\ 00714 <cache> may be cache or nocache")); 00715 00716 add_cmd ("mem", class_vars, mem_enable_command, _("\ 00717 Enable memory region.\n\ 00718 Arguments are the code numbers of the memory regions to enable.\n\ 00719 Usage: enable mem <code number>...\n\ 00720 Do \"info mem\" to see current list of code numbers."), &enablelist); 00721 00722 add_cmd ("mem", class_vars, mem_disable_command, _("\ 00723 Disable memory region.\n\ 00724 Arguments are the code numbers of the memory regions to disable.\n\ 00725 Usage: disable mem <code number>...\n\ 00726 Do \"info mem\" to see current list of code numbers."), &disablelist); 00727 00728 add_cmd ("mem", class_vars, mem_delete_command, _("\ 00729 Delete memory region.\n\ 00730 Arguments are the code numbers of the memory regions to delete.\n\ 00731 Usage: delete mem <code number>...\n\ 00732 Do \"info mem\" to see current list of code numbers."), &deletelist); 00733 00734 add_info ("mem", mem_info_command, 00735 _("Memory region attributes")); 00736 00737 add_prefix_cmd ("mem", class_vars, dummy_cmd, _("\ 00738 Memory regions settings"), 00739 &mem_set_cmdlist, "set mem ", 00740 0/* allow-unknown */, &setlist); 00741 add_prefix_cmd ("mem", class_vars, dummy_cmd, _("\ 00742 Memory regions settings"), 00743 &mem_show_cmdlist, "show mem ", 00744 0/* allow-unknown */, &showlist); 00745 00746 add_setshow_boolean_cmd ("inaccessible-by-default", no_class, 00747 &inaccessible_by_default, _("\ 00748 Set handling of unknown memory regions."), _("\ 00749 Show handling of unknown memory regions."), _("\ 00750 If on, and some memory map is defined, debugger will emit errors on\n\ 00751 accesses to memory not defined in the memory map. If off, accesses to all\n\ 00752 memory addresses will be allowed."), 00753 NULL, 00754 show_inaccessible_by_default, 00755 &mem_set_cmdlist, 00756 &mem_show_cmdlist); 00757 }