程序是如何运行的——基于linux-ARM架构

  本文基于arm架构,通过一些简单的C语言demo,编译成汇编语言,通过对汇编语言进行解析,从而解释程序是如何在cpu核上运行的。
  arm各个模式寄存器如下图:
在这里插入图片描述
  本文不对arm寄存器进行详细的解释。具体可度娘关键词“arm寄存器
  linux内核工作在svc模式,应用程序工作在User模式。本文demo都为linux应用程序,所以都工作在User模式下。

基本概念介绍:

1,R0-R12是通用寄存器,放通用数据、临时数据,然后每个寄存器都是32位的。
2,各个模式的R0到R12与USR模式是共享的(除了FIQ,R8-R12),PC,CPSR是共享的。
3,USR模式没有SPSR

4、r13:sp 用于指向不同模式的栈顶。栈,每种模式都需要开辟一块内存,用于在该模式下 函数调用,临时分配的数据存放在此处,
5、r14 : lr 程序跳转的时候,返回地址保存到此处
7、r15 :pc 要执行的西一条指令地址,就存放在此处,每次指令执行完,就自动+4

7、CPSR:程序状态寄存器。程序执行的时候,有很多临时标记位,结果是0 是否溢出,是否有借位,是否有 进位,当前cpu模式,
8、SPSR:用于模式切换,将切换前的 cpsr 保存到 新的模式的 spsr,模式切换回去的时候,再将spsr的内容还原到cpsr。

程序示例:

编译环境:ubuntu16.04
交叉编译工具链:arm-linux-gnueabi-gcc(gcc version 4.9.4 (Linaro GCC 4.9-2017.01) )
编译执行arm-linux-gnueabi-gcc -S demo.c,就能生成demo.s了。

demo1

源码:

#include <stdio.h>

int main()
{
        return 0;
}

汇编:
(后文将省略.syntax unified此类代码)

        .syntax unified
        .arch armv7-a
        .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, 1
        .eabi_attribute 18, 4
        .thumb
        .file   "demo1.c"
        .text
        .align  2
        .global main
        .thumb
        .thumb_func
        .type   main, %function
main:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 1, uses_anonymous_args = 0
        @ link register save eliminated.
        push    {r7}											(1)
        add     r7, sp, #0										(2)
        movs    r3, #0											(3)
        mov     r0, r3											(4)
        mov     sp, r7											(5)
        @ sp needed
        pop     {r7}											(6)
        bx      lr												(7)
        .size   main, .-main
        .ident  "GCC: (Linaro GCC 4.9-2017.01) 4.9.4"
        .section        .note.GNU-stack,"",%progbits

(1)r7寄存器中的数据入栈(此时r7中的数值为跳转到当前main函数前的值,和当前程序无关,需要先暂存到栈中)
(2)将SP指针地址赋值给r7,意思为r7 = sp + 0
(3)将0赋值给r3
(4)将r3赋值给r0,r0中存的函数返回值(main函数),r0 = r3 = 0,函数返回值为0。
(5)将r7的地址重新返还给SP指针。
(6)出栈,把内存中的数据赋值给r7(恢复程序运行前的r7值)
(7)PC跳转到LR所在地址执行代码(LR中保存了运行demo1前的PC寄存器地址)

demo2(函数临时变量)

源码:

#include <stdio.h>

int main()
{
        int i = 0;
        int j = 1;
        return 0;
}

汇编:

