Magenta

来源:互联网 发布:侠客行 叮叮当当 知乎 编辑:程序博客网 时间:2024/06/05 11:41

Magenta-Userboot


在kernel初始化完毕后,需要跳转至user space并初始化user的init进程(devmgr),此也是user的第一个进程。为了可以顺利的启动user进程,magenta在 编译,初始化和启动 阶段分别做了特殊处理。

userboot就是专为此而实现的模块。包括:

  • kernel userboot:运行在kernel空间,为进入user空间做准备
  • user userboot:运行在user空间,加载init进程。

编译阶段


user userboot的编译


以目录 /system/core/userboot为基础,生成libuserboot.so,这是user userboot的主体部分。libuserboot.so需要通过sys-call功能访问magenta。所以基于目录/system/ulib/magenta 生成了动态库libmagenta.so。其内容形如其下:

...00000000000064cc <_mx_handle_close>:    64cc:   41 52                   push   %r10    64ce:   41 53                   push   %r11    64d0:   49 89 ca                mov    %rcx,%r10    64d3:   b8 02 00 00 00          mov    $0x2,%eax    64d8:   0f 05                   syscall 00000000000064da <CODE_SYSRET_mx_handle_close_VIA_mx_handle_close>:    64da:   41 5b                   pop    %r11    64dc:   41 5a                   pop    %r10    64de:   c3                      retq  ...

可见实现了系统调用接口。user code可以加载此.so以访问kernel所提供功能。

但是user userboot比较特殊,它运行在kernel创建的进程中,且是第一个user process。在它进入user space时,没有现成的user environment,需要自己创建并配置。由于libuserboot.so在初期就需要访问kernel,为了减少加载.so时所需的重定位等等步骤,magenta对libuserboot.so的编译做了特殊处理,即在编译libuserboot.so阶段就固定libuserboot.so和libmagenta.so的相对地址。这样libuserboot.so可以以PC-relative的方式访问libmagenta.so提供的函数。为了达到此目的,需要解析libuserboot.so和libmagenta.so中的代码和数据的layout结构。


编译libuserboot.so后,根据链接脚本rodso.ld,以及其中定义的 CODE_START/CODE_END,利用脚本生成文件userboot-code.h,其内容形如:

#define USERBOOT_FILENAME "./build-magenta-pc-x86-64/system/core/userboot/libuserboot.so"#define USERBOOT_DATA_START_dynsym 0x0000000000000298#define USERBOOT_DATA_END_dynsym 0x00000000000002b0#define USERBOOT_CODE_START 0x0000000000003000#define USERBOOT_ENTRY 0x0000000000003a40#define USERBOOT_ENTRY_SIZE 0x00000000000009df#define USERBOOT_CODE_END 0x000000000000d000

可以得到libuserboot.so的文件偏移layout。这是供kernel userboot加载libuserboot.so时所用。


编译libmagenta.so后,根据链接脚本rodso.ld,以及其中定义的 CODE_START/CODE_END,利用脚本生成文件vdso-code.h,其内容形如:

#define VDSO_FILENAME "./build-magenta-pc-x86-64/system/ulib/magenta/libmagenta.so"#define VDSO_DATA_START_dynsym 0x0000000000000c38#define VDSO_DATA_END_dynsym 0x00000000000027b0#define VDSO_DATA_CONSTANTS 0x0000000000003d20#define VDSO_DATA_CONSTANTS_SIZE 0x0000000000000018#define VDSO_CODE_START 0x0000000000006000#define VDSO_CODE_soft_ticks_get 0x00000000000062a0#define VDSO_CODE_soft_ticks_get_SIZE 0x0000000000000007....#define VDSO_CODE_END 0x0000000000007000...

可以得到libmagenta.so的文件偏移layout。这是供kernel userboot加载libmagenta.so时所用。


为了让libuserboot.so可以直接访问到libmagenta.so,基于libmagenta.so,利用脚本生成2个文件vdso-syms.h、vdso-syms.ld,供libuserboot.so编译和链接使用。此2个文件形如其下:

vdso-syms.h:...FUNCTION(_mx_handle_close, 0x00000000000064cc, 0x0000000000000013)WEAK_FUNCTION(mx_handle_close, 0x00000000000064cc, 0x0000000000000013)...

vdso-syms.ld:...PROVIDE_HIDDEN(_mx_handle_close = CODE_END + 0x00000000000064cc);...

宏的定义如下:

