THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
Description
cle/backends/pe/relocation/__init__.py keys its relocation tables on strings
that archinfo does not produce, so every base relocation in a PE image is
dropped on every architecture except x86 and x86-64:
ALL_RELOCATIONS = {
"AMD64": relocation_table_generic,
"arm": relocation_table_generic | relocation_table_arm,
"X86": relocation_table_generic,
"mips": relocation_table_generic | relocation_table_mips,
"RISCV": relocation_table_generic | relocation_table_riscv,
}
and the lookup is get_relocation(self.arch.name, reloc_type).
"arm", "mips" and "RISCV" are not architecture names — arch.name is
ARMEL, MIPS32, RISCV64 — and AARCH64 has no entry at all. Of the fourteen
IMAGE_FILE_MACHINE_* values the backend can resolve, only two land in the
table:
import archinfo, pefile
from cle.backends.pe.relocation import ALL_RELOCATIONS
for machine in (0x014C, 0x8664, 0xAA64, 0x01C4, 0x0169, 0x01F0, 0x5064):
name = archinfo.arch_from_id(pefile.MACHINE_TYPE[machine]).name
print(f"{machine:#06x} {name:8} in ALL_RELOCATIONS: {name in ALL_RELOCATIONS}")
# 0x014c X86 in ALL_RELOCATIONS: True
# 0x8664 AMD64 in ALL_RELOCATIONS: True
# 0xaa64 AARCH64 in ALL_RELOCATIONS: False
# 0x01c4 ARMEL in ALL_RELOCATIONS: False
# 0x0169 MIPS32 in ALL_RELOCATIONS: False
# 0x01f0 PPC32 in ALL_RELOCATIONS: False
# 0x5064 RISCV64 in ALL_RELOCATIONS: False
get_relocation returns None for every type on those, __register_relocs
skips the entry, and the only thing that survives is log.warning("Unknown reloc %d on %s", ...) — once per (arch, type) pair, because complaint_log dedupes
it. IMAGE_REL_BASED_DIR64 and IMAGE_REL_BASED_HIGHLOW are in
relocation_table_generic and would be handled for any of these architectures if
the key matched; they are dropped along with the rest.
The consequence only shows once an image is rebased, which is exactly when it
matters: PE.__init__ sets mapped_base = linked_base = ImageBase, so a
single-object load happens to be correct, and a load that puts the image
anywhere else — a second DLL with the same preferred base, an explicit
base_addr — leaves every absolute pointer in it pointing at the old base with
no error.
Measurement
Counting IMAGE_BASE_RELOCATION entries out of the .reloc directory directly
and comparing with len(loader.main_object.relocs) after
cle.Loader(path, auto_load_libs=False, main_opts={"backend": "pe"}). The
remainder on the first two rows is DLL imports; not one base relocation is kept:
| image |
arch.name |
base relocations in the file |
relocations cle built |
| aarch64 executable |
AARCH64 |
1,770 (all DIR64) |
173 |
| ARMNT driver |
ARMEL |
8,777 (124 HIGHLOW, 8,653 THUMB_MOV32) |
301 |
| x86-64 executable |
AMD64 |
654 (all DIR64) |
801 |
| x86 executable |
X86 |
36,780 (all HIGHLOW) |
37,021 |
The warning text is what makes this findable: loading an ARM64 Windows image
prints Unknown reloc 10 on AARCH64 — type 10 is IMAGE_REL_BASED_DIR64, which
relocation_table_generic implements.
Reproduction
Any Windows binary for a non-x86 target; this repository's test corpus has none,
so a recipe rather than a path:
$ clang --target=aarch64-pc-windows-msvc -o hello.exe hello.c
import cle
ld = cle.Loader("hello.exe", auto_load_libs=False, main_opts={"backend": "pe"})
print(len(ld.main_object.relocs)) # imports only; no base relocations
The snippet at the top needs no binary at all.
Options
- Key the table on the names
archinfo produces: ARMEL, ARMHF and
ARMCortexM for the ARM table, MIPS32 for the MIPS table, RISCV64 for the
RISC-V table, and AARCH64 and PPC32 for the generic table. This is the
whole fix for DIR64 and HIGHLOW, which is 1,770 of 1,770 relocations on the
ARM64 image above.
- The same, plus the two ARM64-specific types the generic table does not cover,
IMAGE_REL_BASED_ARM64_MOV32A (13) and IMAGE_REL_BASED_ARM64_MOV32T (14).
The ARMNT image above needs the existing THUMB_MOV32 for 8,653 of its 8,777,
so the ARM64 equivalents are likely to matter the same way on ARM64 images
that use them.
- Fail loudly instead. A dropped base relocation cannot be detected downstream,
and log.warning deduplicated per (arch, type) is one line for 8,777
dropped fixups.
Filed as an issue rather than a pull request because which architectures should
map to which table, and whether an unhandled relocation type should be fatal, are
calls for a maintainer. Note also that angr.Project on any of these images
currently raises before this code matters (angr/angr#6794 is the open fix), so a
CFG-level before/after cannot be measured until that lands — the table above is
measured against the files' own .reloc directories instead.
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
Description
cle/backends/pe/relocation/__init__.pykeys its relocation tables on stringsthat
archinfodoes not produce, so every base relocation in a PE image isdropped on every architecture except x86 and x86-64:
and the lookup is
get_relocation(self.arch.name, reloc_type)."arm","mips"and"RISCV"are not architecture names —arch.nameisARMEL,MIPS32,RISCV64— andAARCH64has no entry at all. Of the fourteenIMAGE_FILE_MACHINE_*values the backend can resolve, only two land in thetable:
get_relocationreturnsNonefor every type on those,__register_relocsskips the entry, and the only thing that survives is
log.warning("Unknown reloc %d on %s", ...)— once per(arch, type)pair, becausecomplaint_logdedupesit.
IMAGE_REL_BASED_DIR64andIMAGE_REL_BASED_HIGHLOWare inrelocation_table_genericand would be handled for any of these architectures ifthe key matched; they are dropped along with the rest.
The consequence only shows once an image is rebased, which is exactly when it
matters:
PE.__init__setsmapped_base = linked_base = ImageBase, so asingle-object load happens to be correct, and a load that puts the image
anywhere else — a second DLL with the same preferred base, an explicit
base_addr— leaves every absolute pointer in it pointing at the old base withno error.
Measurement
Counting
IMAGE_BASE_RELOCATIONentries out of the.relocdirectory directlyand comparing with
len(loader.main_object.relocs)aftercle.Loader(path, auto_load_libs=False, main_opts={"backend": "pe"}). Theremainder on the first two rows is DLL imports; not one base relocation is kept:
arch.nameAARCH64DIR64)ARMELHIGHLOW, 8,653THUMB_MOV32)AMD64DIR64)X86HIGHLOW)The warning text is what makes this findable: loading an ARM64 Windows image
prints
Unknown reloc 10 on AARCH64— type 10 isIMAGE_REL_BASED_DIR64, whichrelocation_table_genericimplements.Reproduction
Any Windows binary for a non-x86 target; this repository's test corpus has none,
so a recipe rather than a path:
$ clang --target=aarch64-pc-windows-msvc -o hello.exe hello.cThe snippet at the top needs no binary at all.
Options
archinfoproduces:ARMEL,ARMHFandARMCortexMfor the ARM table,MIPS32for the MIPS table,RISCV64for theRISC-V table, and
AARCH64andPPC32for the generic table. This is thewhole fix for
DIR64andHIGHLOW, which is 1,770 of 1,770 relocations on theARM64 image above.
IMAGE_REL_BASED_ARM64_MOV32A(13) andIMAGE_REL_BASED_ARM64_MOV32T(14).The ARMNT image above needs the existing
THUMB_MOV32for 8,653 of its 8,777,so the ARM64 equivalents are likely to matter the same way on ARM64 images
that use them.
and
log.warningdeduplicated per(arch, type)is one line for 8,777dropped fixups.
Filed as an issue rather than a pull request because which architectures should
map to which table, and whether an unhandled relocation type should be fatal, are
calls for a maintainer. Note also that
angr.Projecton any of these imagescurrently raises before this code matters (angr/angr#6794 is the open fix), so a
CFG-level before/after cannot be measured until that lands — the table above is
measured against the files' own
.relocdirectories instead.