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

一个.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.mzph.cn/diannao/8867.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Spring Ai 快速创建一个AI会话

文章目录 1、创建SpringBoot项目2、引入依赖3、修改配置文件4、一个简单的会话 前期准备 在OpenAI 注册页面创建帐户并在API 密钥页面生成令牌。 Spring AI 项目定义了一个配置属性&#xff0c;您应该将其设置为从 openai.com 获取的spring.ai.openai.api-key值 代码托管于gite…

《深入Linux内核架构》第4章 进程虚拟内存(2)

目录 4.3 内存映射原理 4.4 数据结构 4.4.1 树和链表 4.4.2 虚拟内存区域VMA的表示 4.4.3 相关数据结构 本专栏文章将有70篇左右&#xff0c;欢迎关注&#xff0c;查看后续文章。 本节讲VMA结构体struct vm_area_struct和struct address_space。 4.3 内存映射原理 所有进…

自注意力架构大成者_Transformer(Pytorch 17)

1 模型简介 在上节比较了 卷积神经网络&#xff08;CNN&#xff09;、循环神经网络&#xff08;RNN&#xff09;和 自注意力&#xff08;self‐attention&#xff09;。值得注意的是&#xff0c; 自注意力同时具有并行计算和最短的最大路径长度这两个优势。因此&#xff0c;使…

【微机原理及接口技术】存储器

【微机原理及接口技术】存储器 文章目录 【微机原理及接口技术】存储器前言一、半导体存储器技术1.半导体存储器概述2.半导体存储器的分类3.半导体存储器芯片的结构 二、半导体存储器芯片1.RAM芯片2.ROM芯片 三、半导体存储器与CPU的连接1.存储芯片数据线的连接2.存储芯片地址线…

webrtc初步了解

WebRTC搭建点对点实时音视频对话&#xff0c;起始需要保证完成两点&#xff1a; 1.媒体协商&#xff0c;了解彼此支持的媒体格式。参与视频通讯的双方必须先交换SDP信息&#xff0c;交换SDP的过程。 2.网络协商&#xff0c;了解彼此的网络环境&#xff0c;找到一条相互通讯的链…

Linux学习之禁用防火墙

查看防火墙状态 systemctl status firewalld.service 第一行前面的圆圈是有颜色的就是开启状态 黑色的就是关闭状态 关闭防火墙 systemctl stop firewalld.service 输入密码认证 再次查看防火墙状态 systemctl status firewalld.service 第一行前面的圆圈变成黑色说明关闭…

ThreeJS:补间动画与Tween.JS

补间动画 补间动画指的是做FLASH动画时&#xff0c;在两个关键帧中间需要做“补间动画”&#xff0c;才能实现图画的运动&#xff1b;插入补间动画后两个关键帧之间的插补帧是由计算机自动运算而得到的。 ——摘自《百度百科&#xff1a;补间动画_百度百科》 Tween.js Tween.js…

xCode升级后: Library ‘iconv2.4.0’ not found

报错信息&#xff1a; targets 选中 xxxNotification: Build Phases ——> Link Binary With Libraries 中&#xff0c;移除 libiconv.2.4.0.tbd libiconv.2.4.0.dylib 这两个库&#xff08;只有一个的移除一个就好&#xff09;。 然后重新添加 libiconv.tbd 修改完…

(代码以上传,超级详细)面试必备算法题----Leeecode290单词规律

文章目录 概要题目要求测试and提交结果技术细节 概要 来自Leecode ​ 代码已上传&#xff09;仓库&#xff0c;需要测试实例和其他题型解决&#xff0c;可以去自行浏览 点击这里进入仓库领取代码喔&#xff01;顺便点个star给原子加油吧&#xff01; ​ 题目要求 使用哈希表 …

GD32F103RCT6/GD32F303RCT6(9)高级定时器互补PWM波输出实验

本文章基于兆易创新GD32 MCU所提供的2.2.4版本库函数开发 后续项目主要在下面该专栏中发布&#xff1a; 手把手教你嵌入式国产化_不及你的温柔的博客-CSDN博客 感兴趣的点个关注收藏一下吧! 电机驱动开发可以跳转&#xff1a; 手把手教你嵌入式国产化-实战项目-无刷电机驱动&am…

