目录
前提:在同一目录下运行,不然可能需要指明目录,没试过,读者可自行尝试
知识预备:shell编程小脚本
//终端1脚本: root@ubuntu:/mnt/U_share/caogao/ARM/swap$ cat gdbs.sh #! /bin/bash arm-linux-gcc $1.s -o $1.o -c -g arm-linux-ld $1.o -o $1.elf -Ttext=0x0 qemu-system-arm -machine xilinx-zynq-a9 -m 256M -serial stdio -kernel $1.elf -S -s //终端二脚本: #! /bin/bash arm-none-linux-gnueabi-gdb *.elf //也可将 *.elf 替换成 $1.elf
运行步骤:
终端1运行:
root@ubuntu:/mnt/U_share/caogao/ARM/swap$ bash gdbs.sh swap //swap为程序名,每个不一样,$1本质是替换,详情可查询Liunx的shell脚本的编写 //如果脚本用*.s .o .elf 那么需要将不同的程序置于不同的目录下,就可不写 第二个参数swap arm-linux-ld: warning: cannot find entry symbol _start; defaulting to 00000000
终端二运行:
root@ubuntu:/mnt/U_share/caogao/ARM/swap$ bash gdb.sh GNU gdb 6.8 Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "--host=i686-build_pc-linux-gnu --target=arm-cortex_a8-linux-gnueabi"... (gdb) target remote 127.0.0.1:1234 Remote debugging using 127.0.0.1:1234 //端口必须是1234,IP可回环,可本机IP [New Thread 1] 0x00000000 in a () Current language: auto; currently asm (gdb) l //显示程序 1 /*a=10,b=20 @:表示注释*/ 2 a: .word 0x10 @ int a[1]={0x10}; 3 b: .word 0x20 @ int b[1]={0x14}; 4 .global _swap 5 _swap: 6 ldr r0,=a @伪指令 r0 = a 7 ldr r1,[r0] @get a[0] value 8 ldr r2,=b @get b 9 ldr r3,[r2] @get b[0] 10 (gdb) l 11 str r3,[r0] 12 str r1,[r2] 13 nop (gdb) b 13 //设置断点为13行 Breakpoint 1 at 0x20: file swap.s, line 13. (gdb) c //运行至断点,可s一步一步运行 Continuing. Breakpoint 1, _swap () at swap.s:13 13 nop //作用:辅助延时,占位,设断点 (gdb) x/2 0x0 //从0地址开始看两个数据 0x0 <a>: 0x00000020 0x00000010 (gdb) q The program is running. Exit anyway? (y or n) y
显示寄存器的值: p $r0 p r0:看变量r0的值
x/n 0xy :查看从y地址开始的n个数据
1、交换2个内存数据
root@ubuntu:/mnt/U_share/caogao/ARM/swap$ cat swap.s /*a=10,b=20 @:表示注释*/ a: .word 0x10 @ int a[1]={0x10}; b: .word 0x20 @ int b[1]={0x14}; .global _swap _swap: ldr r0,=a @伪指令 r0 = a ldr r1,[r0] @get a[0] value ldr r2,=b @get b ldr r3,[r2] @get b[0] str r3,[r0] str r1,[r2] nop
运行结果:
(gdb) x/2 0x0 0x0 <a>: 0x00000010 0x00000020 (gdb) c Continuing. Breakpoint 1, _swap () at swap.s:13 13 nop (gdb) x/2 0x0 0x0 <a>: 0x00000020 0x00000010
2: 汇编完成 冒泡
buf: .word 0x33,0x55,0x44,0x11,0x22 @int buf[5]={0x33,...,0x22}; len: .word 4*4 .global _maopao _maopao: ldr r0,=buf @ get buf first addr mov r9,#16 @ get buf tail addr loop: ldr r1,[r0] ldr r2,[r0,#4] cmp r1,r2 strgt r2,[r0] strgt r1,[r0,#4] add r0,r0,#4 @r0 offest 4 bytes cmp r0,r9 blt loop ldr r0,=buf @r0--->buf sub r9,r9,#4 @tail - 4 cmp r0,r9 blt loop nop
运行结果:
(gdb) b 24 Breakpoint 1 at 0x50: file maopao.s, line 24. (gdb) x/5 0x0 0x0 <buf>: 0x00000033 0x00000055 0x00000044 0x00000011 0x10 <buf+16>: 0x00000022 (gdb) c Continuing. Breakpoint 1, loop () at maopao.s:24 24 nop (gdb) x/5 0x0 0x0 <buf>: 0x00000011 0x00000022 0x00000033 0x00000044 0x10 <buf+16>: 0x00000055
3:体验异常处理过程:
.global u_add .global u_sub .globl _start _start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq /*定义异常向量表的空指针,配合上面异常向量表进行相应的跳转,在后面可以改函数指针的指向,如13行*/ _undefined_instruction: .word _undefined_instruction _software_interrupt: .word swi_handler _prefetch_abort: .word _prefetch_abort _data_abort: .word _data_abort _not_used: .word _not_used _irq: .word _irq _fiq: .word _fiq reset: /* 设置cpu模式为SVC模式 */ /*寄存器的基本方式:先清再或,确保安全*/ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr,r0 /* 设置异常向量表起始地址、根据arm的api规定的格式*/ ldr r0, =_start mcr p15, 0, r0, c12, c0, 0 @Set VBAR /*用户需要设置的初始化*/ ldr sp, stacktop /*设置svc sp*/ sub r6, sp , #64 /*计算user需要指向的栈顶地址*/ /*切换到用户模式*/ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0x10 msr cpsr,r0 mov sp, r6 /*设置user sp*/ /*跳转到应用程序*/ bl _main _main: mov r0, #3 mov r1, #4 ldr r3,=mymem str r0,[r3] str r0,[r3,#4] bl u_add bl u_sub mov r3, r0 nop nop loop: b loop u_add: stmfd sp!, {lr} /*连同返回地址一起进入sp指向的栈,用于后续返回*/ ldr r0,=mymem mov r1,#0 str r1,[r0,#8] swi 0 /*产生软中断异常*/ ldr r1,[r0] ldmfd sp!, {pc} /*执行完出栈,根据lr返回*/ u_sub: stmfd sp!, {lr} swi 1 /*产生软中断异常*/ ldmfd sp!, {pc} sys_add: stmfd sp!, {lr} ldr r0,=mymem ldr r1,[r0] ldr r2,[r0,#4] add r3,r1,r2 str r3,[r0] ldmfd sp!, {pc} sys_sub: stmfd sp!,{lr} ldr r0,=mymem ldr r1,[r0] ldr r2,[r0,#4] sub r3,r1,r2 str r3,[r0] ldmfd sp!, {pc} /*swi 异常处理函数 处于svc模式*/ swi_handler: stmfd sp!, {r1-r12,lr} /*r1-r12:简写,r1到r12*/ /*区分中断,这里还能优化中断号*/ // ldr r0,=mymem /*将中断信号类型从内存中取出来*/ // ldr r1,[r0,#8] /*寄存器里面的编码*/ // sub r0,lr,#4 // ldr r1,[r0] //再次优化成一句指令 ldr r1,[lr,#-4] and r1,r1,#1 cmp r1,#0 /*调用真正的加、减法函数*/ bleq sys_add blne sys_sub ldmfd sp!, {r1-r12,pc}^ /*设置栈空间,一般是一片空间,然后自己规划每个模式所使用的栈空间*/ /*然后规定不同的栈顶指针,见34行*/ stack: .space 64*8 stacktop: .word stack+64*8 /*设置内存空间,进行寄存器数据的备份,从而解决寄存器的长时间占用*/ mymem: .space 64
简化版:
.global u_add .globl _start _start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq _undefined_instruction: .word _undefined_instruction _software_interrupt: .word swi_handler _prefetch_abort: .word _prefetch_abort _data_abort: .word _data_abort _not_used: .word _not_used _irq: .word _irq _fiq: .word _fiq reset: /* 设置cpu模式为SVC模式 */ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr,r0 /* 设置异常向量表起始地址 */ ldr r0, =_start mcr p15, 0, r0, c12, c0, 0 @Set VBAR /*用户需要设置的初始化*/ ldr sp, stacktop /*设置svc sp*/ sub r6, sp , #64 /*计算user需要指向的栈顶地址*/ /*切换到用户模式*/ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0x10 msr cpsr,r0 mov sp, r6 /*设置user sp*/ /*跳转到应用程序*/ bl _main _main: mov r0, #3 mov r1, #4 bl u_add mov r3, r0 nop nop loop: b loop u_add: stmfd sp!, {lr} swi 0 /*产生软中断异常*/ ldmfd sp!, {pc} sys_add: stmfd sp!, {lr} add r0,r0,r1 ldmfd sp!, {pc} /*swi 异常处理函数 处于svc模式*/ swi_handler: stmfd sp!, {r1-r12,lr} /*区分中断*/ /*调用真正的加法函数*/ bl sys_add ldmfd sp!, {r1-r12,pc}^ stack: .space 64*8 stacktop: .word stack+64*8
4:体验汇编到c的转换
//test.lds:链接文件,后缀一般为.lds /*ENTRY:程序的入口地址,可重新指定,不过不建议*/ ENTRY(_start) SECTIONS{ . = 0x0; /*代码段text是从0开始的*/ .text : { start.o(.text) /*可以指定顺序,如果不指定机器根据自己的安排进行各种.o文件的链接,这里注意:后一个链接的文件需要前一个文件链接过后的文件则需要指定顺序*/ *(.text) } .data : { *(.data) /*数据段,比如初始化的全局变量a的值等等*/ } .bss : { *(.bss) /*未初始化*/ } }
//makefile文件,类似最上面终端1的shell脚本,值得注意的是需要将-Ttext=0x0改为-Ttest.lds,勾连链接文件 all: arm-linux-gcc myadd.S -o start.o -c -g arm-linux-gcc main.c -o main.o -c -g #-Ttext=0x0:是因为我们安装的虚拟单片机text地址是从0开始的 #如果是真实的单片机,那么会改,如 -Ttext=0x100等 #-Ttest.lds 包含 -Ttext=0x0 arm-linux-ld start.o main.o -o start.elf -Ttest.lds //gdbS.sh文件 #!/bin/bash qemu-system-arm -machine xilinx-zynq-a9 -m 256M -serial stdio -kernel start.elf -S -s
/*myadd.S文件*/ .global u_add .globl _start _start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq _undefined_instruction: .word _undefined_instruction _software_interrupt: .word swi_handler _prefetch_abort: .word _prefetch_abort _data_abort: .word _data_abort _not_used: .word _not_used _irq: .word _irq _fiq: .word _fiq reset: /* 设置cpu模式为SVC模式 */ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr,r0 /* 设置异常向量表起始地址 */ ldr r0, =_start mcr p15, 0, r0, c12, c0, 0 @Set VBAR /*用户需要设置的初始化*/ ldr sp, stacktop /*设置svc sp*/ sub r6, sp , #64 /*计算user需要指向的栈顶地址*/ /*切换到用户模式*/ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0x10 msr cpsr,r0 mov sp, r6 /*设置user sp*/ /*跳转到应用程序*/ bl main /*将_main改为main*/ /*将mian转到c文件中,用c文件来编写之前汇编编写的mian函数*/ u_add: stmfd sp!, {lr} /* 机器自己做: 汇编的寄存器r0,r1,r2,r3接收参数 最多4个,多个压栈重用寄存器 我们需要管返回值 @c--->r0,然后将值返回给c语言 */ @a-->r0 b--->r1 ldr r3, =buf str r0, [r3] str r1, [r3,#4] mov r0, #0x11 //举例:r0可重用 mov r1, #0x22 //如上 swi 0 /*产生软中断异常*/ @c--->r0 ldr r3, =buf ldr r0, [r3,#8] /*取返回值*/ ldmfd sp!, {pc} sys_add: stmfd sp!, {lr} ldr r3, =buf ldr r0, [r3] ldr r1, [r3,#4] add r0,r0,r1 str r0, [r3,#8] mov r0, #0x55 ldmfd sp!, {pc} /*swi 异常处理函数 处于svc模式*/ swi_handler: stmfd sp!, {r0-r12,lr} /*区分中断*/ /*调用真正的加法函数*/ bl sys_add ldmfd sp!, {r0-r12,pc}^ buf: .space 64 stack: .space 64*8 stacktop: .word stack+64*8
//main.c //由于是裸机编程,所以并不包含C库,如使用printf等等 int u_add(int a, int b); //int u_sub(int a, int b); int main() { int a = 3; int b = 4; int c = 0; c = u_add(a,b); //c = u_sub(a,b); b = 5; while(1); return 0; }