Windows下对ARM模拟器开发记录一:开发环境搭建和调试

一、命令行调试(gdb)

程序参考自

http://balau82.wordpress.com/2010/08/17/debugging-arm-programs-inside-qemu/

启动的汇编片段(startup.s)

.global _Reset
_Reset:
	LDR sp, =stack_top
	BL c_entry
	B .

链接脚本(test.ld)

ENTRY(_Reset)
SECTIONS
{
	. = 0x10000;
	.startup . : { startup.o(.text) }
	.text : { *(.text) }
	.data : { *(.data) }
	.bss : { *(.bss) }
	. = . + 0x1000; /* 4kB of stack memory(4kB的堆栈内存)*/
	stack_top = .;
}

(修改二进制文件布局的另一种做法是使用ld的命令行,或既使用-T开关又使用命令行)  

主程序(test.c):

volatile unsigned int * const UART0DR = (unsigned int *)0x101f1000;

void print_uart0(const char *s) {
	while(*s != '\0') { /* Loop until end of string (循环直至字符串结束)*/
		*UART0DR = (unsigned int)(*s); /* Transmit char (发送字符)*/
		s++; /* Next char (下一个字符)*/
	}
}

void c_entry() {
	print_uart0("Hello world!\n");
}

(UART是串口的底层实现,严格来说它是一块芯片,但在嵌入式开发中经常指操纵串口的I/O端口和特殊寄存器,使用方式和内存访问很相似,所以经常用于输出调试信息)

qemu调试批处理文件(qemu_test.bat):

"D:\java\qemu-0.9.0-arm\qemu-system-arm.exe" -s -S -M versatilepb -m 128M -kernel test.bin 
pause

(我使用的是模拟ARM的qemu,注意qemu-system-arm和-M开关,versatile pb是一种使用ARM926EJ-S的开发板)

gdb调试脚本(gdbinit.txt):

file test.elf
target remote localhost:1234
b c_entry
cont

(可以手工输入到gdb控制台,b是breakpoint的缩写)

(file命令用于加载调试信息,所以elf文件在编译时需要加入-g开关)

(target用于远程调试,前提是远程机器运行gdbserver监听1234端口,这里qemu模拟器已经有gdbserver的功能)

(设置程序参数可以用set args,运行程序可以用run)

构建文件(Makefile):

# see http://balau82.wordpress.com/2010/08/17/debugging-arm-programs-inside-qemu/

# for debug:
#   make clean all
# for release: 
# 	make clean all DEBUG=""

PROJECT := test
OBJS := startup.o test.o
DEBUG := -g
ARGS := -mcpu=arm926ej-s ${DEBUG}
EMU := qemu_test.bat

AS := arm-none-eabi-as ${ARGS}
CC := arm-none-eabi-gcc ${ARGS}
LD := arm-none-eabi-ld -T ${PROJECT}.ld -Map ${PROJECT}.map
GDB := arm-none-eabi-gdb --command=gdbinit.txt
RUN := arm-none-eabi-run
SIZE := arm-none-eabi-size
OBJCOPY := arm-none-eabi-objcopy -O binary
RM := rm -f

all:${PROJECT}.bin

${PROJECT}.bin:${PROJECT}.elf
	${SIZE} $<
	${OBJCOPY} $< $@

${PROJECT}.elf:${OBJS} ${LDSCRIPT}
	${LD} ${OBJS} -o $@

%.o:%.s
	${AS} $< -o $@ 

%.o:%.c
	${CC} -c ${ARGS} $< -o $@ 

#run:all
#	${GDB} ${PROJECT}.elf

run:all
	start ${EMU}
	${GDB}
	
clean:
	${RM} *.o *.elf *.bin *.map

(我写的Makefile实在不敢恭维...,惯常方法是使用CFLAGS变量)

(变量值可以用make的参数覆盖,例如make clean all DEBUG="")

(tab缩进是必须的,否则Makefile无法使用)

编译这个Makefile工程需要以下东西

1. Sourcery G++ Lite Edition for ARM

http://www.codesourcery.com/sgpp/lite/arm

