From 43e77712efded6ae3f0ab5f9773af5612179b1ed Mon Sep 17 00:00:00 2001 From: Ricardo Barbedo <13098357+barbedo@users.noreply.github.com> Date: Sat, 4 Feb 2023 01:41:41 +0100 Subject: [PATCH] Add support for RISC-V attributes (#459) * Add support for RISC-V attributes * Fix typos and formatting --- elftools/elf/constants.py | 9 + elftools/elf/descriptions.py | 46 +++- elftools/elf/elffile.py | 4 +- elftools/elf/enums.py | 17 ++ elftools/elf/sections.py | 229 ++++++++++++------ elftools/elf/structs.py | 22 +- scripts/dwarfdump.py | 2 +- scripts/readelf.py | 44 +++- test/test_riscv_support.py | 47 ++++ .../simple_riscv_gcc.o.elf | Bin 0 -> 6632 bytes .../simple_gcc.elf.riscv | Bin 0 -> 6632 bytes .../simple_gcc.riscv.c | 11 + 12 files changed, 342 insertions(+), 89 deletions(-) create mode 100644 test/test_riscv_support.py create mode 100644 test/testfiles_for_readelf/simple_riscv_gcc.o.elf create mode 100644 test/testfiles_for_unittests/simple_gcc.elf.riscv create mode 100644 test/testfiles_for_unittests/simple_gcc.riscv.c diff --git a/elftools/elf/constants.py b/elftools/elf/constants.py index fc55aac..7cb2159 100644 --- a/elftools/elf/constants.py +++ b/elftools/elf/constants.py @@ -51,6 +51,15 @@ class E_FLAGS(object): EF_MIPS_ARCH_32R2=0x70000000 EF_MIPS_ARCH_64R2=0x80000000 + EF_RISCV_RVC=0x00000001 + EF_RISCV_FLOAT_ABI=0x00000006 + EF_RISCV_FLOAT_ABI_SOFT=0x00000000 + EF_RISCV_FLOAT_ABI_SINGLE=0x00000002 + EF_RISCV_FLOAT_ABI_DOUBLE=0x00000004 + EF_RISCV_FLOAT_ABI_QUAD=0x00000006 + EF_RISCV_RVE=0x00000008 + EF_RISCV_TSO=0x00000010 + class E_FLAGS_MASKS(object): """Masks to be used for convenience when working with E_FLAGS diff --git a/elftools/elf/descriptions.py b/elftools/elf/descriptions.py index bea5fbc..ff1edbf 100644 --- a/elftools/elf/descriptions.py +++ b/elftools/elf/descriptions.py @@ -10,7 +10,8 @@ from .enums import ( ENUM_D_TAG, ENUM_E_VERSION, ENUM_P_TYPE_BASE, ENUM_SH_TYPE_BASE, ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64, ENUM_RELOC_TYPE_ARM, ENUM_RELOC_TYPE_AARCH64, ENUM_RELOC_TYPE_PPC64, - ENUM_RELOC_TYPE_MIPS, ENUM_ATTR_TAG_ARM, ENUM_DT_FLAGS, ENUM_DT_FLAGS_1) + ENUM_RELOC_TYPE_MIPS, ENUM_ATTR_TAG_ARM, ENUM_ATTR_TAG_RISCV, + ENUM_DT_FLAGS, ENUM_DT_FLAGS_1) from .constants import ( P_FLAGS, RH_FLAGS, SH_FLAGS, SUNW_SYMINFO_FLAGS, VER_FLAGS) from ..common.utils import bytes2hex @@ -251,6 +252,19 @@ def describe_attr_tag_arm(tag, val, extra): else: return _DESCR_ATTR_TAG_ARM[tag] + d_entry[val] +def describe_attr_tag_riscv(tag, val, extra): + idx = ENUM_ATTR_TAG_RISCV[tag] - 1 + d_entry = _DESCR_ATTR_VAL_RISCV[idx] + + if d_entry is None: + s = _DESCR_ATTR_TAG_RISCV[tag] + s += '"%s"' % val if val else '' + return s + + else: + return _DESCR_ATTR_TAG_RISCV[tag] + d_entry[val] + + def describe_note_gnu_property_x86_feature_1(value): descs = [] @@ -389,6 +403,7 @@ _DESCR_E_MACHINE = dict( EM_BLACKFIN='Analog Devices Blackfin', EM_PPC='PowerPC', EM_PPC64='PowerPC64', + EM_RISCV='RISC-V', RESERVED='RESERVED', ) @@ -410,7 +425,8 @@ _DESCR_P_TYPE = dict( PT_AARCH64_ARCHEXT='AARCH64_ARCHEXT', PT_AARCH64_UNWIND='AARCH64_UNWIND', PT_TLS='TLS', - PT_MIPS_ABIFLAGS='ABIFLAGS' + PT_MIPS_ABIFLAGS='ABIFLAGS', + PT_RISCV_ATTRIBUTES='RISCV_ATTRIBUT', ) @@ -450,6 +466,7 @@ _DESCR_SH_TYPE = dict( SHT_ARM_PREEMPTMAP='ARM_PREEMPTMAP', SHT_ARM_ATTRIBUTES='ARM_ATTRIBUTES', SHT_ARM_DEBUGOVERLAY='ARM_DEBUGOVERLAY', + SHT_RISCV_ATTRIBUTES='RISCV_ATTRIBUTES', SHT_MIPS_LIBLIST='MIPS_LIBLIST', SHT_MIPS_DEBUG='MIPS_DEBUG', SHT_MIPS_REGINFO='MIPS_REGINFO', @@ -731,7 +748,6 @@ _DESCR_ATTR_TAG_ARM = dict( TAG_MPEXTENSION_USE_OLD='Tag_MPextension_use_old: ', ) - _DESCR_ATTR_VAL_ARM = [ None, #1 None, #2 @@ -984,3 +1000,27 @@ _DESCR_ATTR_VAL_ARM = [ 1: 'Allowed', }, ] + +_DESCR_ATTR_TAG_RISCV = dict( + TAG_FILE='File Attributes', + TAG_SECTION='Section Attributes:', + TAG_SYMBOL='Symbol Attributes:', + TAG_STACK_ALIGN='Tag_RISCV_stack_align: ', + TAG_ARCH='Tag_RISCV_arch: ', + TAG_UNALIGNED='Tag_RISCV_unaligned_access: ', +) + +_DESCR_ATTR_VAL_RISCV = [ + None, #1 + None, #2 + None, #3 + { #4 TAG_RISCV_stack_align + 4: '4-bytes', + 16: '16-bytes', + }, + None, #5 TAG_RISCV_arch + { #6 TAG_RISCV_unaligned_access + 0: 'Not Allowed', + 1: 'Allowed', + }, +] diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py index 59f657b..0a945c6 100644 --- a/elftools/elf/elffile.py +++ b/elftools/elf/elffile.py @@ -30,7 +30,7 @@ from .structs import ELFStructs from .sections import ( Section, StringTableSection, SymbolTableSection, SymbolTableIndexSection, SUNWSyminfoTableSection, NullSection, - NoteSection, StabSection, ARMAttributesSection) + NoteSection, StabSection, ARMAttributesSection, RISCVAttributesSection) from .dynamic import DynamicSection, DynamicSegment from .relocation import (RelocationSection, RelocationHandler, RelrRelocationSection) @@ -662,6 +662,8 @@ class ELFFile(object): return StabSection(section_header, name, self) elif sectype == 'SHT_ARM_ATTRIBUTES': return ARMAttributesSection(section_header, name, self) + elif sectype == 'SHT_RISCV_ATTRIBUTES': + return RISCVAttributesSection(section_header, name, self) elif sectype == 'SHT_HASH': return self._make_elf_hash_section(section_header, name) elif sectype == 'SHT_GNU_HASH': diff --git a/elftools/elf/enums.py b/elftools/elf/enums.py index 745aefc..6bcd4bb 100644 --- a/elftools/elf/enums.py +++ b/elftools/elf/enums.py @@ -329,6 +329,10 @@ ENUM_SH_TYPE_ARM = merge_dicts( SHT_ARM_ATTRIBUTES=0x70000003, SHT_ARM_DEBUGOVERLAY=0x70000004)) +ENUM_SH_TYPE_RISCV = merge_dicts( + ENUM_SH_TYPE_BASE, + dict(SHT_RISCV_ATTRIBUTES=0x70000003)) + ENUM_SH_TYPE_MIPS = merge_dicts( ENUM_SH_TYPE_BASE, dict( @@ -422,6 +426,10 @@ ENUM_P_TYPE_MIPS = merge_dicts( ENUM_P_TYPE_BASE, dict(PT_MIPS_ABIFLAGS=0x70000003)) +ENUM_P_TYPE_RISCV = merge_dicts( + ENUM_P_TYPE_BASE, + dict(PT_RISCV_ATTRIBUTES=0x70000003)) + # st_info bindings in the symbol header ENUM_ST_INFO_BIND = dict( STB_LOCAL=0, @@ -1180,6 +1188,15 @@ ENUM_ATTR_TAG_ARM = dict( TAG_MPEXTENSION_USE_OLD=70, ) +ENUM_ATTR_TAG_RISCV = dict( + TAG_FILE=1, + TAG_SECTION=2, + TAG_SYMBOL=3, + TAG_STACK_ALIGN=4, + TAG_ARCH=5, + TAG_UNALIGNED_ACCESS=6, +) + # https://openpowerfoundation.org/wp-content/uploads/2016/03/ABI64BitOpenPOWERv1.1_16July2015_pub4.pdf # See 3.5.3 Relocation Types Table. ENUM_RELOC_TYPE_PPC64 = dict( diff --git a/elftools/elf/sections.py b/elftools/elf/sections.py index 9a97a09..3805962 100644 --- a/elftools/elf/sections.py +++ b/elftools/elf/sections.py @@ -298,68 +298,34 @@ class StabSection(Section): self.stream.seek(offset) yield stabs - -class ARMAttribute(object): - """ ARM attribute object - representing a build attribute of ARM ELF files. +class Attribute(object): + """ Attribute object - representing a build attribute of ELF files. """ - def __init__(self, structs, stream): - self._tag = struct_parse(structs.Elf_Attribute_Tag, stream) + def __init__(self, tag): + self._tag = tag 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(structs.Elf_uleb128('s_number'), stream) - - while s_number != 0: - self.extra.append(s_number) - s_number = struct_parse(structs.Elf_uleb128('s_number'), - stream - ) - - elif self.tag in ('TAG_CPU_RAW_NAME', 'TAG_CPU_NAME', 'TAG_CONFORMANCE'): - self.value = struct_parse(structs.Elf_ntbs('value', - encoding='utf-8'), - 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='utf-8'), - 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(nul == 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 (%s): %r>' % \ + (self.__class__.__name__, 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. +class AttributesSubsubsection(Section): + """ Subsubsection of an ELF attribute section's subsection. """ - def __init__(self, stream, structs, offset): + def __init__(self, stream, structs, offset, attribute): self.stream = stream self.offset = offset self.structs = structs + self.attribute = attribute - self.header = ARMAttribute(self.structs, self.stream) + self.header = self.attribute(self.structs, self.stream) self.attr_start = self.stream.tell() @@ -391,25 +357,24 @@ class ARMAttributesSubsubsection(object): self.stream.seek(self.attr_start) while self.stream.tell() != end: - yield ARMAttribute(self.structs, self.stream) + yield self.attribute(self.structs, self.stream) def __repr__(self): - s = "" - return s % (self.header.tag[4:], self.header.value) + s = "<%s (%s): %d bytes>" + return s % (self.__class__.__name__, + self.header.tag[4:], self.header.value) -class ARMAttributesSubsection(object): - """ Subsection of an ELF .ARM.attributes section. +class AttributesSubsection(Section): + """ Subsection of an ELF attributes section. """ - def __init__(self, stream, structs, offset): + def __init__(self, stream, structs, offset, header, subsubsection): self.stream = stream self.offset = offset self.structs = structs + self.subsubsection = subsubsection - self.header = struct_parse(self.structs.Elf_Attr_Subsection_Header, - self.stream, - self.offset - ) + self.header = struct_parse(header, self.stream, self.offset) self.subsubsec_start = self.stream.tell() @@ -440,9 +405,9 @@ class ARMAttributesSubsection(object): self.stream.seek(self.subsubsec_start) while self.stream.tell() != end: - subsubsec = ARMAttributesSubsubsection(self.stream, - self.structs, - self.stream.tell()) + subsubsec = self.subsubsection(self.stream, + self.structs, + self.stream.tell()) self.stream.seek(self.subsubsec_start + subsubsec.header.value) yield subsubsec @@ -452,24 +417,24 @@ class ARMAttributesSubsection(object): return self.header[name] def __repr__(self): - s = "" - return s % (self.header['vendor_name'], self.header['length']) + s = "<%s (%s): %d bytes>" + return s % (self.__class__.__name__, + self.header['vendor_name'], self.header['length']) -class ARMAttributesSection(Section): - """ ELF .ARM.attributes section. +class AttributesSection(Section): + """ ELF attributes section. """ - def __init__(self, header, name, elffile): - super(ARMAttributesSection, self).__init__(header, name, elffile) + def __init__(self, header, name, elffile, subsection): + super(AttributesSection, self).__init__(header, name, elffile) + self.subsection = subsection fv = struct_parse(self.structs.Elf_byte('format_version'), self.stream, - self['sh_offset'] - ) + self['sh_offset']) elf_assert(chr(fv) == 'A', - "Unknown attributes version %s, expecting 'A'." % chr(fv) - ) + "Unknown attributes version %s, expecting 'A'." % chr(fv)) self.subsec_start = self.stream.tell() @@ -500,8 +465,130 @@ class ARMAttributesSection(Section): self.stream.seek(self.subsec_start) while self.stream.tell() != end: - subsec = ARMAttributesSubsection(self.stream, - self.structs, - self.stream.tell()) + subsec = self.subsection(self.stream, + self.structs, + self.stream.tell()) self.stream.seek(self.subsec_start + subsec['length']) yield subsec + + +class ARMAttribute(Attribute): + """ ARM attribute object - representing a build attribute of ARM ELF files. + """ + def __init__(self, structs, stream): + super(ARMAttribute, self).__init__( + struct_parse(structs.Elf_Arm_Attribute_Tag, stream)) + + 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(structs.Elf_uleb128('s_number'), stream) + + while s_number != 0: + self.extra.append(s_number) + s_number = struct_parse(structs.Elf_uleb128('s_number'), + stream) + + elif self.tag in ('TAG_CPU_RAW_NAME', 'TAG_CPU_NAME', 'TAG_CONFORMANCE'): + self.value = struct_parse(structs.Elf_ntbs('value', + encoding='utf-8'), + 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='utf-8'), + 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(nul == 0, + "Invalid terminating byte %r, expecting NUL." % nul) + + else: + self.value = struct_parse(structs.Elf_uleb128('value'), stream) + + +class ARMAttributesSubsubsection(AttributesSubsubsection): + """ Subsubsection of an ELF .ARM.attributes section's subsection. + """ + def __init__(self, stream, structs, offset): + super(ARMAttributesSubsubsection, self).__init__( + stream, structs, offset, ARMAttribute) + + +class ARMAttributesSubsection(AttributesSubsection): + """ Subsection of an ELF .ARM.attributes section. + """ + def __init__(self, stream, structs, offset): + super(ARMAttributesSubsection, self).__init__( + stream, structs, offset, + structs.Elf_Attr_Subsection_Header, + ARMAttributesSubsubsection) + + +class ARMAttributesSection(AttributesSection): + """ ELF .ARM.attributes section. + """ + def __init__(self, header, name, elffile): + super(ARMAttributesSection, self).__init__( + header, name, elffile, ARMAttributesSubsection) + + +class RISCVAttribute(Attribute): + """ Attribute of an ELF .riscv.attributes section. + """ + def __init__(self, structs, stream): + super(RISCVAttribute, self).__init__( + struct_parse(structs.Elf_RiscV_Attribute_Tag, stream)) + + 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(structs.Elf_uleb128('s_number'), stream) + + while s_number != 0: + self.extra.append(s_number) + s_number = struct_parse(structs.Elf_uleb128('s_number'), + stream) + + elif self.tag == 'TAG_ARCH': + self.value = struct_parse(structs.Elf_ntbs('value', + encoding='utf-8'), + stream) + + else: + self.value = struct_parse(structs.Elf_uleb128('value'), stream) + + +class RISCVAttributesSubsubsection(AttributesSubsubsection): + """ Subsubsection of an ELF .riscv.attributes subsection. + """ + def __init__(self, stream, structs, offset): + super(RISCVAttributesSubsubsection, self).__init__( + stream, structs, offset, RISCVAttribute) + + +class RISCVAttributesSubsection(AttributesSubsection): + """ Subsection of an ELF .riscv.attributes section. + """ + def __init__(self, stream, structs, offset): + super(RISCVAttributesSubsection, self).__init__( + stream, structs, offset, + structs.Elf_Attr_Subsection_Header, + RISCVAttributesSubsubsection) + + +class RISCVAttributesSection(AttributesSection): + """ ELF .riscv.attributes section. + """ + def __init__(self, header, name, elffile): + super(RISCVAttributesSection, self).__init__( + header, name, elffile, RISCVAttributesSubsection) diff --git a/elftools/elf/structs.py b/elftools/elf/structs.py index 74ea54d..1d2cd6b 100644 --- a/elftools/elf/structs.py +++ b/elftools/elf/structs.py @@ -107,7 +107,9 @@ class ELFStructs(object): self._create_gnu_property() self._create_note(e_type) self._create_stabs() + self._create_attributes_subsection() self._create_arm_attributes() + self._create_riscv_attributes() self._create_elf_hash() self._create_gnu_hash() @@ -153,6 +155,8 @@ class ELFStructs(object): p_type_dict = ENUM_P_TYPE_AARCH64 elif self.e_machine == 'EM_MIPS': p_type_dict = ENUM_P_TYPE_MIPS + elif self.e_machine == 'EM_RISCV': + p_type_dict = ENUM_P_TYPE_RISCV if self.elfclass == 32: self.Elf_Phdr = Struct('Elf_Phdr', @@ -189,6 +193,8 @@ class ELFStructs(object): sh_type_dict = ENUM_SH_TYPE_AMD64 elif self.e_machine == 'EM_MIPS': sh_type_dict = ENUM_SH_TYPE_MIPS + if self.e_machine == 'EM_RISCV': + sh_type_dict = ENUM_SH_TYPE_RISCV self.Elf_Shdr = Struct('Elf_Shdr', self.Elf_word('sh_name'), @@ -499,7 +505,7 @@ class ELFStructs(object): self.Elf_word('n_value'), ) - def _create_arm_attributes(self): + def _create_attributes_subsection(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. @@ -509,10 +515,18 @@ class ELFStructs(object): encoding='utf-8') ) - # Structure of a build attribute tag. - self.Elf_Attribute_Tag = Struct('Elf_Attribute_Tag', + def _create_arm_attributes(self): + # Structure of an ARM build attribute tag. + self.Elf_Arm_Attribute_Tag = Struct('Elf_Arm_Attribute_Tag', + Enum(self.Elf_uleb128('tag'), + **ENUM_ATTR_TAG_ARM) + ) + + def _create_riscv_attributes(self): + # Structure of a RISC-V build attribute tag. + self.Elf_RiscV_Attribute_Tag = Struct('Elf_RiscV_Attribute_Tag', Enum(self.Elf_uleb128('tag'), - **ENUM_ATTR_TAG_ARM) + **ENUM_ATTR_TAG_RISCV) ) def _create_elf_hash(self): diff --git a/scripts/dwarfdump.py b/scripts/dwarfdump.py index 10464e4..1ab0832 100644 --- a/scripts/dwarfdump.py +++ b/scripts/dwarfdump.py @@ -342,7 +342,7 @@ class ReadElf(object): self.elffile = ELFFile(file) self.output = output self._dwarfinfo = self.elffile.get_dwarf_info() - arches = {"EM_386": "i386", "EM_X86_64": "x86-64", "EM_ARM": "littlearm", "EM_AARCH64": "littleaarch64", "EM_LOONGARCH64": "loongarch64"} + arches = {"EM_386": "i386", "EM_X86_64": "x86-64", "EM_ARM": "littlearm", "EM_AARCH64": "littleaarch64", "EM_LOONGARCH64": "loongarch64", "EM_RISCV": "littleriscv"} arch = arches[self.elffile['e_machine']] bits = self.elffile.elfclass self._emitline("%s: file format elf%d-%s" % (filename, bits, arch)) diff --git a/scripts/readelf.py b/scripts/readelf.py index bca7a5b..f44a0be 100755 --- a/scripts/readelf.py +++ b/scripts/readelf.py @@ -47,7 +47,7 @@ from elftools.elf.descriptions import ( describe_symbol_type, describe_symbol_bind, describe_symbol_visibility, describe_symbol_shndx, describe_reloc_type, describe_dyn_tag, describe_dt_flags, describe_dt_flags_1, describe_ver_flags, describe_note, - describe_attr_tag_arm, describe_symbol_other + describe_attr_tag_arm, describe_attr_tag_riscv, describe_symbol_other ) from elftools.elf.constants import E_FLAGS from elftools.elf.constants import E_FLAGS_MASKS @@ -243,6 +243,22 @@ class ReadElf(object): if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_64: description += ", mips64" + elif self.elffile['e_machine'] == "EM_RISCV": + if flags & E_FLAGS.EF_RISCV_RVC: + description += ", RVC" + if (flags & E_FLAGS.EF_RISCV_RVE): + description += ", RVE" + if (flags & E_FLAGS.EF_RISCV_TSO): + description += ", TSO" + if (flags & E_FLAGS.EF_RISCV_FLOAT_ABI) == E_FLAGS.EF_RISCV_FLOAT_ABI_SOFT: + description += ", soft-float ABI" + if (flags & E_FLAGS.EF_RISCV_FLOAT_ABI) == E_FLAGS.EF_RISCV_FLOAT_ABI_SINGLE: + description += ", single-float ABI" + if (flags & E_FLAGS.EF_RISCV_FLOAT_ABI) == E_FLAGS.EF_RISCV_FLOAT_ABI_DOUBLE: + description += ", double-float ABI" + if (flags & E_FLAGS.EF_RISCV_FLOAT_ABI) == E_FLAGS.EF_RISCV_FLOAT_ABI_QUAD: + description += ", quad-float ABI" + return description def display_program_headers(self, show_heading=True): @@ -768,6 +784,8 @@ class ReadElf(object): """ if self.elffile['e_machine'] == 'EM_ARM': self._display_arch_specific_arm() + elif self.elffile['e_machine'] == 'EM_RISCV': + self._display_arch_specific_riscv() def display_hex_dump(self, section_spec): """ Display a hex dump of a section. section_spec is either a section @@ -1648,22 +1666,30 @@ class ReadElf(object): last = range_list[-1] self._emitline(' %08x ' % (last.entry_offset + last.entry_length if ver5 else first.entry_offset)) - def _display_arch_specific_arm(self): - """ Display the ARM architecture-specific info contained in the file. + def _display_attributes(self, attr_sec, descriptor): + """ Display the attributes contained in the section. """ - 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)) + self._emitline(descriptor(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)) + self._emitline(descriptor(attr.tag, attr.value, attr.extra)) + + 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') + self._display_attributes(attr_sec, describe_attr_tag_arm) + + def _display_arch_specific_riscv(self): + """ Display the RISC-V architecture-specific info contained in the file. + """ + attr_sec = self.elffile.get_section_by_name('.riscv.attributes') + self._display_attributes(attr_sec, describe_attr_tag_riscv) def _emit(self, s=''): """ Emit an object to output diff --git a/test/test_riscv_support.py b/test/test_riscv_support.py new file mode 100644 index 0000000..f2bc749 --- /dev/null +++ b/test/test_riscv_support.py @@ -0,0 +1,47 @@ +#------------------------------------------------------------------------------- +# elftools tests +# +# Ricardo Barbedo (ricardo@barbedo.me) +# This code is in the public domain +#------------------------------------------------------------------------------- +import unittest +import os + +from elftools.elf.elffile import ELFFile + +class TestRISCVSupport(unittest.TestCase): + def test_hello(self): + with open(os.path.join('test', 'testfiles_for_unittests', + 'simple_gcc.elf.riscv'), 'rb') as f: + elf = ELFFile(f) + self.assertEqual(elf.get_machine_arch(), 'RISC-V') + + # Check some other properties of this ELF file derived from readelf + self.assertEqual(elf['e_entry'], 0x10116) + self.assertEqual(elf.num_sections(), 13) + self.assertEqual(elf.num_segments(), 3) + + def test_build_attributes(self): + with open(os.path.join('test', 'testfiles_for_unittests', + 'simple_gcc.elf.riscv'), 'rb') as f: + elf = ELFFile(f) + + sec = elf.get_section_by_name('.riscv.attributes') + self.assertEqual(sec['sh_type'], 'SHT_RISCV_ATTRIBUTES') + self.assertEqual(sec.num_subsections, 1) + + subsec = sec.subsections[0] + self.assertEqual(subsec.header['vendor_name'], 'riscv') + self.assertEqual(subsec.num_subsubsections, 1) + + subsubsec = subsec.subsubsections[0] + self.assertEqual(subsubsec.header.tag, 'TAG_FILE') + + for i in subsubsec.iter_attributes('TAG_STACK_ALIGN'): + self.assertEqual(i.value, 16) + + for i in subsubsec.iter_attributes('TAG_ARCH'): + self.assertEqual(i.value, 'rv64i2p0_m2p0_a2p0_f2p0_d2p0_c2p0_v1p0_zfh1p0_zfhmin1p0_zba1p0_zbb1p0_zbc1p0_zbs1p0_zve32f1p0_zve32x1p0_zve64d1p0_zve64f1p0_zve64x1p0_zvl128b1p0_zvl32b1p0_zvl64b1p0') + +if __name__ == '__main__': + unittest.main() diff --git a/test/testfiles_for_readelf/simple_riscv_gcc.o.elf b/test/testfiles_for_readelf/simple_riscv_gcc.o.elf new file mode 100644 index 0000000000000000000000000000000000000000..5c32f282dafec5de08c90f09a83524b9a00df521 GIT binary patch literal 6632 zcmeHMeM}t36@Rz)0VIwCK4g>H#F(R|c9Rna7a)y;IN%Q3YETv1scb1;mc0eex#P}z z3xP;h?}IY}$3)IW;7Y0FLSsw>q^q(~Ju*zgA`wTeI5#HDX` zXO3ez%pd)uisX@I-<#k3-rF~CX6I)2e&+tMhcg&}6%PIhq_vS^wY1~hMolrJsD)}U z;`JF=hY~_2#GYA4i8^sX*JgJjlK5IG={*3=#3>dgAy;b?mL&~u&e!aijAUuN3sf>Y zS6=9Mi!|uDs&O_%*|Q%k-&v z;yL<9H+nXEhB34mtcgZjXf@ zidC*5uXy%qo7=m0GTGPYv1T=REOXnZEBa?@#gj8&J@|61)zsjLHf(85n6{Z>!&7r5 zi*E1Lwq##lqcyA7Lzsjmo2%- zS$uWi;?TOObu+H4slb>2$-5Pcd}0}|9k2{@07FA7hl)(`^Ms>dmQo4dUFZu=QD>A-0;myHjJh7k{$KCW9Ji5kHKSb7oKDD ztIn6nw7c-#?bDXIaU9WJtExTl9o~!w3tgzU;BLc z-6?~cti`M%+pY)Czj-Nsw$Wj5|9Q}M{>!rEyjbMe0c2)hH-~YRiuGnNoWYekxwF?Y z{fa44I}Z_n%Z4-fjNOZ&suc^}PhwQ=Pup_{=bKL$tO%`m{BsKre3pSed6<_?? zxgl_8?r&yz`a3_WCj)qrfhQSwl7S}~_wKV`wh>!eSQ~ev7Ip*DQBcI(_A#GCn{q0=cx3iL;W4)D5+Us|eS8J}i{1xR)t8@~L zjd?X`#5bB*^{r+(!w4L(_lEQyBfizN>+(tZjbMWAjJO$CzL)#4z(j^~j1jV6G9#Xy zZl2POkdxAx@(n5`xVy;OOls$hmE$F^Q*5Fs0md8Ve^SF2p>G-Ry!>7H#G$!jXkfmgS#1j^U%OUf$z0mO50e~4^R2_$T_e2C2rI5L(8 z@-2mwU#a|+`_-q2=W!OeuG_CJ6CA(9u>FngTz{ed**ZL;xraLxXX}AH&!jx0INMLI zXfW9En*xcG?He@|&!IS5CtVaTL_8Ot(*oUicTilv-@Zisu=RhQ`q@Qs_MC2@xQpU! z{Xa$V37UtmXnt58cB}VYdVVP#eyZY@75je)`pFbOPw&fqzn-U#4pW@1|6+E7l5XI6 zQSkeDL6N$Bid_U*YV(B^N#3wL3C1RCl}}%R&z?btrNVw8}z2;=P@LKo104j!2?{#|)lNgL#ZVS!N?k zN~hd`QTPI(PFYGRyjM^JAasQMz7}41F(3uPl7g|5aj0>ph8XS%^K`V5EO!LeUeD{U z*WA3jse$K76lj#Xvv)ZVRs>l=XjycG6OyGBSQ^0WmeUwYQyL2TIxzbs=uV9d<-P{j zeivWoZrszP#^wzN`F5eh>z9B{gq99Iq{zs}@r!A}F=S~-h^TW;auAY(ViiZ*JDR|{Or6Mq4w;2p|@x4Jvw`~?@!Q=SOttor9PgYAWkrrKeiuFQv2#P z>6@Vcdt}q|85vF` nRyKZ?mmI{{*ilWD>sC$m>YTH<88Hu)O!m%=8bd!Yz5RaygA5}$ literal 0 HcmV?d00001 diff --git a/test/testfiles_for_unittests/simple_gcc.elf.riscv b/test/testfiles_for_unittests/simple_gcc.elf.riscv new file mode 100644 index 0000000000000000000000000000000000000000..5c32f282dafec5de08c90f09a83524b9a00df521 GIT binary patch literal 6632 zcmeHMeM}t36@Rz)0VIwCK4g>H#F(R|c9Rna7a)y;IN%Q3YETv1scb1;mc0eex#P}z z3xP;h?}IY}$3)IW;7Y0FLSsw>q^q(~Ju*zgA`wTeI5#HDX` zXO3ez%pd)uisX@I-<#k3-rF~CX6I)2e&+tMhcg&}6%PIhq_vS^wY1~hMolrJsD)}U z;`JF=hY~_2#GYA4i8^sX*JgJjlK5IG={*3=#3>dgAy;b?mL&~u&e!aijAUuN3sf>Y zS6=9Mi!|uDs&O_%*|Q%k-&v z;yL<9H+nXEhB34mtcgZjXf@ zidC*5uXy%qo7=m0GTGPYv1T=REOXnZEBa?@#gj8&J@|61)zsjLHf(85n6{Z>!&7r5 zi*E1Lwq##lqcyA7Lzsjmo2%- zS$uWi;?TOObu+H4slb>2$-5Pcd}0}|9k2{@07FA7hl)(`^Ms>dmQo4dUFZu=QD>A-0;myHjJh7k{$KCW9Ji5kHKSb7oKDD ztIn6nw7c-#?bDXIaU9WJtExTl9o~!w3tgzU;BLc z-6?~cti`M%+pY)Czj-Nsw$Wj5|9Q}M{>!rEyjbMe0c2)hH-~YRiuGnNoWYekxwF?Y z{fa44I}Z_n%Z4-fjNOZ&suc^}PhwQ=Pup_{=bKL$tO%`m{BsKre3pSed6<_?? zxgl_8?r&yz`a3_WCj)qrfhQSwl7S}~_wKV`wh>!eSQ~ev7Ip*DQBcI(_A#GCn{q0=cx3iL;W4)D5+Us|eS8J}i{1xR)t8@~L zjd?X`#5bB*^{r+(!w4L(_lEQyBfizN>+(tZjbMWAjJO$CzL)#4z(j^~j1jV6G9#Xy zZl2POkdxAx@(n5`xVy;OOls$hmE$F^Q*5Fs0md8Ve^SF2p>G-Ry!>7H#G$!jXkfmgS#1j^U%OUf$z0mO50e~4^R2_$T_e2C2rI5L(8 z@-2mwU#a|+`_-q2=W!OeuG_CJ6CA(9u>FngTz{ed**ZL;xraLxXX}AH&!jx0INMLI zXfW9En*xcG?He@|&!IS5CtVaTL_8Ot(*oUicTilv-@Zisu=RhQ`q@Qs_MC2@xQpU! z{Xa$V37UtmXnt58cB}VYdVVP#eyZY@75je)`pFbOPw&fqzn-U#4pW@1|6+E7l5XI6 zQSkeDL6N$Bid_U*YV(B^N#3wL3C1RCl}}%R&z?btrNVw8}z2;=P@LKo104j!2?{#|)lNgL#ZVS!N?k zN~hd`QTPI(PFYGRyjM^JAasQMz7}41F(3uPl7g|5aj0>ph8XS%^K`V5EO!LeUeD{U z*WA3jse$K76lj#Xvv)ZVRs>l=XjycG6OyGBSQ^0WmeUwYQyL2TIxzbs=uV9d<-P{j zeivWoZrszP#^wzN`F5eh>z9B{gq99Iq{zs}@r!A}F=S~-h^TW;auAY(ViiZ*JDR|{Or6Mq4w;2p|@x4Jvw`~?@!Q=SOttor9PgYAWkrrKeiuFQv2#P z>6@Vcdt}q|85vF` nRyKZ?mmI{{*ilWD>sC$m>YTH<88Hu)O!m%=8bd!Yz5RaygA5}$ literal 0 HcmV?d00001 diff --git a/test/testfiles_for_unittests/simple_gcc.riscv.c b/test/testfiles_for_unittests/simple_gcc.riscv.c new file mode 100644 index 0000000..721f294 --- /dev/null +++ b/test/testfiles_for_unittests/simple_gcc.riscv.c @@ -0,0 +1,11 @@ +/* + * Compiled using https://github.com/riscv-collab/riscv-gnu-toolchain with + * multilib support enabled. + * + * riscv64-unknown-elf-gcc -march=rv64gcv_zba_zbb_zbc_zbs_zfh simple_gcc.riscv.c -o simple_gcc.elf.riscv + */ + +int main() +{ + return 42; +} -- 2.30.2