master
1/* Assembly macros for 64-bit PowerPC.
2 Copyright (C) 2002-2025 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <sysdeps/powerpc/sysdep.h>
20#include <tls.h>
21
22#ifdef __ASSEMBLER__
23
24/* Stack frame offsets. */
25#define FRAME_BACKCHAIN 0
26#define FRAME_CR_SAVE 8
27#define FRAME_LR_SAVE 16
28#if _CALL_ELF != 2
29#define FRAME_MIN_SIZE 112
30#define FRAME_MIN_SIZE_PARM 112
31#define FRAME_TOC_SAVE 40
32#define FRAME_PARM_SAVE 48
33#else
34#define FRAME_ROP_SAVE -8
35#define FRAME_MIN_SIZE 48 /* Includes space for the ROP save slot */
36#define FRAME_MIN_SIZE_PARM 112 /* Includes space for the ROP save slot */
37#define FRAME_TOC_SAVE 24
38#define FRAME_PARM_SAVE 32
39#endif
40
41/* Support macros for CALL_MCOUNT. */
42 .macro SAVE_ARG NARG
43 .if \NARG
44 SAVE_ARG \NARG-1
45 std 2+\NARG,-FRAME_MIN_SIZE_PARM+FRAME_PARM_SAVE-8+8*(\NARG)(1)
46 .endif
47 .endm
48
49 .macro REST_ARG NARG
50 .if \NARG
51 REST_ARG \NARG-1
52 ld 2+\NARG,FRAME_PARM_SAVE-8+8*(\NARG)(1)
53 .endif
54 .endm
55
56 .macro CFI_SAVE_ARG NARG
57 .if \NARG
58 CFI_SAVE_ARG \NARG-1
59 cfi_offset(2+\NARG,-FRAME_MIN_SIZE_PARM+FRAME_PARM_SAVE-8+8*(\NARG))
60 .endif
61 .endm
62
63 .macro CFI_REST_ARG NARG
64 .if \NARG
65 CFI_REST_ARG \NARG-1
66 cfi_restore(2+\NARG)
67 .endif
68 .endm
69
70/* If compiled for profiling, call `_mcount' at the start of each function.
71 see ppc-mcount.S for more details. */
72 .macro CALL_MCOUNT NARG
73#ifdef PROF
74 mflr r0
75 SAVE_ARG \NARG
76 std r0,FRAME_LR_SAVE(r1)
77 stdu r1,-FRAME_MIN_SIZE_PARM(r1)
78 cfi_adjust_cfa_offset(FRAME_MIN_SIZE_PARM)
79 cfi_offset(lr,FRAME_LR_SAVE)
80 CFI_SAVE_ARG \NARG
81 bl JUMPTARGET (_mcount)
82#ifndef SHARED
83 nop
84#endif
85 ld r0,FRAME_MIN_SIZE_PARM+FRAME_LR_SAVE(r1)
86 REST_ARG \NARG
87 mtlr r0
88 addi r1,r1,FRAME_MIN_SIZE_PARM
89 cfi_adjust_cfa_offset(-FRAME_MIN_SIZE_PARM)
90 cfi_restore(lr)
91 CFI_REST_ARG \NARG
92#endif
93 .endm
94
95#if _CALL_ELF != 2
96
97/* Macro to prepare for calling via a function pointer. */
98 .macro PPC64_LOAD_FUNCPTR PTR
99 ld r12,0(\PTR)
100 ld r2,8(\PTR)
101 mtctr r12
102 ld r11,16(\PTR)
103 .endm
104
105#ifdef USE_PPC64_OVERLAPPING_OPD
106# define OPD_ENT(name) .quad BODY_LABEL (name), .TOC.@tocbase
107#else
108# define OPD_ENT(name) .quad BODY_LABEL (name), .TOC.@tocbase, 0
109#endif
110
111#define ENTRY_1(name) \
112 .type BODY_LABEL(name),@function; \
113 .globl name; \
114 .section ".opd","aw"; \
115 .p2align 3;FUNC_LABEL(name): \
116 OPD_ENT (name); \
117 .previous
118
119#define FUNC_LABEL(X) X
120#define BODY_LABEL(X) .LY##X
121#define ENTRY_2(name) \
122 .type name,@function; \
123 ENTRY_1(name)
124#define END_2(name) \
125 .size name,.-BODY_LABEL(name); \
126 .size BODY_LABEL(name),.-BODY_LABEL(name)
127#define LOCALENTRY(name)
128
129#else /* _CALL_ELF == 2 */
130
131/* Macro to prepare for calling via a function pointer. */
132 .macro PPC64_LOAD_FUNCPTR PTR
133 mr r12,\PTR
134 mtctr r12
135 .endm
136
137#define FUNC_LABEL(X) X
138#define BODY_LABEL(X) X
139#define ENTRY_2(name) \
140 .globl name; \
141 .type name,@function
142#define END_2(name) \
143 .size name,.-name
144#define LOCALENTRY(name) \
1451: addis r2,r12,.TOC.-1b@ha; \
146 addi r2,r2,.TOC.-1b@l; \
147 .localentry name,.-name
148
149#endif /* _CALL_ELF */
150
151 .macro NOPS NARG
152 .if \NARG
153 NOPS \NARG-1
154 nop
155 .endif
156 .endm
157
158 .macro ENTRY_3 name, alignp2=2, nopwords=0
159 .text
160 ENTRY_2(\name)
161 .p2align \alignp2
162 NOPS \nopwords
163BODY_LABEL(\name):
164 .endm
165
166/* Use ENTRY_TOCLESS for functions that make no use of r2 and
167 guarantee r2 is unchanged on exit. Any function that has @toc or
168 @got relocs uses r2. Functions that call other functions via the
169 PLT use r2. Use ENTRY for functions that may use or change r2.
170 The first argument is the function name.
171 The optional second argument specifies alignment of the function's
172 code, as the logarithm base two of the byte alignment. For
173 example, a value of four aligns to a sixteen byte boundary.
174 The optional third argument specifies the number of NOPs to emit
175 before the start of the function's code. */
176#ifndef PROF
177#define ENTRY_TOCLESS(name, ...) \
178 ENTRY_3 name, ## __VA_ARGS__; \
179 cfi_startproc
180
181#define ENTRY(name, ...) \
182 ENTRY_TOCLESS(name, ## __VA_ARGS__); \
183 LOCALENTRY(name)
184#else
185/* The call to _mcount is potentially via the plt, so profiling code
186 is never free of an r2 use. */
187#define ENTRY_TOCLESS(name, ...) \
188 ENTRY_3 name, ## __VA_ARGS__; \
189 cfi_startproc; \
190 LOCALENTRY(name)
191
192#define ENTRY(name, ...) \
193 ENTRY_TOCLESS(name, ## __VA_ARGS__)
194#endif
195
196/* Local labels stripped out by the linker. */
197#undef L
198#define L(x) .L##x
199
200#define tostring(s) #s
201#define stringify(s) tostring(s)
202#define XGLUE(a,b) a##b
203#define GLUE(a,b) XGLUE(a,b)
204#define LT_LABEL(name) GLUE(.LT,name)
205#define LT_LABELSUFFIX(name,suffix) GLUE(GLUE(.LT,name),suffix)
206
207/* Support Traceback tables */
208#define TB_ASM 0x000c000000000000
209#define TB_GLOBALLINK 0x0000800000000000
210#define TB_IS_EPROL 0x0000400000000000
211#define TB_HAS_TBOFF 0x0000200000000000
212#define TB_INT_PROC 0x0000100000000000
213#define TB_HAS_CTL 0x0000080000000000
214#define TB_TOCLESS 0x0000040000000000
215#define TB_FP_PRESENT 0x0000020000000000
216#define TB_LOG_ABORT 0x0000010000000000
217#define TB_INT_HANDL 0x0000008000000000
218#define TB_NAME_PRESENT 0x0000004000000000
219#define TB_USES_ALLOCA 0x0000002000000000
220#define TB_SAVES_CR 0x0000000200000000
221#define TB_SAVES_LR 0x0000000100000000
222#define TB_STORES_BC 0x0000000080000000
223#define TB_FIXUP 0x0000000040000000
224#define TB_FP_SAVED(fprs) (((fprs) & 0x3f) << 24)
225#define TB_GPR_SAVED(gprs) (((fprs) & 0x3f) << 16)
226#define TB_FIXEDPARMS(parms) (((parms) & 0xff) << 8)
227#define TB_FLOATPARMS(parms) (((parms) & 0x7f) << 1)
228#define TB_PARMSONSTK 0x0000000000000001
229
230#define PPC_HIGHER(v) (((v) >> 32) & 0xffff)
231#define TB_DEFAULT TB_ASM | TB_HAS_TBOFF | TB_NAME_PRESENT
232
233#define TRACEBACK(name) \
234LT_LABEL(name): ; \
235 .long 0 ; \
236 .quad TB_DEFAULT ; \
237 .long LT_LABEL(name)-BODY_LABEL(name) ; \
238 .short LT_LABELSUFFIX(name,_name_end)-LT_LABELSUFFIX(name,_name_start) ; \
239LT_LABELSUFFIX(name,_name_start): ;\
240 .ascii stringify(name) ; \
241LT_LABELSUFFIX(name,_name_end): ; \
242 .p2align 2
243
244#define TRACEBACK_MASK(name,mask) \
245LT_LABEL(name): ; \
246 .long 0 ; \
247 .quad TB_DEFAULT | mask ; \
248 .long LT_LABEL(name)-BODY_LABEL(name) ; \
249 .short LT_LABELSUFFIX(name,_name_end)-LT_LABELSUFFIX(name,_name_start) ; \
250LT_LABELSUFFIX(name,_name_start): ;\
251 .ascii stringify(name) ; \
252LT_LABELSUFFIX(name,_name_end): ; \
253 .p2align 2
254
255/* END generates Traceback tables */
256#undef END
257#define END(name) \
258 cfi_endproc; \
259 TRACEBACK(name); \
260 END_2(name)
261
262/* This form supports more informative traceback tables */
263#define END_GEN_TB(name,mask) \
264 cfi_endproc; \
265 TRACEBACK_MASK(name,mask); \
266 END_2(name)
267
268/* We will allocate a new frame to save LR and the non-volatile register used to
269 read the TCB when checking for scv support on syscall code. We actually just
270 need the minimum frame size plus room for 1 reg (8 bytes). But the ABI
271 mandates stack frames should be aligned at 16 Bytes, so we end up allocating
272 a bit more space then what will actually be used. */
273#define SCV_FRAME_SIZE (FRAME_MIN_SIZE+16)
274#define SCV_FRAME_NVOLREG_SAVE FRAME_MIN_SIZE
275
276/* Allocate frame and save register */
277#define NVOLREG_SAVE \
278 stdu r1,-SCV_FRAME_SIZE(r1); \
279 cfi_adjust_cfa_offset(SCV_FRAME_SIZE); \
280 std r31,SCV_FRAME_NVOLREG_SAVE(r1); \
281 cfi_rel_offset(r31,SCV_FRAME_NVOLREG_SAVE);
282
283/* Restore register and destroy frame */
284#define NVOLREG_RESTORE \
285 ld r31,SCV_FRAME_NVOLREG_SAVE(r1); \
286 cfi_restore(r31); \
287 addi r1,r1,SCV_FRAME_SIZE; \
288 cfi_adjust_cfa_offset(-SCV_FRAME_SIZE);
289
290/* Check PPC_FEATURE2_SCV bit from hwcap2 in the TCB. If it is not set, scv is
291 not available, then go to JUMPFALSE (label given by the macro's caller). We
292 save the value we read from the TCB in a non-volatile register so we can
293 reuse it later when exiting from the syscall in PSEUDO_RET. Note that for
294 the static case we need an extra check to guarantee the thread pointer has
295 already been initialized, otherwise we may try to access an invalid address
296 if a syscall is called before the TLS has been setup. */
297 .macro CHECK_SCV_SUPPORT REG JUMPFALSE
298
299#ifndef SHARED
300 /* Check if thread pointer has already been setup. */
301 cmpdi r13,0
302 beq \JUMPFALSE
303#endif
304
305 /* Read PPC_FEATURE2_SCV from TCB and store it in REG */
306 ld \REG,TCB_HWCAP(PT_THREAD_POINTER)
307 andis. \REG,\REG,PPC_FEATURE2_SCV>>16
308
309 beq \JUMPFALSE
310 .endm
311
312#if !defined(USE_PPC_SCV) || IS_IN(rtld)
313# define DO_CALL(syscall) \
314 li r0,syscall; \
315 DO_CALL_SC
316#else
317/* Before doing the syscall, check if we can use scv. scv is supported by P9
318 and later with Linux v5.9 and later. If so, use it. Otherwise, fallback to
319 sc. We use a non-volatile register to save hwcap2 from the TCB, so we need
320 to save its content beforehand. */
321# define DO_CALL(syscall) \
322 li r0,syscall; \
323 NVOLREG_SAVE; \
324 CHECK_SCV_SUPPORT r31 0f; \
325 DO_CALL_SCV; \
326 b 1f; \
3270: DO_CALL_SC; \
3281:
329#endif /* !defined(USE_PPC_SCV) || IS_IN(rtld) */
330
331/* DO_CALL_SC and DO_CALL_SCV expect the syscall number to be in r0. */
332#define DO_CALL_SC \
333 sc
334
335#define DO_CALL_SCV \
336 mflr r9; \
337 std r9,SCV_FRAME_SIZE+FRAME_LR_SAVE(r1); \
338 cfi_rel_offset(lr,SCV_FRAME_SIZE+FRAME_LR_SAVE); \
339 .machine "push"; \
340 .machine "power9"; \
341 scv 0; \
342 .machine "pop"; \
343 ld r9,SCV_FRAME_SIZE+FRAME_LR_SAVE(r1); \
344 mtlr r9; \
345 cfi_restore(lr);
346
347/* ppc64 is always PIC */
348#undef JUMPTARGET
349#define JUMPTARGET(name) FUNC_LABEL(name)
350
351#define PSEUDO(name, syscall_name, args) \
352 .section ".text"; \
353 ENTRY (name); \
354 DO_CALL (SYS_ify (syscall_name))
355
356#ifdef SHARED
357# define TAIL_CALL_NO_RETURN(__func) \
358 b JUMPTARGET (NOTOC (__func))
359#else
360# define TAIL_CALL_NO_RETURN(__func) \
361 .ifdef .Local ## __func; \
362 b .Local ## __func; \
363 .else; \
364.Local ## __func: \
365 mflr 0; \
366 std 0,FRAME_LR_SAVE(1); \
367 stdu 1,-FRAME_MIN_SIZE(1); \
368 cfi_adjust_cfa_offset(FRAME_MIN_SIZE); \
369 cfi_offset(lr,FRAME_LR_SAVE); \
370 bl JUMPTARGET(__func); \
371 nop; \
372 .endif
373#endif
374
375#ifdef SHARED
376#define TAIL_CALL_SYSCALL_ERROR \
377 b JUMPTARGET (NOTOC (__syscall_error))
378#else
379/* Static version might be linked into a large app with a toc exceeding
380 64k. We can't put a toc adjusting stub on a plain branch, so can't
381 tail call __syscall_error. */
382#define TAIL_CALL_SYSCALL_ERROR \
383 .ifdef .Local_syscall_error; \
384 b .Local_syscall_error; \
385 .else; \
386.Local_syscall_error: \
387 mflr 0; \
388 std 0,FRAME_LR_SAVE(1); \
389 stdu 1,-FRAME_MIN_SIZE(1); \
390 cfi_adjust_cfa_offset(FRAME_MIN_SIZE); \
391 cfi_offset(lr,FRAME_LR_SAVE); \
392 bl JUMPTARGET(__syscall_error); \
393 nop; \
394 ld 0,FRAME_MIN_SIZE+FRAME_LR_SAVE(1); \
395 addi 1,1,FRAME_MIN_SIZE; \
396 cfi_adjust_cfa_offset(-FRAME_MIN_SIZE); \
397 mtlr 0; \
398 cfi_restore(lr); \
399 blr; \
400 .endif
401#endif
402
403#if !defined(USE_PPC_SCV) || IS_IN(rtld)
404# define PSEUDO_RET \
405 RET_SC; \
406 TAIL_CALL_SYSCALL_ERROR
407#else
408/* This should only be called after a DO_CALL. In such cases, r31 contains the
409 value of PPC_FEATURE2_SCV read from hwcap2 by CHECK_SCV_SUPPORT. If it is
410 set, we know we have entered the kernel using scv, so handle the return code
411 accordingly. */
412# define PSEUDO_RET \
413 cmpdi cr5,r31,0; \
414 NVOLREG_RESTORE; \
415 beq cr5,0f; \
416 RET_SCV; \
417 b 1f; \
4180: RET_SC; \
4191: TAIL_CALL_SYSCALL_ERROR
420#endif /* !defined(USE_PPC_SCV) || IS_IN(rtld) */
421
422#define RET_SCV \
423 li r9,-4095; \
424 cmpld r3,r9; \
425 bltlr+; \
426 neg r3,r3;
427
428#define RET_SC \
429 bnslr+;
430
431#define ret PSEUDO_RET
432
433#undef PSEUDO_END
434#define PSEUDO_END(name) \
435 END (name)
436
437#define PSEUDO_NOERRNO(name, syscall_name, args) \
438 .section ".text"; \
439 ENTRY (name); \
440 DO_CALL (SYS_ify (syscall_name))
441
442#if !defined(USE_PPC_SCV) || IS_IN(rtld)
443# define PSEUDO_RET_NOERRNO \
444 blr
445#else
446/* This should only be called after a DO_CALL. */
447# define PSEUDO_RET_NOERRNO \
448 NVOLREG_RESTORE; \
449 blr
450#endif /* !defined(USE_PPC_SCV) || IS_IN(rtld) */
451
452#define ret_NOERRNO PSEUDO_RET_NOERRNO
453
454#undef PSEUDO_END_NOERRNO
455#define PSEUDO_END_NOERRNO(name) \
456 END (name)
457
458#define PSEUDO_ERRVAL(name, syscall_name, args) \
459 .section ".text"; \
460 ENTRY (name); \
461 DO_CALL (SYS_ify (syscall_name))
462
463#if !defined(USE_PPC_SCV) || IS_IN(rtld)
464# define PSEUDO_RET_ERRVAL \
465 blr
466#else
467/* This should only be called after a DO_CALL. */
468# define PSEUDO_RET_ERRVAL \
469 NVOLREG_RESTORE; \
470 blr
471#endif /* !defined(USE_PPC_SCV) || IS_IN(rtld) */
472
473#define ret_ERRVAL PSEUDO_RET_ERRVAL
474
475#undef PSEUDO_END_ERRVAL
476#define PSEUDO_END_ERRVAL(name) \
477 END (name)
478
479#ifdef SHARED
480# if IS_IN (rtld)
481 /* Inside ld.so we use the local alias to avoid runtime GOT
482 relocations. */
483# define __GLRO_DEF(var) \
484.LC__ ## var: \
485 .tc _rtld_local_ro[TC],_rtld_local_ro
486# else
487# define __GLRO_DEF(var) \
488.LC__ ## var: \
489 .tc _rtld_global_ro[TC],_rtld_global_ro
490# endif
491# define __GLRO(rOUT, var, offset) \
492 addis rOUT,r2,.LC__ ## var@toc@ha; \
493 ld rOUT,.LC__ ## var@toc@l(rOUT); \
494 lwz rOUT,offset(rOUT)
495#else
496# define __GLRO_DEF(var) \
497.LC__ ## var: \
498 .tc _ ## var[TC],_ ## var
499# define __GLRO(rOUT, var, offset) \
500 addis rOUT,r2,.LC__ ## var@toc@ha; \
501 ld rOUT,.LC__ ## var@toc@l(rOUT); \
502 lwz rOUT,0(rOUT)
503#endif
504
505#ifdef USE_PPC64_NOTOC
506# define NOTOC(l) l@notoc
507#else
508# define NOTOC(l) l
509#endif
510
511#else /* !__ASSEMBLER__ */
512
513#if _CALL_ELF != 2
514
515#define PPC64_LOAD_FUNCPTR(ptr) \
516 "ld 12,0(" #ptr ")\n" \
517 "ld 2,8(" #ptr ")\n" \
518 "mtctr 12\n" \
519 "ld 11,16(" #ptr ")"
520
521#ifdef USE_PPC64_OVERLAPPING_OPD
522# define OPD_ENT(name) ".quad " BODY_PREFIX #name ", .TOC.@tocbase"
523#else
524# define OPD_ENT(name) ".quad " BODY_PREFIX #name ", .TOC.@tocbase, 0"
525#endif
526
527#define ENTRY_1(name) \
528 ".type " BODY_PREFIX #name ",@function\n" \
529 ".globl " #name "\n" \
530 ".pushsection \".opd\",\"aw\"\n" \
531 ".p2align 3\n" \
532#name ":\n" \
533 OPD_ENT (name) "\n" \
534 ".popsection"
535
536#define DOT_PREFIX ""
537#define BODY_PREFIX ".LY"
538#define ENTRY_2(name) \
539 ".type " #name ",@function\n" \
540 ENTRY_1(name)
541#define END_2(name) \
542 ".size " #name ",.-" BODY_PREFIX #name "\n" \
543 ".size " BODY_PREFIX #name ",.-" BODY_PREFIX #name
544#define LOCALENTRY(name)
545
546#else /* _CALL_ELF */
547
548#define PPC64_LOAD_FUNCPTR(ptr) \
549 "mr 12," #ptr "\n" \
550 "mtctr 12"
551
552#define DOT_PREFIX ""
553#define BODY_PREFIX ""
554#define ENTRY_2(name) \
555 ".type " #name ",@function\n" \
556 ".globl " #name
557#define END_2(name) \
558 ".size " #name ",.-" #name
559#define LOCALENTRY(name) \
560 "1: addis 2,12,.TOC.-1b@ha\n" \
561 "addi 2,2,.TOC.-1b@l\n" \
562 ".localentry " #name ",.-" #name
563
564#endif /* _CALL_ELF */
565
566#endif /* __ASSEMBLER__ */