#define FUNCTION(name, address, size) \    PROVIDE_HIDDEN(name = CODE_END + address);#define WEAK_FUNCTION(name, address, size) FUNCTION(name, address, size)

注:CODE_END指libuserboot.so的代码结束地址,即 USERBOOT_CODE_END。定义了属性PROVIDE_HIDDEN,可以生成简单的PC-relative 代码。


经过上述处理后,如libuserboot.so需要访问系统调用mx_handle_close,则会直接调转至CODE_END + 0x00000000000064cc处。所以需要kernel userboot将libmagenta.so加载至libuserboot.so其后。下图是libmagenta.so和libuserboot.so两者之间的布局示意图。

这里写图片描述

最后,需要利用宏

RODSO_IMAGE

将libmagenta.so和libuserboot.so作为数据内嵌到magenta.bin文件中,不依赖文件系统就可以访问此2个.so文件。


初始化阶段


此阶段指的是kernel userboot为进入user space所做的各种准备,所需资源包括如下:

enum bootstrap_handle_index {    BOOTSTRAP_VDSO,            BOOTSTRAP_VDSO_LAST_VARIANT = BOOTSTRAP_VDSO + VDso::variants() - 1,    BOOTSTRAP_RAMDISK,    BOOTSTRAP_RESOURCE_ROOT,    BOOTSTRAP_STACK,    BOOTSTRAP_PROC,    BOOTSTRAP_THREAD,    BOOTSTRAP_JOB,    BOOTSTRAP_VMAR_ROOT,    BOOTSTRAP_HANDLES};

kernel userboot会创建各种object和其对应的handle,并将handle转移并映射到新进程中,从而新进程启动后可以访问此些资源。


  • 创建Job Object: 在root job下创建一个新的job obj,并同步创建对应的handle,此即是BOOTSTRAP_JOB。
  • 创建Process Object :在root job下创建name是 “userboot”的process obj,并创建一个handle 指向此obj,此handle即BOOTSTRAP_PROC;同步也创建的进程的vmaro(virtual memory address region object),用于管理进程的的虚拟空间,并同步创建对应的handle,此即是BOOTSTRAP_VMAR_ROOT。
  • 创建Thread Object : 在”userboot”的process中创建name是 “userboot”的thread obj,并创建一个handle指向此obj,此handle即BOOTSTRAP_THREAD;
  • 创建Ramdisk Object: 根据Ramdisk在ram中的基地址和长度,创建vmo(virtual memory object)。vmo可以理解为RAM中的数据块,可以通过handle对此数据块读写。然后创建一个handle指向此obj,此handle就是BOOTSTRAP_RAMDISK。user process可以根据此obj读取Ramdisk并解析,然后将文件释放到文件系统中。
  • 创建VDSO Object: 根据vdso在ram中的基地址和长度,创建vmo。并同步创建对应的handle,此即是BOOTSTRAP_VDSO;
  • 创建Stack Object: 创建一个大小为stack size的vmo。并同步创建对应的handle,此即是BOOTSTRAP_STACK。此vmo在此阶段并没有实际分配物理page,待后期stack缺页中断时再分配物理page。
  • 创建Resource Object: 创建root resource,并同步创建对应的handle,此即是BOOTSTRAP_VMAR_ROOT。

创建完所需资源后,开始对user userboot的加载:

  • 根据libuserboot.so在ram中基地址和长度,创建 “userboot” vmo;
  • 结合”userboot” vmo和vdso vmo的总长度,即libmagenta.so和libuserboot.so的总长度,在新进程的虚拟空间中分配对应长度的虚拟空间vmar,然后将这2个vmo映射至此vmar。
  • 将stack obj映射进新进程的虚拟空间,从而新线程有课自己的栈空间;
  • 创建channel obj,一端定义为kernel_channel obj,另一端定义为user_channel_handle obj。kernel利用kernel_channel将boot trap msg写入此channel; 创建一个handle指向user_channel_handle obj,并将此handle映射进新进程的handle空间,从而在新进程的user space可以通过此handle读取到boot trap msg。
  • 根据映射时得到的libmagenta.so和libuserboot.so虚拟地址,启动新线程,并将user_channel_handle 和vdso基地址作为参数传给user code。

以上加载时的log表现如下:

