1 //===-- Implementation of crt for x86_64 ----------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "config/linux/app.h"
10 #include "src/__support/OSUtil/syscall.h"
11 #include "src/__support/threads/thread.h"
12 #include "src/string/memory_utils/memcpy_implementations.h"
13 
14 #include <asm/prctl.h>
15 #include <linux/auxvec.h>
16 #include <linux/elf.h>
17 #include <stdint.h>
18 #include <sys/mman.h>
19 #include <sys/syscall.h>
20 
21 extern "C" int main(int, char **, char **);
22 
23 namespace __llvm_libc {
24 
25 #ifdef SYS_mmap2
26 static constexpr long mmapSyscallNumber = SYS_mmap2;
27 #elif SYS_mmap
28 static constexpr long mmapSyscallNumber = SYS_mmap;
29 #else
30 #error "Target platform does not have SYS_mmap or SYS_mmap2 defined"
31 #endif
32 
33 AppProperties app;
34 
35 static ThreadAttributes main_thread_attrib;
36 
37 // TODO: The function is x86_64 specific. Move it to config/linux/app.h
38 // and generalize it. Also, dynamic loading is not handled currently.
init_tls(TLSDescriptor & tls_descriptor)39 void init_tls(TLSDescriptor &tls_descriptor) {
40   if (app.tls.size == 0) {
41     tls_descriptor.size = 0;
42     tls_descriptor.tp = 0;
43     return;
44   }
45 
46   // We will assume the alignment is always a power of two.
47   uintptr_t tlsSize = app.tls.size & -app.tls.align;
48   if (tlsSize != app.tls.size)
49     tlsSize += app.tls.align;
50 
51   // Per the x86_64 TLS ABI, the entry pointed to by the thread pointer is the
52   // address of the TLS block. So, we add more size to accomodate this address
53   // entry.
54   uintptr_t tlsSizeWithAddr = tlsSize + sizeof(uintptr_t);
55 
56   // We cannot call the mmap function here as the functions set errno on
57   // failure. Since errno is implemented via a thread local variable, we cannot
58   // use errno before TLS is setup.
59   long mmapRetVal = __llvm_libc::syscall(
60       mmapSyscallNumber, nullptr, tlsSizeWithAddr, PROT_READ | PROT_WRITE,
61       MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
62   // We cannot check the return value with MAP_FAILED as that is the return
63   // of the mmap function and not the mmap syscall.
64   if (mmapRetVal < 0 && static_cast<uintptr_t>(mmapRetVal) > -app.pageSize)
65     __llvm_libc::syscall(SYS_exit, 1);
66   uintptr_t *tlsAddr = reinterpret_cast<uintptr_t *>(mmapRetVal);
67 
68   // x86_64 TLS faces down from the thread pointer with the first entry
69   // pointing to the address of the first real TLS byte.
70   uintptr_t endPtr = reinterpret_cast<uintptr_t>(tlsAddr) + tlsSize;
71   *reinterpret_cast<uintptr_t *>(endPtr) = endPtr;
72 
73   __llvm_libc::inline_memcpy(reinterpret_cast<char *>(tlsAddr),
74                              reinterpret_cast<const char *>(app.tls.address),
75                              app.tls.init_size);
76 
77   tls_descriptor = {tlsSizeWithAddr, uintptr_t(tlsAddr), endPtr};
78   return;
79 }
80 
cleanup_tls(uintptr_t addr,uintptr_t size)81 void cleanup_tls(uintptr_t addr, uintptr_t size) {
82   if (size == 0)
83     return;
84   __llvm_libc::syscall(SYS_munmap, addr, size);
85 }
86 
87 // Sets the thread pointer to |val|. Returns true on success, false on failure.
set_thread_ptr(uintptr_t val)88 static bool set_thread_ptr(uintptr_t val) {
89   return __llvm_libc::syscall(SYS_arch_prctl, ARCH_SET_FS, val) == -1 ? false
90                                                                       : true;
91 }
92 
93 } // namespace __llvm_libc
94 
95 using __llvm_libc::app;
96 
97 // TODO: Would be nice to use the aux entry structure from elf.h when available.
98 struct AuxEntry {
99   uint64_t type;
100   uint64_t value;
101 };
102 
_start()103 extern "C" void _start() {
104   // This TU is compiled with -fno-omit-frame-pointer. Hence, the previous value
105   // of the base pointer is pushed on to the stack. So, we step over it (the
106   // "+ 1" below) to get to the args.
107   app.args = reinterpret_cast<__llvm_libc::Args *>(
108       reinterpret_cast<uintptr_t *>(__builtin_frame_address(0)) + 1);
109 
110   // The x86_64 ABI requires that the stack pointer is aligned to a 16-byte
111   // boundary. We align it here but we cannot use any local variables created
112   // before the following alignment. Best would be to not create any local
113   // variables before the alignment. Also, note that we are aligning the stack
114   // downwards as the x86_64 stack grows downwards. This ensures that we don't
115   // tread on argc, argv etc.
116   // NOTE: Compiler attributes for alignment do not help here as the stack
117   // pointer on entry to this _start function is controlled by the OS. In fact,
118   // compilers can generate code assuming the alignment as required by the ABI.
119   // If the stack pointers as setup by the OS are already aligned, then the
120   // following code is a NOP.
121   __asm__ __volatile__("andq $0xfffffffffffffff0, %%rsp\n\t" ::: "%rsp");
122   __asm__ __volatile__("andq $0xfffffffffffffff0, %%rbp\n\t" ::: "%rbp");
123 
124   auto tid = __llvm_libc::syscall(SYS_gettid);
125   if (tid <= 0)
126     __llvm_libc::syscall(SYS_exit, 1);
127   __llvm_libc::main_thread_attrib.tid = tid;
128 
129   // After the argv array, is a 8-byte long NULL value before the array of env
130   // values. The end of the env values is marked by another 8-byte long NULL
131   // value. We step over it (the "+ 1" below) to get to the env values.
132   uint64_t *env_ptr = app.args->argv + app.args->argc + 1;
133   uint64_t *env_end_marker = env_ptr;
134   app.envPtr = env_ptr;
135   while (*env_end_marker)
136     ++env_end_marker;
137 
138   // After the env array, is the aux-vector. The end of the aux-vector is
139   // denoted by an AT_NULL entry.
140   Elf64_Phdr *programHdrTable = nullptr;
141   uintptr_t programHdrCount;
142   for (AuxEntry *aux_entry = reinterpret_cast<AuxEntry *>(env_end_marker + 1);
143        aux_entry->type != AT_NULL; ++aux_entry) {
144     switch (aux_entry->type) {
145     case AT_PHDR:
146       programHdrTable = reinterpret_cast<Elf64_Phdr *>(aux_entry->value);
147       break;
148     case AT_PHNUM:
149       programHdrCount = aux_entry->value;
150       break;
151     case AT_PAGESZ:
152       app.pageSize = aux_entry->value;
153       break;
154     default:
155       break; // TODO: Read other useful entries from the aux vector.
156     }
157   }
158 
159   app.tls.size = 0;
160   for (uintptr_t i = 0; i < programHdrCount; ++i) {
161     Elf64_Phdr *phdr = programHdrTable + i;
162     if (phdr->p_type != PT_TLS)
163       continue;
164     // TODO: p_vaddr value has to be adjusted for static-pie executables.
165     app.tls.address = phdr->p_vaddr;
166     app.tls.size = phdr->p_memsz;
167     app.tls.init_size = phdr->p_filesz;
168     app.tls.align = phdr->p_align;
169   }
170 
171   __llvm_libc::TLSDescriptor tls;
172   __llvm_libc::init_tls(tls);
173   if (tls.size != 0 && !__llvm_libc::set_thread_ptr(tls.tp))
174     __llvm_libc::syscall(SYS_exit, 1);
175 
176   __llvm_libc::self.attrib = &__llvm_libc::main_thread_attrib;
177 
178   int retval = main(app.args->argc, reinterpret_cast<char **>(app.args->argv),
179                     reinterpret_cast<char **>(env_ptr));
180   __llvm_libc::cleanup_tls(tls.addr, tls.size);
181   __llvm_libc::syscall(SYS_exit, retval);
182 }
183