Skip to content

Commit 05634f7

Browse files
committed
[BOLT] Move from RuntimeDyld to JITLink
RuntimeDyld has been deprecated in favor of JITLink. [1] This patch replaces all uses of RuntimeDyld in BOLT with JITLink. Care has been taken to minimize the impact on the code structure in order to ease the inspection of this (rather large) changeset. Since BOLT relied on the RuntimeDyld API in multiple places, this wasn't always possible though and I'll explain the changes in code structure first. Design note: BOLT uses a JIT linker to perform what essentially is static linking. No linked code is ever executed; the result of linking is simply written back to an executable file. For this reason, I restricted myself to the use of the core JITLink library and avoided ORC as much as possible. RuntimeDyld contains methods for loading objects (loadObject) and symbol lookup (getSymbol). Since JITLink doesn't provide a class with a similar interface, the BOLTLinker abstract class was added to implement it. It was added to Core since both the Rewrite and RuntimeLibs libraries make use of it. Wherever a RuntimeDyld object was used before, it was replaced with a BOLTLinker object. There is one major difference between the RuntimeDyld and BOLTLinker interfaces: in JITLink, section allocation and the application of fixups (relocation) happens in a single call (jitlink::link). That is, there is no separate method like finalizeWithMemoryManagerLocking in RuntimeDyld. BOLT used to remap sections between allocating (loadObject) and linking them (finalizeWithMemoryManagerLocking). This doesn't work anymore with JITLink. Instead, BOLTLinker::loadObject accepts a callback that is called before fixups are applied which is used to remap sections. The actual implementation of the BOLTLinker interface lives in the JITLinkLinker class in the Rewrite library. It's the only part of the BOLT code that should directly interact with the JITLink API. For loading object, JITLinkLinker first creates a LinkGraph (jitlink::createLinkGraphFromObject) and then links it (jitlink::link). For the latter, it uses a custom JITLinkContext with the following properties: - Use BOLT's ExecutableFileMemoryManager. This one was updated to implement the JITLinkMemoryManager interface. Since BOLT never executes code, its finalization step is a no-op. - Pass config: don't use the default target passes since they modify DWARF sections in a way that seems incompatible with BOLT. Also run a custom pre-prune pass that makes sure sections without symbols are not pruned by JITLink. - Implement symbol lookup. This used to be implemented by BOLTSymbolResolver. - Call the section mapper callback before the final linking step. - Copy symbol values when the LinkGraph is resolved. Symbols are stored inside JITLinkLinker to ensure that later objects (i.e., instrumentation libraries) can find them. This functionality used to be provided by RuntimeDyld but I did not find a way to use JITLink directly for this. Some more minor points of interest: - BinarySection::SectionID: JITLink doesn't have something equivalent to RuntimeDyld's Section IDs. Instead, sections can only be referred to by name. Hence, SectionID was updated to a string. - There seem to be no tests for Mach-O. I've tested a small hello-world style binary but not more than that. - On Mach-O, JITLink "normalizes" section names to include the segment name. I had to parse the section name back from this manually which feels slightly hacky. [1] https://reviews.llvm.org/D145686#4222642 Reviewed By: rafauler Differential Revision: https://reviews.llvm.org/D147544
1 parent 4d339ec commit 05634f7

22 files changed

+570
-332
lines changed

