目录

代码

用strace查看调用流程

分析

总结


看到一篇好文:摘抄记录--Linux应用程序 启动流程-BugMan-ChinaUnix博客

  • 代码

#include <stdio.h>int main (int argc, char *argv[])
{printf ("Hello World\n");return 0;
}
  • 用strace查看调用流程

pc123@ubuntu:~/Public$ strace ./main
execve("./main", ["./main"], 0x7fff3906f3d0 /* 56 vars */) = 0
brk(NULL)                               = 0x55d0fc755000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=81045, ...}) = 0
mmap(NULL, 81045, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe65b878000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\35\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030928, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe65b876000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe65b272000
mprotect(0x7fe65b459000, 2097152, PROT_NONE) = 0
mmap(0x7fe65b659000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7fe65b659000
mmap(0x7fe65b65f000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe65b65f000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7fe65b8774c0) = 0
mprotect(0x7fe65b659000, 16384, PROT_READ) = 0
mprotect(0x55d0fa83f000, 4096, PROT_READ) = 0
mprotect(0x7fe65b88c000, 4096, PROT_READ) = 0
munmap(0x7fe65b878000, 81045)           = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 14), ...}) = 0
brk(NULL)                               = 0x55d0fc755000
brk(0x55d0fc776000)                     = 0x55d0fc776000
write(1, "hello world", 11hello world)             = 11
exit_group(0)                           = ?
+++ exited with 0 +++
pc123@ubuntu:~/Public$
  • 分析

  1. 毫无疑问,在shell内执行一个程序main,本质上是shell去调用execve函数执行main程序
  2. execve是一个系统调用,Linux内核会在这个系统调用里面为main程序映射必要的内存
  3. 动态解释器【动态解释器】:当execve将控制权给/lib64/ld-linux-x86-64.so.2的时候,这个文件进行动态库代码重定向等功能
    $ readelf -l hello | grep interpreter[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  4. 通过读取elf头部信息中的entry部分,即可得到程序入口地址为0x540
    pc123@ubuntu:~/Public$ readelf -h main
    ELF Header:Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class:                             ELF64Data:                              2's complement, little endianVersion:                           1 (current)OS/ABI:                            UNIX - System VABI Version:                       0Type:                              DYN (Shared object file)Machine:                           Advanced Micro Devices X86-64Version:                           0x1Entry point address:               0x540Start of program headers:          64 (bytes into file)Start of section headers:          6448 (bytes into file)Flags:                             0x0Size of this header:               64 (bytes)Size of program headers:           56 (bytes)Number of program headers:         9Size of section headers:           64 (bytes)Number of section headers:         29Section header string table index: 28
    pc123@ubuntu:~/Public$
    
  5. 通过objdump找到程序的入口地址:
    pc123@ubuntu:~/Public$ objdump -axd main > main.s

    在main.s文件找到地址为0x540的代码

    Disassembly of section .text:       段名:.text,表示代码段0000000000000540 <_start>:        函数名_start,存放地址0x540540:    31 ed                   xor    %ebp,%ebp542:    49 89 d1                mov    %rdx,%r9545: 5e                      pop    %rsi546: 48 89 e2                mov    %rsp,%rdx549:    48 83 e4 f0             and    $0xfffffffffffffff0,%rsp54d: 50                      push   %rax54e: 54                      push   %rsp54f: 4c 8d 05 8a 01 00 00    lea    0x18a(%rip),%r8        # 6e0 <__libc_csu_fini>556: 48 8d 0d 13 01 00 00    lea    0x113(%rip),%rcx        # 670 <__libc_csu_init>55d:    48 8d 3d e6 00 00 00    lea    0xe6(%rip),%rdi        # 64a <main>564:    ff 15 76 0a 20 00       callq  *0x200a76(%rip)        # 200fe0 <__libc_start_main@GLIBC_2.2.5>56a:   f4                      hlt    56b: 0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
    

    可见在shell执行main程序,程序的入口是_start函数,这个_start函数实际上是由C库实现的,

  6. _start函数最后调用了__libc_start_main函数【汇编的callq *0x200a76这一行】,而调用__libc_start_main函数传入了0x54f, 0x556, 0x55d对应的这三个参数,其中对应的就是__libc_csu_fini, __libc_csu_initial, main函数【也就是我们写的代码】

  7. 【__libc_start_main@plt表示这是一个延迟加载函数,什么是延迟加载函数呢?延迟加载函数就是指在动态解释阶段不进行代码重定位,只有在真正使用该函数的时候,才去定位该函数的地址, 这样做的目的是加快程序启动】

  8. __libc_start_main函数的运行的顺序为:__libc_csu_init->main->__libc_csu_fini,__libc_csu_init叫构造函数,__libc_csu_fini叫析构函数,在代码中可通过__attribute__ ((constructor))标记构造函数,__attribute__ ((destructor))来标记析构函数                      有这样的例子

    #include <stdio.h>static void hello_after() __attribute__ ((destructor));
    static void hello_before() __attribute__ ((constructor));static void hello_before(void)
    {printf("Before main\n");
    }static void hello_after(void)
    {printf("After main\n");
    }int main (int argc, char *argv[])
    {printf ("Hello World\n");return 0;
    }
    pc123@ubuntu:~/Public$ ./main
    Before main
    Hello World
    After main
    pc123@ubuntu:~/Public$ 
  • 总结

shell通过execve启动main程序------->main函数被C 库作为一个函数----------->整个程序的入口地址是_start【通过objdump对应的0x540确定】----->start函数调用了__libc_start_main,传入了构造,main, 析构函数。

Linux应用程序的启动流程相关推荐

  1. Android应用程序进程启动流程

    在学习应用程序进程启动流程前,先要弄清楚系统启动流程,如果有不清楚的同学,建议先看下以前博主的文章: Android系统启动(上篇)_AD钙奶-lalala的博客-CSDN博客 Android系统启动 ...

  2. linux系统下开机启动流程

    在了解开机启动流程之前,还是得先了解一些磁盘的基本知识.磁盘主要由盘片,机械手臂,磁头,主轴马达构成.盘片就是存储数据的物理单位了.然后盘片上我们可以分成扇区(sector)和柱面(cylinder) ...

  3. Note For Linux By Jes(14)-启动流程、模块管理与 Loader

    Linux的启动流程分析: 启动流程一览 加载BIOS 的硬件资讯与进行自我测试,并依据配置取得第一个可启动的装置: 读取并运行第一个启动装置内MBR 的boot Loader (亦即是grub, s ...

  4. iOS 应用程序的启动流程及其代理

    应用程序的启动步骤 main函数调用UIApplicationMain. UIApplicationMain创建了一个UIApplication UIApplicationMain创建了一个AppDe ...

  5. Linux下程序开机启动

    2019独角兽企业重金招聘Python工程师标准>>> 这个不是指apache这样的service程序. 指进入桌面后,自动启动那些程序,是在~/.config/autostart/ ...

  6. 微信小程序的启动流程

    小程序运行的三种环境 1.ios端,Mac微信端 2.Android端.PC微信端 3.微信开发者模拟器端 第一步环境准备 1.小程序运行进程以及运行环境的准备 2.代码包下载.校验以及初始化 3.视 ...

  7. Linux设置程序开机启动-tomcat开机启动

    假设我有一个tomcat应用需要开机启动. 前提你的JAVA环境变量已经配置好没有问题,检测方法如图 然后找到tomcat的目录,我的目录是 /home/yuqing_4.0/tomcat_share ...

  8. golang程序启动流程详解

    golang程序启动流程详解 环境 go1.16.5 linux/amd64 用例 package mainimport "fmt"func main() {fmt.Println ...

  9. (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序

    (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序 原文:(1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序 版权声明:本作品采用知识共享署名-非商 ...

最新文章

  1. poj 3411(DFS多点访问)
  2. c 语言输出指针的值,C 语言指针
  3. python调用oracle过程 权限不足_Python连接Oracle的一些坑以及出现原因和解决方法...
  4. android 服务是什么问题,Android Studio 中的Service问题
  5. iOS内存管理系列之一:对象所有权与引用计数
  6. RabbitMQ的简单示例
  7. Linux 下 Shell 命令的分类及用法
  8. 对象存储 OSS > 产品简介 > 什么是对象存储OSS
  9. 从零开始刷Leetcode——数组(581.605.628)
  10. linux deepin 15.9双系统,windows10安装双系统Deepin15.9遇到的坑
  11. procedure mysql_所有子节点、Procedure、MySQL
  12. 如何让sublime编译c语言,如何在Sublime Text 3中编译C程序?
  13. 机器学习基石 作业0
  14. docker-compose 部署shipyard
  15. QT HTTP接收多个数据包生成图片
  16. 赵俊峰内蒙古大学计算机学院,内蒙古大学计算机学院研究生导师:赵俊峰
  17. 程序员代码中的希腊字母表示
  18. PJzhang:搜索引擎高级语法与渗透测试
  19. urlrewrite使用介绍
  20. MES系统的实施准备,将MES系统导入到企业的运作体系之中

热门文章

  1. OTA升级包的解释和升级方法
  2. 10 个解放双手超实用在线工具,有些代码真的不用手写
  3. 通达OA 工作流流转过程中使用系统自带的提醒功能设置(图文)
  4. zz 邮件列表的文化与礼节
  5. 【前端基础 四】HTML DOM
  6. 生成器 推导式 练习
  7. 内网渗透(八十一)之搭建Exchange服务器
  8. QT5.50+opencv3.0+VS2013安装教程
  9. 架构设计——接口设计
  10. 学python人工智能电脑要什么配置_本人是大一人工智能专业,不打游戏,我们需要学习C++和Python。用什么配置的笔记本电脑比较好?...