Add support for RISC-V attributes (#459)
authorRicardo Barbedo <13098357+barbedo@users.noreply.github.com>
Sat, 4 Feb 2023 00:41:41 +0000 (01:41 +0100)
committerGitHub <noreply@github.com>
Sat, 4 Feb 2023 00:41:41 +0000 (16:41 -0800)
* Add support for RISC-V attributes

* Fix typos and formatting

12 files changed:
elftools/elf/constants.py
elftools/elf/descriptions.py
elftools/elf/elffile.py
elftools/elf/enums.py
elftools/elf/sections.py
elftools/elf/structs.py
scripts/dwarfdump.py
scripts/readelf.py
test/test_riscv_support.py [new file with mode: 0644]
test/testfiles_for_readelf/simple_riscv_gcc.o.elf [new file with mode: 0644]
test/testfiles_for_unittests/simple_gcc.elf.riscv [new file with mode: 0644]
test/testfiles_for_unittests/simple_gcc.riscv.c [new file with mode: 0644]

index fc55aacaf673452565123495576b9e68d2fcfadd..7cb215927c65c8528d781852f19bafd566523d43 100644 (file)
@@ -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
index bea5fbc88e0ef2d233e0427172386e36dda7f1c9..ff1edbf1403857573d52437bf35a56cad05342bb 100644 (file)
@@ -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',
+    },
+]
index 59f657b720624337d116c6d57b819d7b25fee05d..0a945c6b6f4cff0f3dd06f211d62dbbc3279c8ce 100644 (file)
@@ -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':
index 745aefc793a71a631ed2817c40aaa0bdaacb8274..6bcd4bb8a12d920f296fe03977bd4aee1258a965 100644 (file)
@@ -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(
index 9a97a09f37897c7be30e9bf3955e9baf3c011032..3805962e7b6565c03e100be41123f3818a5854d9 100644 (file)
@@ -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 = '<ARMAttribute (%s): %r>' % (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 = "<ARMAttributesSubsubsection (%s): %d bytes>"
-        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 = "<ARMAttributesSubsection (%s): %d bytes>"
-        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)
index 74ea54d3ad29cec2b7a5f217a247d58b44b10369..1d2cd6b27930230bc0f1de761eb53d7d3533712a 100644 (file)
@@ -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):
index 10464e4496c43915f4bb71894e3670df71c1c8f7..1ab083205909f8dc623c95d39b89e22fce9f79b8 100644 (file)
@@ -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))
index bca7a5b85d9a84386c3651b7c32cc0850f786b7f..f44a0beaf6d71aa51682a8b5b66619d4119d22c7 100755 (executable)
@@ -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 <End of list>' % (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 (file)
index 0000000..f2bc749
--- /dev/null
@@ -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 (file)
index 0000000..5c32f28
Binary files /dev/null and b/test/testfiles_for_readelf/simple_riscv_gcc.o.elf differ
diff --git a/test/testfiles_for_unittests/simple_gcc.elf.riscv b/test/testfiles_for_unittests/simple_gcc.elf.riscv
new file mode 100644 (file)
index 0000000..5c32f28
Binary files /dev/null and b/test/testfiles_for_unittests/simple_gcc.elf.riscv differ
diff --git a/test/testfiles_for_unittests/simple_gcc.riscv.c b/test/testfiles_for_unittests/simple_gcc.riscv.c
new file mode 100644 (file)
index 0000000..721f294
--- /dev/null
@@ -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;
+}