首页
社区
课程
招聘
PWN入门-7-难逃系统调用2-VDSO与VSYSCALL
发表于: 2024-8-4 17:47 3930

PWN入门-7-难逃系统调用2-VDSO与VSYSCALL

2024-8-4 17:47
3930

PWN入门-6-难逃系统调用1中对系统调用的处理流程进行较为详细的解析,其中在解析IDT表中陷阱初始化时,发现该表只会处理int 0x80软中断发起的系统调用,而直接通过x86_64指令集中系统调用指令发起的系统调用则会交给MSR寄存器中的STARLSTAR进行处理,这两个寄存器会保存对应的系统调用处理函数地址。

在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宏也会这样操作,__setupearly_param的归宿都是__setup_param宏,该宏声明的.init.setup节内元素会在内核初始化时进行使用。

每个表项都会按照obs_kernel_param结构体的结构进行放置。

内核针对__setup_param进行初始化的地方分成两个部分,第一部分是early_param对应的parse_early_param函数,第二部分则是after_dashes对应__setup

不管哪一个部分,最终的目标都是通过parse_args对命令行参数进行解析,然后根据指定的处理函数parse_unknown_fnparse_args中最后一个参数)对参数的要求进行处理。

__setupearly_param的区别在于,early_param用于解析静态命令行参数,__setup用于解析动态命令行参数。为了保障动态命令行参数设置的值是生效的,所以__setup对应的解析处理会晚于early_param宏。

静态命令行参数是内核编译时确定下来的,如ARM设备上DTS中的chosen节点中的bootargs项,以及x86_64设备上保存在BIOS内的参数real_mod_date(上电后先进入实模式)。而动态命令行参数指的则是启动引导程序传递给内核的参数,如UEFI中的GRUB配置文件,以及UBoot内向内核传递的参数。

除此之外,__setupearly_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_pagesin_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.cextable.c,它们是内核操作VDSO的核心逻辑所在。

在此之后,会有两个特别的文件,它们以vdso-image-xx.c为标志,这两个文件并不是默认就有的,而是在编译过程中产生的.c文件,然后再对它们进行编译。

它们通过vdso2c可执行文件进行产生,该可执行文件由vdso2c.cvdso2c.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分配出imagevvar的空间后,会使用text_start作为分割,addrtext_start是vvar的范围,text_start往上才是vdso的范围。

这一点也可以在_install_special_mapping中获得验证,其中text_start对应着vdso_mappingaddr对应着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进行滑动时,需要注意rdirsi的数值,避免内核检查时出错,如果确定rdirsi中数值有误,那么就无法使用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

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回