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