blob: 0e0d72908f5331f1351e307cc535348e9e67cc52 [file] [log] [blame]
From e29dc0c6fde284e7f05aa5f45b05c629c9fad295 Mon Sep 17 00:00:00 2001
From: Alex Borcan <alexborcan@fb.com>
Date: Tue, 3 May 2022 21:00:41 -0400
Subject: [PATCH] [lld] Implement safe icf for MachO
This change implements --icf=safe for MachO based on addrsig section that is implemented in D123751.
Reviewed By: int3, #lld-macho
Differential Revision: https://reviews.llvm.org/D123752
---
lld/MachO/Driver.cpp | 8 +++---
lld/MachO/ICF.cpp | 38 +++++++++++++++++++++++-
lld/MachO/ICF.h | 4 +++
lld/MachO/InputFiles.cpp | 3 ++
lld/MachO/InputFiles.h | 1 +
lld/MachO/InputSection.h | 3 ++
lld/test/MachO/icf-options.s | 5 ++--
lld/test/MachO/icf-safe.s | 56 ++++++++++++++++++++++++++++++++++++
8 files changed, 110 insertions(+), 8 deletions(-)
create mode 100644 lld/test/MachO/icf-safe.s
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 5b95278a991c..15daac5e5512 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -721,9 +721,6 @@ static ICFLevel getICFLevel(const ArgList &args) {
warn(Twine("unknown --icf=OPTION `") + icfLevelStr +
"', defaulting to `none'");
icfLevel = ICFLevel::none;
- } else if (icfLevel == ICFLevel::safe) {
- warn(Twine("`--icf=safe' is not yet implemented, reverting to `none'"));
- icfLevel = ICFLevel::none;
}
return icfLevel;
}
@@ -1545,8 +1542,11 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
// ICF assumes that all literals have been folded already, so we must run
// foldIdenticalLiterals before foldIdenticalSections.
foldIdenticalLiterals();
- if (config->icfLevel != ICFLevel::none)
+ if (config->icfLevel != ICFLevel::none) {
+ if (config->icfLevel == ICFLevel::safe)
+ markAddrSigSymbols();
foldIdenticalSections();
+ }
// Write to an output file.
if (target->wordSize == 8)
diff --git a/lld/MachO/ICF.cpp b/lld/MachO/ICF.cpp
index 29892b66b31b..e35944343012 100644
--- a/lld/MachO/ICF.cpp
+++ b/lld/MachO/ICF.cpp
@@ -8,11 +8,14 @@
#include "ICF.h"
#include "ConcatOutputSection.h"
+#include "Config.h"
#include "InputSection.h"
+#include "SymbolTable.h"
#include "Symbols.h"
#include "UnwindInfoSection.h"
#include "lld/Common/CommonLinkerContext.h"
+#include "llvm/Support/LEB128.h"
#include "llvm/Support/Parallel.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/xxhash.h"
@@ -363,6 +366,39 @@ void ICF::segregate(size_t begin, size_t end, EqualsFn equals) {
}
}
+void macho::markSymAsAddrSig(Symbol *s) {
+ if (auto *d = dyn_cast_or_null<Defined>(s))
+ if (d->isec)
+ d->isec->keepUnique = true;
+}
+
+void macho::markAddrSigSymbols() {
+ for (InputFile *file : inputFiles) {
+ ObjFile *obj = dyn_cast<ObjFile>(file);
+ if (!obj)
+ continue;
+
+ Section *addrSigSection = obj->addrSigSection;
+ if (!addrSigSection)
+ continue;
+ assert(addrSigSection->subsections.size() == 1);
+
+ Subsection *subSection = &addrSigSection->subsections[0];
+ ArrayRef<unsigned char> &contents = subSection->isec->data;
+
+ const uint8_t *pData = contents.begin();
+ while (pData != contents.end()) {
+ unsigned size;
+ const char *err;
+ uint32_t symIndex = decodeULEB128(pData, &size, contents.end(), &err);
+ if (err)
+ fatal(toString(file) + ": could not decode addrsig section: " + err);
+ markSymAsAddrSig(obj->symbols[symIndex]);
+ pData += size;
+ }
+ }
+}
+
void macho::foldIdenticalSections() {
TimeTraceScope timeScope("Fold Identical Code Sections");
// The ICF equivalence-class segregation algorithm relies on pre-computed
@@ -385,7 +421,7 @@ void macho::foldIdenticalSections() {
// FIXME: consider non-code __text sections as hashable?
bool isHashable = (isCodeSection(isec) || isCfStringSection(isec) ||
isClassRefsSection(isec)) &&
- !isec->shouldOmitFromOutput() &&
+ !isec->keepUnique && !isec->shouldOmitFromOutput() &&
sectionType(isec->getFlags()) == MachO::S_REGULAR;
if (isHashable) {
hashable.push_back(isec);
diff --git a/lld/MachO/ICF.h b/lld/MachO/ICF.h
index 9500a946601e..a287692d7ffa 100644
--- a/lld/MachO/ICF.h
+++ b/lld/MachO/ICF.h
@@ -9,12 +9,16 @@
#ifndef LLD_MACHO_ICF_H
#define LLD_MACHO_ICF_H
+#include "InputFiles.h"
#include "lld/Common/LLVM.h"
#include <vector>
namespace lld {
namespace macho {
+class Symbol;
+void markAddrSigSymbols();
+void markSymAsAddrSig(Symbol *s);
void foldIdenticalSections();
} // namespace macho
diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp
index de3c3ae39bb0..f0cd4269d282 100644
--- a/lld/MachO/InputFiles.cpp
+++ b/lld/MachO/InputFiles.cpp
@@ -354,6 +354,9 @@ void ObjFile::parseSections(ArrayRef<SectionHeader> sectionHeaders) {
// spurious duplicate symbol errors, we do not parse these sections.
// TODO: Evaluate whether the bitcode metadata is needed.
} else {
+ if (name == section_names::addrSig)
+ addrSigSection = sections.back();
+
auto *isec = make<ConcatInputSection>(section, data, align);
if (isDebugSection(isec->getFlags()) &&
isec->getSegName() == segment_names::dwarf) {
diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h
index ca9943605a99..b33e510a6901 100644
--- a/lld/MachO/InputFiles.h
+++ b/lld/MachO/InputFiles.h
@@ -149,6 +149,7 @@ public:
const uint32_t modTime;
std::vector<ConcatInputSection *> debugSections;
std::vector<CallGraphEntry> callGraph;
+ Section *addrSigSection = nullptr;
private:
template <class LP> void parseLazy();
diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h
index 68d8ed97b11f..c4366c2ed34a 100644
--- a/lld/MachO/InputSection.h
+++ b/lld/MachO/InputSection.h
@@ -71,6 +71,8 @@ protected:
public:
// is address assigned?
bool isFinal = false;
+ // keep the address of the symbol(s) in this section unique in the final binary ?
+ bool keepUnique = false;
uint32_t align = 1;
OutputSection *parent = nullptr;
@@ -328,6 +330,7 @@ constexpr const char threadVars[] = "__thread_vars";
constexpr const char unwindInfo[] = "__unwind_info";
constexpr const char weakBinding[] = "__weak_binding";
constexpr const char zeroFill[] = "__zerofill";
+constexpr const char addrSig[] = "__llvm_addrsig";
} // namespace section_names
diff --git a/lld/test/MachO/icf-options.s b/lld/test/MachO/icf-options.s
index 3a6eddb80361..51383602214b 100644
--- a/lld/test/MachO/icf-options.s
+++ b/lld/test/MachO/icf-options.s
@@ -8,8 +8,8 @@
# RUN: | FileCheck %s --check-prefix=DIAG-EMPTY --allow-empty
# RUN: %lld -lSystem -no_deduplicate -o %t/no_dedup %t/main.o 2>&1 \
# RUN: | FileCheck %s --check-prefix=DIAG-EMPTY --allow-empty
-# RUN: not %lld -lSystem --icf=safe -o %t/safe %t/main.o 2>&1 \
-# RUN: | FileCheck %s --check-prefix=DIAG-SAFE
+# RUN: %lld -lSystem --icf=safe -o %t/safe %t/main.o 2>&1 \
+# RUN: | FileCheck %s --check-prefix=DIAG-EMPTY --allow-empty
# RUN: not %lld -lSystem --icf=junk -o %t/junk %t/main.o 2>&1 \
# RUN: | FileCheck %s --check-prefix=DIAG-JUNK
# RUN: %lld -lSystem --icf=all -no_deduplicate -o %t/none2 %t/main.o 2>&1 \
@@ -18,7 +18,6 @@
# RUN: | FileCheck %s --check-prefix=DIAG-EMPTY --allow-empty
# DIAG-EMPTY-NOT: {{.}}
-# DIAG-SAFE: `--icf=safe' is not yet implemented, reverting to `none'
# DIAG-JUNK: unknown --icf=OPTION `junk', defaulting to `none'
# RUN: llvm-objdump -d --syms %t/all | FileCheck %s --check-prefix=FOLD
diff --git a/lld/test/MachO/icf-safe.s b/lld/test/MachO/icf-safe.s
new file mode 100644
index 000000000000..014156a5543a
--- /dev/null
+++ b/lld/test/MachO/icf-safe.s
@@ -0,0 +1,56 @@
+; RUN: rm -rf %t; mkdir %t
+; RUN: llc -filetype=obj %s -O3 -o %t/icf-obj.o -enable-machine-outliner=never -mtriple arm64-apple-ios -addrsig
+; RUN: %lld -arch arm64 -lSystem --icf=safe -dylib -o %t/icf-safe.dylib %t/icf-obj.o
+; RUN: %lld -arch arm64 -lSystem --icf=all -dylib -o %t/icf-all.dylib %t/icf-obj.o
+; RUN: llvm-objdump %t/icf-safe.dylib -d --macho | FileCheck %s --check-prefix=ICFSAFE
+; RUN: llvm-objdump %t/icf-all.dylib -d --macho | FileCheck %s --check-prefix=ICFALL
+
+; ICFSAFE-LABEL: _callAllFunctions
+; ICFSAFE: bl _func02
+; ICFSAFE-NEXT: bl _func02
+; ICFSAFE-NEXT: bl _func03_takeaddr
+
+; ICFALL-LABEL: _callAllFunctions
+; ICFALL: bl _func03_takeaddr
+; ICFALL-NEXT: bl _func03_takeaddr
+; ICFALL-NEXT: bl _func03_takeaddr
+
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+target triple = "arm64-apple-ios7.0.0"
+
+@result = global i32 0, align 4
+
+define void @func01() local_unnamed_addr noinline {
+entry:
+ %0 = load volatile i32, ptr @result, align 4
+ %add = add nsw i32 %0, 1
+ store volatile i32 %add, ptr @result, align 4
+ ret void
+}
+
+define void @func02() local_unnamed_addr noinline {
+entry:
+ %0 = load volatile i32, ptr @result, align 4
+ %add = add nsw i32 %0, 1
+ store volatile i32 %add, ptr @result, align 4
+ ret void
+}
+
+define void @func03_takeaddr() noinline {
+entry:
+ %0 = load volatile i32, ptr @result, align 4
+ %add = add nsw i32 %0, 1
+ store volatile i32 %add, ptr @result, align 4
+ ret void
+}
+
+define void @callAllFunctions() local_unnamed_addr {
+entry:
+ tail call void @func01()
+ tail call void @func02()
+ tail call void @func03_takeaddr()
+ %0 = load volatile i32, ptr @result, align 4
+ %add = add nsw i32 %0, ptrtoint (ptr @func03_takeaddr to i32)
+ store volatile i32 %add, ptr @result, align 4
+ ret void
+}
--
2.37.0.rc0.161.g10f37bed90-goog