diff options
| author | Mark Wielaard <[email protected]> | 2018-10-15 23:35:47 +0200 |
|---|---|---|
| committer | Mark Wielaard <[email protected]> | 2018-10-29 00:57:57 +0100 |
| commit | 5199e15870e05e5b0b9f98c20fc9b5427aa6dd6a (patch) | |
| tree | 300abbc40ba85162eabf061393f9f5c0cff9b9b2 /libdwfl | |
| parent | b75ff1bbd060404565fa28d72441a9b02f331bae (diff) | |
Recognize and parse GNU Property notes.
GNU Property notes are different from normal notes because they use
variable alignment/padding of their fields. They are 8 byte aligned,
but use 4 byte fields. The name is aligned at 4 bytes and padded so
that, the desc is aligned at 8 bytes. The whole note is padded to
8 bytes again. For normal notes all fields are both 4 bytes wide and
4 bytes aligned.
To recognize these new kind of ELF Notes a new Elf_Type is introduced,
ELF_T_NHDR8. This type is used in the xlate functions to determine
how to align and pad the various fields. Since the fields themselves
can now have different alignments we will have to keep track of the
current alignement and use either NOTE_ALIGN4 or NOTE_ALIGN8 to
determine the padding.
To set the correct Elf_Type on the Elf_Data we use either the section
sh_addralign or the segment p_align values. Assuming 8 means the
section or segment contains the new style notes, otherwise normal
notes.
When we cannot determine the "alignment" directly, like when parsing
special kernel sys files, we check the name "GNU" and type
"GNU_PROPERTY_TYPE_0" fields.
ebl_object_note now parses the new NT_GNU_PROPERTY_TYPE_0 and can
extract the GNU_PROPERTY_STACK_SIZE, GNU_PROPERTY_NO_COPY_ON_PROTECTED
and GNU_PROPERTY_X86_FEATURE_1_AND types GNU_PROPERTY_X86_FEATURE_1_IBT
and GNU_PROPERTY_X86_FEATURE_1_SHSTK.
Tests are added for extracting the note from sections or segments
as set by gcc -fcf-protection.
Signed-off-by: Mark Wielaard <[email protected]>
Diffstat (limited to 'libdwfl')
| -rw-r--r-- | libdwfl/ChangeLog | 10 | ||||
| -rw-r--r-- | libdwfl/core-file.c | 4 | ||||
| -rw-r--r-- | libdwfl/dwfl_segment_report_module.c | 36 | ||||
| -rw-r--r-- | libdwfl/linux-core-attach.c | 4 | ||||
| -rw-r--r-- | libdwfl/linux-kernel-modules.c | 35 |
5 files changed, 71 insertions, 18 deletions
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 <[email protected]> + + * 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 <[email protected]> * 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 |
