1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
5 * (C) Copyright 2016-2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
11 * RSEQ_SIG is used with the following reserved undefined instructions, which
14 * x86-32: 0f b9 3d 53 30 05 53 ud1 0x53053053,%edi
15 * x86-64: 0f b9 3d 53 30 05 53 ud1 0x53053053(%rip),%edi
17 #define RSEQ_SIG 0x53053053
20 * Due to a compiler optimization bug in gcc-8 with asm goto and TLS asm input
21 * operands, we cannot use "m" input operands, and rather pass the __rseq_abi
22 * address through a "r" input operand.
25 /* Offset of cpu_id and rseq_cs fields in struct rseq. */
26 #define RSEQ_CPU_ID_OFFSET 4
27 #define RSEQ_CS_OFFSET 8
31 #define rseq_smp_mb() \
32 __asm__ __volatile__ ("lock; addl $0,-128(%%rsp)" ::: "memory", "cc")
33 #define rseq_smp_rmb() rseq_barrier()
34 #define rseq_smp_wmb() rseq_barrier()
36 #define rseq_smp_load_acquire(p) \
38 __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p); \
43 #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb()
45 #define rseq_smp_store_release(p, v) \
48 RSEQ_WRITE_ONCE(*p, v); \
51 #ifdef RSEQ_SKIP_FASTPATH
52 #include "rseq-skip.h"
53 #else /* !RSEQ_SKIP_FASTPATH */
55 #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \
56 start_ip, post_commit_offset, abort_ip) \
57 ".pushsection __rseq_cs, \"aw\"\n\t" \
59 __rseq_str(label) ":\n\t" \
60 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
61 ".quad " __rseq_str(start_ip) ", " __rseq_str(post_commit_offset) ", " __rseq_str(abort_ip) "\n\t" \
63 ".pushsection __rseq_cs_ptr_array, \"aw\"\n\t" \
64 ".quad " __rseq_str(label) "b\n\t" \
68 #define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \
69 __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \
70 (post_commit_ip - start_ip), abort_ip)
73 * Exit points of a rseq critical section consist of all instructions outside
74 * of the critical section where a critical section can either branch to or
75 * reach through the normal course of its execution. The abort IP and the
76 * post-commit IP are already part of the __rseq_cs section and should not be
77 * explicitly defined as additional exit points. Knowing all exit points is
78 * useful to assist debuggers stepping over the critical section.
80 #define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \
81 ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \
82 ".quad " __rseq_str(start_ip) ", " __rseq_str(exit_ip) "\n\t" \
85 #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
87 "leaq " __rseq_str(cs_label) "(%%rip), %%rax\n\t" \
88 "movq %%rax, " __rseq_str(rseq_cs) "\n\t" \
89 __rseq_str(label) ":\n\t"
91 #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \
93 "cmpl %[" __rseq_str(cpu_id) "], " __rseq_str(current_cpu_id) "\n\t" \
94 "jnz " __rseq_str(label) "\n\t"
96 #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \
97 ".pushsection __rseq_failure, \"ax\"\n\t" \
98 /* Disassembler-friendly signature: ud1 <sig>(%rip),%edi. */ \
99 ".byte 0x0f, 0xb9, 0x3d\n\t" \
100 ".long " __rseq_str(RSEQ_SIG) "\n\t" \
101 __rseq_str(label) ":\n\t" \
103 "jmp %l[" __rseq_str(abort_label) "]\n\t" \
106 #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \
107 ".pushsection __rseq_failure, \"ax\"\n\t" \
108 __rseq_str(label) ":\n\t" \
110 "jmp %l[" __rseq_str(cmpfail_label) "]\n\t" \
113 static inline __attribute__((always_inline
))
114 int rseq_cmpeqv_storev(intptr_t *v
, intptr_t expect
, intptr_t newv
, int cpu
)
118 __asm__ __volatile__
goto (
119 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
120 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
121 #ifdef RSEQ_COMPARE_TWICE
122 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
123 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
125 /* Start rseq by storing table entry pointer into rseq_cs. */
126 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
127 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
129 "cmpq %[v], %[expect]\n\t"
130 "jnz %l[cmpfail]\n\t"
132 #ifdef RSEQ_COMPARE_TWICE
133 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), %l
[error1
])
134 "cmpq %[v], %[expect]\n\t"
138 "movq %[newv], %[v]\n\t"
141 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
142 : /* gcc asm goto does not allow outputs */
143 : [cpu_id
] "r" (cpu
),
144 [rseq_abi
] "r" (&__rseq_abi
),
146 [expect
] "r" (expect
),
148 : "memory", "cc", "rax"
151 #ifdef RSEQ_COMPARE_TWICE
161 #ifdef RSEQ_COMPARE_TWICE
163 rseq_bug("cpu_id comparison failed");
165 rseq_bug("expected value comparison failed");
170 * Compare @v against @expectnot. When it does _not_ match, load @v
171 * into @load, and store the content of *@v + voffp into @v.
173 static inline __attribute__((always_inline
))
174 int rseq_cmpnev_storeoffp_load(intptr_t *v
, intptr_t expectnot
,
175 off_t voffp
, intptr_t *load
, int cpu
)
179 __asm__ __volatile__
goto (
180 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
181 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
182 #ifdef RSEQ_COMPARE_TWICE
183 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
184 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
186 /* Start rseq by storing table entry pointer into rseq_cs. */
187 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
188 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
190 "movq %[v], %%rbx\n\t"
191 "cmpq %%rbx, %[expectnot]\n\t"
194 #ifdef RSEQ_COMPARE_TWICE
195 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), %l
[error1
])
196 "movq %[v], %%rbx\n\t"
197 "cmpq %%rbx, %[expectnot]\n\t"
200 "movq %%rbx, %[load]\n\t"
201 "addq %[voffp], %%rbx\n\t"
202 "movq (%%rbx), %%rbx\n\t"
204 "movq %%rbx, %[v]\n\t"
207 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
208 : /* gcc asm goto does not allow outputs */
209 : [cpu_id
] "r" (cpu
),
210 [rseq_abi
] "r" (&__rseq_abi
),
211 /* final store input */
213 [expectnot
] "r" (expectnot
),
214 [voffp
] "er" (voffp
),
216 : "memory", "cc", "rax", "rbx"
219 #ifdef RSEQ_COMPARE_TWICE
229 #ifdef RSEQ_COMPARE_TWICE
231 rseq_bug("cpu_id comparison failed");
233 rseq_bug("expected value comparison failed");
237 static inline __attribute__((always_inline
))
238 int rseq_addv(intptr_t *v
, intptr_t count
, int cpu
)
242 __asm__ __volatile__
goto (
243 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
244 #ifdef RSEQ_COMPARE_TWICE
245 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
247 /* Start rseq by storing table entry pointer into rseq_cs. */
248 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
249 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
251 #ifdef RSEQ_COMPARE_TWICE
252 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), %l
[error1
])
255 "addq %[count], %[v]\n\t"
258 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
259 : /* gcc asm goto does not allow outputs */
260 : [cpu_id
] "r" (cpu
),
261 [rseq_abi
] "r" (&__rseq_abi
),
262 /* final store input */
265 : "memory", "cc", "rax"
268 #ifdef RSEQ_COMPARE_TWICE
276 #ifdef RSEQ_COMPARE_TWICE
278 rseq_bug("cpu_id comparison failed");
282 #define RSEQ_ARCH_HAS_OFFSET_DEREF_ADDV
288 static inline __attribute__((always_inline
))
289 int rseq_offset_deref_addv(intptr_t *ptr
, off_t off
, intptr_t inc
, int cpu
)
293 __asm__ __volatile__
goto (
294 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
295 #ifdef RSEQ_COMPARE_TWICE
296 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
298 /* Start rseq by storing table entry pointer into rseq_cs. */
299 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
300 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
302 #ifdef RSEQ_COMPARE_TWICE
303 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), %l
[error1
])
306 "movq %[ptr], %%rbx\n\t"
307 "addq %[off], %%rbx\n\t"
309 "movq (%%rbx), %%rcx\n\t"
311 "addq %[inc], (%%rcx)\n\t"
314 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
315 : /* gcc asm goto does not allow outputs */
316 : [cpu_id
] "r" (cpu
),
317 [rseq_abi
] "r" (&__rseq_abi
),
318 /* final store input */
322 : "memory", "cc", "rax", "rbx", "rcx"
325 #ifdef RSEQ_COMPARE_TWICE
333 #ifdef RSEQ_COMPARE_TWICE
335 rseq_bug("cpu_id comparison failed");
339 static inline __attribute__((always_inline
))
340 int rseq_cmpeqv_trystorev_storev(intptr_t *v
, intptr_t expect
,
341 intptr_t *v2
, intptr_t newv2
,
342 intptr_t newv
, int cpu
)
346 __asm__ __volatile__
goto (
347 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
348 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
349 #ifdef RSEQ_COMPARE_TWICE
350 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
351 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
353 /* Start rseq by storing table entry pointer into rseq_cs. */
354 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
355 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
357 "cmpq %[v], %[expect]\n\t"
358 "jnz %l[cmpfail]\n\t"
360 #ifdef RSEQ_COMPARE_TWICE
361 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), %l
[error1
])
362 "cmpq %[v], %[expect]\n\t"
366 "movq %[newv2], %[v2]\n\t"
369 "movq %[newv], %[v]\n\t"
372 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
373 : /* gcc asm goto does not allow outputs */
374 : [cpu_id
] "r" (cpu
),
375 [rseq_abi
] "r" (&__rseq_abi
),
376 /* try store input */
379 /* final store input */
381 [expect
] "r" (expect
),
383 : "memory", "cc", "rax"
386 #ifdef RSEQ_COMPARE_TWICE
396 #ifdef RSEQ_COMPARE_TWICE
398 rseq_bug("cpu_id comparison failed");
400 rseq_bug("expected value comparison failed");
405 static inline __attribute__((always_inline
))
406 int rseq_cmpeqv_trystorev_storev_release(intptr_t *v
, intptr_t expect
,
407 intptr_t *v2
, intptr_t newv2
,
408 intptr_t newv
, int cpu
)
410 return rseq_cmpeqv_trystorev_storev(v
, expect
, v2
, newv2
, newv
, cpu
);
413 static inline __attribute__((always_inline
))
414 int rseq_cmpeqv_cmpeqv_storev(intptr_t *v
, intptr_t expect
,
415 intptr_t *v2
, intptr_t expect2
,
416 intptr_t newv
, int cpu
)
420 __asm__ __volatile__
goto (
421 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
422 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
423 #ifdef RSEQ_COMPARE_TWICE
424 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
425 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
426 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error3
])
428 /* Start rseq by storing table entry pointer into rseq_cs. */
429 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
430 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
432 "cmpq %[v], %[expect]\n\t"
433 "jnz %l[cmpfail]\n\t"
435 "cmpq %[v2], %[expect2]\n\t"
436 "jnz %l[cmpfail]\n\t"
438 #ifdef RSEQ_COMPARE_TWICE
439 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), %l
[error1
])
440 "cmpq %[v], %[expect]\n\t"
442 "cmpq %[v2], %[expect2]\n\t"
446 "movq %[newv], %[v]\n\t"
449 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
450 : /* gcc asm goto does not allow outputs */
451 : [cpu_id
] "r" (cpu
),
452 [rseq_abi
] "r" (&__rseq_abi
),
455 [expect2
] "r" (expect2
),
456 /* final store input */
458 [expect
] "r" (expect
),
460 : "memory", "cc", "rax"
463 #ifdef RSEQ_COMPARE_TWICE
464 , error1
, error2
, error3
473 #ifdef RSEQ_COMPARE_TWICE
475 rseq_bug("cpu_id comparison failed");
477 rseq_bug("1st expected value comparison failed");
479 rseq_bug("2nd expected value comparison failed");
483 static inline __attribute__((always_inline
))
484 int rseq_cmpeqv_trymemcpy_storev(intptr_t *v
, intptr_t expect
,
485 void *dst
, void *src
, size_t len
,
486 intptr_t newv
, int cpu
)
488 uint64_t rseq_scratch
[3];
492 __asm__ __volatile__
goto (
493 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
494 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
495 #ifdef RSEQ_COMPARE_TWICE
496 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
497 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
499 "movq %[src], %[rseq_scratch0]\n\t"
500 "movq %[dst], %[rseq_scratch1]\n\t"
501 "movq %[len], %[rseq_scratch2]\n\t"
502 /* Start rseq by storing table entry pointer into rseq_cs. */
503 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
504 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
506 "cmpq %[v], %[expect]\n\t"
509 #ifdef RSEQ_COMPARE_TWICE
510 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 6f
)
511 "cmpq %[v], %[expect]\n\t"
515 "test %[len], %[len]\n\t" \
518 "movb (%[src]), %%al\n\t" \
519 "movb %%al, (%[dst])\n\t" \
527 "movq %[newv], %[v]\n\t"
531 "movq %[rseq_scratch2], %[len]\n\t"
532 "movq %[rseq_scratch1], %[dst]\n\t"
533 "movq %[rseq_scratch0], %[src]\n\t"
534 RSEQ_ASM_DEFINE_ABORT(4,
535 "movq %[rseq_scratch2], %[len]\n\t"
536 "movq %[rseq_scratch1], %[dst]\n\t"
537 "movq %[rseq_scratch0], %[src]\n\t",
539 RSEQ_ASM_DEFINE_CMPFAIL(5,
540 "movq %[rseq_scratch2], %[len]\n\t"
541 "movq %[rseq_scratch1], %[dst]\n\t"
542 "movq %[rseq_scratch0], %[src]\n\t",
544 #ifdef RSEQ_COMPARE_TWICE
545 RSEQ_ASM_DEFINE_CMPFAIL(6,
546 "movq %[rseq_scratch2], %[len]\n\t"
547 "movq %[rseq_scratch1], %[dst]\n\t"
548 "movq %[rseq_scratch0], %[src]\n\t",
550 RSEQ_ASM_DEFINE_CMPFAIL(7,
551 "movq %[rseq_scratch2], %[len]\n\t"
552 "movq %[rseq_scratch1], %[dst]\n\t"
553 "movq %[rseq_scratch0], %[src]\n\t",
556 : /* gcc asm goto does not allow outputs */
557 : [cpu_id
] "r" (cpu
),
558 [rseq_abi
] "r" (&__rseq_abi
),
559 /* final store input */
561 [expect
] "r" (expect
),
563 /* try memcpy input */
567 [rseq_scratch0
] "m" (rseq_scratch
[0]),
568 [rseq_scratch1
] "m" (rseq_scratch
[1]),
569 [rseq_scratch2
] "m" (rseq_scratch
[2])
570 : "memory", "cc", "rax"
573 #ifdef RSEQ_COMPARE_TWICE
583 #ifdef RSEQ_COMPARE_TWICE
585 rseq_bug("cpu_id comparison failed");
587 rseq_bug("expected value comparison failed");
592 static inline __attribute__((always_inline
))
593 int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v
, intptr_t expect
,
594 void *dst
, void *src
, size_t len
,
595 intptr_t newv
, int cpu
)
597 return rseq_cmpeqv_trymemcpy_storev(v
, expect
, dst
, src
, len
,
601 #endif /* !RSEQ_SKIP_FASTPATH */
605 #define rseq_smp_mb() \
606 __asm__ __volatile__ ("lock; addl $0,-128(%%esp)" ::: "memory", "cc")
607 #define rseq_smp_rmb() \
608 __asm__ __volatile__ ("lock; addl $0,-128(%%esp)" ::: "memory", "cc")
609 #define rseq_smp_wmb() \
610 __asm__ __volatile__ ("lock; addl $0,-128(%%esp)" ::: "memory", "cc")
612 #define rseq_smp_load_acquire(p) \
614 __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p); \
619 #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb()
621 #define rseq_smp_store_release(p, v) \
624 RSEQ_WRITE_ONCE(*p, v); \
627 #ifdef RSEQ_SKIP_FASTPATH
628 #include "rseq-skip.h"
629 #else /* !RSEQ_SKIP_FASTPATH */
632 * Use eax as scratch register and take memory operands as input to
633 * lessen register pressure. Especially needed when compiling in O0.
635 #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \
636 start_ip, post_commit_offset, abort_ip) \
637 ".pushsection __rseq_cs, \"aw\"\n\t" \
639 __rseq_str(label) ":\n\t" \
640 ".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
641 ".long " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) ", 0x0\n\t" \
643 ".pushsection __rseq_cs_ptr_array, \"aw\"\n\t" \
644 ".long " __rseq_str(label) "b, 0x0\n\t" \
647 #define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \
648 __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \
649 (post_commit_ip - start_ip), abort_ip)
652 * Exit points of a rseq critical section consist of all instructions outside
653 * of the critical section where a critical section can either branch to or
654 * reach through the normal course of its execution. The abort IP and the
655 * post-commit IP are already part of the __rseq_cs section and should not be
656 * explicitly defined as additional exit points. Knowing all exit points is
657 * useful to assist debuggers stepping over the critical section.
659 #define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \
660 ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \
661 ".long " __rseq_str(start_ip) ", 0x0, " __rseq_str(exit_ip) ", 0x0\n\t" \
664 #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
666 "movl $" __rseq_str(cs_label) ", " __rseq_str(rseq_cs) "\n\t" \
667 __rseq_str(label) ":\n\t"
669 #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \
671 "cmpl %[" __rseq_str(cpu_id) "], " __rseq_str(current_cpu_id) "\n\t" \
672 "jnz " __rseq_str(label) "\n\t"
674 #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \
675 ".pushsection __rseq_failure, \"ax\"\n\t" \
676 /* Disassembler-friendly signature: ud1 <sig>,%edi. */ \
677 ".byte 0x0f, 0xb9, 0x3d\n\t" \
678 ".long " __rseq_str(RSEQ_SIG) "\n\t" \
679 __rseq_str(label) ":\n\t" \
681 "jmp %l[" __rseq_str(abort_label) "]\n\t" \
684 #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \
685 ".pushsection __rseq_failure, \"ax\"\n\t" \
686 __rseq_str(label) ":\n\t" \
688 "jmp %l[" __rseq_str(cmpfail_label) "]\n\t" \
691 static inline __attribute__((always_inline
))
692 int rseq_cmpeqv_storev(intptr_t *v
, intptr_t expect
, intptr_t newv
, int cpu
)
696 __asm__ __volatile__
goto (
697 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
698 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
699 #ifdef RSEQ_COMPARE_TWICE
700 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
701 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
703 /* Start rseq by storing table entry pointer into rseq_cs. */
704 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
705 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
707 "cmpl %[v], %[expect]\n\t"
708 "jnz %l[cmpfail]\n\t"
710 #ifdef RSEQ_COMPARE_TWICE
711 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), %l
[error1
])
712 "cmpl %[v], %[expect]\n\t"
716 "movl %[newv], %[v]\n\t"
719 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
720 : /* gcc asm goto does not allow outputs */
721 : [cpu_id
] "r" (cpu
),
722 [rseq_abi
] "r" (&__rseq_abi
),
724 [expect
] "r" (expect
),
726 : "memory", "cc", "eax"
729 #ifdef RSEQ_COMPARE_TWICE
739 #ifdef RSEQ_COMPARE_TWICE
741 rseq_bug("cpu_id comparison failed");
743 rseq_bug("expected value comparison failed");
748 * Compare @v against @expectnot. When it does _not_ match, load @v
749 * into @load, and store the content of *@v + voffp into @v.
751 static inline __attribute__((always_inline
))
752 int rseq_cmpnev_storeoffp_load(intptr_t *v
, intptr_t expectnot
,
753 off_t voffp
, intptr_t *load
, int cpu
)
757 __asm__ __volatile__
goto (
758 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
759 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
760 #ifdef RSEQ_COMPARE_TWICE
761 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
762 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
764 /* Start rseq by storing table entry pointer into rseq_cs. */
765 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
766 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
768 "movl %[v], %%ebx\n\t"
769 "cmpl %%ebx, %[expectnot]\n\t"
772 #ifdef RSEQ_COMPARE_TWICE
773 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), %l
[error1
])
774 "movl %[v], %%ebx\n\t"
775 "cmpl %%ebx, %[expectnot]\n\t"
778 "movl %%ebx, %[load]\n\t"
779 "addl %[voffp], %%ebx\n\t"
780 "movl (%%ebx), %%ebx\n\t"
782 "movl %%ebx, %[v]\n\t"
785 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
786 : /* gcc asm goto does not allow outputs */
787 : [cpu_id
] "r" (cpu
),
788 [rseq_abi
] "r" (&__rseq_abi
),
789 /* final store input */
791 [expectnot
] "r" (expectnot
),
792 [voffp
] "ir" (voffp
),
794 : "memory", "cc", "eax", "ebx"
797 #ifdef RSEQ_COMPARE_TWICE
807 #ifdef RSEQ_COMPARE_TWICE
809 rseq_bug("cpu_id comparison failed");
811 rseq_bug("expected value comparison failed");
815 static inline __attribute__((always_inline
))
816 int rseq_addv(intptr_t *v
, intptr_t count
, int cpu
)
820 __asm__ __volatile__
goto (
821 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
822 #ifdef RSEQ_COMPARE_TWICE
823 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
825 /* Start rseq by storing table entry pointer into rseq_cs. */
826 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
827 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
829 #ifdef RSEQ_COMPARE_TWICE
830 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), %l
[error1
])
833 "addl %[count], %[v]\n\t"
836 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
837 : /* gcc asm goto does not allow outputs */
838 : [cpu_id
] "r" (cpu
),
839 [rseq_abi
] "r" (&__rseq_abi
),
840 /* final store input */
843 : "memory", "cc", "eax"
846 #ifdef RSEQ_COMPARE_TWICE
854 #ifdef RSEQ_COMPARE_TWICE
856 rseq_bug("cpu_id comparison failed");
860 static inline __attribute__((always_inline
))
861 int rseq_cmpeqv_trystorev_storev(intptr_t *v
, intptr_t expect
,
862 intptr_t *v2
, intptr_t newv2
,
863 intptr_t newv
, int cpu
)
867 __asm__ __volatile__
goto (
868 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
869 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
870 #ifdef RSEQ_COMPARE_TWICE
871 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
872 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
874 /* Start rseq by storing table entry pointer into rseq_cs. */
875 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
876 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
878 "cmpl %[v], %[expect]\n\t"
879 "jnz %l[cmpfail]\n\t"
881 #ifdef RSEQ_COMPARE_TWICE
882 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), %l
[error1
])
883 "cmpl %[v], %[expect]\n\t"
887 "movl %[newv2], %%eax\n\t"
888 "movl %%eax, %[v2]\n\t"
891 "movl %[newv], %[v]\n\t"
894 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
895 : /* gcc asm goto does not allow outputs */
896 : [cpu_id
] "r" (cpu
),
897 [rseq_abi
] "r" (&__rseq_abi
),
898 /* try store input */
901 /* final store input */
903 [expect
] "r" (expect
),
905 : "memory", "cc", "eax"
908 #ifdef RSEQ_COMPARE_TWICE
918 #ifdef RSEQ_COMPARE_TWICE
920 rseq_bug("cpu_id comparison failed");
922 rseq_bug("expected value comparison failed");
926 static inline __attribute__((always_inline
))
927 int rseq_cmpeqv_trystorev_storev_release(intptr_t *v
, intptr_t expect
,
928 intptr_t *v2
, intptr_t newv2
,
929 intptr_t newv
, int cpu
)
933 __asm__ __volatile__
goto (
934 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
935 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
936 #ifdef RSEQ_COMPARE_TWICE
937 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
938 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
940 /* Start rseq by storing table entry pointer into rseq_cs. */
941 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
942 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
944 "movl %[expect], %%eax\n\t"
945 "cmpl %[v], %%eax\n\t"
946 "jnz %l[cmpfail]\n\t"
948 #ifdef RSEQ_COMPARE_TWICE
949 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), %l
[error1
])
950 "movl %[expect], %%eax\n\t"
951 "cmpl %[v], %%eax\n\t"
955 "movl %[newv2], %[v2]\n\t"
957 "lock; addl $0,-128(%%esp)\n\t"
959 "movl %[newv], %[v]\n\t"
962 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
963 : /* gcc asm goto does not allow outputs */
964 : [cpu_id
] "r" (cpu
),
965 [rseq_abi
] "r" (&__rseq_abi
),
966 /* try store input */
969 /* final store input */
971 [expect
] "m" (expect
),
973 : "memory", "cc", "eax"
976 #ifdef RSEQ_COMPARE_TWICE
986 #ifdef RSEQ_COMPARE_TWICE
988 rseq_bug("cpu_id comparison failed");
990 rseq_bug("expected value comparison failed");
995 static inline __attribute__((always_inline
))
996 int rseq_cmpeqv_cmpeqv_storev(intptr_t *v
, intptr_t expect
,
997 intptr_t *v2
, intptr_t expect2
,
998 intptr_t newv
, int cpu
)
1002 __asm__ __volatile__
goto (
1003 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
1004 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
1005 #ifdef RSEQ_COMPARE_TWICE
1006 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
1007 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
1008 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error3
])
1010 /* Start rseq by storing table entry pointer into rseq_cs. */
1011 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
1012 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
1014 "cmpl %[v], %[expect]\n\t"
1015 "jnz %l[cmpfail]\n\t"
1017 "cmpl %[expect2], %[v2]\n\t"
1018 "jnz %l[cmpfail]\n\t"
1020 #ifdef RSEQ_COMPARE_TWICE
1021 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), %l
[error1
])
1022 "cmpl %[v], %[expect]\n\t"
1023 "jnz %l[error2]\n\t"
1024 "cmpl %[expect2], %[v2]\n\t"
1025 "jnz %l[error3]\n\t"
1027 "movl %[newv], %%eax\n\t"
1029 "movl %%eax, %[v]\n\t"
1032 RSEQ_ASM_DEFINE_ABORT(4, "", abort
)
1033 : /* gcc asm goto does not allow outputs */
1034 : [cpu_id
] "r" (cpu
),
1035 [rseq_abi
] "r" (&__rseq_abi
),
1038 [expect2
] "r" (expect2
),
1039 /* final store input */
1041 [expect
] "r" (expect
),
1043 : "memory", "cc", "eax"
1046 #ifdef RSEQ_COMPARE_TWICE
1047 , error1
, error2
, error3
1056 #ifdef RSEQ_COMPARE_TWICE
1058 rseq_bug("cpu_id comparison failed");
1060 rseq_bug("1st expected value comparison failed");
1062 rseq_bug("2nd expected value comparison failed");
1066 /* TODO: implement a faster memcpy. */
1067 static inline __attribute__((always_inline
))
1068 int rseq_cmpeqv_trymemcpy_storev(intptr_t *v
, intptr_t expect
,
1069 void *dst
, void *src
, size_t len
,
1070 intptr_t newv
, int cpu
)
1072 uint32_t rseq_scratch
[3];
1076 __asm__ __volatile__
goto (
1077 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
1078 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
1079 #ifdef RSEQ_COMPARE_TWICE
1080 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
1081 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
1083 "movl %[src], %[rseq_scratch0]\n\t"
1084 "movl %[dst], %[rseq_scratch1]\n\t"
1085 "movl %[len], %[rseq_scratch2]\n\t"
1086 /* Start rseq by storing table entry pointer into rseq_cs. */
1087 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
1088 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
1090 "movl %[expect], %%eax\n\t"
1091 "cmpl %%eax, %[v]\n\t"
1094 #ifdef RSEQ_COMPARE_TWICE
1095 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 6f
)
1096 "movl %[expect], %%eax\n\t"
1097 "cmpl %%eax, %[v]\n\t"
1101 "test %[len], %[len]\n\t" \
1104 "movb (%[src]), %%al\n\t" \
1105 "movb %%al, (%[dst])\n\t" \
1112 "movl %[newv], %%eax\n\t"
1114 "movl %%eax, %[v]\n\t"
1118 "movl %[rseq_scratch2], %[len]\n\t"
1119 "movl %[rseq_scratch1], %[dst]\n\t"
1120 "movl %[rseq_scratch0], %[src]\n\t"
1121 RSEQ_ASM_DEFINE_ABORT(4,
1122 "movl %[rseq_scratch2], %[len]\n\t"
1123 "movl %[rseq_scratch1], %[dst]\n\t"
1124 "movl %[rseq_scratch0], %[src]\n\t",
1126 RSEQ_ASM_DEFINE_CMPFAIL(5,
1127 "movl %[rseq_scratch2], %[len]\n\t"
1128 "movl %[rseq_scratch1], %[dst]\n\t"
1129 "movl %[rseq_scratch0], %[src]\n\t",
1131 #ifdef RSEQ_COMPARE_TWICE
1132 RSEQ_ASM_DEFINE_CMPFAIL(6,
1133 "movl %[rseq_scratch2], %[len]\n\t"
1134 "movl %[rseq_scratch1], %[dst]\n\t"
1135 "movl %[rseq_scratch0], %[src]\n\t",
1137 RSEQ_ASM_DEFINE_CMPFAIL(7,
1138 "movl %[rseq_scratch2], %[len]\n\t"
1139 "movl %[rseq_scratch1], %[dst]\n\t"
1140 "movl %[rseq_scratch0], %[src]\n\t",
1143 : /* gcc asm goto does not allow outputs */
1144 : [cpu_id
] "r" (cpu
),
1145 [rseq_abi
] "r" (&__rseq_abi
),
1146 /* final store input */
1148 [expect
] "m" (expect
),
1150 /* try memcpy input */
1154 [rseq_scratch0
] "m" (rseq_scratch
[0]),
1155 [rseq_scratch1
] "m" (rseq_scratch
[1]),
1156 [rseq_scratch2
] "m" (rseq_scratch
[2])
1157 : "memory", "cc", "eax"
1160 #ifdef RSEQ_COMPARE_TWICE
1170 #ifdef RSEQ_COMPARE_TWICE
1172 rseq_bug("cpu_id comparison failed");
1174 rseq_bug("expected value comparison failed");
1178 /* TODO: implement a faster memcpy. */
1179 static inline __attribute__((always_inline
))
1180 int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v
, intptr_t expect
,
1181 void *dst
, void *src
, size_t len
,
1182 intptr_t newv
, int cpu
)
1184 uint32_t rseq_scratch
[3];
1188 __asm__ __volatile__
goto (
1189 RSEQ_ASM_DEFINE_TABLE(3, 1f
, 2f
, 4f
) /* start, commit, abort */
1190 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[cmpfail
])
1191 #ifdef RSEQ_COMPARE_TWICE
1192 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error1
])
1193 RSEQ_ASM_DEFINE_EXIT_POINT(1f
, %l
[error2
])
1195 "movl %[src], %[rseq_scratch0]\n\t"
1196 "movl %[dst], %[rseq_scratch1]\n\t"
1197 "movl %[len], %[rseq_scratch2]\n\t"
1198 /* Start rseq by storing table entry pointer into rseq_cs. */
1199 RSEQ_ASM_STORE_RSEQ_CS(1, 3b
, RSEQ_CS_OFFSET(%[rseq_abi
]))
1200 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 4f
)
1202 "movl %[expect], %%eax\n\t"
1203 "cmpl %%eax, %[v]\n\t"
1206 #ifdef RSEQ_COMPARE_TWICE
1207 RSEQ_ASM_CMP_CPU_ID(cpu_id
, RSEQ_CPU_ID_OFFSET(%[rseq_abi
]), 6f
)
1208 "movl %[expect], %%eax\n\t"
1209 "cmpl %%eax, %[v]\n\t"
1213 "test %[len], %[len]\n\t" \
1216 "movb (%[src]), %%al\n\t" \
1217 "movb %%al, (%[dst])\n\t" \
1224 "lock; addl $0,-128(%%esp)\n\t"
1225 "movl %[newv], %%eax\n\t"
1227 "movl %%eax, %[v]\n\t"
1231 "movl %[rseq_scratch2], %[len]\n\t"
1232 "movl %[rseq_scratch1], %[dst]\n\t"
1233 "movl %[rseq_scratch0], %[src]\n\t"
1234 RSEQ_ASM_DEFINE_ABORT(4,
1235 "movl %[rseq_scratch2], %[len]\n\t"
1236 "movl %[rseq_scratch1], %[dst]\n\t"
1237 "movl %[rseq_scratch0], %[src]\n\t",
1239 RSEQ_ASM_DEFINE_CMPFAIL(5,
1240 "movl %[rseq_scratch2], %[len]\n\t"
1241 "movl %[rseq_scratch1], %[dst]\n\t"
1242 "movl %[rseq_scratch0], %[src]\n\t",
1244 #ifdef RSEQ_COMPARE_TWICE
1245 RSEQ_ASM_DEFINE_CMPFAIL(6,
1246 "movl %[rseq_scratch2], %[len]\n\t"
1247 "movl %[rseq_scratch1], %[dst]\n\t"
1248 "movl %[rseq_scratch0], %[src]\n\t",
1250 RSEQ_ASM_DEFINE_CMPFAIL(7,
1251 "movl %[rseq_scratch2], %[len]\n\t"
1252 "movl %[rseq_scratch1], %[dst]\n\t"
1253 "movl %[rseq_scratch0], %[src]\n\t",
1256 : /* gcc asm goto does not allow outputs */
1257 : [cpu_id
] "r" (cpu
),
1258 [rseq_abi
] "r" (&__rseq_abi
),
1259 /* final store input */
1261 [expect
] "m" (expect
),
1263 /* try memcpy input */
1267 [rseq_scratch0
] "m" (rseq_scratch
[0]),
1268 [rseq_scratch1
] "m" (rseq_scratch
[1]),
1269 [rseq_scratch2
] "m" (rseq_scratch
[2])
1270 : "memory", "cc", "eax"
1273 #ifdef RSEQ_COMPARE_TWICE
1283 #ifdef RSEQ_COMPARE_TWICE
1285 rseq_bug("cpu_id comparison failed");
1287 rseq_bug("expected value comparison failed");
1291 #endif /* !RSEQ_SKIP_FASTPATH */