| 1 | /* Copyright (C) 1996-2026 Free Software Foundation, Inc. |
| 2 | This file is part of the GNU C Library. |
| 3 | |
| 4 | The GNU C Library is free software; you can redistribute it and/or |
| 5 | modify it under the terms of the GNU Lesser General Public |
| 6 | License as published by the Free Software Foundation; either |
| 7 | version 2.1 of the License, or (at your option) any later version. |
| 8 | |
| 9 | The GNU C Library is distributed in the hope that it will be useful, |
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | Lesser General Public License for more details. |
| 13 | |
| 14 | You should have received a copy of the GNU Lesser General Public |
| 15 | License along with the GNU C Library. If not, see |
| 16 | <https://www.gnu.org/licenses/>. */ |
| 17 | |
| 18 | #include <sysdeps/unix/sysv/linux/sysdep.h> |
| 19 | #include <tls.h> |
| 20 | |
| 21 | /* Defines RTLD_PRIVATE_ERRNO. */ |
| 22 | #include <dl-sysdep.h> |
| 23 | |
| 24 | /* For Linux we can use the system call table in the header file |
| 25 | 	/usr/include/asm/unistd.h |
| 26 | of the kernel. But these symbols do not follow the SYS_* syntax |
| 27 | so we have to redefine the `SYS_ify' macro here. */ |
| 28 | #undef SYS_ify |
| 29 | #define SYS_ify(syscall_name)	__NR_##syscall_name |
| 30 | |
| 31 | #ifdef __ASSEMBLER__ |
| 32 | |
| 33 | /* Linux uses a negative return value to indicate syscall errors, unlike |
| 34 | most Unices, which use the condition codes' carry flag. |
| 35 | |
| 36 | Since version 2.1 the return value of a system call might be negative |
| 37 | even if the call succeeded. E.g., the `lseek' system call might return |
| 38 | a large offset. Therefore we must not anymore test for < 0, but test |
| 39 | for a real error by making sure the value in %d0 is a real error |
| 40 | number. Linus said he will make sure the no syscall returns a value |
| 41 | in -1 .. -4095 as a valid result so we can safely test with -4095. */ |
| 42 | |
| 43 | /* We don't want the label for the error handler to be visible in the symbol |
| 44 | table when we define it here. */ |
| 45 | #undef SYSCALL_ERROR_LABEL |
| 46 | #ifdef PIC |
| 47 | #define SYSCALL_ERROR_LABEL .Lsyscall_error |
| 48 | #else |
| 49 | #define SYSCALL_ERROR_LABEL __syscall_error |
| 50 | #endif |
| 51 | |
| 52 | #undef PSEUDO |
| 53 | #define	PSEUDO(name, syscall_name, args)				 \ |
| 54 | .text;								 \ |
| 55 | ENTRY (name)								 \ |
| 56 | DO_CALL (syscall_name, args);					 \ |
| 57 | cmp.l &-4095, %d0;							 \ |
| 58 | jcc SYSCALL_ERROR_LABEL |
| 59 | |
| 60 | #undef PSEUDO_END |
| 61 | #define PSEUDO_END(name)						 \ |
| 62 | SYSCALL_ERROR_HANDLER;						 \ |
| 63 | END (name) |
| 64 | |
| 65 | #undef PSEUDO_NOERRNO |
| 66 | #define	PSEUDO_NOERRNO(name, syscall_name, args)			 \ |
| 67 | .text;								 \ |
| 68 | ENTRY (name)								 \ |
| 69 | DO_CALL (syscall_name, args) |
| 70 | |
| 71 | #undef PSEUDO_END_NOERRNO |
| 72 | #define PSEUDO_END_NOERRNO(name)					 \ |
| 73 | END (name) |
| 74 | |
| 75 | #define ret_NOERRNO rts |
| 76 | |
| 77 | /* The function has to return the error code. */ |
| 78 | #undef	PSEUDO_ERRVAL |
| 79 | #define	PSEUDO_ERRVAL(name, syscall_name, args) \ |
| 80 | .text;								 \ |
| 81 | ENTRY (name)								 \ |
| 82 | DO_CALL (syscall_name, args);					 \ |
| 83 | negl %d0 |
| 84 | |
| 85 | #undef	PSEUDO_END_ERRVAL |
| 86 | #define	PSEUDO_END_ERRVAL(name) \ |
| 87 | END (name) |
| 88 | |
| 89 | #define ret_ERRVAL rts |
| 90 | |
| 91 | #ifdef PIC |
| 92 | # if RTLD_PRIVATE_ERRNO |
| 93 | # define SYSCALL_ERROR_HANDLER						 \ |
| 94 | SYSCALL_ERROR_LABEL:							 \ |
| 95 | PCREL_OP (lea, rtld_errno, %a0, %a0);				 \ |
| 96 | neg.l %d0;								 \ |
| 97 | move.l %d0, (%a0);							 \ |
| 98 | move.l &-1, %d0;							 \ |
| 99 | /* Copy return value to %a0 for syscalls that are declared to return \ |
| 100 | a pointer (e.g., mmap). */					 \ |
| 101 | move.l %d0, %a0;							 \ |
| 102 | rts; |
| 103 | # elif defined _LIBC_REENTRANT |
| 104 | # if IS_IN (libc) |
| 105 | # define SYSCALL_ERROR_ERRNO __libc_errno |
| 106 | # else |
| 107 | # define SYSCALL_ERROR_ERRNO errno |
| 108 | # endif |
| 109 | # define SYSCALL_ERROR_HANDLER						 \ |
| 110 | SYSCALL_ERROR_LABEL:							 \ |
| 111 | neg.l %d0;								 \ |
| 112 | move.l %d0, -(%sp);							 \ |
| 113 | cfi_adjust_cfa_offset (4);						 \ |
| 114 | jbsr __m68k_read_tp@PLTPC;						 \ |
| 115 | SYSCALL_ERROR_LOAD_GOT (%a1);					 \ |
| 116 | add.l (SYSCALL_ERROR_ERRNO@TLSIE, %a1), %a0;			 \ |
| 117 | move.l (%sp)+, (%a0);						 \ |
| 118 | cfi_adjust_cfa_offset (-4);						 \ |
| 119 | move.l &-1, %d0;							 \ |
| 120 | /* Copy return value to %a0 for syscalls that are declared to return \ |
| 121 | a pointer (e.g., mmap). */					 \ |
| 122 | move.l %d0, %a0;							 \ |
| 123 | rts; |
| 124 | # else /* !_LIBC_REENTRANT */ |
| 125 | /* Store (- %d0) into errno through the GOT. */ |
| 126 | # define SYSCALL_ERROR_HANDLER						 \ |
| 127 | SYSCALL_ERROR_LABEL:							 \ |
| 128 | move.l (errno@GOTPC, %pc), %a0;					 \ |
| 129 | neg.l %d0;								 \ |
| 130 | move.l %d0, (%a0);							 \ |
| 131 | move.l &-1, %d0;							 \ |
| 132 | /* Copy return value to %a0 for syscalls that are declared to return \ |
| 133 | a pointer (e.g., mmap). */					 \ |
| 134 | move.l %d0, %a0;							 \ |
| 135 | rts; |
| 136 | # endif /* _LIBC_REENTRANT */ |
| 137 | #else |
| 138 | # define SYSCALL_ERROR_HANDLER	/* Nothing here; code in sysdep.S is used. */ |
| 139 | #endif /* PIC */ |
| 140 | |
| 141 | /* Linux takes system call arguments in registers: |
| 142 | |
| 143 | 	syscall number	%d0	 call-clobbered |
| 144 | 	arg 1		%d1	 call-clobbered |
| 145 | 	arg 2		%d2	 call-saved |
| 146 | 	arg 3		%d3	 call-saved |
| 147 | 	arg 4		%d4	 call-saved |
| 148 | 	arg 5		%d5	 call-saved |
| 149 | 	arg 6		%a0	 call-clobbered |
| 150 | |
| 151 | The stack layout upon entering the function is: |
| 152 | |
| 153 | 	24(%sp)		Arg# 6 |
| 154 | 	20(%sp)		Arg# 5 |
| 155 | 	16(%sp)		Arg# 4 |
| 156 | 	12(%sp)		Arg# 3 |
| 157 | 	 8(%sp)		Arg# 2 |
| 158 | 	 4(%sp)		Arg# 1 |
| 159 | 	 (%sp)		Return address |
| 160 | |
| 161 | (Of course a function with say 3 arguments does not have entries for |
| 162 | arguments 4 and 5.) |
| 163 | |
| 164 | Separate move's are faster than movem, but need more space. Since |
| 165 | speed is more important, we don't use movem. Since %a0 and %a1 are |
| 166 | scratch registers, we can use them for saving as well. */ |
| 167 | |
| 168 | #define DO_CALL(syscall_name, args)			 		 \ |
| 169 | move.l &SYS_ify(syscall_name), %d0;					 \ |
| 170 | DOARGS_##args							 \ |
| 171 | trap &0;								 \ |
| 172 | UNDOARGS_##args |
| 173 | |
| 174 | #define	DOARGS_0	/* No arguments to frob. */ |
| 175 | #define	UNDOARGS_0	/* No arguments to unfrob. */ |
| 176 | #define	_DOARGS_0(n)	/* No arguments to frob. */ |
| 177 | |
| 178 | #define	DOARGS_1	_DOARGS_1 (4) |
| 179 | #define	_DOARGS_1(n)	move.l n(%sp), %d1; _DOARGS_0 (n) |
| 180 | #define	UNDOARGS_1	UNDOARGS_0 |
| 181 | |
| 182 | #define	DOARGS_2	_DOARGS_2 (8) |
| 183 | #define	_DOARGS_2(n)	move.l %d2, %a0; cfi_register (%d2, %a0);	 \ |
| 184 | 			move.l n(%sp), %d2; _DOARGS_1 (n-4) |
| 185 | #define	UNDOARGS_2	UNDOARGS_1; move.l %a0, %d2; cfi_restore (%d2) |
| 186 | |
| 187 | #define DOARGS_3	_DOARGS_3 (12) |
| 188 | #define _DOARGS_3(n)	move.l %d3, %a1; cfi_register (%d3, %a1);	 \ |
| 189 | 			move.l n(%sp), %d3; _DOARGS_2 (n-4) |
| 190 | #define UNDOARGS_3	UNDOARGS_2; move.l %a1, %d3; cfi_restore (%d3) |
| 191 | |
| 192 | #define DOARGS_4	_DOARGS_4 (16) |
| 193 | #define _DOARGS_4(n)	move.l %d4, -(%sp);				 \ |
| 194 | 			cfi_adjust_cfa_offset (4); cfi_rel_offset (%d4, 0); \ |
| 195 | 			move.l n+4(%sp), %d4; _DOARGS_3 (n) |
| 196 | #define UNDOARGS_4	UNDOARGS_3; move.l (%sp)+, %d4;			 \ |
| 197 | 			cfi_adjust_cfa_offset (-4); cfi_restore (%d4) |
| 198 | |
| 199 | #define DOARGS_5	_DOARGS_5 (20) |
| 200 | #define _DOARGS_5(n)	move.l %d5, -(%sp); 				 \ |
| 201 | 			cfi_adjust_cfa_offset (4); cfi_rel_offset (%d5, 0); \ |
| 202 | 			move.l n+4(%sp), %d5; _DOARGS_4 (n) |
| 203 | #define UNDOARGS_5	UNDOARGS_4; move.l (%sp)+, %d5;			 \ |
| 204 | 			cfi_adjust_cfa_offset (-4); cfi_restore (%d5) |
| 205 | |
| 206 | #define DOARGS_6	_DOARGS_6 (24) |
| 207 | #define _DOARGS_6(n)	_DOARGS_5 (n-4); move.l %a0, -(%sp);		 \ |
| 208 | 			cfi_adjust_cfa_offset (4);			 \ |
| 209 | 			move.l n+12(%sp), %a0; |
| 210 | #define UNDOARGS_6	move.l (%sp)+, %a0; cfi_adjust_cfa_offset (-4);	 \ |
| 211 | 			UNDOARGS_5 |
| 212 | |
| 213 | |
| 214 | #define	ret	rts |
| 215 | #if 0 /* Not used by Linux */ |
| 216 | #define	r0	%d0 |
| 217 | #define	r1	%d1 |
| 218 | #define	MOVE(x,y)	movel x , y |
| 219 | #endif |
| 220 | |
| 221 | #else /* not __ASSEMBLER__ */ |
| 222 | |
| 223 | /* Define a macro which expands inline into the wrapper code for a system |
| 224 | call. This use is for internal calls that do not need to handle errors |
| 225 | normally. It will never touch errno. This returns just what the kernel |
| 226 | gave back. */ |
| 227 | #undef INTERNAL_SYSCALL |
| 228 | #define INTERNAL_SYSCALL_NCS(name, nr, args...)	\ |
| 229 | ({ unsigned int _sys_result;				\ |
| 230 | {							\ |
| 231 | /* Load argument values in temporary variables |
| 232 | 	 to perform side effects like function calls |
| 233 | 	 before the call used registers are set. */	\ |
| 234 | LOAD_ARGS_##nr (args)				\ |
| 235 | LOAD_REGS_##nr					\ |
| 236 | register int _d0 asm ("%d0") = name;		\ |
| 237 | asm volatile ("trap #0"				\ |
| 238 | 		 : "=d" (_d0)			\ |
| 239 | 		 : "0" (_d0) ASM_ARGS_##nr		\ |
| 240 | 		 : "memory");			\ |
| 241 | _sys_result = _d0;				\ |
| 242 | }							\ |
| 243 | (int) _sys_result; }) |
| 244 | #define INTERNAL_SYSCALL(name, nr, args...)	\ |
| 245 | INTERNAL_SYSCALL_NCS (__NR_##name, nr, ##args) |
| 246 | |
| 247 | #define LOAD_ARGS_0() |
| 248 | #define LOAD_REGS_0 |
| 249 | #define ASM_ARGS_0 |
| 250 | #define LOAD_ARGS_1(a1)				\ |
| 251 | LOAD_ARGS_0 ()				\ |
| 252 | int __arg1 = (int) (a1); |
| 253 | #define LOAD_REGS_1				\ |
| 254 | register int _d1 asm ("d1") = __arg1;		\ |
| 255 | LOAD_REGS_0 |
| 256 | #define ASM_ARGS_1	ASM_ARGS_0, "d" (_d1) |
| 257 | #define LOAD_ARGS_2(a1, a2)			\ |
| 258 | LOAD_ARGS_1 (a1)				\ |
| 259 | int __arg2 = (int) (a2); |
| 260 | #define LOAD_REGS_2				\ |
| 261 | register int _d2 asm ("d2") = __arg2;		\ |
| 262 | LOAD_REGS_1 |
| 263 | #define ASM_ARGS_2	ASM_ARGS_1, "d" (_d2) |
| 264 | #define LOAD_ARGS_3(a1, a2, a3)			\ |
| 265 | LOAD_ARGS_2 (a1, a2)				\ |
| 266 | int __arg3 = (int) (a3); |
| 267 | #define LOAD_REGS_3				\ |
| 268 | register int _d3 asm ("d3") = __arg3;		\ |
| 269 | LOAD_REGS_2 |
| 270 | #define ASM_ARGS_3	ASM_ARGS_2, "d" (_d3) |
| 271 | #define LOAD_ARGS_4(a1, a2, a3, a4)		\ |
| 272 | LOAD_ARGS_3 (a1, a2, a3)			\ |
| 273 | int __arg4 = (int) (a4); |
| 274 | #define LOAD_REGS_4				\ |
| 275 | register int _d4 asm ("d4") = __arg4;		\ |
| 276 | LOAD_REGS_3 |
| 277 | #define ASM_ARGS_4	ASM_ARGS_3, "d" (_d4) |
| 278 | #define LOAD_ARGS_5(a1, a2, a3, a4, a5)		\ |
| 279 | LOAD_ARGS_4 (a1, a2, a3, a4)			\ |
| 280 | int __arg5 = (int) (a5); |
| 281 | #define LOAD_REGS_5				\ |
| 282 | register int _d5 asm ("d5") = __arg5;		\ |
| 283 | LOAD_REGS_4 |
| 284 | #define ASM_ARGS_5	ASM_ARGS_4, "d" (_d5) |
| 285 | #define LOAD_ARGS_6(a1, a2, a3, a4, a5, a6)	\ |
| 286 | LOAD_ARGS_5 (a1, a2, a3, a4, a5)		\ |
| 287 | int __arg6 = (int) (a6); |
| 288 | #define LOAD_REGS_6				\ |
| 289 | register int _a0 asm ("a0") = __arg6;		\ |
| 290 | LOAD_REGS_5 |
| 291 | #define ASM_ARGS_6	ASM_ARGS_5, "a" (_a0) |
| 292 | |
| 293 | #undef HAVE_INTERNAL_BRK_ADDR_SYMBOL |
| 294 | #define HAVE_INTERNAL_BRK_ADDR_SYMBOL 1 |
| 295 | |
| 296 | #endif /* not __ASSEMBLER__ */ |
| 297 | |
| 298 | /* M68K needs system-supplied DSO to access TLS helpers |
| 299 | even when statically linked. */ |
| 300 | #define NEED_STATIC_SYSINFO_DSO 1 |