Page MenuHomeFreeBSD

linker: Improve handling of ifuncs when fetching symbol metadata
AcceptedPublic

Authored by markj on Wed, Jun 4, 2:35 PM.
Tags
None
Referenced Files
F122027173: D50683.id.diff
Tue, Jul 1, 2:06 PM
F121988144: D50683.id157603.diff
Tue, Jul 1, 5:38 AM
F121985581: D50683.diff
Tue, Jul 1, 5:05 AM
F121968522: D50683.id157615.diff
Tue, Jul 1, 1:51 AM
Unknown Object (File)
Mon, Jun 30, 1:42 AM
Unknown Object (File)
Mon, Jun 30, 12:31 AM
Unknown Object (File)
Sun, Jun 29, 8:58 AM
Unknown Object (File)
Thu, Jun 26, 1:38 PM
Subscribers

Details

Reviewers
jhb
kib
jrtc27
Summary

When looking up symbol values, we map ifunc symbols to the value
returned by the resolver. However, the returned symbol size is still
that of the resolver. Be consistent and provide the size of the
implementation symbol as well.

This fixes an inconsistency in dtrace's FBT provider, which enumerates
all function symbols and disassembles their values, using the symbol
size as the bound for the disassembly loop.

Sponsored by: Innovate UK

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped
Build Status
Buildable 65082
Build 61965: arc lint + arc unit

Event Timeline

markj requested review of this revision.Wed, Jun 4, 2:35 PM

What if there is no symbol (which presumably shows up as off != 0, using whatever symbol happens to be before it)? Or it's in another file? Those are legitimate cases (the former in particular, that's for when the implementation is static, and the latter is weird but there's no reason you can't do it).

What if there is no symbol (which presumably shows up as off != 0, using whatever symbol happens to be before it)? Or it's in another file? Those are legitimate cases (the former in particular, that's for when the implementation is static, and the latter is weird but there's no reason you can't do it).

If there's no symbol, what should we do? I don't think it's really correct to keep passing the resolver symbol's size, so should we just set it to 0 instead? In practice this case currently doesn't arise AFAICT, and so I'm rather inclined to signal the failure to the consumer.

For symbols in different linker files, we can fall back to a global search if a lookup in the current linker file fails.

What if there is no symbol (which presumably shows up as off != 0, using whatever symbol happens to be before it)? Or it's in another file? Those are legitimate cases (the former in particular, that's for when the implementation is static, and the latter is weird but there's no reason you can't do it).

If there's no symbol, what should we do? I don't think it's really correct to keep passing the resolver symbol's size, so should we just set it to 0 instead? In practice this case currently doesn't arise AFAICT, and so I'm rather inclined to signal the failure to the consumer.

I don't know, but I'm surprised it's not a common case already. On amd64 sure I guess you don't expect to see it, because the ET_REL .ko file probably has all the symbols for all the static functions still. Looking on arm64 it seems we still have .symtab, not just .dynsym, so the former has names still for all the static functions, and we're using ddbsymtab here which is in fact .symtab, even for link_elf. That's what the if (ef->symtab == ef->ddbsymtab) is for (confusingly named, but ef->symtab is DT_SYMTAB i.e. .dynsym); if equal, it couldn't find .symtab, so fell back on .dynsym for DDB use, but if different, it has full symbol information from .symtab. So I guess this should in fact be fine thanks to the oddities of the kernel linker.

For symbols in different linker files, we can fall back to a global search if a lookup in the current linker file fails.

Yeah, though that sounds like it could confuse FBT to get a value pointing to a different linker file's memory.

sys/kern/link_elf.c
1684

Why ?

It is not wrong for the resolver to return something that is not labeled.

What if there is no symbol (which presumably shows up as off != 0, using whatever symbol happens to be before it)? Or it's in another file? Those are legitimate cases (the former in particular, that's for when the implementation is static, and the latter is weird but there's no reason you can't do it).

If there's no symbol, what should we do? I don't think it's really correct to keep passing the resolver symbol's size, so should we just set it to 0 instead? In practice this case currently doesn't arise AFAICT, and so I'm rather inclined to signal the failure to the consumer.

I don't know, but I'm surprised it's not a common case already. On amd64 sure I guess you don't expect to see it, because the ET_REL .ko file probably has all the symbols for all the static functions still. Looking on arm64 it seems we still have .symtab, not just .dynsym, so the former has names still for all the static functions, and we're using ddbsymtab here which is in fact .symtab, even for link_elf. That's what the if (ef->symtab == ef->ddbsymtab) is for (confusingly named, but ef->symtab is DT_SYMTAB i.e. .dynsym); if equal, it couldn't find .symtab, so fell back on .dynsym for DDB use, but if different, it has full symbol information from .symtab. So I guess this should in fact be fine thanks to the oddities of the kernel linker.

For symbols in different linker files, we can fall back to a global search if a lookup in the current linker file fails.

Yeah, though that sounds like it could confuse FBT to get a value pointing to a different linker file's memory.

I don't think FBT would care. The only requirement I think of is that the kernel module which implements the ifunc is a dependency of the kernel module which returns it from a resolver. That is, if kernel module A contains an ifunc whose resolver returns a symbol from kernel module B, then kernel module B must not be unloaded. But this requirement is not specific to dtrace.

  • If we fail to find a symbol for the resolvee, return a symbol size of 0 instead of copying that of the resolver.
  • Search direct dependencies for the resolvee's symbol if a search of the current linker file fails.
sys/kern/link_elf.c
1648

I think 'resolved place' or 'location' is more common term.

1650

This search is not recursive. IMO it should be (i.e. look into whole dep tree, not just level 1), or not be done at all.

1662

IMO native word size is somewhat more natural, but I do not insist.

markj added inline comments.
sys/kern/link_elf.c
1650

Suppose my linker file contains an ifunc resolver which returns a pointer to a function in a different linker file. Shouldn't that linker file be a direct dependency of my linker file? If it is not, what prevents that linker file from being unloaded while the ifunc is being called?

sys/kern/link_elf.c
1650

Generally no, what resolver returns might be from any object. And it is not the linker duty to ensure that the object is not unloaded.

But I am describing the proper ELF dynamic linker behavior (ld-elf.so). So I retract my comment, since for kernel we do not do the proper recursive search when we resolve symbols on load. All deps must be explicitly specified by MODULE_DEPEND(). Then it does not make sense to start doing it there.

Don't search anything but the requested linker file.

This revision is now accepted and ready to land.Wed, Jun 25, 8:04 PM
sys/kern/link_elf.c
1650

In my testing, if a resolver references symbol from a different linker file, we can still use the entry from the current linker file's symbol table to find a copy of the target symbol. So at the moment I don't have a case where we will search even the direct dependencies.