Kernel/LibC/DynamicLoader: Update process start ABI

We now use SysV abi for process startup
This commit is contained in:
Bananymous 2025-04-15 23:04:20 +03:00
parent e6026cb0b8
commit 36baf7b0af
12 changed files with 314 additions and 280 deletions

View File

@ -41,15 +41,6 @@ start_userspace_thread:
call get_thread_start_sp
movl %eax, %esp
# STACK LAYOUT
# entry
# argc
# argv
# envp
# userspace stack
call get_userspace_thread_stack_top
movw $(0x20 | 3), %bx
movw %bx, %ds
movw %bx, %es
@ -57,15 +48,4 @@ start_userspace_thread:
movw %bx, %gs
xorw %bx, %bx
popl %edi
popl %esi
popl %edx
popl %ecx
popl %ebx
pushl $(0x20 | 3)
pushl %eax
pushl $0x202
pushl $(0x18 | 3)
pushl %ebx
iret

View File

@ -32,24 +32,4 @@ start_userspace_thread:
call get_thread_start_sp
movq %rax, %rsp
# STACK LAYOUT
# entry
# argc
# argv
# envp
# userspace stack
call get_userspace_thread_stack_top
popq %rdi
popq %rsi
popq %rdx
popq %rcx
popq %rbx
pushq $(0x20 | 3)
pushq %rax
pushq $0x202
pushq $(0x18 | 3)
pushq %rbx
iretq

View File

@ -34,15 +34,6 @@ namespace Kernel
public:
using entry_t = Thread::entry_t;
struct userspace_info_t
{
uintptr_t entry { 0 };
int argc { 0 };
char** argv { nullptr };
char** envp { nullptr };
int file_fd { -1 };
};
public:
static Process* create_kernel();
static Process* create_kernel(entry_t, void*);
@ -183,7 +174,7 @@ namespace Kernel
BAN::ErrorOr<long> sys_sigprocmask(int how, const sigset_t* set, sigset_t* oset);
BAN::ErrorOr<long> sys_yield();
BAN::ErrorOr<long> sys_pthread_create(const pthread_attr_t* __restrict attr, void (*entry)(void*), void* arg);
BAN::ErrorOr<long> sys_pthread_create(const pthread_attr_t* attr, void (*entry)(void*), void* arg);
BAN::ErrorOr<long> sys_pthread_exit(void* value);
BAN::ErrorOr<long> sys_pthread_join(pthread_t thread, void** value);
BAN::ErrorOr<long> sys_pthread_self();
@ -208,7 +199,6 @@ namespace Kernel
size_t proc_environ(off_t offset, BAN::ByteSpan) const;
bool is_userspace() const { return m_is_userspace; }
const userspace_info_t& userspace_info() const { return m_userspace_info; }
// Returns error if page could not be allocated
// Returns true if the page was allocated successfully
@ -315,7 +305,6 @@ namespace Kernel
BAN::Vector<BAN::String> m_environ;
bool m_is_userspace { false };
userspace_info_t m_userspace_info;
SpinLock m_child_exit_lock;
BAN::Vector<ChildExitStatus> m_child_exit_statuses;

View File

