百篇博客系列篇.本篇为:

编译构建相关篇为:

一个.c源文件编译的整个过程如图.

编译过程要经过:源文件 --> 预处理 --> 编译(cc1) --> 汇编器(as) --> 链接器(ld) --> 可执行文件(PE/ELF)

GCC

GCC(GNU Compiler Collection,GNU编译器套件),官网:https://gcc.gnu.org/ ,是由 GNU 开发的编程语言编译器

  • GCC源码仓库:https://github.com/gcc-mirror/gcc 有兴趣的可以去阅读源码.
  • GCC用法
    gcc [options] infile
    -o 重定位输入文件位置;
    -E 只对源文件进行预处理,输出.i文件;
    -S 对源文件进行预处理,编译,输出.s文件;
    -c 对源文件进行预处理,编译,汇编,输出.o文件;
    -I 包含头文件路径,如 g++ -Iopencv/include/;
    -L 包含库文件路径,如 g++ -Lopencv/lib/ ;
    -l 链接库文件,如链接lib.so:g++ -llib;
    -shared 编译.so库;
    -fPIC 生成位置无法码;
    -Wall 对代码有问题的地方发出警告;
    -g 在目标文件中嵌入调试信息,便于gdb调试;

本篇以 main.c 文件举例,说明白两个问题

  • main.c是怎么编译的? 详细的整个过程是怎样的,是如何一步步走到了可执行文件的.
  • main.c中的代码,数据,栈,堆是如何形成和构建的? 通过变量地址窥视其内存布局.
  • 这两部分内容将掺杂在一起尽量同时说明白,main.c文件它将经历以下变化过程

    main.c --> main.i --> main.s --> main.o --> main

插播 case_code_100

case_code_100 是百篇博客分析过程中用到的案例汇总,其中可能是代码,也可能是一些文章,序号与博客序号一一对应.仓库地址: https://gitee.com/weharmony/case_code_100,本篇为 57

源文件 | main.c

#include <stdio.h>
#include <stdlib.h>
#define HARMONY_OS "hello harmonyos \n"
const int g_const = 10; //全局常量区
int g_init = 57; //全局变量
int g_no_init; //全局变量无初值
static int s_exter_init = 58;//静态外部变量有初值
static int s_exter_no_init; //静态外部变量无初值
/**************************************************
* main:通过简单案例窥视编译过程和内存布局
**************************************************/
int main()
{
static int s_inter_init = 59;//静态内部变量有初值
static int s_inter_no_init; //静态内部变量无初值
int l_init = 60; //局部变量有初值
int l_no_init; //局部变量无初值
const int l_const = 11; //局部常量
char *heap = (char *)malloc(100);//堆区
printf(HARMONY_OS);
//----------------------
printf("全局常量 g_const:%p\n", &g_const);
printf("全局外部有初值 g_init:%p\n", &g_init);
printf("全局外部无初值 g_no_init:%p\n", &g_no_init);
printf("静态外部有初值 s_exter_init:%p\n", &s_exter_init);
printf("静态外静无初值 s_exter_no_init:%p\n", &s_exter_no_init);
printf("静态内部有初值 s_inter_init:%p\n", &s_inter_init);
printf("静态内部无初值 s_inter_no_init:%p\n", &s_inter_no_init);
//----------------------
printf("局部栈区有初值 l_init:%p\n", &l_init);
printf("局部栈区无初值 l_no_init:%p\n", &l_no_init);
printf("局部栈区常量 l_const:%p\n", &l_const);
//----------------------
printf("堆区地址 heap:%p\n", heap);
//----------------------
printf("代码区地址:%p\n", &main);
return 0;
}

解读

  • 函数具有代表性,有宏,注释,有全局,局部,静态外部,静态内部变量,堆申请.
  • 通过这些值的变化看其中间过程和最后内存布局.

预处理 | main.i