获得其中已经编译好的交叉编译器(可以直接在windows下使用)

2. MSys/MinGW 

http://sourceforge.net/projects/mingw/

获得其中的make工具

3. QEMU on Windows (arm)

http://www.h7.dion.ne.jp/~qemu-win/

下载for ARM平台的那个

http://www.h6.dion.ne.jp/~kazuw/qemu-win/qemu-0.9.0-arm.zip

在MSys的/etc/profile.d目录中创建sourcery.sh

#!/bin/sh
export PATH="/arm-2010.09/bin:$PATH"

(可以在msys中手工输入export PATH="/arm-2010.09/bin:$PATH"更改环境变量,大小写敏感)

把Sourcery G++的arm工具链加入MSys的PATH中。

然后运行msys.bat,切换到Makefile工程的目录下执行

make clean all

重建工程,生成带调试信息的test.bin和符号表test.map

 
$ make clean all
rm -f *.o *.elf *.bin *.map
arm-none-eabi-as -mcpu=arm926ej-s -g startup.s -o startup.o
arm-none-eabi-gcc -mcpu=arm926ej-s -g -c -mcpu=arm926ej-s -g test.c -o test.o
arm-none-eabi-ld -T test.ld -Map test.map startup.o test.o -o test.elf
arm-none-eabi-size test.elf
text data bss dec hex filename
144 0 0 144 90 test.elf
arm-none-eabi-objcopy -O binary test.elf test.bin

(clean和all不是指硬盘上的文件,而是特定的目标)

然后执行

make run

运行qemu和gdb进行调试

常用语句

* list:显示附近代码

* step:步进

* display / undisplay / print:计算表达式/取消计算表达式/临时计算表达式

* break / continue:设置断点 / 继续执行

* bt:调用堆栈回溯

20110502:

这里有个教程链接集(日文)

http://850mb.net/pukiwiki/index.php?gdb

http://rat.cis.k.hosei.ac.jp/article/devel/debugongccgdb3.html

backtrace bt 関数呼び出し情報のトレース 【bt】调用堆栈回溯
break b ブレークポイントの設定 【b】添加断点
continue c プログラムの再開 【c】继续执行程序
command comm ブレークポイントヒット時の動作の設定 【comm】设置断点命中时的动作
delete d ブレークポイントの削除 【d】删除断点
delete display d d displayの削除 【d d】删除
display disp 常に表示 【d】普通的变量值显示
finish fin 現在の関数の終了まで実行 【fin】执行直至现在的函数结束
frame f 関数フレームの移動 【f】移动函数帧
help h ヘルプを表示 【h】查看帮助
info i 各種情報のリストを表示 【i】获取各种信息
info breakpoints i b ブレークポイント情報を表示 【i b】获取断点信息
info display i di displayの状態を表示 【i di】获取变量显示信息
info watchpoints i wat ウォッチポイント情報を表示 【i wat】获取watch值信息
list l ソースプログラムを表示 【l】显示源程序
next n ステップアウト実行 【n】step out步出
print p 式を評価して結果を表示 【p】计算表达式的值
printf printf printのフォーマット表示 【printf】格式化计算表达式的值
ptype pt 式の型を表示 (詳細) 【ptype】详细的原型
quit q gdbの終了 【quit】退出gdb会话
return ret 現在の関数をその場で終了 【ret】现在的函数在这里返回
run r プログラムの実行開始 【r】程序开始执行
rwatch rw 読み出しウォッチポイントの設定【rw】读取watch设置
set set 変数の値の設定など 【set】设置变量值
step s ステップイン実行 【step】step in步进
watch wa 書き込みウォッチポイントの設定 【wa】设置写入的watch点
whatis wha 式の型を表示 (簡易) 【wha】简单的表达式类型

上面的【】表示gdb命令缩写  

二、使用Eclipse CDT和qemu进行ARM二进制程序调试

1. Sourcery G++ Lite Edition for ARM

下载第一个EABI版的IA32 Windows Installer即可

http://www.codesourcery.com/sgpp/lite/arm