@ -7,6 +7,8 @@
#include <kernel/Memory/VirtualRange.h>
#include <kernel/ThreadBlocker.h>
#include <LibELF/AuxiliaryVector.h>
#include <signal.h>
#include <sys/types.h>
@ -41,9 +43,10 @@ namespace Kernel
BAN::ErrorOr<Thread*> pthread_create(entry_t, void*);
BAN::ErrorOr<Thread*> clone(Process*, uintptr_t sp, uintptr_t ip);
void setup_exec();
void setup_process_cleanup();
BAN::ErrorOr<void> initialize_userspace(vaddr_t entry, BAN::Span<BAN::String> argv, BAN::Span<BAN::String> envp, BAN::Span<LibELF::AuxiliaryVector> auxv);
// Returns true, if thread is going to trigger signal
bool is_interrupted_by_signal() const;
@ -100,7 +103,7 @@ namespace Kernel
private:
Thread(pid_t tid, Process*);
void setup_exec_impl(uintptr_t entry, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
void setup_exec(vaddr_t ip, vaddr_t sp);
static void on_exit_trampoline(Thread*);
void on_exit();

View File

@ -248,11 +248,7 @@ namespace Kernel
}
if (Thread::current().has_process() && Process::current().is_userspace())
{
const char* const* argv = Process::current().userspace_info().argv;
if (argv && *argv)
process_name = *argv;
}
process_name = Process::current().name();
#if ARCH(x86_64)
dwarnln(

View File

@ -17,6 +17,8 @@
#include <kernel/Terminal/PseudoTerminal.h>
#include <kernel/Timer/Timer.h>
#include <LibELF/AuxiliaryVector.h>
#include <LibInput/KeyboardLayout.h>
#include <fcntl.h>
@ -116,76 +118,51 @@ namespace Kernel
process->m_working_directory = VirtualFileSystem::get().root_file();
process->m_page_table = BAN::UniqPtr<PageTable>::adopt(MUST(PageTable::create_userspace()));
TRY(process->m_cmdline.push_back({}));
TRY(process->m_cmdline.emplace_back());
TRY(process->m_cmdline.back().append(path));
for (auto argument : arguments)
{
TRY(process->m_cmdline.emplace_back());
TRY(process->m_cmdline.back().append(argument));
}
LockGuard _(process->m_process_lock);
auto executable_inode = TRY(process->find_file(AT_FDCWD, path.data(), O_EXEC)).inode;
auto executable_file = TRY(process->find_file(AT_FDCWD, path.data(), O_EXEC));
auto executable_inode = executable_file.inode;
auto executable = TRY(ELF::load_from_inode(executable_inode, process->m_credentials, process->page_table()));
process->m_mapped_regions = BAN::move(executable.regions);
char** argv_addr = nullptr;
{
size_t needed_bytes = sizeof(char*) + path.size() + 1;
for (auto argument : arguments)
needed_bytes += sizeof(char*) + argument.size() + 1;
needed_bytes += sizeof(char*);
if (auto rem = needed_bytes % PAGE_SIZE)
needed_bytes += PAGE_SIZE - rem;
auto argv_region = MUST(MemoryBackedRegion::create(
process->page_table(),
needed_bytes,
{ .start = 0x400000, .end = KERNEL_OFFSET },
MemoryRegion::Type::PRIVATE,
PageTable::Flags::UserSupervisor | PageTable::Flags::Present
));
argv_addr = reinterpret_cast<char**>(argv_region->vaddr());
uintptr_t offset = sizeof(char*) * (1 + arguments.size() + 1);
for (size_t i = 0; i <= arguments.size(); i++)
{
const uintptr_t addr = argv_region->vaddr() + offset;
TRY(argv_region->copy_data_to_region(i * sizeof(char*), reinterpret_cast<const uint8_t*>(&addr), sizeof(char*)));
const auto current = (i == 0) ? path : arguments[i - 1];
TRY(argv_region->copy_data_to_region(offset, reinterpret_cast<const uint8_t*>(current.data()), current.size()));
const uint8_t zero = 0;
TRY(argv_region->copy_data_to_region(offset + current.size(), &zero, 1));
offset += current.size() + 1;
}
const uintptr_t zero = 0;
TRY(argv_region->copy_data_to_region((1 + arguments.size()) * sizeof(char*), reinterpret_cast<const uint8_t*>(&zero), sizeof(char*)));
TRY(process->m_mapped_regions.push_back(BAN::move(argv_region)));
}
if (executable_inode->mode().mode & +Inode::Mode::ISUID)
process->m_credentials.set_euid(executable_inode->uid());
if (executable_inode->mode().mode & +Inode::Mode::ISGID)
process->m_credentials.set_egid(executable_inode->gid());
BAN::Vector<LibELF::AuxiliaryVector> auxiliary_vector;
TRY(auxiliary_vector.reserve(1 + executable.open_execfd));
if (executable.has_interpreter)
{
VirtualFileSystem::File file;
TRY(file.canonical_path.append("<self>"));
file.inode = executable_inode;
process->m_userspace_info.file_fd = TRY(process->m_open_file_descriptors.open(BAN::move(file), O_RDONLY));
const int execfd = TRY(process->m_open_file_descriptors.open(BAN::move(executable_file), O_RDONLY));
TRY(auxiliary_vector.push_back({
.a_type = LibELF::AT_EXECFD,
.a_un = { .a_val = static_cast<uint32_t>(execfd) },
}));
}
process->m_is_userspace = true;
process->m_userspace_info.entry = executable.entry_point;
process->m_userspace_info.argc = 1 + arguments.size();
process->m_userspace_info.argv = argv_addr;
process->m_userspace_info.envp = nullptr;
TRY(auxiliary_vector.push_back({
.a_type = LibELF::AT_NULL,
.a_un = { .a_val = 0 },
}));
auto* thread = MUST(Thread::create_userspace(process, process->page_table()));
thread->setup_exec();
MUST(thread->initialize_userspace(
executable.entry_point,
process->m_cmdline.span(),
process->m_environ.span(),
auxiliary_vector.span()
));
process->add_thread(thread);
process->register_to_scheduler();
@ -534,7 +511,6 @@ namespace Kernel
forked->m_open_file_descriptors = BAN::move(*open_file_descriptors);
forked->m_mapped_regions = BAN::move(mapped_regions);
forked->m_is_userspace = m_is_userspace;
forked->m_userspace_info = m_userspace_info;
forked->m_has_called_exec = false;
memcpy(forked->m_signal_handlers, m_signal_handlers, sizeof(m_signal_handlers));
@ -561,77 +537,62 @@ namespace Kernel
TRY(validate_string_access(path));
auto executable_file = TRY(find_file(AT_FDCWD, path, O_EXEC));
auto executable_inode = executable_file.inode;
BAN::Vector<BAN::String> str_argv;
for (int i = 0; argv && argv[i]; i++)
{
TRY(validate_pointer_access(argv + i, sizeof(char*), false));
TRY(validate_string_access(argv[i]));
TRY(str_argv.emplace_back(argv[i]));
TRY(str_argv.emplace_back());
TRY(str_argv.back().append(argv[i]));
}
BAN::Vector<BAN::String> str_envp;
for (int i = 0; envp && envp[i]; i++)
{
TRY(validate_pointer_access(envp + 1, sizeof(char*), false));
TRY(validate_pointer_access(envp + i, sizeof(char*), false));
TRY(validate_string_access(envp[i]));
TRY(str_envp.emplace_back(envp[i]));
TRY(str_envp.emplace_back());
TRY(str_envp.back().append(envp[i]));
}
auto executable_file = TRY(find_file(AT_FDCWD, path, O_EXEC));
auto executable_inode = executable_file.inode;
auto executable = TRY(ELF::load_from_inode(executable_inode, m_credentials, *new_page_table));
auto new_mapped_regions = BAN::move(executable.regions);
int file_fd = -1;
BAN::Vector<LibELF::AuxiliaryVector> auxiliary_vector;
TRY(auxiliary_vector.reserve(1 + executable.open_execfd));
BAN::ScopeGuard execfd_guard([this, &auxiliary_vector] {
if (auxiliary_vector.empty())
return;
if (auxiliary_vector.front().a_type != LibELF::AT_EXECFD)
return;
MUST(m_open_file_descriptors.close(auxiliary_vector.front().a_un.a_val));
});
if (executable.has_interpreter)
{
VirtualFileSystem::File file;
file.canonical_path = BAN::move(executable_file.canonical_path);
file.inode = executable_inode;
file_fd = TRY(m_open_file_descriptors.open(BAN::move(file), O_RDONLY));
const int execfd = TRY(m_open_file_descriptors.open(BAN::move(executable_file), O_RDONLY));
TRY(auxiliary_vector.push_back({
.a_type = LibELF::AT_EXECFD,
.a_un = { .a_val = static_cast<uint32_t>(execfd) },
}));
}
BAN::ScopeGuard file_closer([&] { if (file_fd != -1) MUST(m_open_file_descriptors.close(file_fd)); });
// allocate memory on the new process for arguments and environment
auto create_region =
[&](BAN::Span<BAN::String> container) -> BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>>
{
size_t bytes = sizeof(char*);
for (auto& elem : container)
bytes += sizeof(char*) + elem.size() + 1;
if (auto rem = bytes % PAGE_SIZE)
bytes += PAGE_SIZE - rem;
auto region = TRY(MemoryBackedRegion::create(
*new_page_table,
bytes,
{ .start = executable.entry_point, .end = KERNEL_OFFSET },
MemoryRegion::Type::PRIVATE,
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present
));
size_t data_offset = sizeof(char*) * (container.size() + 1);
for (size_t i = 0; i < container.size(); i++)
{
uintptr_t ptr_addr = region->vaddr() + data_offset;
TRY(region->copy_data_to_region(sizeof(char*) * i, (const uint8_t*)&ptr_addr, sizeof(char*)));
TRY(region->copy_data_to_region(data_offset, (const uint8_t*)container[i].data(), container[i].size()));
data_offset += container[i].size() + 1;
}
uintptr_t null = 0;
TRY(region->copy_data_to_region(sizeof(char*) * container.size(), (const uint8_t*)&null, sizeof(char*)));
return BAN::UniqPtr<MemoryRegion>(BAN::move(region));
};
TRY(new_mapped_regions.reserve(new_mapped_regions.size() + 2));
MUST(new_mapped_regions.push_back(TRY(create_region(str_argv.span()))));
MUST(new_mapped_regions.push_back(TRY(create_region(str_envp.span()))));
TRY(auxiliary_vector.push_back({
.a_type = LibELF::AT_NULL,
.a_un = { .a_val = 0 },
}));
auto* new_thread = TRY(Thread::create_userspace(this, *new_page_table));
TRY(new_thread->initialize_userspace(
executable.entry_point,
str_argv.span(),
str_envp.span(),
auxiliary_vector.span()
));
ASSERT(Processor::get_interrupt_state() == InterruptState::Enabled);
Processor::set_interrupt_state(InterruptState::Disabled);
@ -655,8 +616,8 @@ namespace Kernel
m_threads.front()->m_process = nullptr;
m_threads.front()->give_keep_alive_page_table(BAN::move(m_page_table));
MUST(Processor::scheduler().add_thread(new_thread));
m_threads.front() = new_thread;
MUST(Processor::scheduler().add_thread(m_threads.front()));
for (size_t i = 0; i < sizeof(m_signal_handlers) / sizeof(*m_signal_handlers); i++)
{
@ -673,21 +634,13 @@ namespace Kernel
m_mapped_regions = BAN::move(new_mapped_regions);
m_page_table = BAN::move(new_page_table);
file_closer.disable();
m_userspace_info.argc = str_argv.size();
m_userspace_info.argv = reinterpret_cast<char**>(m_mapped_regions[m_mapped_regions.size() - 2]->vaddr());
m_userspace_info.envp = reinterpret_cast<char**>(m_mapped_regions[m_mapped_regions.size() - 1]->vaddr());
m_userspace_info.entry = executable.entry_point;
m_userspace_info.file_fd = file_fd;
execfd_guard.disable();
m_cmdline = BAN::move(str_argv);
m_environ = BAN::move(str_envp);
}
m_has_called_exec = true;
m_threads.front()->setup_exec();
Processor::yield();
ASSERT_NOT_REACHED();
}
@ -2084,7 +2037,7 @@ namespace Kernel
return 0;
}
BAN::ErrorOr<long> Process::sys_pthread_create(const pthread_attr_t* __restrict attr, void (*entry)(void*), void* arg)
BAN::ErrorOr<long> Process::sys_pthread_create(const pthread_attr_t* attr, void (*entry)(void*), void* arg)
{
if (attr != nullptr)
{

View File

@ -44,11 +44,7 @@ namespace Kernel
BAN::ErrorOr<long> ret = BAN::Error::from_errno(ENOSYS);
const char* process_path = nullptr;
if (Process::current().userspace_info().argc > 0 && Process::current().userspace_info().argv)
process_path = Process::current().userspace_info().argv[0];
if (process_path == nullptr)
process_path = "<null>";
const char* process_path = Process::current().name();
#if DUMP_ALL_SYSCALLS
dprintln("{} pid {}: {}", process_path, Process::current().pid(), s_syscall_names[syscall]);

View File

@ -29,11 +29,6 @@ namespace Kernel
return Thread::current().interrupt_stack().sp;
}
extern "C" uintptr_t get_userspace_thread_stack_top()
{
return Thread::current().userspace_stack_top() - 4 * sizeof(uintptr_t);
}
extern "C" void load_thread_sse()
{
Thread::current().load_sse();
@ -176,11 +171,12 @@ namespace Kernel
save_sse();
memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage));
thread->setup_exec_impl(
reinterpret_cast<uintptr_t>(entry),
reinterpret_cast<uintptr_t>(arg),
0, 0, 0
);
PageTable::with_fast_page(thread->userspace_stack().paddr_of(thread->userspace_stack_top() - PAGE_SIZE), [=] {
PageTable::fast_page_as<void*>(PAGE_SIZE - sizeof(uintptr_t)) = arg;
});
const vaddr_t entry_addr = reinterpret_cast<vaddr_t>(entry);
thread->setup_exec(entry_addr, thread->userspace_stack_top() - sizeof(uintptr_t));
return thread;
}
@ -222,21 +218,112 @@ namespace Kernel
return thread;
}
void Thread::setup_exec()
BAN::ErrorOr<void> Thread::initialize_userspace(vaddr_t entry, BAN::Span<BAN::String> argv, BAN::Span<BAN::String> envp, BAN::Span<LibELF::AuxiliaryVector> auxv)
{
const auto& userspace_info = process().userspace_info();
ASSERT(userspace_info.entry);
// System V ABI: Initial process stack
setup_exec_impl(
userspace_info.entry,
userspace_info.argc,
reinterpret_cast<uintptr_t>(userspace_info.argv),
reinterpret_cast<uintptr_t>(userspace_info.envp),
userspace_info.file_fd
);
ASSERT(m_is_userspace);
ASSERT(m_userspace_stack);
size_t needed_size = 0;
// argc
needed_size += sizeof(uintptr_t);
// argv
needed_size += (argv.size() + 1) * sizeof(uintptr_t);
for (auto arg : argv)
needed_size += arg.size() + 1;
// envp
needed_size += (envp.size() + 1) * sizeof(uintptr_t);
for (auto env : envp)
needed_size += env.size() + 1;
// auxv
needed_size += auxv.size() * sizeof(LibELF::AuxiliaryVector);
if (needed_size > m_userspace_stack->size())
return BAN::Error::from_errno(ENOBUFS);
vaddr_t vaddr = userspace_stack_top() - needed_size;
const auto stack_copy_buf =
[this](BAN::ConstByteSpan buffer, vaddr_t vaddr) -> void
{
ASSERT(vaddr + buffer.size() <= userspace_stack_top());
size_t bytes_copied = 0;
while (bytes_copied < buffer.size())
{
const size_t to_copy = BAN::Math::min<size_t>(buffer.size() - bytes_copied, PAGE_SIZE - (vaddr % PAGE_SIZE));
PageTable::with_fast_page(userspace_stack().paddr_of(vaddr & PAGE_ADDR_MASK), [=]() {
memcpy(PageTable::fast_page_as_ptr(vaddr % PAGE_SIZE), buffer.data() + bytes_copied, to_copy);
});
vaddr += to_copy;
bytes_copied += to_copy;
}
};
const auto stack_push_buf =
[&stack_copy_buf, &vaddr](BAN::ConstByteSpan buffer) -> void
{
stack_copy_buf(buffer, vaddr);
vaddr += buffer.size();
};
const auto stack_push_uint =
[&stack_push_buf](uintptr_t value) -> void
{
stack_push_buf(BAN::ConstByteSpan::from(value));
};
const auto stack_push_str =
[&stack_push_buf](BAN::StringView string) -> void
{
const uint8_t* string_u8 = reinterpret_cast<const uint8_t*>(string.data());
stack_push_buf(BAN::ConstByteSpan(string_u8, string.size() + 1));
};
// argc
stack_push_uint(argv.size());
// argv
const vaddr_t argv_vaddr = vaddr;
vaddr += argv.size() * sizeof(uintptr_t);
stack_push_uint(0);
// envp
const vaddr_t envp_vaddr = vaddr;
vaddr += envp.size() * sizeof(uintptr_t);
stack_push_uint(0);
// auxv
for (auto aux : auxv)
stack_push_buf(BAN::ConstByteSpan::from(aux));
// information
for (size_t i = 0; i < argv.size(); i++)
{
stack_copy_buf(BAN::ConstByteSpan::from(vaddr), argv_vaddr + i * sizeof(uintptr_t));
stack_push_str(argv[i]);
}
for (size_t i = 0; i < envp.size(); i++)
{
stack_copy_buf(BAN::ConstByteSpan::from(vaddr), envp_vaddr + i * sizeof(uintptr_t));
stack_push_str(envp[i]);
}
ASSERT(vaddr == userspace_stack_top());
setup_exec(entry, userspace_stack_top() - needed_size);
return {};
}
void Thread::setup_exec_impl(uintptr_t entry, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3)
void Thread::setup_exec(vaddr_t ip, vaddr_t sp)
{
ASSERT(is_userspace());
m_state = State::NotStarted;
@ -244,13 +331,13 @@ namespace Kernel
// Signal mask is inherited
// Initialize stack for returning
PageTable::with_fast_page(process().page_table().physical_address_of(kernel_stack_top() - PAGE_SIZE), [&] {
uintptr_t sp = PageTable::fast_page() + PAGE_SIZE;
write_to_stack(sp, entry);
write_to_stack(sp, arg3);
write_to_stack(sp, arg2);
write_to_stack(sp, arg1);
write_to_stack(sp, arg0);
PageTable::with_fast_page(kernel_stack().paddr_of(kernel_stack_top() - PAGE_SIZE), [=] {
uintptr_t cur_sp = PageTable::fast_page() + PAGE_SIZE;
write_to_stack(cur_sp, 0x20 | 3);
write_to_stack(cur_sp, sp);
write_to_stack(cur_sp, 0x202);
write_to_stack(cur_sp, 0x18 | 3);
write_to_stack(cur_sp, ip);
});
m_interrupt_stack.ip = reinterpret_cast<vaddr_t>(start_userspace_thread);
@ -286,7 +373,7 @@ namespace Kernel
m_signal_pending_mask = 0;
m_signal_block_mask = ~0ull;
PageTable::with_fast_page(process().page_table().physical_address_of(kernel_stack_top() - PAGE_SIZE), [&] {
PageTable::with_fast_page(kernel_stack().paddr_of(kernel_stack_top() - PAGE_SIZE), [&] {
uintptr_t sp = PageTable::fast_page() + PAGE_SIZE;
write_to_stack(sp, this);
write_to_stack(sp, &Thread::on_exit_trampoline);

View File

@ -2,18 +2,22 @@
.global _start
_start:
pushl $0
# get argc, argv, envp
movl (%esp), %edi
leal 4(%esp), %esi
leal 4(%esi, %edi, 4), %edx
# align stack
andl $-16, %esp
xorl %ebp, %ebp
# save argc, argv, envp
subl $4, %esp
pushl %edx
pushl %esi
pushl %edi
# STACK LAYOUT
# null
# argc
# argv
# envp
xorl %ebp, %ebp
subl $4, %esp
pushl $__fini_array_end
pushl $__fini_array_start

View File

@ -2,21 +2,22 @@
.global _start
_start:
pushq $0
# get argc, argv, envp
movq (%rsp), %rdi
leaq 8(%rsp), %rsi
leaq 8(%rsi, %rdi, 8), %rdx
# align stack
andq $-16, %rsp
xorq %rbp, %rbp
# save argc, argv, envp
subq $8, %rsp
pushq %rdi
pushq %rsi
pushq %rdx
# STACK LAYOUT
# null
# argc
# argv
# envp
xorq %rbp, %rbp
# init libc
movq 0(%rsp), %rdi
movq %rdx, %rdi # environ
pushq $__fini_array_end
pushq $__fini_array_start
@ -31,9 +32,10 @@ _start:
addq $(6 * 8), %rsp
# call main
movq 16(%rsp), %rdi
movq 8(%rsp), %rsi
movq 0(%rsp), %rdx
popq %rdx
popq %rsi
popq %rdi
addq $8, %rsp
call main
# call exit

View File

@ -2,6 +2,8 @@
#include <BAN/Atomic.h>
#include <BAN/PlacementNew.h>
#include <kernel/Arch.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
@ -16,15 +18,30 @@ struct pthread_trampoline_info_t
};
// stack is 16 byte aligned on entry, this `call` is used to align it
extern "C" void pthread_trampoline(void*);
asm("pthread_trampoline: call pthread_trampoline_cpp");
extern "C" void _pthread_trampoline(void*);
asm(
#if ARCH(x86_64)
"_pthread_trampoline:"
"popq %rdi;"
"andq $-16, %rsp;"
"xorq %rbp, %rbp;"
"call _pthread_trampoline_cpp"
#elif ARCH(i686)
"_pthread_trampoline:"
"ud2;"
"popl %edi;"
"andl $-16, %esp;"
"xorl %ebp, %ebp;"
"subl $12, %esp;"
"pushl %edi;"
"call _pthread_trampoline_cpp"
#endif
);
extern "C" void pthread_trampoline_cpp(void* arg)
extern "C" void _pthread_trampoline_cpp(void* arg)
{
pthread_trampoline_info_t info;
memcpy(&info, arg, sizeof(pthread_trampoline_info_t));
auto info = *reinterpret_cast<pthread_trampoline_info_t*>(arg);
free(arg);
pthread_exit(info.start_routine(info.arg));
ASSERT_NOT_REACHED();
}
@ -63,24 +80,30 @@ void pthread_cleanup_push(void (*routine)(void*), void* arg)
s_cleanup_stack = cleanup;
}
int pthread_create(pthread_t* __restrict thread, const pthread_attr_t* __restrict attr, void* (*start_routine)(void*), void* __restrict arg)
int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __restrict attr, void* (*start_routine)(void*), void* __restrict arg)
{
auto* info = static_cast<pthread_trampoline_info_t*>(malloc(sizeof(pthread_trampoline_info_t)));
if (info == nullptr)
return -1;
info->start_routine = start_routine;
info->arg = arg;
return errno;
*info = {
.start_routine = start_routine,
.arg = arg,
};
const auto ret = syscall(SYS_PTHREAD_CREATE, attr, pthread_trampoline, info);
if (ret == -1)
{
free(info);
return -1;
}
goto pthread_create_error;
if (thread)
*thread = ret;
if (thread_id)
*thread_id = ret;
return 0;
pthread_create_error:
const int return_code = errno;
free(info);
return return_code;
}
void pthread_exit(void* value_ptr)

View File

@ -1,5 +1,6 @@
#include "utils.h"
#include <LibELF/AuxiliaryVector.h>
#include <LibELF/Types.h>
#include <LibELF/Values.h>
@ -15,42 +16,44 @@ void _start()
{
#if defined(__x86_64__)
asm volatile(
"xorq %rbp, %rbp;"
"movq (%rsp), %rdi;"
"leaq 8(%rsp), %rsi;"
"leaq 8(%rsi, %rdi, 8), %rdx;"
"movq %rsp, %rbp;"
"andq $-16, %rsp;"
"call _entry;"
"movq %rbp, %rsp;"
"xorq %rbp, %rbp;"
"jmp *%rax;"
"ud2;"
);
#elif defined(__i686__)
asm volatile(
"xorl %ebp, %ebp;"
"pushl %ecx;"
"movl (%esp), %edi;"
"leal 4(%esp), %esi;"
"leal 4(%esi, %edi, 4), %edx;"
"movl %esp, %ebp;"
"andl $-16, %esp;"
"subl $4, %esp;"
"pushl %edx;"
"pushl %esi;"
"pushl %edi;"
"call _entry;"
"ud2;"
);
#else
#error "unsupported architecture"
#endif
}
__attribute__((naked, noreturn))
static void call_entry_point(int, char**, char**, uintptr_t)
{
#if defined(__x86_64__)
asm volatile(
"andq $-16, %rsp;"
"jmp *%rcx;"
);
#elif defined(__i686__)
asm volatile(
"addl $4, %esp;"
"popl %edi;"
"popl %esi;"
"popl %edx;"
"popl %ecx;"
"andl $-16, %esp;"
"jmp *%ecx;"
"call _entry;"
"movl %ebp, %esp;"
"xorl %ebp, %ebp;"
"jmp *%eax;"
"ud2;"
);
#else
#error "unsupported architecture"
@ -901,16 +904,33 @@ static void call_init_funcs(LoadedElf& elf, bool is_main_elf)
reinterpret_cast<init_t>(elf.init)();
for (size_t i = 0; i < elf.init_arraysz / sizeof(init_t); i++)
reinterpret_cast<init_t*>(elf.init_array)[i]();
}
static LibELF::AuxiliaryVector* find_auxv(char** envp)
{
if (envp == nullptr)
return nullptr;
char** null_env = envp;
while (*null_env)
null_env++;
return reinterpret_cast<LibELF::AuxiliaryVector*>(null_env + 1);
}
extern "C"
__attribute__((used, noreturn))
int _entry(int argc, char** argv, char** envp, int fd)
__attribute__((used))
uintptr_t _entry(int argc, char* argv[], char* envp[])
{
const bool invoked_directly = (fd < 0);
if (invoked_directly)
int execfd = -1;
if (auto* auxv = find_auxv(envp))
for (auto* aux = auxv; aux->a_type != LibELF::AT_NULL; aux++)
if (aux->a_type == LibELF::AT_EXECFD) {
execfd = aux->a_un.a_val;
aux->a_type = LibELF::AT_IGNORE;
}
if (execfd == -1)
{
if (argc < 2)
print_error_and_exit("missing program name", 0);
@ -918,18 +938,19 @@ int _entry(int argc, char** argv, char** envp, int fd)
argc--;
argv++;
fd = syscall(SYS_OPENAT, AT_FDCWD, argv[0], O_RDONLY);
if (fd < 0)
print_error_and_exit("could not open program", fd);
execfd = syscall(SYS_OPENAT, AT_FDCWD, argv[0], O_RDONLY);
if (execfd < 0)
print_error_and_exit("could not open program", execfd);
}
init_random();
auto& elf = load_elf(argv[0], fd);
auto& elf = load_elf(argv[0], execfd);
syscall(SYS_CLOSE, fd);
fini_random();
relocate_elf(elf, true);
initialize_environ(envp);
call_init_funcs(elf, true);
call_entry_point(argc, argv, envp, elf.base + elf.file_header.e_entry);
return elf.base + elf.file_header.e_entry;
}