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

Linux 内核如何装载和启动一个可执行程序

一、理论知识

1、理解编译链接的过程和ELF可执行文件格式
1.预处理、编译、链接:

1)预处理,处理代码中的宏定义和 include 文件,并做语法检查

gcc -E hello_world.c -o hello_world.i

2)编译,生成汇编代码

gcc -S hello_world.i -o hello_world.s

3)汇编,生成 ELF 格式的目标代码

gcc -c hello_world.s -o hello_world.o

4)链接,生成可执行代码

gcc hello_world.o -o hello_world

5)执行程序

./hello_world hello, world!

该过程总结如下图所示
在这里插入图片描述

2.ELF文件格式

ELF 格式:可执行和可链接(Executable and Linkable Format) 是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储的标准文件格式。

可重定位文件,如:.o 文件,包含代码和数据,可以被链接成可执行文件或共享目标文件,静态链接库属于这类。 可执行文件,如:/bin/bash
文件,包含可直接执行的程序,没有扩展名。
共享目标文件,如:.so文件,包含代码和数据,可以跟其他可重定位文件和共享目标文件链接产生新的目标文件,也可以跟可执行文件结合作为进程映像的一部分。

ELF 文件由 ELF header 和文件数据组成,文件数据包括:

Program header table, 程序头:描述段信息 .text, 代码段:保存编译后得到的指令数据 .data,
数据段:保存已经初始化的全局静态变量和局部静态变量 Section header table, 节头表:链接与重定位需要的数据

二、实验过程

1、编程使用 exec*库函数加载一个可执行文件
动态链接分为可执行程序装载时动态链接和运行时动态链接,如下代码演示了这两种动态链接。
1)运行时动态连接方式

#include <stdio.h>
#include <unistd.h>

int main() {
    char *args[] = {"ls", "-l", NULL}; // 用于传递给被执行程序的命令行参数

    // 使用execvp执行ls命令
    execvp("ls", args);

    // 如果execvp失败,会执行以下代码
    perror("execvp"); // 打印错误信息
    return 1;
}

2)加载动态链接库方式

#include <stdio.h>
#include <dlfcn.h>

int main() {
    // 打开动态链接库
    void *handle = dlopen("./mylib.so", RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "dlopen() error: %s\n", dlerror());
        return 1;
    }

    // 获取共享库中的函数指针
    void (*my_function)() = dlsym(handle, "my_function");
    if (!my_function) {
        fprintf(stderr, "dlsym() error: %s\n", dlerror());
        return 1;
    }

    // 调用共享库中的函数
    my_function();

    // 关闭共享库
    dlclose(handle);

    return 0;
}

2、使用 gdb 跟踪分析一个 execve 系统调用内核处理函数 sys_execve
打开实验楼中的虚拟机,在shell中依次运行以下命令,获取本次实验的代码,并编译运行

cd LinuxKernel
rm menu -rf
git clone https://github.com/mengning/menu.git
cd menu
mv test_exec.c test.c
make rootfs 

效果如下
在这里插入图片描述
然后启动调试内核:
在这里插入图片描述
再开一个shell,打开gdb,并加载符号表,连接到端口1234:
在这里插入图片描述
然后在gdb,中设置断点:这里我设置了sys_execve,load_elf_binary,
start_thread三处断点。
在这里插入图片描述
在这里插入图片描述
然后在menu界面中输入命令:exec ,触发了断点。
在这里插入图片描述
发现程序第一个断点停在了sys_execve处:

在这里插入图片描述
按s,单步执行,发现接下来会运行do_execve:
在这里插入图片描述
按c继续执行,发现接下来停在了load_elf_binary处:
在这里插入图片描述
使用命令po new_ip 查看new_ip的值,它等于0x8048d0a
在这里插入图片描述

三、实验总结

调用execve的可执行程序时,系统调用exceve陷入内核,这时会创建一个新的用户态堆栈,实际是把命令行参数的内容和环境变量的内容通过指针的方式传递给系统调用内核处理函数的,然后内核处理函数在创建可执行程序新的用户态堆栈的时候,会把这些拷贝到用户态堆栈初始化新的可执行程序的执行上下文环境(先函数调用参数传递,再系统调用参数传递)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

20232806安星宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值