@@ -45,6 +45,7 @@ class ICF {
45
45
const ConcatInputSection *ib);
46
46
bool equalsVariable (const ConcatInputSection *ia,
47
47
const ConcatInputSection *ib);
48
+ void applySafeThunksToRange (size_t begin, size_t end);
48
49
49
50
// ICF needs a copy of the inputs vector because its equivalence-class
50
51
// segregation algorithm destroys the proper sequence.
@@ -251,6 +252,63 @@ void ICF::forEachClassRange(size_t begin, size_t end,
251
252
}
252
253
}
253
254
255
+ // Given a range of identical icfInputs's, replace address significant functions
256
+ // with a thunk that is just a direct branch to the first function in the
257
+ // series. This way we end up we keep only one main body of the function but we
258
+ // still retain address uniqueness of rellevant functions by having them be a
259
+ // direct branch thunk rather than contain a full copy of the actual function
260
+ // body.
261
+ void ICF::applySafeThunksToRange (size_t begin, size_t end) {
262
+ // If we need to create a unique ICF thunk, use the first section as the
263
+ // section that all thunks will branch to.
264
+ ConcatInputSection *masterIsec = icfInputs[begin];
265
+ uint32_t thunkSize = target->getICFSafeThunkSize ();
266
+ static std::mutex thunkInsertionMutex;
267
+
268
+ uint32_t keepUniqueCount = masterIsec->keepUnique ? 1 : 0 ;
269
+ for (size_t i = begin + 1 ; i < end; ++i) {
270
+ ConcatInputSection *isec = icfInputs[i];
271
+ if (isec->keepUnique )
272
+ ++keepUniqueCount;
273
+
274
+ // We create thunks for the 2nd, 3rd, ... keepUnique sections. The first
275
+ // keepUnique section we leave as is - as it will not end up sharing an
276
+ // address with any other keepUnique section.
277
+ if (keepUniqueCount >= 2 && isec->keepUnique ) {
278
+ // If the target to be folded is smaller than the thunk size, then just
279
+ // leave it as-is - creating the thunk would be a net loss.
280
+ if (isec->data .size () <= thunkSize)
281
+ return ;
282
+
283
+ // applySafeThunksToRange is called from multiple threads, but
284
+ // `makeSyntheticInputSection` and `addInputSection` are not thread safe.
285
+ // So we need to guard them with a mutex.
286
+ ConcatInputSection *thunk;
287
+ {
288
+ std::lock_guard<std::mutex> lock (thunkInsertionMutex);
289
+ thunk = makeSyntheticInputSection (isec->getSegName (), isec->getName ());
290
+ addInputSection (thunk);
291
+ }
292
+
293
+ target->initICFSafeThunkBody (thunk, masterIsec);
294
+ thunk->foldIdentical (isec);
295
+
296
+ // Since we're folding the target function into a thunk, we need to adjust
297
+ // the symbols that now got relocated from the target function to the
298
+ // thunk.
299
+ // Since the thunk is only one branch, we move all symbols to offset 0 and
300
+ // make sure that the size of all non-zero-size symbols is equal to the
301
+ // size of the branch.
302
+ for (auto *sym : isec->symbols ) {
303
+ if (sym->value != 0 )
304
+ sym->value = 0 ;
305
+ if (sym->size != 0 )
306
+ sym->size = thunkSize;
307
+ }
308
+ }
309
+ }
310
+ }
311
+
254
312
// Split icfInputs into shards, then parallelize invocation of FUNC on subranges
255
313
// with matching equivalence class
256
314
void ICF::forEachClass (llvm::function_ref<void (size_t , size_t )> func) {
@@ -335,9 +393,20 @@ void ICF::run() {
335
393
forEachClass ([&](size_t begin, size_t end) {
336
394
if (end - begin < 2 )
337
395
return ;
396
+ bool useSafeThunks = config->icfLevel == ICFLevel::safe_thunks;
397
+
398
+ // For ICF level safe_thunks, replace keepUnique function bodies with
399
+ // thunks. For all other ICF levles, directly merge the functions.
400
+ if (useSafeThunks)
401
+ applySafeThunksToRange (begin, end);
402
+
338
403
ConcatInputSection *beginIsec = icfInputs[begin];
339
- for (size_t i = begin + 1 ; i < end; ++i)
404
+ for (size_t i = begin + 1 ; i < end; ++i) {
405
+ // When using safe_thunks, keepUnique inputs are already handeled above
406
+ if (useSafeThunks && icfInputs[i]->keepUnique )
407
+ continue ;
340
408
beginIsec->foldIdentical (icfInputs[i]);
409
+ }
341
410
});
342
411
}
343
412
@@ -421,11 +490,22 @@ void macho::foldIdenticalSections(bool onlyCfStrings) {
421
490
// can still fold it.
422
491
bool hasFoldableFlags = (isSelRefsSection (isec) ||
423
492
sectionType (isec->getFlags ()) == MachO::S_REGULAR);
493
+
494
+ bool isCodeSec = isCodeSection (isec);
495
+
496
+ // When keepUnique is true, the section is not foldable. Unless we are at
497
+ // icf level safe_thunks, in which case we still want to fold code sections.
498
+ // When using safe_thunks we'll apply the safe_thunks logic at merge time
499
+ // based on the 'keepUnique' flag.
500
+ bool noUniqueRequirement =
501
+ !isec->keepUnique ||
502
+ ((config->icfLevel == ICFLevel::safe_thunks) && isCodeSec);
503
+
424
504
// FIXME: consider non-code __text sections as foldable?
425
505
bool isFoldable = (!onlyCfStrings || isCfStringSection (isec)) &&
426
- (isCodeSection (isec) || isFoldableWithAddendsRemoved ||
506
+ (isCodeSec || isFoldableWithAddendsRemoved ||
427
507
isGccExceptTabSection (isec)) &&
428
- !isec-> keepUnique && !isec->hasAltEntry &&
508
+ noUniqueRequirement && !isec->hasAltEntry &&
429
509
!isec->shouldOmitFromOutput () && hasFoldableFlags;
430
510
if (isFoldable) {
431
511
foldable.push_back (isec);
0 commit comments