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