This is an archive of the discontinued LLVM Phabricator instance.

[JITLink] Introduce WeakExternal linkage type.
AbandonedPublic

Authored by sunho on Jul 24 2022, 6:47 PM.

Details

Reviewers
lhames
sgallagher
Summary

Introduces WeakExternal linkage type to JITLink that looks up external definition before trying to use definition in current linkgraph.

In COFF, there is a notion of weak external symbol. The exact behaviour is defined as follwing in the spec: "If a definition of external symbol is linked, then an external reference to the symbol is resolved normally. If a definition of external symbol is not linked, then all references to the weak external for external symbol refer to stub symbol instead."

An aspect where it's inaccurate to use current Weak jitlink linkage type is that it does not eagerly looks for external definition, but everyone defines it blindly and the one defined earlier is used. So, in the testcase added in this patch, it will not look for definition in strong_def.yaml, thus linkgraph of strong_def.yaml will not even be emitted. WeakExternal linkage type gives a priority to external definition which solves this issue.

Diff Detail

Event Timeline

sunho created this revision.Jul 24 2022, 6:47 PM
Herald added a project: Restricted Project. · View Herald TranscriptJul 24 2022, 6:47 PM
sunho requested review of this revision.Jul 24 2022, 6:47 PM
Herald added a project: Restricted Project. · View Herald TranscriptJul 24 2022, 6:47 PM

"If a definition of external symbol is linked, then an external reference to the symbol is resolved normally. If a definition of external symbol is not linked, then all references to the weak external for external symbol refer to stub symbol instead."

This is curious. Is it just a weaker weak? I.e. "Prefer another weak def if you have one, but use me as a last resort"? What happens if you have multiple instances of a symbol all tagged with this linkage? Does the linker just choose arbitrarily the same way that it would for a regular weak symbol? And how does this interact with COMDATs? Or is it never used on symbols in COMDAT sections?

If this behaves differently to "weak" or "strong" we'll need a new JITSymbolFlag for it too, and handling in the ORC symbol tables.

sunho added a comment.EditedJul 25 2022, 8:20 PM

"If a definition of external symbol is linked, then an external reference to the symbol is resolved normally. If a definition of external symbol is not linked, then all references to the weak external for external symbol refer to stub symbol instead."

This is curious. Is it just a weaker weak? I.e. "Prefer another weak def if you have one, but use me as a last resort"? What happens if you have multiple instances of a symbol all tagged with this linkage? Does the linker just choose arbitrarily the same way that it would for a regular weak symbol? And how does this interact with COMDATs? Or is it never used on symbols in COMDAT sections?

If this behaves differently to "weak" or "strong" we'll need a new JITSymbolFlag for it too, and handling in the ORC symbol tables.

@lhames The way I understand this now is that the characteristics flag decide whether it will be exported to the external objects or not. For instance, if anything else than weak_alias flag is used it will be set "SF_undefined" in symbol list provided in libObject implementation. So, technically weak_alias is more similar to traditional weak symbol while other flags like search_library/no_library are "really weak."

For weak_alias case, it's fine to deal it with weak linkage type as they will be visible to the outside world and potentially crash with strong definition. But, for search_library and alternatename case, it will be not visible in symbol list cause SF_undefined flag is set or symbol is just not in symbol table. In this case, strong definition will not even be emitted so they don't get a chance to even crash with strong definition, or user provided definition is ignored.

For COMDATs, they are fine to be dealt with weak as they are always visible to external world AFAIK. There is one inaccuracy in how we're dealing with them -- strong + COMDAT any is duplicate definition error. But, I think that is fine.

I just read up on the details here -- https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-3-weak-externals. I was too focused on the Weak bit, not enough on the "External" bit. :)

@lhames The way I understand this now is that the characteristics flag decide whether it will be exported to the external objects or not. For instance, if anything else than weak_alias flag is used it will be set "SF_undefined" in symbol list provided in libObject implementation. So, technically weak_alias is more similar to traditional weak symbol while other flags like search_library/no_library are "really weak."

Let me see if I understand these modes:

weak_alias -- A weak extern with Characteristics == IMAGE_WEAK_EXTERN_SEARCH_ALIAS. Should be treated as a weak alias (we should introduce a proper alias concept into JITLink)
alternatename -- A weak extern with either Characteristics == IMAGE_WEAK_EXTERN_SEARCH_LIBRARY or Characteristics == IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY. Should be treated as a weak-ref of the external name, but transform into an alias for the alternatename if the external name is not found. The Characteristics determine whether the weak-ref should proceed into other libraries, or stay within the current one.

Does that sound right?

I think that we probably need new LinkGraph infrastructure for this, but I don't think a new LinkageType is the right approach. I suspect that we want to define symbol alias support, and encode this behavior into that. I'll wait to get confirmation from you about how I'm looking at this before I dig too much deeper though.

sunho abandoned this revision.Jul 30 2022, 5:26 PM

Copy pasting the message I sent eariler again here to make context clear:

I've confirmed a lot of behavior through toying with link.exe.
IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY:

  • Impression: does not look up strong definitions from static libraries, but if a strong symbol from a static library was loaded by other means, it will use it. Strong definition inside input object files of executable was always honored – I believe this is because MSVC load all object files of executable by default
  • Symbol is not visible to the outside; usually for importing functions that are to be used internally within an object file.
  • Used a lot by msvc.

IMAGE_WEAK_EXTERN_SEARCH_LIBRARY:

  • This one does look up strong definitions from static archives.
  • I haven’t seen it used by both msvc and clang.

IMAGE_WEAK_EXTERN_SEARCH_ALIAS:

  • Basically the same as IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY, but it is visible to outside.
  • Used a lot by clang, never seem to be used by msvc.

Summing up, we can just use ordinary strong and weak linkage types except for IMAGE_WEAK_EXTERN_SEARCH_LIBRARY if we are not going to support alias to external symbol. (alias to external symbol is not used by both toolchains so far) I probably got confused by it always honoring strong def from object files of executable. For "force loading" object files of executable, this shouldn't be dealt inside jitlink at all. (probably inside llvm-jitllnk tool or ORC linkinglayer?)

Anyways, we definitely don't need this patch -- some part of it can be taken to implement search_library or executable object files behaviour though.

BTW, Lookup api might still be worth it even if we are not going to implement search_library. Relocations should reference symbols of other jitdylib only through dllimport stub. It makes sense to split lookup ranges.