前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C程序是如何跑起来的01 —— 可执行文件的构成

C程序是如何跑起来的01 —— 可执行文件的构成

作者头像
Mculover666
发布2022-09-26 15:00:23
6710
发布2022-09-26 15:00:23
举报
文章被收录于专栏:TencentOS-tiny

学习目的

  • 程序烧到什么地方?
  • 程序加载到内存什么地方?
  • 程序如何执行?

一、编译环境搭建

ubuntu 20.04 使用 arm-linux-gnueabihf-gcc 7.5.0。

二、程序源码

main.c:

代码语言:javascript
复制
#include <stdio.h>
#include "calc.h"

int main(int argc, char *argv[])
{
    int a, b;
    static int local_val = 2;
    static int uninit_local_val;

    a = add(2, 3);
    b = sub(5, 4);

    printf("a = %d\n", a);
    printf("b = %d\n", b);

    return 0;
}

calc.h:

代码语言:javascript
复制
#ifndef _CALC_H_
#define _CALC_H_

int add(int a, int b);
int sub(int a, int b);

#endif

calc.c:

代码语言:javascript
复制
#include "calc.h"

int add(int a, int b)
{
    return a + b;
}

int sub(int a, int b)
{
    return a - b;
}

编译:

代码语言:javascript
复制
arm-linux-gnueabihf-gcc main.c calc.c

交叉编译生成 a.out 可执行文件,文件类型是 32 位 ARM 平台可执行文件。

三、readelf 工具

readelf 工具由编译器提供,用来列出关于可执行文件的内容的相关信息

使用格式如下:

代码语言:javascript
复制
Usage: readelf <option(s)> elf-file(s)

(1)查看可执行文件的头部 信息

  • -h:用于列出 ELF 文件的头部信息,包括可执行文件运行的平台、软件版本、程序入口地址,以及 program headers、section header 等信息;

(2)查看 section header

  • -S:用于列出程序中 section 的头部信息

四、可执行文件的组成结构

一个可执行文件由一系列 section 构成,section 称为段,包括:代码段 text、只读数据段 rodata、数据段 data、bss 段等。

每个 section 用一个section header描述,包括段名、段的类型、段的起始地址、段的偏移、段的大小等。

将可执行文件的所有 section header 集合到一起就是 section header table,使用 readelf 的 -S 参数查看的就是该表。

在程序编译的时候,对 C 语言代码中定义的函数、变量、未初始化的全局变量进行编译分类,放置在不同的段中:

  • 普通代码翻译成二进制放到代码段(text)中
  • 常量放在只读数据段(rodata)中
  • 初始化的全局变量和静态局部变量放在数据段(data)中

BSS 段比较特殊,未初始化的全局变量和静态变量都会放置到 bss 段中,但因为这些变量的值都是 0,没有必要再开辟空间存储,所以在可执行文件中 bss 段是不占用空间的。

但是 BSS 段的大小、起始地址、各个变量的地址信息都会分别保存在 section header table 和符号表 symtab 中,当程序运行的时候,加载器会根据这些信息在内存中紧挨着数据段之后的空间,为 BSS 段开辟一片存储空间,为各个变量分配存储空间。

总而言之:BSS 段在可执行文件中不占用空间,在程序运行的时候才分配对应的空间

如果在编译时开启了调试选项,则可执行文件中还会有 .debug section,用来保存可执行文件中每一条二进制指令对应的源码位置信息,根据这些信息,GDB 调试器就可以支持源码级的单步调试。

在最后环节,编译器还会在可执行文件中添加一些其它的 section,比如 .init section,这些代码来自 C 语言运行库的一些汇编代码,用来初始化 C 程序所依赖的环境。

参考资料

  • 嵌入式 C 语言自我修养 从芯片、编译器到操作系统
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-07-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Mculover666 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 学习目的
  • 一、编译环境搭建
  • 二、程序源码
  • 三、readelf 工具
  • 四、可执行文件的组成结构
  • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档