2023-2024-1 20232808《Linux内核原理分析与设计》第八周作业

本文详细介绍了编译链接过程,包括预处理、编译、汇编和链接,重点讲解了ELF可执行文件格式,涉及exec*库函数、动态链接和内核调试。通过实验和问题解答,阐述了新可执行程序的执行起点和execve系统调用的作用。
摘要由CSDN通过智能技术生成

一、实验相关知识

1.1 编译链接的过程

编译链接是将源代码转换为可执行程序的过程,它通常包括以下几个步骤:

  • 预处理(Preprocessing):
    预处理是编译链接过程的第一步。在这个阶段,预处理器会对源代码进行处理,包括展开宏定义、插入头文件内容等。预处理器根据以字符 ‘#’ 开头的预处理指令(如#include、#define等)修改源代码,生成一个经过预处理的代码文件。
  • 编译(Compilation):
    编译是将预处理后的代码文件转换为汇编代码的过程。编译器会将源代码转换为汇编语言,生成相应的汇编代码文件。在这个过程中,编译器会进行词法分析、语法分析、语义分析等操作,检查代码的正确性,并将其转化为汇编代码。
  • 汇编(Assembly):
    汇编是将汇编代码转换为机器代码的过程。汇编器会读取汇编代码文件,将其转换为机器可执行的指令,生成目标文件。每条汇编指令通常对应一条机器指令,包括操作码、寄存器、内存地址等。
  • 链接(Linking):
    链接是将多个目标文件和库文件合并成一个可执行程序的过程。在链接过程中,链接器会解析目标文件中的符号引用和定义,处理符号表,将各个目标文件之间的引用关系进行连接。链接器还会处理库文件,将需要的库函数与程序进行关联。最终生成一个完整的可执行程序文件。

1.2 ELF 可执行文件格式

在这里插入图片描述

ELF可执行文件格式由以下几个主要组成部分构成:

  • ELF文件头(ELF Header):
    ELF文件头位于文件的开头,包含了描述整个文件的基本信息,如文件类型、目标体系结构、入口点地址、程序头表和节头表的偏移等。文件头提供了读取和解析ELF文件的基本信息。
  • 程序头表(Program Header Table):
    程序头表描述了ELF文件在内存中的布局,包括加载和执行所需的段(段是ELF中的逻辑组织单位)信息。每个程序头表项描述了一个段的起始地址、大小、访问权限等信息,用于操作系统加载和执行可执行文件。
  • 节头表(Section Header Table):
    节头表包含了关于各个节的信息,如代码段、数据段、符号表等。每个节头表项描述了一个节的起始地址、大小、访问权限、符号表索引等信息。节头表对于调试和链接程序非常重要。
  • 节区(Sections):
    节区是ELF文件中的逻辑组织单位,包含各种数据和代码。常见的节区有代码段(.text)、数据段(.data)、只读数据段(.rodata)、符号表(.symtab)等。每个节区可以有不同的属性,如可执行、可写、可读等。
  • 符号表(Symbol Table):
    符号表存储了程序中定义和引用的符号信息,如变量、函数、全局变量等。符号表中的每个符号项包含了符号的名称、类型、大小、绑定等信息,用于链接和调试程序。
    除了上述主要组成部分,ELF文件还包含其他一些辅助信息,如动态链接信息、重定位表、调试信息等,用于支持动态链接、程序调试和代码重定位等功能。

ELF可执行文件格式的设计使得它具有良好的可扩展性和可移植性,能够适应不同的操作系统和目标体系结构。它被广泛应用于各种编程环境和开发工具中,为程序的开发、编译、链接和执行提供了基础支持。

二、实验过程

2.1 编程使用 exec*库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链接和运行时动态链接

git代码

cd LinuxKernel
rm -rf menu
git clone https://github.com/mengning/menu.git

替换原代码
在这里插入图片描述
make 一下,查看exec函数的实现

make rootfs

在这里插入图片描述
int Exec(int argc, char *argv[]):函数定义,函数名为Exec,它接受两个参数:整数argc(命令行参数的数量)和字符指针数组argv(命令行参数的值)。
int pid;:定义一个整数变量pid,用于存储进程ID。
pid = fork();:调用fork()函数创建一个新进程。fork()函数会返回两次:在父进程中返回子进程的PID,在子进程中返回0。
if (pid < 0):检查pid的值。如果pid小于0,表示进程创建失败。
fprintf(stderr,“Fork Failed!”);:如果创建进程失败,将错误消息打印到标准错误流(stderr)。
exit(-1);:退出程序,返回状态码-1。
else if (pid == 0):如果pid等于0,表示这是子进程。
printf(“This is Child Process!\n”);:打印消息,表明这是子进程。
execlp(“/hello”,“hello”,NULL);:使用execlp()函数在子进程中执行程序/hello。execlp()函数会替换当前进程的映像为新的程序映像。
else:如果pid不小于0且不等于0(这应该只在父进程中),表示这是父进程。
printf(“This is Parent Process!\n”);:打印消息,表明这是父进程。
wait(NULL);:父进程等待子进程完成。wait(NULL)会阻塞父进程,直到子进程结束。
printf(“Child Complete!\n”);:当子进程结束时,打印消息。

2.2 通过gdb进行跟踪分析

启动内核并调试

gdb
file linux-3.18.6/vmlinux
target remote:1234

在这里插入图片描述
打断点

b load_elf_binary
b start_thread

在这里插入图片描述

三、实验问题回答

新的可执行程序是从哪里开始执行的?为什么execve系统调用返回后新的可执行程序能顺利执行?对于静态链接的可执行程序和动态链接的可执行程序execve系统调用返回时会有什么不同?
新的可执行程序通过修改内核堆栈eip作为新程序的起点,从new_ip开始执行后start_thread把返回到用户态的位置从int 0x80的下一条指令变成新加载的可执行文件的入口位置。
当执行到execve系统调用时,进入内核态,用execve()加载的可执行文件覆盖当前进程的可执行程序。当execve系统调用返回时,返回新的可执行程序的执行起点(main函数),所以execve系统调用返回后新的可执行程序能顺利执行。execve系统调用返回时,如果是静态链接,elf_entry指向可执行文件规定的头部(main函数对应的位置0x8048***);如果需要依赖动态链接库,elf_entry指向动态链接器的起点。动态链接主要是由动态链接器ld来完成的。

四、AI帮助

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值