默认安装在C:\Program Files\CodeSourcery\Sourcery G++ Lite

选择Do not modify PATH(不加入PATH,不过加入也无妨,

因为有交叉编译器前缀可以区分)

另一种选择是下载绿色版IA32 Windows TAR

随便找个地方解压即可用(好像MinGW那样)

2. JDK 1.6

JRE应该也可以,主要是后面Eclipse需要用到

http://www.oracle.com/technetwork/java/javase/downloads/index.html

3. MSys/MinGW

真正有用的是MSys,不过不排除你整个MinGW都装了(我试过在线装MinGW,结果顺带把MSys也装好了)

好像有单独的安装文件,但最好在线安装

http://sourceforge.net/projects/mingw/

MSys的好处是自带了make工具,不需要使用Sourcery工具链那个。

另外还有很多类Unix的系统工具,对使用Makefile很有帮助。

4. Eclipse CDT 

最新版可能不支持GNU ARM Eclipse Plug-in,所以我用CDT 6.0.2 for Eclipse Galileo:

http://www.eclipse.org/downloads/packages/eclipse-ide-cc-developers/galileosr2

点击下载这个:

Windows 32-bit

然后解压即可。

启动前最好写个批处理导入PATH。

set PATH=
set PATH=C:\Program Files\CodeSourcery\Sourcery G++ Lite\bin;%PATH%
set PATH=D:\java\jdk1.6.0_20\bin;%PATH%
set PATH=C:\MinGW\msys\1.0\bin;%PATH%
start eclipse.exe

重命名为start_cdt.bat,放入我的cdt目录中

D:\java\eclipse-cpp-helios-SR2-win32\eclipse

双击启动Eclipse

把Workspace设置为C:\Documents and Settings\Administrator\workspace_arm

勾选默认,然后按OK

5. GNU ARM Eclipse Plug-in

http://sourceforge.net/projects/gnuarmeclipse/

把zip下载到本地,然后把其中的plugins和features两个文件夹复制到CDT目录下

D:\java\eclipse-cpp-helios-SR2-win32\eclipse

然后重新启动Eclipse CDT

可以发现在File->New->C Project时

Project type:会多出两项

* ARM Cross Target Application

* ARM Cross Target Static Library

选择ARM Cross Target Application->ARM Windows GCC (Sourcery G++ Lite)

Project Name:填helloworld

创建新的C工程。

创建完后会发现左面的工程树会显示

C:\Program Files\CodeSourcery\Sourcery G++ Lite\

的头文件工程树,这是自动检测的结果,可以查看工程选项

右键->Properties->C/C++ Build->Discovery Options

默认勾选了自动扫描

Automate discovery of paths and symbols

可能插件会自动在PATH环境变量中搜索带arm-none-eabi-前缀的exe

以确定需要使用的gcc

另一个要留意的地方是

右键->Properties->C/C++ Build->Settings

这里可以设置各种编译参数,并且附有一些关键开关的英文说明。

Binary Parser选项页默认选中GNU Elf Parser,

用于在工程树中直接查看生成debug版elf文件的类型和变量信息。

6. 创建C工程

File->New->Source File

Source File:填入main.c

输入如下内容

/*
 * main.c
 *
 *  Created on: 2011-3-27
 *      Author: Administrator
 */
#include <stdio.h>

int main()
{
	printf("Hello, world!\n");
	return 0;
}

然后菜单->Project->Clean->Clean projects selected below

清除然后重新编译helloworld

输出如下信息:

 
**** Build of configuration Debug for project helloworld ****

cs-make all
Building file: ../main.c
Invoking: ARM Sourcery Windows GCC C Compiler
arm-none-eabi-gcc -O0 -Wall -Wa,-adhlns="main.o.lst" -c -fmessage-length=0 -MMD -MP -MF"main.d" -MT"main.d" -mcpu=cortex-m3 -mthumb -g3 -gdwarf-2 -o"main.o" "../main.c"
Finished building: ../main.c

