您的位置:首页 > 其它

GNU ARM汇编--(二)汇编编译链接与运行

2012-10-15 21:33 525 查看
GNU的汇编器是GNU Tools的一部分,可以用来ARM的汇编语言源代码编译为二进制文件.关于GNU汇编器的介绍可以搜索《GNU Assembler Manual》.这里我们只是做一个简短的介绍,对GNU汇编器有一个大概的认识,同时通过两个例子了解一下GNU ARM汇编.

给出一个模板文件:

[cpp] view plaincopyprint?

.text ; Executable code follows

_start: .global _start ; "_start" is required by the linker

.global main ; "main" is our main program

b main ; Start running the main program

main: ; Entry to the function "main"

; Insert your code here

mov pc,lr ; Return to the caller

.end

汇编器的使用:

一种汇编器是arm-elf-as,一种是arm-linux-as之类的,这两种汇编器是有细微区别.但是一般做开发,半导体厂商都会提供特定的编译器,用那个编译器应该是没错的,而且优化效果应该是最优的,毕竟是芯片公司提供的嘛.他们对体系架构最了解,很清楚的知道怎么去优化.而我们一般的开发者也可以了解处理器的体系架构和嵌入式系统的系统的特征来对汇编代码和c代码做优化.

编译过程:

arm-elf-as -marm7tdmi --gdwarf2 -o filename.o filename.s

-marm7tdmi是指定CPU,arm7tdmi是属于ARMv4T的,一般来说同是ARMv4T应该是兼容的.

--gdwarf2是表示包含debug信息.

链接过程:

arm-elf-ld -o filename.elf filename.o

和UNIX系统编程一样,我们可以根据上面的步骤写makefile,然后make一下.

具体ARM的指令集,伪指令就不写了,资料很多.

下面举两个ARM汇编的实例,一个是裸机下的蜂鸣器(简单的控制GPIO而已,比流水灯还简单),一个是ARM linux下的"hello world"(利用系统调用来实现的).

蜂鸣器的例子如下:

beep.lds beep.S Makefile start.S

start.S:

[cpp] view plaincopyprint?

.text

.global _start

_start:

ldr r3, =0x53000000 @ WATCHDOG寄存器地址

mov r4, #0x0

str r4, [r3] @ 写入0,禁止WATCHDOG,否则CPU会不断重启

ldr sp, =1024*2 @ 设置堆栈,注意:不能大于4k, 因为现在可用的内存只有4K

@ nand flash中的代码在复位后会移到内部ram中,此ram只有4K

bl _main @ 跳转到main函数

halt_loop:

b halt_loop

beep.S

[cpp] view plaincopyprint?

.equ GPBCON, 0x56000010

.equ GPBDAT, 0x56000014

.global _main

_main:

ldr r0,=GPBCON

ldr r1,=0x1

str r1, [r0]

loop:

ldr r2,=GPBDAT

ldr r1,=0x1

str r1,[r2]

bl delay

ldr r2,=GPBDAT

ldr r1,=0x0

str r1,[r2]

bl delay

b loop

delay:

ldr r3,=0x4ffffff

delay1:

sub r3,r3,#1

cmp r3,#0x0

bne delay1

mov pc,lr

.end

beep.lds

[cpp] view plaincopyprint?

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")

OUTPUT_ARCH(arm)

ENTRY(_start)

SECTIONS{

. = 0x33000000;

.text : {

*(.text)

*(.rodata)

}

.data ALIGN(4): {

*(.data)

}

.bss ALIGN(4): {

*(.bss)

}

}

makefile:

[cpp] view plaincopyprint?

CROSS = arm-linux-

CFLAGS = -nostdlib

beep.bin: start.S beep.S

${CROSS}gcc $(CFLAGS) -c -o start.o start.S

${CROSS}gcc $(CFLAGS) -c -o beep.o beep.S

${CROSS}ld -Tbeep.lds start.o beep.o -o beep.elf

${CROSS}objcopy -O binary -S beep.elf beep.bin

rm -f *.o

clean:

rm -f *.elf *.o

rm -f beep.bin

编译后将beep.bin文件烧写到dram中,就可以听到声音了.虽然可以运行了,但还是有两个疑问:

1.lds编译链接文件的写法和技巧 //后续要继续追

2.elf文件的格式 //elf格式是比较新的可执行文件格式,目前在很多OS上都是用这种格式.这个格式可以在有操作系统的情况下直接运行,但是对于裸机的情况,必须对elf文件 做objcopy处理 后续也要继续追

hello world的例子如下:

helloworld.S:

[cpp] view plaincopyprint?

.data

msg: .asciz "hello, world\n"

.text

.align 2

.global _start

_start:

ldr r1, =msg @ address

mov r0, #1 @ stdout

mov r2, #13 @ length

swi #0x900004 @ sys_write

mov r0, #0

swi #0x900001 @ sys_exit

.align 2

makefile:

[cpp] view plaincopyprint?

all:

arm-linux-as helloworld.S -o helloworld.o

arm-linux-ld helloworld.o -o helloworld

将elf文件放到跑有linux的arm板子中,运行就输出hello world.也可以在ubuntu中qemu-arm helloworld模拟.

对比x86下同样用系统调用来输出hello world的程序:

[cpp] view plaincopyprint?

.data

msg: .string "hello\n"

len = . - msg

.text

.global _start

_start:

nop

movl $len, %edx

movl $msg, %ecx

movl $1, %ebx

movl $4, %eax

int $0x80

movl $0, %ebx

movl $1, %eax

int $0x80

它们有几点不同:

1.arm是用swi指令来进行软中断,陷入内核态来实现系统调用的.而x86是用int $0x80

2.x86的系统调用号是用eax寄存器表示的,是第一个参数.而arm的swi直接带有系统调用号,0x900004是0x900000+4,其中0x900000是base.

根据google,做了上面的总结,对GNU ARM汇编有了认识,并且对系统调用软中断,中断处理,uboot异常向量表等等有了探究的欲望,也对elf格式和编译链接有了兴趣,根据自己的方向和精力,后续对这些内容做一个或深或浅的学习.

转载于:blog.csdn.net/dndxhej
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: