155120aadSJeff Bailey //===-- Implementation of crt for aarch64 ---------------------------------===//
255120aadSJeff Bailey //
355120aadSJeff Bailey // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
455120aadSJeff Bailey // See https://llvm.org/LICENSE.txt for license information.
555120aadSJeff Bailey // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
655120aadSJeff Bailey //
755120aadSJeff Bailey //===----------------------------------------------------------------------===//
855120aadSJeff Bailey 
955120aadSJeff Bailey #include "config/linux/app.h"
1055120aadSJeff Bailey #include "src/__support/OSUtil/syscall.h"
118dc42802SSiva Chandra Reddy #include "src/__support/threads/thread.h"
12be6af89fSSiva Chandra #include "src/string/memory_utils/memcpy_implementations.h"
13be6af89fSSiva Chandra 
14be6af89fSSiva Chandra #include <arm_acle.h>
1555120aadSJeff Bailey 
1655120aadSJeff Bailey #include <linux/auxvec.h>
1755120aadSJeff Bailey #include <linux/elf.h>
1855120aadSJeff Bailey #include <stdint.h>
19be6af89fSSiva Chandra #include <sys/mman.h>
2086bebe1aSSiva Chandra Reddy #include <sys/syscall.h>
2155120aadSJeff Bailey 
2255120aadSJeff Bailey extern "C" int main(int, char **, char **);
2355120aadSJeff Bailey 
2455120aadSJeff Bailey // Source documentation:
2555120aadSJeff Bailey // https://github.com/ARM-software/abi-aa/tree/main/sysvabi64
2655120aadSJeff Bailey 
2755120aadSJeff Bailey namespace __llvm_libc {
2855120aadSJeff Bailey 
29be6af89fSSiva Chandra #ifdef SYS_mmap2
30be6af89fSSiva Chandra static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap2;
31be6af89fSSiva Chandra #elif SYS_mmap
32be6af89fSSiva Chandra static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap;
33be6af89fSSiva Chandra #else
34be6af89fSSiva Chandra #error "Target platform does not have SYS_mmap or SYS_mmap2 defined"
35be6af89fSSiva Chandra #endif
36be6af89fSSiva Chandra 
3755120aadSJeff Bailey AppProperties app;
3855120aadSJeff Bailey 
398dc42802SSiva Chandra Reddy static ThreadAttributes main_thread_attrib;
408dc42802SSiva Chandra Reddy 
init_tls(TLSDescriptor & tls_descriptor)41859c1897SSiva Chandra Reddy void init_tls(TLSDescriptor &tls_descriptor) {
42859c1897SSiva Chandra Reddy   if (app.tls.size == 0) {
43859c1897SSiva Chandra Reddy     tls_descriptor.size = 0;
44859c1897SSiva Chandra Reddy     tls_descriptor.tp = 0;
45be6af89fSSiva Chandra     return;
46859c1897SSiva Chandra Reddy   }
47be6af89fSSiva Chandra 
48be6af89fSSiva Chandra   // aarch64 follows the variant 1 TLS layout:
49be6af89fSSiva Chandra   //
50be6af89fSSiva Chandra   // 1. First entry is the dynamic thread vector pointer
51be6af89fSSiva Chandra   // 2. Second entry is a 8-byte reserved word.
52be6af89fSSiva Chandra   // 3. Padding for alignment.
53be6af89fSSiva Chandra   // 4. The TLS data from the ELF image.
54be6af89fSSiva Chandra   //
55be6af89fSSiva Chandra   // The thread pointer points to the first entry.
56be6af89fSSiva Chandra 
57859c1897SSiva Chandra Reddy   const uintptr_t size_of_pointers = 2 * sizeof(uintptr_t);
58859c1897SSiva Chandra Reddy   uintptr_t padding = 0;
59859c1897SSiva Chandra Reddy   const uintptr_t ALIGNMENT_MASK = app.tls.align - 1;
60859c1897SSiva Chandra Reddy   uintptr_t diff = size_of_pointers & ALIGNMENT_MASK;
61be6af89fSSiva Chandra   if (diff != 0)
62be6af89fSSiva Chandra     padding += (ALIGNMENT_MASK - diff) + 1;
63be6af89fSSiva Chandra 
64859c1897SSiva Chandra Reddy   uintptr_t alloc_size = size_of_pointers + padding + app.tls.size;
65be6af89fSSiva Chandra 
66be6af89fSSiva Chandra   // We cannot call the mmap function here as the functions set errno on
67be6af89fSSiva Chandra   // failure. Since errno is implemented via a thread local variable, we cannot
68be6af89fSSiva Chandra   // use errno before TLS is setup.
69be6af89fSSiva Chandra   long mmap_ret_val = __llvm_libc::syscall(MMAP_SYSCALL_NUMBER, nullptr,
70be6af89fSSiva Chandra                                            alloc_size, PROT_READ | PROT_WRITE,
71be6af89fSSiva Chandra                                            MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
72be6af89fSSiva Chandra   // We cannot check the return value with MAP_FAILED as that is the return
73be6af89fSSiva Chandra   // of the mmap function and not the mmap syscall.
74be6af89fSSiva Chandra   if (mmap_ret_val < 0 && static_cast<uintptr_t>(mmap_ret_val) > -app.pageSize)
75be6af89fSSiva Chandra     __llvm_libc::syscall(SYS_exit, 1);
76be6af89fSSiva Chandra   uintptr_t thread_ptr = uintptr_t(reinterpret_cast<uintptr_t *>(mmap_ret_val));
77be6af89fSSiva Chandra   uintptr_t tls_addr = thread_ptr + size_of_pointers + padding;
78be6af89fSSiva Chandra   __llvm_libc::inline_memcpy(reinterpret_cast<char *>(tls_addr),
79be6af89fSSiva Chandra                              reinterpret_cast<const char *>(app.tls.address),
80be6af89fSSiva Chandra                              app.tls.init_size);
81859c1897SSiva Chandra Reddy   tls_descriptor.size = alloc_size;
82859c1897SSiva Chandra Reddy   tls_descriptor.addr = thread_ptr;
83859c1897SSiva Chandra Reddy   tls_descriptor.tp = thread_ptr;
84be6af89fSSiva Chandra }
85be6af89fSSiva Chandra 
cleanup_tls(uintptr_t addr,uintptr_t size)86859c1897SSiva Chandra Reddy void cleanup_tls(uintptr_t addr, uintptr_t size) {
87859c1897SSiva Chandra Reddy   if (size == 0)
88859c1897SSiva Chandra Reddy     return;
89859c1897SSiva Chandra Reddy   __llvm_libc::syscall(SYS_munmap, addr, size);
90859c1897SSiva Chandra Reddy }
91859c1897SSiva Chandra Reddy 
set_thread_ptr(uintptr_t val)92859c1897SSiva Chandra Reddy static void set_thread_ptr(uintptr_t val) { __arm_wsr64("tpidr_el0", val); }
93859c1897SSiva Chandra Reddy 
9455120aadSJeff Bailey } // namespace __llvm_libc
9555120aadSJeff Bailey 
9655120aadSJeff Bailey using __llvm_libc::app;
9755120aadSJeff Bailey 
9855120aadSJeff Bailey // TODO: Would be nice to use the aux entry structure from elf.h when available.
9955120aadSJeff Bailey struct AuxEntry {
10055120aadSJeff Bailey   uint64_t type;
10155120aadSJeff Bailey   uint64_t value;
10255120aadSJeff Bailey };
10355120aadSJeff Bailey 
_start()10455120aadSJeff Bailey extern "C" void _start() {
10555120aadSJeff Bailey   // Skip the Frame Pointer and the Link Register
10655120aadSJeff Bailey   // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst
10755120aadSJeff Bailey   // Section 6.2.3
108fe2cc14aSSiva Chandra Reddy   app.args = reinterpret_cast<__llvm_libc::Args *>(
109fe2cc14aSSiva Chandra Reddy       reinterpret_cast<uintptr_t *>(__builtin_frame_address(0)) + 2);
11055120aadSJeff Bailey 
1118dc42802SSiva Chandra Reddy   auto tid = __llvm_libc::syscall(SYS_gettid);
1128dc42802SSiva Chandra Reddy   if (tid <= 0)
1138dc42802SSiva Chandra Reddy     __llvm_libc::syscall(SYS_exit, 1);
1148dc42802SSiva Chandra Reddy   __llvm_libc::main_thread_attrib.tid = tid;
1158dc42802SSiva Chandra Reddy 
11655120aadSJeff Bailey   // After the argv array, is a 8-byte long NULL value before the array of env
11755120aadSJeff Bailey   // values. The end of the env values is marked by another 8-byte long NULL
11855120aadSJeff Bailey   // value. We step over it (the "+ 1" below) to get to the env values.
119fe2cc14aSSiva Chandra Reddy   uint64_t *env_ptr = app.args->argv + app.args->argc + 1;
12055120aadSJeff Bailey   uint64_t *env_end_marker = env_ptr;
121*98fdabecSSiva Chandra   app.envPtr = env_ptr;
12255120aadSJeff Bailey   while (*env_end_marker)
12355120aadSJeff Bailey     ++env_end_marker;
12455120aadSJeff Bailey 
12555120aadSJeff Bailey   // After the env array, is the aux-vector. The end of the aux-vector is
12655120aadSJeff Bailey   // denoted by an AT_NULL entry.
127be6af89fSSiva Chandra   Elf64_Phdr *programHdrTable = nullptr;
128be6af89fSSiva Chandra   uintptr_t programHdrCount;
12955120aadSJeff Bailey   for (AuxEntry *aux_entry = reinterpret_cast<AuxEntry *>(env_end_marker + 1);
13055120aadSJeff Bailey        aux_entry->type != AT_NULL; ++aux_entry) {
13155120aadSJeff Bailey     switch (aux_entry->type) {
132be6af89fSSiva Chandra     case AT_PHDR:
133be6af89fSSiva Chandra       programHdrTable = reinterpret_cast<Elf64_Phdr *>(aux_entry->value);
134be6af89fSSiva Chandra       break;
135be6af89fSSiva Chandra     case AT_PHNUM:
136be6af89fSSiva Chandra       programHdrCount = aux_entry->value;
137be6af89fSSiva Chandra       break;
13855120aadSJeff Bailey     case AT_PAGESZ:
13955120aadSJeff Bailey       app.pageSize = aux_entry->value;
14055120aadSJeff Bailey       break;
14155120aadSJeff Bailey     default:
14255120aadSJeff Bailey       break; // TODO: Read other useful entries from the aux vector.
14355120aadSJeff Bailey     }
14455120aadSJeff Bailey   }
14555120aadSJeff Bailey 
146be6af89fSSiva Chandra   app.tls.size = 0;
147be6af89fSSiva Chandra   for (uintptr_t i = 0; i < programHdrCount; ++i) {
148be6af89fSSiva Chandra     Elf64_Phdr *phdr = programHdrTable + i;
149be6af89fSSiva Chandra     if (phdr->p_type != PT_TLS)
150be6af89fSSiva Chandra       continue;
151be6af89fSSiva Chandra     // TODO: p_vaddr value has to be adjusted for static-pie executables.
152be6af89fSSiva Chandra     app.tls.address = phdr->p_vaddr;
153be6af89fSSiva Chandra     app.tls.size = phdr->p_memsz;
154be6af89fSSiva Chandra     app.tls.init_size = phdr->p_filesz;
155be6af89fSSiva Chandra     app.tls.align = phdr->p_align;
156be6af89fSSiva Chandra   }
157be6af89fSSiva Chandra 
158859c1897SSiva Chandra Reddy   __llvm_libc::TLSDescriptor tls;
159859c1897SSiva Chandra Reddy   __llvm_libc::init_tls(tls);
160859c1897SSiva Chandra Reddy   if (tls.size != 0)
161859c1897SSiva Chandra Reddy     __llvm_libc::set_thread_ptr(tls.tp);
16255120aadSJeff Bailey 
1638dc42802SSiva Chandra Reddy   __llvm_libc::self.attrib = &__llvm_libc::main_thread_attrib;
1648dc42802SSiva Chandra Reddy 
165859c1897SSiva Chandra Reddy   int retval = main(app.args->argc, reinterpret_cast<char **>(app.args->argv),
166859c1897SSiva Chandra Reddy                     reinterpret_cast<char **>(env_ptr));
167859c1897SSiva Chandra Reddy   __llvm_libc::cleanup_tls(tls.addr, tls.size);
168859c1897SSiva Chandra Reddy   __llvm_libc::syscall(SYS_exit, retval);
16955120aadSJeff Bailey }
170