Skip to content

Commit bcaf57c

Browse files
committed
[lld-macho] Parse relocations quickly by assuming sorted order
clang and gcc both seem to emit relocations in reverse order of address. That means we can match relocations to their containing subsections in `O(relocs + subsections)` rather than the `O(relocs * log(subsections))` that our previous binary search implementation required. Unfortunately, `ld -r` can still emit unsorted relocations, so we have a fallback code path for that (less common) case. Numbers for linking chromium_framework on my 3.2 GHz 16-Core Intel Xeon W: N Min Max Median Avg Stddev x 20 4.04 4.11 4.075 4.0775 0.018027756 + 20 3.95 4.02 3.98 3.985 0.020900768 Difference at 95.0% confidence -0.0925 +/- 0.0124919 -2.26855% +/- 0.306361% (Student's t, pooled s = 0.0195172) Reviewed By: #lld-macho, thakis Differential Revision: https://reviews.llvm.org/D105410
1 parent 0dad3f6 commit bcaf57c

File tree

3 files changed

+127
-1
lines changed

3 files changed

+127
-1
lines changed

lld/MachO/ConcatOutputSection.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ void ConcatOutputSection::finalize() {
241241
}
242242
// Process relocs by ascending address, i.e., ascending offset within isec
243243
std::vector<Reloc> &relocs = isec->relocs;
244+
// FIXME: This property does not hold for object files produced by ld64's
245+
// `-r` mode.
244246
assert(is_sorted(relocs,
245247
[](Reloc &a, Reloc &b) { return a.offset > b.offset; }));
246248
for (Reloc &r : reverse(relocs)) {

lld/MachO/InputFiles.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ void ObjFile::parseRelocations(ArrayRef<Section> sectionHeaders,
366366
ArrayRef<relocation_info> relInfos(
367367
reinterpret_cast<const relocation_info *>(buf + sec.reloff), sec.nreloc);
368368

369+
auto subsecIt = subsecMap.rbegin();
369370
for (size_t i = 0; i < relInfos.size(); i++) {
370371
// Paired relocations serve as Mach-O's method for attaching a
371372
// supplemental datum to a primary relocation record. ELF does not
@@ -440,7 +441,24 @@ void ObjFile::parseRelocations(ArrayRef<Section> sectionHeaders,
440441
r.addend = referentOffset;
441442
}
442443

443-
InputSection *subsec = findContainingSubsection(subsecMap, &r.offset);
444+
// Find the subsection that this relocation belongs to.
445+
// Though not required by the Mach-O format, clang and gcc seem to emit
446+
// relocations in order, so let's take advantage of it. However, ld64 emits
447+
// unsorted relocations (in `-r` mode), so we have a fallback for that
448+
// uncommon case.
449+
InputSection *subsec;
450+
while (subsecIt != subsecMap.rend() && subsecIt->offset > r.offset)
451+
++subsecIt;
452+
if (subsecIt == subsecMap.rend() ||
453+
subsecIt->offset + subsecIt->isec->getSize() <= r.offset) {
454+
subsec = findContainingSubsection(subsecMap, &r.offset);
455+
// Now that we know the relocs are unsorted, avoid trying the 'fast path'
456+
// for the other relocations.
457+
subsecIt = subsecMap.rend();
458+
} else {
459+
subsec = subsecIt->isec;
460+
r.offset -= subsecIt->offset;
461+
}
444462
subsec->relocs.push_back(r);
445463

446464
if (isSubtrahend) {
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
## This tests that we can handle relocations that are not sorted by address.
2+
## llvm-mc isn't able to emit such a file, hence the use of yaml2obj. ld64
3+
## may emit files with unsorted relocations in `-r` mode, so we need to support
4+
## this.
5+
6+
# RUN: yaml2obj %s -o %t.o
7+
# RUN: %lld -dylib -o %t %t.o
8+
# RUN: llvm-objdump --macho -d %t | FileCheck %s
9+
10+
# CHECK: _foo:
11+
# CHECK-NEXT: movq _bar(%rip), %rax
12+
# CHECK-NEXT: _bar:
13+
# CHECK-NEXT: movq _baz(%rip), %rax
14+
# CHECK-NEXT: _baz:
15+
# CHECK-NEXT: movq _foo(%rip), %rax
16+
17+
--- !mach-o
18+
FileHeader:
19+
magic: 0xFEEDFACF
20+
cputype: 0x1000007
21+
cpusubtype: 0x3
22+
filetype: 0x1
23+
ncmds: 2
24+
sizeofcmds: 280
25+
flags: 0x2000
26+
reserved: 0x0
27+
LoadCommands:
28+
- cmd: LC_SEGMENT_64
29+
cmdsize: 152
30+
segname: ''
31+
vmaddr: 0
32+
vmsize: 21
33+
fileoff: 312
34+
filesize: 21
35+
maxprot: 7
36+
initprot: 7
37+
nsects: 1
38+
flags: 0
39+
Sections:
40+
- sectname: __text
41+
segname: __TEXT
42+
addr: 0x0
43+
size: 21
44+
offset: 0x138
45+
align: 0
46+
reloff: 0x150
47+
nreloc: 3
48+
flags: 0x80000400
49+
reserved1: 0x0
50+
reserved2: 0x0
51+
reserved3: 0x0
52+
content: 488B0500000000488B0500000000488B0500000000
53+
relocations:
54+
- address: 0x3
55+
symbolnum: 1
56+
pcrel: true
57+
length: 2
58+
extern: true
59+
type: 1
60+
scattered: false
61+
value: 0
62+
- address: 0x11
63+
symbolnum: 0
64+
pcrel: true
65+
length: 2
66+
extern: true
67+
type: 1
68+
scattered: false
69+
value: 0
70+
- address: 0xA
71+
symbolnum: 2
72+
pcrel: true
73+
length: 2
74+
extern: true
75+
type: 1
76+
scattered: false
77+
value: 0
78+
- cmd: LC_SYMTAB
79+
cmdsize: 24
80+
symoff: 360
81+
nsyms: 3
82+
stroff: 408
83+
strsize: 16
84+
LinkEditData:
85+
NameList:
86+
- n_strx: 11
87+
n_type: 0xE
88+
n_sect: 1
89+
n_desc: 0
90+
n_value: 0
91+
- n_strx: 6
92+
n_type: 0xE
93+
n_sect: 1
94+
n_desc: 0
95+
n_value: 7
96+
- n_strx: 1
97+
n_type: 0xE
98+
n_sect: 1
99+
n_desc: 0
100+
n_value: 14
101+
StringTable:
102+
- ''
103+
- _baz
104+
- _bar
105+
- _foo
106+
...

0 commit comments

Comments
 (0)