进阶技术:Linux Arm32是如何调用C Main的
创始人
2024-05-24 15:53:55
0

前言:

Linux x64通过qemu的rdinit方式调用的C Main,实际上是通过load_elf_binary(加载和解析elf)和start_thread(设置Ip和sp),用缺页异常来调用。关于这点可以看这篇文章:点击查看。那么Arm32里面是如何调用C Main的呢?本篇看下。

概括:

Arm32也是通过load_elf_binary加载被 gcc-arm-linux-gnueabi编译的C Main Demo。同样是也是通过ret_from_fork调用kernel_Init来调用elf_load_binary来操纵elf文件的。这点可以看下堆栈:

* thread #1, stop reason = breakpoint 1.1* frame #0: 0x802fbc0c vmlinux`load_elf_binary(bprm=0x811cd000) at binfmt_elf.c:824:1frame #1: 0x8028eeb0 vmlinux`bprm_execve at exec.c:1735:12frame #2: 0x8028ee38 vmlinux`bprm_execve at exec.c:1776:9frame #3: 0x8028edf4 vmlinux`bprm_execve at exec.c:1845:11frame #4: 0x8028ecd8 vmlinux`bprm_execve(bprm=0x811cd000, fd=-100, filename=0x81160000, flags=0) at exec.c:1807:12frame #5: 0x8028f644 vmlinux`kernel_execve(kernel_filename=, argv=0x80c07714, envp=0x80c077a0) at exec.c:2006:11frame #6: 0x8083d8d8 vmlinux`run_init_process(init_filename=) at main.c:1438:9 [artificial]frame #7: 0x80847010 vmlinux`kernel_init(unused=) at main.c:1534:9frame #8: 0x80100130 vmlinux`ret_from_fork at entry-common.S:146

同样x64和Arm32的用户态入口也是Glibc的_start函数。
那么不同点在哪儿呢?上说过x64设置IP和SP的是start_thread,而Arm32里面则是START_THREAD这个宏定义来设置IP和SP,实质上做的东西是一样的,但是代码不一样,他们都是通过返回到ret_from_fork,然后获取到被设置的regs变量,里面包含了被start_thread OR START_THREAD宏设置的IP和SP。
看下START_THREAD宏定义:

#define START_THREAD	COMPAT_START_THREAD#define COMPAT_START_THREAD(ex, regs, new_ip, new_sp)	\
compat_start_thread(regs, new_ip, new_sp, ex->e_machine == EM_X86_64)void compat_start_thread(struct pt_regs *regs, u32 new_ip, u32 new_sp, bool x32)
{start_thread_common(regs, new_ip, new_sp,x32 ? __USER_CS : __USER32_CS,__USER_DS, __USER_DS);
}static inline void start_thread_common(struct pt_regs *regs, unsigned long pc)
{s32 previous_syscall = regs->syscallno;memset(regs, 0, sizeof(*regs));regs->syscallno = previous_syscall;regs->pc = pc;if (system_uses_irq_prio_masking())regs->pmr_save = GIC_PRIO_IRQON;
}

最后的start_thread_common设置了PC也就上面的IP。Glibc库的入口_start函数。这一点也可以验证下:

arm-linux-gnueabi-gcc -static -o hello hello.c
readelf -s hello
3127: 00010418     0 FUNC    GLOBAL DEFAULT    4 _start

在以下地方下断点

linux-source-5.15 Version b binfmt_elf.c:1325也可以br s --file binfmt_elf.c --line 1325
c运行到此处,再n几次让START_TRHEAD宏运行完如下:
p/x *regs
(pt_regs) $7 = {uregs = {[13] = 0x7e952f10[14] = 0x00000000[15] = 0x00010418[16] = 0x00000010[17] = 0x00000000}

我们看到[15]=后面就是10418,也就是上面Glibc的入口_start函数。

结尾:

这里还有几个问题:
ret_from_fork是否同xx64一样被_schedule和schedule调度。
Arm32的_start调用是否也是缺页异常。
关于这两点下篇再看看。

作者:江湖评谈(jianghupt)公众号同名。欢迎关注我,带你了解高阶技术。image

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...