From: Pierre-Marie de Rodat Date: Thu, 17 Aug 2017 03:52:45 +0000 (+0200) Subject: Compressed sections (#152) X-Git-Tag: v0.25~36 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=1d7e6945d939197aa722fcd909522c079c69aa3c;p=pyelftools.git Compressed sections (#152) * Add constants, enums and structs for compressed section * ELFStructs: update comments for new section numbers in Oracle docs * Make the ELFFile's stream/structs available to all Section instances This harmonizes the signature of Section constructors. Besides, structs will be required to read compressed sections. * ELFFile._read_dwarf_section: use Section.data to read the section An upcoming change will add compressed sections handling. In this context, the DWARF must be parsed from the decompressed data, so reading the ELFFile stream will be wrong. * Add ELF compressed debug sections handling --- diff --git a/elftools/common/exceptions.py b/elftools/common/exceptions.py index 26f1ba0..5e409cf 100644 --- a/elftools/common/exceptions.py +++ b/elftools/common/exceptions.py @@ -15,6 +15,8 @@ class ELFRelocationError(ELFError): class ELFParseError(ELFError): pass -class DWARFError(Exception): +class ELFCompressionError(ELFError): pass +class DWARFError(Exception): + pass diff --git a/elftools/elf/constants.py b/elftools/elf/constants.py index df6e0af..3ea3295 100644 --- a/elftools/elf/constants.py +++ b/elftools/elf/constants.py @@ -70,6 +70,7 @@ class SH_FLAGS(object): SHF_OS_NONCONFORMING=0x100 SHF_GROUP=0x200 SHF_TLS=0x400 + SHF_COMPRESSED=0x800 SHF_MASKOS=0x0ff00000 SHF_EXCLUDE=0x80000000 SHF_MASKPROC=0xf0000000 diff --git a/elftools/elf/dynamic.py b/elftools/elf/dynamic.py index 82887ed..9282284 100644 --- a/elftools/elf/dynamic.py +++ b/elftools/elf/dynamic.py @@ -164,10 +164,11 @@ class Dynamic(object): class DynamicSection(Section, Dynamic): """ ELF dynamic table section. Knows how to process the list of tags. """ - def __init__(self, header, name, stream, elffile): - Section.__init__(self, header, name, stream) + def __init__(self, header, name, elffile): + Section.__init__(self, header, name, elffile) stringtable = elffile.get_section(header['sh_link']) - Dynamic.__init__(self, stream, elffile, stringtable, self['sh_offset']) + Dynamic.__init__(self, self.stream, self.elffile, stringtable, + self['sh_offset']) class DynamicSegment(Segment, Dynamic): diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py index 6fd2a73..fedd14d 100644 --- a/elftools/elf/elffile.py +++ b/elftools/elf/elffile.py @@ -297,9 +297,9 @@ class ELFFile(object): sectype = section_header['sh_type'] if sectype == 'SHT_STRTAB': - return StringTableSection(section_header, name, self.stream) + return StringTableSection(section_header, name, self) elif sectype == 'SHT_NULL': - return NullSection(section_header, name, self.stream) + return NullSection(section_header, name, self) elif sectype in ('SHT_SYMTAB', 'SHT_DYNSYM', 'SHT_SUNW_LDYNSYM'): return self._make_symbol_table_section(section_header, name) elif sectype == 'SHT_SUNW_syminfo': @@ -311,16 +311,15 @@ class ELFFile(object): elif sectype == 'SHT_GNU_versym': return self._make_gnu_versym_section(section_header, name) elif sectype in ('SHT_REL', 'SHT_RELA'): - return RelocationSection( - section_header, name, self.stream, self) + return RelocationSection(section_header, name, self) elif sectype == 'SHT_DYNAMIC': - return DynamicSection(section_header, name, self.stream, self) + return DynamicSection(section_header, name, self) elif sectype == 'SHT_NOTE': - return NoteSection(section_header, name, self.stream, self) + return NoteSection(section_header, name, self) elif sectype == 'SHT_PROGBITS' and name == '.stab': - return StabSection(section_header, name, self.stream, self) + return StabSection(section_header, name, self) else: - return Section(section_header, name, self.stream) + return Section(section_header, name, self) def _make_symbol_table_section(self, section_header, name): """ Create a SymbolTableSection @@ -328,7 +327,7 @@ class ELFFile(object): linked_strtab_index = section_header['sh_link'] strtab_section = self.get_section(linked_strtab_index) return SymbolTableSection( - section_header, name, self.stream, + section_header, name, elffile=self, stringtable=strtab_section) @@ -338,7 +337,7 @@ class ELFFile(object): linked_strtab_index = section_header['sh_link'] strtab_section = self.get_section(linked_strtab_index) return SUNWSyminfoTableSection( - section_header, name, self.stream, + section_header, name, elffile=self, symboltable=strtab_section) @@ -348,7 +347,7 @@ class ELFFile(object): linked_strtab_index = section_header['sh_link'] strtab_section = self.get_section(linked_strtab_index) return GNUVerNeedSection( - section_header, name, self.stream, + section_header, name, elffile=self, stringtable=strtab_section) @@ -358,7 +357,7 @@ class ELFFile(object): linked_strtab_index = section_header['sh_link'] strtab_section = self.get_section(linked_strtab_index) return GNUVerDefSection( - section_header, name, self.stream, + section_header, name, elffile=self, stringtable=strtab_section) @@ -368,7 +367,7 @@ class ELFFile(object): linked_strtab_index = section_header['sh_link'] strtab_section = self.get_section(linked_strtab_index) return GNUVerSymSection( - section_header, name, self.stream, + section_header, name, elffile=self, symboltable=strtab_section) @@ -387,7 +386,7 @@ class ELFFile(object): return StringTableSection( header=self._get_section_header(stringtable_section_num), name='', - stream=self.stream) + elffile=self) def _parse_elf_header(self): """ Parses the ELF file header and assigns the result to attributes @@ -399,10 +398,9 @@ class ELFFile(object): """ Read the contents of a DWARF section from the stream and return a DebugSectionDescriptor. Apply relocations if asked to. """ - self.stream.seek(section['sh_offset']) # The section data is read into a new stream, for processing section_stream = BytesIO() - section_stream.write(self.stream.read(section['sh_size'])) + section_stream.write(section.data()) if relocate_dwarf_sections: reloc_handler = RelocationHandler(self) diff --git a/elftools/elf/enums.py b/elftools/elf/enums.py index 904a3ba..13ef68f 100644 --- a/elftools/elf/enums.py +++ b/elftools/elf/enums.py @@ -341,6 +341,15 @@ ENUM_SH_TYPE = dict( _default_=Pass, ) +ENUM_ELFCOMPRESS_TYPE = dict( + ELFCOMPRESS_ZLIB=1, + ELFCOMPRESS_LOOS=0x60000000, + ELFCOMPRESS_HIOS=0x6fffffff, + ELFCOMPRESS_LOPROC=0x70000000, + ELFCOMPRESS_HIPROC=0x7fffffff, + _default_=Pass, +) + # p_type in the program header # some values scavenged from the ELF headers in binutils-2.21 ENUM_P_TYPE = dict( diff --git a/elftools/elf/gnuversions.py b/elftools/elf/gnuversions.py index 4a4473f..3ad8dd1 100644 --- a/elftools/elf/gnuversions.py +++ b/elftools/elf/gnuversions.py @@ -56,10 +56,9 @@ class GNUVersionSection(Section): sections class which contains shareable code """ - def __init__(self, header, name, stream, elffile, stringtable, + def __init__(self, header, name, elffile, stringtable, field_prefix, version_struct, version_auxiliaries_struct): - super(GNUVersionSection, self).__init__(header, name, stream) - self.elffile = elffile + super(GNUVersionSection, self).__init__(header, name, elffile) self.stringtable = stringtable self.field_prefix = field_prefix self.version_struct = version_struct @@ -130,9 +129,9 @@ class GNUVerNeedSection(GNUVersionSection): """ ELF SUNW or GNU Version Needed table section. Has an associated StringTableSection that's passed in the constructor. """ - def __init__(self, header, name, stream, elffile, stringtable): + def __init__(self, header, name, elffile, stringtable): super(GNUVerNeedSection, self).__init__( - header, name, stream, elffile, stringtable, 'vn', + header, name, elffile, stringtable, 'vn', elffile.structs.Elf_Verneed, elffile.structs.Elf_Vernaux) self._has_indexes = None @@ -173,9 +172,9 @@ class GNUVerDefSection(GNUVersionSection): """ ELF SUNW or GNU Version Definition table section. Has an associated StringTableSection that's passed in the constructor. """ - def __init__(self, header, name, stream, elffile, stringtable): + def __init__(self, header, name, elffile, stringtable): super(GNUVerDefSection, self).__init__( - header, name, stream, elffile, stringtable, 'vd', + header, name, elffile, stringtable, 'vd', elffile.structs.Elf_Verdef, elffile.structs.Elf_Verdaux) def get_version(self, index): @@ -195,10 +194,8 @@ class GNUVerSymSection(Section): """ ELF SUNW or GNU Versym table section. Has an associated SymbolTableSection that's passed in the constructor. """ - def __init__(self, header, name, stream, elffile, symboltable): - super(GNUVerSymSection, self).__init__(header, name, stream) - self.elffile = elffile - self.elfstructs = self.elffile.structs + def __init__(self, header, name, elffile, symboltable): + super(GNUVerSymSection, self).__init__(header, name, elffile) self.symboltable = symboltable def num_symbols(self): @@ -214,7 +211,7 @@ class GNUVerSymSection(Section): # Grab the symbol's entry from the stream entry_offset = self['sh_offset'] + n * self['sh_entsize'] entry = struct_parse( - self.elfstructs.Elf_Versym, + self.structs.Elf_Versym, self.stream, stream_pos=entry_offset) # Find the symbol name in the associated symbol table diff --git a/elftools/elf/relocation.py b/elftools/elf/relocation.py index 690309f..c202bf9 100644 --- a/elftools/elf/relocation.py +++ b/elftools/elf/relocation.py @@ -47,16 +47,14 @@ class Relocation(object): class RelocationSection(Section): """ ELF relocation section. Serves as a collection of Relocation entries. """ - def __init__(self, header, name, stream, elffile): - super(RelocationSection, self).__init__(header, name, stream) - self.elffile = elffile - self.elfstructs = self.elffile.structs + def __init__(self, header, name, elffile): + super(RelocationSection, self).__init__(header, name, elffile) if self.header['sh_type'] == 'SHT_REL': - expected_size = self.elfstructs.Elf_Rel.sizeof() - self.entry_struct = self.elfstructs.Elf_Rel + expected_size = self.structs.Elf_Rel.sizeof() + self.entry_struct = self.structs.Elf_Rel elif self.header['sh_type'] == 'SHT_RELA': - expected_size = self.elfstructs.Elf_Rela.sizeof() - self.entry_struct = self.elfstructs.Elf_Rela + expected_size = self.structs.Elf_Rela.sizeof() + self.entry_struct = self.structs.Elf_Rela else: elf_assert(False, 'Unknown relocation type section') diff --git a/elftools/elf/sections.py b/elftools/elf/sections.py index c772958..eb4d40a 100644 --- a/elftools/elf/sections.py +++ b/elftools/elf/sections.py @@ -6,9 +6,12 @@ # Eli Bendersky (eliben@gmail.com) # This code is in the public domain #------------------------------------------------------------------------------- +from ..common.exceptions import ELFCompressionError 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 class Section(object): @@ -19,16 +22,84 @@ class Section(object): > sec = Section(...) > sec['sh_type'] # section type """ - def __init__(self, header, name, stream): + def __init__(self, header, name, elffile): self.header = header self.name = name - self.stream = stream + self.elffile = elffile + self.stream = self.elffile.stream + self.structs = self.elffile.structs + self._compressed = header['sh_flags'] & SH_FLAGS.SHF_COMPRESSED + + if self.compressed: + # Read the compression header now to know about the size/alignment + # of the decompressed data. + header = struct_parse(self.structs.Elf_Chdr, + self.stream, + stream_pos=self['sh_offset']) + self._compression_type = header['ch_type'] + self._decompressed_size = header['ch_size'] + self._decompressed_align = header['ch_addralign'] + else: + self._decompressed_size = header['sh_size'] + self._decompressed_align = header['sh_addralign'] + + @property + def compressed(self): + """ Is this section compressed? + """ + return self._compressed + + @property + def data_size(self): + """ Return the logical size for this section's data. + + This can be different from the .sh_size header field when the section + is compressed. + """ + return self._decompressed_size + + @property + def data_alignment(self): + """ Return the logical alignment for this section's data. + + This can be different from the .sh_addralign header field when the + section is compressed. + """ + return self._decompressed_align def data(self): """ The section data from the file. + + Note that data is decompressed if the stored section data is + compressed. """ - self.stream.seek(self['sh_offset']) - return self.stream.read(self['sh_size']) + # If this section is compressed, deflate it + if self.compressed: + c_type = self._compression_type + if c_type == 'ELFCOMPRESS_ZLIB': + # Read the data to decompress starting right after the + # compression header until the end of the section. + hdr_size = self.structs.Elf_Chdr.sizeof() + self.stream.seek(self['sh_offset'] + hdr_size) + compressed = self.stream.read(self['sh_size'] - hdr_size) + + decomp = zlib.decompressobj() + result = decomp.decompress(compressed, self.data_size) + else: + raise ELFCompressionError( + 'Unknown compression type: {:#0x}'.format(c_type) + ) + + if len(result) != self._decompressed_size: + raise ELFCompressionError( + 'Decompressed data is {} bytes long, should be {} bytes' + ' long'.format(len(result), self._decompressed_size) + ) + else: + self.stream.seek(self['sh_offset']) + result = self.stream.read(self._decompressed_size) + + return result def is_null(self): """ Is this a null section? @@ -53,9 +124,6 @@ class Section(object): class NullSection(Section): """ ELF NULL section """ - def __init__(self, header, name, stream): - super(NullSection, self).__init__(header, name, stream) - def is_null(self): return True @@ -63,9 +131,6 @@ class NullSection(Section): class StringTableSection(Section): """ ELF string table section. """ - def __init__(self, header, name, stream): - super(StringTableSection, self).__init__(header, name, stream) - def get_string(self, offset): """ Get the string stored at the given offset in this string table. """ @@ -78,10 +143,8 @@ class SymbolTableSection(Section): """ ELF symbol table section. Has an associated StringTableSection that's passed in the constructor. """ - def __init__(self, header, name, stream, elffile, stringtable): - super(SymbolTableSection, self).__init__(header, name, stream) - self.elffile = elffile - self.elfstructs = self.elffile.structs + def __init__(self, header, name, elffile, stringtable): + super(SymbolTableSection, self).__init__(header, name, elffile) self.stringtable = stringtable elf_assert(self['sh_entsize'] > 0, 'Expected entry size of section %r to be > 0' % name) @@ -100,7 +163,7 @@ class SymbolTableSection(Section): # Grab the symbol's entry from the stream entry_offset = self['sh_offset'] + n * self['sh_entsize'] entry = struct_parse( - self.elfstructs.Elf_Sym, + self.structs.Elf_Sym, self.stream, stream_pos=entry_offset) # Find the symbol name in the associated string table @@ -149,10 +212,8 @@ class SUNWSyminfoTableSection(Section): """ ELF .SUNW Syminfo table section. Has an associated SymbolTableSection that's passed in the constructor. """ - def __init__(self, header, name, stream, elffile, symboltable): - super(SUNWSyminfoTableSection, self).__init__(header, name, stream) - self.elffile = elffile - self.elfstructs = self.elffile.structs + def __init__(self, header, name, elffile, symboltable): + super(SUNWSyminfoTableSection, self).__init__(header, name, elffile) self.symboltable = symboltable def num_symbols(self): @@ -168,7 +229,7 @@ class SUNWSyminfoTableSection(Section): # Grab the symbol's entry from the stream entry_offset = self['sh_offset'] + n * self['sh_entsize'] entry = struct_parse( - self.elfstructs.Elf_Sunw_Syminfo, + self.structs.Elf_Sunw_Syminfo, self.stream, stream_pos=entry_offset) # Find the symbol name in the associated symbol table @@ -185,10 +246,6 @@ class SUNWSyminfoTableSection(Section): class NoteSection(Section): """ ELF NOTE section. Knows how to parse notes. """ - def __init__(self, header, name, stream, elffile): - super(NoteSection, self).__init__(header, name, stream) - self.elffile = elffile - def iter_notes(self): """ Yield all the notes in the section. Each result is a dictionary- like object with "n_name", "n_type", and "n_desc" fields, amongst @@ -199,24 +256,19 @@ class NoteSection(Section): class StabSection(Section): """ ELF stab section. """ - def __init__(self, header, name, stream, elffile): - super(StabSection, self).__init__(header, name, stream) - self.elffile = elffile - def iter_stabs(self): """ Yield all stab entries. Result type is ELFStructs.Elf_Stabs. """ - elffile = self.elffile offset = self['sh_offset'] size = self['sh_size'] end = offset + size while offset < end: stabs = struct_parse( - elffile.structs.Elf_Stabs, - elffile.stream, + self.structs.Elf_Stabs, + self.elffile.stream, stream_pos=offset) stabs['n_offset'] = offset - offset += elffile.structs.Elf_Stabs.sizeof() - elffile.stream.seek(offset) + offset += self.structs.Elf_Stabs.sizeof() + self.stream.seek(offset) yield stabs diff --git a/elftools/elf/structs.py b/elftools/elf/structs.py index 7c2c5d1..8cc87d2 100644 --- a/elftools/elf/structs.py +++ b/elftools/elf/structs.py @@ -76,6 +76,7 @@ class ELFStructs(object): """ self._create_phdr() self._create_shdr() + self._create_chdr() self._create_sym() self._create_rel() self._create_dyn() @@ -153,6 +154,20 @@ class ELFStructs(object): self.Elf_xword('sh_entsize'), ) + def _create_chdr(self): + # Structure of compressed sections header. It is documented in Oracle + # "Linker and Libraries Guide", Part IV ELF Application Binary + # Interface, Chapter 13 Object File Format, Section Compression: + # https://docs.oracle.com/cd/E53394_01/html/E54813/section_compression.html + fields = [ + Enum(self.Elf_word('ch_type'), **ENUM_ELFCOMPRESS_TYPE), + self.Elf_xword('ch_size'), + self.Elf_xword('ch_addralign'), + ] + if self.elfclass == 64: + fields.insert(1, self.Elf_word('ch_reserved')) + self.Elf_Chdr = Struct('Elf_Chdr', *fields) + def _create_rel(self): # r_info is also taken apart into r_info_sym and r_info_type. # This is done in Value to avoid endianity issues while parsing. @@ -226,7 +241,7 @@ class ELFStructs(object): def _create_gnu_verneed(self): # Structure of "version needed" entries is documented in - # Oracle "Linker and Libraries Guide", Chapter 7 Object File Format + # Oracle "Linker and Libraries Guide", Chapter 13 Object File Format self.Elf_Verneed = Struct('Elf_Verneed', self.Elf_half('vn_version'), self.Elf_half('vn_cnt'), @@ -243,8 +258,8 @@ class ELFStructs(object): ) def _create_gnu_verdef(self): - # Structure off "version definition" entries are documented in - # Oracle "Linker and Libraries Guide", Chapter 7 Object File Format + # Structure of "version definition" entries are documented in + # Oracle "Linker and Libraries Guide", Chapter 13 Object File Format self.Elf_Verdef = Struct('Elf_Verdef', self.Elf_half('vd_version'), self.Elf_half('vd_flags'), @@ -260,8 +275,8 @@ class ELFStructs(object): ) def _create_gnu_versym(self): - # Structure off "version symbol" entries are documented in - # Oracle "Linker and Libraries Guide", Chapter 7 Object File Format + # Structure of "version symbol" entries are documented in + # Oracle "Linker and Libraries Guide", Chapter 13 Object File Format self.Elf_Versym = Struct('Elf_Versym', Enum(self.Elf_half('ndx'), **ENUM_VERSYM), ) diff --git a/test/test_compressed_support.py b/test/test_compressed_support.py new file mode 100644 index 0000000..9672342 --- /dev/null +++ b/test/test_compressed_support.py @@ -0,0 +1,93 @@ +#------------------------------------------------------------------------------- +# Test handling for compressed debug sections +# +# Pierre-Marie de Rodat (pmderodat@kawie.fr) +# This code is in the public domain +#------------------------------------------------------------------------------- + +from contextlib import contextmanager +import os +import unittest + +from elftools.elf.elffile import ELFFile +from elftools.common.exceptions import ELFCompressionError + + +class TestCompressedSupport(unittest.TestCase): + + def test_compressed_32(self): + with self.elffile('32') as elf: + section = elf.get_section_by_name('.debug_info') + self.assertTrue(section.compressed) + self.assertEqual(section.data_size, 0x330) + self.assertEqual(section.data_alignment, 1) + + self.assertEqual(self.get_cus_info(elf), ['CU 0x0: 0xb-0x322']) + + def test_compressed_64(self): + with self.elffile('64') as elf: + section = elf.get_section_by_name('.debug_info') + self.assertTrue(section.compressed) + self.assertEqual(section.data_size, 0x327) + self.assertEqual(section.data_alignment, 1) + self.assertEqual(self.get_cus_info(elf), ['CU 0x0: 0xb-0x319']) + + def test_compressed_unknown_type(self): + with self.elffile('unknown_type') as elf: + section = elf.get_section_by_name('.debug_info') + try: + section.data() + except ELFCompressionError as exc: + self.assertEqual( + str(exc), 'Unknown compression type: 0x7ffffffe' + ) + else: + self.fail('An exception was exected') + + def test_compressed_bad_size(self): + with self.elffile('bad_size') as elf: + section = elf.get_section_by_name('.debug_info') + try: + section.data() + except ELFCompressionError as exc: + self.assertEqual( + str(exc), + 'Decompressed data is 807 bytes long, should be 808 bytes' + ' long' + ) + else: + self.fail('An exception was exected') + + # Test helpers + + @contextmanager + def elffile(self, name): + """ Context manager to open and parse an ELF file. + """ + with open(os.path.join('test', 'testfiles_for_unittests', + 'compressed_{}.o'.format(name)), 'rb') as f: + yield ELFFile(f) + + def get_cus_info(self, elffile): + """ Return basic info about the compile units in `elffile`. + + This is used as a basic sanity check for decompressed DWARF data. + """ + result = [] + + dwarf = elffile.get_dwarf_info() + for cu in dwarf.iter_CUs(): + dies = [] + + def traverse(die): + dies.append(die.offset) + for child in die.iter_children(): + traverse(child) + + traverse(cu.get_top_DIE()) + result.append('CU {:#0x}: {:#0x}-{:#0x}'.format( + cu.cu_offset, + dies[0], dies[-1] + )) + + return result diff --git a/test/testfiles_for_unittests/compressed.c b/test/testfiles_for_unittests/compressed.c new file mode 100644 index 0000000..8387362 --- /dev/null +++ b/test/testfiles_for_unittests/compressed.c @@ -0,0 +1,20 @@ +/* Generated by compiling with any GCC version and with a binutils +** distribution that includes support for compressed sections. GNU binutils +** 2.28 is fine. +** +** gcc -c -m32 -O0 -g compressed.c -o compressed_32.o +** gcc -c -m64 -O0 -g compressed.c -o compressed_64.o +** +** compressed_unknown_type.o is a copy of compressed_64.o that is hand +** hex-edited to replace the ch_field with 0x7ffffffe. +** +** compressed_bad_size.o is a copy of compressed_64.o that is hand +** hex-edited to replace the ch_size with 0x328 (instead of 0x327). +*/ + +#include + +int foo(int i) { + printf ("i = %i\n", i); + return 0; +} diff --git a/test/testfiles_for_unittests/compressed_32.o b/test/testfiles_for_unittests/compressed_32.o new file mode 100644 index 0000000..da39bd9 Binary files /dev/null and b/test/testfiles_for_unittests/compressed_32.o differ diff --git a/test/testfiles_for_unittests/compressed_64.o b/test/testfiles_for_unittests/compressed_64.o new file mode 100644 index 0000000..dc420bd Binary files /dev/null and b/test/testfiles_for_unittests/compressed_64.o differ diff --git a/test/testfiles_for_unittests/compressed_bad_size.o b/test/testfiles_for_unittests/compressed_bad_size.o new file mode 100644 index 0000000..602e17c Binary files /dev/null and b/test/testfiles_for_unittests/compressed_bad_size.o differ diff --git a/test/testfiles_for_unittests/compressed_unknown_type.o b/test/testfiles_for_unittests/compressed_unknown_type.o new file mode 100644 index 0000000..67970e5 Binary files /dev/null and b/test/testfiles_for_unittests/compressed_unknown_type.o differ