zoukankan      html  css  js  c++  java
  • arm平台下使用bl和ldr跳转应当注意的地方

    arm平台下使用bl和ldr跳转应当注意的地方(arm-linux-gcc环境)

    发布时间:2008-01-02 02:23:00  来源: ChinaUnix博客    作者: ChinaUnix博客    点击:949

         arm平台下使用bl和ldr跳转应当注意的地方(arm-linux-gcc环境)
             作者 :  MSN:
    panhuachun@hotmail.com

    一,按lds文件连接的不同模块,不能用bl实现跳转
    一个错误的例子:
    1.crt0.s
    @******************************************************************************
    @ File:crt0.s
    @ 功能:通过它转入C程序
    @******************************************************************************       
    .extern  main
    .text
    .global _start
    _start:
        ldr  sp, =1024*4 @设置堆栈,注意:不能大于4k ,
            @这儿堆栈可以设置为0x34000000,根据内存地址空间分配确定
             
       bl  main         @调用C程序中的main函数
    halt_loop: 
       b  halt_loop
    2.leds.c
    @******************************************************************************
    @ file leds.c
    @ main函数
    @******************************************************************************    
    int main()
    {
           __asm__
           ( 
                 " ldrb  r0, [r1], #1\n"
                 " strb  r0, [r2], #1\n"
           );
    return 0;
    }
    3.Makefile
    CFLAGS  := -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -ffreestanding -c
    leds : crt0.s  leds.c
    arm-linux-gcc $(CFLAGS) -o crt0.o crt0.s
    arm-linux-gcc $(CFLAGS) -o leds.o leds.c
    arm-linux-ld -Tleds.lds  crt0.o leds.o -o leds_tmp.o
    arm-linux-objcopy -O binary -S leds_tmp.o leds
    arm-linux-objdump -D -b binary -m arm  leds >ttt.s
    clean:
    rm -f   leds
    rm -f   leds.o
    rm -f   leds_tmp.o
    rm -f   crt0.o
    4.leds.lds 文件
    SECTIONS { 
      firtst   0x00000000 : { crt0.o }
      second  0x00000000 :  AT(0x0100) { leds.o }

    5.反汇编代码ttt.s
    00000000 :
       0: e3a0da01  mov sp, #40Array6 ; 0x1000
       4: ebfffffd  bl 0x0                      //这儿不能调转到main 因为bl跳转有限制
       8: eafffffe  b 0x8
    ...
    100: e24dd040  sub sp, sp, #64 ; 0x40
    104: e3a00000  mov r0, #0 ; 0x0
    108: e28dd040  add sp, sp, #64 ; 0x40
    10c: e1a0f00e  mov pc, lr
    110: 43434700  cmpmi r3, #0 ; 0x0
    114: 4728203a  undefined
    118: 202Array554e  eorcs r5, rArray, lr, asr #10
    11c: 2e332e33  mrccs 14, 1, r2, cr3, cr3, {1}
    120: 00000032  andeq r0, r0, r2, lsr r0
    通过上面的例子可以看到crt0中的bl main出错
    "4: ebfffffd  bl 0x0   "
    bl没有成功。                   
    6.改正方法1:
    原lds文件把俩个目标文件分开排列,这里把俩个目标文件指定到一起,这样不能重定位。
    修改后的lds文件
    SECTIONS { 
      firtst   0x00000000 : { crt0.o leds.o  }
      

    改正后的效果
        0: e3a0da01  mov sp, #40Array6 ; 0x1000
       4: eb000000  bl 0xc                  //这里bl跳转到正确的地址
       8: eafffffe  b 0x8
       c: e24dd040  sub sp, sp, #64 ; 0x40
      10: e3a00000  mov r0, #0 ; 0x0
      14: e28dd040  add sp, sp, #64 ; 0x40
      18: e1a0f00e  mov pc, lr
      1c: 43434700  cmpmi r3, #0 ; 0x0
      20: 4728203a  undefined
      24: 202Array554e  eorcs r5, rArray, lr, asr #10
      28: 2e332e33  mrccs 14, 1, r2, cr3, cr3, {1}
      2c: 00000032  andeq r0, r0, r2, lsr r0
    二,使用ldr命令来实现长跳转(改正方法2)
    1.
    ldr pc,      =main  @调用C程序中的main函数
    通过ldr 对pc赋值来实现跳转
    @******************************************************************************
    @ File:crt0.s
    @ 功能:通过它转入C程序
    @******************************************************************************       
    .extern  main
    .text
    .global _start
    _start:
        ldr  sp, =1024*4 @设置堆栈,注意:不能大于4k,nand flash中的代码在复位后会移到内部ram中,此ram只有4k
       ldr pc,      =main  @调用C程序中的main函数
    halt_loop:
       b  halt_loop
    2.leds.lds文件
    SECTIONS { 
      firtst   0x00000000 : { crt0.o }
      second  0x30000000 :  AT(0x1000) { leds.o }


    3.反汇编结果
    00000000 :
           0: e3a0da01  mov sp, #40Array6 ; 0x1000
           4: e5Arrayff000  ldr pc, [pc, #0] ; 0xc
           8: eafffffe  b 0x8
           c: 30000000  andcc r0, r0, r0
    ...
        1000: e4d10001  ldrb r0, [r1], #1
     
     
     
     
     
     
     
     
     
     
     
     
    地址无关: 编译地址不等于运行地址.
    地址相关: 编译地址等于运行地址.

    常见的一些Boot(如, U-Boot, VIVI)和Linux Kernel代码开始的一段是位置无关的, 意思就是说运行地址与编译地址无关. 如, Kernel编译地址是0xc0008000, 而运行地址是0x30008000.
    为什么?
    为什么代码的编译地址和运行地址会不相等呢? 原因主要有以下几种: 1) 对于Boot, 用于存放Boot代码的存储器容量小于代码量. 如, Boot片有4K, 而代码通常有50-60K. 这样, 通常会在前4K代码里, 让Boot把自己复制到RAM, 再接着运行.这里我们需要作出一个选择, 是让前面的代码与地址相关, 还是让后面的代码与地址相关呢? 显然我们会选择前面一段代码量小的与地址无关. 2) 对于Linux Kernel, 它是运行在虚拟地址空间的, 如0xc0008000, 但在MMU打开之前, 通常这个地址是
    不存在的, 也就是说在MMU打开之前, Kernel的代码必须是地址无关的.
    怎么办?
    对于位置无关的代码, 寻址是基于pc值的, 在pc值上+/-一个偏移值, 得到运行地址.以ARM为例, 用adr来寻址, adr的实际上是一个宏指令, 在代码编译时, 会被编译器替换成对pc的+/-运算
    这里要注意, 对pc的+/-运行显然是有一个地址范围的, 所以我们在上面选择代码量小的地址无关, 是很明智的.

    而访问地址相关的代码, 只需要使用其它的寻址指令就行了. 但在这之前, 必须保证代码被放在正确的地址上, 所以通常都会有一个复制代码的过程, 然后就是跳转到一个标号, 地址相关代码就开始运行了
  • 相关阅读:
    jvm
    java8新特性Lambada,Steam流
    数组链表栈队列 散列表
    数据结构算法基本知识
    设计模式七大原则
    java关键字
    Excel导出(适合项目开发)
    Excel导出(适合初学者)
    angular.min.js:80 Error:
    angular中出现错误的提示指令[ng:areq]的原因
  • 原文地址:https://www.cnblogs.com/shenlian/p/2269341.html
Copyright © 2011-2022 走看看