......
main:
        @ args = 0, pretend = 0, frame = 8
        @ frame_needed = 1, uses_anonymous_args = 0
        @ link register save eliminated.
        push    {r7}
        sub     sp, sp, #12										(1)
        add     r7, sp, #0
        movs    r3, #0											(2)
        str     r3, [r7, #4]									(3)
        movs    r3, #1											(4)
        str     r3, [r7]										(5)
        movs    r3, #0
        mov     r0, r3
        adds    r7, r7, #12										(6)
        mov     sp, r7
        @ sp needed
        pop     {r7}
        bx      lr
        .size   main, .-main
        .ident  "GCC: (Linaro GCC 4.9-2017.01) 4.9.4"
        .section        .note.GNU-stack,"",%progbits

(1)SP寄存器存的地址-12。(arm栈向下生长,等于空出12字节空间)
(2)r3做临时变量,将r3赋值为0(i=0)
(3)r3中的数据存到r7+4所在内存地址中。
(4)r3做临时变量,将r3赋值为1(i=0)
(5)r3中的数据存到r7所在内存地址中。
在这里插入图片描述
(6)main函数执行完毕,r7地址+12,即恢复函数初始的SP指针地址。
其余操作都和demo1中相同。

demo3(静态变量、全局变量)

源码:

#include <stdio.h>

static int x = 3;
int y = 4;

int main()
{
        static int k = 2;

        k = k + 1;
        x = x + 1;
        y = y + 1;

        return 0;
}

汇编:

......
        .data
        .align  2
        .type   x, %object
        .size   x, 4
x:
        .word   3
        .global y
        .align  2
        .type   y, %object
        .size   y, 4
y:
        .word   4
        .text
        .align  2
        .global main
        .thumb
        .thumb_func
        .type   main, %function
main:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 1, uses_anonymous_args = 0
        @ link register save eliminated.
        push    {r7}
        add     r7, sp, #0
        movw    r3, #:lower16:k.4588						(1)
        movt    r3, #:upper16:k.4588						(2)
        ldr     r3, [r3]									(3)
        adds    r2, r3, #1									(4)
        movw    r3, #:lower16:k.4588						(5)
        movt    r3, #:upper16:k.4588						(6)
        str     r2, [r3]									(7)
        movw    r3, #:lower16:x
        movt    r3, #:upper16:x
        ldr     r3, [r3]
        adds    r2, r3, #1
        movw    r3, #:lower16:x
        movt    r3, #:upper16:x
        str     r2, [r3]
        movw    r3, #:lower16:y
        movt    r3, #:upper16:y
        ldr     r3, [r3]
        adds    r2, r3, #1
        movw    r3, #:lower16:y
        movt    r3, #:upper16:y
        str     r2, [r3]
        movs    r3, #0
        mov     r0, r3
        mov     sp, r7
        @ sp needed
        pop     {r7}
        bx      lr
        .size   main, .-main
        .data
        .align  2
        .type   k.4588, %object
        .size   k.4588, 4
k.4588:
        .word   2
        .ident  "GCC: (Linaro GCC 4.9-2017.01) 4.9.4"
        .section        .note.GNU-stack,"",%progbits

  我们都知道,C语言全局变量、静态变量存放在全局存储区(静态存储区),这里可以看到代码中的X、Y、K并没有存放在栈中,而是存在某一内存地址,所有函数执行完,并不会随着栈的变化而消失。
(1)MOVW 把 16 位立即数放到寄存器的底16位,高16位清0
(2)MOVT 把 16 位立即数放到寄存器的高16位,低 16位不影响((1)(2)等于将变量K所在内存地址赋值给r3)
(3)r3当前存储的为内存地址,取内存地址中的数据赋值给r3(存储地址->存储地址中的数据)
(4)做加法,结果存放在r2中。
(5)(6)重复1、2步骤,等于将变量K所在内存地址赋值给r3
(7)将r2的值存储到r3代表的内存地址内,即将改变后的k值重新存储回k所在的内存。

demo4(函数)

源码:

#include <stdio.h>

static void test2(void)
{
}

static void test1(void)
{
        test2();
}

int main()
{
        test1();
        return 0;
}

汇编:

......
test2:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 1, uses_anonymous_args = 0
        @ link register save eliminated.
        push    {r7}										(5)
        add     r7, sp, #0
        mov     sp, r7
        @ sp needed
        pop     {r7}
        bx      lr
        .size   test2, .-test2
        .align  2
        .thumb
        .thumb_func
        .type   test1, %function
test1:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 1, uses_anonymous_args = 0
        push    {r7, lr}									(3)
        add     r7, sp, #0
        bl      test2										(4)
        pop     {r7, pc}									(6)
        .size   test1, .-test1
        .align  2
        .global main
        .thumb
        .thumb_func
        .type   main, %function
main:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 1, uses_anonymous_args = 0
        push    {r7, lr}									(1)
        add     r7, sp, #0
        bl      test1										(2)
        movs    r3, #0
        mov     r0, r3
        pop     {r7, pc}									(7)
        .size   main, .-main
        .ident  "GCC: (Linaro GCC 4.9-2017.01) 4.9.4"
        .section        .note.GNU-stack,"",%progbits

(1)和demo123中不同,这里不止要将r7入栈,还要将lr入栈(lr中保存函数返回的地址,因为main函数中调用别的函数,在调用别的函数时,pc跳转到test1前会将pc修改,所以要事先存储main函数返回地址LR)
(2)pc跳转到test1函数执行。
(3)同(1)步骤(test1中也调用了函数(test2)所以lr也需要入栈存储)
(4)跳转到test2中执行
(5)test2中没有包含任何函数,所以不需要存储lr地址,函数执行完pc跳转lr地址即可。
(6)恢复r7,原来lr寄存器的地址直接赋值给pc,相当于直接跳转(省去先恢复到lr,pc再跳转lr地址的过程)
(7)同(6)操作
注意:内联函数(inline)在编译时会展开,就会省去bl test1过程,而是直接在当前函数中操作内联函数中的命令。

总结

  暂时就写这么多,有空想到了再补。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 嵌入式系统设计与应用是指在特定应用领域中,使用嵌入式处理器和相关硬件资源,结合嵌入式操作系统和软件开发工具,设计和开发满足特定需求的嵌入式系统。 ARM Cortex-A8是一款高性能的嵌入式处理器,广泛应用于手机、平板电脑、智能电视等嵌入式设备中。它具有强大的计算能力和低功耗特性,能够提供快速、高效的数据处理和多任务处理能力。 Linux是一种开源的嵌入式操作系统,提供了丰富的软件资源和开发工具,可以满足各种嵌入式应用的需求。在使用ARM Cortex-A8和Linux进行嵌入式系统设计和应用时,可以借助Linux的强大功能和丰富的软件生态系统,快速开发出满足特定需求的嵌入式应用。 设计和开发嵌入式系统时,首先需要选择合适的硬件平台和操作系统。选择ARM Cortex-A8作为处理器可以得到高性能和低功耗的优势,而选择Linux作为操作系统可以借助其丰富的软件资源和开发工具。 然后,根据具体的嵌入式应用需求,对系统进行架构设计和软件模块划分。在嵌入式系统设计中,需要考虑系统的实时性、功耗控制、硬件接口与外设的驱动、应用程序的开发等方面。 在应用开发阶段,可以使用C/C++等编程语言,结合相应的开发工具,编写应用程序和驱动程序。同时,可以借助Linux的丰富资源,如网络协议栈、文件系统、数据库等,快速实现系统的功能。 最后,在系统调试和测试阶段,可以使用调试工具和仿真平台进行系统性能测试和调试,以确保系统的稳定性和可靠性。 综上所述,嵌入式系统设计与应用基于ARM Cortex-A8和Linux可以提供高性能、低功耗和丰富软件资源的优势,能够快速开发出满足特定需求的嵌入式应用。 ### 回答2: 嵌入式系统设计与应用是指将计算机系统嵌入到特定的电子设备中,以完成特定的功能。基于ARM Cortex-A8和Linux的嵌入式系统设计与应用是指利用ARM Cortex-A8处理器和Linux操作系统来设计和开发嵌入式系统。 ARM Cortex-A8是一种高性能、低功耗的32位RISC处理器。它采用精简指令集架构,具有较高的运算能力和较低的能耗。Cortex-A8处理器广泛应用于嵌入式领域,可用于智能手机、平板电脑、汽车导航系统等各种嵌入式设备。 Linux是一种开源的操作系统内核,具有广泛的硬件支持和强大的软件生态系统。在嵌入式系统设计中,Linux提供了丰富的功能和驱动支持,能够提供稳定可靠的操作环境。同时,Linux还可以方便地进行定制和扩展,以满足各种应用需求。 在基于ARM Cortex-A8和Linux的嵌入式系统设计中,我们可以利用Linux提供的运行时库、工具链以及开发环境来进行系统开发。可以利用C/C++编程语言来进行应用程序的开发,使用Linux提供的设备驱动程序来进行硬件的控制和交互。同时,我们还可以利用Linux的网络支持和文件系统功能来实现网络连接和数据存储。 综上所述,基于ARM Cortex-A8和Linux的嵌入式系统设计与应用具有高性能、低功耗、可定制和可扩展等优势,可以适用于各种嵌入式设备的开发和应用。它在智能手机、平板电脑、汽车导航系统等领域具有广泛的应用前景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值