[ld.so] Properly handle stack alignment in ld.so args

Now when loading a process, init will push all the loader args, align
the stack so that the next pointer will have correct alignment, then
push a pointer to the loader args.

Also:
* _ldso_start will pop all loader args off of the stack before jumping
  to the loaded program's entrypoint, as if it had never run.
* The sentinel arg structures have a size instead of being all zeros, so
  that they can be popped off properly when done.
This commit is contained in:
Justin C. Miller
2024-02-20 19:42:12 -08:00
parent e17119254b
commit 9f8e75f680
3 changed files with 28 additions and 5 deletions

View File

@@ -43,7 +43,6 @@ struct j6_arg_loader
uintptr_t *got; uintptr_t *got;
uintptr_t entrypoint; uintptr_t entrypoint;
uintptr_t start_addr; uintptr_t start_addr;
uintptr_t other;
}; };
struct j6_arg_driver struct j6_arg_driver

View File

@@ -17,10 +17,12 @@ _ldso_start:
; Call ldso_init with the loader-provided stack data and ; Call ldso_init with the loader-provided stack data and
; also the address of the GOT, since clang refuses to take ; also the address of the GOT, since clang refuses to take
; the address of it, only dereference it. ; the address of it, only dereference it.
mov rdi, rbp mov rdi, [rbp]
lea rsi, [rel _GLOBAL_OFFSET_TABLE_] lea rsi, [rel _GLOBAL_OFFSET_TABLE_]
call ldso_init call ldso_init
; The real program's entrypoint is now in rax
; The real program's entrypoint is now in rax, save it to r11
mov r11, rax
; Put the function call params back ; Put the function call params back
pop r9 pop r9
@@ -30,7 +32,19 @@ _ldso_start:
pop rsi pop rsi
pop rdi pop rdi
jmp rax ; Pop all the loader args
pop rsp ; Point the stack at the first arg
mov rax, 0
mov rbx, 0
.poploop:
mov eax, [dword rsp] ; size
mov ebx, [dword rsp+4] ; type
add rsp, rax
cmp ebx, 0
jne .poploop
mov rbp, rsp
jmp r11
.end: .end:

View File

@@ -49,7 +49,7 @@ stack_push_sentinel(uint8_t *&stack)
memset(stack, 0, size); memset(stack, 0, size);
j6_arg_header *header = reinterpret_cast<j6_arg_header*>(stack); j6_arg_header *header = reinterpret_cast<j6_arg_header*>(stack);
header->type = j6_arg_type_none; header->type = j6_arg_type_none;
header->size = 0; header->size = size;
} }
template <typename T> T * template <typename T> T *
@@ -222,6 +222,7 @@ load_program(
uintptr_t entrypoint = program_elf.entrypoint() + program_image_base; uintptr_t entrypoint = program_elf.entrypoint() + program_image_base;
if (dyn) { if (dyn) {
stack_push_sentinel(stack);
j6_arg_loader *loader_arg = stack_push<j6_arg_loader>(stack, 0); j6_arg_loader *loader_arg = stack_push<j6_arg_loader>(stack, 0);
const elf::file_header *h = program_elf.header(); const elf::file_header *h = program_elf.header();
loader_arg->image_base = program_image_base; loader_arg->image_base = program_image_base;
@@ -240,6 +241,15 @@ load_program(
handles_arg->handles[1].handle = vfs; handles_arg->handles[1].handle = vfs;
handles_arg->handles[1].proto = j6::proto::vfs::id; handles_arg->handles[1].proto = j6::proto::vfs::id;
// Align the stack to be one word short of 16-byte aligned, so
// that the arg address will be aligned when pushed
while ((reinterpret_cast<uintptr_t>(stack) & 0xf) != 0x8) --stack;
// Push the args list address itself
stack -= sizeof(uintptr_t);
uintptr_t *args_addr = reinterpret_cast<uintptr_t*>(stack);
*args_addr = stack_top - (stack_orig - reinterpret_cast<uint8_t*>(handles_arg));
uintptr_t ldso_image_base = (eop & ~(MiB-1)) + MiB; uintptr_t ldso_image_base = (eop & ~(MiB-1)) + MiB;
for (auto seg : program_elf.segments()) { for (auto seg : program_elf.segments()) {