This is an archive of the discontinued LLVM Phabricator instance.

[lld-macho] -flat_namespace for dylibs should make all externs interposable
ClosedPublic

Authored by int3 on Feb 8 2022, 3:00 PM.

Details

Summary

All references to interposable symbols can be redirected at runtime to
point to a different symbol definition (with the same name). For
example, if both dylib A and B define symbol _foo, and we load A before
B at runtime, then all references to _foo within dylib B will point to
the definition in dylib A.

ld64 makes all extern symbols interposable when linking with
-flat_namespace.

TODO 1: Support -interposable and -interposable_list, which should
just be a matter of parsing those CLI flags and setting the
Defined::interposable bit.

TODO 2: Set Reloc::FinalDefinitionInLinkageUnit correctly with this info
(we are currently not setting it at all, so we're erring on the
conservative side, but we should help the LTO backend generate more
optimal code.)

Diff Detail

Event Timeline

int3 created this revision.Feb 8 2022, 3:00 PM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 8 2022, 3:00 PM
int3 requested review of this revision.Feb 8 2022, 3:00 PM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 8 2022, 3:00 PM
int3 edited the summary of this revision. (Show Details)Feb 8 2022, 3:02 PM
int3 edited the summary of this revision. (Show Details)Feb 8 2022, 9:12 PM
int3 edited the summary of this revision. (Show Details)
int3 updated this revision to Diff 407212.Feb 9 2022, 10:49 AM

update test

modimo added a subscriber: modimo.Mar 9 2022, 1:13 PM
modimo added inline comments.
lld/MachO/SymbolTable.cpp
99

Could this be a config property rather than a per-symbol? It looks like a link invocation will always have flat_namespace or never have it.

lld/MachO/SyntheticSections.cpp
422

Note that the non-lazy binding and lazy binding sections now have to accommodate both DylibSymbols and (interposable) Defined symbols.

Aside from s/DylibSymbol/Symbol/g is the only other change to support this here?

Also, would recommended factoring out the s/DylibSymbol/Symbol/g change to a separate diff as a parent to make it easier to see what files have logic changes in them

Herald added a project: Restricted Project. · View Herald TranscriptMar 9 2022, 1:13 PM
int3 marked 2 inline comments as done.Mar 9 2022, 8:42 PM
int3 added inline comments.
lld/MachO/SymbolTable.cpp
99

ah this was done with an eye for future support of -interposable_list, which will allow specific symbols to be treated as interposable. I'll add a comment.

lld/MachO/SyntheticSections.cpp
422

Pretty much yeah. I'll split out the NFC changes 👍

int3 updated this revision to Diff 414272.Mar 9 2022, 8:43 PM
int3 marked 2 inline comments as done.
int3 edited the summary of this revision. (Show Details)

split out NFC changes

int3 updated this revision to Diff 414468.Mar 10 2022, 12:46 PM

Internally in LLVM IR processing interposability is represented by the following check:

bool GlobalValue::isInterposable() const {
  if (isInterposableLinkage(getLinkage()))
    return true;
  return getParent() && getParent()->getSemanticInterposition() &&
         !isDSOLocal();
}

I'm wondering: is there's a case where a dso_local function/global that comes into link with -flat_namespace/-interposable set? In ELF land interposability is set at compile time but with these flags on MachO it looks possible to set them at link time. If so that also makes pre-LTO optimizations strange because it might inline a function that later turns interposable. Given the previous statement I suspect I'm missing something here.

lld/MachO/SyntheticSections.cpp
422

Thanks!

int3 updated this revision to Diff 414525.Mar 10 2022, 4:08 PM

move assert from other diff

int3 added a comment.Mar 10 2022, 4:09 PM

Hmm, maybe that's why clang doesn't seem to set dso_local when compiling for macos?

~/tmp: cat test.c
void f1(char a) {}
~/tmp: clang -target x86_64-unknown-linux-gnu -c -emit-llvm test.c -o - | llvm-dis
; ModuleID = '<stdin>'
source_filename = "test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: noinline nounwind optnone uwtable
define dso_local void @f1(i8 signext %0) #0 {
  %2 = alloca i8, align 1
  store i8 %0, i8* %2, align 1
  ret void
}

attributes #0 = { noinline nounwind optnone uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"Apple clang version 13.0.0 (clang-1300.0.29.30)"}
~/tmp: clang -target x86_64-apple-darwin -c -emit-llvm test.c -o - | llvm-dis
; ModuleID = '<stdin>'
source_filename = "test.c"
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx12.0.0"

; Function Attrs: noinline nounwind optnone ssp uwtable
define void @f1(i8 signext %0) #0 {
  %2 = alloca i8, align 1
  store i8 %0, i8* %2, align 1
  ret void
}

attributes #0 = { noinline nounwind optnone ssp uwtable "darwin-stkchk-strong-link" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "probe-stack"="___chkstk_darwin" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}

!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 12, i32 1]}
!1 = !{i32 1, !"wchar_size", i32 4}
!2 = !{i32 7, !"PIC Level", i32 2}
!3 = !{!"Apple clang version 13.0.0 (clang-1300.0.29.30)"}
int3 added a comment.Mar 10 2022, 4:12 PM

Not sure where in the clang codebase this dso_local (or lack thereof) is being determined, though...

Not sure where in the clang codebase this dso_local (or lack thereof) is being determined, though...

@MaskRay has done the most with dso_local recently, I think.

Clang-12 maintains dso_local on f1 as well, clang-13 and trunk lose it: https://godbolt.org/z/oejTzWsoW

MaskRay added a comment.EditedMar 11 2022, 4:49 PM

I'm wondering: is there's a case where a dso_local function/global that comes into link with -flat_namespace/-interposable set? In ELF land interposability is set at compile time but with these flags on MachO it looks possible to set them at link time. If so that also makes pre-LTO optimizations strange because it might inline a function that later turns interposable. Given the previous statement I suspect I'm missing something here.

https://maskray.me/blog/2021-05-09-fno-semantic-interposition#clang--fno-semantic-interposition has some information about ELF -fno-semantic-interposition.

For ELF, yes. -fno-pic and -fpie have no interposition on defined symbols.
For -fpic, Clang defaults to -fhalf-no-semantic-interposition where interprocedural optimizations are available at compile time even if at link time the symbol are preemptible.
This seems strange but is important to retain the longstanding behavior (not regressing optimizations people get with default clang -fpic -O2 a.cc).
I actually hope that we can transit from the current confusing 3 states to: "Can Clang default to -fno-semantic-interposition?" but there may be some risk (perhaps some people can complain about some cases as described in https://maskray.me/blog/2021-05-16-elf-interposition-and-bsymbolic#interaction-with-ld_preload)

Hmm, maybe that's why clang doesn't seem to set dso_local when compiling for macos?

D98458 (in release/13.x but not in release/12.x) is related. Seems that XNU has reliance on not having dso_local for some cases.

MaskRay added inline comments.Mar 11 2022, 4:53 PM
lld/test/MachO/flat-namespace-interposable.s
51

add .private_extern test

int3 updated this revision to Diff 415224.Mar 14 2022, 2:20 PM
int3 marked an inline comment as done.

exclude private_externs from being interposable

lld/test/MachO/flat-namespace-interposable.s
51

whoops, I'd indeed forgotten to handle this case in the code. Thanks!

MaskRay accepted this revision as: MaskRay.Mar 14 2022, 2:29 PM
This revision is now accepted and ready to land.Mar 14 2022, 2:29 PM
modimo accepted this revision.Mar 14 2022, 4:29 PM