/* Support for GNU properties. x86 version. Copyright (C) 2018-2024 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see . */ #ifndef _DL_PROP_H #define _DL_PROP_H #include extern void _dl_cet_check (struct link_map *, const char *) attribute_hidden; extern void _dl_cet_open_check (struct link_map *) attribute_hidden; static void dl_isa_level_check (struct link_map *m, const char *program) { const struct cpu_features *cpu_features = __get_cpu_features (); unsigned int i; struct link_map *l; i = m->l_searchlist.r_nlist; while (i-- > 0) { /* Check each shared object to see if ISA level is compatible. */ l = m->l_initfini[i]; /* Skip ISA level check if functions have been executed. */ if (l->l_init_called) continue; #ifdef SHARED /* Skip ISA level check for ld.so since ld.so won't run if its ISA level is higher than CPU. */ if (l == &GL(dl_rtld_map) || l->l_real == &GL(dl_rtld_map)) continue; #endif if ((l->l_x86_isa_1_needed & cpu_features->isa_1) != l->l_x86_isa_1_needed) { if (program) _dl_fatal_printf ("%s: CPU ISA level is lower than required\n", *l->l_name != '\0' ? l->l_name : program); else _dl_signal_error (0, l->l_name, "dlopen", N_("CPU ISA level is lower than required")); } } } static inline void __attribute__ ((always_inline)) _rtld_main_check (struct link_map *m, const char *program) { dl_isa_level_check (m, program); #if CET_ENABLED _dl_cet_check (m, program); #endif } static inline void __attribute__ ((always_inline)) _dl_open_check (struct link_map *m) { dl_isa_level_check (m, NULL); #if CET_ENABLED _dl_cet_open_check (m); #endif } /* Check the GNU property and return its value. It returns: -1: Skip this note. 0: Stop checking. 1: Continue to check. */ static inline int _dl_check_gnu_property (unsigned int type, unsigned int datasz, void *ptr, unsigned int *feature_1_and, unsigned int *needed_1, unsigned int *isa_1_needed) { if (type == GNU_PROPERTY_X86_FEATURE_1_AND || type == GNU_PROPERTY_X86_ISA_1_NEEDED || type == GNU_PROPERTY_1_NEEDED) { /* The sizes of types which we are searching for are 4 bytes. There is no point to continue if this note is ill-formed. */ if (datasz != 4) return -1; /* NB: Stop the scan only after seeing all types which we are searching for. */ _Static_assert (((GNU_PROPERTY_X86_ISA_1_NEEDED > GNU_PROPERTY_X86_FEATURE_1_AND) && (GNU_PROPERTY_X86_FEATURE_1_AND > GNU_PROPERTY_1_NEEDED)), "GNU_PROPERTY_X86_ISA_1_NEEDED > " "GNU_PROPERTY_X86_FEATURE_1_AND && " "GNU_PROPERTY_X86_FEATURE_1_AND > " "GNU_PROPERTY_1_NEEDED"); if (type == GNU_PROPERTY_X86_FEATURE_1_AND) *feature_1_and = *(unsigned int *) ptr; else if (type == GNU_PROPERTY_1_NEEDED) *needed_1 = *(unsigned int *) ptr; else { *isa_1_needed = *(unsigned int *) ptr; /* Keep searching for the next GNU property note generated by the older linker. */ return 0; } } else if (type > GNU_PROPERTY_X86_ISA_1_NEEDED) { /* Stop the scan since property type is in ascending order. */ return 0; } return 1; } 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) { /* Skip if we have seen a NT_GNU_PROPERTY_TYPE_0 note before. */ if (l->l_property != lc_property_unknown) return; /* The NT_GNU_PROPERTY_TYPE_0 note must be aligned to 4 bytes in 32-bit objects and to 8 bytes in 64-bit objects. Skip notes with incorrect alignment. */ if (align != (__ELF_NATIVE_CLASS / 8)) return; const ElfW(Addr) start = (ElfW(Addr)) note; unsigned int needed_1 = 0; unsigned int feature_1_and = 0; unsigned int isa_1_needed = 0; unsigned int last_type = 0; while ((ElfW(Addr)) (note + 1) - start < size) { /* Find the NT_GNU_PROPERTY_TYPE_0 note. */ if (note->n_namesz == 4 && note->n_type == NT_GNU_PROPERTY_TYPE_0 && memcmp (note + 1, "GNU", 4) == 0) { /* Stop if we see more than one GNU property note which may be generated by the older linker. */ if (l->l_property != lc_property_unknown) return; /* Check CET status and ISA levels now. */ l->l_property = lc_property_none; /* Check for invalid property. */ if (note->n_descsz < 8 || (note->n_descsz % sizeof (ElfW(Addr))) != 0) return; /* Start and end of property array. */ unsigned char *ptr = (unsigned char *) (note + 1) + 4; unsigned char *ptr_end = ptr + note->n_descsz; do { unsigned int type = *(unsigned int *) ptr; unsigned int datasz = *(unsigned int *) (ptr + 4); /* Property type must be in ascending order. */ if (type < last_type) return; ptr += 8; if ((ptr + datasz) > ptr_end) return; last_type = type; int result = _dl_check_gnu_property (type, datasz, ptr, &feature_1_and, &needed_1, &isa_1_needed); if (result == -1) return; /* Skip this note. */ else if (result == 0) break; /* Stop checking. */ /* Check the next property item. */ ptr += ALIGN_UP (datasz, sizeof (ElfW(Addr))); } while ((ptr_end - ptr) >= 8); } /* NB: Note sections like .note.ABI-tag and .note.gnu.build-id are aligned to 4 bytes in 64-bit ELF objects. */ note = ((const void *) note + ELF_NOTE_NEXT_OFFSET (note->n_namesz, note->n_descsz, align)); } /* We get here only if there is one or no GNU property note. */ if (needed_1 != 0 || isa_1_needed != 0 || feature_1_and != 0) { l->l_property = lc_property_valid; l->l_1_needed = needed_1; l->l_x86_isa_1_needed = isa_1_needed; l->l_x86_feature_1_and = feature_1_and; } else l->l_property = lc_property_none; } static inline void __attribute__ ((unused)) _dl_process_pt_note (struct link_map *l, int fd, const ElfW(Phdr) *ph) { const ElfW(Nhdr) *note = (const void *) (ph->p_vaddr + l->l_addr); _dl_process_property_note (l, note, ph->p_memsz, ph->p_align); } static inline int __attribute__ ((always_inline)) _dl_process_gnu_property (struct link_map *l, int fd, uint32_t type, uint32_t datasz, void *data) { /* This is called on each GNU property. */ unsigned int needed_1 = 0; unsigned int feature_1_and = 0; unsigned int isa_1_needed = 0; int result = _dl_check_gnu_property (type, datasz, data, &feature_1_and, &needed_1, &isa_1_needed); if (needed_1 != 0) l->l_1_needed = needed_1; if (isa_1_needed != 0) l->l_x86_isa_1_needed = isa_1_needed; if (feature_1_and != 0) l->l_x86_feature_1_and = feature_1_and; if ((needed_1 | isa_1_needed | feature_1_and) != 0) l->l_property = lc_property_valid; else if (l->l_property == lc_property_unknown) l->l_property = lc_property_none; return result <= 0 ? 0 : result; } #endif /* _DL_PROP_H */