# and strings are different types and bytes hold numeric values when
# iterated over.
- def bytes2hex(b): return b.hex()
+ def bytes2hex(b, sep=''):
+ if not sep:
+ return b.hex()
+ return sep.join(map('{:02x}'.format, b))
+
def bytes2str(b): return b.decode('latin-1')
def str2bytes(s): return s.encode('latin-1')
def int2byte(i): return bytes((i,))
import cStringIO
StringIO = BytesIO = cStringIO.StringIO
- def bytes2hex(b): return b.encode('hex')
+ def bytes2hex(b, sep=''):
+ res = b.encode('hex')
+ if not sep:
+ return res
+ return sep.join(res[i:i+2] for i in range(0, len(res), 2))
+
def bytes2str(b): return b
def str2bytes(s): return s
int2byte = chr
desc = '\n Build ID: %s' % (n_desc)
elif x['n_type'] == 'NT_GNU_GOLD_VERSION':
desc = '\n Version: %s' % (n_desc)
+ elif x['n_type'] == 'NT_GNU_PROPERTY_TYPE_0':
+ desc = '\n Properties: ' + describe_note_gnu_properties(x['n_desc'])
else:
- desc = '\n description data: {}'.format(bytes2hex(n_desc))
+ desc = '\n description data: {}'.format(bytes2hex(n_desc))
if x['n_type'] == 'NT_GNU_ABI_TAG' and x['n_name'] == 'Android':
note_type = 'NT_VERSION'
return _DESCR_ATTR_TAG_ARM[tag] + d_entry[val]
+def describe_note_gnu_properties(properties):
+ descriptions = []
+ for prop in properties:
+ t, d, sz = prop.pr_type, prop.pr_data, prop.pr_datasz
+ if t == 'GNU_PROPERTY_STACK_SIZE':
+ if type(d) is int:
+ prop_desc = 'stack size: 0x%x' % d
+ else:
+ prop_desc = 'stack size: <corrupt length: 0x%x>' % sz
+ elif t == 'GNU_PROPERTY_NO_COPY_ON_PROTECTED':
+ if sz != 0:
+ prop_desc = ' <corrupt length: 0x%x>' % sz
+ else:
+ prop_desc = 'no copy on protected'
+ elif _DESCR_NOTE_GNU_PROPERTY_TYPE_LOPROC <= t <= _DESCR_NOTE_GNU_PROPERTY_TYPE_HIPROC:
+ prop_desc = '<processor-specific type 0x%x data: %s >' % (t, bytes2hex(d, sep=' '))
+ elif _DESCR_NOTE_GNU_PROPERTY_TYPE_LOUSER <= t <= _DESCR_NOTE_GNU_PROPERTY_TYPE_HIUSER:
+ prop_desc = '<application-specific type 0x%x data: %s >' % (t, bytes2hex(d, sep=' '))
+ else:
+ prop_desc = '<unknown type 0x%x data: %s >' % (t, bytes2hex(d, sep=' '))
+ descriptions.append(prop_desc)
+ return '\n '.join(descriptions)
+
#-------------------------------------------------------------------------------
_unknown = '<unknown>'
PT_GNU_EH_FRAME='GNU_EH_FRAME',
PT_GNU_STACK='GNU_STACK',
PT_GNU_RELRO='GNU_RELRO',
+ PT_GNU_PROPERTY='GNU_PROPERTY',
PT_ARM_ARCHEXT='ARM_ARCHEXT',
PT_ARM_EXIDX='EXIDX', # binutils calls this EXIDX, not ARM_EXIDX
PT_AARCH64_ARCHEXT='AARCH64_ARCHEXT',
NT_GNU_HWCAP='DSO-supplied software HWCAP info',
NT_GNU_BUILD_ID='unique build ID bitstring',
NT_GNU_GOLD_VERSION='gold version',
+ NT_GNU_PROPERTY_TYPE_0='program properties'
)
ELF_NOTE_OS_SYLLABLE='Syllable',
)
+# Values in GNU .note.gnu.property notes (n_type=='NT_GNU_PROPERTY_TYPE_0') have
+# different formats which need to be parsed/described differently
+_DESCR_NOTE_GNU_PROPERTY_TYPE_LOPROC=0xc0000000
+_DESCR_NOTE_GNU_PROPERTY_TYPE_HIPROC=0xdfffffff
+_DESCR_NOTE_GNU_PROPERTY_TYPE_LOUSER=0xe0000000
+_DESCR_NOTE_GNU_PROPERTY_TYPE_HIUSER=0xffffffff
+
def _reverse_dict(d, low_priority=()):
"""
This is a tiny helper function to "reverse" the keys/values of a dictionary
NT_GNU_HWCAP=2,
NT_GNU_BUILD_ID=3,
NT_GNU_GOLD_VERSION=4,
+ NT_GNU_PROPERTY_TYPE_0=5,
_default_=Pass,
)
_default_=Pass,
)
+# Values in GNU .note.gnu.property notes (n_type=='NT_GNU_PROPERTY_TYPE_0')
+ENUM_NOTE_GNU_PROPERTY_TYPE = dict(
+ GNU_PROPERTY_STACK_SIZE=1,
+ GNU_PROPERTY_NO_COPY_ON_PROTECTED=2,
+ _default_=Pass,
+)
+
ENUM_RELOC_TYPE_ARM = dict(
R_ARM_NONE=0,
R_ARM_PC24=1,
note['n_desc'] = struct_parse(elffile.structs.Elf_Nt_File,
elffile.stream,
offset)
+ elif note['n_type'] == 'NT_GNU_PROPERTY_TYPE_0':
+ off = offset
+ props = []
+ while off < end:
+ p = struct_parse(elffile.structs.Elf_Prop, elffile.stream, off)
+ off += roundup(p.pr_datasz + 8, 2 if elffile.elfclass == 32 else 3)
+ props.append(p)
+ note['n_desc'] = props
else:
note['n_desc'] = desc_data
offset += roundup(note['n_descsz'], 2)
# Eli Bendersky (eliben@gmail.com)
# This code is in the public domain
#-------------------------------------------------------------------------------
+from elftools.construct.macros import AlignedStruct, IfThenElse, UNInt8
from ..construct import (
UBInt8, UBInt16, UBInt32, UBInt64,
ULInt8, ULInt16, ULInt32, ULInt64,
SBInt32, SLInt32, SBInt64, SLInt64,
- Struct, Array, Enum, Padding, BitStruct, BitField, Value, String, CString
+ Struct, Array, Enum, Padding, BitStruct, BitField, Value, String, CString,
+ Switch, Field
)
from ..common.construct_utils import ULEB128
+from ..common.utils import roundup
from .enums import *
self._create_gnu_verdef()
self._create_gnu_versym()
self._create_gnu_abi()
+ self._create_gnu_property()
self._create_note(e_type)
self._create_stabs()
self._create_arm_attributes()
self.Elf_word('abi_tiny'),
)
+ def _create_gnu_property(self):
+ # Structure of GNU property notes is documented in
+ # https://github.com/hjl-tools/linux-abi/wiki/linux-abi-draft.pdf
+ def roundup_padding(ctx):
+ if self.elfclass == 32:
+ return roundup(ctx.pr_datasz, 2) - ctx.pr_datasz
+ return roundup(ctx.pr_datasz, 3) - ctx.pr_datasz
+
+ self.Elf_Prop = Struct('Elf_Prop',
+ Enum(self.Elf_word('pr_type'), **ENUM_NOTE_GNU_PROPERTY_TYPE),
+ self.Elf_word('pr_datasz'),
+ Switch('pr_data',
+ lambda ctx: (ctx.pr_type, ctx.pr_datasz, self.elfclass),
+ {
+ ('GNU_PROPERTY_STACK_SIZE', 4, 32): self.Elf_word('pr_data'),
+ ('GNU_PROPERTY_STACK_SIZE', 8, 64): self.Elf_word64('pr_data')
+ },
+ default=Field('pr_data', lambda ctx: ctx.pr_datasz)
+ ),
+ Padding(roundup_padding)
+ )
+
def _create_note(self, e_type=None):
# Structure of "PT_NOTE" section
for note in section.iter_notes():
self._emitline("\nDisplaying notes found in: {}".format(
section.name))
- self._emitline(' Owner Data size Description')
+ self._emitline(' Owner Data size Description')
self._emitline(' %s %s\t%s' % (
note['n_name'].ljust(20),
self._format_hex(note['n_descsz'], fieldsize=8),
if 'symbol table' in lines1[i]:
flag_after_symtable = True
+ # readelf spelling error for GNU property notes
+ lines1[i] = lines1[i].replace('procesor-specific type', 'processor-specific type')
+
# Compare ignoring whitespace
lines1_parts = lines1[i].split()
lines2_parts = lines2[i].split()
elif ( 'unknown at value' in lines1[i] and
'dw_at_apple' in lines2[i]):
ok = True
+ elif 'loos+0x474e553' in lines1[i]:
+ # readelf v2.29 does not know about PT_GNU_PROPERTY apparently
+ ok = lines2_parts[0] == 'gnu_property'
+ elif len(lines1_parts) == 3 and lines1_parts[2] == 'nt_gnu_property_type_0':
+ # readelf does not seem to print a readable description for this
+ ok = lines1_parts == lines2_parts[:3]
else:
for s in ('t (tls)', 'l (large)'):
if s in lines1[i] or s in lines2[i]:
--- /dev/null
+/**
+ * Test ELF for .note.gnu.property, built on x86-64.
+ *
+ * Object file:
+ * gcc -c note_gnu_property.S -o note_gnu_property.o.elf
+ *
+ * ELF executable (to also have a PT_GNU_PROPERTY program header):
+ * gcc -DEXE -c note_gnu_property.S -o /tmp/x.o
+ * ld /tmp/x.o -o note_gnu_property.elf
+ * strip
+ */
+
+// https://github.com/hjl-tools/linux-abi/wiki/linux-abi-draft.pdf
+#define NT_GNU_PROPERTY_TYPE_0 5
+#define GNU_PROPERTY_STACK_SIZE 1
+#define GNU_PROPERTY_NO_COPY_ON_PROTECTED 2
+#define GNU_PROPERTY_LOPROC 0xc0000000
+#define GNU_PROPERTY_HIPROC 0xdfffffff
+#define GNU_PROPERTY_LOUSER 0xe0000000
+#define GNU_PROPERTY_HIUSER 0xffffffff
+
+// Unknown property types for testing purposes
+#define GNU_PROPERTY_TEST_UNKNOWN 0x12345678
+#define GNU_PROPERTY_TEST_UNKNOWN_PROC 0xc1234567
+#define GNU_PROPERTY_TEST_UNKNOWN_USER 0xe1234567
+
+// https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/x86-64-psABI
+#define GNU_PROPERTY_X86_FEATURE_1_AND 0xc0000002
+#define GNU_PROPERTY_X86_FEATURE_1_IBT 0x00000001
+#define GNU_PROPERTY_X86_FEATURE_1_SHSTK 0x00000002
+
+#ifdef __x86_64__
+#define ALIGN .p2align 3
+#else
+#define ALIGN .p2align 2
+#endif
+
+.section ".text"
+.global _start
+_start:
+ ud2
+
+.section ".note.gnu.property", "a"
+ ALIGN
+ .long 1f - 0f // n_namesz
+ .long end - 2f // n_descsz
+ .long NT_GNU_PROPERTY_TYPE_0 // n_type
+0: .asciz "GNU" // n_name
+1:
+ ALIGN
+2: .long GNU_PROPERTY_STACK_SIZE // pr_type
+ .long 4f - 3f // pr_datasz
+3:
+ .dc.a 0x123000
+4:
+ ALIGN
+ .long GNU_PROPERTY_NO_COPY_ON_PROTECTED // pr_type
+ .long 0 // pr_datasz
+ ALIGN
+
+// Avoid these if linking to executable, linkers may not recognize them
+#ifndef EXE
+ .long GNU_PROPERTY_TEST_UNKNOWN // pr_type
+ .long 6f-5f // pr_datasz
+5:
+ .ascii "hello world"
+6:
+ ALIGN
+ .long GNU_PROPERTY_TEST_UNKNOWN_PROC // pr_type
+ .long 8f-7f // pr_datasz
+7:
+ .ascii "foobar"
+8:
+ ALIGN
+ .long GNU_PROPERTY_TEST_UNKNOWN_USER // pr_type
+ .long 10f-9f // pr_datasz
+9:
+ .ascii "bazquuz"
+10:
+ ALIGN
+#endif
+
+/* TODO: add support for these later...
+6: .long GNU_PROPERTY_X86_FEATURE_1_AND // pr_type.
+ .long 8f - 7f // pr_datasz
+7:
+ .long GNU_PROPERTY_X86_FEATURE_1_IBT|GNU_PROPERTY_X86_FEATURE_1_SHSTK
+8:
+ ALIGN
+*/
+end: