手机版
你好,游客 登录 注册
背景:
阅读新闻

GNU ARM汇编

汇编编译链接与运行

[日期:2012-08-12] 来源:Linux社区  作者:dndxhej [字体: ]
GNU的汇编器是GNU Tools的一部分,可以用来ARM的汇编语言源代码编译为二进制文件.关于GNU汇编器的介绍可以搜索《GNU Assembler Manual》.这里我们只是做一个简短的介绍,对GNU汇编器有一个大概的认识,同时通过两个例子了解一下GNU ARM汇编.

给出一个模板文件:

  1.     .text                                               ; Executable code follows  
  2. _start: .global _start                                  ; "_start" is required by the linker  
  3.     .global main                                        ; "main" is our main program  
  4.     b main                                              ; Start running the main program  
  5. main:                                                   ; Entry to the function "main"  
  6. ; Insert your code here  
  7.     mov pc,lr                                           ; Return to the caller  
  8.     .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:

  1. .text  
  2. .global _start  
  3. _start:  
  4.     ldr     r3, =0x53000000     @ WATCHDOG寄存器地址  
  5.     mov     r4, #0x0                       
  6.     str   r4, [r3]              @ 写入0,禁止WATCHDOG,否则CPU会不断重启  
  7.   
  8.     ldr     sp, =1024*2         @ 设置堆栈,注意:不能大于4k, 因为现在可用的内存只有4K  
  9.                                         @ nand flash中的代码在复位后会移到内部ram中,此ram只有4K  
  10.     bl      _main                @ 跳转到main函数  
  11. halt_loop:  
  12.     b       halt_loop  
beep.S
  1. .equ    GPBCON,   0x56000010    
  2. .equ    GPBDAT,   0x56000014    
  3.   
  4. .global _main    
  5. _main:   
  6.     ldr r0,=GPBCON  
  7.   
  8.     ldr r1,=0x1  
  9.     str r1, [r0]  
  10. loop:  
  11.   
  12.     ldr r2,=GPBDAT  
  13.     ldr r1,=0x1  
  14.     str r1,[r2]  
  15.     bl delay  
  16.   
  17.     ldr r2,=GPBDAT  
  18.     ldr r1,=0x0  
  19.     str r1,[r2]  
  20.     bl delay  
  21.   
  22.     b loop  
  23.   
  24. delay:  
  25.     ldr r3,=0x4ffffff  
  26.   
  27. delay1:  
  28.     sub r3,r3,#1  
  29.   
  30.     cmp r3,#0x0  
  31.   
  32.     bne delay1  
  33.   
  34.     mov pc,lr  
  35. .end  
      beep.lds
  1. OUTPUT_FORMAT("elf32-littlearm""elf32-littlearm""elf32-littlearm")  
  2. OUTPUT_ARCH(arm)  
  3. ENTRY(_start)  
  4.   
  5. SECTIONS{  
  6.     . = 0x33000000;  
  7.     .text : {  
  8.         *(.text)  
  9.         *(.rodata)  
  10.     }  
  11.   
  12.     .data ALIGN(4): {  
  13.         *(.data)  
  14.     }  
  15.   
  16.     .bss ALIGN(4): {  
  17.         *(.bss)  
  18.     }  
  19. }  
       makefile:
  1. CROSS =  arm-linux-  
  2. CFLAGS = -nostdlib  
  3.   
  4. beep.bin: start.S beep.S  
  5.     ${CROSS}gcc $(CFLAGS) -c -o start.o start.S  
  6.     ${CROSS}gcc $(CFLAGS) -c -o beep.o beep.S  
  7.     ${CROSS}ld -Tbeep.lds start.o beep.o -o beep.elf  
  8.     ${CROSS}objcopy -O binary -S beep.elf beep.bin  
  9.     rm -f  *.o  
  10.   
  11.   
  12. clean:  
  13.     rm -f *.elf *.o  
  14.     rm -f beep.bin  
        编译后将beep.bin文件烧写到dram中,就可以听到声音了.虽然可以运行了,但还是有两个疑问:

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

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


        hello world的例子如下:

        helloworld.S:

  1. .data  
  2.     msg:  .asciz  "hello, world\n"  
  3.   
  4. .text  
  5.        .align  2  
  6.        .global _start  
  7. _start:  
  8.        ldr     r1, =msg         @ address  
  9.        mov     r0, #1          @ stdout  
  10.        mov     r2, #13         @ length  
  11.        swi     #0x900004       @ sys_write  
  12.        mov     r0, #0  
  13.        swi     #0x900001       @ sys_exit  
  14.        .align  2  

  1. all:  
  2.     arm-linux-as helloworld.S -o helloworld.o  
  3.     arm-linux-ld  helloworld.o -o helloworld  
将elf文件放到跑有linux的arm板子中,运行就输出hello world.也可以在Ubuntu中qemu-arm helloworld模拟.

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

  1. .data  
  2.     msg: .string "hello\n"  
  3.     len = . - msg  
  4. .text  
  5. .global _start  
  6.   
  7. _start:  
  8.     nop  
  9.     movl $len, %edx  
  10.     movl $msg, %ecx  
  11.     movl $1, %ebx  
  12.     movl $4, %eax  
  13.     int $0x80  
  14.   
  15.     movl $0, %ebx  
  16.     movl $1, %eax  
  17.     int $0x80  
           它们有几点不同:

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

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


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

linux
【内容导航】
第1页:开篇 第2页:汇编编译链接与运行
第3页:ARM处理器的基本原则 第4页:中断汇编之非嵌套中断处理
第5页:中断汇编之嵌套中断处理 第6页:s3c2440的时钟控制
第7页:s3c2440的串口控制 第8页:s3c2440的watchdog
第9页:s3c2440的PWM 第10页:s3c2440的RTC
第11页:小结 第12页:arm汇编指令的B真的那么简单吗?
第13页:GNU ARM汇编下的linker script 第14页:GNU ARM汇编下做任务调度
第15页:Linux下的printascii 第16页:bootloader与kernel之间的commandline的传递
第17页:u-boot的makefile和mkconfig解读 第18页:u-boot-采用nand_spl方式的启动方法
第19页:u-boot-nand-spl启动过程分析 第20页:总结
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款