Building target: helloworld.elf
Invoking: ARM Sourcery Windows GCC C Linker
arm-none-eabi-gcc -nostartfiles -Wl,-Map,helloworld.map -mcpu=cortex-m3 -mthumb -g3 -gdwarf-2 -o"helloworld.elf" ./main.o
c:/program files/codesourcery/sourcery g++ lite/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/bin/ld.exe: warning: cannot find entry symbol _start; defaulting to 00008000
c:/program files/codesourcery/sourcery g++ lite/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-sbrkr.o): In function `_sbrk_r':
sbrkr.c:(.text+0x12): undefined reference to `_sbrk'
c:/program files/codesourcery/sourcery g++ lite/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-writer.o): In function `_write_r':
writer.c:(.text+0x16): undefined reference to `_write'
c:/program files/codesourcery/sourcery g++ lite/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-closer.o): In function `_close_r':
closer.c:(.text+0x12): undefined reference to `_close'
c:/program files/codesourcery/sourcery g++ lite/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-lseekr.o): In function `_lseek_r':
lseekr.c:(.text+0x16): undefined reference to `_lseek'
c:/program files/codesourcery/sourcery g++ lite/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-readr.o): In function `_read_r':
readr.c:(.text+0x16): undefined reference to `_read'
c:/program files/codesourcery/sourcery g++ lite/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-fstatr.o): In function `_fstat_r':
fstatr.c:(.text+0x14): undefined reference to `_fstat'
c:/program files/codesourcery/sourcery g++ lite/bin/../lib/gcc/arm-none-eabi/4.5.1/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-isattyr.o): In function `_isatty_r':
isattyr.c:(.text+0x12): undefined reference to `_isatty'
collect2: ld returned 1 exit status
cs-make: *** [helloworld.elf] Error 1

提示说找不到符号

(如果使用GNU/Linux工具链而非EABI工具链,则没有此问题。GNU/Linux工具链是基于Linux系统的ABI,所以可以使用C库,而EABI工具链则不行)

删除helloworld工程。

重新创建一个Makefile工程

Makefile project->ARM Windows GCC (Sourcery G++ Lite)

工程名称为test

创建完后工程树显示红色的叉叉(因为找不到Makefile)

无视之,重要是要手工修改工程选项:

1) 工程树->右键->Properties->C/C++ Build->Discovery Options

Discovery profile:选择

Managed Build System - per project scanner info profile (Sourcery G++ Lite Windows/C)

下面的Compiler invocation command会自动变成arm-none-eabi-gcc

2) 工程树->右键->Properties->C/C++ Build->Settings->Binary Parsers

勾选Elf Parser。

3) 工程树->右键->Properties->C/C++ General->Paths and Symbols->Includes

可以手工修改头文件和库文件的搜索路径。

最后把前面手工写的Makefile工程的所有文件复制过来(直接粘贴到工程树上)

再按菜单->Project->Clean->Clean projects selected below

重新编译这个工程,使那个红色的叉叉消失:

 
**** Build of configuration Default for project test ****

make all
arm-none-eabi-as -mcpu=arm926ej-s -g startup.s -o startup.o
arm-none-eabi-gcc -mcpu=arm926ej-s -g -c -mcpu=arm926ej-s -g test.c -o test.o
arm-none-eabi-ld -T test.ld -Map test.map startup.o test.o -o test.elf
arm-none-eabi-size test.elf
text data bss dec hex filename
144 0 0 144 90 test.elf
arm-none-eabi-objcopy -O binary test.elf test.bin

发现工程树发生一些变化:

由于前面设置了Elf Parser,test.elf、startup.o和test.o被自动识别为arm/le格式

Windows->Show View打开Make Targets视图

右键->New

Target name:填入clean all

按OK,再展开树,双击clean all即可重新编译工程,

不需要按菜单->Project->Clean->Clean projects selected below那么麻烦了。

7. 调试

把qemu_test.bat修改为使用绝对路径的bin

"D:\java\qemu-0.9.0-arm\qemu-system-arm.exe" -s -S -M versatilepb -m 128M -kernel "C:\Documents and Settings\Administrator\workspace_arm\test\test.bin"
pause

