1 //===-- Implementation of crt for riscv64 ---------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "config/linux/app.h"
10 #include "src/__support/OSUtil/syscall.h"
11 #include "src/__support/threads/thread.h"
12 #include "src/stdlib/atexit.h"
13 #include "src/stdlib/exit.h"
14 #include "src/string/memory_utils/inline_memcpy.h"
16 #include <linux/auxvec.h>
17 #include <linux/elf.h>
20 #include <sys/syscall.h>
23 extern "C" int main(int, char **, char **);
25 namespace LIBC_NAMESPACE
{
28 static constexpr long MMAP_SYSCALL_NUMBER
= SYS_mmap2
;
30 static constexpr long MMAP_SYSCALL_NUMBER
= SYS_mmap
;
32 #error "mmap and mmap2 syscalls not available."
37 static ThreadAttributes main_thread_attrib
;
39 void init_tls(TLSDescriptor
&tls_descriptor
) {
40 if (app
.tls
.size
== 0) {
41 tls_descriptor
.size
= 0;
42 tls_descriptor
.tp
= 0;
46 // riscv64 follows the variant 1 TLS layout:
47 const uintptr_t size_of_pointers
= 2 * sizeof(uintptr_t);
48 uintptr_t padding
= 0;
49 const uintptr_t ALIGNMENT_MASK
= app
.tls
.align
- 1;
50 uintptr_t diff
= size_of_pointers
& ALIGNMENT_MASK
;
52 padding
+= (ALIGNMENT_MASK
- diff
) + 1;
54 uintptr_t alloc_size
= size_of_pointers
+ padding
+ app
.tls
.size
;
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 mmap_ret_val
= LIBC_NAMESPACE::syscall_impl
<long>(
60 MMAP_SYSCALL_NUMBER
, nullptr, alloc_size
, 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 (mmap_ret_val
< 0 && static_cast<uintptr_t>(mmap_ret_val
) > -app
.pageSize
)
65 LIBC_NAMESPACE::syscall_impl
<long>(SYS_exit
, 1);
66 uintptr_t thread_ptr
= uintptr_t(reinterpret_cast<uintptr_t *>(mmap_ret_val
));
67 uintptr_t tls_addr
= thread_ptr
+ size_of_pointers
+ padding
;
68 LIBC_NAMESPACE::inline_memcpy(reinterpret_cast<char *>(tls_addr
),
69 reinterpret_cast<const char *>(app
.tls
.address
),
71 tls_descriptor
.size
= alloc_size
;
72 tls_descriptor
.addr
= thread_ptr
;
73 tls_descriptor
.tp
= tls_addr
;
76 void cleanup_tls(uintptr_t addr
, uintptr_t size
) {
79 LIBC_NAMESPACE::syscall_impl
<long>(SYS_munmap
, addr
, size
);
82 static void set_thread_ptr(uintptr_t val
) {
83 LIBC_INLINE_ASM("mv tp, %0\n\t" : : "r"(val
));
86 using InitCallback
= void(int, char **, char **);
87 using FiniCallback
= void(void);
90 // These arrays are present in the .init_array and .fini_array sections.
91 // The symbols are inserted by linker when it sees references to them.
92 extern uintptr_t __preinit_array_start
[];
93 extern uintptr_t __preinit_array_end
[];
94 extern uintptr_t __init_array_start
[];
95 extern uintptr_t __init_array_end
[];
96 extern uintptr_t __fini_array_start
[];
97 extern uintptr_t __fini_array_end
[];
100 static void call_init_array_callbacks(int argc
, char **argv
, char **env
) {
101 size_t preinit_array_size
= __preinit_array_end
- __preinit_array_start
;
102 for (size_t i
= 0; i
< preinit_array_size
; ++i
)
103 reinterpret_cast<InitCallback
*>(__preinit_array_start
[i
])(argc
, argv
, env
);
104 size_t init_array_size
= __init_array_end
- __init_array_start
;
105 for (size_t i
= 0; i
< init_array_size
; ++i
)
106 reinterpret_cast<InitCallback
*>(__init_array_start
[i
])(argc
, argv
, env
);
109 static void call_fini_array_callbacks() {
110 size_t fini_array_size
= __fini_array_end
- __fini_array_start
;
111 for (size_t i
= fini_array_size
; i
> 0; --i
)
112 reinterpret_cast<FiniCallback
*>(__fini_array_start
[i
- 1])();
115 } // namespace LIBC_NAMESPACE
117 using LIBC_NAMESPACE::app
;
119 // TODO: Would be nice to use the aux entry structure from elf.h when available.
121 LIBC_NAMESPACE::AuxEntryType type
;
122 LIBC_NAMESPACE::AuxEntryType value
;
125 #if defined(LIBC_TARGET_ARCH_IS_X86_64) || \
126 defined(LIBC_TARGET_ARCH_IS_AARCH64) || \
127 defined(LIBC_TARGET_ARCH_IS_RISCV64)
128 typedef Elf64_Phdr PgrHdrTableType
;
129 #elif defined(LIBC_TARGET_ARCH_IS_RISCV32)
130 typedef Elf32_Phdr PgrHdrTableType
;
132 #error "Program header table type is not defined for the target platform."
135 __attribute__((noinline
)) static void do_start() {
136 LIBC_INLINE_ASM(".option push\n\t"
137 ".option norelax\n\t"
138 "lla gp, __global_pointer$\n\t"
140 auto tid
= LIBC_NAMESPACE::syscall_impl
<long>(SYS_gettid
);
142 LIBC_NAMESPACE::syscall_impl
<long>(SYS_exit
, 1);
143 LIBC_NAMESPACE::main_thread_attrib
.tid
= static_cast<int>(tid
);
145 // After the argv array, is a 8-byte long NULL value before the array of env
146 // values. The end of the env values is marked by another 8-byte long NULL
147 // value. We step over it (the "+ 1" below) to get to the env values.
148 LIBC_NAMESPACE::ArgVEntryType
*env_ptr
= app
.args
->argv
+ app
.args
->argc
+ 1;
149 LIBC_NAMESPACE::ArgVEntryType
*env_end_marker
= env_ptr
;
150 app
.envPtr
= env_ptr
;
151 while (*env_end_marker
)
154 // Initialize the POSIX global declared in unistd.h
155 environ
= reinterpret_cast<char **>(env_ptr
);
157 // After the env array, is the aux-vector. The end of the aux-vector is
158 // denoted by an AT_NULL entry.
159 PgrHdrTableType
*programHdrTable
= nullptr;
160 uintptr_t programHdrCount
;
161 for (AuxEntry
*aux_entry
= reinterpret_cast<AuxEntry
*>(env_end_marker
+ 1);
162 aux_entry
->type
!= AT_NULL
; ++aux_entry
) {
163 switch (aux_entry
->type
) {
165 programHdrTable
= reinterpret_cast<PgrHdrTableType
*>(aux_entry
->value
);
168 programHdrCount
= aux_entry
->value
;
171 app
.pageSize
= aux_entry
->value
;
174 break; // TODO: Read other useful entries from the aux vector.
179 for (uintptr_t i
= 0; i
< programHdrCount
; ++i
) {
180 PgrHdrTableType
*phdr
= programHdrTable
+ i
;
181 if (phdr
->p_type
!= PT_TLS
)
183 // TODO: p_vaddr value has to be adjusted for static-pie executables.
184 app
.tls
.address
= phdr
->p_vaddr
;
185 app
.tls
.size
= phdr
->p_memsz
;
186 app
.tls
.init_size
= phdr
->p_filesz
;
187 app
.tls
.align
= phdr
->p_align
;
190 LIBC_NAMESPACE::TLSDescriptor tls
;
191 LIBC_NAMESPACE::init_tls(tls
);
193 LIBC_NAMESPACE::set_thread_ptr(tls
.tp
);
195 LIBC_NAMESPACE::self
.attrib
= &LIBC_NAMESPACE::main_thread_attrib
;
196 LIBC_NAMESPACE::main_thread_attrib
.atexit_callback_mgr
=
197 LIBC_NAMESPACE::internal::get_thread_atexit_callback_mgr();
199 // We want the fini array callbacks to be run after other atexit
200 // callbacks are run. So, we register them before running the init
201 // array callbacks as they can potentially register their own atexit
203 LIBC_NAMESPACE::atexit(&LIBC_NAMESPACE::call_fini_array_callbacks
);
205 LIBC_NAMESPACE::call_init_array_callbacks(
206 static_cast<int>(app
.args
->argc
),
207 reinterpret_cast<char **>(app
.args
->argv
),
208 reinterpret_cast<char **>(env_ptr
));
210 int retval
= main(static_cast<int>(app
.args
->argc
),
211 reinterpret_cast<char **>(app
.args
->argv
),
212 reinterpret_cast<char **>(env_ptr
));
214 // TODO: TLS cleanup should be done after all other atexit callbacks
215 // are run. So, register a cleanup callback for it with atexit before
217 LIBC_NAMESPACE::cleanup_tls(tls
.addr
, tls
.size
);
218 LIBC_NAMESPACE::exit(retval
);
221 extern "C" void _start() {
222 // Fetch the args using the frame pointer.
223 app
.args
= reinterpret_cast<LIBC_NAMESPACE::Args
*>(
224 reinterpret_cast<uintptr_t *>(__builtin_frame_address(0)));