Page MenuHomeFreeBSD

Modify the kernel linker to handle ifunc call site relocations.
ClosedPublic

Authored by markj on Aug 16 2018, 4:23 PM.
Tags
None
Referenced Files
F87368935: D16749.id46864.diff
Tue, Jul 2, 4:59 AM
Unknown Object (File)
Sat, Jun 22, 12:57 AM
Unknown Object (File)
May 4 2024, 6:37 PM
Unknown Object (File)
Apr 25 2024, 9:30 PM
Unknown Object (File)
Apr 24 2024, 5:26 AM
Unknown Object (File)
Apr 24 2024, 5:26 AM
Unknown Object (File)
Feb 14 2024, 10:41 PM
Unknown Object (File)
Jan 11 2024, 9:06 PM

Details

Summary

The linker code currently assumes that ifunc relocations are of type
R_[*]_IRELATIVE, but with -z ifunc-noplt this is no longer true.
In particular, we may also have to apply PC-relative relocations.
Such relocations require a symbol lookup. The default symbol lookup
function, elf_lookup(), calls a kern_linker.c function which performs
a kobj call, which can't work early during boot.

Address these problems:

  • Add a custom symbol lookup function which simply invokes the ifunc resolver and otherwise returns ENOENT if the symbol is not of type GNU_IFUNC. Add a lookup callback to relocate_file1() so that we can use this custom lookup routine early during boot. I like this approach because it helps minimize the amount of kernel code that may be executed for ifunc resolution is performed.
  • Apply all relocations in the kernel executable during boot, rather than trying to filter ifunc relocations. I believe we can safely do this on at least x86.
  • Don't apply ifunc relocations in a separate pass when loading a .so kernel module. elf_reloc_internal() no longer has an easy way of determining whether a given relocation applies to an ifunc or not, and we cannot simply apply all relocations twice since they may not be idempotent.
Test Plan

I tested on i386 and amd64, with and without -zifunc-noplt.

Diff Detail

Repository
rS FreeBSD src repository - subversion
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

sys/kern/link_elf_obj.c
1563 ↗(On Diff #46784)

So this and previous condition body calls are identical now ? How is it supposed to work ?

Let me explain a fundamental issues with ifuncs: we must resolve all non-ifunc relocations when ifuncs resolvers are called. Otherwise, resolvers cannot access global symbols like cpu_feature and cannot call other functions. This is why two-stage resolution is coded there.

sys/kern/link_elf_obj.c
1563 ↗(On Diff #46784)

Oops. But this means that the else case can simply be removed, so the logic becomes "apply relocations against GNU_IFUNC symbols iff 'ifuncs' is true." And link_elf_reloc_local() is never called with ifuncs == true when i386 and amd64 aren't defined.

sys/kern/link_elf_obj.c
1563 ↗(On Diff #46784)

We need to handle both IRELATIVE and STB_LOCAL with target symbol having ifunc type there. Second (now identical) part handled IRELATIVE. This was done after normal relocs processed.

sys/kern/link_elf_obj.c
1563 ↗(On Diff #46784)

Sorry, I still don't see the problem here once the else case is removed. We are applying relocations in two passes, first with ifunc == false and then with ifunc == true. Note that elf_reloc_local() now handles IRELATIVE relocations.

sys/kern/link_elf_obj.c
1563 ↗(On Diff #46784)

Yes, elf_reloc_local() does handle IRELATIVE, but when ? From what I see, they are handled during the first pass, which is too early.

Attempt to fix handling of ifunc relocations in kernel modules.

We must process all non-ifunc relocations first, where an ifunc relocation
is a relocation targetting a symbol of type STT_GNU_IFUNC, or of type
R_*_IRELATIVE. Add the MD predicate function elf_is_ifunc_reloc() for this
purpose.

sys/kern/link_elf_obj.c
1563 ↗(On Diff #46784)

I see, I had forgotten that IRELATIVE relocations do not reference a symbol. I believe the latest upload handles this properly. If you agree with the approach I'll add stub elf_is_ifunc_reloc() predicates to the other platforms.

sys/kern/link_elf.c
1213 ↗(On Diff #46861)

Don't this expression excludes relocations with the referenced symbol type GNU_IFUNC always, even when ifunc == true ?

sys/kern/link_elf.c
1213 ↗(On Diff #46861)

I don't see how. We are comparing "ifuncs" with the expression "symbol_type() == GNU_IFUNC || elf_is_ifunc_reloc()". That is, relocations referencing GNU_IFUNC symbols are processed iff ifuncs == true.

This looks fine then.

sys/kern/link_elf.c
1213 ↗(On Diff #46861)

Ah, I see. I mis-readed the braces.

  • Add elf_is_ifunc_reloc() for non-x86 platforms.
This revision is now accepted and ready to land.Aug 17 2018, 11:11 PM
This revision was automatically updated to reflect the committed changes.