-
-
PWN入门-7-难逃系统调用2-VDSO与VSYSCALL
-
发表于: 2024-8-4 17:47 3930
-
在PWN入门-6-难逃系统调用1
中对系统调用的处理流程进行较为详细的解析,其中在解析IDT表中陷阱初始化时,发现该表只会处理int 0x80
软中断发起的系统调用,而直接通过x86_64指令集中系统调用指令发起的系统调用则会交给MSR寄存器中的STAR
和LSTAR
进行处理,这两个寄存器会保存对应的系统调用处理函数地址。
在Linux内核的早期,由于x86指令集并没有专门的指令处理系统调用,所以Linux内核通过int 0x80
来触发软中断,随着时间的推移x86_64指令集提供了专门的系统调用指令,但是不同的处理器实现是不一样的,对于用户态程序来讲,它有天然的理由不去理会这些问题,“都是内核和处理器的原因啊,为什么要我管”,处理器当然也是一副硬姿态,反正我就这样实现了,内核你看着办吧!
因此没有办法的内核,只能担负起处理这个“脏”任务的责任,内核的处理方式是这样的:在启动阶段根据具体处理器加载不同的系统调用处理镜像,程序发起系统调用时会将执行权限交给系统调用处理镜像,而不需要考虑具体的实现。
VSyscall的初始化是在内核启动阶段进行的,它由著名start_kernel
启动函数发起指定架构的初始化操作setup_arch
,在setup_arch
函数的内部会针对系统调用映射内存页,提供给用户态程序使用。
VSyscall依赖编译选项CONFIG_X86_VSYSCALL_EMULATION
,只有当它开启时,VSyscall的功能才会被启用。
当map_vsyscall
函数开始时,它会先进行一个很重要的操作,就是获取VSyscall所在内存页的物理地址。
__pa_symbol
宏是Linux中较为常见的一种宏,它接收一个地址作为参数,然后减去内核映射的基地址获取偏移值,最后加上物理地址的基地址,获取形参地址对应的物理地址。
根据__START_KERNEL_map
可以知道内核映射的基地址是0xffffffff80000000。
在获取__vsyscall_page
的物理地址physaddr_vsyscall
后,会对其所在内存页的属性进行设置,设置的依据是vsyscall_mode
,它有两种设置方式,一是根据内核配置选项CONFIG_LEGACY_VSYSCALL_XONLY
设定的默认值,二是根据内核命令行参数进行配置。
early_param
宏会将名称及符号放入.init.setup
节中,不同表项间通过拼接的符号__setup_##unique_id
进行区分。
除了early_param
宏外,__setup
宏也会这样操作,__setup
和early_param
的归宿都是__setup_param
宏,该宏声明的.init.setup
节内元素会在内核初始化时进行使用。
每个表项都会按照obs_kernel_param
结构体的结构进行放置。
内核针对__setup_param
进行初始化的地方分成两个部分,第一部分是early_param
对应的parse_early_param
函数,第二部分则是after_dashes
对应__setup
。
不管哪一个部分,最终的目标都是通过parse_args
对命令行参数进行解析,然后根据指定的处理函数parse_unknown_fn
(parse_args
中最后一个参数)对参数的要求进行处理。
__setup
和early_param
的区别在于,early_param
用于解析静态命令行参数,__setup
用于解析动态命令行参数。为了保障动态命令行参数设置的值是生效的,所以__setup
对应的解析处理会晚于early_param
宏。
静态命令行参数是内核编译时确定下来的,如ARM设备上DTS中的chosen
节点中的bootargs
项,以及x86_64设备上保存在BIOS内的参数real_mod_date
(上电后先进入实模式)。而动态命令行参数指的则是启动引导程序传递给内核的参数,如UEFI中的GRUB配置文件,以及UBoot内向内核传递的参数。
除此之外,__setup
和early_param
另一个区别在于参数的预处理函数不同,__setup
会把解析好的参数交给unknown_bootoption
函数,而early_param
则会交给do_early_param
函数。
两个预处理函数最终都会将处理权限交给通过__setup_param
宏指定的处理函数,且都是用__setup_start
和__setup_end
判断.init.setup
节的区间,两者的主要区别在于__setup_start
不会对early
为真的参数进行处理,但__setup_param
就会。
__setup_start
和__setup_end
会在vmlinux链接时产生,vmlinux.lds.S
汇编代码会让它们标记.init.setup
节的起始位置。
从上面可以看到.init.setup
被放置于.init.data
节当中。
不过可惜的是,当Linux完成启动后.init.xxx
节的信息就会被释放掉,因此内核当中写入.init.xxx
中的信息都是无法在Linux运行阶段查看的。
需要释放的内存区域会在vmlinux.ld.S
内标记出来。
对于VSyscall来讲,模式不同对内存页设置的参数也会不同。
当模式为EMULATE
时,会通过__set_fixmap
将Vsyscall处理函数映射到指定的位置,VSYSCALL_PAGE
是VSyscall虚拟地址对应的偏移值,physaddr_vsyscall
是Vsyscall处理函数的物理地址,PAGE_KERNEL_VVAR
是内存页的属性。
PAGE_KERNEL_VVAR
对应的页属性在下方进行了展示,其中__PP
代表当前内存页位于内存中,首个0代表内存页不可写不可读,_USR
代表该内存页可以用户态程序访问,___A
代表线性地址转换中该表项被使用,__NX
代表内存页开启数据执行保护,第二个0代表该内存页未被写入数据,第三个0代表表项直接未被直接映射到内存页,___G
代表该页的TLB是全局的。
set_vsyscall_pgtable_user_bits
函数的作用是确保VSyscall所在内存页是设置了_PAGE_USER
的。
当模式为XONLY
时,内存页的设置方式会发生改变。
在这里VSyscall的内存信息会被标记在gate_vma
当中,该结构体会在内核初始化时借助__get_user_pages
和in_gate_area
分配用户态可以使用的内存页。
拿到gate_vmd
的地址后就会通过vm_flags_init
将内存页标记为可执行的状态。
通过下面的驱动代码,我们可以检验VSyscall的内存信息。
可以获取的内存信息还有很多,这里只打印了页名以及页范围,从下面可以看到VSyscall的内存信息的确是上面论证的结果。
其中VSyscall的起始地址是VSYSCALL_ADDR
宏规定的,并且通过观察不难指定,VSyscall的地址永远都是固定的,不会产生变化。
下面展示VSyscall的处理代码,Linux在启动阶段根据处理器加载对应的VSyscall信息,比如下方展示的存放系统调用号的rax
寄存器,以及触发系统调用的指令syscall
都是根据x64架构的处理器特殊设置的。
VDSO由init_vdso_image_xx
进行初始化,该函数被注册到了驱动初始化列表内,内核启动时会遍历该列表,执行初始化动作,init_vdso_image_xx
函数这里使用的subsys_initcall
。
在Linux内核当中,经常可以看到某某驱动初始化函数由xxx_initcall
进行注册,然后内核就会在启动阶段对它进行调用。
根据指定的xxx_initcall
不同,驱动初始化的级别也会不同,被注册的信息会被放入.init.data
节内,并且针对不同级别进行区分,每个级别都有一段单独的空间。
内核启动时需要加载的驱动,都位于.initcallxx
里面了。内核真正加载驱动时,首先会通过do_initcalls
遍历所有的级别,每个级别内的驱动由do_initcall_level
函数进行遍历并加载,遍历的依据就是vmlinux.lds文件内针对指定级别生成的地址,比如级别0对应着__initcall0_start
。
VDSO的生成分成用户态和内核态两大部分,下面会对各部分分别进行解析。
vdso.so是专门提供给用户态程序使用,它的生成分成32位和64位两个部分。
不同位的so通过指定的文件编译产生,每个文件生成单独.o
文件后,会先链接成vdso32.so.dbg
文件,最后由objcopy
生成最终的动态链接库。
linux-vdso.so.1
的名字也是Makefile内指定的。
内核首先处理的文件是vma.c
和extable.c
,它们是内核操作VDSO的核心逻辑所在。
在此之后,会有两个特别的文件,它们以vdso-image-xx.c
为标志,这两个文件并不是默认就有的,而是在编译过程中产生的.c
文件,然后再对它们进行编译。
它们通过vdso2c
可执行文件进行产生,该可执行文件由vdso2c.c
和vdso2c.h
编译而成,程序内部通过go
函数写入具体的内容,写入的内容是根据前面生成的vdso.so而产生的。
vdso2c
程序接收3个参数,第一个参数是vdsoxx.so.dbg
,第二个参数vdso.so
,第三个参数是生成文件的名字,传入带有调试符号版本的so
文件,主要目的是辅助展示vdso_image
的信息。
生成的文件由下面五大部分组成,其中raw_data
是根据vdso.so
生成的(包含动态链接库的原始数据),extable
是根据vdsoxx.so.dbg
生成的,结构体struct vdso_image
用于描述VDSO的信息。
非调试版本的动态链接库只会负责原始二进制文件内信息的展示,至于vdso_image
需要的其他信息,则要借助调试版本进行产生,调试版本展示的信息有两类,一是部分节的位置信息,二是被剥离出来的__ex_table
节信息。
vdso_image
中的alt
对应着.altinstructions
节,该节的全称是Alternative Instructions
代替指令,是为指令集的扩展功能而准备的,alternative
会将可以替换的指令放入.altinstructions
节内,留给内核在运行时决定是否使用指令集的新指令。
__ex_table
节是调试版中被剥离出去的节,其中ex
是异常的缩写,该节定义了异常的处理方式,vdso_image
中特地将它添加了进来,用于处理VDSO的异常情况。
vvar
对应着内核态与用户态共享数据的内存页,pvclock
代表着管道监控数据进度的内存页,hvclock
代表宿主机与虚拟机间通信的内存页,timens
代表时钟命名空间的内存页。
在内核启动时,subsys_initcall
会起到初始化的作用,并将VDSO的处理权限交给init_vdso_image
函数,该函数内部实现比较简单,只是通过apply_alternatives
函数对旧指令进行替换。
在Linux内核当中,处理程序运行的是著名的load_elf_binary
函数。该函数会在读取ELF文件段信息之后,设置内存信息之间对VDSO进行加载。
load_elf_binary
函数会让arch_setup_additional_pages
函数通过map_vdso
去映射VDSO给当前进程。
map_vdso
函数第一步做的是通过get_unmapped_area
接口申请一段未使用的内存。get_unmapped_area
函数的第二个参数是待分配的内存地址,地址为零就代表申请未使用的内存,否则则按指定的地址进行查找,三号参数指定了申请的内存空间大小。
值得注意的是,这里符号地址使用的均是负数,当get_unmapped_area
分配出image
和vvar
的空间后,会使用text_start
作为分割,addr
到text_start
是vvar的范围,text_start
往上才是vdso的范围。
这一点也可以在_install_special_mapping
中获得验证,其中text_start
对应着vdso_mapping
,addr
对应着vvar_mapping
,_install_special_mapping
函数会将分配好的内存空间信息vma
填入内存管理结构体mm
中,如果_install_special_mapping
函数没有出错,那么就会继续往内存管理结构体mm
填写vdso的信息。
此时内核就完成了加载程序的vdso和vvar的任务。
VDSO和Vsyscall实现的系统调用并不多,目前支持的系统调用均列在了下方,它们是需要经常使用的系统调用。
用户态程序对VDSO及VSyscall的使用,是在LD与GLibC的帮助下运行的,程序使用GlibC封装的函数时,LD会将它们解析到VDSO或VSyscall内。
LD程序的_start
函数缸开始运行时,可以发现栈空间内已经有一部分空间被赋值了,其中最具有标志性的信息就是环境变量字符串了。显然这些信息是内核进行赋值的。
考虑到程序所需的信息是内核进行操作的,程序有使用它们的必要性,所以内核会将这些信息放入程序可以使用的栈空间内。内核压入栈空间内的数据通过create_elf_tables
函数进行操作,数据分成下面的四种,第一种是程序的命令行参数数量,第二种是命令行参数,第三种是环境变量参数,第四种是辅助向量(包含程序需要的辅助信息)。
辅助向量有非常多的种类,从AT_BASE
一直到AT_UID
,具体的向量及其含义可以通过man
手册进行查看,Linux内核当中,向量数据通过NEW_AUX_ENT
接口进行放置(首个参数为向量,第二个参数为数值)。在create_elf_tables
函数中VDSO的起始地址是第一个压入的向量,在create_elf_tables
函数的内部,它通过saved_auxv
保存向量信息,向量id占据前八个字节,数值占据后八个字节,索引saved_auxv
时,通过压入的顺序进行索引,比如VDSO的起始地址就对应着saved_auxv[1]
。
Linux下可以通过设置LD_SHOW_AUXV=1
查看辅助向量的内容,这里借用的是LD的参数。
VSyscall和VDSO都是内核映射到用户空间的,其中VSyscall使用的是固定地址,在任何情况下都是一样的,而VDSO则是程序每次运行时新分配地址,所以地址是会动态变化的。
VSyscall映射中虽然没有什么可以利用的空间,但是如果由于它固定的地址,以及含有ret
指令的特性,我们可以不断利用它滑动到我们期望的位置,再进行利用。
在这里我大胆下一个判断VDSO就是一个废物。
首先不管64位还是32位的VDSO,它们对于用户态程序都是不可感知的,想要了解动态链接库中的内容就只有两个选择,一是将内存转储出来,二是找到内核编译出来的动态链接库。
其次目前的VDSO内支持的功能少之又少,对于64位而言,想要构造ROP是相当困难的,相当于在沙漠里找水喝,并且VDSO与其他动态链接库相比,并没有很明显的优势,而且内核向栈上提交的信息也不止VDSO一个,为什么不利用其他的信息呢?
当然32位的VDSO中存在的函数会更多一些,利用的机会也会更大,但应该将它作为第一选择吗。
下面给出了示例代码的反汇编结果,程序会接收两次输入,然后通过user_check
函数检查输入的信息,如果正确就会调用Shell,反之则不会。
下面展示了程序及系统目前的保护措施,出来金丝雀之外的其他保护已经全部开启了,并且程序具有明显的栈溢出漏洞,固定地址的VSyscall刚好可以辅助我们完成栈溢出的利用。
在main
函数之前LD和LibC做了大量的工作,其中与main
函数最为贴近的就是__libc_init_first
函数,这个函数是__libc_start_main
进行调用的。
__libc_init_first
函数会对主程序的main
函数进行调用,下面对其汇编代码进行了分析。
显然rbp-0x78
的位置保存着main
函数的起始地址,它与main
函数的rbp
间隔了0x32的空间。如果我们利用VSyscall对这片区域进行填充,然后借助rbp-0x78
中的高位地址,仅对第2个字节进行覆盖,就可以使得程序的控制流返回到system("/bin/sh")
的位置。
system
函数在程序中被调用的偏移值是0x1258,程序的基地址一定是按照页大小(0x1000)进行偏移的,所以高4个比特位不能确定,需要通过爆破的方式进行猜测,不过4个比特位对应着1/16
的概率,已经相当高了。
根据上面的分析得到下面的利用脚本。由于程序访问错误的地址会收到段错误信号SIGSEGV
,因此这里可以通过recv
进行接收,并通过except
对收到的错误进行处理,这里选择的是继续循环执行。
运行利用脚本后,就可以成功的获取Shell。通过VSyscall获得,我们成功绕过了除金丝雀外的NX、ASLR、PTE、FULL-Relro保护机制,并且在不进行信息泄露的情况下,完成了PWN,这是相当成功的。
值得注意的是,VSyscall的固定地址并不是一直可用的,比如下方展示了一段出现在内核消息dmesg
中的内容,它显示程序访问地址ffffffffff600000
时出现错误。
VSyscall已经渐渐退出Linux舞台,甚至在有些发行版本中直接将它去除掉了,即使没有去除,保留下来的VSyscall也只剩下零星几个的系统调用可以利用的空间实在是少的可怜(现在可能一共就9条汇编指令)。所谓麻绳专挑细处断,Linux内核在本就孱弱的可利用空间上由砍了一刀,做出了下方的check_fault
检查。
这个安全检查做的事情并不复杂,可以分成两个部分,首先检查的内容是系统调用的所需参数(根据调用协议的寄存器获取数据),检查的依据是用户态程序的最大地址0x7ffffffff000
,检测到地址不是用户态的地址就会报错。
第二部是系统调用执行是的put_user
,该函数会讲输入放入指定的地址内,如果地址是不可写的就会出错。
由于si
的地址是0x555555556009
,由于该地址所在内存页是不可写的,内核判断它可能会是恶意的利用,所以这里就直接设置段错误了,不让你继续往下运行。
在使用VSyscall进行滑动时,需要注意rdi
、rsi
的数值,避免内核检查时出错,如果确定rdi
、rsi
中数值有误,那么就无法使用VSyscall进行滑动。最好是通过VSyscall中的__NR_time
进行滑动(偏移0x400),因为它只接收一个参数,所以只会对rdi
进行检查,因此对__NR_time
进行利用会更加轻松一些。
asmlinkage __visible __init __no_sanitize_address __noreturn __no_stack_protector
void start_kernel(void)
{
......
setup_arch(&command_line);
......
}
void __init setup_arch(char
*
*
cmdline_p)
{
......
map_vsyscall();
......
}
asmlinkage __visible __init __no_sanitize_address __noreturn __no_stack_protector
void start_kernel(void)
{
......
setup_arch(&command_line);
......
}
void __init setup_arch(char
*
*
cmdline_p)
{
......
map_vsyscall();
......
}
#ifdef CONFIG_X86_VSYSCALL_EMULATION
extern void map_vsyscall(void);
extern void set_vsyscall_pgtable_user_bits(pgd_t
*
root);
/
*
*
Called on instruction fetch fault
in
vsyscall page.
*
Returns true
if
handled.
*
/
extern
bool
emulate_vsyscall(unsigned
long
error_code,
struct pt_regs
*
regs, unsigned
long
address);
#else
static inline void map_vsyscall(void) {}
static inline
bool
emulate_vsyscall(unsigned
long
error_code,
struct pt_regs
*
regs, unsigned
long
address)
{
return
false;
}
#endif
#ifdef CONFIG_X86_VSYSCALL_EMULATION
extern void map_vsyscall(void);
extern void set_vsyscall_pgtable_user_bits(pgd_t
*
root);
/
*
*
Called on instruction fetch fault
in
vsyscall page.
*
Returns true
if
handled.
*
/
extern
bool
emulate_vsyscall(unsigned
long
error_code,
struct pt_regs
*
regs, unsigned
long
address);
#else
static inline void map_vsyscall(void) {}
static inline
bool
emulate_vsyscall(unsigned
long
error_code,
struct pt_regs
*
regs, unsigned
long
address)
{
return
false;
}
#endif
void __init map_vsyscall(void)
{
extern char __vsyscall_page;
unsigned
long
physaddr_vsyscall
=
__pa_symbol(&__vsyscall_page);
/
*
*
For full emulation, the page needs to exist
for
real. In
*
execute
-
only mode, there
is
no PTE at
all
backing the vsyscall
*
page.
*
/
if
(vsyscall_mode
=
=
EMULATE) {
__set_fixmap(VSYSCALL_PAGE, physaddr_vsyscall,
PAGE_KERNEL_VVAR);
set_vsyscall_pgtable_user_bits(swapper_pg_dir);
}
if
(vsyscall_mode
=
=
XONLY)
vm_flags_init(&gate_vma, VM_EXEC);
BUILD_BUG_ON((unsigned
long
)__fix_to_virt(VSYSCALL_PAGE) !
=
(unsigned
long
)VSYSCALL_ADDR);
}
void __init map_vsyscall(void)
{
extern char __vsyscall_page;
unsigned
long
physaddr_vsyscall
=
__pa_symbol(&__vsyscall_page);
/
*
*
For full emulation, the page needs to exist
for
real. In
*
execute
-
only mode, there
is
no PTE at
all
backing the vsyscall
*
page.
*
/
if
(vsyscall_mode
=
=
EMULATE) {
__set_fixmap(VSYSCALL_PAGE, physaddr_vsyscall,
PAGE_KERNEL_VVAR);
set_vsyscall_pgtable_user_bits(swapper_pg_dir);
}
if
(vsyscall_mode
=
=
XONLY)
vm_flags_init(&gate_vma, VM_EXEC);
BUILD_BUG_ON((unsigned
long
)__fix_to_virt(VSYSCALL_PAGE) !
=
(unsigned
long
)VSYSCALL_ADDR);
}
#ifdef __ASSEMBLY__
#define _AC(X,Y) X
#define _AT(T,X) X
#else
#define __AC(X,Y) (X##Y)
#define _AC(X,Y) __AC(X,Y)
#define _AT(T,X) ((T)(X))
#endif
#define __START_KERNEL_map _AC(0xffffffff80000000, UL)
static __always_inline unsigned
long
__phys_addr_nodebug(unsigned
long
x)
{
unsigned
long
y
=
x
-
__START_KERNEL_map;
/
*
use the carry flag to determine
if
x was < __START_KERNEL_map
*
/
x
=
y
+
((x > y) ? phys_base : (__START_KERNEL_map
-
PAGE_OFFSET));
return
x;
}
#define __phys_addr(x) __phys_addr_nodebug(x)
#ifndef __pa
#define __pa(x) __phys_addr((unsigned long)(x))
#endif
#ifdef __ASSEMBLY__
#define _AC(X,Y) X
#define _AT(T,X) X
#else
#define __AC(X,Y) (X##Y)
#define _AC(X,Y) __AC(X,Y)
#define _AT(T,X) ((T)(X))
#endif
#define __START_KERNEL_map _AC(0xffffffff80000000, UL)
static __always_inline unsigned
long
__phys_addr_nodebug(unsigned
long
x)
{
unsigned
long
y
=
x
-
__START_KERNEL_map;
/
*
use the carry flag to determine
if
x was < __START_KERNEL_map
*
/
x
=
y
+
((x > y) ? phys_base : (__START_KERNEL_map
-
PAGE_OFFSET));
return
x;
}
#define __phys_addr(x) __phys_addr_nodebug(x)
#ifndef __pa
#define __pa(x) __phys_addr((unsigned long)(x))
#endif
static enum { EMULATE, XONLY, NONE } vsyscall_mode __ro_after_init
=
#ifdef CONFIG_LEGACY_VSYSCALL_NONE
NONE;
#elif defined(CONFIG_LEGACY_VSYSCALL_XONLY)
XONLY;
#else
#error VSYSCALL config is broken
#endif
static
int
__init vsyscall_setup(char
*
str
)
{
if
(
str
) {
if
(!strcmp(
"emulate"
,
str
))
vsyscall_mode
=
EMULATE;
else
if
(!strcmp(
"xonly"
,
str
))
vsyscall_mode
=
XONLY;
else
if
(!strcmp(
"none"
,
str
))
vsyscall_mode
=
NONE;
else
return
-
EINVAL;
return
0
;
}
return
-
EINVAL;
}
early_param(
"vsyscall"
, vsyscall_setup);
static enum { EMULATE, XONLY, NONE } vsyscall_mode __ro_after_init
=
#ifdef CONFIG_LEGACY_VSYSCALL_NONE
NONE;
#elif defined(CONFIG_LEGACY_VSYSCALL_XONLY)
XONLY;
#else
#error VSYSCALL config is broken
#endif
static
int
__init vsyscall_setup(char
*
str
)
{
if
(
str
) {
if
(!strcmp(
"emulate"
,
str
))
vsyscall_mode
=
EMULATE;
else
if
(!strcmp(
"xonly"
,
str
))
vsyscall_mode
=
XONLY;
else
if
(!strcmp(
"none"
,
str
))
vsyscall_mode
=
NONE;
else
return
-
EINVAL;
return
0
;
}
return
-
EINVAL;
}
early_param(
"vsyscall"
, vsyscall_setup);
#define __setup_param(str, unique_id, fn, early) \
static const char __setup_str_
##unique_id[] __initconst \
__aligned(
1
)
=
str
; \
static struct obs_kernel_param __setup_
##unique_id \
__used __section(
".init.setup"
) \
__aligned(__alignof__(struct obs_kernel_param)) \
=
{ __setup_str_
##unique_id, fn, early }
#define __setup(str, fn) \
__setup_param(
str
, fn, fn,
0
)
#define early_param(str, fn) \
__setup_param(
str
, fn, fn,
1
)
#define __setup_param(str, unique_id, fn, early) \
static const char __setup_str_
##unique_id[] __initconst \
__aligned(
1
)
=
str
; \
static struct obs_kernel_param __setup_
##unique_id \
__used __section(
".init.setup"
) \
__aligned(__alignof__(struct obs_kernel_param)) \
=
{ __setup_str_
##unique_id, fn, early }
#define __setup(str, fn) \
__setup_param(
str
, fn, fn,
0
)
#define early_param(str, fn) \
__setup_param(
str
, fn, fn,
1
)
struct obs_kernel_param {
const char
*
str
;
int
(
*
setup_func)(char
*
);
int
early;
};
struct obs_kernel_param {
const char
*
str
;
int
(
*
setup_func)(char
*
);
int
early;
};
void __init parse_early_options(char
*
cmdline)
{
parse_args(
"early options"
, cmdline, NULL,
0
,
0
,
0
, NULL,
do_early_param);
}
void __init parse_early_param(void)
{
static
int
done __initdata;
static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata;
if
(done)
return
;
/
*
All
fall through to do_early_param.
*
/
strscpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
parse_early_options(tmp_cmdline);
done
=
1
;
}
asmlinkage __visible __init __no_sanitize_address __noreturn __no_stack_protector
void start_kernel(void)
{
......
parse_early_param();
after_dashes
=
parse_args(
"Booting kernel"
,
static_command_line, __start___param,
__stop___param
-
__start___param,
-
1
,
-
1
, NULL, &unknown_bootoption);
......
}
void __init parse_early_options(char
*
cmdline)
{
parse_args(
"early options"
, cmdline, NULL,
0
,
0
,
0
, NULL,
do_early_param);
}
void __init parse_early_param(void)
{
static
int
done __initdata;
static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata;
if
(done)
return
;
/
*
All
fall through to do_early_param.
*
/
strscpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
parse_early_options(tmp_cmdline);
done
=
1
;
}
asmlinkage __visible __init __no_sanitize_address __noreturn __no_stack_protector
void start_kernel(void)
{
......
parse_early_param();
after_dashes
=
parse_args(
"Booting kernel"
,
static_command_line, __start___param,
__stop___param
-
__start___param,
-
1
,
-
1
, NULL, &unknown_bootoption);
......
}
static
int
parse_one(char
*
param,
char
*
val,
const char
*
doing,
const struct kernel_param
*
params,
unsigned num_params,
s16 min_level,
s16 max_level,
void
*
arg, parse_unknown_fn handle_unknown)
{
......
if
(handle_unknown) {
pr_debug(
"doing %s: %s='%s'\n"
, doing, param, val);
return
handle_unknown(param, val, doing, arg);
}
......
}
char
*
parse_args(const char
*
doing,
char
*
args,
const struct kernel_param
*
params,
unsigned num,
s16 min_level,
s16 max_level,
void
*
arg, parse_unknown_fn unknown)
{
......
ret
=
parse_one(param, val, doing, params, num,
min_level, max_level, arg, unknown);
......
}
static
int
parse_one(char
*
param,
char
*
val,
const char
*
doing,
const struct kernel_param
*
params,
unsigned num_params,
s16 min_level,
s16 max_level,
void
*
arg, parse_unknown_fn handle_unknown)
{
......
if
(handle_unknown) {
pr_debug(
"doing %s: %s='%s'\n"
, doing, param, val);
return
handle_unknown(param, val, doing, arg);
}
......
}
char
*
parse_args(const char
*
doing,
char
*
args,
const struct kernel_param
*
params,
unsigned num,
s16 min_level,
s16 max_level,
void
*
arg, parse_unknown_fn unknown)
{
......
ret
=
parse_one(param, val, doing, params, num,
min_level, max_level, arg, unknown);
......
}
static
bool
__init obsolete_checksetup(char
*
line)
{
const struct obs_kernel_param
*
p;
bool
had_early_param
=
false;
p
=
__setup_start;
do {
int
n
=
strlen(p
-
>
str
);
if
(parameqn(line, p
-
>
str
, n)) {
if
(p
-
>early) {
/
*
Already done
in
parse_early_param?
*
(Needs exact match on param part).
*
Keep iterating, as we can have early
*
params
and
__setups of same names
8
(
*
/
if
(line[n]
=
=
'\0'
|| line[n]
=
=
'='
)
had_early_param
=
true;
}
else
if
(!p
-
>setup_func) {
pr_warn(
"Parameter %s is obsolete, ignored\n"
,
p
-
>
str
);
return
true;
}
else
if
(p
-
>setup_func(line
+
n))
return
true;
}
p
+
+
;
}
while
(p < __setup_end);
return
had_early_param;
}
static
int
__init unknown_bootoption(char
*
param, char
*
val,
const char
*
unused, void
*
arg)
{
......
if
(obsolete_checksetup(param))
return
0
;
......
}
static
int
__init do_early_param(char
*
param, char
*
val,
const char
*
unused, void
*
arg)
{
const struct obs_kernel_param
*
p;
for
(p
=
__setup_start; p < __setup_end; p
+
+
) {
if
((p
-
>early && parameq(param, p
-
>
str
)) ||
(strcmp(param,
"console"
)
=
=
0
&&
strcmp(p
-
>
str
,
"earlycon"
)
=
=
0
)
) {
if
(p
-
>setup_func(val) !
=
0
)
pr_warn(
"Malformed early option '%s'\n"
, param);
}
}
/
*
We accept everything at this stage.
*
/
return
0
;
}
static
bool
__init obsolete_checksetup(char
*
line)
{
const struct obs_kernel_param
*
p;
bool
had_early_param
=
false;
p
=
__setup_start;
do {
int
n
=
strlen(p
-
>
str
);
if
(parameqn(line, p
-
>
str
, n)) {
if
(p
-
>early) {
/
*
Already done
in
parse_early_param?
*
(Needs exact match on param part).
*
Keep iterating, as we can have early
*
params
and
__setups of same names
8
(
*
/
if
(line[n]
=
=
'\0'
|| line[n]
=
=
'='
)
had_early_param
=
true;
}
else
if
(!p
-
>setup_func) {
pr_warn(
"Parameter %s is obsolete, ignored\n"
,
p
-
>
str
);
return
true;
}
else
if
(p
-
>setup_func(line
+
n))
return
true;
}
p
+
+
;
}
while
(p < __setup_end);
return
had_early_param;
}
static
int
__init unknown_bootoption(char
*
param, char
*
val,
const char
*
unused, void
*
arg)
{
......
if
(obsolete_checksetup(param))
return
0
;
......
}
static
int
__init do_early_param(char
*
param, char
*
val,
const char
*
unused, void
*
arg)
{
const struct obs_kernel_param
*
p;
for
(p
=
__setup_start; p < __setup_end; p
+
+
) {
if
((p
-
>early && parameq(param, p
-
>
str
)) ||
(strcmp(param,
"console"
)
=
=
0
&&
strcmp(p
-
>
str
,
"earlycon"
)
=
=
0
)
) {
if
(p
-
>setup_func(val) !
=
0
)
pr_warn(
"Malformed early option '%s'\n"
, param);
}
}
/
*
We accept everything at this stage.
*
/
return
0
;
}
vmlinux.lds.S:
INIT_DATA_SECTION(
16
)
vmlinux.lds.h:
#define INIT_SETUP(initsetup_align) \
.
=
ALIGN(initsetup_align); \
BOUNDED_SECTION_POST_LABEL(.init.setup, __setup, _start, _end)
#define INIT_DATA_SECTION(initsetup_align) \
.init.data : AT(ADDR(.init.data)
-
LOAD_OFFSET) { \
INIT_DATA \
INIT_SETUP(initsetup_align) \
INIT_CALLS \
CON_INITCALL \
INIT_RAM_FS \
}
生成的vmlinux.lds:
.init.data : AT(ADDR(.init.data)
-
0xffffffff80000000
) {
......
__setup_start
=
.; KEEP(
*
(.init.setup)) __setup_end
=
.;
......
}
vmlinux.lds.S:
INIT_DATA_SECTION(
16
)
vmlinux.lds.h:
#define INIT_SETUP(initsetup_align) \
.
=
ALIGN(initsetup_align); \
BOUNDED_SECTION_POST_LABEL(.init.setup, __setup, _start, _end)
#define INIT_DATA_SECTION(initsetup_align) \
.init.data : AT(ADDR(.init.data)
-
LOAD_OFFSET) { \
INIT_DATA \
INIT_SETUP(initsetup_align) \
INIT_CALLS \
CON_INITCALL \
INIT_RAM_FS \
}
生成的vmlinux.lds:
.init.data : AT(ADDR(.init.data)
-
0xffffffff80000000
) {
......
__setup_start
=
.; KEEP(
*
(.init.setup)) __setup_end
=
.;
......
}
readelf
-
l .
/
vmlinux:
Section to Segment mapping:
Segment Sections...
00
.text .rodata .pci_fixup .tracedata .printk_index __ksymtab __ksymtab_gpl __ksymtab_strings __init_rodata __param __modver __ex_table .notes .BTF .BTF_ids
01
.data __bug_table .orc_unwind_ip .orc_unwind .orc_lookup .vvar
02
.data..percpu
03
.init.text .altinstr_aux .init.data .x86_cpu_dev.init .parainstructions .retpoline_sites .return_sites .ibt_endbr_seal .altinstructions .altinstr_replacement .apicdrivers .exit.text .smp_locks .data_nosave .bss .brk .init.scratch
04
.notes
readelf
-
l .
/
vmlinux:
Section to Segment mapping:
Segment Sections...
00
.text .rodata .pci_fixup .tracedata .printk_index __ksymtab __ksymtab_gpl __ksymtab_strings __init_rodata __param __modver __ex_table .notes .BTF .BTF_ids
01
.data __bug_table .orc_unwind_ip .orc_unwind .orc_lookup .vvar
02
.data..percpu
03
.init.text .altinstr_aux .init.data .x86_cpu_dev.init .parainstructions .retpoline_sites .return_sites .ibt_endbr_seal .altinstructions .altinstr_replacement .apicdrivers .exit.text .smp_locks .data_nosave .bss .brk .init.scratch
04
.notes
#define __init __section(".init.text") __cold __latent_entropy __noinitretpoline
#define __initdata __section(".init.data")
#define __initconst __section(".init.rodata")
#define __init __section(".init.text") __cold __latent_entropy __noinitretpoline
#define __initdata __section(".init.data")
#define __initconst __section(".init.rodata")
void mark_rodata_ro(void)
{
......
free_kernel_image_pages(
"unused kernel image (text/rodata gap)"
,
(void
*
)text_end, (void
*
)rodata_start);
free_kernel_image_pages(
"unused kernel image (rodata/data gap)"
,
(void
*
)rodata_end, (void
*
)_sdata);
}
void __ref free_initmem(void)
{
......
free_kernel_image_pages(
"unused kernel image (initmem)"
,
&__init_begin, &__init_end);
}
vmmlinux.ld.S:
/
*
Init code
and
data
-
will be freed after init
*
/
.
=
ALIGN(PAGE_SIZE);
.init.begin : AT(ADDR(.init.begin)
-
LOAD_OFFSET) {
__init_begin
=
.;
/
*
paired with __init_end
*
/
}
.init.end : AT(ADDR(.init.end)
-
LOAD_OFFSET) {
__init_end
=
.;
}
void mark_rodata_ro(void)
{
......
free_kernel_image_pages(
"unused kernel image (text/rodata gap)"
,
(void
*
)text_end, (void
*
)rodata_start);
free_kernel_image_pages(
"unused kernel image (rodata/data gap)"
,
(void
*
)rodata_end, (void
*
)_sdata);
}
void __ref free_initmem(void)
{
......
free_kernel_image_pages(
"unused kernel image (initmem)"
,
&__init_begin, &__init_end);
}
vmmlinux.ld.S:
/
*
Init code
and
data
-
will be freed after init
*
/
.
=
ALIGN(PAGE_SIZE);
.init.begin : AT(ADDR(.init.begin)
-
LOAD_OFFSET) {
__init_begin
=
.;
/
*
paired with __init_end
*
/
}
.init.end : AT(ADDR(.init.end)
-
LOAD_OFFSET) {
__init_end
=
.;
}
#ifdef CONFIG_X86_VSYSCALL_EMULATION
VSYSCALL_PAGE
=
(FIXADDR_TOP
-
VSYSCALL_ADDR) >> PAGE_SHIFT,
#endif
#define __PAGE_KERNEL_VVAR (__PP| 0|_USR|___A|__NX| 0| 0|___G)
#define PAGE_KERNEL_VVAR __pgprot_mask(__PAGE_KERNEL_VVAR | _ENC)
if
(vsyscall_mode
=
=
EMULATE) {
__set_fixmap(VSYSCALL_PAGE, physaddr_vsyscall,
PAGE_KERNEL_VVAR);
set_vsyscall_pgtable_user_bits(swapper_pg_dir);
}
#ifdef CONFIG_X86_VSYSCALL_EMULATION
VSYSCALL_PAGE
=
(FIXADDR_TOP
-
VSYSCALL_ADDR) >> PAGE_SHIFT,
#endif
#define __PAGE_KERNEL_VVAR (__PP| 0|_USR|___A|__NX| 0| 0|___G)
#define PAGE_KERNEL_VVAR __pgprot_mask(__PAGE_KERNEL_VVAR | _ENC)
if
(vsyscall_mode
=
=
EMULATE) {
__set_fixmap(VSYSCALL_PAGE, physaddr_vsyscall,
PAGE_KERNEL_VVAR);
set_vsyscall_pgtable_user_bits(swapper_pg_dir);
}
if
(vsyscall_mode
=
=
XONLY)
vm_flags_init(&gate_vma, VM_EXEC);
if
(vsyscall_mode
=
=
XONLY)
vm_flags_init(&gate_vma, VM_EXEC);
static struct vm_area_struct gate_vma __ro_after_init
=
{
.vm_start
=
VSYSCALL_ADDR,
.vm_end
=
VSYSCALL_ADDR
+
PAGE_SIZE,
.vm_page_prot
=
PAGE_READONLY_EXEC,
.vm_flags
=
VM_READ | VM_EXEC,
.vm_ops
=
&gate_vma_ops,
};
struct vm_area_struct
*
get_gate_vma(struct mm_struct
*
mm)
{
#ifdef CONFIG_COMPAT
if
(!mm || !test_bit(MM_CONTEXT_HAS_VSYSCALL, &mm
-
>context.flags))
return
NULL;
#endif
if
(vsyscall_mode
=
=
NONE)
return
NULL;
return
&gate_vma;
}
int
in_gate_area(struct mm_struct
*
mm, unsigned
long
addr)
{
struct vm_area_struct
*
vma
=
get_gate_vma(mm);
if
(!vma)
return
0
;
return
(addr >
=
vma
-
>vm_start) && (addr < vma
-
>vm_end);
}
static
long
__get_user_pages(struct mm_struct
*
mm,
unsigned
long
start, unsigned
long
nr_pages,
unsigned
int
gup_flags, struct page
*
*
pages,
int
*
locked)
{
vma
=
gup_vma_lookup(mm, start);
if
(!vma && in_gate_area(mm, start)) {
ret
=
get_gate_page(mm, start & PAGE_MASK,
gup_flags, &vma,
pages ? &page : NULL);
if
(ret)
goto out;
ctx.page_mask
=
0
;
goto next_page;
}
}
static struct vm_area_struct gate_vma __ro_after_init
=
{
.vm_start
=
VSYSCALL_ADDR,
.vm_end
=
VSYSCALL_ADDR
+
PAGE_SIZE,
.vm_page_prot
=
PAGE_READONLY_EXEC,
.vm_flags
=
VM_READ | VM_EXEC,
.vm_ops
=
&gate_vma_ops,
};
struct vm_area_struct
*
get_gate_vma(struct mm_struct
*
mm)
{
#ifdef CONFIG_COMPAT
if
(!mm || !test_bit(MM_CONTEXT_HAS_VSYSCALL, &mm
-
>context.flags))
return
NULL;
#endif
if
(vsyscall_mode
=
=
NONE)
return
NULL;
return
&gate_vma;
}
int
in_gate_area(struct mm_struct
*
mm, unsigned
long
addr)
{
struct vm_area_struct
*
vma
=
get_gate_vma(mm);
if
(!vma)
return
0
;
return
(addr >
=
vma
-
>vm_start) && (addr < vma
-
>vm_end);
}
static
long
__get_user_pages(struct mm_struct
*
mm,
unsigned
long
start, unsigned
long
nr_pages,
unsigned
int
gup_flags, struct page
*
*
pages,
int
*
locked)
{
vma
=
gup_vma_lookup(mm, start);
if
(!vma && in_gate_area(mm, start)) {
ret
=
get_gate_page(mm, start & PAGE_MASK,
gup_flags, &vma,
pages ? &page : NULL);
if
(ret)
goto out;
ctx.page_mask
=
0
;
goto next_page;
}
}
void vsyscall_info_show(void)
{
struct vm_area_struct
*
vsyscall_info;
int
*
vsyscall_mode;
const char
*
vsc_mode_str[]
=
{
"EMULATE"
,
"XONLY"
,
"NONE"
};
vsyscall_info
=
(struct vm_area_struct
*
)LDE_KLN_PTR(
"gate_vma"
);
vsyscall_mode
=
(
int
*
)LDE_KLN_PTR(
"vsyscall_mode"
);
if
(!vsyscall_info || !vsyscall_mode) {
printk(KERN_ERR
"unable to find symbol by [kallsyms_lookup_name], will exit ...\n"
);
return
;
}
else
{
printk(KERN_INFO
"found symbol, vma at 0x%px, mode: %s\n"
,
vsyscall_info, vsc_mode_str[
*
vsyscall_mode]);
}
printk(KERN_INFO
"VSYSCALL_ADDR: %lx, %s %lx - %lx\n"
, VSYSCALL_ADDR,
vsyscall_info
-
>vm_ops
-
>name(NULL), vsyscall_info
-
>vm_start, vsyscall_info
-
>vm_end);
}
void vsyscall_info_show(void)
{
struct vm_area_struct
*
vsyscall_info;
int
*
vsyscall_mode;
const char
*
vsc_mode_str[]
=
{
"EMULATE"
,
"XONLY"
,
"NONE"
};
vsyscall_info
=
(struct vm_area_struct
*
)LDE_KLN_PTR(
"gate_vma"
);
vsyscall_mode
=
(
int
*
)LDE_KLN_PTR(
"vsyscall_mode"
);
if
(!vsyscall_info || !vsyscall_mode) {
printk(KERN_ERR
"unable to find symbol by [kallsyms_lookup_name], will exit ...\n"
);
return
;
}
else
{
printk(KERN_INFO
"found symbol, vma at 0x%px, mode: %s\n"
,
vsyscall_info, vsc_mode_str[
*
vsyscall_mode]);
}
printk(KERN_INFO
"VSYSCALL_ADDR: %lx, %s %lx - %lx\n"
, VSYSCALL_ADDR,
vsyscall_info
-
>vm_ops
-
>name(NULL), vsyscall_info
-
>vm_start, vsyscall_info
-
>vm_end);
}
驱动打印的消息:
[
1804.290063
] found symbols by [kprobe], kallsyms_lookup_name at
0xffffffffa3db7e14
, ret:
0
[
1811.881820
] lde_proc_write called legnth
0x9
,
0x000061bcc8981a60
[
1811.882274
] found symbol, vma at
0xffffffffa5531000
, mode: XONLY
[
1811.882278
] VSYSCALL_ADDR: ffffffffff600000, [vsyscall] ffffffffff600000
-
ffffffffff601000
应用程序的内存信息:
cat
/
proc
/
294
/
maps
ffffffffff600000
-
ffffffffff601000
-
-
xp
00000000
00
:
00
0
[vsyscall]
cat
/
proc
/
1
/
maps
ffffffffff600000
-
ffffffffff601000
-
-
xp
00000000
00
:
00
0
[vsyscall]
驱动打印的消息:
[
1804.290063
] found symbols by [kprobe], kallsyms_lookup_name at
0xffffffffa3db7e14
, ret:
0
[
1811.881820
] lde_proc_write called legnth
0x9
,
0x000061bcc8981a60
[
1811.882274
] found symbol, vma at
0xffffffffa5531000
, mode: XONLY
[
1811.882278
] VSYSCALL_ADDR: ffffffffff600000, [vsyscall] ffffffffff600000
-
ffffffffff601000
应用程序的内存信息:
cat
/
proc
/
294
/
maps
ffffffffff600000
-
ffffffffff601000
-
-
xp
00000000
00
:
00
0
[vsyscall]
cat
/
proc
/
1
/
maps
ffffffffff600000
-
ffffffffff601000
-
-
xp
00000000
00
:
00
0
[vsyscall]
__PAGE_ALIGNED_DATA
.globl __vsyscall_page
.balign PAGE_SIZE,
0xcc
.
type
__vsyscall_page, @
object
__vsyscall_page:
mov $__NR_gettimeofday,
%
rax
syscall
ret
int3
.balign
1024
,
0xcc
mov $__NR_time,
%
rax
syscall
ret
int3
.balign
1024
,
0xcc
mov $__NR_getcpu,
%
rax
syscall
ret
int3
.balign
4096
,
0xcc
.size __vsyscall_page,
4096
__PAGE_ALIGNED_DATA
.globl __vsyscall_page
.balign PAGE_SIZE,
0xcc
.
type
__vsyscall_page, @
object
__vsyscall_page:
mov $__NR_gettimeofday,
%
rax
syscall
ret
int3
.balign
1024
,
0xcc
mov $__NR_time,
%
rax
syscall
ret
int3
.balign
1024
,
0xcc
mov $__NR_getcpu,
%
rax
syscall
ret
int3
.balign
4096
,
0xcc
.size __vsyscall_page,
4096
static __init
int
init_vdso_image_xx(void) {
return
init_vdso_image(&vdso_image_x);
};
subsys_initcall(init_vdso_image_xx);
static __init
int
init_vdso_image_xx(void) {
return
init_vdso_image(&vdso_image_x);
};
subsys_initcall(init_vdso_image_xx);
#define __initcall_section(__sec, __iid) \
#__sec ".init"
#define ____define_initcall(fn, __unused, __name, __sec) \
static initcall_t __name __used \
__attribute__((__section__(__sec)))
=
fn;
#define ____define_initcall(fn, __stub, __name, __sec) \
__define_initcall_stub(__stub, fn) \
asm(
".section \""
__sec
"\", \"a\" \n"
\
__stringify(__name)
": \n"
\
".long "
__stringify(__stub)
" - . \n"
\
".previous \n"
); \
static_assert(__same_type(initcall_t, &fn));
#define __unique_initcall(fn, id, __sec, __iid) \
____define_initcall(fn, \
__initcall_stub(fn, __iid,
id
), \
__initcall_name(initcall, __iid,
id
), \
__initcall_section(__sec, __iid))
#define ___define_initcall(fn, id, __sec) \
__unique_initcall(fn,
id
, __sec, __initcall_id(fn))
#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
#define core_initcall(fn) __define_initcall(fn, 1)
#define subsys_initcall(fn) __define_initcall(fn, 4)
#define __initcall_section(__sec, __iid) \
#__sec ".init"
#define ____define_initcall(fn, __unused, __name, __sec) \
static initcall_t __name __used \
__attribute__((__section__(__sec)))
=
fn;
#define ____define_initcall(fn, __stub, __name, __sec) \
__define_initcall_stub(__stub, fn) \
asm(
".section \""
__sec
"\", \"a\" \n"
\
__stringify(__name)
": \n"
\
".long "
__stringify(__stub)
" - . \n"
\
".previous \n"
); \
static_assert(__same_type(initcall_t, &fn));
#define __unique_initcall(fn, id, __sec, __iid) \
____define_initcall(fn, \
__initcall_stub(fn, __iid,
id
), \
__initcall_name(initcall, __iid,
id
), \
__initcall_section(__sec, __iid))
#define ___define_initcall(fn, id, __sec) \
__unique_initcall(fn,
id
, __sec, __initcall_id(fn))
#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
#define core_initcall(fn) __define_initcall(fn, 1)
#define subsys_initcall(fn) __define_initcall(fn, 4)
生成的vmlinux.lds的内容:
.init.data : AT(ADDR(.init.data)
-
0xffffffff80000000
) {
__initcall_start
=
.; KEEP(
*
(.initcallearly.init))
__initcall0_start
=
.; KEEP(
*
(.initcall0.init)) KEEP(
*
(.initcall0s.init))
__initcall1_start
=
.; KEEP(
*
(.initcall1.init)) KEEP(
*
(.initcall1s.init))
__initcall2_start
=
.; KEEP(
*
(.initcall2.init)) KEEP(
*
(.initcall2s.init))
__initcall3_start
=
.; KEEP(
*
(.initcall3.init)) KEEP(
*
(.initcall3s.init))
__initcall4_start
=
.; KEEP(
*
(.initcall4.init)) KEEP(
*
(.initcall4s.init))
__initcall5_start
=
.; KEEP(
*
(.initcall5.init)) KEEP(
*
(.initcall5s.init))
__initcallrootfs_start
=
.; KEEP(
*
(.initcallrootfs.init)) KEEP(
*
(.initcallrootfss.init))
__initcall6_start
=
.; KEEP(
*
(.initcall6.init)) KEEP(
*
(.initcall6s.init))
__initcall7_start
=
.; KEEP(
*
(.initcall7.init)) KEEP(
*
(.initcall7s.init))
__initcall_end
=
.;
}
生成的vmlinux.lds的内容:
.init.data : AT(ADDR(.init.data)
-
0xffffffff80000000
) {
__initcall_start
=
.; KEEP(
*
(.initcallearly.init))
__initcall0_start
=
.; KEEP(
*
(.initcall0.init)) KEEP(
*
(.initcall0s.init))
__initcall1_start
=
.; KEEP(
*
(.initcall1.init)) KEEP(
*
(.initcall1s.init))
__initcall2_start
=
.; KEEP(
*
(.initcall2.init)) KEEP(
*
(.initcall2s.init))
__initcall3_start
=
.; KEEP(
*
(.initcall3.init)) KEEP(
*
(.initcall3s.init))
__initcall4_start
=
.; KEEP(
*
(.initcall4.init)) KEEP(
*
(.initcall4s.init))
__initcall5_start
=
.; KEEP(
*
(.initcall5.init)) KEEP(
*
(.initcall5s.init))
__initcallrootfs_start
=
.; KEEP(
*
(.initcallrootfs.init)) KEEP(
*
(.initcallrootfss.init))
__initcall6_start
=
.; KEEP(
*
(.initcall6.init)) KEEP(
*
(.initcall6s.init))
__initcall7_start
=
.; KEEP(
*
(.initcall7.init)) KEEP(
*
(.initcall7s.init))
__initcall_end
=
.;
}
static initcall_entry_t
*
initcall_levels[] __initdata
=
{
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
};
static void __init do_initcall_level(
int
level, char
*
command_line)
{
initcall_entry_t
*
fn;
parse_args(initcall_level_names[level],
command_line, __start___param,
__stop___param
-
__start___param,
level, level,
NULL, ignore_unknown_bootoption);
trace_initcall_level(initcall_level_names[level]);
for
(fn
=
initcall_levels[level]; fn < initcall_levels[level
+
1
]; fn
+
+
)
do_one_initcall(initcall_from_entry(fn));
}
static void __init do_initcalls(void)
{
int
level;
size_t
len
=
saved_command_line_len
+
1
;
char
*
command_line;
command_line
=
kzalloc(
len
, GFP_KERNEL);
if
(!command_line)
panic(
"%s: Failed to allocate %zu bytes\n"
, __func__,
len
);
for
(level
=
0
; level < ARRAY_SIZE(initcall_levels)
-
1
; level
+
+
) {
/
*
Parser modifies command_line, restore it each time
*
/
strcpy(command_line, saved_command_line);
do_initcall_level(level, command_line);
}
kfree(command_line);
}
static initcall_entry_t
*
initcall_levels[] __initdata
=
{
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
};
static void __init do_initcall_level(
int
level, char
*
command_line)
{
initcall_entry_t
*
fn;
parse_args(initcall_level_names[level],
command_line, __start___param,
__stop___param
-
__start___param,
level, level,
NULL, ignore_unknown_bootoption);
trace_initcall_level(initcall_level_names[level]);
for
(fn
=
initcall_levels[level]; fn < initcall_levels[level
+
1
]; fn
+
+
)
do_one_initcall(initcall_from_entry(fn));
}
static void __init do_initcalls(void)
{
int
level;
size_t
len
=
saved_command_line_len
+
1
;
char
*
command_line;
command_line
=
kzalloc(
len
, GFP_KERNEL);
if
(!command_line)
panic(
"%s: Failed to allocate %zu bytes\n"
, __func__,
len
);
for
(level
=
0
; level < ARRAY_SIZE(initcall_levels)
-
1
; level
+
+
) {
/
*
Parser modifies command_line, restore it each time
*
/
strcpy(command_line, saved_command_line);
do_initcall_level(level, command_line);
}
kfree(command_line);
}
编译过程:
make arch
/
x86
/
entry
/
vdso
/
64
位开始生成:
LDS arch
/
x86
/
entry
/
vdso
/
vdso.lds
AS arch
/
x86
/
entry
/
vdso
/
vdso
-
note.o
CC arch
/
x86
/
entry
/
vdso
/
vclock_gettime.o
CC arch
/
x86
/
entry
/
vdso
/
vgetcpu.o
AS arch
/
x86
/
entry
/
vdso
/
vsgx.o
VDSO arch
/
x86
/
entry
/
vdso
/
vdso64.so.dbg
OBJCOPY arch
/
x86
/
entry
/
vdso
/
vdso64.so
32
位开始生成(分成
32
和x32):
LDS arch
/
x86
/
entry
/
vdso
/
vdsox32.lds
X32 arch
/
x86
/
entry
/
vdso
/
vdso
-
note
-
x32.o
X32 arch
/
x86
/
entry
/
vdso
/
vclock_gettime
-
x32.o
X32 arch
/
x86
/
entry
/
vdso
/
vgetcpu
-
x32.o
X32 arch
/
x86
/
entry
/
vdso
/
vsgx
-
x32.o
VDSO arch
/
x86
/
entry
/
vdso
/
vdsox32.so.dbg
OBJCOPY arch
/
x86
/
entry
/
vdso
/
vdsox32.so
LDS arch
/
x86
/
entry
/
vdso
/
vdso32
/
vdso32.lds
AS arch
/
x86
/
entry
/
vdso
/
vdso32
/
note.o
AS arch
/
x86
/
entry
/
vdso
/
vdso32
/
system_call.o
AS arch
/
x86
/
entry
/
vdso
/
vdso32
/
sigreturn.o
CC arch
/
x86
/
entry
/
vdso
/
vdso32
/
vclock_gettime.o
CC arch
/
x86
/
entry
/
vdso
/
vdso32
/
vgetcpu.o
VDSO arch
/
x86
/
entry
/
vdso
/
vdso32.so.dbg
OBJCOPY arch
/
x86
/
entry
/
vdso
/
vdso32.so
编译过程:
make arch
/
x86
/
entry
/
vdso
/
64
位开始生成:
LDS arch
/
x86
/
entry
/
vdso
/
vdso.lds
AS arch
/
x86
/
entry
/
vdso
/
vdso
-
note.o
CC arch
/
x86
/
entry
/
vdso
/
vclock_gettime.o
CC arch
/
x86
/
entry
/
vdso
/
vgetcpu.o
AS arch
/
x86
/
entry
/
vdso
/
vsgx.o
VDSO arch
/
x86
/
entry
/
vdso
/
vdso64.so.dbg
OBJCOPY arch
/
x86
/
entry
/
vdso
/
vdso64.so
32
位开始生成(分成
32
和x32):
LDS arch
/
x86
/
entry
/
vdso
/
vdsox32.lds
X32 arch
/
x86
/
entry
/
vdso
/
vdso
-
note
-
x32.o
X32 arch
/
x86
/
entry
/
vdso
/
vclock_gettime
-
x32.o
X32 arch
/
x86
/
entry
/
vdso
/
vgetcpu
-
x32.o
X32 arch
/
x86
/
entry
/
vdso
/
vsgx
-
x32.o
VDSO arch
/
x86
/
entry
/
vdso
/
vdsox32.so.dbg
OBJCOPY arch
/
x86
/
entry
/
vdso
/
vdsox32.so
LDS arch
/
x86
/
entry
/
vdso
/
vdso32
/
vdso32.lds
AS arch
/
x86
/
entry
/
vdso
/
vdso32
/
note.o