[00000.064] 00000.00000> userboot: ramdisk        0x358000 @ 0xffffff8000252000[00000.069] 00000.00000> userboot: userboot rodata       0 @ [0xe3d00fc6000,0xe3d00fc9000)[00000.069] 00000.00000> userboot: userboot code    0x3000 @ [0xe3d00fc9000,0xe3d00fd3000)[00000.069] 00000.00000> userboot: vdso/full rodata       0 @ [0xe3d00fd3000,0xe3d00fd9000)[00000.070] 00000.00000> userboot: vdso/full code    0x6000 @ [0xe3d00fd9000,0xe3d00fda000)[00000.071] 00000.00000> userboot: entry point             @ 0xe3d00fc9a40

新线程经如下调用

UserThread::Start    --》 UserThread::StartRoutine        --》arch_enter_uspace

切换至user space。不同的arch,arch_enter_uspace的实现不一样。


启动阶段


此阶段是在user space,新线程的entry地址是_start,实现在文件start.c中。有如下流程:

  • 从bootstrap_message中读取环境变量和参数,以及handles;
    • 在channel read时,kernel会将handles映射于当前process中,且将得到的
      handle返回给user;
  • 创建process “bin/devmgr”
  • load文件”bin/devmgr”至process “bin/devmgr”;
    • 由于devmgr中有section interp,所以其实是load此interp “lib/ld.so.1”
    • 将相关msg写入msgpipe,以便ld.so.1读取并解析;参数包括需要加载的文件名,即”bin/devmgr”;
  • 创建并映射stack至process “bin/devmgr”;
  • 创建新thread,
  • 将启动参数和handles写于msgpipe;
  • start process, 在新进程中执行lib/ld.so.1的entry函数。
  • 启动loader_service,以响应ld.so.1的加载module的请求。如ld.so.1依赖其他lib,则当ld.so.1初始化时,需要加载依赖库。如”bin/devmgr”有依赖libs,也需此service帮忙load libs。当从ld.so.1跳转至“bin/devmgr”后,会关闭此service。
  • 如果loader service退出了,则”userboot”进程退出。

注: ld.so.1 其实本身就是libc.so(musl)


从上可见,分了2个步骤来加载文件”bin/devmgr”:

  • libuserboot.so在用户进程中加载ld.so.1,并跳转至其entry函数;
  • ld.so.1完成其自身初始化后,加载文件”bin/devmgr”;

libc.so的entry函数是_start,不同arch有不同的定义,但流程都一样。

  • _start
    • _dl_start :重定位;
    • __dls2 :映射”libc.so”和””,这2个模块由libuserboot.so之前已经加载完毕
    • __dls3 :读取并解析bootstrap msg,包含handles以及argc/env,配置了当前进程的运行环境;得到application vmo,即文件”bin/devmgr”的vmo。
    • dls3 : load “bin/devmgr” vmo至当前process;在此过程中,以请求loader service加载依赖libs;
    • jump to application entry(即devmgr entry)

相关的log如下:

[00000.119] 01029.01036> userboot: searching bootfs for program "bin/devmgr"[00000.120] 01029.01036> userboot: bin/devmgr has PT_INTERP "lib/ld.so.1"[00000.120] 01029.01036> userboot: searching bootfs for dynamic linker "lib/ld.so.1"[00000.126] 01029.01036> userboot: process bin/devmgr started.[00000.126] 01029.01036> userboot: waiting for loader-service requests...

以上是 libuserboot.so运行时的log。

[00000.128] 01029.01036> userboot: searching bootfs for shared library "lib/libfs-management.so"[00000.129] 01029.01036> userboot: searching bootfs for shared library "lib/liblaunchpad.so"[00000.130] 01029.01036> userboot: searching bootfs for shared library "lib/libmxio.so"[00000.133] 01043.01046> Loaded at [0x51ab193c6000,0x51ab193e4000): <application>[00000.133] 01043.01046> Loaded at [0x6904190c000,0x69041910000): libfs-management.so[00000.133] 01043.01046> Loaded at [0x229a645d5000,0x229a645de000): liblaunchpad.so[00000.133] 01043.01046> Loaded at [0x3f0516fc9000,0x3f0516fe2000): libmxio.so[00000.133] 01043.01046> Loaded at [0x6212fec84000,0x6212fec8b000): <vDSO> (libmagenta.so)[00000.133] 01043.01046> Loaded at [0x65db29b63000,0x65db29c43000): libc.so

以上是libc.so运行时的log。可见加载了3个依赖libs。

[00000.141] 01043.01046> devmgr: main()

以上是libc.so初始化完后,跳转至devmgr。

...[00000.147] 01029.01036> userboot: loader-service channel peer closed[00000.147] 01029.01036> userboot: finished!

以上是loader service退出后,userboot进程也自动退出。

原创粉丝点击