将参数传递给自己的 execv 内核实现
Passing arguments to own execv kernel implementation
我目前正在将 execv 系统调用实现到我自己的内核中。但是,我设法计算了用户 space 中的参数 count(argc) 并通过系统调用将 argc 和 argv 传递给我的内核 exec 函数。
其实我现在不知道怎么把这两个变量传给新的用户线程主函数。我分配了一个新的堆栈页并将argc 和argv 依次存储到此页space。我的想法是将 EDI 或 ESI 用户寄存器指向第一个参数 (argc),然后是 argv 数组。
我不确定为此目的使用 EDI 或 ESI 是否正确。有人知道参数是如何在其他内核中传递的吗?
您需要正确设置用户堆栈,然后 ELF 可执行文件的入口点将负责在堆栈上定位 argc
和 argv
。 Linux内核中负责设置栈的函数create_elf_tables. For a visual representation, you can check out this nice figure from this很棒LWN.net文章:
------------------------------------------------------------- 0x7fff6c845000
0x7fff6c844ff8: 0x0000000000000000
_ 4fec: './stackdump[=10=]' <------+
env / 4fe2: 'ENVVAR2=2[=10=]' | <----+
\_ 4fd8: 'ENVVAR1=1[=10=]' | <---+ |
/ 4fd4: 'two[=10=]' | | | <----+
args | 4fd0: 'one[=10=]' | | | <---+ |
\_ 4fcb: 'zero[=10=]' | | | <--+ | |
3020: random gap padded to 16B boundary | | | | | |
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -| | | | | |
3019: 'x86_64[=10=]' <-+ | | | | | |
auxv 3009: random data: ed99b6...2adcc7 | <-+ | | | | | |
data 3000: zero padding to align stack | | | | | | | |
. . . . . . . . . . . . . . . . . . . . . . . . . . .|. .|. .| | | | | |
2ff0: AT_NULL(0)=0 | | | | | | | |
2fe0: AT_PLATFORM(15)=0x7fff6c843019 --+ | | | | | | |
2fd0: AT_EXECFN(31)=0x7fff6c844fec ------|---+ | | | | |
2fc0: AT_RANDOM(25)=0x7fff6c843009 ------+ | | | | |
ELF 2fb0: AT_SECURE(23)=0 | | | | |
auxiliary 2fa0: AT_EGID(14)=1000 | | | | |
vector: 2f90: AT_GID(13)=1000 | | | | |
(id,val) 2f80: AT_EUID(12)=1000 | | | | |
pairs 2f70: AT_UID(11)=1000 | | | | |
2f60: AT_ENTRY(9)=0x4010c0 | | | | |
2f50: AT_FLAGS(8)=0 | | | | |
2f40: AT_BASE(7)=0x7ff6c1122000 | | | | |
2f30: AT_PHNUM(5)=9 | | | | |
2f20: AT_PHENT(4)=56 | | | | |
2f10: AT_PHDR(3)=0x400040 | | | | |
2f00: AT_CLKTCK(17)=100 | | | | |
2ef0: AT_PAGESZ(6)=4096 | | | | |
2ee0: AT_HWCAP(16)=0xbfebfbff | | | | |
2ed0: AT_SYSINFO_EHDR(33)=0x7fff6c86b000 | | | | |
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | | | | |
2ec8: environ[2]=(nil) | | | | |
2ec0: environ[1]=0x7fff6c844fe2 ------------------|-+ | | |
2eb8: environ[0]=0x7fff6c844fd8 ------------------+ | | |
2eb0: argv[3]=(nil) | | |
2ea8: argv[2]=0x7fff6c844fd4 ---------------------------|-|-+
2ea0: argv[1]=0x7fff6c844fd0 ---------------------------|-+
2e98: argv[0]=0x7fff6c844fcb ---------------------------+
0x7fff6c842e90: argc=3
在 ELF 二进制文件(glibc 称之为 _start
)入口点的第一条指令处,栈顶(0(%rsp)
)将指向 argc
,如如图所示。然后是 argv
数组,它只是一个指针数组。环境变量数组也是 ABI 的一部分,ELF auxiliary vector (which may be used by the glibc initialization code) 也是如此。虽然如果您确定(或假设)ELF 二进制文件不会使用环境变量数组或辅助向量,那么您只需设置 argv
和 argc
,如图所示。
我目前正在将 execv 系统调用实现到我自己的内核中。但是,我设法计算了用户 space 中的参数 count(argc) 并通过系统调用将 argc 和 argv 传递给我的内核 exec 函数。
其实我现在不知道怎么把这两个变量传给新的用户线程主函数。我分配了一个新的堆栈页并将argc 和argv 依次存储到此页space。我的想法是将 EDI 或 ESI 用户寄存器指向第一个参数 (argc),然后是 argv 数组。
我不确定为此目的使用 EDI 或 ESI 是否正确。有人知道参数是如何在其他内核中传递的吗?
您需要正确设置用户堆栈,然后 ELF 可执行文件的入口点将负责在堆栈上定位 argc
和 argv
。 Linux内核中负责设置栈的函数create_elf_tables. For a visual representation, you can check out this nice figure from this很棒LWN.net文章:
------------------------------------------------------------- 0x7fff6c845000
0x7fff6c844ff8: 0x0000000000000000
_ 4fec: './stackdump[=10=]' <------+
env / 4fe2: 'ENVVAR2=2[=10=]' | <----+
\_ 4fd8: 'ENVVAR1=1[=10=]' | <---+ |
/ 4fd4: 'two[=10=]' | | | <----+
args | 4fd0: 'one[=10=]' | | | <---+ |
\_ 4fcb: 'zero[=10=]' | | | <--+ | |
3020: random gap padded to 16B boundary | | | | | |
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -| | | | | |
3019: 'x86_64[=10=]' <-+ | | | | | |
auxv 3009: random data: ed99b6...2adcc7 | <-+ | | | | | |
data 3000: zero padding to align stack | | | | | | | |
. . . . . . . . . . . . . . . . . . . . . . . . . . .|. .|. .| | | | | |
2ff0: AT_NULL(0)=0 | | | | | | | |
2fe0: AT_PLATFORM(15)=0x7fff6c843019 --+ | | | | | | |
2fd0: AT_EXECFN(31)=0x7fff6c844fec ------|---+ | | | | |
2fc0: AT_RANDOM(25)=0x7fff6c843009 ------+ | | | | |
ELF 2fb0: AT_SECURE(23)=0 | | | | |
auxiliary 2fa0: AT_EGID(14)=1000 | | | | |
vector: 2f90: AT_GID(13)=1000 | | | | |
(id,val) 2f80: AT_EUID(12)=1000 | | | | |
pairs 2f70: AT_UID(11)=1000 | | | | |
2f60: AT_ENTRY(9)=0x4010c0 | | | | |
2f50: AT_FLAGS(8)=0 | | | | |
2f40: AT_BASE(7)=0x7ff6c1122000 | | | | |
2f30: AT_PHNUM(5)=9 | | | | |
2f20: AT_PHENT(4)=56 | | | | |
2f10: AT_PHDR(3)=0x400040 | | | | |
2f00: AT_CLKTCK(17)=100 | | | | |
2ef0: AT_PAGESZ(6)=4096 | | | | |
2ee0: AT_HWCAP(16)=0xbfebfbff | | | | |
2ed0: AT_SYSINFO_EHDR(33)=0x7fff6c86b000 | | | | |
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | | | | |
2ec8: environ[2]=(nil) | | | | |
2ec0: environ[1]=0x7fff6c844fe2 ------------------|-+ | | |
2eb8: environ[0]=0x7fff6c844fd8 ------------------+ | | |
2eb0: argv[3]=(nil) | | |
2ea8: argv[2]=0x7fff6c844fd4 ---------------------------|-|-+
2ea0: argv[1]=0x7fff6c844fd0 ---------------------------|-+
2e98: argv[0]=0x7fff6c844fcb ---------------------------+
0x7fff6c842e90: argc=3
在 ELF 二进制文件(glibc 称之为 _start
)入口点的第一条指令处,栈顶(0(%rsp)
)将指向 argc
,如如图所示。然后是 argv
数组,它只是一个指针数组。环境变量数组也是 ABI 的一部分,ELF auxiliary vector (which may be used by the glibc initialization code) 也是如此。虽然如果您确定(或假设)ELF 二进制文件不会使用环境变量数组或辅助向量,那么您只需设置 argv
和 argc
,如图所示。