目录

嵌入式八股C语言-文件,可执行文件的加载与运行篇

嵌入式八股C语言—文件,可执行文件的加载与运行篇

C语言文件操作

  • fopen
  • fseek
  • fread
  • fwrite
  • fclose

可执行文件

  • 可执行文件的格式

    在裸机环境下,得到的是HEX/BIN格式的文件,而使用操作系统时,操作的往往是ELF格式的文件

    BIN/HEX文件是纯指令文件,没有其他杂七杂八的辅助信息,BIN文件最纯粹,而HEX文件会有一些附加的信息比如校验什么的;而ELF文件除了基本的代码段、数据段,还有文件头、符号表、program header table等用来辅助程序运行的信息

  • 裸机环境下程序的运行

    在ARM架构下 bin文件的头部的第一个存放的是SP指针的值 第二个存放的是RestHandler的地址 这样的话一上电SP指针和PC指针得到了相关的值就知道怎么运行了,同时在进入main函数之前会有一个__main函数,__main函数解析了bin文件中关于ZI段,数据段等信息,并对相应的SRAM内存做了操作,比如分配堆/栈,清空ZI段,加载数据段等等

  • ELF的加载与运行

    ELF通过加载器加载到内存当中

    https://i-blog.csdnimg.cn/direct/bc694a12e5444641b3a960b2634bee8f.png

    .text段:代码段

    .bss段: 未初始化的全局变量(实际上此时只记录了符号 并没有分配内存,只有使用的时候才会加载)

    .data段: 初始化的全局变量

    .rodata段: 字符串/const修饰的常量

    .elf headler:记录了一些关键信息不可缺少

  • 可执行文件加载的过程

    如何把一个可执行文件转换为进程呢

    https://i-blog.csdnimg.cn/direct/ee7f1dd28d3b48ba80e7e56b63cc4fe9.png

  • 静态链接

    静态链接就是在程序链接的过程中 就把引用的函数和变量编入可执行文件里面了 这样做的话会大大增加可执行文件的体积,并且假设多个文件都使用了printf 每个人一个副本的话也 浪费内存 ,关键是C语言 是以文件为基本单位的 ,所以实际上多的不止printf而是整个的xx.c文件

    • ar命令可以用来生成静态库
    • gcc -L -l用来指定链接的库
  • 动态链接

    动态链接就是为了解决静态链接的缺点而存在的 同样的动态链接有一个动态链接器。

    动态链接器通过解析ELF,做这样几件事

    1. 对动态链接库进行 重定位 :调整库内代码和数据的地址以适应实际加载地址的过程

    2. 对用到动态库的符号重定位( 更新GOT表 ):(也不知道动态库被加载到哪里了 所以动态库加载完毕后,需要重新找然后告诉程序更新了)

    3. 然后再把控制权交给程序本身

      https://i-blog.csdnimg.cn/direct/02087a9380a5400fa7914437e0832540.png

    • 与地址无关的代码与GOT表

      你不能每个进程都用printf就重新加载一遍动态链接库到对应进程,这不白操作了吗 我们希望的是虽然多个进程都是用了printf 但实际上printf的代码执被加载了一次,这个事情需要动态链接库和应用程序双方的努力。

      • 动态链接库:生成与地址无关的代码(比如偏移量靠绝对的)

      • 应用程序: GOT表 把使用动态链接库的符号都取出来,到时候动态库加载了就实时更新这个表

        https://i-blog.csdnimg.cn/direct/a070aa51a4bc4ec0bf54b452a486b24e.png

    • 延迟绑定

      没有必要一次性把所有动态库都加载到内存,用到谁就加载谁

    • 模块的加载流程

      1. 初始化 init_module–放在特殊的section段
      2. 拷贝到内核 copy_from_user
      3. 地址空间分配
      4. 符号解析
      5. 重定位
      6. 执行