root@5e3abe332c5a:/home/docker/case_code_100/57# gcc -E main.c -o main.i
root@5e3abe332c5a:/home/docker/case_code_100/57# cat main.i
# 1 "main.c"
# 1 "<built-in>"
# 1 "<command-line>"
......
typedef __u_char u_char;
typedef __u_short u_short;
extern int printf (const char *__restrict __format, ...);
......
const int g_const = 10;
int g_init = 57;
int g_no_init;
static int s_exter_init = 58;
static int s_exter_no_init;
int main()
{
static int s_inter_init = 59;
static int s_inter_no_init; int l_init = 60;
int l_no_init;
const int l_const = 11; char *heap = (char *)malloc(100); printf("hello harmonyos \n"); printf("全局常量 g_const:%p\n", &g_const);
printf("全局外部有初值 g_init:%p\n", &g_init);
printf("全局外部无初值 g_no_init:%p\n", &g_no_init);
printf("静态外部有初值 s_exter_init:%p\n", &s_exter_init);
printf("静态外静无初值 s_exter_no_init:%p\n", &s_exter_no_init);
printf("静态内部有初值 s_inter_init:%p\n", &s_inter_init);
printf("静态内部无初值 s_inter_no_init:%p\n", &s_inter_no_init); printf("局部栈区有初值 l_init:%p\n", &l_init);
printf("局部栈区无初值 l_no_init:%p\n", &l_no_init);
printf("局部栈区常量 l_const:%p\n", &l_const); printf("堆区地址 heap:%p\n", heap); printf("代码区地址:%p\n", &main);
return 0;
}

解读

main.i文件很大,1000多行,此处只列出重要部分,全部代码前往case_code_100查看

预处理过程主要处理那些源代码中以#开始的预处理指令,主要处理规则如下:

  • 将所有的#define删除,并且展开所有的宏定义;
  • 处理所有条件编译指令,如#if,#ifdef等;
  • 处理#include预处理指令,将被包含的文件插入到该预处理指令的位置。该过程递归进行,及被包含的文件可能还包含其他文件。
  • 删除所有的注释//和 /**/;
  • 添加行号和文件标识,如# 1 "main.c",以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号信息;
  • 保留所有的#pragma编译器指令,因为编译器须要使用它们;
  • 经过预编译后的.i文件不包含任何宏定义,因为所有的宏都已经被展开,并且包含的文件也已经被插入到.i文件中。所以当无法判断宏定义是否正确或头文件包含是否正确使,可以查看预编译后的文件来确定问题。

编译 | main.s

编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。这个过程是整个程序构建的核心部分,也是最复杂的部分之一。

