Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1896935
  • 博文数量: 333
  • 博客积分: 10791
  • 博客等级: 上将
  • 技术积分: 4314
  • 用 户 组: 普通用户
  • 注册时间: 2007-08-08 07:39
文章分类

全部博文(333)

文章存档

2015年(1)

2011年(116)

2010年(187)

2009年(25)

2008年(3)

2007年(1)

分类: C/C++

2010-11-17 10:18:59

调试一个运行在模拟ARM系统中的Linux程序。我碰到过一些麻烦,因此我会将我的工作过程写在这里。我想用gdbserver来在QEMU中运行一个程序,然后用TCP链接将其连接到运行在我PC上的GDB实例。gdbserver是一个软件层,它实现了的一部分功能(调试残桩),并提供了通过网络(或者串口)连接一个完整的GDB实例的可能性。我想说明的这些都可以通过下面这张图来表示。

使用QEMU内嵌的gdbserver

蓝颜色的部分表示用来运行在我的 PC32x86)上的软件,而绿色的部分则表示运行在ARM上的软件。qemu-system-arm是一个模拟VersatilePB平台的软件;我尝试过运行可以通过Ubuntu仓库里安装的那个版本(这个软件包叫qemu-kvm-extras),但它必须和最新的Linux版本(2.6.35)一起。因此我打算编译并使用QEMU的上流版本。GDBserver“client”都来自于,也就是用于交叉编译ARM软件的编译器。在本例中我想要调试的程序是,这个程序除了打印“Hello World”外没有做多少工作,不过这是一个用GNU Autotools进行交叉编译的好例子。 

先决条件

为了接下来的流程,我首先安装了:

用于ARMCodeSourcery GNU/Linux工具链

用于编译QEMU的本地x86工具链(Ubuntubuild-essentials软件包)

作为调试器图形界面的DDD

用于创建Linux文件系统镜像的cpio工具

libncurses5-dev软件包,用于运行Linux内核和Busybox的菜单式配置

用于编译QEMUlibsdl-devzlib1g-dev

1

$ sudo apt-get install build-essential ddd cpio libncurses5-dev libsdl-dev zlib1g-dev

2

$ wget 

 

3

$ chmod +x arm-2010q1-202-arm-none-linux-gnueabi.bin

4

$ ./arm-2010q1-202-arm-none-linux-gnueabi.bin

我将工具链安装在默认的“~/CodeSourcery”目录下。而这种情况下gdbserver的可执行文件可以在路径“/home/francesco/CodeSourcery/Sourcery_G++_Lite/arm-none-linux-gnueabi/libc/usr/bin/gdbserver”下找到。

 

注意以后的过程都会在一个专用的文件夹下运行,并且从此以后不在需要root的访问权限。整个过程到结束大概需要使用1Gigabyte的硬盘空间。

 

Linux内核

首先,我从中获取了最新的内核版本。

1

$ wget 

2

$ tar xjf linux-2.6.35.tar.bz2

 

3

$ cd linux-2.6.35/

4

$ make ARCH=arm versatile_defconfig

 

5

$ make ARCH=arm menuconfig

当菜单出现时,我来到“Kernel Features”节并打开EABI支持;然后退出 (保存配置) 并编译:

1

$ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- all

2

$ cd ..

结果是得到一个压缩的内核镜像文件“./linux-2.6.35/arch/arm/boot/zImage”

 

Busybox

接下来,我下载了最新版的中我使用了静态编译,但这一次我不会,因为gdbserver(我计划就是要用它)无论如何都需要共享库。 

1

$ wget 

2

$ tar xjf busybox-1.17.1.tar.bz2

 

3

$ cd busybox-1.17.1

4

$ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- defconfig

 

5

$ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- install

6

$ cd ..

结果是得到文件夹“busybox-1.17.1/_install”,包括了一个最小的不包含共享库的根文件系统。

 

QEMU

我重新从源码编译了QEMU,只包含我需要的模拟ARM系统的二进制文件。

1

$ wget tar.gz

2

$ tar xzf qemu-0.12.5.tar.gz

 

3

$ cd qemu-0.12.5

4

$ ./configure --enable-sdl --disable-kvm --enable-debug --target-list=arm-softmmu

 

5

$ ./make

6

$ cd ..

相应的结果是程序“./qemu-0.12.5/arm-softmmu/qemu-system-arm”,会被用来模拟VersatilePB平台。

 

GNU Hello

这个软件包需要配置成使用交叉编译;不过这其实很容易做;它只需要一个交叉编译器的前缀。

1

$ wget http://ftp.gnu.org/gnu/hello/hello-2.6.tar.gz

2

$ tar xzf hello-2.6.tar.gz

 

3

$ cd hello-2.6

4

$ ./configure --host=arm-none-linux-gnueabi

 

5

$ make

6

$ cd ..

结果就是一个ARM架构的可执行文件“hello-2.6/src/hello”

 

完成文件系统构建

所有涉及到的ARM二进制文件 (busybox, gdbserver, hello) 都需要共享库。Codesourcery工具链在安装目录的一个子文件夹下提供了这些库。在我这种情况下就是“/home/francesco/CodeSourcery/Sourcery_G++_Lite/arm-none-linux-gnueabi/libc/lib/”。为了探索哪些库是必需的我使用CodeSourcery工具链所分发的readelf工具。具体说来,我运行了:

