[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: What integer type should ELF note header have?



On Mon, Dec 11, 2017 at 1:43 AM, Mark Wielaard <mark@klomp.org> wrote:
> On Sun, 2017-12-10 at 22:22 -0700, Ali Bahrami wrote:
>> To my reading, the words of the gABI support what HP-UX did, and the
>> comment in the Solaris code makes me think our definition of
>> ELF64_Nhdr
>> was a mistake. And given that the rest of us don't have any installed
>> base (yet) of 8 byte alignment notes, it seems that the most
>> compatible
>> thing that we could do would be to also use 8-byte words for these
>> fields.
>> triggered by an sh_addralign, or p_align, of 8. That seems to match
>> the
>> gABI words, and preserves interoperability with HP-UX.
>>
>> An open question to you and Mark, and any others in the GNU world:
>> Would you consider making that change? If so, I think we (Solaris)
>> would have no problem in doing likewise.
>
> I am afraid changing ELF64_Nhdr/GElf_Nhdr now from 32bit Words, or

Changing ELF64_Nhdr is going to be hard, but not impossible.  In theory,
an 64-bit object may have a note entry which is bigger than 4 GB.  We
can add a new note header:

typedef struct
{
  Elf64_Xword n_namesz;                  /* Length of the note's name.  */
  Elf64_Xword n_descsz;                  /* Length of the note's descriptor.  */
  Elf64_Xword n_type;                    /* Type of the note.  */
} Elf64_Nhdr64;

Note segments/sections with 8 byte alignment should use Elf64_Nhdr64.
If we want to do it, we should do it now before NT_GNU_PROPERTY_TYPE_0
notes with the existing Elf64_Nhdr are generated by GCC 8 with
-fcf-protection -mcet.

> changing the alignment of the namesz or descsz from 4 byte alignment is

I have updated glibc and binutils to support 4 byte and 8 byte alignments
in 64-bit objects by checking p_align/sh_addralign, instead of assuming
4 byte alignment, like

commit 8d81ce0c6d6ca923571e8b2bac132929f9a02973
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Tue Nov 28 09:56:47 2017 -0800

    Properly compute offsets of note descriptor and next note [BZ #22370]

    A note header has 3 4-bytes fields, followed by note name and note
    descriptor.  According to gABI, in a note entry, the note name field,
    not note name size, is padded for the note descriptor.  And the note
    descriptor field, not note descriptor size, is padded for the next
    note entry.  Notes are aligned to 4 bytes in 32-bit objects and 8 bytes
    in 64-bit objects.

    For all GNU notes, the name is "GNU" which is 4 bytes.  They have the
    same format in the first 16 bytes in both 32-bit and 64-bit objects.
    They differ by note descriptor size and note type.  So far, .note.ABI-tag
    and .note.gnu.build-id notes are always aligned to 4 bytes.  The exsting
    codes compute the note size by aligning the note name size and note
    descriptor size to 4 bytes.  It happens to produce the same value as
    the actual note size by luck since the name size is 4 and offset of the
    note descriptor is 16.  But it will produce the wrong size when note
    alignment is 8 bytes in 64-bit objects.

    This patch defines ELF_NOTE_DESC_OFFSET and ELF_NOTE_NEXT_OFFSET to
    properly compute offsets of note descriptor and next note.  It uses
    alignment of PT_NOTE segment to support both 4-byte and 8-byte note
    alignments in 64-bit objects.  To handle PT_NOTE segments with
    incorrect alignment, which may lead to an infinite loop, if segment
    alignment is less than 4, we treate alignment as 4 bytes since some
    note segments have 0 or 1 byte alignment.

>  not going to work. It is too hardcoded in various GNU/Linux code bases
> now to change. On GNU/Linux ELF notes have been used a lot already, if
> only to parse the build-id embedded in every executable. So you will
> find a lot of code that will simply do:
>
> /*
>  * Align offset to 4 bytes as needed for note name and descriptor data.
>  */
> #define NOTE_ALIGN(n) (((n) + 3) & -4U)
>
>                 GElf_Nhdr *nhdr = ptr;
>                 size_t namesz = NOTE_ALIGN(nhdr->n_namesz),
>                        descsz = NOTE_ALIGN(nhdr->n_descsz);
>                 const char *name;
>
>                 ptr += sizeof(*nhdr);
>                 name = ptr;
>                 ptr += namesz;
> [...]
>                 ptr += descsz;
>
> The above is from the linux kernel perf tool (tools/perf/util/symbol-
> elf.c), but I can easily find more examples of hard coded parsing of
> ELF notes that simply assumes they are 32 bit Words, 4 byte aligned (it
> is basically what elfutils libelf also does).
>
> You could play with the SHT_NOTE sh_align and PT_NOTE p_align values,
> but I haven't actually found any code that pays attention to it. So a
> lot of code would have to be rewritten.
>

These can be easily fixed with

/* Compute the offset of the note descriptor from size of note entry's
   owner string and note alignment.  */
# define ELF_NOTE_DESC_OFFSET(namesz, align) \
  ALIGN_UP (sizeof (ElfW(Nhdr)) + (namesz), (align))

/* Compute the offset of the next note entry from size of note entry's
   owner string, size of the note descriptor and note alignment.  */
# define ELF_NOTE_NEXT_OFFSET(namesz, descsz, align) \
  ALIGN_UP (ELF_NOTE_DESC_OFFSET ((namesz), (align)) + (descsz), (align))

I have added them to glibc, binutils as well as the upcoming kernel
patch to enable CET.

-- 
H.J.