summaryrefslogtreecommitdiffstats
path: root/libelf
diff options
context:
space:
mode:
authorMark Wielaard <[email protected]>2018-10-15 23:35:47 +0200
committerMark Wielaard <[email protected]>2018-10-29 00:57:57 +0100
commit5199e15870e05e5b0b9f98c20fc9b5427aa6dd6a (patch)
tree300abbc40ba85162eabf061393f9f5c0cff9b9b2 /libelf
parentb75ff1bbd060404565fa28d72441a9b02f331bae (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 'libelf')
-rw-r--r--libelf/ChangeLog28
-rw-r--r--libelf/elf32_xlatetom.c2
-rw-r--r--libelf/elf_compress.c3
-rw-r--r--libelf/elf_compress_gnu.c2
-rw-r--r--libelf/elf_getdata.c19
-rw-r--r--libelf/gelf_fsize.c2
-rw-r--r--libelf/gelf_getnote.c47
-rw-r--r--libelf/gelf_xlate.c3
-rw-r--r--libelf/libelf.h2
-rw-r--r--libelf/libelfP.h12
-rw-r--r--libelf/note_xlate.h52
11 files changed, 129 insertions, 43 deletions
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 <[email protected]>
+
+ * 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 <[email protected]>
* 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);
+}