load_elf_binary

load_elf_binary 流程:

填充并且检查目标程序ELF头部

load_elf_phdrs加载目标程序的程序头表

如果需要动态链接, 则寻找和处理解释器段

检查并读取解释器的程序表头

装入目标程序的段segment

填写程序的入口地址

create_elf_tables填写目标文件的参数环境变量等必要信息

start_kernel宏准备进入新的程序入口

 ————————————————

版权声明:本文为优快云博主「JeanCheng」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.youkuaiyun.com/gatieme/article/details/51628257

  1.  

 

#if defined(CONFIG_MMU)

{

.procname        = "randomize_va_space",

.data                = &randomize_va_space,

.maxlen                = sizeof(int),

.mode                = 0644,

.proc_handler        = proc_dointvec,

},

#endif

 * This structure is used to hold the arguments that are used when loading binaries.

struct linux_binprm {

内核解析elf文件,随机化。

static int load_elf_binary(struct linux_binprm *bprm)

load_elf_phdrs                 load ELF program headers

 

typedef struct elf64_phdr {------------------------Program Header Table

  Elf64_Word p_type;

  Elf64_Word p_flags;

 

  Elf64_Off p_offset;                /* Segment file offset */

 

  Elf64_Addr p_vaddr;                /* Segment virtual address */

  Elf64_Addr p_paddr;                /* Segment physical address */

 

  Elf64_Xword p_filesz;                /* Segment size in file */

  Elf64_Xword p_memsz;                /* Segment size in memory */

 

  Elf64_Xword p_align;                /* Segment alignment, file & memory */

} Elf64_Phdr;

p_offset : 段偏移,这个段的第一个自己字节到文件开始的距离,也就是该段在文件中的偏移;

p_vaddr : 加载到内存中的虚拟地址;

p_paddr : 加载到内存中的物理地址;

p_filesz : 文件映像中段的大小/字节数,可能是零,比如说GNU_STACK段;

p_memsz : 内存映像中段的大小/字节数,可能是零,比如说GNU_STACK段;

p_flags : flags relevant to the segment. 和这个段有关的标志;

 

来自 <https://mudongliang.github.io/2015/10/31/linuxelf.html>

 

 

if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)---- DGL proc variable

current->flags |= PF_RANDOMIZE;

 

  1.  else if (loc->elf_ex.e_type == ET_DYN) {

if (interpreter) {

load_bias = ELF_ET_DYN_BASE;

if (current->flags & PF_RANDOMIZE)

load_bias += arch_mmap_rnd();

rnd = get_random_long() & ((1UL << mmap_rnd_bits) - 1);

return rnd << PAGE_SHIFT;--------------------------dgl_4k aligned

elf_flags |= elf_fixed;

} else

load_bias = 0;

 

 

  1. if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) {

current->mm->brk = current->mm->start_brk =

arch_randomize_brk(current->mm);

 

void setup_new_exec(struct linux_binprm * bprm)

void arch_pick_mmap_layout(struct mm_struct *mm, struct rlimit *rlim_stack)

{

unsigned long random_factor = 0UL;

 

if (current->flags & PF_RANDOMIZE)

random_factor = arch_mmap_rnd();

 

mm->mmap_base = mmap_base(random_factor, rlim_stack);

create_elf_tables

elf_map

vm_mmap

start_thread

regs->pc = pc;

regs->sp = sp;

 

It sounds like your GCC is configured to build -pie binaries by default. These binaries really areshared libraries

 (of type ET_DYN), except they run just like a normal executable would.

 

来自 <https://stackoverflow.com/questions/34519521/why-does-gcc-create-a-shared-object-instead-of-an-executable-binary-according-to/34522357#34522357>

Or you could link your binary with gcc -no-pie ... and that should produce a non-PIE executable of type

 ET_EXEC, for which file will say ELF 64-bit LSB executable.

 

来自 <https://stackoverflow.com/questions/34519521/why-does-gcc-create-a-shared-object-instead-of-an-executable-binary-according-to/34522357#34522357>

 

使用通俗易懂的语言举例并详细解释Shell进程之后的内容 预处理hello.c,主要是处理程序里面的文件包含、处理宏定义、条件编译。 把c文件编译成为汇编文件(.s),其中进行了词法分析,语法分析,语义分析、生成中间代码、对代码进行优化等工作。 把汇编文件(.s)编译成可重定位文件(.o)。 把可重定位文件(.o)链接成为可执行文件,其中链接可分为静态链接和动态链接 静态链接:在编译阶段就会把所有用到的库打包到自己的可执行程序中,其优点是具有较好的兼容性,不依赖外部环境,但是生成的程序比较大。 动态链接:在应用程序运行时,链接器去加载外部的共享库,并完成共享库和动态编译程序之间的链接。不同的程序可以共用代码库,节省内存空间。 控制台输入./hello命令后,Shell会创建一个新的进程来执行该程序。fork()函数就是用于创建一个新的进程的。这里的进程可以先简单理解为程序的容器。 exeve()函数可以理解为向上一步新建的进程,填充一个可执行程序(hello)。 sys_execve()函数为linux系统调用,被exeve()函数调用,这里的系统调用可以理解为是操作系统系统开放给用户的最底层接口。 do_exeve()函数是sys_execve()函数的核心。 load_elf_binary()函数会去文件系统中读取hello程序到内存,然后判断它是否是动态链接的可执行程序,如果不是,则进一步判断是否是静态链接的文件。 ld-linux-xx.so是glibc库中的动态连接器。如果hello程序是动态链接程序,该动态链接器会去加载共享库,并完成共享库和程序的链接工作, 然后准备真正开始执行hell程序。 相反,如果hello程序是静态编译的程序,则无需再加载链接共享库,直接开始准备执行hello程序。 第10和11步分别执行之后.都会开始执行hello程序,_start是程序的真正入口,而该符号在glibc中。也就是说程序的真正入口在glibc。 __libc_start_main()也是glibc中的函数,用于在执行用户程序前进行一些初始化工作。 调用用户程序中的mian()函数,开始执行printf打印函数。 程序执行完了之后,调用glibc库中的_exit()函数,来结束当前进程。
07-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值