1/*- 2 * Copyright (c) 2014 The FreeBSD Foundation 3 * 4 * This software was developed by Andrew Turner under 5 * sponsorship from the FreeBSD Foundation. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <machine/asm.h> 30#include <sys/elf_common.h> 31 32ENTRY(.rtld_start) 33 .cfi_undefined x30 34 mov x19, x0 /* Put ps_strings in a callee-saved register */ 35 36 sub sp, sp, #16 /* Make room for obj_main & exit proc */ 37 .cfi_adjust_cfa_offset 16 38 39 mov x1, sp /* exit_proc */ 40 add x2, x1, #8 /* obj_main */ 41 bl _rtld /* Call the loader */ 42 mov x8, x0 /* Backup the entry point */ 43 ldp x2, x1, [sp], #16 /* Load cleanup, obj_main */ 44 .cfi_adjust_cfa_offset 0 45 46 mov x0, x19 /* Restore ps_strings */ 47 br x8 /* Jump to the entry point */ 48END(.rtld_start) 49 50/* 51 * sp + 0 = &GOT[x + 3] 52 * sp + 8 = RA 53 * x16 = &GOT[2] 54 * x17 = &_rtld_bind_start 55 */ 56ENTRY(_rtld_bind_start) 57 mov x17, sp 58 59 /* Save frame pointer and SP */ 60 stp x29, x30, [sp, #-16]! 61 mov x29, sp 62 .cfi_def_cfa x29, 16 63 .cfi_offset x30, -8 64 .cfi_offset x29, -16 65 66 /* Save the arguments */ 67 stp x0, x1, [sp, #-16]! 68 stp x2, x3, [sp, #-16]! 69 stp x4, x5, [sp, #-16]! 70 stp x6, x7, [sp, #-16]! 71 stp x8, xzr, [sp, #-16]! 72 73 /* Save any floating-point arguments */ 74 stp q0, q1, [sp, #-32]! 75 stp q2, q3, [sp, #-32]! 76 stp q4, q5, [sp, #-32]! 77 stp q6, q7, [sp, #-32]! 78 79 /* Calculate reloff */ 80 ldr x2, [x17, #0] /* Get the address of the entry */ 81 sub x1, x2, x16 /* Find its offset */ 82 sub x1, x1, #8 /* Adjust for x16 not being at offset 0 */ 83 /* Each rela item has 3 entriesso we need reloff = 3 * index */ 84 lsl x3, x1, #1 /* x3 = 2 * offset */ 85 add x1, x1, x3 /* x1 = x3 + offset = 3 * offset */ 86 87 /* Load obj */ 88 ldr x0, [x16, #-8] 89 90 /* Call into rtld */ 91 bl _rtld_bind 92 93 /* Backup the address to branch to */ 94 mov x16, x0 95 96 /* restore the arguments */ 97 ldp q6, q7, [sp], #32 98 ldp q4, q5, [sp], #32 99 ldp q2, q3, [sp], #32 100 ldp q0, q1, [sp], #32 101 ldp x8, xzr, [sp], #16 102 ldp x6, x7, [sp], #16 103 ldp x4, x5, [sp], #16 104 ldp x2, x3, [sp], #16 105 ldp x0, x1, [sp], #16 106 107 /* Restore frame pointer */ 108 ldp x29, xzr, [sp], #16 109 110 /* Restore link register saved by the plt code */ 111 ldp xzr, x30, [sp], #16 112 113 /* Call into the correct function */ 114 br x16 115END(_rtld_bind_start) 116 117/* 118 * struct rel_tlsdesc { 119 * uint64_t resolver_fnc; 120 * uint64_t resolver_arg; 121 * 122 * 123 * uint64_t _rtld_tlsdesc_static(struct rel_tlsdesc *); 124 * 125 * Resolver function for TLS symbols resolved at load time 126 */ 127ENTRY(_rtld_tlsdesc_static) 128 ldr x0, [x0, #8] 129 ret 130END(_rtld_tlsdesc_static) 131 132/* 133 * uint64_t _rtld_tlsdesc_undef(void); 134 * 135 * Resolver function for weak and undefined TLS symbols 136 */ 137ENTRY(_rtld_tlsdesc_undef) 138 str x1, [sp, #-16]! 139 .cfi_adjust_cfa_offset 16 140 141 mrs x1, tpidr_el0 142 ldr x0, [x0, #8] 143 sub x0, x0, x1 144 145 ldr x1, [sp], #16 146 .cfi_adjust_cfa_offset -16 147 ret 148END(_rtld_tlsdesc_undef) 149 150/* 151 * uint64_t _rtld_tlsdesc_dynamic(struct rel_tlsdesc *); 152 * 153 * Resolver function for TLS symbols from dlopen() 154 */ 155ENTRY(_rtld_tlsdesc_dynamic) 156 /* Save registers used in fast path */ 157 stp x1, x2, [sp, #(-2 * 16)]! 158 stp x3, x4, [sp, #(1 * 16)] 159 .cfi_adjust_cfa_offset 2 * 16 160 .cfi_rel_offset x1, 0 161 .cfi_rel_offset x2, 8 162 .cfi_rel_offset x3, 16 163 .cfi_rel_offset x4, 24 164 165 /* Test fastpath - inlined version of tls_get_addr_common(). */ 166 ldr x1, [x0, #8] /* tlsdesc ptr */ 167 mrs x4, tpidr_el0 168 ldr x0, [x4] /* DTV pointer */ 169 ldr x2, [x0] /* dtv[0] (generation count) */ 170 ldr x3, [x1] /* tlsdec->dtv_gen */ 171 cmp x2, x3 172 b.ne 1f /* dtv[0] != tlsdec->dtv_gen */ 173 174 ldr w2, [x1, #8] /* tlsdec->tls_index */ 175 add w2, w2, #1 176 ldr x3, [x0, w2, sxtw #3] /* dtv[tlsdesc->tls_index + 1] */ 177 cbz x3, 1f 178 179 /* Return (dtv[tlsdesc->tls_index + 1] + tlsdesc->tls_offs - tp) */ 180 ldr x2, [x1, #16] /* tlsdec->tls_offs */ 181 add x2, x2, x3 182 sub x0, x2, x4 183 /* Restore registers and return */ 184 ldp x3, x4, [sp, #(1 * 16)] 185 ldp x1, x2, [sp], #(2 * 16) 186 .cfi_adjust_cfa_offset -2 * 16 187 ret 188 189 /* 190 * Slow path 191 * return( 192 * tls_get_addr_common(tp, tlsdesc->tls_index, tlsdesc->tls_offs)); 193 * 194 */ 1951: 196 /* Save all integer registers */ 197 stp x29, x30, [sp, #-(8 * 16)]! 198 .cfi_adjust_cfa_offset 8 * 16 199 .cfi_rel_offset x29, 0 200 .cfi_rel_offset x30, 8 201 202 mov x29, sp 203 stp x5, x6, [sp, #(1 * 16)] 204 stp x7, x8, [sp, #(2 * 16)] 205 stp x9, x10, [sp, #(3 * 16)] 206 stp x11, x12, [sp, #(4 * 16)] 207 stp x13, x14, [sp, #(5 * 16)] 208 stp x15, x16, [sp, #(6 * 16)] 209 stp x17, x18, [sp, #(7 * 16)] 210 .cfi_rel_offset x5, 16 211 .cfi_rel_offset x6, 24 212 .cfi_rel_offset x7, 32 213 .cfi_rel_offset x8, 40 214 .cfi_rel_offset x9, 48 215 .cfi_rel_offset x10, 56 216 .cfi_rel_offset x11, 64 217 .cfi_rel_offset x12, 72 218 .cfi_rel_offset x13, 80 219 .cfi_rel_offset x14, 88 220 .cfi_rel_offset x15, 96 221 .cfi_rel_offset x16, 104 222 .cfi_rel_offset x17, 112 223 .cfi_rel_offset x18, 120 224 225 /* Find the tls offset */ 226 mov x0, x4 /* tp */ 227 mov x3, x1 /* tlsdesc ptr */ 228 ldr w1, [x3, #8] /* tlsdec->tls_index */ 229 ldr x2, [x3, #16] /* tlsdec->tls_offs */ 230 bl tls_get_addr_common 231 mrs x1, tpidr_el0 232 sub x0, x0, x1 233 234 /* Restore slow patch registers */ 235 ldp x17, x18, [sp, #(7 * 16)] 236 ldp x15, x16, [sp, #(6 * 16)] 237 ldp x13, x14, [sp, #(5 * 16)] 238 ldp x11, x12, [sp, #(4 * 16)] 239 ldp x9, x10, [sp, #(3 * 16)] 240 ldp x7, x8, [sp, #(2 * 16)] 241 ldp x5, x6, [sp, #(1 * 16)] 242 ldp x29, x30, [sp], #(8 * 16) 243 .cfi_adjust_cfa_offset -8 * 16 244 .cfi_restore x29 245 .cfi_restore x30 246 247 /* Restore fast path registers and return */ 248 ldp x3, x4, [sp, #16] 249 ldp x1, x2, [sp], #(2 * 16) 250 .cfi_adjust_cfa_offset -2 * 16 251 ret 252END(_rtld_tlsdesc_dynamic) 253 254GNU_PROPERTY_AARCH64_FEATURE_1_NOTE(GNU_PROPERTY_AARCH64_FEATURE_1_VAL) 255