鸿蒙内核源码分析(编译过程篇) | 简单案例窥视编译全过程

news/2024/5/20 21:47:55

一个.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

源文件 | 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       @保存spsub     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_initstr     r0, [r11, #-8]@l_init入栈mov     r0, #11       @r0 = 11,即变量l_conststr     r0, [sp, #8]  @l_const入栈mov     r0, #100      @r0=100,为malloc参数bl      malloc        @调用mallocstr     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_initbl      printf        @调用printf("局部栈区有初值 l_init:%p\n", &l_init);ldr     r0, .LCPI0_16 @准备参数add     r1, sp, #12   @r1=&l_no_initbl      printf        @调用printf("局部栈区无初值  l_no_init:%p\n", &l_no_init);ldr     r0, .LCPI0_17 @准备参数add     r1, sp, #8    @r1=&l_constbl      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, sppop     {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                              @ 0x39g_no_init:.long   0                               @ 0x0main::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             endbr644:   55                      push   %rbp5:   48 89 e5                mov    %rsp,%rbp8:   48 83 ec 20             sub    $0x20,%rspc:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax13:   00 0015:   48 89 45 f8             mov    %rax,-0x8(%rbp)19:   31 c0                   xor    %eax,%eax1b:   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,%edi2e:   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,%eax56:   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,%eax6e:   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,%eax86:   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,%eax9e:   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,%eaxb6:   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,%eaxce:   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,%eaxe6:   e8 00 00 00 00          callq  eb <main+0xeb>eb:   48 8d 45 e4             lea    -0x1c(%rbp),%raxef:   48 89 c6                mov    %rax,%rsif2:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # f9 <main+0xf9>f9:   b8 00 00 00 00          mov    $0x0,%eaxfe:   e8 00 00 00 00          callq  103 <main+0x103>103:   48 8d 45 e8             lea    -0x18(%rbp),%rax107:   48 89 c6                mov    %rax,%rsi10a:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # 111 <main+0x111>111:   b8 00 00 00 00          mov    $0x0,%eax116:   e8 00 00 00 00          callq  11b <main+0x11b>11b:   48 8d 45 ec             lea    -0x14(%rbp),%rax11f:   48 89 c6                mov    %rax,%rsi122:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # 129 <main+0x129>129:   b8 00 00 00 00          mov    $0x0,%eax12e:   e8 00 00 00 00          callq  133 <main+0x133>133:   48 8b 45 f0             mov    -0x10(%rbp),%rax137:   48 89 c6                mov    %rax,%rsi13a:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # 141 <main+0x141>141:   b8 00 00 00 00          mov    $0x0,%eax146:   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,%eax15e:   e8 00 00 00 00          callq  163 <main+0x163>163:   b8 00 00 00 00          mov    $0x0,%eax168:   48 8b 55 f8             mov    -0x8(%rbp),%rdx16c:   64 48 33 14 25 28 00    xor    %fs:0x28,%rdx173:   00 00175:   74 05                   je     17c <main+0x17c>177:   e8 00 00 00 00          callq  17c <main+0x17c>17c:   c9                      leaveq17d:   c3                      retq

解读

  • 此时的main.o是个REL (Relocatable file)重定位文件,关于重定位可前往翻看

链接 | 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 AlignPHDR           0x000040 0x0000000000000040 0x0000000000000040 0x0002d8 0x0002d8 R   0x8INTERP         0x000318 0x0000000000000318 0x0000000000000318 0x00001c 0x00001c R   0x1[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x0006c8 0x0006c8 R   0x1000LOAD           0x001000 0x0000000000001000 0x0000000000001000 0x0003b5 0x0003b5 R E 0x1000LOAD           0x002000 0x0000000000002000 0x0000000000002000 0x000338 0x000338 R   0x1000LOAD           0x002da0 0x0000000000003da0 0x0000000000003da0 0x00027c 0x000290 RW  0x1000DYNAMIC        0x002db0 0x0000000000003db0 0x0000000000003db0 0x0001f0 0x0001f0 RW  0x8NOTE           0x000338 0x0000000000000338 0x0000000000000338 0x000020 0x000020 R   0x8NOTE           0x000358 0x0000000000000358 0x0000000000000358 0x000044 0x000044 R   0x4GNU_PROPERTY   0x000338 0x0000000000000338 0x0000000000000338 0x000020 0x000020 R   0x8GNU_EH_FRAME   0x0021e8 0x00000000000021e8 0x00000000000021e8 0x000044 0x000044 R   0x4GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10GNU_RELRO      0x002da0 0x0000000000003da0 0x0000000000003da0 0x000260 0x000260 R   0x1Section to Segment mapping:Segment Sections...0001     .interp02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt03     .init .plt .plt.got .plt.sec .text .fini04     .rodata .eh_frame_hdr .eh_frame05     .init_array .fini_array .dynamic .got .data .bss06     .dynamic07     .note.gnu.property08     .note.gnu.build-id .note.ABI-tag09     .note.gnu.property10     .eh_frame_hdr1112     .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)

解读

  • 区头表位置的顺序将是加载到内存中映像的顺序,即虚拟地址的大小顺序,可以看出 .text < .rodata < .data < .bss

运行 | ./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_initl_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个字节,这是为什么呢?

鸿蒙全栈开发全新学习指南

也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大厂APP实战项目开发】

本路线共分为四个阶段:

第一阶段:鸿蒙初中级开发必备技能

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:https://gitee.com/MNxiaona/733GH

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

鸿蒙入门教学视频:

美团APP实战开发教学:gitee.com/MNxiaona/733GH

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH


http://www.mrgr.cn/p/41504266

相关文章

新火种AI|AI让大家都变“土”了!

作者&#xff1a;一号 编辑&#xff1a;美美 AI不仅要把人变“土”&#xff0c;还要把人变多样。 这个世界&#xff0c;终究是变“土”了。 今年五一假期&#xff0c;一个名为“Remini”的AI修图APP火遍了全网。注意&#xff0c;是Remini&#xff0c;而不是Redmi&#xff0…

nacos下载安装和nacos启动报错

nacos简介: Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称&#xff0c;一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集&#xff0c;帮助您…

自动驾驶纵向控制算法

本文来源——b站忠厚老实的老王&#xff0c;链接&#xff1a;忠厚老实的老王投稿视频-忠厚老实的老王视频分享-哔哩哔哩视频 (bilibili.com)&#xff0c;侵删。 功率和转速之间的关系就是&#xff1a;功率P等于转矩M乘以转速ω。并不是油门越大加速度就越大。 发动机和电机的转…

宁波方太集团项目管理办公室负责人王博受邀为第十三届中国PMO大会演讲嘉宾

全国PMO专业人士年度盛会 宁波方太集团项目管理办公室负责人王博先生受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾&#xff0c;演讲议题为“系统性建设卓越的组织级项目管理体系”。大会将于6月29-30日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&#xff1…

C# WinForm —— 12 ListBox绑定数据

ListBox加载大量数据时&#xff0c;避免窗体闪烁的方法&#xff1a; 在加载语句的前后分别加上 BeginUpdate()方法 和 EndUpdate()方法 指定一个集合为绑定的数据源 1. 首先&#xff0c;右键项目&#xff0c;添加类 2. 在新建的类文件中添加属性值信息 3. 构建初始化的对象…

【华为】IPSec VPN手动配置

【华为】IPSec VPN手动配置 拓扑配置ISP - 2AR1NAT - Easy IPIPSec VPN AR3NATIPsec VPN PC检验 配置文档AR1AR2 拓扑 配置 配置步骤 1、配置IP地址&#xff0c;ISP 路由器用 Lo0 模拟互联网 2、漳州和福州两个出口路由器配置默认路由指向ISP路由器 3、进行 IPsec VPN配置&…

【C++】C++11--- 列表初始化|关键字

目录 前言 列表初始化 创建对象时的列表初始化 单参数隐式类型转换 多参数的隐式类型转换 new表达式中使用列表初始化 列表初始化适用于STL 容器 模板类initializer_list 关键字auto 关键字decltype 关键字nullptr 前言 C标准10年磨一剑&#xff0c;第二个真正意义上…

【强训笔记】day16

NO.1 代码实现&#xff1a; class StringFormat { public:string formatString(string A, int n, vector<char> arg, int m) {string ret;int j0;for(int i0;i<n;i){if(A[i]%){if(i1<n&&A[i1]s){retarg[j];i;}else {retA[i];}}else {retA[i];}}while(j&l…

Golang:deepcopy深拷贝工具库

Golang:deepcopy深拷贝工具库 原创 吃个大西瓜 Coding Big Tree 2024-05-02 08:30 云南 听全文Deep copy things译文:事物的深度复制文档github https://github.com/mohae/deepcopy pkg.go https://pkg.go.dev/github.com/mohae/deepcopy安装 go get github.com/mohae/deepco…

SpringCloud微服务之Eureka、Ribbon、Nacos详解

SpringCloud微服务之Eureka、Ribbon、Nacos详解 1、认识微服务1.1、单体架构1.2、分布式架构1.3、微服务1.4、SpringCloud 2、服务拆分与远程调用2.1、服务拆分的原则2.2、服务拆分示例2.2、提供者与消费者 3、Eureka注册中心3.1、Eureka的结构和作用3.2、搭建eureka-server3.2…

Sealos急速部署生产用k8s集群

最近一段时间部署k8s全部使用sealos了&#xff0c;整体使用感觉良好&#xff0c;基本没有什么坑。推荐给大家。 使用 Sealos&#xff0c;可以安装一个不包含任何组件的裸 Kubernetes 集群。 最大的好处是提供 99 年证书&#xff0c;用到我跑路是足够了。不用像之前kubeadm安装…

小程序引入 Vant Weapp 极简教程

一切以 Vant Weapp 官方文档 为准 Vant Weapp 官方文档 - 快速入手 1. 安装nodejs 前往官网下载安装即可 nodejs官网 安装好后 在命令行&#xff08;winr&#xff0c;输入cmd&#xff09;输入 node -v若显示版本信息&#xff0c;即为安装成功 2. 在 小程序根目录 命令行/终端…

毕业设计:《基于 Prometheus 和 ELK 的基础平台监控系统设计与实现》

前言 《基于 Prometheus 和 ELK 的基础平台监控系统设计与实现》&#xff0c;这是我在本科阶段的毕业设计&#xff0c;通过引入 Prometheus 和 ELK 架构实现企业对指标与日志的全方位监控。并且基于云原生&#xff0c;使用容器化持续集成部署的开发方式&#xff0c;通过 Sprin…

95、动态规划-编辑距离

递归暴力解法 递归方法的基本思想是考虑最后一个字符的操作&#xff0c;然后根据这些操作递归处理子问题。 递归函数定义&#xff1a;定义一个递归函数 minDistance(i, j)&#xff0c;表示将 word1 的前 i 个字符转换成 word2 的前 j 个字符所需的最小操作数。 递归终止条件…

day1_slidingWindow

一、滑动窗口模板 // 注意&#xff1a;java 代码由 chatGPT&#x1f916; 根据我的 cpp 代码翻译&#xff0c;旨在帮助不同背景的读者理解算法逻辑。 // 本代码不保证正确性&#xff0c;仅供参考。如有疑惑&#xff0c;可以参照我写的 cpp 代码对比查看。import java.util.Has…

C++ 引用

引用函数的形参还有引用传参这一形式 引用:是一个变量的别名,它是某个已经存在的变量的另一个名字。 引用创建后,不可更改 因不可更改,所以必须初始化 必须初始化,所以不可为空(不能被修改) 语法:引用传参语法:函数三种传参模式对比

.Net MAUI 搭建Android 开发环境

一、 安装最新版本 VS 2022 安装时候选择上 .Net MAUI 跨平台开发 二、安装成功后,创建 .Net MAUI 应用 三、使用 VS 自带的 Android SDK 下载 ,Android镜像、编译工具、加速工具 四、使用Vs 自带的 Android Avd 创建虚拟机 五、使用 Android 手机真机调试

基于springboot实现医院药品管理系统项目【项目源码+论文说明】

基于springboot实现医院药品管理系统演示 摘要 身处网络时代&#xff0c;随着网络系统体系发展的不断成熟和完善&#xff0c;人们的生活也随之发生了很大的变化&#xff0c;人们在追求较高物质生活的同时&#xff0c;也在想着如何使自身的精神内涵得到提升&#xff0c;而读书就…

AI 绘画神器 Fooocus 本地部署指南:简介、硬件要求、部署步骤、界面介绍

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里&#xff0c;订阅后可阅读专栏内所有文章。 大家好&#xff0c;我是水滴~~ 随着人工智能技术的飞速发展&#xff0c;AI 绘画逐渐成为创意领域的新宠。Fooocus 作为一款免费开源的 AI 绘画工具&am…