tests: test compressed debug sections

Test both .zdebug_* sections and SHF_COMPRESSED .debug_* sections.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
Omar Sandoval 2023-06-16 13:51:48 -07:00
parent 2ee625fc74
commit 104a14781d
5 changed files with 144 additions and 26 deletions

View File

@ -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<enum>"
+ "|".join(enums)
+ r")_(?P<name>\w+)\s+(?P<value>0x[0-9a-fA-F]+|[0-9]+)",
+ r")_(?P<name>\w+)\s+(?:(?P<value>0x[0-9a-fA-F]+|[0-9]+)|(?:\(\s*1U?\s*<<\s*(?P<bitshift>[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

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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))