【设计模式】之观察者模式

系列文章目录 【设计模式】之装饰器模式【设计模式】之工厂模式&#xff08;三种&#xff09;【设计模式】之工厂模式&#xff08;三种&#xff09; 前言 今天给大家介绍另一种设计模式--观察者模式&#xff0c;有了解webscoket实现原理的小伙伴应该对这个设计模式不陌生。不清…

微服务核心01-Maven【项目管理工具】基础

一、Maven 简介 1.1 传统项目管理&#xff1a; 1.2 Maven 的作用 项目构建&#xff1a;提供标准的、跨平台的自动化项目构建方式。依赖管理&#xff1a;管理项目依赖的资源&#xff08;jar 包&#xff09;&#xff0c;避免资源间的版本冲突问题统一开发结构&#xff1a;提供标…

Web LLM 攻击实验:利用 LLM API 实现 SQL 注入

前言 Web LLM 攻击 各组织都在急于集成大型语言模型 (LLM)&#xff0c;以改善其在线客户体验。这使他们面临 Web LLM 攻击&#xff0c;这些攻击利用模型对攻击者无法直接访问的数据、API 或用户信息的访问权限。例如&#xff0c;攻击可能&#xff1a; 检索 LLM 有权访问的数…

【valse 2024】会议内容汇总(持续更新)

系列文章目录 提示&#xff1a;更新中&#xff0c;一周左右更新完毕。需要具体课件的可私信 文章目录 系列文章目录开幕式主旨报告-1:大模型时代的机遇和挑战主旨报告-2:以深度学习框架为牵引促进自主 AI生态发展主旨报告-3:从洞穴的影子到智能的光辉--连接和交互方式的改变塑造…

别出心裁的自动化网页数据采集:Chrome插件和mitmproxy

别出心裁的自动化网页数据采集&#xff1a;Chrome插件和mitmproxy 前言 在信息时代&#xff0c;数据已成为决策的关键。传统的数据采集方法往往依赖于手动操作或简单的自动化脚本&#xff0c;这限制了数据的时效性和精确性。为了克服这些限制&#xff0c;本文介绍了一种结合C…

文件批量移动:按路径名称指引,高效文件管理与批量归类实战

在数字化时代&#xff0c;文件批量移动成为了一项至关重要的技能&#xff0c;它能够帮助我们高效地管理和归类大量的文件。通过按路径名称指引进行文件批量移动&#xff0c;我们可以使文件组织更加有序&#xff0c;提高文件检索的速度&#xff0c;从而提升工作效率。 一、明确路…

多线程学习Day09

10.Tomcat线程池 LimitLatch 用来限流&#xff0c;可以控制最大连接个数&#xff0c;类似 J.U.C 中的 Semaphore 后面再讲 Acceptor 只负责【接收新的 socket 连接】 Poller 只负责监听 socket channel 是否有【可读的 I/O 事件】 一旦可读&#xff0c;封装一个任务对象&#x…

【建议收藏】CSP-J/S信奥赛,小白报名教程!

✅ 信奥介绍 信息学奥赛是五大学科&#xff08;数学、物理、化学、生物、信息学&#xff09;奥林匹克竞赛中唯一一个可以贯穿小学、初中、高中的特长生项目。由中国计算机学会主办&#xff0c;主要考察信息学&#xff0c;即编程的相关知识和能力。 ✅ 报名流程 &#x1f449;登…

智能绘画系统源码系统 后台自由设置会员套餐 带网站的安装包以及安装部署教程

在当今数字化与智能化快速发展的时代&#xff0c;艺术与技术正以前所未有的速度相互融合。为了满足广大绘画爱好者和专业艺术家的需求&#xff0c;我们精心打造了一款智能绘画系统源码系统。该系统不仅具备高度的智能化特性&#xff0c;还提供了丰富的后台管理功能&#xff0c;…

CTF-密码学基础

概述 密码学(Cryptolopy)&#xff1a;是研究信息系统安全保密的科学 密码学研究的两个方向&#xff1a; 密码编码学(Cryptography)&#xff1a;主要研究对信息进行编码&#xff0c;实现对信息的隐蔽密码分析学(Cryptanalytics)&#xff1a;主要研究加密信息的破译或消息的伪造…