Apart from running entire guest operating systems, the emulator can also be used as a kind of debugger.
These sections will quickly go through some of the basic concepts and commands in GXemul.
If you want to follow along with the exact commands as specified below, you should start by downloading the program used in the examples. It is an OpenBSD kernel for LUNA 88K:
https://ftp.eu.openbsd.org/pub/OpenBSD/6.8/luna88k/bsd.rd.
You can run file bsd.rd to check that it is of a reasonable executable file format.
$ file bsd.rd bsd.rd: ELF 32-bit MSB executable, Motorola m88k, version 1 (SYSV), statically linked, not stripped
When starting GXemul, we need to supplying the name of the machine we want to emulate (using -e) and the kernel we want to run/debug.
$ gxemul -e luna-88k bsd.rd
This will start the emulator, start running the kernel, and stop after printing
Welcome to the OpenBSD/luna88k 6.8 installation program. (I)nstall, (U)pgrade, (A)utoinstall or (S)hell?
Type CTRL-C to break into GXemul's command prompt. Type quit whenever you wish to quit from GXemul.
GXemul> quit
(If instead you wish to send a CTRL-C to the emulated system, type CTRL-B.)
You really need to know what machine type you want to emulate. The emulator cannot simply load a binary without having a machine to load it into. There are some machine types called "testXXXX" or "bareXXXX" which are minimal machines with just a CPU (bareXXXX) or just a CPU plus some test devices (testXXXX), but these do not correspond to any real world machine.
The next very useful command line switch, when using the emulator as a debugger, is -V. This causes the emulator to start in a paused state, showing the command prompt.
$ gxemul -V -e luna-88k bsd.rd GXemul (unknown version) Copyright (C) 2003-2021 Anders Gavare Read the source code and/or documentation for other Copyright messages. Simple setup... net: simulated network: 10.0.0.0/8 (max outgoing: TCP=100, UDP=100) simulated gateway+nameserver: 10.0.0.254 (60:50:40:30:20:10) simulated nameserver uses real nameserver 192.168.8.1 machine: memory: 112 MB cpu0: 88100 machine: LUNA 88K loading bsd.rd cpu0: starting at 0x00081004 ------------------------------------------------------------------------------- GXemul>
Next, we want to look at the code in the program that we are about to run.
GXemul> unassemble <__start> s00081004: c0000004 br 0x00081014 ; <main_start> s00081008: c0000003 br 0x00081014 ; <main_start> s0008100c: c0000002 br 0x00081014 ; <main_start> s00081010: c0000001 br 0x00081014 ; <main_start> <main_start> s00081014: c80340a7 bsr 0x001512b0 ; <setup_psr> s00081018: 800080e0 stcr r0,VBR s0008101c: f000d800 tb1 0,r0,0x0 s00081020: 5d600041 or.u r11,r0,0x41 s00081024: 596b162c or r11,r11,0x162c ; <cpu_hatch_mutex> s00081028: f000d800 tb1 0,r0,0x0 s0008102c: 5ac00001 or r22,r0,0x1 s00081030: f6cb0400 xmem r22,r11,r0 s00081034: e8560007 bcnd eq0,r22,0x00081050 s00081038: f6cb1400 ld r22,r11,r0 s0008103c: e9b6ffff bcnd ne0,r22,0x00081038 s00081040: 584003e8 or r2,r0,0x3e8 s00081044: 64420001 subu r2,r2,1 s00081048: e842ffff bcnd eq0,r2,0x00081044 s0008104c: c3fffff7 br 0x00081028 s00081050: f000d800 tb1 0,r0,0x0 GXemul>
In the example above, the unassemble command has been used to unassemble the code which is about to be executed. You don't have to write out unassemble all the time, you can just type u. If you type u followed by an address, it will try to disassemble from that address. Typing just u will continue unassembling from where it last unassembled, or, in the case of the first invocation of that command, from the program's entry point.
As you can see, there are annotations about <main_start>, <setup_psr>, and so on. Those are symbols read from the file. Usually, when an address is taken as an argument to a command such as unassemble, you can type either a numeric address (usually hexadecimal with the 0x prefix) or a symbol. Some simple arithmetic is allowed, typically useful to disassemble some distance before a symbol or similar:
GXemul> u setup_psr-0x20 s00151290: 20020028 st.d r0,r2,0x28 s00151294: 20020030 st.d r0,r2,0x30 s00151298: 20020038 st.d r0,r2,0x38 s0015129c: 60420040 addu r2,r2,64 s001512a0: f4827c0c cmp r4,r2,r12 s001512a4: d864fff6 bb1 3,r4,0x0015127c s001512a8: f400c003 jmp (r3) s001512ac: f4005800 nop <setup_psr> s001512b0: 80404000 ldcr r2,PID s001512b4: f0629908 extu r3,r2,8<8> s001512b8: 7c830001 cmp r4,r3,1 s001512bc: d8440002 bb1 2,r4,0x001512c4 s001512c0: 80008060 stcr r0,SSBR s001512c4: 80008240 stcr r0,SR1 s001512c8: 5c408000 or.u r2,r0,0x8000 s001512cc: 584203f2 or r2,r2,0x3f2 ; 0x800003f2 s001512d0: 80028022 stcr r2,PSR s001512d4: f000d800 tb1 0,r0,0x0 s001512d8: f400c001 jmp (r1) <set_vbr> s001512dc: 80604020 ldcr r3,PSR GXemul>
To dump the register contents, use the reg command:
GXemul> reg cpu0: pc = 0x00081004 <__start> cpu0: r1 = 0x00000000 r2 = 0x00000000 r3 = 0x00000000 cpu0: r4 = 0x00000000 r5 = 0x00000000 r6 = 0x00000000 r7 = 0x00000000 cpu0: r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 r11 = 0x00000000 cpu0: r12 = 0x00000000 r13 = 0x00000000 r14 = 0x00000000 r15 = 0x00000000 cpu0: r16 = 0x00000000 r17 = 0x00000000 r18 = 0x00000000 r19 = 0x00000000 cpu0: r20 = 0x00000000 r21 = 0x00000000 r22 = 0x00000000 r23 = 0x00000000 cpu0: r24 = 0x00000000 r25 = 0x00000000 r26 = 0x00000000 r27 = 0x00000000 cpu0: r28 = 0x00000000 r29 = 0x00000000 r30 = 0x00000000 r31 = 0x06fffc00
You can modify registers by typing commands such as r8 = r31 + 0x1234.
reg takes a CPU id and coprocessor number as optional arguments. To view the system registers of the 88K CPU, type:
GXemul> reg ,0 cpu0: PID=0x00000007 PSR=0x80000002 EPSR=0x00000000 SSBR=0x00000000 cpu0: SXIP=0x00000000 SNIP=0x00000000 SFIP=0x00000000 VBR=0x00000000 cpu0: DMT0=0x00000000 DMD0=0x00000000 DMA0=0x00000000 DMT1=0x00000000 cpu0: DMD1=0x00000000 DMA1=0x00000000 DMT2=0x00000000 DMD2=0x00000000 cpu0: DMA2=0x00000000 SR0=0x00000000 SR1=0x00000000 SR2=0x00000000 cpu0: SR3=0x00000000 CR21=0x00000000 CR22=0x00000000 CR23=0x00000000 cpu0: CR24=0x00000000 CR25=0x00000000 CR26=0x00000000 CR27=0x00000000 cpu0: CR28=0x00000000 CR29=0x00000000 CR30=0x00000000 CR31=0x00000000
reg, (without any number) is a convenient shorthand for showing the system registers (reg ,0).
reg ,1 shows the floating point control registers.
tlbdump dumps the CPUs TLB registers, if there are any.
Similar to unassemble, there is a dump command. Note that you can use register names too, not just symbol names, in the address expressions:
GXemul> dump pc+64 0x00081040 64420001 e842ffff c3fffff7 dB...B...... 0x00081050 f000d800 5d600020 596b7004 5ac00001 ....]`. Ykp.Z... 0x00081060 f6cb0400 e9b60037 5c400041 58423ef8 .......7\@.AXB>. 0x00081070 5c800047 588452c0 cc032402 f4646402 \..GX.R...$..dd. 0x00081080 5c800047 588452c0 5ca00047 24854c1c \..GX.R.\..G$.L. 0x00081090 5d600040 596bc1b8 800b822b 5fe00020 ]`.@Yk.....+_.. 0x000810a0 5bff5000 5c600040 5863c9e4 cc03f661 [.P.\`.@Xc.....a 0x000810b0 804040e0 594000b6 5d604900 2d4b000c .@@.Y@..]`I.-K.. 0x000810c0 0d4b0000 f14aa008 0d8b0004 f54a580c .K...J.......JX. 0x000810d0 5d600041 294b1654 d9ca0004 f5405800 ]`.A)K.T.....@X. 0x000810e0 5d600041 254b1650 15401114 5d600047 ]`.A%K.P.@..]`.G 0x000810f0 254b4b98 59400084 5d604d00 2d4b000c %KK.Y@..]`M.-K.. 0x00081100 59400009 2d4b000c 5d40e100 1d6a0010 Y@..-K..]@...j.. 0x00081110 2d6a0010 5c404100 24020000 c803f65d -j..\@A.$......] 0x00081120 81404220 17ea0008 63ff2000 c8002de1 .@B ....c. ...-. 0x00081130 5c400020 cc00d1db 58420000 f4005800 \@. ....XB....X. 0x00081140 c0000000 .... GXemul>
Now, if you want to start running the emulation (just as if the -V command line option had not been used), type continue at the GXemul> prompt, or simply c, and press enter. If not, you may want to just run a few instructions at a time in order to see where the program is going. By typing step (or just s) followed by a number, the emulator will single-step that number of instructions. If the number is omitted, it will execute 1.
GXemul> step 7 <__start> s00081004: c0000004 br 0x00081014 ; <main_start> <main_start> s00081014: c80340a7 bsr 0x001512b0 ; <setup_psr> <setup_psr(0,0,0,0,0,0,0,0,..)> <setup_psr> s001512b0: 80404000 ldcr r2,PID ; PID = 0x00000007 s001512b4: f0629908 extu r3,r2,8<8> s001512b8: 7c830001 cmp r4,r3,1 s001512bc: d8440002 bb1 2,r4,0x001512c4 s001512c0: 80008060 stcr r0,SSBR ; r0 = 0x00000000 GXemul> s001512c4: 80008240 stcr r0,SR1 ; r0 = 0x00000000 GXemul>
Note that when single-stepping, as opposed to just unassembling, there may be some additional info as "comments" regarding register contents or other things.
As a convenience, when single-stepping, you can just press enter (entering a blank line), which will do the same thing as s 1.
There are two ways to set breakpoints. If you only need to set one or more breakpoints and run until the first breakpoint is hit, you can start the emulator with one or more -p flags. Here is an example:
$ gxemul -p bcopy -e luna-88k bsd.rd
That will run the program until the program counter reaches the symbol (or address) bcopy.
Alternatively, breakpoints can be added interactively from the GXemul> prompt using breakpoint add:
$ gxemul -V -e luna-88k bsd.rd GXemul (unknown version) Copyright (C) 2003-2021 Anders Gavare ... GXemul> breakpoint add bcopy 0: 0x0014a170 (bcopy) GXemul> c CPU0 is associated to 2 MC88200 CMMUs CPU1 is associated to 2 MC88200 CMMUs CPU2 is associated to 2 MC88200 CMMUs CPU3 is associated to 2 MC88200 CMMUs Copyright (c) 1982, 1986, 1989, 1991, 1993 The Regents of the University of California. All rights reserved. Copyright (c) 1995-2018 OpenBSD. All rights reserved. https://www.OpenBSD.org OpenBSD 6.4 (RAMDISK) #0: Sat Oct 20 07:14:59 JST 2018 aoyama@rhea.in.nk-home.net:/w1/o/6.4/src/sys/arch/luna88k/compile/RAMDISK real mem = 117440512 (112MB) avail mem = 110448640 (105MB) mainbus0 at root: OMRON LUNA-88K, 25MHz cpu0: M88100 rev 0x3, 2 CMMU cpu0: M88200 (16K) rev 0x9, full Icache cpu0: M88200 (16K) rev 0x9, full Dcache clock0 at mainbus0: MK48T02 le0 at mainbus0: address 00:00:00:00:00:00 le0: 32 receive buffers, 8 transmit buffers <bcopy> s0014a170: e84400a6 bcnd eq0,r4,0x0014a408 ; <bcopy_out> BREAKPOINT: pc = 0x14a170 (The instruction has not yet executed.) GXemul>
Function call trace is toggled using the trace command from the GXemul> prompt, or using the -t command line option. This will attempt to show subroutine calls with arguments, although it does not really know the function signatures so it will just make a crude guess as to what an argument is. Each call to a subroutine should increment the indentation, and each return should decrement it. Sometimes it works, sometimes it doesn't. The number of arguments is also not known, so it just prints a bunch of them.
$ gxemul -t -p badaddr -e luna-88k bsd.rd ... ------------------------------------------------------------------------------- <__start(0,0,0,0,0,0,0,0,..)> <setup_psr(0,0,0,0,0,0,0,0,..)> <bzero(&edata,0x613c8)> <luna88k_vector_init(0,&vector_list,&end,0x470000,"$",0,0,0,..)> <vector_init(0,&vector_list,1,0x470000,"$",0,0,0,..)> <vector_init(&kernelstart,&vector_list,0,0x470000,0xc005f0fc,0xc005f09a,0xc005f0fc,0x17c3f0,..)> <luna88k_bootstrap(0x41000000,&vector_list,0,0x470000,0xc003f0fc,0xc003f09a,0xc003f0fc,0x17c3f0,..)> <uvm_setpagesize(0x41000000,&vector_list,0,0x470000,0xc003f0fc,0xc003f09a,0xc003f0fc,0x17c3f0,..)> <size_memory(0x41000000,&vector_list,0,0x470000,0xc003f0fc,0xc003f09a,0xc003f0fc,0x470000,..)> <badaddr(0x476000,4,0,0x470000,0xc003f0fc,0xc003f09a,0xc003f0fc,0x470000,..)> <badaddr> s0017d574: 81004020 ldcr r8,PSR ; PSR = 0x800003f2 BREAKPOINT: pc = 0x17d574 (The instruction has not yet executed.) GXemul>
In this example, some of the arguments are guessed to be small integers (e.g. 0 or 4), large integers or general pointer values (prefixed with 0x), or addresses of known symbols (&vector_list). Strings are also sometimes shown, if an argument looks like readable memory which points to a string.