From 906f28610b7b2193d12ee68ec22b37b61ab618de Mon Sep 17 00:00:00 2001 From: tyb0807 Date: Tue, 21 Nov 2017 06:00:08 +0100 Subject: [PATCH] ARM build attributes section parsing (#160) --- elftools/common/construct_utils.py | 49 +++- elftools/dwarf/structs.py | 55 +---- elftools/elf/descriptions.py | 374 ++++++++++++++++++++++++++++- elftools/elf/elffile.py | 4 +- elftools/elf/enums.py | 47 ++++ elftools/elf/sections.py | 211 ++++++++++++++++ elftools/elf/structs.py | 29 ++- scripts/readelf.py | 30 ++- test/test_arm_support.py | 22 ++ 9 files changed, 765 insertions(+), 56 deletions(-) diff --git a/elftools/common/construct_utils.py b/elftools/common/construct_utils.py index 8ace30e..3633249 100644 --- a/elftools/common/construct_utils.py +++ b/elftools/common/construct_utils.py @@ -6,7 +6,10 @@ # Eli Bendersky (eliben@gmail.com) # This code is in the public domain #------------------------------------------------------------------------------- -from ..construct import Subconstruct, ConstructError, ArrayError +from ..construct import ( + Subconstruct, ConstructError, ArrayError, Adapter, Field, RepeatUntil, + Rename + ) class RepeatUntilExcluding(Subconstruct): @@ -42,3 +45,47 @@ class RepeatUntilExcluding(Subconstruct): raise NotImplementedError('no building') def _sizeof(self, context): raise SizeofError("can't calculate size") + + +def _LEB128_reader(): + """ Read LEB128 variable-length data from the stream. The data is terminated + by a byte with 0 in its highest bit. + """ + return RepeatUntil( + lambda obj, ctx: ord(obj) < 0x80, + Field(None, 1)) + + +class _ULEB128Adapter(Adapter): + """ An adapter for ULEB128, given a sequence of bytes in a sub-construct. + """ + def _decode(self, obj, context): + value = 0 + for b in reversed(obj): + value = (value << 7) + (ord(b) & 0x7F) + return value + + +class _SLEB128Adapter(Adapter): + """ An adapter for SLEB128, given a sequence of bytes in a sub-construct. + """ + def _decode(self, obj, context): + value = 0 + for b in reversed(obj): + value = (value << 7) + (ord(b) & 0x7F) + if ord(obj[-1]) & 0x40: + # negative -> sign extend + value |= - (1 << (7 * len(obj))) + return value + + +def ULEB128(name): + """ A construct creator for ULEB128 encoding. + """ + return Rename(name, _ULEB128Adapter(_LEB128_reader())) + + +def SLEB128(name): + """ A construct creator for SLEB128 encoding. + """ + return Rename(name, _SLEB128Adapter(_LEB128_reader())) diff --git a/elftools/dwarf/structs.py b/elftools/dwarf/structs.py index 9234cf8..f3b6ef3 100644 --- a/elftools/dwarf/structs.py +++ b/elftools/dwarf/structs.py @@ -10,11 +10,10 @@ from ..construct import ( UBInt8, UBInt16, UBInt32, UBInt64, ULInt8, ULInt16, ULInt32, ULInt64, SBInt8, SBInt16, SBInt32, SBInt64, SLInt8, SLInt16, SLInt32, SLInt64, - Adapter, Struct, ConstructError, If, RepeatUntil, Field, Rename, Enum, - Array, PrefixedArray, CString, Embed, StaticField + Adapter, Struct, ConstructError, If, Enum, Array, PrefixedArray, + CString, Embed, StaticField ) -from ..common.construct_utils import RepeatUntilExcluding - +from ..common.construct_utils import RepeatUntilExcluding, ULEB128, SLEB128 from .enums import * @@ -148,8 +147,8 @@ class DWARFStructs(object): self.Dwarf_initial_length = _InitialLength def _create_leb128(self): - self.Dwarf_uleb128 = _ULEB128 - self.Dwarf_sleb128 = _SLEB128 + self.Dwarf_uleb128 = ULEB128 + self.Dwarf_sleb128 = SLEB128 def _create_cu_header(self): self.Dwarf_CU_header = Struct('Dwarf_CU_header', @@ -303,47 +302,3 @@ class _InitialLengthAdapter(Adapter): else: raise ConstructError("Failed decoding initial length for %X" % ( obj.first)) - - -def _LEB128_reader(): - """ Read LEB128 variable-length data from the stream. The data is terminated - by a byte with 0 in its highest bit. - """ - return RepeatUntil( - lambda obj, ctx: ord(obj) < 0x80, - Field(None, 1)) - - -class _ULEB128Adapter(Adapter): - """ An adapter for ULEB128, given a sequence of bytes in a sub-construct. - """ - def _decode(self, obj, context): - value = 0 - for b in reversed(obj): - value = (value << 7) + (ord(b) & 0x7F) - return value - - -class _SLEB128Adapter(Adapter): - """ An adapter for SLEB128, given a sequence of bytes in a sub-construct. - """ - def _decode(self, obj, context): - value = 0 - for b in reversed(obj): - value = (value << 7) + (ord(b) & 0x7F) - if ord(obj[-1]) & 0x40: - # negative -> sign extend - value |= - (1 << (7 * len(obj))) - return value - - -def _ULEB128(name): - """ A construct creator for ULEB128 encoding. - """ - return Rename(name, _ULEB128Adapter(_LEB128_reader())) - - -def _SLEB128(name): - """ A construct creator for SLEB128 encoding. - """ - return Rename(name, _SLEB128Adapter(_LEB128_reader())) diff --git a/elftools/elf/descriptions.py b/elftools/elf/descriptions.py index 7e394f2..4d92730 100644 --- a/elftools/elf/descriptions.py +++ b/elftools/elf/descriptions.py @@ -9,7 +9,8 @@ from .enums import ( ENUM_D_TAG, ENUM_E_VERSION, ENUM_P_TYPE, ENUM_SH_TYPE, ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64, - ENUM_RELOC_TYPE_ARM, ENUM_RELOC_TYPE_AARCH64, ENUM_RELOC_TYPE_MIPS) + ENUM_RELOC_TYPE_ARM, ENUM_RELOC_TYPE_AARCH64, ENUM_RELOC_TYPE_MIPS, + ENUM_ATTR_TAG_ARM) from .constants import P_FLAGS, SH_FLAGS, SUNW_SYMINFO_FLAGS, VER_FLAGS from ..common.py3compat import iteritems @@ -17,27 +18,34 @@ from ..common.py3compat import iteritems def describe_ei_class(x): return _DESCR_EI_CLASS.get(x, _unknown) + def describe_ei_data(x): return _DESCR_EI_DATA.get(x, _unknown) + def describe_ei_version(x): s = '%d' % ENUM_E_VERSION[x] if x == 'EV_CURRENT': s += ' (current)' return s + def describe_ei_osabi(x): return _DESCR_EI_OSABI.get(x, _unknown) + def describe_e_type(x): return _DESCR_E_TYPE.get(x, _unknown) + def describe_e_machine(x): return _DESCR_E_MACHINE.get(x, _unknown) + def describe_e_version_numeric(x): return '0x%x' % ENUM_E_VERSION[x] + def describe_p_type(x): if x in _DESCR_P_TYPE: return _DESCR_P_TYPE.get(x) @@ -46,12 +54,14 @@ def describe_p_type(x): else: return _unknown + def describe_p_flags(x): s = '' for flag in (P_FLAGS.PF_R, P_FLAGS.PF_W, P_FLAGS.PF_X): s += _DESCR_P_FLAGS[flag] if (x & flag) else ' ' return s + def describe_sh_type(x): if x in _DESCR_SH_TYPE: return _DESCR_SH_TYPE.get(x) @@ -60,6 +70,7 @@ def describe_sh_type(x): else: return _unknown + def describe_sh_flags(x): s = '' for flag in ( @@ -70,18 +81,23 @@ def describe_sh_flags(x): s += _DESCR_SH_FLAGS[flag] if (x & flag) else '' return s + def describe_symbol_type(x): return _DESCR_ST_INFO_TYPE.get(x, _unknown) + def describe_symbol_bind(x): return _DESCR_ST_INFO_BIND.get(x, _unknown) + def describe_symbol_visibility(x): return _DESCR_ST_VISIBILITY.get(x, _unknown) + def describe_symbol_shndx(x): return _DESCR_ST_SHNDX.get(x, '%3s' % x) + def describe_reloc_type(x, elffile): arch = elffile.get_machine_arch() if arch == 'x86': @@ -97,6 +113,7 @@ def describe_reloc_type(x, elffile): else: return 'unrecognized: %-7x' % (x & 0xFFFFFFFF) + def describe_dyn_tag(x): return _DESCR_D_TAG.get(x, _unknown) @@ -114,15 +131,18 @@ def describe_syminfo_flags(x): SUNW_SYMINFO_FLAGS.SYMINFO_FLG_INTERPOSE, SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DEFERRED) if x & flag) + def describe_symbol_boundto(x): return _DESCR_SYMINFO_BOUNDTO.get(x, '%3s' % x) + def describe_ver_flags(x): return ' | '.join(_DESCR_VER_FLAGS[flag] for flag in ( VER_FLAGS.VER_FLG_WEAK, VER_FLAGS.VER_FLG_BASE, VER_FLAGS.VER_FLG_INFO) if x & flag) + def describe_note(x): n_desc = x['n_desc'] desc = '' @@ -144,6 +164,34 @@ def describe_note(x): _DESCR_NOTE_N_TYPE.get(x['n_type'], _unknown)) return '%s (%s)%s' % (note_type, note_type_desc, desc) + +def describe_attr_tag_arm(tag, val, extra): + idx = ENUM_ATTR_TAG_ARM[tag] - 1 + d_entry = _DESCR_ATTR_VAL_ARM[idx] + + if d_entry is None: + if tag == 'TAG_COMPATIBILITY': + return (_DESCR_ATTR_TAG_ARM[tag] + + 'flag = %d, vendor = %s' % (val, extra)) + + elif tag == 'TAG_ALSO_COMPATIBLE_WITH': + if val.tag == 'TAG_CPU_ARCH': + return _DESCR_ATTR_TAG_ARM[tag] + d_entry[val] + + else: + return _DESCR_ATTR_TAG_ARM[tag] + '??? (%d)' % val.tag + + elif tag == 'TAG_NODEFAULTS': + return _DESCR_ATTR_TAG_ARM[tag] + 'True' + + s = _DESCR_ATTR_TAG_ARM[tag] + s += '"%s"' % val if val else '' + return s + + else: + return _DESCR_ATTR_TAG_ARM[tag] + d_entry[val] + + #------------------------------------------------------------------------------- _unknown = '' @@ -154,12 +202,14 @@ _DESCR_EI_CLASS = dict( ELFCLASS64='ELF64', ) + _DESCR_EI_DATA = dict( ELFDATANONE='none', ELFDATA2LSB="2's complement, little endian", ELFDATA2MSB="2's complement, big endian", ) + _DESCR_EI_OSABI = dict( ELFOSABI_SYSV='UNIX - System V', ELFOSABI_HPUX='UNIX - HP-UX', @@ -184,6 +234,7 @@ _DESCR_EI_OSABI = dict( ELFOSABI_STANDALONE='Standalone App', ) + _DESCR_E_TYPE = dict( ET_NONE='NONE (None)', ET_REL='REL (Relocatable file)', @@ -193,6 +244,7 @@ _DESCR_E_TYPE = dict( PROC_SPECIFIC='Processor Specific', ) + _DESCR_E_MACHINE = dict( EM_NONE='None', EM_M32='WE32100', @@ -213,6 +265,7 @@ _DESCR_E_MACHINE = dict( RESERVED='RESERVED', ) + _DESCR_P_TYPE = dict( PT_NULL='NULL', PT_LOAD='LOAD', @@ -230,12 +283,14 @@ _DESCR_P_TYPE = dict( PT_AARCH64_UNWIND='AARCH64_UNWIND', ) + _DESCR_P_FLAGS = { P_FLAGS.PF_X: 'E', P_FLAGS.PF_R: 'R', P_FLAGS.PF_W: 'W', } + _DESCR_SH_TYPE = dict( SHT_NULL='NULL', SHT_PROGBITS='PROGBITS', @@ -301,6 +356,7 @@ _DESCR_SH_TYPE = dict( SHT_MIPS_PDR_EXCEPTION='MIPS_PDR_EXCEPTION', ) + _DESCR_SH_FLAGS = { SH_FLAGS.SHF_WRITE: 'W', SH_FLAGS.SHF_ALLOC: 'A', @@ -315,6 +371,7 @@ _DESCR_SH_FLAGS = { SH_FLAGS.SHF_EXCLUDE: 'E', } + _DESCR_ST_INFO_TYPE = dict( STT_NOTYPE='NOTYPE', STT_OBJECT='OBJECT', @@ -328,12 +385,14 @@ _DESCR_ST_INFO_TYPE = dict( STT_SRELC='SRELC', ) + _DESCR_ST_INFO_BIND = dict( STB_LOCAL='LOCAL', STB_GLOBAL='GLOBAL', STB_WEAK='WEAK', ) + _DESCR_ST_VISIBILITY = dict( STV_DEFAULT='DEFAULT', STV_INTERNAL='INTERNAL', @@ -344,12 +403,14 @@ _DESCR_ST_VISIBILITY = dict( STV_ELIMINATE='ELIMINATE', ) + _DESCR_ST_SHNDX = dict( SHN_UNDEF='UND', SHN_ABS='ABS', SHN_COMMON='COM', ) + _DESCR_SYMINFO_FLAGS = { SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DIRECT: 'D', SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DIRECTBIND: 'B', @@ -363,6 +424,7 @@ _DESCR_SYMINFO_FLAGS = { SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DEFERRED: 'P', } + _DESCR_SYMINFO_BOUNDTO = dict( SYMINFO_BT_SELF='', SYMINFO_BT_PARENT='', @@ -370,6 +432,7 @@ _DESCR_SYMINFO_BOUNDTO = dict( SYMINFO_BT_EXTERN='', ) + _DESCR_VER_FLAGS = { 0: '', VER_FLAGS.VER_FLG_BASE: 'BASE', @@ -377,6 +440,7 @@ _DESCR_VER_FLAGS = { VER_FLAGS.VER_FLG_INFO: 'INFO', } + # PT_NOTE section types _DESCR_NOTE_N_TYPE = dict( NT_GNU_ABI_TAG='ABI version tag', @@ -385,6 +449,7 @@ _DESCR_NOTE_N_TYPE = dict( NT_GNU_GOLD_VERSION='gold version', ) + # Values in GNU .note.ABI-tag notes (n_type=='NT_GNU_ABI_TAG') _DESCR_NOTE_ABI_TAG_OS = dict( ELF_NOTE_OS_LINUX='Linux', @@ -395,20 +460,327 @@ _DESCR_NOTE_ABI_TAG_OS = dict( ELF_NOTE_OS_SYLLABLE='Syllable', ) + _DESCR_RELOC_TYPE_i386 = dict( (v, k) for k, v in iteritems(ENUM_RELOC_TYPE_i386)) + _DESCR_RELOC_TYPE_x64 = dict( (v, k) for k, v in iteritems(ENUM_RELOC_TYPE_x64)) + _DESCR_RELOC_TYPE_ARM = dict( (v, k) for k, v in iteritems(ENUM_RELOC_TYPE_ARM)) + _DESCR_RELOC_TYPE_AARCH64 = dict( (v, k) for k, v in iteritems(ENUM_RELOC_TYPE_AARCH64)) + _DESCR_RELOC_TYPE_MIPS = dict( (v, k) for k, v in iteritems(ENUM_RELOC_TYPE_MIPS)) + _DESCR_D_TAG = dict( (v, k) for k, v in iteritems(ENUM_D_TAG)) + + +_DESCR_ATTR_TAG_ARM = dict( + TAG_FILE='File Attributes', + TAG_SECTION='Section Attributes:', + TAG_SYMBOL='Symbol Attributes:', + TAG_CPU_RAW_NAME='Tag_CPU_raw_name: ', + TAG_CPU_NAME='Tag_CPU_name: ', + TAG_CPU_ARCH='Tag_CPU_arch: ', + TAG_CPU_ARCH_PROFILE='Tag_CPU_arch_profile: ', + TAG_ARM_ISA_USE='Tag_ARM_ISA_use: ', + TAG_THUMB_ISA_USE='Tag_Thumb_ISA_use: ', + TAG_FP_ARCH='Tag_FP_arch: ', + TAG_WMMX_ARCH='Tag_WMMX_arch: ', + TAG_ADVANCED_SIMD_ARCH='Tag_Advanced_SIMD_arch: ', + TAG_PCS_CONFIG='Tag_PCS_config: ', + TAG_ABI_PCS_R9_USE='Tag_ABI_PCS_R9_use: ', + TAG_ABI_PCS_RW_DATA='Tag_ABI_PCS_RW_use: ', + TAG_ABI_PCS_RO_DATA='Tag_ABI_PCS_RO_use: ', + TAG_ABI_PCS_GOT_USE='Tag_ABI_PCS_GOT_use: ', + TAG_ABI_PCS_WCHAR_T='Tag_ABI_PCS_wchar_t: ', + TAG_ABI_FP_ROUNDING='Tag_ABI_FP_rounding: ', + TAG_ABI_FP_DENORMAL='Tag_ABI_FP_denormal: ', + TAG_ABI_FP_EXCEPTIONS='Tag_ABI_FP_exceptions: ', + TAG_ABI_FP_USER_EXCEPTIONS='Tag_ABI_FP_user_exceptions: ', + TAG_ABI_FP_NUMBER_MODEL='Tag_ABI_FP_number_model: ', + TAG_ABI_ALIGN_NEEDED='Tag_ABI_align_needed: ', + TAG_ABI_ALIGN_PRESERVED='Tag_ABI_align_preserved: ', + TAG_ABI_ENUM_SIZE='Tag_ABI_enum_size: ', + TAG_ABI_HARDFP_USE='Tag_ABI_HardFP_use: ', + TAG_ABI_VFP_ARGS='Tag_ABI_VFP_args: ', + TAG_ABI_WMMX_ARGS='Tag_ABI_WMMX_args: ', + TAG_ABI_OPTIMIZATION_GOALS='Tag_ABI_optimization_goals: ', + TAG_ABI_FP_OPTIMIZATION_GOALS='Tag_ABI_FP_optimization_goals: ', + TAG_COMPATIBILITY='Tag_compatibility: ', + TAG_CPU_UNALIGNED_ACCESS='Tag_CPU_unaligned_access: ', + TAG_FP_HP_EXTENSION='Tag_FP_HP_extension: ', + TAG_ABI_FP_16BIT_FORMAT='Tag_ABI_FP_16bit_format: ', + TAG_MPEXTENSION_USE='Tag_MPextension_use: ', + TAG_DIV_USE='Tag_DIV_use: ', + TAG_NODEFAULTS='Tag_nodefaults: ', + TAG_ALSO_COMPATIBLE_WITH='Tag_also_compatible_with: ', + TAG_T2EE_USE='Tag_T2EE_use: ', + TAG_CONFORMANCE='Tag_conformance: ', + TAG_VIRTUALIZATION_USE='Tag_Virtualization_use: ', + TAG_MPEXTENSION_USE_OLD='Tag_MPextension_use_old: ', +) + + +_DESCR_ATTR_VAL_ARM = [ + None, #1 + None, #2 + None, #3 + None, #4 + None, #5 + { #6 TAG_CPU_ARCH + 0 : 'Pre-v4', + 1 : 'v4', + 2 : 'v4T', + 3 : 'v5T', + 4 : 'v5TE', + 5 : 'v5TEJ', + 6 : 'v6', + 7 : 'v6KZ', + 8 : 'v6T2', + 9 : 'v6K', + 10: 'v7', + 11: 'v6-M', + 12: 'v6S-M', + 13: 'v7E-M', + 14: 'v8', + 15: 'v8-R', + 16: 'v8-M.baseline', + 17: 'v8-M.mainline', + }, + { #7 TAG_CPU_ARCH_PROFILE + 0x00: 'None', + 0x41: 'Application', + 0x52: 'Realtime', + 0x4D: 'Microcontroller', + 0x53: 'Application or Realtime', + }, + { #8 TAG_ARM_ISA + 0: 'No', + 1: 'Yes', + }, + { #9 TAG_THUMB_ISA + 0: 'No', + 1: 'Thumb-1', + 2: 'Thumb-2', + 3: 'Yes', + }, + { #10 TAG_FP_ARCH + 0: 'No', + 1: 'VFPv1', + 2: 'VFPv2 ', + 3: 'VFPv3', + 4: 'VFPv3-D16', + 5: 'VFPv4', + 6: 'VFPv4-D16', + 7: 'FP ARM v8', + 8: 'FPv5/FP-D16 for ARMv8', + }, + { #11 TAG_WMMX_ARCH + 0: 'No', + 1: 'WMMXv1', + 2: 'WMMXv2', + }, + { #12 TAG_ADVANCED_SIMD_ARCH + 0: 'No', + 1: 'NEONv1', + 2: 'NEONv1 with Fused-MAC', + 3: 'NEON for ARMv8', + 4: 'NEON for ARMv8.1', + }, + { #13 TAG_PCS_CONFIG + 0: 'None', + 1: 'Bare platform', + 2: 'Linux application', + 3: 'Linux DSO', + 4: 'PalmOS 2004', + 5: 'PalmOS (reserved)', + 6: 'SymbianOS 2004', + 7: 'SymbianOS (reserved)', + }, + { #14 TAG_ABI_PCS_R9_USE + 0: 'v6', + 1: 'SB', + 2: 'TLS', + 3: 'Unused', + }, + { #15 TAG_ABI_PCS_RW_DATA + 0: 'Absolute', + 1: 'PC-relative', + 2: 'SB-relative', + 3: 'None', + }, + { #16 TAG_ABI_PCS_RO_DATA + 0: 'Absolute', + 1: 'PC-relative', + 2: 'None', + }, + { #17 TAG_ABI_PCS_GOT_USE + 0: 'None', + 1: 'direct', + 2: 'GOT-indirect', + }, + { #18 TAG_ABI_PCS_WCHAR_T + 0: 'None', + 1: '??? 1', + 2: '2', + 3: '??? 3', + 4: '4', + }, + { #19 TAG_ABI_FP_ROUNDING + 0: 'Unused', + 1: 'Needed', + }, + { #20 TAG_ABI_FP_DENORMAL + 0: 'Unused', + 1: 'Needed', + 2: 'Sign only', + }, + { #21 TAG_ABI_FP_EXCEPTIONS + 0: 'Unused', + 1: 'Needed', + }, + { #22 TAG_ABI_FP_USER_EXCEPTIONS + 0: 'Unused', + 1: 'Needed', + }, + { #23 TAG_ABI_FP_NUMBER_MODEL + 0: 'Unused', + 1: 'Finite', + 2: 'RTABI', + 3: 'IEEE 754', + }, + { #24 TAG_ABI_ALIGN_NEEDED + 0: 'None', + 1: '8-byte', + 2: '4-byte', + 3: '??? 3', + }, + { #25 TAG_ABI_ALIGN_PRESERVED + 0: 'None', + 1: '8-byte, except leaf SP', + 2: '8-byte', + 3: '??? 3', + }, + { #26 TAG_ABI_ENUM_SIZE + 0: 'Unused', + 1: 'small', + 2: 'int', + 3: 'forced to int', + }, + { #27 TAG_ABI_HARDFP_USE + 0: 'As Tag_FP_arch', + 1: 'SP only', + 2: 'Reserved', + 3: 'Deprecated', + }, + { #28 TAG_ABI_VFP_ARGS + 0: 'AAPCS', + 1: 'VFP registers', + 2: 'custom', + 3: 'compatible', + }, + { #29 TAG_ABI_WMMX_ARGS + 0: 'AAPCS', + 1: 'WMMX registers', + 2: 'custom', + }, + { #30 TAG_ABI_OPTIMIZATION_GOALS + 0: 'None', + 1: 'Prefer Speed', + 2: 'Aggressive Speed', + 3: 'Prefer Size', + 4: 'Aggressive Size', + 5: 'Prefer Debug', + 6: 'Aggressive Debug', + }, + { #31 TAG_ABI_FP_OPTIMIZATION_GOALS + 0: 'None', + 1: 'Prefer Speed', + 2: 'Aggressive Speed', + 3: 'Prefer Size', + 4: 'Aggressive Size', + 5: 'Prefer Accuracy', + 6: 'Aggressive Accuracy', + }, + { #32 TAG_COMPATIBILITY + 0: 'No', + 1: 'Yes', + }, + None, #33 + { #34 TAG_CPU_UNALIGNED_ACCESS + 0: 'None', + 1: 'v6', + }, + None, #35 + { #36 TAG_FP_HP_EXTENSION + 0: 'Not Allowed', + 1: 'Allowed', + }, + None, #37 + { #38 TAG_ABI_FP_16BIT_FORMAT + 0: 'None', + 1: 'IEEE 754', + 2: 'Alternative Format', + }, + None, #39 + None, #40 + None, #41 + { #42 TAG_MPEXTENSION_USE + 0: 'Not Allowed', + 1: 'Allowed', + }, + None, #43 + { #44 TAG_DIV_USE + 0: 'Allowed in Thumb-ISA, v7-R or v7-M', + 1: 'Not allowed', + 2: 'Allowed in v7-A with integer division extension', + }, + None, #45 + None, #46 + None, #47 + None, #48 + None, #49 + None, #50 + None, #51 + None, #52 + None, #53 + None, #54 + None, #55 + None, #56 + None, #57 + None, #58 + None, #59 + None, #60 + None, #61 + None, #62 + None, #63 + None, #64 + None, #65 + { #66 TAG_FP_HP_EXTENSION + 0: 'Not Allowed', + 1: 'Allowed', + }, + None, #67 + { #68 TAG_VIRTUALIZATION_USE + 0: 'Not Allowed', + 1: 'TrustZone', + 2: 'Virtualization Extensions', + 3: 'TrustZone and Virtualization Extensions', + }, + None, #69 + { #70 TAG_MPEXTENSION_USE_OLD + 0: 'Not Allowed', + 1: 'Allowed', + }, +] diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py index 584e016..815e941 100644 --- a/elftools/elf/elffile.py +++ b/elftools/elf/elffile.py @@ -25,7 +25,7 @@ from .structs import ELFStructs from .sections import ( Section, StringTableSection, SymbolTableSection, SUNWSyminfoTableSection, NullSection, NoteSection, - StabSection) + StabSection, ARMAttributesSection) from .dynamic import DynamicSection, DynamicSegment from .relocation import RelocationSection, RelocationHandler from .gnuversions import ( @@ -324,6 +324,8 @@ class ELFFile(object): return NoteSection(section_header, name, self) elif sectype == 'SHT_PROGBITS' and name == '.stab': return StabSection(section_header, name, self) + elif sectype == 'SHT_ARM_ATTRIBUTES': + return ARMAttributesSection(section_header, name, self) else: return Section(section_header, name, self) diff --git a/elftools/elf/enums.py b/elftools/elf/enums.py index 81798ac..dede762 100644 --- a/elftools/elf/enums.py +++ b/elftools/elf/enums.py @@ -700,6 +700,7 @@ ENUM_VERSYM = dict( VER_NDX_ELIMINATE=0xff01, _default_=Pass, ) + # Sunw Syminfo Bound To special values ENUM_SUNW_SYMINFO_BOUNDTO = dict( SYMINFO_BT_SELF=0xffff, @@ -983,3 +984,49 @@ ENUM_RELOC_TYPE_AARCH64 = dict( R_AARCH64_TLS_DTPMOD32=1032, R_AARCH64_TLS_TPREL32=1033, ) + +ENUM_ATTR_TAG_ARM = dict( + TAG_FILE=1, + TAG_SECTION=2, + TAG_SYMBOL=3, + TAG_CPU_RAW_NAME=4, + TAG_CPU_NAME=5, + TAG_CPU_ARCH=6, + TAG_CPU_ARCH_PROFILE=7, + TAG_ARM_ISA_USE=8, + TAG_THUMB_ISA_USE=9, + TAG_FP_ARCH=10, + TAG_WMMX_ARCH=11, + TAG_ADVANCED_SIMD_ARCH=12, + TAG_PCS_CONFIG=13, + TAG_ABI_PCS_R9_USE=14, + TAG_ABI_PCS_RW_DATA=15, + TAG_ABI_PCS_RO_DATA=16, + TAG_ABI_PCS_GOT_USE=17, + TAG_ABI_PCS_WCHAR_T=18, + TAG_ABI_FP_ROUNDING=19, + TAG_ABI_FP_DENORMAL=20, + TAG_ABI_FP_EXCEPTIONS=21, + TAG_ABI_FP_USER_EXCEPTIONS=22, + TAG_ABI_FP_NUMBER_MODEL=23, + TAG_ABI_ALIGN_NEEDED=24, + TAG_ABI_ALIGN_PRESERVED=25, + TAG_ABI_ENUM_SIZE=26, + TAG_ABI_HARDFP_USE=27, + TAG_ABI_VFP_ARGS=28, + TAG_ABI_WMMX_ARGS=29, + TAG_ABI_OPTIMIZATION_GOALS=30, + TAG_ABI_FP_OPTIMIZATION_GOALS=31, + TAG_COMPATIBILITY=32, + TAG_CPU_UNALIGNED_ACCESS=34, + TAG_FP_HP_EXTENSION=36, + TAG_ABI_FP_16BIT_FORMAT=38, + TAG_MPEXTENSION_USE=42, + TAG_DIV_USE=44, + TAG_NODEFAULTS=64, + TAG_ALSO_COMPATIBLE_WITH=65, + TAG_T2EE_USE=66, + TAG_CONFORMANCE=67, + TAG_VIRTUALIZATION_USE=68, + TAG_MPEXTENSION_USE_OLD=70, +) diff --git a/elftools/elf/sections.py b/elftools/elf/sections.py index eb4d40a..c2d9220 100644 --- a/elftools/elf/sections.py +++ b/elftools/elf/sections.py @@ -11,6 +11,7 @@ from ..common.utils import struct_parse, elf_assert, parse_cstring_from_stream from collections import defaultdict from .constants import SH_FLAGS from .notes import iter_notes + import zlib @@ -253,6 +254,7 @@ class NoteSection(Section): """ return iter_notes(self.elffile, self['sh_offset'], self['sh_size']) + class StabSection(Section): """ ELF stab section. """ @@ -272,3 +274,212 @@ class StabSection(Section): self.stream.seek(offset) yield stabs + +class ARMAttribute(object): + """ ARM attribute object - representing a build attribute of ARM ELF files. + """ + def __init__(self, structs, stream): + self._tag = struct_parse(structs.Elf_Attribute_Tag, stream) + self.extra = None + + if self.tag in ('TAG_FILE', 'TAG_SECTION', 'TAG_SYMBOL'): + self.value = struct_parse(structs.Elf_word('value'), stream) + + if self.tag != 'TAG_FILE': + self.extra = [] + s_number = struct_parse(self.structs.Elf_uleb128('s_number'), + self.stream + ) + + while s_number != 0: + self.extra.append(s_number) + s_number = struct_parse(self.structs.Elf_uleb128('s_number'), + self.stream + ) + + elif self.tag in ('TAG_CPU_RAW_NAME', 'TAG_CPU_NAME', 'TAG_CONFORMANCE'): + self.value = struct_parse(structs.Elf_ntbs('value', + encoding='ascii'), + stream) + + elif self.tag == 'TAG_COMPATIBILITY': + self.value = struct_parse(structs.Elf_uleb128('value'), stream) + self.extra = struct_parse(structs.Elf_ntbs('vendor_name', + encoding='ascii'), + stream) + + elif self.tag == 'TAG_ALSO_COMPATIBLE_WITH': + self.value = ARMAttribute(structs, stream) + + if type(self.value.value) is not str: + nul = struct_parse(structs.Elf_byte('nul'), stream) + elf_assert(null_byte == 0, + "Invalid terminating byte %r, expecting NUL." % nul) + + else: + self.value = struct_parse(structs.Elf_uleb128('value'), stream) + + @property + def tag(self): + return self._tag['tag'] + + def __repr__(self): + s = '' % (self.tag, self.value) + s += ' %s' % self.extra if self.extra is not None else '' + return s + + +class ARMAttributesSubsubsection(object): + """ Subsubsection of an ELF .ARM.attributes section's subsection. + """ + def __init__(self, stream, structs, offset): + self.stream = stream + self.offset = offset + self.structs = structs + + self.header = ARMAttribute(self.structs, self.stream) + + self.attr_start = self.stream.tell() + + def iter_attributes(self, tag=None): + """ Yield all attributes (limit to |tag| if specified). + """ + for attribute in self._make_attributes(): + if tag is None or attribute.tag == tag: + yield attribute + + @property + def num_attributes(self): + """ Number of attributes in the subsubsection. + """ + return sum(1 for _ in self.iter_attributes()) + 1 + + @property + def attributes(self): + """ List of all attributes in the subsubsection. + """ + return [self.header] + list(self.iter_attributes()) + + def _make_attributes(self): + """ Create all attributes for this subsubsection except the first one + which is the header. + """ + end = self.offset + self.header.value + + self.stream.seek(self.attr_start) + + while self.stream.tell() != end: + yield ARMAttribute(self.structs, self.stream) + + def __repr__(self): + s = "" + return s % (self.header.tag[4:], self.header.value) + + +class ARMAttributesSubsection(object): + """ Subsection of an ELF .ARM.attributes section. + """ + def __init__(self, stream, structs, offset): + self.stream = stream + self.offset = offset + self.structs = structs + + self.header = struct_parse(self.structs.Elf_Attr_Subsection_Header, + self.stream, + self.offset + ) + + self.subsubsec_start = self.stream.tell() + + def iter_subsubsections(self, scope=None): + """ Yield all subsubsections (limit to |scope| if specified). + """ + for subsubsec in self._make_subsubsections(): + if scope is None or subsubsec.header.tag == scope: + yield subsubsec + + @property + def num_subsubsections(self): + """ Number of subsubsections in the subsection. + """ + return sum(1 for _ in self.iter_subsubsections()) + + @property + def subsubsections(self): + """ List of all subsubsections in the subsection. + """ + return list(self.iter_subsubsections()) + + def _make_subsubsections(self): + """ Create all subsubsections for this subsection. + """ + end = self.offset + self['length'] + + self.stream.seek(self.subsubsec_start) + + while self.stream.tell() != end: + subsubsec = ARMAttributesSubsubsection(self.stream, + self.structs, + self.stream.tell()) + self.stream.seek(self.subsubsec_start + subsubsec.header.value) + yield subsubsec + + def __getitem__(self, name): + """ Implement dict-like access to header entries. + """ + return self.header[name] + + def __repr__(self): + s = "" + return s % (self.header['vendor_name'], self.header['length']) + + +class ARMAttributesSection(Section): + """ ELF .ARM.attributes section. + """ + def __init__(self, header, name, elffile): + super(ARMAttributesSection, self).__init__(header, name, elffile) + + fv = struct_parse(self.structs.Elf_byte('format_version'), + self.stream, + self['sh_offset'] + ) + + elf_assert(chr(fv) == 'A', + "Unknown attributes version %s, expecting 'A'." % chr(fv) + ) + + self.subsec_start = self.stream.tell() + + def iter_subsections(self, vendor_name=None): + """ Yield all subsections (limit to |vendor_name| if specified). + """ + for subsec in self._make_subsections(): + if vendor_name is None or subsec['vendor_name'] == vendor_name: + yield subsec + + @property + def num_subsections(self): + """ Number of subsections in the section. + """ + return sum(1 for _ in self.iter_subsections()) + + @property + def subsections(self): + """ List of all subsections in the section. + """ + return list(self.iter_subsections()) + + def _make_subsections(self): + """ Create all subsections for this section. + """ + end = self['sh_offset'] + self.data_size + + self.stream.seek(self.subsec_start) + + while self.stream.tell() != end: + subsec = ARMAttributesSubsection(self.stream, + self.structs, + self.stream.tell()) + self.stream.seek(self.subsec_start + subsec['length']) + yield subsec diff --git a/elftools/elf/structs.py b/elftools/elf/structs.py index 8cc87d2..4fb665e 100644 --- a/elftools/elf/structs.py +++ b/elftools/elf/structs.py @@ -11,9 +11,9 @@ 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 ) - +from ..common.construct_utils import ULEB128 from .enums import * @@ -69,6 +69,8 @@ class ELFStructs(object): self.Elf_xword = UBInt32 if self.elfclass == 32 else UBInt64 self.Elf_sxword = SBInt32 if self.elfclass == 32 else SBInt64 self._create_ehdr() + self._create_leb128() + self._create_ntbs() def create_advanced_structs(self, elftype=None): """ Create all ELF structs except the ehdr. They may possibly depend @@ -87,6 +89,7 @@ class ELFStructs(object): self._create_gnu_abi() self._create_note(elftype) self._create_stabs() + self._create_arm_attributes() #-------------------------------- PRIVATE --------------------------------# @@ -116,6 +119,12 @@ class ELFStructs(object): self.Elf_half('e_shstrndx'), ) + def _create_leb128(self): + self.Elf_uleb128 = ULEB128 + + def _create_ntbs(self): + self.Elf_ntbs = CString + def _create_phdr(self): if self.elfclass == 32: self.Elf_Phdr = Struct('Elf_Phdr', @@ -347,3 +356,19 @@ class ELFStructs(object): self.Elf_half('n_desc'), self.Elf_word('n_value'), ) + + def _create_arm_attributes(self): + # Structure of a build attributes subsection header. A subsection is + # either public to all tools that process the ELF file or private to + # the vendor's tools. + self.Elf_Attr_Subsection_Header = Struct('Elf_Attr_Subsection', + self.Elf_word('length'), + self.Elf_ntbs('vendor_name', + encoding='ascii') + ) + + # Structure of a build attribute tag. + self.Elf_Attribute_Tag = Struct('Elf_Attribute_Tag', + Enum(self.Elf_uleb128('tag'), + **ENUM_ATTR_TAG_ARM) + ) diff --git a/scripts/readelf.py b/scripts/readelf.py index 915d93b..a09ba7d 100755 --- a/scripts/readelf.py +++ b/scripts/readelf.py @@ -37,7 +37,7 @@ from elftools.elf.descriptions import ( describe_sh_type, describe_sh_flags, describe_symbol_type, describe_symbol_bind, describe_symbol_visibility, describe_symbol_shndx, describe_reloc_type, describe_dyn_tag, - describe_ver_flags, describe_note + describe_ver_flags, describe_note, describe_attr_tag_arm ) from elftools.elf.constants import E_FLAGS from elftools.dwarf.dwarfinfo import DWARFInfo @@ -589,6 +589,12 @@ class ReadElf(object): offset += verneed['vn_next'] + def display_arch_specific(self): + """ Display the architecture-specific info contained in the file. + """ + if self.elffile['e_machine'] == 'EM_ARM': + self._display_arch_specific_arm() + def display_hex_dump(self, section_spec): """ Display a hex dump of a section. section_spec is either a section number or a name. @@ -1192,6 +1198,23 @@ class ReadElf(object): self._dwarfinfo.debug_frame_sec, self._dwarfinfo.CFI_entries()) + def _display_arch_specific_arm(self): + """ Display the ARM architecture-specific info contained in the file. + """ + attr_sec = self.elffile.get_section_by_name('.ARM.attributes') + + for s in attr_sec.iter_subsections(): + self._emitline("Attribute Section: %s" % s.header['vendor_name']) + for ss in s.iter_subsubsections(): + h_val = "" if ss.header.extra is None else " ".join("%d" % x for x in ss.header.extra) + self._emitline(describe_attr_tag_arm(ss.header.tag, h_val, None)) + + for attr in ss.iter_attributes(): + self._emit(' ') + self._emitline(describe_attr_tag_arm(attr.tag, + attr.value, + attr.extra)) + def _emit(self, s=''): """ Emit an object to output """ @@ -1251,6 +1274,9 @@ def main(stream=None): optparser.add_option('-V', '--version-info', action='store_true', dest='show_version_info', help='Display the version sections (if present)') + optparser.add_option('-A', '--arch-specific', + action='store_true', dest='show_arch_specific', + help='Display the architecture-specific information (if present)') optparser.add_option('--debug-dump', action='store', dest='debug_dump_what', metavar='', help=( @@ -1291,6 +1317,8 @@ def main(stream=None): readelf.display_relocations() if options.show_version_info: readelf.display_version_info() + if options.show_arch_specific: + readelf.display_arch_specific() if options.show_hex_dump: readelf.display_hex_dump(options.show_hex_dump) if options.show_string_dump: diff --git a/test/test_arm_support.py b/test/test_arm_support.py index 87b1805..6b94f7c 100644 --- a/test/test_arm_support.py +++ b/test/test_arm_support.py @@ -22,6 +22,28 @@ class TestARMSupport(unittest.TestCase): self.assertEqual(elf.num_sections(), 14) self.assertEqual(elf.num_segments(), 2) + def test_build_attributes(self): + with open(os.path.join('test', 'testfiles_for_unittests', + 'simple_gcc.elf.arm'), 'rb') as f: + elf = ELFFile(f) + + sec = elf.get_section_by_name('.ARM.attributes') + self.assertEqual(sec['sh_type'], 'SHT_ARM_ATTRIBUTES') + self.assertEqual(sec.num_subsections, 1) + + subsec = sec.subsections[0] + self.assertEqual(subsec.header['vendor_name'], 'aeabi') + self.assertEqual(subsec.num_subsubsections, 1) + + subsubsec = subsec.subsubsections[0] + self.assertEqual(subsubsec.header.tag, 'TAG_FILE') + + for i in subsubsec.iter_attributes('TAG_CPU_NAME'): + self.assertEqual(i.value, 'ARM7TDMI-S') + + for i in subsubsec.iter_attributes('TAG_CPU_ARCH'): + self.assertEqual(i.value, 2) + def test_DWARF_indirect_forms(self): # This file uses a lot of DW_FORM_indirect, and is also an ARM ELF # with non-trivial DWARF info. -- 2.30.2