1 /* Support for GNU properties. x86 version.
2 Copyright (C) 2018-2022 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 #ifndef _DL_PROP_H
20 #define _DL_PROP_H
21
22 #include <libintl.h>
23
24 extern void _dl_cet_check (struct link_map *, const char *)
25 attribute_hidden;
26 extern void _dl_cet_open_check (struct link_map *)
27 attribute_hidden;
28
29 static void
dl_isa_level_check(struct link_map * m,const char * program)30 dl_isa_level_check (struct link_map *m, const char *program)
31 {
32 const struct cpu_features *cpu_features = __get_cpu_features ();
33 unsigned int i;
34 struct link_map *l;
35
36 i = m->l_searchlist.r_nlist;
37 while (i-- > 0)
38 {
39 /* Check each shared object to see if ISA level is compatible. */
40 l = m->l_initfini[i];
41
42 /* Skip ISA level check if functions have been executed. */
43 if (l->l_init_called)
44 continue;
45
46 #ifdef SHARED
47 /* Skip ISA level check for ld.so since ld.so won't run if its ISA
48 level is higher than CPU. */
49 if (l == &GL(dl_rtld_map) || l->l_real == &GL(dl_rtld_map))
50 continue;
51 #endif
52
53 if ((l->l_x86_isa_1_needed & cpu_features->isa_1)
54 != l->l_x86_isa_1_needed)
55 {
56 if (program)
57 _dl_fatal_printf ("%s: CPU ISA level is lower than required\n",
58 *l->l_name != '\0' ? l->l_name : program);
59 else
60 _dl_signal_error (0, l->l_name, "dlopen",
61 N_("CPU ISA level is lower than required"));
62 }
63 }
64 }
65
66 static inline void __attribute__ ((always_inline))
_rtld_main_check(struct link_map * m,const char * program)67 _rtld_main_check (struct link_map *m, const char *program)
68 {
69 dl_isa_level_check (m, program);
70 #if CET_ENABLED
71 _dl_cet_check (m, program);
72 #endif
73 }
74
75 static inline void __attribute__ ((always_inline))
_dl_open_check(struct link_map * m)76 _dl_open_check (struct link_map *m)
77 {
78 dl_isa_level_check (m, NULL);
79 #if CET_ENABLED
80 _dl_cet_open_check (m);
81 #endif
82 }
83
84 static inline void __attribute__ ((unused))
_dl_process_property_note(struct link_map * l,const ElfW (Nhdr)* note,const ElfW (Addr)size,const ElfW (Addr)align)85 _dl_process_property_note (struct link_map *l, const ElfW(Nhdr) *note,
86 const ElfW(Addr) size, const ElfW(Addr) align)
87 {
88 /* Skip if we have seen a NT_GNU_PROPERTY_TYPE_0 note before. */
89 if (l->l_property != lc_property_unknown)
90 return;
91
92 /* The NT_GNU_PROPERTY_TYPE_0 note must be aliged to 4 bytes in
93 32-bit objects and to 8 bytes in 64-bit objects. Skip notes
94 with incorrect alignment. */
95 if (align != (__ELF_NATIVE_CLASS / 8))
96 return;
97
98 const ElfW(Addr) start = (ElfW(Addr)) note;
99
100 unsigned int needed_1 = 0;
101 unsigned int feature_1_and = 0;
102 unsigned int isa_1_needed = 0;
103 unsigned int last_type = 0;
104
105 while ((ElfW(Addr)) (note + 1) - start < size)
106 {
107 /* Find the NT_GNU_PROPERTY_TYPE_0 note. */
108 if (note->n_namesz == 4
109 && note->n_type == NT_GNU_PROPERTY_TYPE_0
110 && memcmp (note + 1, "GNU", 4) == 0)
111 {
112 /* Stop if we see more than one GNU property note which may
113 be generated by the older linker. */
114 if (l->l_property != lc_property_unknown)
115 return;
116
117 /* Check CET status and ISA levels now. */
118 l->l_property = lc_property_none;
119
120 /* Check for invalid property. */
121 if (note->n_descsz < 8
122 || (note->n_descsz % sizeof (ElfW(Addr))) != 0)
123 return;
124
125 /* Start and end of property array. */
126 unsigned char *ptr = (unsigned char *) (note + 1) + 4;
127 unsigned char *ptr_end = ptr + note->n_descsz;
128
129 do
130 {
131 unsigned int type = *(unsigned int *) ptr;
132 unsigned int datasz = *(unsigned int *) (ptr + 4);
133
134 /* Property type must be in ascending order. */
135 if (type < last_type)
136 return;
137
138 ptr += 8;
139 if ((ptr + datasz) > ptr_end)
140 return;
141
142 last_type = type;
143
144 if (type == GNU_PROPERTY_X86_FEATURE_1_AND
145 || type == GNU_PROPERTY_X86_ISA_1_NEEDED
146 || type == GNU_PROPERTY_1_NEEDED)
147 {
148 /* The sizes of types which we are searching for are
149 4 bytes. There is no point to continue if this
150 note is ill-formed. */
151 if (datasz != 4)
152 return;
153
154 /* NB: Stop the scan only after seeing all types which
155 we are searching for. */
156 _Static_assert (((GNU_PROPERTY_X86_ISA_1_NEEDED
157 > GNU_PROPERTY_X86_FEATURE_1_AND)
158 && (GNU_PROPERTY_X86_FEATURE_1_AND
159 > GNU_PROPERTY_1_NEEDED)),
160 "GNU_PROPERTY_X86_ISA_1_NEEDED > "
161 "GNU_PROPERTY_X86_FEATURE_1_AND && "
162 "GNU_PROPERTY_X86_FEATURE_1_AND > "
163 "GNU_PROPERTY_1_NEEDED");
164 if (type == GNU_PROPERTY_X86_FEATURE_1_AND)
165 feature_1_and = *(unsigned int *) ptr;
166 else if (type == GNU_PROPERTY_1_NEEDED)
167 needed_1 = *(unsigned int *) ptr;
168 else
169 {
170 isa_1_needed = *(unsigned int *) ptr;
171
172 /* Keep searching for the next GNU property note
173 generated by the older linker. */
174 break;
175 }
176 }
177 else if (type > GNU_PROPERTY_X86_ISA_1_NEEDED)
178 {
179 /* Stop the scan since property type is in ascending
180 order. */
181 break;
182 }
183
184 /* Check the next property item. */
185 ptr += ALIGN_UP (datasz, sizeof (ElfW(Addr)));
186 }
187 while ((ptr_end - ptr) >= 8);
188 }
189
190 /* NB: Note sections like .note.ABI-tag and .note.gnu.build-id are
191 aligned to 4 bytes in 64-bit ELF objects. */
192 note = ((const void *) note
193 + ELF_NOTE_NEXT_OFFSET (note->n_namesz, note->n_descsz,
194 align));
195 }
196
197 /* We get here only if there is one or no GNU property note. */
198 if (needed_1 != 0 || isa_1_needed != 0 || feature_1_and != 0)
199 {
200 l->l_property = lc_property_valid;
201 l->l_1_needed = needed_1;
202 l->l_x86_isa_1_needed = isa_1_needed;
203 l->l_x86_feature_1_and = feature_1_and;
204 }
205 else
206 l->l_property = lc_property_none;
207 }
208
209 static inline void __attribute__ ((unused))
_dl_process_pt_note(struct link_map * l,int fd,const ElfW (Phdr)* ph)210 _dl_process_pt_note (struct link_map *l, int fd, const ElfW(Phdr) *ph)
211 {
212 const ElfW(Nhdr) *note = (const void *) (ph->p_vaddr + l->l_addr);
213 _dl_process_property_note (l, note, ph->p_memsz, ph->p_align);
214 }
215
216 static inline int __attribute__ ((always_inline))
_dl_process_gnu_property(struct link_map * l,int fd,uint32_t type,uint32_t datasz,void * data)217 _dl_process_gnu_property (struct link_map *l, int fd, uint32_t type,
218 uint32_t datasz, void *data)
219 {
220 return 0;
221 }
222
223 #endif /* _DL_PROP_H */
224