122 changes: 66 additions & 56 deletions lld/MachO/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -780,70 +780,80 @@ template <class LP> void Writer::createLoadCommands() {
if (config->outputType == MH_EXECUTE)
in.header->addLoadCommand(make<LCMain>());

// See ld64's OutputFile::buildDylibOrdinalMapping for the corresponding
// library ordinal computation code in ld64.
int64_t dylibOrdinal = 1;
DenseMap<StringRef, int64_t> ordinalForInstallName;

std::vector<DylibFile *> dylibFiles;
for (InputFile *file : inputFiles) {
if (auto *dylibFile = dyn_cast<DylibFile>(file)) {
if (dylibFile->isBundleLoader) {
dylibFile->ordinal = BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE;
// Shortcut since bundle-loader does not re-export the symbols.
if (auto *dylibFile = dyn_cast<DylibFile>(file))
dylibFiles.push_back(dylibFile);
}
for (size_t i = 0; i < dylibFiles.size(); ++i)
dylibFiles.insert(dylibFiles.end(), dylibFiles[i]->extraDylibs.begin(),
dylibFiles[i]->extraDylibs.end());

dylibFile->reexport = false;
continue;
}
for (DylibFile *dylibFile : dylibFiles) {
if (dylibFile->isBundleLoader) {
dylibFile->ordinal = BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE;
// Shortcut since bundle-loader does not re-export the symbols.

// Don't emit load commands for a dylib that is not referenced if:
// - it was added implicitly (via a reexport, an LC_LOAD_DYLINKER --
// if it's on the linker command line, it's explicit)
// - or it's marked MH_DEAD_STRIPPABLE_DYLIB
// - or the flag -dead_strip_dylibs is used
// FIXME: `isReferenced()` is currently computed before dead code
// stripping, so references from dead code keep a dylib alive. This
// matches ld64, but it's something we should do better.
if (!dylibFile->isReferenced() && !dylibFile->forceNeeded &&
(!dylibFile->explicitlyLinked || dylibFile->deadStrippable ||
config->deadStripDylibs))
continue;
dylibFile->reexport = false;
continue;
}

// Several DylibFiles can have the same installName. Only emit a single
// load command for that installName and give all these DylibFiles the
// same ordinal.
// This can happen in several cases:
// - a new framework could change its installName to an older
// framework name via an $ld$ symbol depending on platform_version
// - symlinks (for example, libpthread.tbd is a symlink to libSystem.tbd;
// Foo.framework/Foo.tbd is usually a symlink to
// Foo.framework/Versions/Current/Foo.tbd, where
// Foo.framework/Versions/Current is usually a symlink to
// Foo.framework/Versions/A)
// - a framework can be linked both explicitly on the linker
// command line and implicitly as a reexport from a different
// framework. The re-export will usually point to the tbd file
// in Foo.framework/Versions/A/Foo.tbd, while the explicit link will
// usually find Foo.framework/Foo.tbd. These are usually symlinks,
// but in a --reproduce archive they will be identical but distinct
// files.
// In the first case, *semantically distinct* DylibFiles will have the
// same installName.
int64_t &ordinal = ordinalForInstallName[dylibFile->installName];
if (ordinal) {
dylibFile->ordinal = ordinal;
continue;
}
// Don't emit load commands for a dylib that is not referenced if:
// - it was added implicitly (via a reexport, an LC_LOAD_DYLINKER --
// if it's on the linker command line, it's explicit)
// - or it's marked MH_DEAD_STRIPPABLE_DYLIB
// - or the flag -dead_strip_dylibs is used
// FIXME: `isReferenced()` is currently computed before dead code
// stripping, so references from dead code keep a dylib alive. This
// matches ld64, but it's something we should do better.
if (!dylibFile->isReferenced() && !dylibFile->forceNeeded &&
(!dylibFile->isExplicitlyLinked() || dylibFile->deadStrippable ||
config->deadStripDylibs))
continue;

ordinal = dylibFile->ordinal = dylibOrdinal++;
LoadCommandType lcType =
dylibFile->forceWeakImport || dylibFile->refState == RefState::Weak
? LC_LOAD_WEAK_DYLIB
: LC_LOAD_DYLIB;
in.header->addLoadCommand(make<LCDylib>(lcType, dylibFile->installName,
dylibFile->compatibilityVersion,
dylibFile->currentVersion));

if (dylibFile->reexport)
in.header->addLoadCommand(
make<LCDylib>(LC_REEXPORT_DYLIB, dylibFile->installName));
// Several DylibFiles can have the same installName. Only emit a single
// load command for that installName and give all these DylibFiles the
// same ordinal.
// This can happen in several cases:
// - a new framework could change its installName to an older
// framework name via an $ld$ symbol depending on platform_version
// - symlinks (for example, libpthread.tbd is a symlink to libSystem.tbd;
// Foo.framework/Foo.tbd is usually a symlink to
// Foo.framework/Versions/Current/Foo.tbd, where
// Foo.framework/Versions/Current is usually a symlink to
// Foo.framework/Versions/A)
// - a framework can be linked both explicitly on the linker
// command line and implicitly as a reexport from a different
// framework. The re-export will usually point to the tbd file
// in Foo.framework/Versions/A/Foo.tbd, while the explicit link will
// usually find Foo.framework/Foo.tbd. These are usually symlinks,
// but in a --reproduce archive they will be identical but distinct
// files.
// In the first case, *semantically distinct* DylibFiles will have the
// same installName.
int64_t &ordinal = ordinalForInstallName[dylibFile->installName];
if (ordinal) {
dylibFile->ordinal = ordinal;
continue;
}

ordinal = dylibFile->ordinal = dylibOrdinal++;
LoadCommandType lcType =
dylibFile->forceWeakImport || dylibFile->refState == RefState::Weak
? LC_LOAD_WEAK_DYLIB
: LC_LOAD_DYLIB;
in.header->addLoadCommand(make<LCDylib>(lcType, dylibFile->installName,
dylibFile->compatibilityVersion,
dylibFile->currentVersion));

if (dylibFile->reexport)
in.header->addLoadCommand(
make<LCDylib>(LC_REEXPORT_DYLIB, dylibFile->installName));
}

if (functionStartsSection)
Expand Down
80 changes: 69 additions & 11 deletions lld/test/MachO/special-symbol-ld-previous.s
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,110 @@

# RUN: rm -rf %t; split-file --no-leading-lines %s %t

# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/foo.s -o %t/foo.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/ref_xxx.s -o %t/ref_xxx.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/ref_ySyy.s -o %t/ref_ySyy.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/ref_zzz.s -o %t/ref_zzz.o

## Case 1: special symbol $ld$previous affects the install name / compatibility version
## since the specified version 11.0.0 is within the affected range [3.0, 14.0).

# RUN: %lld -o %t/libfoo1.dylib %t/libLDPreviousInstallName.tbd %t/foo.o -dylib -platform_version macos 11.0.0 11.0.0
# RUN: %lld -o %t/libfoo1.dylib %t/libLDPreviousInstallName.tbd %t/ref_xxx.o -dylib -platform_version macos 11.0.0 11.0.0
# RUN: llvm-objdump --macho --dylibs-used %t/libfoo1.dylib | FileCheck --check-prefix=CASE1 %s
# CASE1: /New (compatibility version 1.2.3, current version 5.0.0)
# CASE1: /Old (compatibility version 1.2.3, current version 5.0.0)

## Case 2: special symbol $ld$previous does not affect the install name / compatibility version
## since the specified version 2.0.0 is lower than the affected range [3.0, 14.0).

# RUN: %lld -o %t/libfoo2.dylib %t/libLDPreviousInstallName.tbd %t/foo.o -dylib -platform_version macos 2.0.0 2.0.0
# RUN: %lld -o %t/libfoo2.dylib %t/libLDPreviousInstallName.tbd %t/ref_xxx.o -dylib -platform_version macos 2.0.0 2.0.0
# RUN: llvm-objdump --macho --dylibs-used %t/libfoo2.dylib | FileCheck --check-prefix=CASE2 %s
# CASE2: /Old (compatibility version 1.1.1, current version 5.0.0)
# CASE2: /New (compatibility version 1.1.1, current version 5.0.0)

## Case 3: special symbol $ld$previous does not affect the install name / compatibility version
## since the specified version 14.0.0 is higher than the affected range [3.0, 14.0).

# RUN: %lld -o %t/libfoo3.dylib %t/libLDPreviousInstallName.tbd %t/foo.o -dylib -platform_version macos 2.0.0 2.0.0
# RUN: %lld -o %t/libfoo3.dylib %t/libLDPreviousInstallName.tbd %t/ref_xxx.o -dylib -platform_version macos 2.0.0 2.0.0
# RUN: llvm-objdump --macho --dylibs-used %t/libfoo3.dylib | FileCheck --check-prefix=CASE3 %s
# CASE3: /Old (compatibility version 1.1.1, current version 5.0.0)
# CASE3: /New (compatibility version 1.1.1, current version 5.0.0)

## The remaining cases test handling when a symbol name is part of $ld$previous.

## Case 4: special symbol $ld$previous affects the install name / compatibility version
## when the specified version 11.0.0 is within the affected range [3.0, 14.0) when a symbol
## is part of $previous$ if and only if that named symbol is referenced.
## That is, for $ld$previous$/NewName$$3.0$14.0$_symNam$, if _symNam is
## referenced, it refers to dylib /NewName if the deployment target is
## in [3.0, 14.0).

# RUN: %lld -o %t/libfoo4_yes.dylib %t/libLDPreviousInstallName-Symbol.tbd %t/ref_ySyy.o -dylib -platform_version macos 11.0.0 11.0.0
# RUN: llvm-otool -L %t/libfoo4_yes.dylib | FileCheck --check-prefix=CASE4-YES --implicit-check-not=/New %s
# CASE4-YES: /Old (compatibility version 1.2.3, current version 1.2.3)

## $previous has no effect because deployment target is too new.
# RUN: %lld -o %t/libfoo4_no.dylib %t/libLDPreviousInstallName-Symbol.tbd %t/ref_ySyy.o -dylib -platform_version macos 14.0.0 14.0.0
# RUN: llvm-otool -L %t/libfoo4_no.dylib | FileCheck --check-prefix=CASE4-NO --implicit-check-not=/Old %s
# CASE4-NO: /New (compatibility version 1.1.1, current version 5.0.0)

## $previous has no effect because named symbol isn't referenced.
# RUN: %lld -o %t/libfoo4_no.dylib %t/libLDPreviousInstallName-Symbol.tbd %t/ref_zzz.o -dylib -platform_version macos 11.0.0 11.0.0
# RUN: llvm-otool -L %t/libfoo4_no.dylib | FileCheck --check-prefix=CASE4-NO %s

## Case 5: Reference two symbols that add different $previous names each,
## and one that references the "normal" dylib.
## This should produce three different load commands.
# RUN: %lld -o %t/libfoo5.dylib %t/libLDPreviousInstallName-Symbol.tbd %t/ref_xxx.o %t/ref_ySyy.o %t/ref_zzz.o -dylib -platform_version macos 11.0.0 11.0.0
# RUN: llvm-otool -L %t/libfoo5.dylib | FileCheck --check-prefix=CASE5 %s
# CASE5: /New (compatibility version 1.1.1, current version 5.0.0)
# CASE5-DAG: /Another (compatibility version 1.1.1, current version 5.0.0)
# CASE5-DAG: /Old (compatibility version 1.2.3, current version 1.2.3)

## Check that we emit a warning for an invalid start, end and compatibility versions.

# RUN: %no-fatal-warnings-lld -o %t/libfoo1.dylib %t/libLDPreviousInvalid.tbd %t/foo.o -dylib \
# RUN: %no-fatal-warnings-lld -o %t/libfoo1.dylib %t/libLDPreviousInvalid.tbd %t/ref_xxx.o -dylib \
# RUN: -platform_version macos 11.0.0 11.0.0 2>&1 | FileCheck --check-prefix=INVALID-VERSION %s

# INVALID-VERSION-DAG: failed to parse start version, symbol '$ld$previous$/New$1.2.3$1$3.a$14.0$$' ignored
# INVALID-VERSION-DAG: failed to parse end version, symbol '$ld$previous$/New$1.2.3$1$3.0$14.b$$' ignored
# INVALID-VERSION-DAG: failed to parse compatibility version, symbol '$ld$previous$/New$1.2.c$1$3.0$14.0$$' ignored

#--- foo.s
#--- ref_xxx.s
.long _xxx@GOTPCREL

#--- ref_ySyy.s
.long _y$yy@GOTPCREL

#--- ref_zzz.s
.long _zzz@GOTPCREL

#--- libLDPreviousInstallName.tbd
--- !tapi-tbd-v3
archs: [ x86_64 ]
uuids: [ 'x86_64: 19311019-01AB-342E-812B-73A74271A715' ]
platform: macosx
install-name: '/Old'
install-name: '/New'
current-version: 5
compatibility-version: 1.1.1
exports:
- archs: [ x86_64 ]
symbols: [ '$ld$previous$/Old$1.2.3$1$3.0$14.0$$', _xxx ]
...

#--- libLDPreviousInstallName-Symbol.tbd
--- !tapi-tbd-v3
archs: [ x86_64 ]
uuids: [ 'x86_64: 19311019-01AB-342E-812B-73A74271A715' ]
platform: macosx
install-name: '/New'
current-version: 5
compatibility-version: 1.1.1
exports:
- archs: [ x86_64 ]
symbols: [ '$ld$previous$/New$1.2.3$1$3.0$14.0$$', _xxx ]
symbols: [
'$ld$previous$/Another$$1$3.0$14.0$_xxx$',
'$ld$previous$/Old$1.2.3$1$3.0$14.0$_y$yy$',
_xxx,
'_y$yy',
_zzz,
]
...

#--- libLDPreviousInvalid.tbd
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Support/Host.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ namespace sys {
StringRef getHostCPUNameForARM(StringRef ProcCpuinfoContent);
StringRef getHostCPUNameForS390x(StringRef ProcCpuinfoContent);
StringRef getHostCPUNameForRISCV(StringRef ProcCpuinfoContent);
StringRef getHostCPUNameForSPARC(StringRef ProcCpuinfoContent);
StringRef getHostCPUNameForBPF();

/// Helper functions to extract CPU details from CPUID on x86.
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ elseif( CMAKE_HOST_UNIX )
if( UNIX AND NOT (BEOS OR HAIKU) )
set(system_libs ${system_libs} m)
endif()
if( UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" )
set(system_libs ${system_libs} kstat)
endif()
if( FUCHSIA )
set(system_libs ${system_libs} zircon)
endif()
Expand Down
108 changes: 108 additions & 0 deletions llvm/lib/Support/Host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
#ifdef _AIX
#include <sys/systemcfg.h>
#endif
#if defined(__sun__) && defined(__svr4__)
#include <kstat.h>
#endif

#define DEBUG_TYPE "host-detection"

Expand Down Expand Up @@ -1413,6 +1416,111 @@ StringRef sys::getHostCPUName() {
#endif
#endif
}
#elif defined(__sparc__)
#if defined(__linux__)
StringRef sys::detail::getHostCPUNameForSPARC(StringRef ProcCpuinfoContent) {
SmallVector<StringRef> Lines;
ProcCpuinfoContent.split(Lines, "\n");

// Look for cpu line to determine cpu name
StringRef Cpu;
for (unsigned I = 0, E = Lines.size(); I != E; ++I) {
if (Lines[I].startswith("cpu")) {
Cpu = Lines[I].substr(5).ltrim("\t :");
break;
}
}

return StringSwitch<const char *>(Cpu)
.StartsWith("SuperSparc", "supersparc")
.StartsWith("HyperSparc", "hypersparc")
.StartsWith("SpitFire", "ultrasparc")
.StartsWith("BlackBird", "ultrasparc")
.StartsWith("Sabre", " ultrasparc")
.StartsWith("Hummingbird", "ultrasparc")
.StartsWith("Cheetah", "ultrasparc3")
.StartsWith("Jalapeno", "ultrasparc3")
.StartsWith("Jaguar", "ultrasparc3")
.StartsWith("Panther", "ultrasparc3")
.StartsWith("Serrano", "ultrasparc3")
.StartsWith("UltraSparc T1", "niagara")
.StartsWith("UltraSparc T2", "niagara2")
.StartsWith("UltraSparc T3", "niagara3")
.StartsWith("UltraSparc T4", "niagara4")
.StartsWith("UltraSparc T5", "niagara4")
.StartsWith("LEON", "leon3")
// niagara7/m8 not supported by LLVM yet.
.StartsWith("SPARC-M7", "niagara4" /* "niagara7" */)
.StartsWith("SPARC-S7", "niagara4" /* "niagara7" */)
.StartsWith("SPARC-M8", "niagara4" /* "m8" */)
.Default("generic");
}
#endif

StringRef sys::getHostCPUName() {
#if defined(__linux__)
std::unique_ptr<llvm::MemoryBuffer> P = getProcCpuinfoContent();
StringRef Content = P ? P->getBuffer() : "";
return detail::getHostCPUNameForSPARC(Content);
#elif defined(__sun__) && defined(__svr4__)
char *buf = NULL;
kstat_ctl_t *kc;
kstat_t *ksp;
kstat_named_t *brand = NULL;

kc = kstat_open();
if (kc != NULL) {
ksp = kstat_lookup(kc, const_cast<char *>("cpu_info"), -1, NULL);
if (ksp != NULL && kstat_read(kc, ksp, NULL) != -1 &&
ksp->ks_type == KSTAT_TYPE_NAMED)
brand =
(kstat_named_t *)kstat_data_lookup(ksp, const_cast<char *>("brand"));
if (brand != NULL && brand->data_type == KSTAT_DATA_STRING)
buf = KSTAT_NAMED_STR_PTR(brand);
}
kstat_close(kc);

return StringSwitch<const char *>(buf)
.Case("TMS390S10", "supersparc") // Texas Instruments microSPARC I
.Case("TMS390Z50", "supersparc") // Texas Instruments SuperSPARC I
.Case("TMS390Z55",
"supersparc") // Texas Instruments SuperSPARC I with SuperCache
.Case("MB86904", "supersparc") // Fujitsu microSPARC II
.Case("MB86907", "supersparc") // Fujitsu TurboSPARC
.Case("RT623", "hypersparc") // Ross hyperSPARC
.Case("RT625", "hypersparc")
.Case("RT626", "hypersparc")
.Case("UltraSPARC-I", "ultrasparc")
.Case("UltraSPARC-II", "ultrasparc")
.Case("UltraSPARC-IIe", "ultrasparc")
.Case("UltraSPARC-IIi", "ultrasparc")
.Case("SPARC64-III", "ultrasparc")
.Case("SPARC64-IV", "ultrasparc")
.Case("UltraSPARC-III", "ultrasparc3")
.Case("UltraSPARC-III+", "ultrasparc3")
.Case("UltraSPARC-IIIi", "ultrasparc3")
.Case("UltraSPARC-IIIi+", "ultrasparc3")
.Case("UltraSPARC-IV", "ultrasparc3")
.Case("UltraSPARC-IV+", "ultrasparc3")
.Case("SPARC64-V", "ultrasparc3")
.Case("SPARC64-VI", "ultrasparc3")
.Case("SPARC64-VII", "ultrasparc3")
.Case("UltraSPARC-T1", "niagara")
.Case("UltraSPARC-T2", "niagara2")
.Case("UltraSPARC-T2", "niagara2")
.Case("UltraSPARC-T2+", "niagara2")
.Case("SPARC-T3", "niagara3")
.Case("SPARC-T4", "niagara4")
.Case("SPARC-T5", "niagara4")
// niagara7/m8 not supported by LLVM yet.
.Case("SPARC-M7", "niagara4" /* "niagara7" */)
.Case("SPARC-S7", "niagara4" /* "niagara7" */)
.Case("SPARC-M8", "niagara4" /* "m8" */)
.Default("generic");
#else
return "generic";
#endif
}
#else
StringRef sys::getHostCPUName() { return "generic"; }
namespace llvm {
Expand Down