Skip to content

Apply relocs for ifuncs - #1536

Open
kStor2poche wants to merge 14 commits into
cea-sec:masterfrom
kStor2poche:apply-relocs-for-ifuncs
Open

kStor2poche wants to merge 14 commits into
cea-sec:masterfrom
kStor2poche:apply-relocs-for-ifuncs

Conversation

@kStor2poche

Copy link
Copy Markdown
Contributor

This implements ifunc relocation for ELFs containing them by running the ifunc on a jitter that copies the currently-being-loaded file's memory.
While this should technically be the job of the libc init functions, it made sense for my application to apply this relocation along with the other types of relocation.
This only works for ifuncs in statically and dynamically loaded executables, as per the glibc manual.

As it is a bit intensive, it could make sense to put it behind a separate option in vm_load_eld, this is up for debate.

Comment thread miasm/jitter/loader/elf.py Resolved
Comment thread miasm/jitter/loader/elf.py Outdated
addr_symb = loc_db.get_name_offset(symbol_entry.name.decode())
if addr_symb is None:
log.warning("Unable to find symbol %r" % symbol_entry.name)
log.warning("Unable to find symbol %r" % symbol_entry.name.decode())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're at it, just like you did further down, might as well switch to an f-string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since it's a logger, we can also keep the %r syntax and put the name as an argument

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I switched to f-strings right now but if you feel like % encoded in logs is more idiomatic in this project I can to that too !

@Redhpm

Redhpm commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

And a more general comment, maybe we should hide this behind a parameter like run_ifuncs=False for the apply_reloc_x86 function, and another one for the vm_load_elf function.

We also need a test, as it needs quite realistic examples, might as well add a full on script in example/jitter with a real sample

Comment thread example/jitter/resolve_ifunc.py Outdated
myjit.init_stack()
base_addr = 0x400000

reloc_options = RelocOptions(run_ifuncs=True, ifunc_jitter_engine=args.jitter)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_ifuncs=True -> I think this is a typo, you might want to use the function argument

with open(args.filename, 'rb') as f:
elf: ContainerELF = Container.from_stream(f, addr=base_addr, loc_db=loc_db, vm=myjit.vm, apply_reloc=True, reloc_options=reloc_options)
assert isinstance(elf, ContainerELF)
if elf.executable.Ehdr.type == ET_EXEC:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe clearer in the example if regrouped with the original definition of the base address ? (line 21)

Maybe it can be passed as an argument and put the if statement in the main ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good with the first change but I'm not sure about the second since it would lead to unnecessarily duplicated code

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair

Comment thread example/jitter/resolve_ifunc.py Outdated

if args.verbose >= 2:
jitter.set_trace_log(True, True)
run_at = jitter.lifter.loc_db.get_name_offset("intermediate")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe find a clearer name for the symbol, if it's really not possible to have all the logic in the main

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically, it would be possible to have all the logic in main and put a breakpoint right after the call to func with a hardcoded address which might be fine for our example.
I could also try to use the .got.plt entry for the relocation to directly execute what the resolved ifunc it points to but no symbol contains that value so it's a bit akward to retrieve.
In the end I find it more elegant and easily understandable to have a wrapper (and then wrapper might be a more appropriate name for the function) for that but that's just my opinion.

Comment thread miasm/analysis/binary.py Outdated
@apply_reloc (optional): if set, apply relocation during ELF loading
@reloc_options (optional): see miasm.jitter.loader.elf.RelocOptions

@addr and @apply_reloc are only meaningful in the context of a

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also the reloc options

Comment thread miasm/analysis/binary.py Outdated
# Build the bin_stream instance and set the entry point
try:
self._bin_stream = bin_stream_elf(self._executable)
if self._executable.Ehdr.type == ET_EXEC:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not sooner in the function ? Also the warning can only show up if addr is actually not 0.

def __init__(self, run_ifuncs: bool = False, ifunc_jitter_engine: Literal["python", "gcc", "llvm"] | None = None, ifunc_jitter = None) -> None:
"""@run_ifuncs: whether or not to run, resolve and apply ifuncs
@ifunc_jitter_engine: set to "gcc" by default, unless @ifunc_jitter is used. The engine to use for the default ifunc jitter.
@ifunc_jitter: a user-provided jitter to run/resolve ifuncs with. Needs to have an initialized stack. Overrides the default ifunc jitter.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to have an initialized stack

Maybe add "usually" ? Theoretically an ifunc resolver could be a function not using the stack ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually needed regardless of the resolver using the stack since we create a fake stack frame for our function with 0x1337beef as our saved pc and breakpoint in order to run the resolver and gather its result in apply_reloc_x86.

Comment thread miasm/jitter/loader/elf.py Outdated
reloc_options = RelocOptions()

if reloc_options.run_ifuncs and elf.Ehdr.type == elf_csts.ET_EXEC:
log.warning("Running ifuncs as a part of the loading process is only accurate for dynamically-linked executables, as they are normally ran during glibc initialization for static and static-pie executables. See https://sourceware.org/glibc/manual/latest/html_node/Indirect-Functions.html#When-IFUNC-Resolvers-Run.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can cite something more "timeproof" as well ?
Like "GNU C Library Reference Manual, version 2.44, chapter/section 37.4.2 When IFUNC Resolvers Run"

Comment thread miasm/jitter/loader/elf.py Outdated

return elf

def get_ifuncs(elf, base_addr, with_syms=False):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the following 3 functions are x86 (32/64) specific. Maybe it should be mentioned in their name, the same way apply_reloc_x86 is specific about it.

def _resolve_ifunc(reloc_addr, resolver_addr, elf, run_jitter):
"""
WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during glibc initialization.
WARNING: this requires the jitter to have an initialized stack

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as before

def apply_ifunc(reloc_addr, resolver_addr, elf, jitter, run_jitter=None):
"""
WARNING: this is only accurate for dynamically-linked binaries. Static and static-pie executables' ifuncs are loaded at runtime during glibc initialization.
WARNING: this requires the jitter or run_jitter (if present) to have an initialized stack

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idem

@kStor2poche
kStor2poche force-pushed the apply-relocs-for-ifuncs branch from 0b82e67 to 545fb49 Compare October 7, 2026 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants