• android ARM 汇编学习 —— hello world


    android ARM 汇编学习—— 在 android 设备上编译c/cpp代码并用objdump/readelf等工具分析

    adb putty 连上手机,用busybox vi 写一个 helloworld c 

    root@HM2014813:/data/local/tmp # busybox vi hello.c

    #include <stdio.h>

    int main(int argc, char* argv[])
    {
      printf("Hello ARM World ");
      return 0;
    }

    用 gcc 编译成汇编,并查看

    root@HM2014813:/data/local/tmp # gcc hello.c -S && cat ./hello.s

      .arch armv5te
            .fpu softvfp
            .eabi_attribute 20, 1
            .eabi_attribute 21, 1
            .eabi_attribute 23, 3
            .eabi_attribute 24, 1
            .eabi_attribute 25, 1
            .eabi_attribute 26, 2
            .eabi_attribute 30, 6
            .eabi_attribute 34, 0
            .eabi_attribute 18, 4
            .file   "hello.c"
            .section        .rodata
            .align  2
    .LC0:
            .ascii  "Hello ARM World00"
            .text
            .align  2
            .global main
            .type   main, %function
    main:
            @ args = 0, pretend = 0, frame = 8
            @ frame_needed = 1, uses_anonymous_args = 0
            stmfd   sp!, {fp, lr}
            add     fp, sp, #4
            sub     sp, sp, #8
            str     r0, [fp, #-8]
            str     r1, [fp, #-12]
            ldr     r3, .L3
    .LPIC0:
            add     r3, pc, r3
            mov     r0, r3
            bl      puts(PLT)
            mov     r3, #0
            mov     r0, r3
            sub     sp, fp, #4
            @ sp needed
            ldmfd   sp!, {fp, pc}
    .L4:
            .align  2
    .L3:
            .word   .LC0-(.LPIC0+8)
            .size   main, .-main
            .ident  "GCC: (GNU) 4.8.2"
            .section        .note.GNU-stack,"",%progbits

    下面开始分析:

    .arch armv5te
    .fpu softvfp
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 2
    .eabi_attribute 30, 6
    .eabi_attribute 34, 0
    .eabi_attribute 18, 4
    .file "hello.c"

    开始这几行是给 ARM cpu 的声明,.arch 指明体系架构类型, .fpu 指明 Floating Point Unit  浮点运算单元的运算模式,接下去几行是  ARM Embedded Application Binary Interface EABI 的 option, 最后一行指明文件名称

     .section        .rodata
            .align  2
    .LC0:
            .ascii  "Hello ARM World00"

    c code 里的字符串 "Hello ARM World " 编译后存入 .rodata section 。

     .text
            .align  2
            .global main
            .type   main, %function
    main:

    上面这几行是函数声明,在 .text section , 函数名是 main

    .LC0:
            .ascii  "Hello ARM World00"  ; label .LC0 对应的内存地址存放字符串,或者说 .LC0 存放的是字符串的首地址
            .text
            .align  2
            .global main
            .type   main, %function

    stmfd sp!, {fp, lr} ; 压栈,栈向地地址生长,sp 保存着栈顶地址, fp 是帧指针,lr 是返回地址
                   ; fp : 在C程序编译过程中,函数局部变量被分配在一个连续存储区内,这个连续存储区就是该函数的帧
                   ; 这句指令效果是,帧指针被保存在栈顶位置,lr 指针被保存在地址为 (fp - 4Byte) 的位置上(因为一条指令是4Byte),然后sp生长2个指针长度,即往低地址移动 8 个字节

    add fp, sp, #4 ; fp = sp + 4 sub sp, sp, #8 ; sp = sp - 8, 这一句和接下来给 r0,r1赋值的两句都没看懂 str r0, [fp, #-8] str r1, [fp, #-12] ldr r3, .L3 ; 将 label .L3 的值赋给 r3 , .L3 是一个label, 可以理解为一个地址, 这个地址是这么算的 .LC0-(.LPIC0+8)               ; .LC0 是要打印的字符串的地址, .LPIC0 其实是指令 ‘add r3,pc,r3’ 的地址, +8 即往前越2条指令,所以其实是指令 'bl puts(PLT)' 的地址,即调用printf函数的地址
                  ; 结合起来看, .L3 保存的是要打印的字符串的地址和跳转指令的地址的 offset, 这个 offset 的作用是? 这个 offset 加上 pc 之后就是字符串的地址
    .LPIC0: add r3, pc, r3 ; ArmV5 的流水线是3,意味着执行这条指令的时候,pc 的值指向的指令是 bl puts(PLT), r3 即 .L3 , 加起来就是要打印的字符串的地址 mov r0, r3 ; 将字符串地址保存在 r0 bl puts(PLT) ; 打印 mov r3, #0 mov r0, r3 ; return 0 sub sp, fp, #4 @ sp needed ldmfd sp!, {fp, pc} ; 恢复pc寄存器,跳出函数
    .L4:
            .align  2
    .L3:
            .word   .LC0-(.LPIC0+8)
            .size   main, .-main
            .ident  "GCC: (GNU) 4.8.2"
            .section        .note.GNU-stack,"",%progbits

    上面这个过程比较难理解的是,传给printf的字符串的地址是动态算出来的,  .LC0-(.LPIC0+8) + pc ,为什么这么做,网上找了个大概靠谱的解释:http://tieba.baidu.com/p/2983668645

    ARM立即数在我的印象里最大是4096再大就只能相对寻址,显然所有的指针都只能间接寻址,也就是需要一个内存来存地址那就是L3。。。。L3是个常量,存LPIC0到字符串LC0的相对位置,只要PC+该相对位置就是字符串位置,因为ARM9是3级流水线一次区取条指令,你当前的PC位置并不是紧接的下一条,而是下3条的位置,通常单片机是1级流水线当前PC就是下条的位置,不用加减任何数,但ARM9需要+8 如果是ARM11的五级流水线加的更多。。。。
    ARM不能像单片机那样,想取某个标签地址,就可以 mov r1,#标签 因为ARM立即数寻址有限制。。。所有的指针都会超过限制,所以会用另一种方式直接算出寻址位置的地址和
    全局变量位置的相对地址,在调用时用PC+相对地址即可 但要 PC+(相对地址-8), 因为
    仪的在减号后面的括号里面所以 按结合律规则 -8编程+8

    参考 : 

    Android ARM Assembly: Registers, Memory and Addressing Modes

    ARMv7匯編代碼分析

  • 相关阅读:
    递归函数的原理
    html布局-子div浮动后,父容器撑不开解决
    权限设计——控制显示字段——设计思考
    winform上传文件到服务器——资料整理
    windows server自动化发布——技术积累与整理
    Docker——概念学习
    javascript中的var,let,const关键字
    Web前端面试图
    上传与预览图片
    每个程序员都应该知道延迟数—Latency Numbers Every Programmer Should Know
  • 原文地址:https://www.cnblogs.com/jiayy/p/4365852.html
Copyright © 2020-2023  润新知