Skip to content

[LLD, MachO] Default objc_relative_method_lists on MacOS11+/iOS14+ #101360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 55 additions & 43 deletions lld/MachO/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1042,20 +1042,36 @@ static bool shouldAdhocSignByDefault(Architecture arch, PlatformType platform) {
platform == PLATFORM_XROS_SIMULATOR;
}

static bool dataConstDefault(const InputArgList &args) {
static const std::array<std::pair<PlatformType, VersionTuple>, 6> minVersion =
{{{PLATFORM_MACOS, VersionTuple(10, 15)},
{PLATFORM_IOS, VersionTuple(13, 0)},
{PLATFORM_TVOS, VersionTuple(13, 0)},
{PLATFORM_WATCHOS, VersionTuple(6, 0)},
{PLATFORM_XROS, VersionTuple(1, 0)},
{PLATFORM_BRIDGEOS, VersionTuple(4, 0)}}};
PlatformType platform = removeSimulator(config->platformInfo.target.Platform);
auto it = llvm::find_if(minVersion,
template <unsigned long N>
using MinVersions = std::array<std::pair<PlatformType, VersionTuple>, N>;

/// Returns true if the platform is greater than the min version.
/// Returns false if the platform does not exist.
template <unsigned long N>
static bool greaterEqMinVersion(const MinVersions<N> &minVersions,
bool ignoreSimulator) {
PlatformType platform = config->platformInfo.target.Platform;
if (ignoreSimulator)
platform = removeSimulator(platform);
auto it = llvm::find_if(minVersions,
[&](const auto &p) { return p.first == platform; });
if (it != minVersion.end())
if (config->platformInfo.target.MinDeployment < it->second)
return false;
if (it != minVersions.end())
if (config->platformInfo.target.MinDeployment >= it->second)
return true;
return false;
}

static bool dataConstDefault(const InputArgList &args) {
static const MinVersions<6> minVersion = {{
{PLATFORM_MACOS, VersionTuple(10, 15)},
{PLATFORM_IOS, VersionTuple(13, 0)},
{PLATFORM_TVOS, VersionTuple(13, 0)},
{PLATFORM_WATCHOS, VersionTuple(6, 0)},
{PLATFORM_XROS, VersionTuple(1, 0)},
{PLATFORM_BRIDGEOS, VersionTuple(4, 0)},
}};
if (!greaterEqMinVersion(minVersion, true))
return false;

switch (config->outputType) {
case MH_EXECUTE:
Expand Down Expand Up @@ -1106,30 +1122,18 @@ static bool shouldEmitChainedFixups(const InputArgList &args) {
if (requested)
return true;

static const std::array<std::pair<PlatformType, VersionTuple>, 9> minVersion =
{{
{PLATFORM_IOS, VersionTuple(13, 4)},
{PLATFORM_IOSSIMULATOR, VersionTuple(16, 0)},
{PLATFORM_MACOS, VersionTuple(13, 0)},
{PLATFORM_TVOS, VersionTuple(14, 0)},
{PLATFORM_TVOSSIMULATOR, VersionTuple(15, 0)},
{PLATFORM_WATCHOS, VersionTuple(7, 0)},
{PLATFORM_WATCHOSSIMULATOR, VersionTuple(8, 0)},
{PLATFORM_XROS, VersionTuple(1, 0)},
{PLATFORM_XROS_SIMULATOR, VersionTuple(1, 0)},
}};
PlatformType platform = config->platformInfo.target.Platform;
auto it = llvm::find_if(minVersion,
[&](const auto &p) { return p.first == platform; });

// We don't know the versions for other platforms, so default to disabled.
if (it == minVersion.end())
return false;

if (it->second > config->platformInfo.target.MinDeployment)
return false;

return true;
static const MinVersions<9> minVersion = {{
{PLATFORM_IOS, VersionTuple(13, 4)},
{PLATFORM_IOSSIMULATOR, VersionTuple(16, 0)},
{PLATFORM_MACOS, VersionTuple(13, 0)},
{PLATFORM_TVOS, VersionTuple(14, 0)},
{PLATFORM_TVOSSIMULATOR, VersionTuple(15, 0)},
{PLATFORM_WATCHOS, VersionTuple(7, 0)},
{PLATFORM_WATCHOSSIMULATOR, VersionTuple(8, 0)},
{PLATFORM_XROS, VersionTuple(1, 0)},
{PLATFORM_XROS_SIMULATOR, VersionTuple(1, 0)},
}};
return greaterEqMinVersion(minVersion, false);
}

static bool shouldEmitRelativeMethodLists(const InputArgList &args) {
Expand All @@ -1140,12 +1144,20 @@ static bool shouldEmitRelativeMethodLists(const InputArgList &args) {
if (arg && arg->getOption().getID() == OPT_no_objc_relative_method_lists)
return false;

// TODO: If no flag is specified, don't default to false, but instead:
// - default false on < ios14
// - default true on >= ios14
// For now, until this feature is confirmed stable, default to false if no
// flag is explicitly specified
return false;
// If no flag is specified, enable this on newer versions by default.
// The min versions is taken from
// ld64(https://github.com/apple-oss-distributions/ld64/blob/47f477cb721755419018f7530038b272e9d0cdea/src/ld/ld.hpp#L310)
// to mimic to operation of ld64
// [here](https://github.com/apple-oss-distributions/ld64/blob/47f477cb721755419018f7530038b272e9d0cdea/src/ld/Options.cpp#L6085-L6101)
static const MinVersions<6> minVersion = {{
{PLATFORM_MACOS, VersionTuple(10, 16)},
{PLATFORM_IOS, VersionTuple(14, 0)},
{PLATFORM_WATCHOS, VersionTuple(7, 0)},
{PLATFORM_TVOS, VersionTuple(14, 0)},
{PLATFORM_BRIDGEOS, VersionTuple(5, 0)},
{PLATFORM_XROS, VersionTuple(1, 0)},
}};
return greaterEqMinVersion(minVersion, true);
}

void SymbolPatterns::clear() {
Expand Down
19 changes: 11 additions & 8 deletions lld/test/MachO/objc-category-conflicts.s
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# `-no_objc_relative_method_lists` needs to be explicitly added to this test to avoid crashing after `-objc_relative_method_lists` was made default.
# TODO: Make this test compatible with default `-objc_relative_method_lists` and remove the `-no_objc_relative_method_lists` flag. Issue #101419

# REQUIRES: x86
# RUN: rm -rf %t; split-file %s %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos11.0 -I %t %t/cat1.s -o %t/cat1.o
Expand All @@ -10,31 +13,31 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos11.0 -I %t %t/cat2.s --defsym MAKE_LOAD_METHOD=1 -o %t/cat2-with-load.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos11.0 -I %t %t/klass.s --defsym MAKE_LOAD_METHOD=1 -o %t/klass-with-load.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos11.0 -I %t %t/klass-with-no-rodata.s -o %t/klass-with-no-rodata.o
# RUN: %lld -dylib -lobjc %t/klass.o -o %t/libklass.dylib
# RUN: %lld -no_objc_relative_method_lists -dylib -lobjc %t/klass.o -o %t/libklass.dylib

# RUN: %no-fatal-warnings-lld --check-category-conflicts -dylib -lobjc %t/klass.o %t/cat1.o %t/cat2.o -o \
# RUN: %no-fatal-warnings-lld -no_objc_relative_method_lists --check-category-conflicts -dylib -lobjc %t/klass.o %t/cat1.o %t/cat2.o -o \
# RUN: /dev/null 2>&1 | FileCheck %s --check-prefixes=CATCLS,CATCAT
# RUN: %no-fatal-warnings-lld --check-category-conflicts -dylib -lobjc %t/libklass.dylib %t/cat1.o \
# RUN: %no-fatal-warnings-lld -no_objc_relative_method_lists --check-category-conflicts -dylib -lobjc %t/libklass.dylib %t/cat1.o \
# RUN: %t/cat2.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=CATCAT

# RUN: %no-fatal-warnings-lld --check-category-conflicts -dylib -lobjc %t/klass_w_sym.o %t/cat1_w_sym.o %t/cat2_w_sym.o -o \
# RUN: %no-fatal-warnings-lld -no_objc_relative_method_lists --check-category-conflicts -dylib -lobjc %t/klass_w_sym.o %t/cat1_w_sym.o %t/cat2_w_sym.o -o \
# RUN: /dev/null 2>&1 | FileCheck %s --check-prefixes=CATCLS_W_SYM,CATCAT_W_SYM
# RUN: %no-fatal-warnings-lld --check-category-conflicts -dylib -lobjc %t/libklass.dylib %t/cat1_w_sym.o \
# RUN: %no-fatal-warnings-lld -no_objc_relative_method_lists --check-category-conflicts -dylib -lobjc %t/libklass.dylib %t/cat1_w_sym.o \
# RUN: %t/cat2_w_sym.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=CATCAT_W_SYM

## Check that we don't emit spurious warnings around the +load method while
## still emitting the other warnings. Note that we have made separate
## `*-with-load.s` files for ease of comparison with ld64; ld64 will not warn
## at all if multiple +load methods are present.
# RUN: %no-fatal-warnings-lld --check-category-conflicts -dylib -lobjc %t/klass-with-load.o \
# RUN: %no-fatal-warnings-lld -no_objc_relative_method_lists --check-category-conflicts -dylib -lobjc %t/klass-with-load.o \
# RUN: %t/cat1-with-load.o %t/cat2-with-load.o -o /dev/null 2>&1 | \
# RUN: FileCheck %s --check-prefixes=CATCLS,CATCAT --implicit-check-not '+load'

## Regression test: Check that we don't crash.
# RUN: %no-fatal-warnings-lld --check-category-conflicts -dylib -lobjc %t/klass-with-no-rodata.o -o /dev/null
# RUN: %no-fatal-warnings-lld -no_objc_relative_method_lists --check-category-conflicts -dylib -lobjc %t/klass-with-no-rodata.o -o /dev/null

## Check that we don't emit any warnings without --check-category-conflicts.
# RUN: %no-fatal-warnings-lld -dylib -lobjc %t/klass.o %t/cat1.o %t/cat2.o -o \
# RUN: %no-fatal-warnings-lld -no_objc_relative_method_lists -dylib -lobjc %t/klass.o %t/cat1.o %t/cat2.o -o \
# RUN: /dev/null 2>&1 | FileCheck %s --implicit-check-not 'warning' --allow-empty

# CATCLS: warning: method '+s1' has conflicting definitions:
Expand Down
13 changes: 8 additions & 5 deletions lld/test/MachO/objc-category-merging-complete-test.s
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# `-no_objc_relative_method_lists` needs to be explicitly added to this test to avoid crashing after `-objc_relative_method_lists` was made default.
# TODO: Make this test compatible with default `-objc_relative_method_lists` and remove the `-no_objc_relative_method_lists` flag. Issue #101419

# REQUIRES: aarch64
# RUN: rm -rf %t; split-file %s %t && cd %t

Expand All @@ -7,18 +10,18 @@
# RUN: %lld -arch arm64 a64_file1.o -o a64_file1.dylib -dylib

# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o a64_file2.o a64_file2.s
# RUN: %lld -arch arm64 -o a64_file2_no_merge.exe a64_file1.dylib a64_file2.o
# RUN: %lld -arch arm64 -o a64_file2_no_merge_v2.exe a64_file1.dylib a64_file2.o -no_objc_category_merging
# RUN: %lld -arch arm64 -o a64_file2_no_merge_v3.exe a64_file1.dylib a64_file2.o -objc_category_merging -no_objc_category_merging
# RUN: %lld -arch arm64 -o a64_file2_merge.exe -objc_category_merging a64_file1.dylib a64_file2.o
# RUN: %lld -no_objc_relative_method_lists -arch arm64 -o a64_file2_no_merge.exe a64_file1.dylib a64_file2.o
# RUN: %lld -no_objc_relative_method_lists -arch arm64 -o a64_file2_no_merge_v2.exe a64_file1.dylib a64_file2.o -no_objc_category_merging
# RUN: %lld -no_objc_relative_method_lists -arch arm64 -o a64_file2_no_merge_v3.exe a64_file1.dylib a64_file2.o -objc_category_merging -no_objc_category_merging
# RUN: %lld -no_objc_relative_method_lists -arch arm64 -o a64_file2_merge.exe -objc_category_merging a64_file1.dylib a64_file2.o

# RUN: llvm-objdump --objc-meta-data --macho a64_file2_no_merge.exe | FileCheck %s --check-prefixes=NO_MERGE_CATS
# RUN: llvm-objdump --objc-meta-data --macho a64_file2_no_merge_v2.exe | FileCheck %s --check-prefixes=NO_MERGE_CATS
# RUN: llvm-objdump --objc-meta-data --macho a64_file2_no_merge_v3.exe | FileCheck %s --check-prefixes=NO_MERGE_CATS
# RUN: llvm-objdump --objc-meta-data --macho a64_file2_merge.exe | FileCheck %s --check-prefixes=MERGE_CATS

############ Test merging multiple categories into the base class ############
# RUN: %lld -arch arm64 -o a64_file2_merge_into_class.exe -objc_category_merging a64_file1.o a64_file2.o
# RUN: %lld -no_objc_relative_method_lists -arch arm64 -o a64_file2_merge_into_class.exe -objc_category_merging a64_file1.o a64_file2.o
# RUN: llvm-objdump --objc-meta-data --macho a64_file2_merge_into_class.exe | FileCheck %s --check-prefixes=MERGE_CATS_CLS


Expand Down
5 changes: 4 additions & 1 deletion lld/test/MachO/objc-category-merging-erase-objc-name-test.s
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# `-no_objc_relative_method_lists` needs to be explicitly added to this test to avoid crashing after `-objc_relative_method_lists` was made default.
# TODO: Make this test compatible with default `-objc_relative_method_lists` and remove the `-no_objc_relative_method_lists` flag. Issue #101419

; REQUIRES: aarch64

; Here we test that if we defined a protocol MyTestProtocol and also a category MyTestProtocol
; then when merging the category into the base class (and deleting the category), we don't
; delete the 'MyTestProtocol' name

; RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o %T/erase-objc-name.o %s
; RUN: %lld -arch arm64 -dylib -o %T/erase-objc-name.dylib %T/erase-objc-name.o -objc_category_merging
; RUN: %lld -no_objc_relative_method_lists -arch arm64 -dylib -o %T/erase-objc-name.dylib %T/erase-objc-name.o -objc_category_merging
; RUN: llvm-objdump --objc-meta-data --macho %T/erase-objc-name.dylib | FileCheck %s --check-prefixes=MERGE_CATS

; === Check merge categories enabled ===
Expand Down
9 changes: 6 additions & 3 deletions lld/test/MachO/objc-category-merging-minimal.s
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# `-no_objc_relative_method_lists` needs to be explicitly added to this test to avoid crashing after `-objc_relative_method_lists` was made default.
# TODO: Make this test compatible with default `-objc_relative_method_lists` and remove the `-no_objc_relative_method_lists` flag. Issue #101419

# REQUIRES: aarch64
# RUN: rm -rf %t; split-file %s %t && cd %t

Expand All @@ -9,23 +12,23 @@
## Create our main testing dylib - linking against the fake dylib above
# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o merge_cat_minimal.o merge_cat_minimal.s
# RUN: %lld -arch arm64 -dylib -o merge_cat_minimal_no_merge.dylib a64_fakedylib.dylib merge_cat_minimal.o
# RUN: %lld -arch arm64 -dylib -o merge_cat_minimal_merge.dylib -objc_category_merging a64_fakedylib.dylib merge_cat_minimal.o
# RUN: %lld -no_objc_relative_method_lists -arch arm64 -dylib -o merge_cat_minimal_merge.dylib -objc_category_merging a64_fakedylib.dylib merge_cat_minimal.o

## Now verify that the flag caused category merging to happen appropriatelly
# RUN: llvm-objdump --objc-meta-data --macho merge_cat_minimal_no_merge.dylib | FileCheck %s --check-prefixes=NO_MERGE_CATS
# RUN: llvm-objdump --objc-meta-data --macho merge_cat_minimal_merge.dylib | FileCheck %s --check-prefixes=MERGE_CATS

############ Test merging multiple categories into the base class ############
# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o merge_base_class_minimal.o merge_base_class_minimal.s
# RUN: %lld -arch arm64 -dylib -o merge_base_class_minimal_yes_merge.dylib -objc_category_merging merge_base_class_minimal.o merge_cat_minimal.o
# RUN: %lld -no_objc_relative_method_lists -arch arm64 -dylib -o merge_base_class_minimal_yes_merge.dylib -objc_category_merging merge_base_class_minimal.o merge_cat_minimal.o
# RUN: %lld -arch arm64 -dylib -o merge_base_class_minimal_no_merge.dylib merge_base_class_minimal.o merge_cat_minimal.o

# RUN: llvm-objdump --objc-meta-data --macho merge_base_class_minimal_no_merge.dylib | FileCheck %s --check-prefixes=NO_MERGE_INTO_BASE
# RUN: llvm-objdump --objc-meta-data --macho merge_base_class_minimal_yes_merge.dylib | FileCheck %s --check-prefixes=YES_MERGE_INTO_BASE

############ Test merging swift category into the base class ############
# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o MyBaseClassSwiftExtension.o MyBaseClassSwiftExtension.s
# RUN: %lld -arch arm64 -dylib -o merge_base_class_swift_minimal_yes_merge.dylib -objc_category_merging MyBaseClassSwiftExtension.o merge_base_class_minimal.o
# RUN: %lld -no_objc_relative_method_lists -arch arm64 -dylib -o merge_base_class_swift_minimal_yes_merge.dylib -objc_category_merging MyBaseClassSwiftExtension.o merge_base_class_minimal.o
# RUN: llvm-objdump --objc-meta-data --macho merge_base_class_swift_minimal_yes_merge.dylib | FileCheck %s --check-prefixes=YES_MERGE_INTO_BASE_SWIFT

#### Check merge categories enabled ###
Expand Down
16 changes: 12 additions & 4 deletions lld/test/MachO/objc-relative-method-lists-simple.s
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,28 @@
# RUN: rm -rf %t; split-file %s %t && cd %t

## Compile a64_rel_dylib.o
# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o a64_rel_dylib.o a64_simple_class.s
# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos10.15 -o a64_rel_dylib.o a64_simple_class.s

## Test arm64 + relative method lists
# RUN: %no-lsystem-lld a64_rel_dylib.o -o a64_rel_dylib.dylib -map a64_rel_dylib.map -dylib -arch arm64 -objc_relative_method_lists
# RUN: %no-lsystem-lld a64_rel_dylib.o -o a64_rel_dylib.dylib -map a64_rel_dylib.map -dylib -arch arm64
# RUN: llvm-objdump --macho --objc-meta-data a64_rel_dylib.dylib | FileCheck %s --check-prefix=CHK_REL

## Test arm64 + relative method lists + dead-strip
# RUN: %no-lsystem-lld a64_rel_dylib.o -o a64_rel_dylib.dylib -map a64_rel_dylib.map -dylib -arch arm64 -objc_relative_method_lists -dead_strip
# RUN: %no-lsystem-lld a64_rel_dylib.o -o a64_rel_dylib.dylib -map a64_rel_dylib.map -dylib -arch arm64 -dead_strip
# RUN: llvm-objdump --macho --objc-meta-data a64_rel_dylib.dylib | FileCheck %s --check-prefix=CHK_REL

## Test arm64 + traditional method lists (no relative offsets)
# RUN: %no-lsystem-lld a64_rel_dylib.o -o a64_rel_dylib.dylib -map a64_rel_dylib.map -dylib -arch arm64 -no_objc_relative_method_lists
# RUN: llvm-objdump --macho --objc-meta-data a64_rel_dylib.dylib | FileCheck %s --check-prefix=CHK_NO_REL

## Test arm64 + relative method lists by explicitly adding `-objc_relative_method_lists`.
# RUN: %lld a64_rel_dylib.o -o a64_rel_dylib.dylib -map a64_rel_dylib.map -dylib -arch arm64 -platform_version macOS 10.15 10.15 -objc_relative_method_lists
# RUN: llvm-objdump --macho --objc-meta-data a64_rel_dylib.dylib | FileCheck %s --check-prefix=CHK_REL

## Test arm64 + no relative method lists by default.
# RUN: %lld a64_rel_dylib.o -o a64_rel_dylib.dylib -map a64_rel_dylib.map -dylib -arch arm64 -platform_version macOS 10.15 10.15
# RUN: llvm-objdump --macho --objc-meta-data a64_rel_dylib.dylib | FileCheck %s --check-prefix=CHK_NO_REL


CHK_REL: Contents of (__DATA_CONST,__objc_classlist) section
CHK_REL-NEXT: _OBJC_CLASS_$_MyClass
Expand Down Expand Up @@ -125,7 +133,7 @@ CHK_NO_REL-NEXT: imp +[MyClass class_method_02]
.include "objc-macros.s"

.section __TEXT,__text,regular,pure_instructions
.build_version macos, 11, 0
.build_version macos, 10, 15

.objc_selector_def "-[MyClass instance_method_00]"
.objc_selector_def "-[MyClass instance_method_01]"
Expand Down
Loading