linux内存管理 (五) 4 用户空间 运行一个helloword的全部过程解析

根据shell/exec加载elf程序的原理,是先解析ELF,
定位并加载ld-linux.so这个程序加载器(该so路径需要通过ELF直接确定,且不依赖其他库),
然后再解析elf中需要load的共享库,但这之前可以通过LD_PRELOAD来提前预先加载共享库
  • next step
https://zhuanlan.zhihu.com/p/188276588
-------------------------
hello 源代码为
int main(int argc, char const *argv[])
{
        return 0;
}

编译过程为静态编译

hello程序怎么被加载到内存
	1.加载了哪些东西到内存
	2.加载到哪些位置
	3.什么命令A加载的
	4.命令A是如何被加载的

hello程序是怎么开始运行的
	1.hello程序的内存分布
	2.怎么转交给hello运行的
	3.从什么地址开始运行
	4.hello程序返回之后做了什么

-------------------------
hello.c 不变,更改为动态编译
hello.c 添加printf , 更改为动态编译
  • i686 外部程序 执行过程
ELF loader 分为 内核空间Loader(Program loader) 和 用户空间Linker(Program interpreter,ld-xxx.so,/lib/ld-linux.so.2指向的文件)
	Program loader 		内核提供代码,负责加载segement
	Program interpreter C库(glibc)提供代码,负责重定位和链接动态库


注意 ELF image 与 Process Image 的不同
	ELF image 		 : 程序二进制文件,对应实体文件
	Process Image    : 进程在3G虚拟空间的布局,及各个段加载过后的状态

1.用户空间(用户在shell中执行外部程序,通过系统调用进入内核)
	用户在shell执行外部程序
	shell以 fork+exec syscall 的方式执行外部程序
	通过 int 0x80 软件终端触发 kernel 的 exec syscall 服务 (sys_execvp)

2.内核空间(执行Program loader,载入外部程序二进制文件)
	exec syscall 执行 内核中的ELF Loader(Program loader) ,载入与建立 外部程序的 ELF image

3. 整个 Process Image 的建立(内核空间(Loader)和用户空间(Linker))
	3.1 内核空间(Program loader 装入 外部程序的text和data和bss,装入program interpreter,并exec program interpreter)
		Program loader 找到 PT_INTERP segment
		Program loader 将 PT_LOAD segment mapping 为新的 text/data segment
			text segment 由虚拟地址 0x0804 8000 开始 ,data segment 紧跟其后
		Program loader 将 BSS segment 准备好

		Program loader 呼叫 interpreter loader 将 program interpreter(ld.so)载入,并mapping到process memory
			interpreter 的 text segment 由 虚拟地址 0x4000 0000 开始, interpreter 的data segment 紧跟其后
		Program loader 将 process 的 register %eip(user-mode)修改为 program interpreter 的 进入点,并将 %eip设定为usermode 的stack , 从而 program interpreter 开始执行

	3.2 用户空间(program interpreter 加载 外部程序所需的 动态库)
		Program interpreter 会找到 process 所需的 shared library(名称与路径)
		program interpreter 通过mamp ,将 shared library 给 mapping 到 process memory , 以完成整个 Process Image 的建立
		program interpreter 更新共享库的符号表(symbol table)

4.用户空间(program interpreter 调用 外部程序的main函数)
	program interpreter 执行 x86 jump 动作,到 process 的 entry point (entry point被记录在ELF header中)
	程序从entry point(一般为main函数)开始执行

参考文档
内核的工作

	内核首先读取ELF文件头部,再读如各种数据结构,从这些数据结构中可知各段或节的地址及标识,然后调用mmap()把找到的可加载段的内容加载到内存中。同时读取段标记,以标识该段在内存中是否可读、可写、可执行。其中,文本段是程序代码,只读且可执行,而数据段是可读且可写。

	从PT_INTERP的段中找到所对应的动态链接器名称,并加载动态链接器。通常是/lib/ld-linux.so.2.

	内核把新进程的堆栈中设置一些标记对,以指示动态链接器的相关操作。

	内核把控制权传递给动态链接器。

	动态链接器的工作并不是在内核空间完成的, 而是在用户空间完成的, 比如C语言程序则交给C运行时库来完成
	fs/exec.c
	SYSCALL_DEFINE3(execve,const char __user *, filename,const char __user *const __user *, argv,const char __user *const __user *, envp)
		do_execve
			do_execveat_common
				bprm_execve
					exec_binprm
						search_binary_handler
							fmt->load_binary(bprm); // 对应elf为load_elf_binary
							load_elf_binary
								...
								create_elf_tables
								start_thread(regs, elf_entry, bprm->p); 
									// start_thread()这个宏操作会将eip和esp改成新的地址,就使得CPU在返回用户空间时就进入新的程序入口。
									// 如果存在解释器映像,那么这就是解释器映像的程序入口,否则就是目标映像的程序入口。
  • 2 加载应用程序过程中 用户空间的工作
动态链接器的工作

	动态链接器检查程序对共享库的依赖性,并在需要时对其进行加载。

	动态链接器对程序的外部引用进行重定位,并告诉程序其引用的外部变量/函数的地址,此地址位于共享库被加载在内存的区间内。动态链接还有一个延迟定位的特性,即只有在“真正”需要引用符号时才重定位,这对提高程序运行效率有极大帮助。

	动态链接器执行在ELF文件中标记为.init的节的代码,进行程序运行的初始化。 
	动态链接器把控制传递给程序,从ELF文件头部中定义的程序进入点(main)开始执行。在a.out格式和ELF格式中,程序进入点的值是显式存在的,而在COFF格式中则是由规范隐含定义。

	程序开始执行
动态连接器其实 就是 glibc编译出来的 ld.so

ld.so入口为
elf/rtld.c 中的 RTLD_START // RTLD_START 在  sysdeps/i386/dl-machine.h 中定义

ld的整个过程(加载库,调用main)都在 RTLD_START 中描述了,其中调用了 _dl_start (为RTLD_START 的主体部分)
_dl_start 返回了 user program's entry point
RTLD_START
	_dl_start
		_dl_start_final
		...
		_dl_sysdep_start
			dl_main
				process_envars
		return ELF_MACHINE_START_ADDRESS (GL(dl_ns)[LM_ID_BASE]._ns_loaded, entry);
	...
	# Jump to the user's entry point.\n\
	jmp *%edi\n\  // 这一句就是跳转到 应用程序的入口(一般是main函数)
	.previous\n\


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值