diff --git a/scripts/gen_tests_elf_py.py b/scripts/gen_tests_elf_py.py index 0b717fa6..040d87b6 100755 --- a/scripts/gen_tests_elf_py.py +++ b/scripts/gen_tests_elf_py.py @@ -22,6 +22,7 @@ def main() -> None: for name in ( "ET", "PT", + "SHF", "SHN", "SHT", "STB", @@ -32,13 +33,16 @@ def main() -> None: for match in re.finditer( r"^\s*#\s*define\s+(?P" + "|".join(enums) - + r")_(?P\w+)\s+(?P0x[0-9a-fA-F]+|[0-9]+)", + + r")_(?P\w+)\s+(?:(?P0x[0-9a-fA-F]+|[0-9]+)|(?:\(\s*1U?\s*<<\s*(?P[0-9]+)\s*\)))", contents, re.MULTILINE, ): enum = match.group("enum") name = match.group("name") - value = int(match.group("value"), 0) + if match.group("value"): + value = int(match.group("value"), 0) + else: + value = 1 << int(match.group("bitshift"), 10) enums[enum].append((name, value)) f = sys.stdout diff --git a/tests/dwarfwriter.py b/tests/dwarfwriter.py index 0e81bb9c..5e77256f 100644 --- a/tests/dwarfwriter.py +++ b/tests/dwarfwriter.py @@ -3,6 +3,7 @@ import os.path from typing import Any, NamedTuple, Optional, Sequence, Union +import zlib from tests.assembler import _append_sleb128, _append_uleb128 from tests.dwarf import DW_AT, DW_FORM, DW_TAG @@ -232,7 +233,13 @@ _UNIT_TAGS = frozenset({DW_TAG.type_unit, DW_TAG.compile_unit}) def dwarf_sections( - dies, little_endian=True, bits=64, *, lang=None, use_dw_form_indirect=False + dies, + little_endian=True, + bits=64, + *, + lang=None, + use_dw_form_indirect=False, + compress=None, ): if isinstance(dies, DwarfDie): dies = (dies,) @@ -263,29 +270,47 @@ def dwarf_sections( unit_dies, little_endian, bits, use_dw_form_indirect ) + if compress == "zlib-gnu": + + def debug_section(name, data): + assert name.startswith(".debug") + compressed_data = bytearray(b"ZLIB") + compressed_data.extend(len(data).to_bytes(8, "big")) + compressed_data.extend(zlib.compress(data)) + return ElfSection( + name=".z" + name[1:], sh_type=SHT.PROGBITS, data=compressed_data + ) + + else: + assert compress is None or compress == "zlib-gabi", compress + compressed = compress is not None + + def debug_section(name, data): + return ElfSection( + name=name, sh_type=SHT.PROGBITS, data=data, compressed=compressed + ) + sections = [ - ElfSection( - name=".debug_abbrev", - sh_type=SHT.PROGBITS, - data=_compile_debug_abbrev(unit_dies, use_dw_form_indirect), + debug_section( + ".debug_abbrev", _compile_debug_abbrev(unit_dies, use_dw_form_indirect) ), - ElfSection(name=".debug_info", sh_type=SHT.PROGBITS, data=debug_info), - ElfSection( - name=".debug_line", - sh_type=SHT.PROGBITS, - data=_compile_debug_line(unit_dies, little_endian), - ), - ElfSection(name=".debug_str", sh_type=SHT.PROGBITS, data=b"\0"), + debug_section(".debug_info", data=debug_info), + debug_section(".debug_line", _compile_debug_line(unit_dies, little_endian)), + debug_section(".debug_str", b"\0"), ] if debug_types: - sections.append( - ElfSection(name=".debug_types", sh_type=SHT.PROGBITS, data=debug_types) - ) + sections.append(debug_section(".debug_types", debug_types)) return sections def compile_dwarf( - dies, little_endian=True, bits=64, *, lang=None, use_dw_form_indirect=False + dies, + little_endian=True, + bits=64, + *, + lang=None, + use_dw_form_indirect=False, + compress=None, ): return create_elf_file( ET.EXEC, @@ -295,6 +320,7 @@ def compile_dwarf( bits=bits, lang=lang, use_dw_form_indirect=use_dw_form_indirect, + compress=compress, ), little_endian=little_endian, bits=bits, diff --git a/tests/elf.py b/tests/elf.py index 2674495a..cc81219f 100644 --- a/tests/elf.py +++ b/tests/elf.py @@ -63,6 +63,48 @@ class PT(enum.IntEnum): return hex(value) +class SHF(enum.IntEnum): + WRITE = 0x1 + ALLOC = 0x2 + EXECINSTR = 0x4 + MERGE = 0x10 + STRINGS = 0x20 + INFO_LINK = 0x40 + LINK_ORDER = 0x80 + OS_NONCONFORMING = 0x100 + GROUP = 0x200 + TLS = 0x400 + COMPRESSED = 0x800 + MASKOS = 0xFF00000 + MASKPROC = 0xF0000000 + GNU_RETAIN = 0x200000 + ORDERED = 0x40000000 + EXCLUDE = 0x80000000 + MIPS_GPREL = 0x10000000 + MIPS_MERGE = 0x20000000 + MIPS_ADDR = 0x40000000 + MIPS_STRINGS = 0x80000000 + MIPS_NOSTRIP = 0x8000000 + MIPS_LOCAL = 0x4000000 + MIPS_NAMES = 0x2000000 + MIPS_NODUPE = 0x1000000 + PARISC_SHORT = 0x20000000 + PARISC_HUGE = 0x40000000 + PARISC_SBP = 0x80000000 + ALPHA_GPREL = 0x10000000 + ARM_ENTRYSECT = 0x10000000 + ARM_COMDEF = 0x80000000 + IA_64_SHORT = 0x10000000 + IA_64_NORECOV = 0x20000000 + + @classmethod + def str(cls, value: int) -> Text: + try: + return f"SHF_{cls(value).name}" + except ValueError: + return hex(value) + + class SHN(enum.IntEnum): UNDEF = 0x0 LORESERVE = 0xFF00 diff --git a/tests/elfwriter.py b/tests/elfwriter.py index e0f35556..c8f80dc4 100644 --- a/tests/elfwriter.py +++ b/tests/elfwriter.py @@ -3,8 +3,9 @@ import struct from typing import List, NamedTuple, Optional, Sequence +import zlib -from tests.elf import ET, PT, SHN, SHT, STB, STT, STV +from tests.elf import ET, PT, SHF, SHN, SHT, STB, STT, STV class ElfSection: @@ -21,14 +22,16 @@ class ElfSection: sh_link: int = 0, sh_info: int = 0, sh_entsize: int = 0, + compressed=False, ): self.data = data self.name = name self.sh_type = sh_type + self.sh_flags = SHF.COMPRESSED if compressed else 0 self.p_type = p_type self.vaddr = vaddr self.paddr = paddr - self.memsz = len(self.data) if memsz is None else memsz + self.memsz = memsz self.p_align = p_align self.sh_link = sh_link self.sh_info = sh_info @@ -36,6 +39,7 @@ class ElfSection: assert (self.name is not None) or (self.p_type is not None) assert (self.name is None) == (self.sh_type is None) + assert self.p_type is None or not compressed class ElfSymbol(NamedTuple): @@ -122,12 +126,14 @@ def create_elf_file( ehdr_struct = struct.Struct(endian + "16BHHIQQQIHHHHHH") shdr_struct = struct.Struct(endian + "IIQQQQIIQQ") phdr_struct = struct.Struct(endian + "IIQQQQQQ") + chdr_struct = struct.Struct(endian + "IIQQ") e_machine = 62 if little_endian else 43 # EM_X86_64 or EM_SPARCV9 else: assert bits == 32 ehdr_struct = struct.Struct(endian + "16BHHIIIIIHHHHHH") shdr_struct = struct.Struct(endian + "10I") phdr_struct = struct.Struct(endian + "8I") + chdr_struct = struct.Struct(endian + "III") e_machine = 3 if little_endian else 8 # EM_386 or EM_MIPS sections = list(sections) @@ -189,6 +195,15 @@ def create_elf_file( shdr_offset += shdr_struct.size for section in sections: + ch_addralign = 1 if section.p_type is None else bits // 8 + memsz = len(section.data) if section.memsz is None else section.memsz + if section.sh_flags & SHF.COMPRESSED: + sh_addralign = bits // 8 + compressed_data = zlib.compress(section.data) + sh_size = chdr_struct.size + len(compressed_data) + else: + sh_addralign = ch_addralign + sh_size = memsz if section.p_align: padding = section.vaddr % section.p_align - len(buf) % section.p_align buf.extend(bytes(padding)) @@ -198,13 +213,13 @@ def create_elf_file( shdr_offset, shstrtab.index(section.name.encode()), # sh_name section.sh_type, # sh_type - 0, # sh_flags + section.sh_flags, # sh_flags section.vaddr, # sh_addr len(buf), # sh_offset - section.memsz, # sh_size + sh_size, # sh_size section.sh_link, # sh_link section.sh_info, # sh_info - 1 if section.p_type is None else bits // 8, # sh_addralign + sh_addralign, # sh_addralign section.sh_entsize, # sh_entsize ) shdr_offset += shdr_struct.size @@ -220,7 +235,7 @@ def create_elf_file( section.vaddr, # p_vaddr section.paddr, # p_paddr len(section.data), # p_filesz - section.memsz, # p_memsz + memsz, # p_memsz section.p_align, # p_align ) else: @@ -232,11 +247,32 @@ def create_elf_file( section.vaddr, # p_vaddr section.paddr, # p_paddr len(section.data), # p_filesz - section.memsz, # p_memsz + memsz, # p_memsz flags, # p_flags section.p_align, # p_align ) phdr_offset += phdr_struct.size - buf.extend(section.data) + if section.sh_flags & SHF.COMPRESSED: + ELFCOMPRESS_ZLIB = 1 + if bits == 64: + buf.extend( + chdr_struct.pack( + ELFCOMPRESS_ZLIB, # ch_type + 0, # ch_reserved + memsz, # ch_size + ch_addralign, # ch_addralign + ) + ) + else: + buf.extend( + chdr_struct.pack( + ELFCOMPRESS_ZLIB, # ch_type + memsz, # ch_size + ch_addralign, # ch_addralign + ) + ) + buf.extend(compressed_data) + else: + buf.extend(section.data) return buf diff --git a/tests/test_dwarf.py b/tests/test_dwarf.py index 50263908..46dba40e 100644 --- a/tests/test_dwarf.py +++ b/tests/test_dwarf.py @@ -6490,3 +6490,13 @@ class TestProgram(TestCase): *labeled_int_die, ) self.assertIsNotNone(repr(dwarf_program(dies).type("TEST").type.parameters[0])) + + +class TestCompressedDebugSections(TestCase): + def test_zlib_gnu(self): + prog = dwarf_program(wrap_test_type_dies(int_die), compress="zlib-gnu") + self.assertIdentical(prog.type("TEST").type, prog.int_type("int", 4, True)) + + def test_zlib_gabi(self): + prog = dwarf_program(wrap_test_type_dies(int_die), compress="zlib-gabi") + self.assertIdentical(prog.type("TEST").type, prog.int_type("int", 4, True))