双击工程树中的qemu_test.bat运行qemu。

然后点击上方的绿色小虫子旁边的下拉,

选择Debug Configuration,

双击C/C++ Application,创建一个test Default条目,

然后切换到Debugger页面,

Main->Debugger:选择gdbserver Debugger

Main->GDB debugger:改为arm-none-eabi-gdb

Main->GDB command set:改为Standard (Windows)

Main->Stop on Startup at改为c_entry(根据需要,见test.c)

Main->Use full file path to set breakpoints取消勾选

(因为这个选项可能影响断点添加)

Connection->Type:改为TCP

Connection->Port number:改为1234

Main->C/C++ Applicaton:填入test.elf

(test.elf是用于让arm-none-eabi-gdb加载调试版的符号信息,所以不要填test.bin)

然后按Debug按钮调试(必须保证前面的qemu已经正常运行起来)

弹出窗口Confirm Perspective Switch

勾选Remember my decision,然后Yes切换到Debug视角。

Perspective(视角)是Eclipse的布局,由右上角的标签页来切换,不同于视图。

在调试视角中Debug视图有三个按钮表示

Step Into

Step Over

Step Return

用于单步调试。

也可以双击代码左侧空白处添加断点,然后按Debug视图上的Resume继续运行程序。

如果程序在断点处中断,可以使用以下特性来调试:

* Variables视图中查看局部变量值(包括字符串)。

* Variables视图中添加全局变量的监视

* Debug视图使用Instruction Stepping Mode进行指令步进

* Debug视图的调用堆栈回溯(自动跳到外层调用所在的文件行)

* Registers视图的寄存器数值查看(好像可以立即修改值)

* Expressions视图添加表达式监视点(它比查看局部变量更灵活,但可以是较复杂的表达式)

缺点有:

* 每次调试需要重启qemu(qemu无法被远程重启)。

* 内存查看可能会导致qemu崩溃。

(eclipse貌似无法保存debug会话到工程目录中)

(如果学过TCP/IP原理,这种调试方式很容易理解,调试时qemu是TCP服务器,gdb是TCP客户端,而CDT充当gdb的图形界面。insight的调试机制类似)

三、使用arm-none-eabi-run和arm-none-eabi-gdb

arm-none-eabi-gdb可以识别由当前工具链编译获得的调试版elf文件(即,使用gcc -g开关编译和链接的二进制文件)中的符号(如果无法识别,在gdb中使用file test.elf将获得警告信息)但貌似无法使用target sim进行调试,官方解释说因为Lite版工具链无调试能力(?)。如果使用-T generic-hosted.ld开关(即使用本机的Linker Script),编译得到的文件可以用arm-none-eabi-run运行(生成的elf文件是pe格式的?),此时编译的源文件即使使用了printf这样的库函数也可以正常通过(如果手工写ld脚本,一般要自己实现printf之类的库函数,或者使用-lc显式包含libc.a)。

(20110404)(这是误区!)如果使用Sourcery G++提供的工具链,应该注意eabi和GNU/ARM工具链的区别(前者一般用于编译内核故不使用libc,后者则是编译在ARM Linux上运行的应用程序,故允许编译静态链接libc的。

(未完成)

四、使用skyeye

调试方法和qemu类似,但配置和输入输出方式和qemu不同,详细见

http://sourceforge.net/apps/trac/skyeye/wiki/Linux

它可以和CDT、gdb和insight这些工具交互实现断点调试(类似于qemu)

主要问题是:

* skyeye.conf和skyeye的版本有关,可能导致运行失败

* skyeye.conf的配置缺乏详细的说明和示例

* skyeye对高版本gdb的支持似乎有问题

(未完成)

五、使用insight

(未完成)

六、使用VisualBoyAdvance

VisualBoyAdvance是gba在windows上的模拟器,

可以模拟低端的ARM处理器ARM7TDMI

http://sourceforge.net/projects/vba/

内置gba调试功能

(未完成)

猜你喜欢

转载自weimingtom.iteye.com/blog/976946