//编译器: armv7-a clang (trunk)
root@5e3abe332c5a:/home/docker/case_code_100/57# gcc -S main.i -o main.s
root@5e3abe332c5a:/home/docker/case_code_100/57# cat main.s
main:
push {r11, lr} @保存r11和lr寄存器,因为内部有函数调用,lr表示函数的返回地址,所以需要保存
mov r11, sp @保存sp
sub sp, sp, #24 @申请栈空间
mov r0, #0 @r0 = 0,这个代表 return 0;
str r0, [sp] @栈顶保存 main函数返回值
str r0, [r11, #-4]@r0 = 0,即变量l_no_init入栈,优化了指令的顺序
mov r0, #60 @r0 = 60,即变量l_init
str r0, [r11, #-8]@l_init入栈
mov r0, #11 @r0 = 11,即变量l_const
str r0, [sp, #8] @l_const入栈
mov r0, #100 @r0=100,为malloc参数
bl malloc @调用malloc
str r0, [sp, #4] @malloc函数返回值入栈
ldr r0, .LCPI0_0 @为printf准备参数 "hello harmonyos \n"
bl printf @调用printf("hello harmonyos \n");
ldr r0, .LCPI0_1 @准备参数
ldr r1, .LCPI0_2 @准备参数
bl printf @调用printf("全局常量 g_const:%p\n", &g_const);
ldr r0, .LCPI0_3 @准备参数
ldr r1, .LCPI0_4 @准备参数
bl printf @调用printf("全局外部有初值 g_init:%p\n", &g_init);
ldr r0, .LCPI0_5 @准备参数
ldr r1, .LCPI0_6 @准备参数
bl printf @调用printf("全局外部无初值 g_no_init:%p\n", &g_no_init);
ldr r0, .LCPI0_7 @准备参数
ldr r1, .LCPI0_8 @准备参数
bl printf @调用printf("静态外部有初值 s_exter_init:%p\n", &s_exter_init);
ldr r0, .LCPI0_9 @准备参数
ldr r1, .LCPI0_10 @准备参数
bl printf @调用printf("静态外静无初值 s_exter_no_init:%p\n", &s_exter_no_init);
ldr r0, .LCPI0_11 @准备参数
ldr r1, .LCPI0_12 @准备参数
bl printf @调用printf("静态内部有初值 s_inter_init:%p\n", &s_inter_init);
ldr r0, .LCPI0_13 @准备参数
ldr r1, .LCPI0_14 @准备参数
bl printf @调用printf("静态内部无初值 s_inter_no_init:%p\n", &s_inter_no_init);
ldr r0, .LCPI0_15 @准备参数
sub r1, r11, #8 @r1=&l_init
bl printf @调用printf("局部栈区有初值 l_init:%p\n", &l_init);
ldr r0, .LCPI0_16 @准备参数
add r1, sp, #12 @r1=&l_no_init
bl printf @调用printf("局部栈区无初值 l_no_init:%p\n", &l_no_init);
ldr r0, .LCPI0_17 @准备参数
add r1, sp, #8 @r1=&l_const
bl printf @调用printf("局部栈区常量 l_const:%p\n", &l_const);
ldr r1, [sp, #4] @r1=heap,即malloc返回值出栈
ldr r0, .LCPI0_18 @准备参数
bl printf @调用printf("堆区地址 heap:%p\n", heap);
ldr r0, .LCPI0_19 @准备参数
ldr r1, .LCPI0_20 @准备参数
bl printf @调用printf("代码区地址:%p\n", &main);
ldr r0, [sp] @即 r0=0,代表main函数的返回值 对应开头的 str r0, [sp]
mov sp, r11 @恢复值,对应开头的 mov r11, sp
pop {r11, lr} @出栈,对应开头的 push {r11, lr}
bx lr @退出main,跳到调用main()函数处,返回值保存在r0 | =0
.LCPI0_0: @以下全部是申请和定义代码中的一个个变量
.long .L.str
.LCPI0_1:
.long .L.str.1
.LCPI0_2:
.long g_const
.LCPI0_3:
.long .L.str.2
.LCPI0_4:
.long g_init
.LCPI0_5:
.long .L.str.3
.LCPI0_6:
.long g_no_init
.LCPI0_7:
.long .L.str.4
.LCPI0_8:
.long s_exter_init
.LCPI0_9:
.long .L.str.5
.LCPI0_10:
.long _ZL15s_exter_no_init
.LCPI0_11:
.long .L.str.6
.LCPI0_12:
.long main::s_inter_init
.LCPI0_13:
.long .L.str.7
.LCPI0_14:
.long _ZZ4mainE15s_inter_no_init
.LCPI0_15:
.long .L.str.8
.LCPI0_16:
.long .L.str.9
.LCPI0_17:
.long .L.str.10
.LCPI0_18:
.long .L.str.11
.LCPI0_19:
.long .L.str.12
.LCPI0_20:
.long main
g_init:
.long 57 @ 0x39 g_no_init:
.long 0 @ 0x0 main::s_inter_init:
.long 59 @ 0x3b .L.str:
.asciz "hello harmonyos \n" .L.str.1:
.asciz "\345\205\250\345\261\200\345\270\270\351\207\217 g_const\357\274\232%p\n" g_const:
.long 10 @ 0xa .L.str.2:
.asciz "\345\205\250\345\261\200\345\244\226\351\203\250\346\234\211\345\210\235\345\200\274 g_init\357\274\232%p\n" .L.str.3:
.asciz "\345\205\250\345\261\200\345\244\226\351\203\250\346\227\240\345\210\235\345\200\274 g_no_init\357\274\232%p\n" .L.str.4:
.asciz "\351\235\231\346\200\201\345\244\226\351\203\250\346\234\211\345\210\235\345\200\274 s_exter_init\357\274\232%p\n" s_exter_init:
.long 58 @ 0x3a .L.str.5:
.asciz "\351\235\231\346\200\201\345\244\226\351\235\231\346\227\240\345\210\235\345\200\274 s_exter_no_init\357\274\232%p\n" .L.str.6:
.asciz "\351\235\231\346\200\201\345\206\205\351\203\250\346\234\211\345\210\235\345\200\274 s_inter_init\357\274\232%p\n" .L.str.7:
.asciz "\351\235\231\346\200\201\345\206\205\351\203\250\346\227\240\345\210\235\345\200\274 s_inter_no_init\357\274\232%p\n" .L.str.8:
.asciz "\345\261\200\351\203\250\346\240\210\345\214\272\346\234\211\345\210\235\345\200\274 l_init\357\274\232%p\n" .L.str.9:
.asciz "\345\261\200\351\203\250\346\240\210\345\214\272\346\227\240\345\210\235\345\200\274 l_no_init\357\274\232%p\n" .L.str.10:
.asciz "\345\261\200\351\203\250\346\240\210\345\214\272\345\270\270\351\207\217 l_const\357\274\232%p\n" .L.str.11:
.asciz "\345\240\206\345\214\272\345\234\260\345\235\200 heap\357\274\232%p\n" .L.str.12:
.asciz "\344\273\243\347\240\201\345\214\272\345\234\260\345\235\200\357\274\232%p\n"

解读

汇编 | main.o

汇编器是将汇编代码转变成机器可以执行的命令,每一个汇编语句几乎都对应一条机器指令。汇编相对于编译过程比较简单,根据汇编指令和机器指令的对照表一一翻译即可。

main.o的内容为机器码,不能以文本形式方便的呈现,不过可以利用 objdump -S file 查看源码反汇编

root@5e3abe332c5a:/home/docker/case_code_100/57# gcc -c main.s -o main.o
root@5e3abe332c5a:/home/docker/case_code_100/57#objdump -S main.o
main.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 83 ec 20 sub $0x20,%rsp
c: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
13: 00 00
15: 48 89 45 f8 mov %rax,-0x8(%rbp)
19: 31 c0 xor %eax,%eax
1b: c7 45 e4 3c 00 00 00 movl $0x3c,-0x1c(%rbp)
22: c7 45 ec 0b 00 00 00 movl $0xb,-0x14(%rbp)
29: bf 64 00 00 00 mov $0x64,%edi
2e: e8 00 00 00 00 callq 33 <main+0x33>
33: 48 89 45 f0 mov %rax,-0x10(%rbp)
37: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 3e <main+0x3e>
3e: e8 00 00 00 00 callq 43 <main+0x43>
43: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # 4a <main+0x4a>
4a: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 51 <main+0x51>
51: b8 00 00 00 00 mov $0x0,%eax
56: e8 00 00 00 00 callq 5b <main+0x5b>
5b: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # 62 <main+0x62>
62: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 69 <main+0x69>
69: b8 00 00 00 00 mov $0x0,%eax
6e: e8 00 00 00 00 callq 73 <main+0x73>
73: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # 7a <main+0x7a>
7a: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 81 <main+0x81>
81: b8 00 00 00 00 mov $0x0,%eax
86: e8 00 00 00 00 callq 8b <main+0x8b>
8b: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # 92 <main+0x92>
92: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 99 <main+0x99>
99: b8 00 00 00 00 mov $0x0,%eax
9e: e8 00 00 00 00 callq a3 <main+0xa3>
a3: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # aa <main+0xaa>
aa: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # b1 <main+0xb1>
b1: b8 00 00 00 00 mov $0x0,%eax
b6: e8 00 00 00 00 callq bb <main+0xbb>
bb: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # c2 <main+0xc2>
c2: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # c9 <main+0xc9>
c9: b8 00 00 00 00 mov $0x0,%eax
ce: e8 00 00 00 00 callq d3 <main+0xd3>
d3: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # da <main+0xda>
da: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # e1 <main+0xe1>
e1: b8 00 00 00 00 mov $0x0,%eax
e6: e8 00 00 00 00 callq eb <main+0xeb>
eb: 48 8d 45 e4 lea -0x1c(%rbp),%rax
ef: 48 89 c6 mov %rax,%rsi
f2: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # f9 <main+0xf9>
f9: b8 00 00 00 00 mov $0x0,%eax
fe: e8 00 00 00 00 callq 103 <main+0x103>
103: 48 8d 45 e8 lea -0x18(%rbp),%rax
107: 48 89 c6 mov %rax,%rsi
10a: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 111 <main+0x111>
111: b8 00 00 00 00 mov $0x0,%eax
116: e8 00 00 00 00 callq 11b <main+0x11b>
11b: 48 8d 45 ec lea -0x14(%rbp),%rax
11f: 48 89 c6 mov %rax,%rsi
122: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 129 <main+0x129>
129: b8 00 00 00 00 mov $0x0,%eax
12e: e8 00 00 00 00 callq 133 <main+0x133>
133: 48 8b 45 f0 mov -0x10(%rbp),%rax
137: 48 89 c6 mov %rax,%rsi
13a: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 141 <main+0x141>
141: b8 00 00 00 00 mov $0x0,%eax
146: e8 00 00 00 00 callq 14b <main+0x14b>
14b: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # 152 <main+0x152>
152: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 159 <main+0x159>
159: b8 00 00 00 00 mov $0x0,%eax
15e: e8 00 00 00 00 callq 163 <main+0x163>
163: b8 00 00 00 00 mov $0x0,%eax
168: 48 8b 55 f8 mov -0x8(%rbp),%rdx
16c: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx
173: 00 00
175: 74 05 je 17c <main+0x17c>
177: e8 00 00 00 00 callq 17c <main+0x17c>
17c: c9 leaveq
17d: c3 retq

解读

链接 | main

链接器ld将各个目标文件组装在一起,解决符号依赖,库依赖关系,并生成可执行文件。

root@5e3abe332c5a:/home/docker/case_code_100/57# gcc main.o -o main
root@5e3abe332c5a:/home/docker/case_code_100/57# readelf -lW main
Elf file type is DYN (Shared object file)
Entry point 0x10c0
There are 13 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x0002d8 0x0002d8 R 0x8
INTERP 0x000318 0x0000000000000318 0x0000000000000318 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0006c8 0x0006c8 R 0x1000
LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x0003b5 0x0003b5 R E 0x1000
LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000338 0x000338 R 0x1000
LOAD 0x002da0 0x0000000000003da0 0x0000000000003da0 0x00027c 0x000290 RW 0x1000
DYNAMIC 0x002db0 0x0000000000003db0 0x0000000000003db0 0x0001f0 0x0001f0 RW 0x8
NOTE 0x000338 0x0000000000000338 0x0000000000000338 0x000020 0x000020 R 0x8
NOTE 0x000358 0x0000000000000358 0x0000000000000358 0x000044 0x000044 R 0x4
GNU_PROPERTY 0x000338 0x0000000000000338 0x0000000000000338 0x000020 0x000020 R 0x8
GNU_EH_FRAME 0x0021e8 0x00000000000021e8 0x00000000000021e8 0x000044 0x000044 R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x002da0 0x0000000000003da0 0x0000000000003da0 0x000260 0x000260 R 0x1 Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03 .init .plt .plt.got .plt.sec .text .fini
04 .rodata .eh_frame_hdr .eh_frame
05 .init_array .fini_array .dynamic .got .data .bss
06 .dynamic
07 .note.gnu.property
08 .note.gnu.build-id .note.ABI-tag
09 .note.gnu.property
10 .eh_frame_hdr
11
12 .init_array .fini_array .dynamic .got
root@5e3abe332c5a:/home/docker/case_code_100/57# readelf -SW main
There are 31 section headers, starting at offset 0x3b10: Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 0000000000000318 000318 00001c 00 A 0 0 1
[ 2] .note.gnu.property NOTE 0000000000000338 000338 000020 00 A 0 0 8
[ 3] .note.gnu.build-id NOTE 0000000000000358 000358 000024 00 A 0 0 4
[ 4] .note.ABI-tag NOTE 000000000000037c 00037c 000020 00 A 0 0 4
[ 5] .gnu.hash GNU_HASH 00000000000003a0 0003a0 000024 00 A 6 0 8
[ 6] .dynsym DYNSYM 00000000000003c8 0003c8 0000f0 18 A 7 1 8
[ 7] .dynstr STRTAB 00000000000004b8 0004b8 0000ab 00 A 0 0 1
[ 8] .gnu.version VERSYM 0000000000000564 000564 000014 02 A 6 0 2
[ 9] .gnu.version_r VERNEED 0000000000000578 000578 000030 00 A 7 1 8
[10] .rela.dyn RELA 00000000000005a8 0005a8 0000c0 18 A 6 0 8
[11] .rela.plt RELA 0000000000000668 000668 000060 18 AI 6 24 8
[12] .init PROGBITS 0000000000001000 001000 00001b 00 AX 0 0 4
[13] .plt PROGBITS 0000000000001020 001020 000050 10 AX 0 0 16
[14] .plt.got PROGBITS 0000000000001070 001070 000010 10 AX 0 0 16
[15] .plt.sec PROGBITS 0000000000001080 001080 000040 10 AX 0 0 16
[16] .text PROGBITS 00000000000010c0 0010c0 0002e5 00 AX 0 0 16
[17] .fini PROGBITS 00000000000013a8 0013a8 00000d 00 AX 0 0 4
[18] .rodata PROGBITS 0000000000002000 002000 0001e8 00 A 0 0 8
[19] .eh_frame_hdr PROGBITS 00000000000021e8 0021e8 000044 00 A 0 0 4
[20] .eh_frame PROGBITS 0000000000002230 002230 000108 00 A 0 0 8
[21] .init_array INIT_ARRAY 0000000000003da0 002da0 000008 08 WA 0 0 8
[22] .fini_array FINI_ARRAY 0000000000003da8 002da8 000008 08 WA 0 0 8
[23] .dynamic DYNAMIC 0000000000003db0 002db0 0001f0 10 WA 7 0 8
[24] .got PROGBITS 0000000000003fa0 002fa0 000060 08 WA 0 0 8
[25] .data PROGBITS 0000000000004000 003000 00001c 00 WA 0 0 8
[26] .bss NOBITS 000000000000401c 00301c 000014 00 WA 0 0 4
[27] .comment PROGBITS 0000000000000000 00301c 00002a 01 MS 0 0 1
[28] .symtab SYMTAB 0000000000000000 003048 000708 18 29 50 8
[29] .strtab STRTAB 0000000000000000 003750 0002a3 00 0 0 1
[30] .shstrtab STRTAB 0000000000000000 0039f3 00011a 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)

解读

运行 | ./main

root@5e3abe332c5a:/home/docker/case_code_100/57# ./main
hello harmonyos
全局常量 g_const:0x5599b1a00008
全局外部有初值 g_init:0x5599b1a02010
全局外部无初值 g_no_init:0x5599b1a02028
静态外部有初值 s_exter_init:0x5599b1a02014
静态外静无初值 s_exter_no_init:0x5599b1a02020
静态内部有初值 s_inter_init:0x5599b1a02018
静态内部无初值 s_inter_no_init:0x5599b1a02024
局部栈区有初值 l_init:0x7ffda7f4dc94
局部栈区无初值 l_no_init:0x7ffda7f4dc98
局部栈区常量 l_const:0x7ffda7f4dc9c
堆区地址 heap:0x5599b2f522a0
代码区地址:0x5599b19ff1a9

解读

  • 栈区地址最高 0x7ffda7******,局部变量是放在栈中的,这些局部变量地址大小为 l_init< l_no_init < l_const, 这和变量定义的方向是一致的,从而佐证了其用栈方式为递减满栈.越是在前面的变量内存的虚拟地址越小.这个在

  • 代码区.text地址最低 0x5599b1******,代码区第二个LOAD加载段,其flag为(R/E)
  • 全局地址顺序是 g_no_init(.bss) > g_init(.data) > g_const(rodata),刚好和三个区的地址范围吻合.
  • 关于静态变量,看地址的顺序 s_exter_init < s_inter_init < s_exter_no_init < s_inter_no_init ,说明前两个因为有初始值都放在了.data区,后两个都放到了.bss.
  • 对于同样在.bss区的三个变量地址顺序是 s_exter_no_init(2020) < s_inter_no_init(2024) < g_no_init(2028)
  • 对于同样在.data区的三个变量地址顺序是 g_init(2010) < s_exter_init(2014) < s_inter_init(2018)
  • 从地址上看.bss,.data挨在一起的,因为实际的ELF区分布上它们也确实是挨在一起的
    [25] .data             PROGBITS        0000000000004000 003000 00001c 00  WA  0   0  8
    [26] .bss NOBITS 000000000000401c 00301c 000014 00 WA 0 0 4
  • 堆区在中间位置 0x5599b2******,并且可以发现在 .bss(0x5599b1a0****).heap(0x5599b2f5****)区之间还有大量的虚拟地址没有被使用
  • ELF如何被加载运行可翻看

问题

细心的可能会发现了一个问题,s_inter_init(2018),s_exter_no_init(2020)这两个地址之间只相差两个字节,但是int变量是4个字节,这是为什么呢?

鸿蒙内核源码分析.总目录

v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 百篇博客分析 | 51.c.h .o

百万汉字注解.百篇博客分析

百万汉字注解 >> 精读鸿蒙源码,中文注解分析, 深挖地基工程,大脑永久记忆,四大码仓每日同步更新< gitee| github| csdn| coding >

百篇博客分析 >> 故事说内核,问答式导读,生活式比喻,表格化说明,图形化展示,主流站点定期更新中< 51cto| csdn| harmony| osc >

关注不迷路.代码即人生

QQ群:790015635 | 入群密码: 666

原创不易,欢迎转载,但请注明出处.

鸿蒙内核源码分析(编译过程篇) | 简单案例窥视GCC编译全过程 | 百篇博客分析OpenHarmony源码| v57.01的更多相关文章

  1. 鸿蒙内核源码分析(构建工具篇) | 顺瓜摸藤调试鸿蒙构建过程 | 百篇博客分析OpenHarmony源码 | v59.01

    百篇博客系列篇.本篇为: v59.xx 鸿蒙内核源码分析(构建工具篇) | 顺瓜摸藤调试鸿蒙构建过程 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿 ...

  2. 鸿蒙内核源码分析(编译脚本篇) | 如何防编译环境中的牛皮癣 | 百篇博客分析OpenHarmony源码 | v58.01

    百篇博客系列篇.本篇为: v58.xx 鸿蒙内核源码分析(环境脚本篇) | 编译鸿蒙原来如此简单 | 51.c.h.o 本篇用两个脚本完成鸿蒙(L1)的编译环境安装/源码下载/编译过程,让编译,调试鸿 ...

  3. 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙看这篇或许真的够了 | 百篇博客分析OpenHarmony源码 | v50.06

    百篇博客系列篇.本篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙防掉坑指南 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙防掉 ...

  4. 鸿蒙内核源码分析(忍者ninja篇) | 都忍者了能不快吗 | 百篇博客分析OpenHarmony源码 | v61.02

    百篇博客系列篇.本篇为: v61.xx 鸿蒙内核源码分析(忍者ninja篇) | 都忍者了能不快吗 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙 ...

  5. 鸿蒙内核源码分析(GN应用篇) | GN语法及在鸿蒙的使用 | 百篇博客分析OpenHarmony源码 | v60.01

    百篇博客系列篇.本篇为: v60.xx 鸿蒙内核源码分析(gn应用篇) | gn语法及在鸿蒙的使用 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙 ...

  6. 鸿蒙源码分析系列(总目录) | 百万汉字注解 百篇博客分析 | 深入挖透OpenHarmony源码 | v8.23

    百篇博客系列篇.本篇为: v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 百篇博客分析 | 51.c.h .o 百篇博客.往期回顾 在给OpenHarmony内核源码加注过程中,整理出以下 ...

  7. 鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程 | 百篇博客分析OpenHarmony源码 | v54.01

    百篇博客系列篇.本篇为: v54.xx 鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程 | 51.c.h.o 下图是一个可执行文件编译,链接的过程. 本篇将通过一个完整的小工程来阐述E ...

  8. 鸿蒙内核源码分析(信号生产篇) | 信号安装和发送过程是怎样的? | 百篇博客分析OpenHarmony源码 | v48.03

    百篇博客系列篇.本篇为: v48.xx 鸿蒙内核源码分析(信号生产篇) | 年过半百,依然活力十足 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管 ...

  9. 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 百篇博客分析OpenHarmony源码 | v69.01

    百篇博客系列篇.本篇为: v69.xx 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说 ...

随机推荐

  1. Java Web基础 --- Servlet 综述(理论篇)

    摘要: Web 技术成为当今主流的互联网 Web 应用技术之一,而 Servlet 是 Java Web 技术的核心基础.本文首先从请求/响应架构应用的大背景谈起 Servlet 的由来,明确 Ser ...

  2. centos7 wget安装Tomcat7

    2021-07-15 1.环境介绍 操作系统:centos7 jdk版本:jdk1.8.0.211 tomcat版本:tomcat7.0.109 2. 检查系统中是否已经安装 jdk ,如未安装, 请 ...

  3. 类的基础语法阅读【Python3.8官网文档】

    英文官方文档: https://docs.python.org/3.8/tutorial/classes.html 中文官方文档: https://docs.python.org/zh-cn/3.8/ ...

  4. 源码解析Spring AOP的加载与生效

    本次博主主要进行Spring AOP这里的解析,因为在工作中使用后,却不知道背后的实现原理并在使用的过程中发现了一些认知缺陷,所以决定写这么一篇文章以供大家参考参考,进入正题. 本次博主使用了@Asp ...

  5. VS Code闪现,巨头纷纷入局的Web IDE缘何崛起?

    我发了,我装的. 就在前几天,微软简短的发布了Visual Studio Code for the Web 的公告,而没过一阵,这则公告就被删除了,现在点经相关内容已经是404状态了.虽然公告的内容已 ...

  6. Coreos配置docker镜像加速器

    CoreOS配置docker镜像加速器 CoreOS下的Docker配置是通过flannel unit来实现的. 1) 通过命令 systemctl cat docker 可以看出配置文件的默认位置 ...

  7. 写了一年golang,来聊聊进程、线程与协程

    本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star. 进程 在早期的单任务计算机中,用户一次只能提交一个作业,独享系统的全部资源,同时也只能干一件事 ...

  8. Cobar源码分析之AST

    本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star. 背景 Cobar Cobar是阿里开源的数据库中间件,关于它的介绍这里不再赘述,可以参考之前的 ...

  9. 如何将 Ubuntu 版本升级到新版本

    @ 目录 0.将 Ubuntu 版本升级到新版本的注意事项 1.以图形方式升级到 Ubuntu 20.04(适用于桌面用户) 2.使用命令行升级到 Ubuntu 21.10 本教程通过从 Ubuntu ...

  10. ElasticSearch集成SpringData史上最全查询教程

    1.简单介绍 springboot 使用springdata操作es,ElasticsearchRepository使用QueryBuilder构造查询条件 2.集成es //maven集成 < ...