测试环境
Windows 8上运行qemu(qemu中自带gdbserver)IP: 192.168.3.102
Qemu: qemu-w64-setup-20170824.exe
虚拟机中运行为龙芯1c编译的gdb(不能使用系统自带的gdb,除非是使用的龙芯的pc)
IP: 192.168.3.200
Gdb: gdb-7.0.1a.tar.bz2
Helloworld的linux工具链使用龙芯官方推出的gcc-4.3-ls232.tar.gz
RT-Thread的Linux下的交叉编译工具链mips-2015.05-19-mips-sde-elf-i686-pc-linux-gnu.tar.bz2
以上所用到的软件可以到官网下载,也可以去我网盘http://pan.baidu.com/s/1miuYGze
安装qemu
qemu重自带gdb server。
我是在windows 8上使用的qemu,按照和其它软件一样,双击安装就可以了,安装完成后可能需要手动将目录“C:\Program Files\qemu”添加到环境变量。这里需要用到的可执行文件“qemu-system-mipsel.exe”就在该目录中。
编译龙芯1c的gdb
GDB远程调试套件包括Host端的gdb和Target端的gdbserver,对于gdb,宿主机上发行版本自带的(x64)PC版gdb是不能用的,它没有目标架构(MIPS)相关的调试支持。所以我们应该使用gdb的源码,针对MIPS平台编译一个(toolchain还是Host上的)特别的版本
./configure --target=mipsel-linux --prefix=/home/gdb/gdb-dest --program-prefix=mipsel-linux-
测试全过程
启动qemu(自带gdbserver)
在windows上的命令窗口中运行如下命令启动qemu
qemu-system-mipsel -M mips -nographic -kernel bin/image.elf -gdb tcp::1234 -S
其中,-M mips指定machine为mips r4k platform。可以通过命令“qemu-system-mipsel -M ?”查看支持那些machine,用命令“qemu-system-mipsel -cpu ?”查看支持那些cpu。
-kernel后的bin/image.elf为本次调试对象,即运行在qemu上的可执行程序;
-gdb tcp:1234的作用是打开qemu中的gdbserver,并且使用tcp,端口为1234;
最后的一个参数“-S”的作用是qemu启动后立刻挂起gdbserver,等待客户端gdb的连接。
运行mipsel-linux-gdb并开始调试
运行mipsel-linux-gdb并连接qemu中的gdbserver
运行前面已经编译好的mipsel-linux-gdb,并用命令”target remote 192.168.3.102:1234”连接qemu中的gdbserver。
命令”target remote 192.168.3.102:1234“中的IP地址192.168.3.102为运行qemu的PC,端口1234为启动qemu时,传给qemu的gdbserver监听的端口。
读取符号表
编译源码时,在Makefile中加入了编译选项”-g“,所以符号表已经在可执行文件”bin/image.elf“中,那么使用命令”symbol-file”读取即可,如下
好,现在可以开始单步,打断点调试了
单步调试
单步执行最开始那段汇编,源码如下
.text .globl _start _start: b reset nop .org 0x180 /* General exception */ 1: b 1b nop .align 4 reset: /* Clear CP0_SR:BEV to 0 */ mfc0 $t0, $12 and $t0, 0xffbfffff mtc0 $t0, $12 /* Setup stack address */ lui $sp, %hi(stack_top) addiu $sp, $sp, %lo(stack_top) lui $t9, %hi(main) addiu $t9, %lo(main) jalr $t9 nop hang: b hang
调试过程如下
第一个单步,执行了一条汇编指令”mfc0 $t0, $12”,并提示自动切换到汇编模式
然后执行list命令,显示了当前上下文代码,可以看到其后的第二条汇编指令为”and $t0, 0xffbfffff”
第二个单步,执行了汇编指令”and $t0, 0xffbfffff”
第三个单步,执行了汇编指令”mtc0 $t0, $12”
打断点
在main函数处打个断点
注意右边为qemu中还没有打印helloworld,代码中13行打印helloworld,那么在15行再打个断点看看
测试源码
这里只贴部分源码,完整的代码请移步到https://gitee.com/caogos/qemu_gdb_mips_hello
start.S
.text .globl _start _start: b reset nop .org 0x180 /* General exception */ 1: b 1b nop .align 4 reset: /* Clear CP0_SR:BEV to 0 */ mfc0 $t0, $12 and $t0, 0xffbfffff mtc0 $t0, $12 /* Setup stack address */ lui $sp, %hi(stack_top) addiu $sp, $sp, %lo(stack_top) lui $t9, %hi(main) addiu $t9, %lo(main) jalr $t9 nop hang: b hang
main.c
#include <bsp.h> #include <uart.h> int main() { unsigned int tt = 0; int i = 0; // Test exception //writel(0xffffffff, 0x12341234); init_serial(); print_uart0("Hello world!\n"); for (i = 0 ; i < 10 ; i++) { tt = read_c0_count(); } // Never return while(1); return 0; }
uart.h
#ifndef __UART_H__ #define __UART_H__ //Define #define UART0_BASE 0xB40003F8 /* 16550 COM1 */ // API void init_serial(void); void print_uart0(const char *s); void putc(char a); #endif
uart.c
#include <bsp.h> #include <uart.h> static int is_transmit_empty() { volatile char *addr = (volatile char *)(UART0_BASE + 5); return *addr & 0x20; } void putc(char a) { volatile char *addr = (volatile char*)UART0_BASE; while (is_transmit_empty() == 0); *addr = a; } void print_uart0(const char *s) { while (*s != '\0') { /* Loop until end of string */ putc(*s); s++; /* Next char */ } } void init_serial() { writeb(0xc1, UART0_BASE + 2); writeb(0x03, UART0_BASE + 3); writeb(0x03, UART0_BASE + 4); writeb(0x60, UART0_BASE + 5); writeb(0xb0, UART0_BASE + 6); writeb(0x00, UART0_BASE + 7); }
Makefile
OBJDIR = obj/ BINDIR = bin/ DRVDIR = Drivers/ CROSS_COMPILE = mipsel-linux- CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld AS = $(CROSS_COMPILE)as AR = $(CROSS_COMPILE)ar OBJCOPY = $(CROSS_COMPILE)objcopy SYS_INC = include/ DRV_INC =Drivers/include/ INC_FLAGS += -I$(SYS_INC) -I$(DRV_INC) CFLAGS += -mips32 -EL -Wall -g -fno-exceptions -fno-builtin -mno-abicalls -nostdinc -fno-stack-protector LDFLAGS += -nostdlib -nostartfiles -nodefaultlibs -EL ELF_IMAGE = $(BINDIR)image.elf TARGET = $(BINDIR)image.bin LINKER_SCRIPT = boot.ld APP_OBJS += main.o STARTUP_OBJ += start.o DRIVER_OBJ += uart.o OBJS = $(addprefix $(OBJDIR), $(STARTUP_OBJ) $(DRIVER_OBJ) $(APP_OBJS)) all: $(TARGET) $(TARGET):$(OBJDIR) $(BINDIR) $(ELF_IMAGE) $(OBJCOPY) -O binary $(ELF_IMAGE) $(TARGET) $(ELF_IMAGE):$(OBJS) $(LD) $(LDFLAGS) -T $(LINKER_SCRIPT) $(OBJS) -o $@ $(OBJDIR)start.o:start.S $(CC) $(CFLAGS) -c $< -o $@ $(OBJDIR)main.o:main.c $(CC) $(CFLAGS) $(INC_FLAGS) -c $< -o $@ # Drivers $(OBJDIR)uart.o:$(DRVDIR)uart.c $(CC) $(CFLAGS) $(INC_FLAGS) -c $< -o $@ $(OBJDIR): mkdir -p $@ $(BINDIR): mkdir -p $@ clean: rm -rf obj bin
boot.ld
ENTRY(_start) SECTIONS { . = 0x80000000; .startup . : { obj/start.o(.text) } .text : { *(.text) } .data : { *(.data) } .bss : { *(.bss) } . = . + 0x1000; /* 4kB of stack memory */ stack_top = .; }
虽然源码中的串口基地址目前还不是龙芯1c的,但演示一个非常精简的,使用串口打印调试信息的例子,还是很有参考意义的。
查看qemu帮助信息
查看支持的CPU型号
使用命令“qemu-system-mipsel -cpu ?”查看支持的cpu型号,简单来说就是在命令后面跟一个问号,就会把支持的所以cpu型号打印出来。如下
D:\VmShare\rt-thread\bsp\ls1cdev>qemu-system-mipsel -cpu ? MIPS '4Kc' MIPS '4Km' MIPS '4KEcR1' MIPS '4KEmR1' MIPS '4KEc' MIPS '4KEm' MIPS '24Kc' MIPS '24KEc' MIPS '24Kf' MIPS '34Kf' MIPS '74Kf' MIPS 'M14K' MIPS 'M14Kc' MIPS 'P5600' MIPS 'mips32r6-generic'
查看支持的Machine
使用命令“qemu-system-mipsel -M ?”或“qemu-system-mipsel -machine ?”查看支持的Machine
D:\VmShare\qemu_gdb_rtt_ls1c\bsp\ls1cdev>qemu-system-mipsel -machine ? Supported machines are: ar7-amd MIPS AR7 with AMD flash ar7 MIPS 4KEc / AR7 platform fbox-4mb FBox 4 MiB flash (AR7 platform) fbox-8mb FBox 8 MiB flash (AR7 platform) malta MIPS Malta Core LV (default) mips mips r4k platform mipsel misp r4k platform (little endian) mipssim MIPS MIPSsim platform none empty machine sinus-basic-3 Sinus DSL Basic 3 (AR7 platform) sinus-basic-se Sinus DSL Basic SE (AR7 platform) sinus-se Sinus DSL SE (AR7 platform) speedport Speedport (AR7 platform) tnetd7200 MIPS 4KEc / TNETD7200 platform tnetd7300 MIPS 4KEc / TNETD7300 platform D:\VmShare\qemu_gdb_rtt_ls1c\bsp\ls1cdev>qemu-system-mipsel -M ? Supported machines are: ar7-amd MIPS AR7 with AMD flash ar7 MIPS 4KEc / AR7 platform fbox-4mb FBox 4 MiB flash (AR7 platform) fbox-8mb FBox 8 MiB flash (AR7 platform) malta MIPS Malta Core LV (default) mips mips r4k platform mipsel misp r4k platform (little endian) mipssim MIPS MIPSsim platform none empty machine sinus-basic-3 Sinus DSL Basic 3 (AR7 platform) sinus-basic-se Sinus DSL Basic SE (AR7 platform) sinus-se Sinus DSL SE (AR7 platform) speedport Speedport (AR7 platform) tnetd7200 MIPS 4KEc / TNETD7200 platform tnetd7300 MIPS 4KEc / TNETD7300 platform
龙芯官方的qemu
龙芯官方有最新的,支持龙芯1c的qemu,使用上和qemu官网下载的有点区别,网盘(本文前面有链接)中有个简要的说明文档,串口信息通过vncviewer查看,但目前我始终未能看到串口打印,有会的欢迎留言分享,谢谢!
http://ftp.loongnix.org/embed/ls1c/qemu-ls1c.tar.gz
http://ftp.loongnix.org/embed/ls1b/simulator/qemu.pdf
这是龙芯qemu配套说明文档中的部分截图。另外把测试时使用的命令也贴上,仅供参考
export PATH=$PATH:/home/develop/test/qemu-ls1c/qemu386/bin qemu-system-mipsel -M ls1c -serial vc -kernel bin/image.elf -gdb tcp::1234 -S target remote localhost:1234 symbol-file /mnt/hgfs/VmShare/qemu_gdb_mips_hello/bin/image.elf vncview 127.0.0.1:5900