diff options
29 files changed, 478 insertions, 65 deletions
diff --git a/libdwelf/ChangeLog b/libdwelf/ChangeLog index a3326556..ba921347 100644 --- a/libdwelf/ChangeLog +++ b/libdwelf/ChangeLog @@ -1,3 +1,8 @@ +2018-10-18 Mark Wielaard <mark@klomp.org> + + * dwelf_elf_gnu_build_id.c (find_elf_build_id): Check p_align to + set ELF type. + 2015-10-11 Akihiko Odaki <akihiko.odaki.4i@stu.hosei.ac.jp> * dwelf_strtab.c: Remove sys/param.h include. diff --git a/libdwelf/dwelf_elf_gnu_build_id.c b/libdwelf/dwelf_elf_gnu_build_id.c index 8c78c700..dbcfc829 100644 --- a/libdwelf/dwelf_elf_gnu_build_id.c +++ b/libdwelf/dwelf_elf_gnu_build_id.c @@ -88,7 +88,9 @@ find_elf_build_id (Dwfl_Module *mod, int e_type, Elf *elf, result = check_notes (elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz, - ELF_T_NHDR), + (phdr->p_align == 8 + ? ELF_T_NHDR8 + : ELF_T_NHDR)), phdr->p_vaddr, build_id_bits, build_id_elfaddr, diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 6c333d83..9e7bb316 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,13 @@ +2018-10-18 Mark Wielaard <mark@klomp.org> + + * dwfl_segment_report_module.c (consider_note): Take align as new + argument. Use align to set d_type and calculate padding. + (dwfl_segment_report_module): Pass align to consider_notes. + * core-file.c (dwfl_core_file_report): Check p_align to set ELF + type. + * linux-kernel-modules.c (check_notes): Check name and type of note + to determine padding. + 2018-10-19 Mark Wielaard <mark@klomp.org> * dwfl_module_getdwarf.c (find_aux_sym): Check sh_entsize is not zero. diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c index 84cb89ac..01109f4b 100644 --- a/libdwfl/core-file.c +++ b/libdwfl/core-file.c @@ -496,7 +496,9 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable) Elf_Data *notes = elf_getdata_rawchunk (elf, notes_phdr.p_offset, notes_phdr.p_filesz, - ELF_T_NHDR); + (notes_phdr.p_align == 8 + ? ELF_T_NHDR8 + : ELF_T_NHDR)); if (likely (notes != NULL)) { size_t pos = 0; diff --git a/libdwfl/dwfl_segment_report_module.c b/libdwfl/dwfl_segment_report_module.c index 87498846..0d633ffe 100644 --- a/libdwfl/dwfl_segment_report_module.c +++ b/libdwfl/dwfl_segment_report_module.c @@ -27,7 +27,7 @@ not, see <http://www.gnu.org/licenses/>. */ #include <config.h> -#include "../libelf/libelfP.h" /* For NOTE_ALIGN. */ +#include "../libelf/libelfP.h" /* For NOTE_ALIGN4 and NOTE_ALIGN8. */ #undef _ #include "libdwflP.h" #include "common.h" @@ -451,7 +451,8 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, GElf_Addr build_id_vaddr = 0; /* Consider a PT_NOTE we've found in the image. */ - inline void consider_notes (GElf_Addr vaddr, GElf_Xword filesz) + inline void consider_notes (GElf_Addr vaddr, GElf_Xword filesz, + GElf_Xword align) { /* If we have already seen a build ID, we don't care any more. */ if (build_id != NULL || filesz == 0) @@ -478,7 +479,8 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, notes = malloc (filesz); if (unlikely (notes == NULL)) return; - xlatefrom.d_type = xlateto.d_type = ELF_T_NHDR; + xlatefrom.d_type = xlateto.d_type = (align == 8 + ? ELF_T_NHDR8 : ELF_T_NHDR); xlatefrom.d_buf = (void *) data; xlatefrom.d_size = filesz; xlateto.d_buf = notes; @@ -489,15 +491,23 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, } const GElf_Nhdr *nh = notes; - while ((const void *) nh < (const void *) notes + filesz) - { - const void *note_name = nh + 1; - const void *note_desc = note_name + NOTE_ALIGN (nh->n_namesz); - if (unlikely ((size_t) ((const void *) notes + filesz - - note_desc) < nh->n_descsz)) + size_t len = 0; + while (filesz > len + sizeof (*nh)) + { + const void *note_name; + const void *note_desc; + + len += sizeof (*nh); + note_name = notes + len; + + len += nh->n_namesz; + len = align == 8 ? NOTE_ALIGN8 (len) : NOTE_ALIGN4 (len); + note_desc = notes + len; + + if (unlikely (filesz < len + nh->n_descsz)) break; - if (nh->n_type == NT_GNU_BUILD_ID + if (nh->n_type == NT_GNU_BUILD_ID && nh->n_descsz > 0 && nh->n_namesz == sizeof "GNU" && !memcmp (note_name, "GNU", sizeof "GNU")) @@ -510,7 +520,9 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, break; } - nh = note_desc + NOTE_ALIGN (nh->n_descsz); + len += nh->n_descsz; + len = align == 8 ? NOTE_ALIGN8 (len) : NOTE_ALIGN4 (len); + nh = (void *) notes + len; } done: @@ -535,7 +547,7 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, case PT_NOTE: /* We calculate from the p_offset of the note segment, because we don't yet know the bias for its p_vaddr. */ - consider_notes (start + offset, filesz); + consider_notes (start + offset, filesz, align); break; case PT_LOAD: diff --git a/libdwfl/linux-core-attach.c b/libdwfl/linux-core-attach.c index 9f05f72a..6c99b9e2 100644 --- a/libdwfl/linux-core-attach.c +++ b/libdwfl/linux-core-attach.c @@ -355,7 +355,9 @@ dwfl_core_file_attach (Dwfl *dwfl, Elf *core) if (phdr != NULL && phdr->p_type == PT_NOTE) { note_data = elf_getdata_rawchunk (core, phdr->p_offset, - phdr->p_filesz, ELF_T_NHDR); + phdr->p_filesz, (phdr->p_align == 8 + ? ELF_T_NHDR8 + : ELF_T_NHDR)); break; } } diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c index 9d0fef2c..360e4ee9 100644 --- a/libdwfl/linux-kernel-modules.c +++ b/libdwfl/linux-kernel-modules.c @@ -39,6 +39,7 @@ #include <config.h> #include <system.h> +#include "libelfP.h" #include "libdwflP.h" #include <inttypes.h> #include <errno.h> @@ -554,15 +555,41 @@ check_notes (Dwfl_Module *mod, const char *notesfile, return 1; unsigned char *p = buf.data; + size_t len = 0; while (p < &buf.data[n]) { /* No translation required since we are reading the native kernel. */ GElf_Nhdr *nhdr = (void *) p; - p += sizeof *nhdr; + len += sizeof *nhdr; + p += len; unsigned char *name = p; - p += (nhdr->n_namesz + 3) & -4U; - unsigned char *bits = p; - p += (nhdr->n_descsz + 3) & -4U; + unsigned char *bits; + /* This is somewhat ugly, GNU Property notes use different padding, + but all we have is the file content, so we have to actually check + the name and type. */ + if (nhdr->n_type == NT_GNU_PROPERTY_TYPE_0 + && nhdr->n_namesz == sizeof "GNU" + && name + nhdr->n_namesz < &buf.data[n] + && !memcmp (name, "GNU", sizeof "GNU")) + { + len += nhdr->n_namesz; + len = NOTE_ALIGN8 (len); + p = buf.data + len; + bits = p; + len += nhdr->n_descsz; + len = NOTE_ALIGN8 (len); + p = buf.data + len; + } + else + { + len += nhdr->n_namesz; + len = NOTE_ALIGN4 (len); + p = buf.data + len; + bits = p; + len += nhdr->n_descsz; + len = NOTE_ALIGN4 (len); + p = buf.data + len; + } if (p <= &buf.data[n] && nhdr->n_type == NT_GNU_BUILD_ID diff --git a/libebl/ChangeLog b/libebl/ChangeLog index aec848b9..120c84c0 100644 --- a/libebl/ChangeLog +++ b/libebl/ChangeLog @@ -1,3 +1,9 @@ +2018-10-18 Mark Wielaard <mark@klomp.org> + + * eblobjnote.c (ebl_object_note): Handle NT_GNU_PROPERTY_TYPE_0. + * eblobjnotetypename.c (ebl_object_note_type_name): Add + GNU_PROPERTY_TYPE_0. + 2018-10-02 Andreas Schwab <schwab@suse.de> * ebl-hooks.h (EBLHOOK(reloc_simple_type)): Add third parameter. diff --git a/libebl/eblobjnote.c b/libebl/eblobjnote.c index ca4f155d..57e9f52f 100644 --- a/libebl/eblobjnote.c +++ b/libebl/eblobjnote.c @@ -1,5 +1,5 @@ /* Print contents of object file note. - Copyright (C) 2002, 2007, 2009, 2011, 2015, 2016 Red Hat, Inc. + Copyright (C) 2002, 2007, 2009, 2011, 2015, 2016, 2018 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2002. @@ -37,6 +37,8 @@ #include <string.h> #include <libeblP.h> +#include "libelfP.h" + void ebl_object_note (Ebl *ebl, const char *name, uint32_t type, @@ -153,6 +155,187 @@ ebl_object_note (Ebl *ebl, const char *name, uint32_t type, (int) descsz, desc); break; + case NT_GNU_PROPERTY_TYPE_0: + if (strcmp (name, "GNU") == 0 && descsz > 0) + { + /* There are at least 2 words. type and datasz. */ + while (descsz >= 8) + { + struct pr_prop + { + GElf_Word pr_type; + GElf_Word pr_datasz; + } prop; + + Elf_Data in = + { + .d_version = EV_CURRENT, + .d_type = ELF_T_WORD, + .d_size = 8, + .d_buf = (void *) desc + }; + Elf_Data out = + { + .d_version = EV_CURRENT, + .d_type = ELF_T_WORD, + .d_size = descsz, + .d_buf = (void *) &prop + }; + + if (gelf_xlatetom (ebl->elf, &out, &in, + elf_getident (ebl->elf, + NULL)[EI_DATA]) == NULL) + { + printf ("%s\n", elf_errmsg (-1)); + return; + } + + desc += 8; + descsz -= 8; + + int elfclass = gelf_getclass (ebl->elf); + char *elfident = elf_getident (ebl->elf, NULL); + GElf_Ehdr ehdr; + gelf_getehdr (ebl->elf, &ehdr); + + /* Prefix. */ + printf (" "); + if (prop.pr_type == GNU_PROPERTY_STACK_SIZE) + { + printf ("STACK_SIZE "); + if (prop.pr_datasz == 4 || prop.pr_datasz == 8) + { + GElf_Addr addr; + in.d_type = ELF_T_ADDR; + out.d_type = ELF_T_ADDR; + in.d_size = prop.pr_datasz; + out.d_size = sizeof (addr); + in.d_buf = (void *) desc; + out.d_buf = (void *) &addr; + + if (gelf_xlatetom (ebl->elf, &out, &in, + elfident[EI_DATA]) == NULL) + { + printf ("%s\n", elf_errmsg (-1)); + return; + } + printf ("%#" PRIx64 "\n", addr); + } + else + printf (" (garbage datasz: %" PRIx32 ")\n", + prop.pr_datasz); + } + else if (prop.pr_type == GNU_PROPERTY_NO_COPY_ON_PROTECTED) + { + printf ("NO_COPY_ON_PROTECTION"); + if (prop.pr_datasz == 0) + printf ("\n"); + else + printf (" (garbage datasz: %" PRIx32 ")\n", + prop.pr_datasz); + } + else if (prop.pr_type >= GNU_PROPERTY_LOPROC + && prop.pr_type <= GNU_PROPERTY_HIPROC + && (ehdr.e_machine == EM_386 + || ehdr.e_machine == EM_X86_64)) + { + printf ("X86 "); + if (prop.pr_type == GNU_PROPERTY_X86_FEATURE_1_AND) + { + printf ("FEATURE_1_AND: "); + + if (prop.pr_datasz == 4) + { + GElf_Word data; + in.d_type = ELF_T_WORD; + out.d_type = ELF_T_WORD; + in.d_size = 4; + out.d_size = 4; + in.d_buf = (void *) desc; + out.d_buf = (void *) &data; + + if (gelf_xlatetom (ebl->elf, &out, &in, + elfident[EI_DATA]) == NULL) + { + printf ("%s\n", elf_errmsg (-1)); + return; + } + printf ("%08" PRIx32 " ", data); + + if ((data & GNU_PROPERTY_X86_FEATURE_1_IBT) + != 0) + { + printf ("IBT"); + data &= ~GNU_PROPERTY_X86_FEATURE_1_IBT; + if (data != 0) + printf (" "); + } + + if ((data & GNU_PROPERTY_X86_FEATURE_1_SHSTK) + != 0) + { + printf ("SHSTK"); + data &= ~GNU_PROPERTY_X86_FEATURE_1_SHSTK; + if (data != 0) + printf (" "); + } + + if (data != 0) + printf ("UNKNOWN"); + } + else + printf ("<bad datasz: %" PRId32 ">", + prop.pr_datasz); + + printf ("\n"); + } + else + { + printf ("%#" PRIx32, prop.pr_type); + if (prop.pr_datasz > 0) + { + printf (" data: "); + size_t i; + for (i = 0; i < prop.pr_datasz - 1; i++) + printf ("%02" PRIx8 " ", (uint8_t) desc[i]); + printf ("%02" PRIx8 "\n", (uint8_t) desc[i]); + } + } + } + else + { + if (prop.pr_type >= GNU_PROPERTY_LOPROC + && prop.pr_type <= GNU_PROPERTY_HIPROC) + printf ("proc_type %#" PRIx32, prop.pr_type); + else if (prop.pr_type >= GNU_PROPERTY_LOUSER + && prop.pr_type <= GNU_PROPERTY_HIUSER) + printf ("app_type %#" PRIx32, prop.pr_type); + else + printf ("unknown_type %#" PRIx32, prop.pr_type); + + if (prop.pr_datasz > 0) + { + printf (" data: "); + size_t i; + for (i = 0; i < prop.pr_datasz - 1; i++) + printf ("%02" PRIx8 " ", (uint8_t) desc[i]); + printf ("%02" PRIx8 "\n", (uint8_t) desc[i]); + } + } + if (elfclass == ELFCLASS32) + { + desc += NOTE_ALIGN4 (prop.pr_datasz); + descsz -= NOTE_ALIGN4 (prop.pr_datasz); + } + else + { + desc += NOTE_ALIGN8 (prop.pr_datasz); + descsz -= NOTE_ALIGN8 (prop.pr_datasz); + } + } + } + break; + case NT_GNU_ABI_TAG: if (strcmp (name, "GNU") == 0 && descsz >= 8 && descsz % 4 == 0) { diff --git a/libebl/eblobjnotetypename.c b/libebl/eblobjnotetypename.c index db040d29..af23caea 100644 --- a/libebl/eblobjnotetypename.c +++ b/libebl/eblobjnotetypename.c @@ -91,6 +91,7 @@ ebl_object_note_type_name (Ebl *ebl, const char *name, uint32_t type, KNOWNSTYPE (GNU_HWCAP), KNOWNSTYPE (GNU_BUILD_ID), KNOWNSTYPE (GNU_GOLD_VERSION), + KNOWNSTYPE (GNU_PROPERTY_TYPE_0), }; /* Handle standard names. */ diff --git a/libelf/ChangeLog b/libelf/ChangeLog index be37ab60..af565036 100644 --- a/libelf/ChangeLog +++ b/libelf/ChangeLog @@ -1,3 +1,31 @@ +2018-10-18 Mark Wielaard <mark@klomp.org> + + * libelf.h (Elf_Type): Add ELF_T_NHDR8. + * libelfP.h (__libelf_data_type): Add align argument. + (NOTE_ALIGN): Split into... + (NOTE_ALIGN4): ... and ... + (NOTE_ALIGN8): this. + * elf32_xlatetom.c (xlatetom): Recognize both ELF_T_NHDR and + ELF_T_NHDR8. + * elf_compress.c (elf_compress): Pass zdata_align to + __libelf_data_type. + * elf_compress_gnu.c (elf_compress_gnu): Pass sh_addralign to + __libelf_data_type. + * elf_getdata.c (shtype_map): Add ELF_T_NHDR8. + (__libelf_data_type): Take align as extra argument, use it to + determine Elf_Type. + (__libelf_set_rawdata_wrlock): Recognize ELF_T_NHDR8. Pass align to + __libelf_data_type. + * gelf_fsize.c (__libelf_type_sizes): Add ELF_T_NHDR8. + * gelf_getnote.c (gelf_getnote): Use Elf_Type of Elf_Data to calculate + padding. + * gelf_xlate.c (__elf_xfctstom): Set ELF_T_NHDR to elf_cvt_note4, + add ELF_T_NHDR8. + * note_xlate.h (elf_cvt_note): Take nhdr8 argument and use it to + determine padding. + (elf_cvt_note4): New function. + (elf_cvt_note8): Likewise. + 2018-09-13 Mark Wielaard <mark@klomp.org> * elf32_updatefile.c (updatemmap): Use shnum, not ehdr->e_shnum. diff --git a/libelf/elf32_xlatetom.c b/libelf/elf32_xlatetom.c index 13cd485d..3b94cac7 100644 --- a/libelf/elf32_xlatetom.c +++ b/libelf/elf32_xlatetom.c @@ -60,7 +60,7 @@ elfw2(LIBELFBITS, xlatetom) (Elf_Data *dest, const Elf_Data *src, /* We shouldn't require integer number of records when processing notes. Payload bytes follow the header immediately, it's not an array of records as is the case otherwise. */ - if (src->d_type != ELF_T_NHDR + if (src->d_type != ELF_T_NHDR && src->d_type != ELF_T_NHDR8 && src->d_size % recsize != 0) { __libelf_seterrno (ELF_E_INVALID_DATA); diff --git a/libelf/elf_compress.c b/libelf/elf_compress.c index 711be591..fd412e8a 100644 --- a/libelf/elf_compress.c +++ b/libelf/elf_compress.c @@ -513,7 +513,8 @@ elf_compress (Elf_Scn *scn, int type, unsigned int flags) __libelf_reset_rawdata (scn, scn->zdata_base, scn->zdata_size, scn->zdata_align, - __libelf_data_type (elf, sh_type)); + __libelf_data_type (elf, sh_type, + scn->zdata_align)); return 1; } diff --git a/libelf/elf_compress_gnu.c b/libelf/elf_compress_gnu.c index dfa7c571..198dc7d5 100644 --- a/libelf/elf_compress_gnu.c +++ b/libelf/elf_compress_gnu.c @@ -196,7 +196,7 @@ elf_compress_gnu (Elf_Scn *scn, int inflate, unsigned int flags) } __libelf_reset_rawdata (scn, buf_out, size, sh_addralign, - __libelf_data_type (elf, sh_type)); + __libelf_data_type (elf, sh_type, sh_addralign)); scn->zdata_base = buf_out; diff --git a/libelf/elf_getdata.c b/libelf/elf_getdata.c index 278dfa8f..4f80aaf2 100644 --- a/libelf/elf_getdata.c +++ b/libelf/elf_getdata.c @@ -65,7 +65,7 @@ static const Elf_Type shtype_map[EV_NUM - 1][TYPEIDX (SHT_HISUNW) + 1] = [SHT_PREINIT_ARRAY] = ELF_T_ADDR, [SHT_GROUP] = ELF_T_WORD, [SHT_SYMTAB_SHNDX] = ELF_T_WORD, - [SHT_NOTE] = ELF_T_NHDR, + [SHT_NOTE] = ELF_T_NHDR, /* Need alignment to guess ELF_T_NHDR8. */ [TYPEIDX (SHT_GNU_verdef)] = ELF_T_VDEF, [TYPEIDX (SHT_GNU_verneed)] = ELF_T_VNEED, [TYPEIDX (SHT_GNU_versym)] = ELF_T_HALF, @@ -106,6 +106,7 @@ const uint_fast8_t __libelf_type_aligns[EV_NUM - 1][ELFCLASSNUM - 1][ELF_T_NUM] [ELF_T_GNUHASH] = __alignof__ (Elf32_Word), \ [ELF_T_AUXV] = __alignof__ (ElfW2(Bits,auxv_t)), \ [ELF_T_CHDR] = __alignof__ (ElfW2(Bits,Chdr)), \ + [ELF_T_NHDR8] = 8 /* Special case for GNU Property note. */ \ } [EV_CURRENT - 1] = { @@ -118,7 +119,7 @@ const uint_fast8_t __libelf_type_aligns[EV_NUM - 1][ELFCLASSNUM - 1][ELF_T_NUM] Elf_Type internal_function -__libelf_data_type (Elf *elf, int sh_type) +__libelf_data_type (Elf *elf, int sh_type, GElf_Xword align) { /* Some broken ELF ABI for 64-bit machines use the wrong hash table entry size. See elf-knowledge.h for more information. */ @@ -129,7 +130,13 @@ __libelf_data_type (Elf *elf, int sh_type) return (SH_ENTSIZE_HASH (ehdr) == 4 ? ELF_T_WORD : ELF_T_XWORD); } else - return shtype_map[LIBELF_EV_IDX][TYPEIDX (sh_type)]; + { + Elf_Type t = shtype_map[LIBELF_EV_IDX][TYPEIDX (sh_type)]; + /* Special case for GNU Property notes. */ + if (t == ELF_T_NHDR && align == 8) + t = ELF_T_NHDR8; + return t; + } } /* Convert the data in the current section. */ @@ -272,7 +279,9 @@ __libelf_set_rawdata_wrlock (Elf_Scn *scn) else { Elf_Type t = shtype_map[LIBELF_EV_IDX][TYPEIDX (type)]; - if (t == ELF_T_VDEF || t == ELF_T_NHDR + if (t == ELF_T_NHDR && align == 8) + t = ELF_T_NHDR8; + if (t == ELF_T_VDEF || t == ELF_T_NHDR || t == ELF_T_NHDR8 || (t == ELF_T_GNUHASH && elf->class == ELFCLASS64)) entsize = 1; else @@ -357,7 +366,7 @@ __libelf_set_rawdata_wrlock (Elf_Scn *scn) if ((flags & SHF_COMPRESSED) != 0) scn->rawdata.d.d_type = ELF_T_CHDR; else - scn->rawdata.d.d_type = __libelf_data_type (elf, type); + scn->rawdata.d.d_type = __libelf_data_type (elf, type, align); scn->rawdata.d.d_off = 0; /* Make sure the alignment makes sense. d_align should be aligned both diff --git a/libelf/gelf_fsize.c b/libelf/gelf_fsize.c index 0c509265..d04ec5d5 100644 --- a/libelf/gelf_fsize.c +++ b/libelf/gelf_fsize.c @@ -64,6 +64,8 @@ const size_t __libelf_type_sizes[EV_NUM - 1][ELFCLASSNUM - 1][ELF_T_NUM] = [ELF_T_VNEED] = sizeof (ElfW2(LIBELFBITS, Ext_Verneed)), \ [ELF_T_VNAUX] = sizeof (ElfW2(LIBELFBITS, Ext_Vernaux)), \ [ELF_T_NHDR] = sizeof (ElfW2(LIBELFBITS, Ext_Nhdr)), \ + /* Note the header size is the same, but padding is different. */ \ + [ELF_T_NHDR8] = sizeof (ElfW2(LIBELFBITS, Ext_Nhdr)), \ [ELF_T_SYMINFO] = sizeof (ElfW2(LIBELFBITS, Ext_Syminfo)), \ [ELF_T_MOVE] = sizeof (ElfW2(LIBELFBITS, Ext_Move)), \ [ELF_T_LIB] = sizeof (ElfW2(LIBELFBITS, Ext_Lib)), \ diff --git a/libelf/gelf_getnote.c b/libelf/gelf_getnote.c index c75eddab..6d33b355 100644 --- a/libelf/gelf_getnote.c +++ b/libelf/gelf_getnote.c @@ -1,5 +1,5 @@ /* Get note information at the supplied offset. - Copyright (C) 2007, 2014, 2015 Red Hat, Inc. + Copyright (C) 2007, 2014, 2015, 2018 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -43,7 +43,7 @@ gelf_getnote (Elf_Data *data, size_t offset, GElf_Nhdr *result, if (data == NULL) return 0; - if (unlikely (data->d_type != ELF_T_NHDR)) + if (unlikely (data->d_type != ELF_T_NHDR && data->d_type != ELF_T_NHDR8)) { __libelf_seterrno (ELF_E_INVALID_HANDLE); return 0; @@ -69,27 +69,42 @@ gelf_getnote (Elf_Data *data, size_t offset, GElf_Nhdr *result, const GElf_Nhdr *n = data->d_buf + offset; offset += sizeof *n; - /* Include padding. Check below for overflow. */ - GElf_Word namesz = NOTE_ALIGN (n->n_namesz); - GElf_Word descsz = NOTE_ALIGN (n->n_descsz); - - if (unlikely (offset > data->d_size - || data->d_size - offset < namesz - || (namesz == 0 && n->n_namesz != 0))) + if (offset > data->d_size) offset = 0; else { + /* This is slightly tricky, offset is guaranteed to be 4 + byte aligned, which is what we need for the name_offset. + And normally desc_offset is also 4 byte aligned, but not + for GNU Property notes, then it should be 8. So align + the offset, after adding the namesz, and include padding + in descsz to get to the end. */ *name_offset = offset; - offset += namesz; - if (unlikely (offset > data->d_size - || data->d_size - offset < descsz - || (descsz == 0 && n->n_descsz != 0))) + offset += n->n_namesz; + if (offset > data->d_size) offset = 0; else { - *desc_offset = offset; - offset += descsz; - *result = *n; + /* Include padding. Check below for overflow. */ + GElf_Word descsz = (data->d_type == ELF_T_NHDR8 + ? NOTE_ALIGN8 (n->n_descsz) + : NOTE_ALIGN4 (n->n_descsz)); + + if (data->d_type == ELF_T_NHDR8) + offset = NOTE_ALIGN8 (offset); + else + offset = NOTE_ALIGN4 (offset); + + if (unlikely (offset > data->d_size + || data->d_size - offset < descsz + || (descsz == 0 && n->n_descsz != 0))) + offset = 0; + else + { + *desc_offset = offset; + offset += descsz; + *result = *n; + } } } } diff --git a/libelf/gelf_xlate.c b/libelf/gelf_xlate.c index 479f1436..b5d6ef3d 100644 --- a/libelf/gelf_xlate.c +++ b/libelf/gelf_xlate.c @@ -195,7 +195,8 @@ const xfct_t __elf_xfctstom[EV_NUM - 1][EV_NUM - 1][ELFCLASSNUM - 1][ELF_T_NUM] [ELF_T_VDAUX] = elf_cvt_Verdef, \ [ELF_T_VNEED] = elf_cvt_Verneed, \ [ELF_T_VNAUX] = elf_cvt_Verneed, \ - [ELF_T_NHDR] = elf_cvt_note, \ + [ELF_T_NHDR] = elf_cvt_note4, \ + [ELF_T_NHDR8] = elf_cvt_note8, \ [ELF_T_SYMINFO] = ElfW2(Bits, cvt_Syminfo), \ [ELF_T_MOVE] = ElfW2(Bits, cvt_Move), \ [ELF_T_LIB] = ElfW2(Bits, cvt_Lib), \ diff --git a/libelf/libelf.h b/libelf/libelf.h index d11358cc..1ff11c95 100644 --- a/libelf/libelf.h +++ b/libelf/libelf.h @@ -117,6 +117,8 @@ typedef enum ELF_T_GNUHASH, /* GNU-style hash section. */ ELF_T_AUXV, /* Elf32_auxv_t, Elf64_auxv_t, ... */ ELF_T_CHDR, /* Compressed, Elf32_Chdr, Elf64_Chdr, ... */ + ELF_T_NHDR8, /* Special GNU Properties note. Same as Nhdr, + except padding. */ /* Keep this the last entry. */ ELF_T_NUM } Elf_Type; diff --git a/libelf/libelfP.h b/libelf/libelfP.h index ed216c8c..fa6d55d8 100644 --- a/libelf/libelfP.h +++ b/libelf/libelfP.h @@ -452,7 +452,8 @@ extern const uint_fast8_t __libelf_type_aligns[EV_NUM - 1][ELFCLASSNUM - 1][ELF_ /* Given an Elf handle and a section type returns the Elf_Data d_type. Should not be called when SHF_COMPRESSED is set, the d_type should be ELF_T_BYTE. */ -extern Elf_Type __libelf_data_type (Elf *elf, int sh_type) internal_function; +extern Elf_Type __libelf_data_type (Elf *elf, int sh_type, GElf_Xword align) + internal_function; /* The libelf API does not have such a function but it is still useful. Get the memory size for the given type. @@ -624,8 +625,13 @@ extern void __libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, } \ } while (0) -/* Align offset to 4 bytes as needed for note name and descriptor data. */ -#define NOTE_ALIGN(n) (((n) + 3) & -4U) +/* Align offset to 4 bytes as needed for note name and descriptor data. + This is almost always used, except for GNU Property notes, which use + 8 byte padding... */ +#define NOTE_ALIGN4(n) (((n) + 3) & -4U) + +/* Special note padding rule for GNU Property notes. */ +#define NOTE_ALIGN8(n) (((n) + 7) & -8U) /* Convenience macro. */ #define INVALID_NDX(ndx, type, data) \ diff --git a/libelf/note_xlate.h b/libelf/note_xlate.h index 62c6f63d..9bdc3e2c 100644 --- a/libelf/note_xlate.h +++ b/libelf/note_xlate.h @@ -1,5 +1,5 @@ /* Conversion functions for notes. - Copyright (C) 2007, 2009, 2014 Red Hat, Inc. + Copyright (C) 2007, 2009, 2014, 2018 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -27,38 +27,60 @@ not, see <http://www.gnu.org/licenses/>. */ static void -elf_cvt_note (void *dest, const void *src, size_t len, int encode) +elf_cvt_note (void *dest, const void *src, size_t len, int encode, + bool nhdr8) { + /* Note that the header is always the same size, but the padding + differs for GNU Property notes. */ assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr)); while (len >= sizeof (Elf32_Nhdr)) { + /* Convert the header. */ (1 ? Elf32_cvt_Nhdr : Elf64_cvt_Nhdr) (dest, src, sizeof (Elf32_Nhdr), encode); const Elf32_Nhdr *n = encode ? src : dest; - Elf32_Word namesz = NOTE_ALIGN (n->n_namesz); - Elf32_Word descsz = NOTE_ALIGN (n->n_descsz); - len -= sizeof *n; - src += sizeof *n; - dest += sizeof *n; + size_t note_len = sizeof *n; - if (namesz > len) + /* desc needs to be aligned. */ + note_len += n->n_namesz; + note_len = nhdr8 ? NOTE_ALIGN8 (note_len) : NOTE_ALIGN4 (note_len); + if (note_len > len || note_len < 8) break; - len -= namesz; - if (descsz > len) + + /* data as a whole needs to be aligned. */ + note_len += n->n_descsz; + note_len = nhdr8 ? NOTE_ALIGN8 (note_len) : NOTE_ALIGN4 (note_len); + if (note_len > len || note_len < 8) break; - len -= descsz; + /* Copy or skip the note data. */ + size_t note_data_len = note_len - sizeof *n; + src += sizeof *n; + dest += sizeof *n; if (src != dest) - memcpy (dest, src, namesz + descsz); + memcpy (dest, src, note_data_len); - src += namesz + descsz; - dest += namesz + descsz; + src += note_data_len; + dest += note_data_len; + len -= note_len; } - /* Copy opver any leftover data unconcerted. Probably part of + /* Copy over any leftover data unconverted. Probably part of truncated name/desc data. */ if (unlikely (len > 0) && src != dest) memcpy (dest, src, len); } + +static void +elf_cvt_note4 (void *dest, const void *src, size_t len, int encode) +{ + elf_cvt_note (dest, src, len, encode, false); +} + +static void +elf_cvt_note8 (void *dest, const void *src, size_t len, int encode) +{ + elf_cvt_note (dest, src, len, encode, true); +} diff --git a/src/ChangeLog b/src/ChangeLog index 5061cc11..758534de 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,10 @@ +2018-10-18 Mark Wielaard <mark@klomp.org> + + * elflint.c (check_note_data): Recognize NT_GNU_PROPERTY_TYPE_0. + (check_note): Use p_align to pass either ELF_T_NHDR or ELF_T_NHDR8 to + elf_getdata_rawchunk. + * readelf (handle_notes): Likewise. + 2018-10-24 Mark Wielaard <mark@klomp.org> * addr2line.c (print_addrsym): Use elf_getshdrstrndx instead of diff --git a/src/elflint.c b/src/elflint.c index 3d445954..fa3af4c5 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -4331,6 +4331,7 @@ section [%2d] '%s': unknown core file note type %" PRIu32 case NT_GNU_HWCAP: case NT_GNU_BUILD_ID: case NT_GNU_GOLD_VERSION: + case NT_GNU_PROPERTY_TYPE_0: break; case 0: @@ -4376,7 +4377,8 @@ phdr[%d]: no note entries defined for the type of file\n"), GElf_Off notes_size = 0; Elf_Data *data = elf_getdata_rawchunk (ebl->elf, phdr->p_offset, phdr->p_filesz, - ELF_T_NHDR); + (phdr->p_align == 8 + ? ELF_T_NHDR8 : ELF_T_NHDR)); if (data != NULL && data->d_buf != NULL) notes_size = check_note_data (ebl, ehdr, data, 0, cnt, phdr->p_offset); diff --git a/src/readelf.c b/src/readelf.c index 72ae04ec..ccd07eb7 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -12300,7 +12300,8 @@ handle_notes (Ebl *ebl, GElf_Ehdr *ehdr) handle_notes_data (ebl, ehdr, phdr->p_offset, elf_getdata_rawchunk (ebl->elf, phdr->p_offset, phdr->p_filesz, - ELF_T_NHDR)); + (phdr->p_align == 8 + ? ELF_T_NHDR8 : ELF_T_NHDR))); } } diff --git a/tests/ChangeLog b/tests/ChangeLog index 751a0810..d5a06563 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,12 @@ +2018-10-18 Mark Wielaard <mark@klomp.org> + + * run-readelf-n.sh: New test. + * testfile-gnu-property-note.bz2: New testfile. + * testfile-gnu-property-note.o.bz2: Likewise. + * Makefile.am (TESTS): Add run-readelf-n.sh. + (EXTRA_DIST): Likewise and testfile-gnu-property-note.bz2, + testfile-gnu-property-note.o.bz2. + 2018-10-12 Mark Wielaard <mark@klomp.org> * run-readelf-zdebug.sh: Adjust flags output. diff --git a/tests/Makefile.am b/tests/Makefile.am index 15b429b7..a2a381ac 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -106,6 +106,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \ run-readelf-test4.sh run-readelf-twofiles.sh \ run-readelf-macro.sh run-readelf-loc.sh run-readelf-ranges.sh \ run-readelf-aranges.sh run-readelf-line.sh run-readelf-z.sh \ + run-readelf-n.sh \ run-native-test.sh run-bug1-test.sh \ run-debuglink.sh run-debugaltlink.sh run-buildid.sh \ dwfl-bug-addr-overflow run-addrname-test.sh \ @@ -272,6 +273,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ testfile-dwzstr.bz2 testfile-dwzstr.multi.bz2 \ run-readelf-addr.sh run-readelf-str.sh \ run-readelf-types.sh \ + run-readelf-n.sh \ + testfile-gnu-property-note.bz2 testfile-gnu-property-note.o.bz2 \ run-allfcts-multi.sh \ test-offset-loop.bz2 test-offset-loop.alt.bz2 \ run-prelink-addr-test.sh \ diff --git a/tests/run-readelf-n.sh b/tests/run-readelf-n.sh new file mode 100755 index 00000000..3ae7cf02 --- /dev/null +++ b/tests/run-readelf-n.sh @@ -0,0 +1,55 @@ +# Copyright (C) 2018 Red Hat, Inc. +# This file is part of elfutils. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# elfutils 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +. $srcdir/test-subr.sh + +# - testfile-gnu-property-note.c +# int +# main () +# { +# return 0; +# } +# +# gcc -fcf-protection -c testfile-gnu-property-note.c +# gcc -o testfile-gnu-property-note testfile-gnu-property-note.o +# eu-strip --strip-sections testfile-gnu-property-note + +testfiles testfile-gnu-property-note.o testfile-gnu-property-note + +# Test reading notes through sections +testrun_compare ${abs_top_builddir}/src/readelf -n testfile-gnu-property-note.o << EOF + +Note section [ 6] '.note.gnu.property' of 32 bytes at offset 0x80: + Owner Data size Type + GNU 16 GNU_PROPERTY_TYPE_0 + X86 FEATURE_1_AND: 00000003 IBT SHSTK +EOF + +# Test reading notes through segments +testrun_compare ${abs_top_builddir}/src/readelf -n testfile-gnu-property-note << EOF + +Note segment of 32 bytes at offset 0x300: + Owner Data size Type + GNU 16 GNU_PROPERTY_TYPE_0 + X86 FEATURE_1_AND: 00000003 IBT SHSTK + +Note segment of 68 bytes at offset 0x320: + Owner Data size Type + GNU 16 VERSION + OS: Linux, ABI: 3.2.0 + GNU 20 GNU_BUILD_ID + Build ID: 83cb2229fabd2065d1361f5b46424cd75270f94b +EOF diff --git a/tests/testfile-gnu-property-note.bz2 b/tests/testfile-gnu-property-note.bz2 Binary files differnew file mode 100755 index 00000000..c03bd9bb --- /dev/null +++ b/tests/testfile-gnu-property-note.bz2 diff --git a/tests/testfile-gnu-property-note.o.bz2 b/tests/testfile-gnu-property-note.o.bz2 Binary files differnew file mode 100644 index 00000000..5147c64f --- /dev/null +++ b/tests/testfile-gnu-property-note.o.bz2 |