1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ 2 /* 3 * ARM specific definitions for NOLIBC 4 * Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu> 5 */ 6 7 #ifndef _NOLIBC_ARCH_ARM_H 8 #define _NOLIBC_ARCH_ARM_H 9 10 #include "compiler.h" 11 12 /* The struct returned by the stat() syscall, 32-bit only, the syscall returns 13 * exactly 56 bytes (stops before the unused array). In big endian, the format 14 * differs as devices are returned as short only. 15 */ 16 struct sys_stat_struct { 17 #if defined(__ARMEB__) 18 unsigned short st_dev; 19 unsigned short __pad1; 20 #else 21 unsigned long st_dev; 22 #endif 23 unsigned long st_ino; 24 unsigned short st_mode; 25 unsigned short st_nlink; 26 unsigned short st_uid; 27 unsigned short st_gid; 28 29 #if defined(__ARMEB__) 30 unsigned short st_rdev; 31 unsigned short __pad2; 32 #else 33 unsigned long st_rdev; 34 #endif 35 unsigned long st_size; 36 unsigned long st_blksize; 37 unsigned long st_blocks; 38 39 unsigned long st_atime; 40 unsigned long st_atime_nsec; 41 unsigned long st_mtime; 42 unsigned long st_mtime_nsec; 43 44 unsigned long st_ctime; 45 unsigned long st_ctime_nsec; 46 unsigned long __unused[2]; 47 }; 48 49 /* Syscalls for ARM in ARM or Thumb modes : 50 * - registers are 32-bit 51 * - stack is 8-byte aligned 52 * ( http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka4127.html) 53 * - syscall number is passed in r7 54 * - arguments are in r0, r1, r2, r3, r4, r5 55 * - the system call is performed by calling svc #0 56 * - syscall return comes in r0. 57 * - only lr is clobbered. 58 * - the arguments are cast to long and assigned into the target registers 59 * which are then simply passed as registers to the asm code, so that we 60 * don't have to experience issues with register constraints. 61 * - the syscall number is always specified last in order to allow to force 62 * some registers before (gcc refuses a %-register at the last position). 63 * - in thumb mode without -fomit-frame-pointer, r7 is also used to store the 64 * frame pointer, and we cannot directly assign it as a register variable, 65 * nor can we clobber it. Instead we assign the r6 register and swap it 66 * with r7 before calling svc, and r6 is marked as clobbered. 67 * We're just using any regular register which we assign to r7 after saving 68 * it. 69 * 70 * Also, ARM supports the old_select syscall if newselect is not available 71 */ 72 #define __ARCH_WANT_SYS_OLD_SELECT 73 74 #if (defined(__THUMBEB__) || defined(__THUMBEL__)) && \ 75 !defined(NOLIBC_OMIT_FRAME_POINTER) 76 /* swap r6,r7 needed in Thumb mode since we can't use nor clobber r7 */ 77 #define _NOLIBC_SYSCALL_REG "r6" 78 #define _NOLIBC_THUMB_SET_R7 "eor r7, r6\neor r6, r7\neor r7, r6\n" 79 #define _NOLIBC_THUMB_RESTORE_R7 "mov r7, r6\n" 80 81 #else /* we're in ARM mode */ 82 /* in Arm mode we can directly use r7 */ 83 #define _NOLIBC_SYSCALL_REG "r7" 84 #define _NOLIBC_THUMB_SET_R7 "" 85 #define _NOLIBC_THUMB_RESTORE_R7 "" 86 87 #endif /* end THUMB */ 88 89 #define my_syscall0(num) \ 90 ({ \ 91 register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \ 92 register long _arg1 __asm__ ("r0"); \ 93 \ 94 __asm__ volatile ( \ 95 _NOLIBC_THUMB_SET_R7 \ 96 "svc #0\n" \ 97 _NOLIBC_THUMB_RESTORE_R7 \ 98 : "=r"(_arg1), "=r"(_num) \ 99 : "r"(_arg1), \ 100 "r"(_num) \ 101 : "memory", "cc", "lr" \ 102 ); \ 103 _arg1; \ 104 }) 105 106 #define my_syscall1(num, arg1) \ 107 ({ \ 108 register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \ 109 register long _arg1 __asm__ ("r0") = (long)(arg1); \ 110 \ 111 __asm__ volatile ( \ 112 _NOLIBC_THUMB_SET_R7 \ 113 "svc #0\n" \ 114 _NOLIBC_THUMB_RESTORE_R7 \ 115 : "=r"(_arg1), "=r" (_num) \ 116 : "r"(_arg1), \ 117 "r"(_num) \ 118 : "memory", "cc", "lr" \ 119 ); \ 120 _arg1; \ 121 }) 122 123 #define my_syscall2(num, arg1, arg2) \ 124 ({ \ 125 register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \ 126 register long _arg1 __asm__ ("r0") = (long)(arg1); \ 127 register long _arg2 __asm__ ("r1") = (long)(arg2); \ 128 \ 129 __asm__ volatile ( \ 130 _NOLIBC_THUMB_SET_R7 \ 131 "svc #0\n" \ 132 _NOLIBC_THUMB_RESTORE_R7 \ 133 : "=r"(_arg1), "=r" (_num) \ 134 : "r"(_arg1), "r"(_arg2), \ 135 "r"(_num) \ 136 : "memory", "cc", "lr" \ 137 ); \ 138 _arg1; \ 139 }) 140 141 #define my_syscall3(num, arg1, arg2, arg3) \ 142 ({ \ 143 register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \ 144 register long _arg1 __asm__ ("r0") = (long)(arg1); \ 145 register long _arg2 __asm__ ("r1") = (long)(arg2); \ 146 register long _arg3 __asm__ ("r2") = (long)(arg3); \ 147 \ 148 __asm__ volatile ( \ 149 _NOLIBC_THUMB_SET_R7 \ 150 "svc #0\n" \ 151 _NOLIBC_THUMB_RESTORE_R7 \ 152 : "=r"(_arg1), "=r" (_num) \ 153 : "r"(_arg1), "r"(_arg2), "r"(_arg3), \ 154 "r"(_num) \ 155 : "memory", "cc", "lr" \ 156 ); \ 157 _arg1; \ 158 }) 159 160 #define my_syscall4(num, arg1, arg2, arg3, arg4) \ 161 ({ \ 162 register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \ 163 register long _arg1 __asm__ ("r0") = (long)(arg1); \ 164 register long _arg2 __asm__ ("r1") = (long)(arg2); \ 165 register long _arg3 __asm__ ("r2") = (long)(arg3); \ 166 register long _arg4 __asm__ ("r3") = (long)(arg4); \ 167 \ 168 __asm__ volatile ( \ 169 _NOLIBC_THUMB_SET_R7 \ 170 "svc #0\n" \ 171 _NOLIBC_THUMB_RESTORE_R7 \ 172 : "=r"(_arg1), "=r" (_num) \ 173 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ 174 "r"(_num) \ 175 : "memory", "cc", "lr" \ 176 ); \ 177 _arg1; \ 178 }) 179 180 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ 181 ({ \ 182 register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \ 183 register long _arg1 __asm__ ("r0") = (long)(arg1); \ 184 register long _arg2 __asm__ ("r1") = (long)(arg2); \ 185 register long _arg3 __asm__ ("r2") = (long)(arg3); \ 186 register long _arg4 __asm__ ("r3") = (long)(arg4); \ 187 register long _arg5 __asm__ ("r4") = (long)(arg5); \ 188 \ 189 __asm__ volatile ( \ 190 _NOLIBC_THUMB_SET_R7 \ 191 "svc #0\n" \ 192 _NOLIBC_THUMB_RESTORE_R7 \ 193 : "=r"(_arg1), "=r" (_num) \ 194 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ 195 "r"(_num) \ 196 : "memory", "cc", "lr" \ 197 ); \ 198 _arg1; \ 199 }) 200 201 #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ 202 ({ \ 203 register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \ 204 register long _arg1 __asm__ ("r0") = (long)(arg1); \ 205 register long _arg2 __asm__ ("r1") = (long)(arg2); \ 206 register long _arg3 __asm__ ("r2") = (long)(arg3); \ 207 register long _arg4 __asm__ ("r3") = (long)(arg4); \ 208 register long _arg5 __asm__ ("r4") = (long)(arg5); \ 209 register long _arg6 __asm__ ("r5") = (long)(arg6); \ 210 \ 211 __asm__ volatile ( \ 212 _NOLIBC_THUMB_SET_R7 \ 213 "svc #0\n" \ 214 _NOLIBC_THUMB_RESTORE_R7 \ 215 : "=r"(_arg1), "=r" (_num) \ 216 : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ 217 "r"(_arg6), "r"(_num) \ 218 : "memory", "cc", "lr" \ 219 ); \ 220 _arg1; \ 221 }) 222 223 224 char **environ __attribute__((weak)); 225 const unsigned long *_auxv __attribute__((weak)); 226 227 /* startup code */ 228 void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __no_stack_protector _start(void) 229 { 230 __asm__ volatile ( 231 #ifdef _NOLIBC_STACKPROTECTOR 232 "bl __stack_chk_init\n" /* initialize stack protector */ 233 #endif 234 "pop {%r0}\n" /* argc was in the stack */ 235 "mov %r1, %sp\n" /* argv = sp */ 236 237 "add %r2, %r0, $1\n" /* envp = (argc + 1) ... */ 238 "lsl %r2, %r2, $2\n" /* * 4 ... */ 239 "add %r2, %r2, %r1\n" /* + argv */ 240 "ldr %r3, 1f\n" /* r3 = &environ (see below) */ 241 "str %r2, [r3]\n" /* store envp into environ */ 242 243 "mov r4, r2\n" /* search for auxv (follows NULL after last env) */ 244 "0:\n" 245 "mov r5, r4\n" /* r5 = r4 */ 246 "add r4, r4, #4\n" /* r4 += 4 */ 247 "ldr r5,[r5]\n" /* r5 = *r5 = *(r4-4) */ 248 "cmp r5, #0\n" /* and stop at NULL after last env */ 249 "bne 0b\n" 250 "ldr %r3, 2f\n" /* r3 = &_auxv (low bits) */ 251 "str r4, [r3]\n" /* store r4 into _auxv */ 252 253 "mov %r3, $8\n" /* AAPCS : sp must be 8-byte aligned in the */ 254 "neg %r3, %r3\n" /* callee, and bl doesn't push (lr=pc) */ 255 "and %r3, %r3, %r1\n" /* so we do sp = r1(=sp) & r3(=-8); */ 256 "mov %sp, %r3\n" 257 258 "bl main\n" /* main() returns the status code, we'll exit with it. */ 259 "movs r7, $1\n" /* NR_exit == 1 */ 260 "svc $0x00\n" 261 ".align 2\n" /* below are the pointers to a few variables */ 262 "1:\n" 263 ".word environ\n" 264 "2:\n" 265 ".word _auxv\n" 266 ); 267 __builtin_unreachable(); 268 } 269 270 #endif /* _NOLIBC_ARCH_ARM_H */ 271