(blob:bytes, force_section_align:int=1, link_libs:list[str]|None=None)
| 13 | raise RuntimeError(f'Attempting to relocate against an undefined symbol {sym}') |
| 14 | |
| 15 | def elf_loader(blob:bytes, force_section_align:int=1, link_libs:list[str]|None=None) -> tuple[memoryview, list[ElfSection], list[tuple]]: |
| 16 | assert blob[:4] == libc.ELFMAG.encode(), "blob is not an ELF, missing magic bytes" |
| 17 | ecls = {libc.ELFCLASS32: "Elf32", libc.ELFCLASS64: "Elf64"}[blob[libc.EI_CLASS]] |
| 18 | |
| 19 | def _strtab(blob: bytes, idx: int) -> str: return blob[idx:blob.find(b'\x00', idx)].decode('utf-8') |
| 20 | |
| 21 | header = getattr(libc, f"{ecls}_Ehdr").from_buffer_copy(blob) |
| 22 | section_headers = (getattr(libc, f"{ecls}_Shdr") * header.e_shnum).from_buffer_copy(blob[header.e_shoff:]) |
| 23 | sh_strtab = blob[(shstrst:=section_headers[header.e_shstrndx].sh_offset):shstrst+section_headers[header.e_shstrndx].sh_size] |
| 24 | sections = [ElfSection(_strtab(sh_strtab, sh.sh_name), sh, blob[sh.sh_offset:sh.sh_offset+sh.sh_size]) for sh in section_headers] |
| 25 | |
| 26 | def _to_carray(sh, ctype): return (ctype * (sh.header.sh_size // sh.header.sh_entsize)).from_buffer_copy(sh.content) |
| 27 | rel = [(sh, sh.name[4:], _to_carray(sh, getattr(libc, f"{ecls}_Rel"))) for sh in sections if sh.header.sh_type == libc.SHT_REL] |
| 28 | rela = [(sh, sh.name[5:], _to_carray(sh, getattr(libc, f"{ecls}_Rela"))) for sh in sections if sh.header.sh_type == libc.SHT_RELA] |
| 29 | symtab = next((_to_carray(sh, getattr(libc, f"{ecls}_Sym")) for sh in sections if sh.header.sh_type == libc.SHT_SYMTAB), None) |
| 30 | progbits = [sh for sh in sections if sh.header.sh_type == libc.SHT_PROGBITS] |
| 31 | |
| 32 | # Prealloc image for all fixed addresses. |
| 33 | image = bytearray(max([sh.header.sh_addr + sh.header.sh_size for sh in progbits if sh.header.sh_addr != 0] + [0])) |
| 34 | for sh in progbits: |
| 35 | if sh.header.sh_addr != 0: image[sh.header.sh_addr:sh.header.sh_addr+sh.header.sh_size] = sh.content |
| 36 | else: |
| 37 | image += b'\0' * (((align:=max(sh.header.sh_addralign, force_section_align)) - len(image) % align) % align) + sh.content |
| 38 | sh.header.sh_addr = len(image) - len(sh.content) |
| 39 | |
| 40 | # Relocations |
| 41 | relocs = [] |
| 42 | for sh, trgt_sh_name, c_rels in rel + rela: |
| 43 | if trgt_sh_name == ".eh_frame": continue |
| 44 | target_image_off = next(tsh for tsh in sections if tsh.name == trgt_sh_name).header.sh_addr |
| 45 | rels = [(r.r_offset, unwrap(symtab)[getattr(libc, f"{ecls.upper()}_R_SYM")(r.r_info)], getattr(libc, f"{ecls.upper()}_R_TYPE")(r.r_info), |
| 46 | getattr(r, "r_addend", 0)) for r in c_rels] |
| 47 | relocs += [(target_image_off + roff, link_sym(_strtab(sh_strtab, sym.st_name), link_libs or []) if sym.st_shndx == 0 else |
| 48 | sections[sym.st_shndx].header.sh_addr + sym.st_value, rtype, raddend) for roff, sym, rtype, raddend in rels] |
| 49 | |
| 50 | return memoryview(image), sections, relocs |
| 51 | |
| 52 | def jit_loader(obj: bytes, base:int=0, link_libs:list[str]|None=None) -> bytes: |
| 53 | image_, _, relocs = elf_loader(obj, link_libs=link_libs) |
searching dependent graphs…