]>
Commit | Line | Data |
---|---|---|
b168057a | 1 | /* Copyright (C) 2003-2015 Free Software Foundation, Inc. |
02a9f771 DJ |
2 | This file is part of the GNU C Library. |
3 | Contributed by Jakub Jelinek <jakub@redhat.com>. | |
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 License as | |
7 | published by the Free Software Foundation; either version 2.1 of the | |
8 | 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 | |
ab84e3ff PE |
16 | License along with the GNU C Library. If not, see |
17 | <http://www.gnu.org/licenses/>. */ | |
02a9f771 DJ |
18 | |
19 | #include <dlfcn.h> | |
20 | #include <stdio.h> | |
21 | #include <unwind.h> | |
22 | #include <pthreadP.h> | |
23 | ||
f281f9cf | 24 | static void *libgcc_s_handle; |
175cef41 JM |
25 | static void (*libgcc_s_resume) (struct _Unwind_Exception *exc) |
26 | __attribute_used__; | |
02a9f771 DJ |
27 | static _Unwind_Reason_Code (*libgcc_s_personality) |
28 | (_Unwind_State, struct _Unwind_Exception *, struct _Unwind_Context *); | |
29 | static _Unwind_Reason_Code (*libgcc_s_forcedunwind) | |
30 | (struct _Unwind_Exception *, _Unwind_Stop_Fn, void *); | |
31 | static _Unwind_Word (*libgcc_s_getcfa) (struct _Unwind_Context *); | |
32 | ||
33 | void | |
8323b1ab | 34 | __attribute_noinline__ |
02a9f771 DJ |
35 | pthread_cancel_init (void) |
36 | { | |
37 | void *resume, *personality, *forcedunwind, *getcfa; | |
38 | void *handle; | |
39 | ||
f281f9cf | 40 | if (__builtin_expect (libgcc_s_handle != NULL, 1)) |
8323b1ab DJ |
41 | { |
42 | /* Force gcc to reload all values. */ | |
43 | asm volatile ("" ::: "memory"); | |
44 | return; | |
45 | } | |
02a9f771 DJ |
46 | |
47 | handle = __libc_dlopen ("libgcc_s.so.1"); | |
48 | ||
49 | if (handle == NULL | |
50 | || (resume = __libc_dlsym (handle, "_Unwind_Resume")) == NULL | |
51 | || (personality = __libc_dlsym (handle, "__gcc_personality_v0")) == NULL | |
52 | || (forcedunwind = __libc_dlsym (handle, "_Unwind_ForcedUnwind")) | |
53 | == NULL | |
54 | || (getcfa = __libc_dlsym (handle, "_Unwind_GetCFA")) == NULL | |
55 | #ifdef ARCH_CANCEL_INIT | |
56 | || ARCH_CANCEL_INIT (handle) | |
57 | #endif | |
58 | ) | |
59 | __libc_fatal ("libgcc_s.so.1 must be installed for pthread_cancel to work\n"); | |
60 | ||
61 | libgcc_s_resume = resume; | |
62 | libgcc_s_personality = personality; | |
63 | libgcc_s_forcedunwind = forcedunwind; | |
f281f9cf | 64 | libgcc_s_getcfa = getcfa; |
8323b1ab DJ |
65 | /* Make sure libgcc_s_getcfa is written last. Otherwise, |
66 | pthread_cancel_init might return early even when the pointer the | |
67 | caller is interested in is not initialized yet. */ | |
68 | atomic_write_barrier (); | |
f281f9cf JM |
69 | libgcc_s_handle = handle; |
70 | } | |
71 | ||
72 | void | |
73 | __libc_freeres_fn_section | |
74 | __unwind_freeres (void) | |
75 | { | |
76 | void *handle = libgcc_s_handle; | |
77 | if (handle != NULL) | |
78 | { | |
79 | libgcc_s_handle = NULL; | |
80 | __libc_dlclose (handle); | |
81 | } | |
02a9f771 DJ |
82 | } |
83 | ||
84 | /* It's vitally important that _Unwind_Resume not have a stack frame; the | |
85 | ARM unwinder relies on register state at entrance. So we write this in | |
86 | assembly. */ | |
87 | ||
783a65c2 RH |
88 | #define STR1(S) #S |
89 | #define STR(S) STR1(S) | |
90 | ||
02a9f771 DJ |
91 | asm ( |
92 | " .globl _Unwind_Resume\n" | |
93 | " .type _Unwind_Resume, %function\n" | |
94 | "_Unwind_Resume:\n" | |
01b32e73 TS |
95 | " .cfi_sections .debug_frame\n" |
96 | " " CFI_STARTPROC "\n" | |
55668624 | 97 | " push {r4, r5, r6, lr}\n" |
01b32e73 TS |
98 | " " CFI_ADJUST_CFA_OFFSET (16)" \n" |
99 | " " CFI_REL_OFFSET (r4, 0) "\n" | |
100 | " " CFI_REL_OFFSET (r5, 4) "\n" | |
101 | " " CFI_REL_OFFSET (r6, 8) "\n" | |
102 | " " CFI_REL_OFFSET (lr, 12) "\n" | |
103 | " " CFI_REMEMBER_STATE "\n" | |
02a9f771 DJ |
104 | " ldr r4, 1f\n" |
105 | " ldr r5, 2f\n" | |
106 | "3: add r4, pc, r4\n" | |
107 | " ldr r3, [r4, r5]\n" | |
108 | " mov r6, r0\n" | |
109 | " cmp r3, #0\n" | |
110 | " beq 4f\n" | |
111 | "5: mov r0, r6\n" | |
55668624 | 112 | " pop {r4, r5, r6, lr}\n" |
01b32e73 TS |
113 | " " CFI_ADJUST_CFA_OFFSET (-16) "\n" |
114 | " " CFI_RESTORE (r4) "\n" | |
115 | " " CFI_RESTORE (r5) "\n" | |
116 | " " CFI_RESTORE (r6) "\n" | |
117 | " " CFI_RESTORE (lr) "\n" | |
02a9f771 | 118 | " bx r3\n" |
01b32e73 | 119 | " " CFI_RESTORE_STATE "\n" |
02a9f771 DJ |
120 | "4: bl pthread_cancel_init\n" |
121 | " ldr r3, [r4, r5]\n" | |
122 | " b 5b\n" | |
01b32e73 | 123 | " " CFI_ENDPROC "\n" |
5631abde | 124 | " .align 2\n" |
783a65c2 | 125 | "1: .word _GLOBAL_OFFSET_TABLE_ - 3b - " STR (PC_OFS) "\n" |
02a9f771 DJ |
126 | "2: .word libgcc_s_resume(GOTOFF)\n" |
127 | " .size _Unwind_Resume, .-_Unwind_Resume\n" | |
128 | ); | |
129 | ||
130 | _Unwind_Reason_Code | |
131 | __gcc_personality_v0 (_Unwind_State state, | |
132 | struct _Unwind_Exception *ue_header, | |
133 | struct _Unwind_Context *context) | |
134 | { | |
135 | if (__builtin_expect (libgcc_s_personality == NULL, 0)) | |
136 | pthread_cancel_init (); | |
8323b1ab | 137 | |
02a9f771 DJ |
138 | return libgcc_s_personality (state, ue_header, context); |
139 | } | |
140 | ||
141 | _Unwind_Reason_Code | |
142 | _Unwind_ForcedUnwind (struct _Unwind_Exception *exc, _Unwind_Stop_Fn stop, | |
143 | void *stop_argument) | |
144 | { | |
145 | if (__builtin_expect (libgcc_s_forcedunwind == NULL, 0)) | |
146 | pthread_cancel_init (); | |
8323b1ab | 147 | |
02a9f771 DJ |
148 | return libgcc_s_forcedunwind (exc, stop, stop_argument); |
149 | } | |
150 | ||
151 | _Unwind_Word | |
152 | _Unwind_GetCFA (struct _Unwind_Context *context) | |
153 | { | |
154 | if (__builtin_expect (libgcc_s_getcfa == NULL, 0)) | |
155 | pthread_cancel_init (); | |
8323b1ab | 156 | |
02a9f771 DJ |
157 | return libgcc_s_getcfa (context); |
158 | } |