bolt/include/bolt/Core/BinaryContext.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ using namespace object;
5959
namespace bolt {
6060

6161
class BinaryFunction;
62-
class ExecutableFileMemoryManager;
6362

6463
/// Information on loadable part of the file.
6564
struct SegmentInfo {
@@ -313,10 +312,6 @@ class BinaryContext {
313312
FilterIterator<binary_data_const_iterator>;
314313
using FilteredBinaryDataIterator = FilterIterator<binary_data_iterator>;
315314

316-
/// Memory manager for sections and segments. Used to communicate with ORC
317-
/// among other things.
318-
std::shared_ptr<ExecutableFileMemoryManager> EFMM;
319-
320315
StringRef getFilename() const { return Filename; }
321316
void setFilename(StringRef Name) { Filename = std::string(Name); }
322317

bolt/include/bolt/Core/BinarySection.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class BinarySection {
9090
uint64_t OutputFileOffset{0}; // File offset in the rewritten binary file.
9191
StringRef OutputContents; // Rewritten section contents.
9292
const uint64_t SectionNumber; // Order in which the section was created.
93-
unsigned SectionID{-1u}; // Unique ID used for address mapping.
93+
std::string SectionID; // Unique ID used for address mapping.
9494
// Set by ExecutableFileMemoryManager.
9595
uint32_t Index{0}; // Section index in the output file.
9696
mutable bool IsReordered{false}; // Have the contents been reordered?
@@ -430,18 +430,18 @@ class BinarySection {
430430
}
431431
uint64_t getOutputAddress() const { return OutputAddress; }
432432
uint64_t getOutputFileOffset() const { return OutputFileOffset; }
433-
unsigned getSectionID() const {
433+
StringRef getSectionID() const {
434434
assert(hasValidSectionID() && "trying to use uninitialized section id");
435435
return SectionID;
436436
}
437-
bool hasValidSectionID() const { return SectionID != -1u; }
437+
bool hasValidSectionID() const { return !SectionID.empty(); }
438438
bool hasValidIndex() { return Index != 0; }
439439
uint32_t getIndex() const { return Index; }
440440

441441
// mutation
442442
void setOutputAddress(uint64_t Address) { OutputAddress = Address; }
443443
void setOutputFileOffset(uint64_t Offset) { OutputFileOffset = Offset; }
444-
void setSectionID(unsigned ID) {
444+
void setSectionID(StringRef ID) {
445445
assert(!hasValidSectionID() && "trying to set section id twice");
446446
SectionID = ID;
447447
}

bolt/include/bolt/Core/Linker.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===- bolt/Core/Linker.h - BOLTLinker interface ----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file contains the interface BOLT uses for linking.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef BOLT_CORE_LINKER_H
14+
#define BOLT_CORE_LINKER_H
15+
16+
#include "llvm/ADT/StringRef.h"
17+
#include "llvm/Support/MemoryBufferRef.h"
18+
19+
#include <cstdint>
20+
#include <functional>
21+
#include <optional>
22+
23+
namespace llvm {
24+
namespace bolt {
25+
26+
class BinarySection;
27+
28+
class BOLTLinker {
29+
public:
30+
using SectionMapper =
31+
std::function<void(const BinarySection &Section, uint64_t Address)>;
32+
using SectionsMapper = std::function<void(SectionMapper)>;
33+
34+
virtual ~BOLTLinker() = default;
35+
36+
/// Load and link \p Obj. \p MapSections will be called before the object is
37+
/// linked to allow section addresses to be remapped. When called, the address
38+
/// of a section can be changed by calling the passed SectionMapper.
39+
virtual void loadObject(MemoryBufferRef Obj, SectionsMapper MapSections) = 0;
40+
41+
/// Return the address of a symbol or std::nullopt if it cannot be found.
42+
virtual std::optional<uint64_t> lookupSymbol(StringRef Name) const = 0;
43+
};
44+
45+
} // namespace bolt
46+
} // namespace llvm
47+
48+
#endif // BOLT_CORE_LINKER_H

bolt/include/bolt/Rewrite/ExecutableFileMemoryManager.h

Lines changed: 11 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#define BOLT_REWRITE_EXECUTABLE_FILE_MEMORY_MANAGER_H
1111

1212
#include "llvm/ADT/StringRef.h"
13-
#include "llvm/ExecutionEngine/RuntimeDyld.h"
13+
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
1414
#include <cstdint>
1515
#include <string>
1616

@@ -20,20 +20,12 @@ namespace bolt {
2020
class BinaryContext;
2121

2222
/// Class responsible for allocating and managing code and data sections.
23-
class ExecutableFileMemoryManager : public RuntimeDyld::MemoryManager {
23+
class ExecutableFileMemoryManager : public jitlink::JITLinkMemoryManager {
2424
private:
25-
uint8_t *allocateSection(uintptr_t Size, unsigned Alignment,
26-
unsigned SectionID, StringRef SectionName,
27-
bool IsCode, bool IsReadOnly);
28-
BinaryContext &BC;
29-
bool AllowStubs;
25+
void updateSection(const jitlink::Section &Section, uint8_t *Contents,
26+
size_t Size, size_t Alignment);
3027

31-
struct AllocInfo {
32-
uint8_t *Address;
33-
size_t Size;
34-
size_t Alignment;
35-
};
36-
SmallVector<AllocInfo, 8> AllocatedSections;
28+
BinaryContext &BC;
3729

3830
// All new sections will be identified by the following prefix.
3931
std::string NewSecPrefix;
@@ -50,48 +42,14 @@ class ExecutableFileMemoryManager : public RuntimeDyld::MemoryManager {
5042
// user-supplied objects into the main input executable.
5143
uint32_t ObjectsLoaded{0};
5244

53-
ExecutableFileMemoryManager(BinaryContext &BC, bool AllowStubs)
54-
: BC(BC), AllowStubs(AllowStubs) {}
55-
56-
~ExecutableFileMemoryManager();
57-
58-
uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
59-
unsigned SectionID,
60-
StringRef SectionName) override {
61-
return allocateSection(Size, Alignment, SectionID, SectionName,
62-
/*IsCode=*/true, true);
63-
}
64-
65-
uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
66-
unsigned SectionID, StringRef SectionName,
67-
bool IsReadOnly) override {
68-
return allocateSection(Size, Alignment, SectionID, SectionName,
69-
/*IsCode=*/false, IsReadOnly);
70-
}
71-
72-
// Ignore TLS sections by treating them as a regular data section
73-
TLSSection allocateTLSSection(uintptr_t Size, unsigned Alignment,
74-
unsigned SectionID,
75-
StringRef SectionName) override {
76-
TLSSection Res;
77-
Res.Offset = 0;
78-
Res.InitializationImage = allocateDataSection(
79-
Size, Alignment, SectionID, SectionName, /*IsReadOnly=*/false);
80-
return Res;
81-
}
82-
83-
bool allowStubAllocation() const override { return AllowStubs; }
45+
ExecutableFileMemoryManager(BinaryContext &BC) : BC(BC) {}
8446

85-
/// Count processed objects and skip memory finalization.
86-
bool finalizeMemory(std::string *ErrMsg) override {
87-
++ObjectsLoaded;
88-
return false;
89-
}
47+
void allocate(const jitlink::JITLinkDylib *JD, jitlink::LinkGraph &G,
48+
OnAllocatedFunction OnAllocated) override;
9049

91-
/// Ignore EH frames.
92-
void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr,
93-
size_t Size) override {}
94-
void deregisterEHFrames() override {}
50+
void deallocate(std::vector<FinalizedAlloc> Allocs,
51+
OnDeallocatedFunction OnDeallocated) override;
52+
using JITLinkMemoryManager::deallocate;
9553

9654
/// Section name management.
9755
void setNewSecPrefix(StringRef Prefix) { NewSecPrefix = Prefix; }
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//===- bolt/Rewrite/JITLinkLinker.h - Linker using JITLink ------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// BOLTLinker using JITLink.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef BOLT_REWRITE_JITLINK_LINKER_H
14+
#define BOLT_REWRITE_JITLINK_LINKER_H
15+
16+
#include "bolt/Core/Linker.h"
17+
#include "bolt/Rewrite/ExecutableFileMemoryManager.h"
18+
#include "llvm/ExecutionEngine/JITLink/JITLinkDylib.h"
19+
20+
#include <map>
21+
#include <memory>
22+
#include <vector>
23+
24+
namespace llvm {
25+
namespace bolt {
26+
27+
class BinaryContext;
28+
29+
class JITLinkLinker : public BOLTLinker {
30+
private:
31+
struct Context;
32+
friend struct Context;
33+
34+
BinaryContext &BC;
35+
std::unique_ptr<ExecutableFileMemoryManager> MM;
36+
jitlink::JITLinkDylib Dylib{"main"};
37+
std::vector<ExecutableFileMemoryManager::FinalizedAlloc> Allocs;
38+
std::map<std::string, uint64_t> Symtab;
39+
40+
public:
41+
JITLinkLinker(BinaryContext &BC,
42+
std::unique_ptr<ExecutableFileMemoryManager> MM);
43+
~JITLinkLinker();
44+
45+
void loadObject(MemoryBufferRef Obj, SectionsMapper MapSections) override;
46+
std::optional<uint64_t> lookupSymbol(StringRef Name) const override;
47+
48+
static SmallVector<jitlink::Block *, 2>
49+
orderedBlocks(const jitlink::Section &Section);
50+
static size_t sectionSize(const jitlink::Section &Section);
51+
};
52+
53+
} // namespace bolt
54+
} // namespace llvm
55+
56+
#endif // BOLT_REWRITE_JITLINK_LINKER_H

bolt/include/bolt/Rewrite/MachORewriteInstance.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313
#ifndef BOLT_REWRITE_MACHO_REWRITE_INSTANCE_H
1414
#define BOLT_REWRITE_MACHO_REWRITE_INSTANCE_H
1515

16+
#include "bolt/Core/Linker.h"
1617
#include "bolt/Utils/NameResolver.h"
1718
#include "llvm/Support/Error.h"
1819
#include <memory>
1920

2021
namespace llvm {
2122
class ToolOutputFile;
22-
class RuntimeDyld;
2323
class raw_pwrite_stream;
2424
namespace object {
2525
class MachOObjectFile;
@@ -37,7 +37,7 @@ class MachORewriteInstance {
3737

3838
NameResolver NR;
3939

40-
std::unique_ptr<RuntimeDyld> RTDyld;
40+
std::unique_ptr<BOLTLinker> Linker;
4141

4242
std::unique_ptr<ToolOutputFile> Out;
4343

@@ -49,8 +49,9 @@ class MachORewriteInstance {
4949
static StringRef getNewSecPrefix() { return ".bolt.new"; }
5050
static StringRef getOrgSecPrefix() { return ".bolt.org"; }
5151

52-
void mapInstrumentationSection(StringRef SectionName);
53-
void mapCodeSections();
52+
void mapInstrumentationSection(StringRef SectionName,
53+
BOLTLinker::SectionMapper MapSection);
54+
void mapCodeSections(BOLTLinker::SectionMapper MapSection);
5455

5556
void adjustCommandLineOptions();
5657
void readSpecialSections();

bolt/include/bolt/Rewrite/RewriteInstance.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define BOLT_REWRITE_REWRITE_INSTANCE_H
1515

1616
#include "bolt/Core/BinaryContext.h"
17+
#include "bolt/Core/Linker.h"
1718
#include "bolt/Utils/NameResolver.h"
1819
#include "llvm/ADT/ArrayRef.h"
1920
#include "llvm/MC/StringTableBuilder.h"
@@ -206,13 +207,13 @@ class RewriteInstance {
206207
std::vector<BinarySection *> getCodeSections();
207208

208209
/// Map all sections to their final addresses.
209-
void mapFileSections(RuntimeDyld &RTDyld);
210+
void mapFileSections(BOLTLinker::SectionMapper MapSection);
210211

211212
/// Map code sections generated by BOLT.
212-
void mapCodeSections(RuntimeDyld &RTDyld);
213+
void mapCodeSections(BOLTLinker::SectionMapper MapSection);
213214

214215
/// Map the rest of allocatable sections.
215-
void mapAllocatableSections(RuntimeDyld &RTDyld);
216+
void mapAllocatableSections(BOLTLinker::SectionMapper MapSection);
216217

217218
/// Update output object's values based on the final \p Layout.
218219
void updateOutputValues(const MCAsmLayout &Layout);
@@ -459,7 +460,7 @@ class RewriteInstance {
459460
std::unique_ptr<CFIReaderWriter> CFIRdWrt;
460461

461462
// Run ExecutionEngine linker with custom memory manager and symbol resolver.
462-
std::unique_ptr<RuntimeDyld> RTDyld;
463+
std::unique_ptr<BOLTLinker> Linker;
463464

464465
/// Output file where we mix original code from the input binary and
465466
/// optimized code for selected functions.

bolt/include/bolt/RuntimeLibs/HugifyRuntimeLibrary.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ class HugifyRuntimeLibrary : public RuntimeLibrary {
2828

2929
void emitBinary(BinaryContext &BC, MCStreamer &Streamer) final {}
3030

31-
void link(BinaryContext &BC, StringRef ToolPath, RuntimeDyld &RTDyld,
32-
std::function<void(RuntimeDyld &)> OnLoad) final;
31+
void link(BinaryContext &BC, StringRef ToolPath, BOLTLinker &Linker,
32+
BOLTLinker::SectionsMapper MapSections) override;
3333
};
3434

3535
} // namespace bolt

bolt/include/bolt/RuntimeLibs/InstrumentationRuntimeLibrary.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ class InstrumentationRuntimeLibrary : public RuntimeLibrary {
3535

3636
void emitBinary(BinaryContext &BC, MCStreamer &Streamer) final;
3737

38-
void link(BinaryContext &BC, StringRef ToolPath, RuntimeDyld &RTDyld,
39-
std::function<void(RuntimeDyld &)> OnLoad) final;
38+
void link(BinaryContext &BC, StringRef ToolPath, BOLTLinker &Linker,
39+
BOLTLinker::SectionsMapper MapSections) override;
4040

4141
private:
4242
std::string buildTables(BinaryContext &BC);

bolt/include/bolt/RuntimeLibs/RuntimeLibrary.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
#ifndef BOLT_RUNTIMELIBS_RUNTIME_LIBRARY_H
1616
#define BOLT_RUNTIMELIBS_RUNTIME_LIBRARY_H
1717

18+
#include "bolt/Core/Linker.h"
1819
#include "llvm/ADT/StringRef.h"
1920
#include <functional>
2021
#include <vector>
2122

2223
namespace llvm {
2324

2425
class MCStreamer;
25-
class RuntimeDyld;
2626

2727
namespace bolt {
2828

@@ -51,8 +51,8 @@ class RuntimeLibrary {
5151
virtual void emitBinary(BinaryContext &BC, MCStreamer &Streamer) = 0;
5252

5353
/// Link with the library code.
54-
virtual void link(BinaryContext &BC, StringRef ToolPath, RuntimeDyld &RTDyld,
55-
std::function<void(RuntimeDyld &)> OnLoad) = 0;
54+
virtual void link(BinaryContext &BC, StringRef ToolPath, BOLTLinker &Linker,
55+
BOLTLinker::SectionsMapper MapSections) = 0;
5656

5757
protected:
5858
/// The fini and init address set by the runtime library.
@@ -63,7 +63,8 @@ class RuntimeLibrary {
6363
static std::string getLibPath(StringRef ToolPath, StringRef LibFileName);
6464

6565
/// Load a static runtime library specified by \p LibPath.
66-
static void loadLibrary(StringRef LibPath, RuntimeDyld &RTDyld);
66+
static void loadLibrary(StringRef LibPath, BOLTLinker &Linker,
67+
BOLTLinker::SectionsMapper MapSections);
6768
};
6869

6970
} // namespace bolt

bolt/lib/Core/BinaryContext.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1975,7 +1975,9 @@ void BinaryContext::deregisterUnusedSections() {
19751975
ErrorOr<BinarySection &> AbsSection = getUniqueSectionByName("<absolute>");
19761976
for (auto SI = Sections.begin(); SI != Sections.end();) {
19771977
BinarySection *Section = *SI;
1978-
if (Section->hasSectionRef() || Section->getOutputSize() ||
1978+
// We check getOutputData() instead of getOutputSize() because sometimes
1979+
// zero-sized .text.cold sections are allocated.
1980+
if (Section->hasSectionRef() || Section->getOutputData() ||
19791981
(AbsSection && Section == &AbsSection.get())) {
19801982
++SI;
19811983
continue;

0 commit comments

Comments
 (0)