01

$ arm-none-linux-gnueabi-readelf hello-2.6/src/hello -a |grep lib

02

 [Requesting program interpreter: /lib/ld-linux.so.3]

 

03

 0x00000001 (NEEDED)                     Shared library: [libgcc_s.so.1]

04

 0x00000001 (NEEDED)                     Shared library: [libc.so.6]

 

05

00010694  00000216 R_ARM_JUMP_SLOT   0000835c   __libc_start_main

06

 2: 0000835c     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.4 (2)

 

07

 89: 0000844c     4 FUNC    GLOBAL DEFAULT   12 __libc_csu_fini

08

 91: 0000835c     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_

 

09

 101: 00008450   204 FUNC    GLOBAL DEFAULT   12 __libc_csu_init

10

 000000: Version: 1  File: libgcc_s.so.1  Cnt: 1

 

11

 0x0020: Version: 1  File: libc.so.6  Cnt: 1

Hello这个二进制文件需要三个共享库: “ld-linux.so.3″, “libgcc_s.so.1″ “libc.so.6″。我对所有的3个二进制文件都执行了这个命令,并且将所需的库和gdbserverhello等可执行文件拷贝到Busybox的文件系统里面。

01

$ cd busybox-1.17.1/_install

02

$ mkdir -p lib

 

03

$ cp /home/francesco/CodeSourcery/Sourcery_G++_Lite/arm-none-linux-gnueabi/libc/lib/ld-linux.so.3 lib/

04

$ cp /home/francesco/CodeSourcery/Sourcery_G++_Lite/arm-none-linux-gnueabi/libc/lib/libgcc_s.so.1 lib/

 

05

$ cp /home/francesco/CodeSourcery/Sourcery_G++_Lite/arm-none-linux-gnueabi/libc/lib/libm.so.6 lib/

06

$ cp /home/francesco/CodeSourcery/Sourcery_G++_Lite/arm-none-linux-gnueabi/libc/lib/libc.so.6 lib/

 

07

$ cp /home/francesco/CodeSourcery/Sourcery_G++_Lite/arm-none-linux-gnueabi/libc/lib/libdl.so.2 lib/

08

$ cp /home/francesco/CodeSourcery/Sourcery_G++_Lite/arm-none-linux-gnueabi/libc/usr/bin/gdbserver usr/bin/

 

09

$ cp ../../hello-2.6/src/hello usr/bin/

10

$ cd ../../

在我的实验中我需要ARM客户机系统那边有一个可用的网络,因此我准备了一个初始化脚本来开启它。该脚本从扩展而来,下面是我用的脚本“rcS”的内容:

1

#!/bin/sh

2

mount -t proc none /proc

 

3

mount -t sysfs none /sys

4

/sbin/mdev -s

 

5

ifconfig lo up

6

ifconfig eth0 10.0.2.15 netmask 255.255.255.0

 

7

route add default gw 10.0.2.1

然后,我将rcS拷贝到Busybox文件系统的“etc/init.d”目录里面并创建压缩的文件系统镜像:

1

$ cd busybox-1.17.1/_install

2

$ mkdir -p proc sys dev etc etc/init.d

 

3

$ cp ../../rcS etc/init.d

4

$ chmod +x etc/init.d/rcS

 

5

$ find . | cpio -o --format=newc | gzip > ../../rootfs.img.gz

6

$ cd ../../

运行与调试

现在我已经将所有东西准备好:

·         一个压缩的内核镜像

·         QEMU

·         一个压缩的文件系统镜像,包括:

o    Busybox

o    rcS初始化脚本

o    ARM编译的GNU Hello二进制文件

o    用于ARMgdbserver

o    所需的共享库

 

运行该平台的命令行是:

1

$ ./qemu-0.12.5/arm-softmmu/qemu-system-arm -M versatilepb -m 128M -kernel ./linux-2.6.35/arch/arm/boot/zImage -initrd ./rootfs.img.gz -append "root=/dev/ram rdinit=/sbin/init" -redir tcp:1234::1234

这里“redir”选项会将我Ubuntu PC的端口1234上的所有TCP通信重定向到ARM客户机系统的1234端口上。系统将会启动,然后会打开一个root权限的控制台。在bash提示符下,我运行调试服务器: 

1

# gdbserver --multi 10.0.2.15:1234

该命令会启动一个服务器并等待1234端口上的GDB连接。在PC上我可以打开调试器:

1

$ ddd --debugger arm-none-linux-gnueabi-gdb

也可以单独运行arm-none-linux-gnueabi-gdb命令或者连接到另外一个前端。要调试远端的程序,我需要告诉GDB使用ARM的共享库而不是本地库(那些用于32x86的);否则执行的时候调试器会抱怨说库不匹配。

1

set solib-absolute-prefix nonexistantpath

2

set solib-search-path /home/francesco/CodeSourcery/Sourcery_G++_Lite/arm-none-linux-gnueabi/libc/lib/

 

3

file ./hello-2.6/src/hello

4

target extended-remote localhost:1234

 

5

set remote exec-file /usr/bin/hello

6

break main

 

7

run

到这里后,调试过程就跟平常一样了。

 

阅读(6966) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~