-
Notifications
You must be signed in to change notification settings - Fork 0
Support/jitlink coff seh #5
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
base: windows-easy-eh-plugin
Are you sure you want to change the base?
Changes from all commits
296a6d1
2019be5
8fcef75
5bb9763
f9f8a22
aa807e6
59c2bca
f226c46
8327fd8
905aef3
4198c16
1b574b0
13c2f43
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| //===- SEHFrameRegistrationPlugin.h - Register COFF EH info in-process -*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Register and deregister .pdata sections in-process using RtlAddFunctionTable | ||
| // and RtlDeleteFunctionTable. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_EXECUTIONENGINE_ORC_SEHFRAMEREGISTRATIONPLUGIN_H | ||
| #define LLVM_EXECUTIONENGINE_ORC_SEHFRAMEREGISTRATIONPLUGIN_H | ||
|
|
||
| #include "llvm/ExecutionEngine/Orc/LinkGraphLinkingLayer.h" | ||
|
|
||
| namespace llvm::orc { | ||
|
|
||
| /// Registers .pdata sections with the Windows unwinder via RtlAddFunctionTable. | ||
| /// | ||
| /// This plugin enables SEH-based stack unwinding for JIT'd code on Windows | ||
| /// by registering unwind metadata at finalization and deregistering it on | ||
| /// deallocation. | ||
| class LLVM_ABI SEHFrameRegistrationPlugin : public LinkGraphLinkingLayer::Plugin { | ||
| public: | ||
| /// Adds a pass to PassConfig that registers .pdata sections in LG with the | ||
| /// OS unwinder. | ||
| void modifyPassConfig(MaterializationResponsibility &MR, | ||
| jitlink::LinkGraph &LG, | ||
| jitlink::PassConfiguration &PassConfig) override; | ||
|
|
||
| Error notifyFailed(MaterializationResponsibility &MR) override { | ||
| return Error::success(); | ||
| } | ||
|
|
||
| Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override { | ||
| return Error::success(); | ||
| } | ||
|
|
||
| void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, | ||
| ResourceKey SrcKey) override {} | ||
|
|
||
| private: | ||
| /// Registers .pdata sections in G with the OS unwinder. | ||
| /// Returns an error if registration setup fails. | ||
| Error registerFrameInfo(jitlink::LinkGraph &G); | ||
| }; | ||
|
|
||
| } // namespace llvm::orc | ||
|
|
||
| #endif // LLVM_EXECUTIONENGINE_ORC_SEHFRAMEREGISTRATIONPLUGIN_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -247,6 +247,106 @@ class COFFLinkGraphLowering_x86_64 { | |
| GetImageBaseSymbol GetImageBase; | ||
| DenseMap<Section *, orc::ExecutorAddr> SectionStartCache; | ||
| }; | ||
|
|
||
| // A LinkGraph pass that resolves __ImageBase for COFF x86_64 graphs | ||
| class COFFImageBaseResolution_x86_64 { | ||
| public: | ||
| // Resolves __ImageBase to the lowest allocated section address in G | ||
| Error operator()(LinkGraph &G) { | ||
| GetImageBaseSymbol GetImageBase; | ||
|
|
||
| auto ImageBase = GetImageBase(G); | ||
| if (ImageBase) { | ||
| orc::ExecutorAddr Base(~uint64_t(0)); | ||
| for (auto &Sec : G.sections()) { | ||
| if (Sec.empty()) | ||
| continue; | ||
| SectionRange SR(Sec); | ||
| Base = std::min(Base, SR.getStart()); | ||
| } | ||
| assert(ImageBase && "__ImageBase symbol must be defined"); | ||
| ImageBase->getAddressable().setAddress(Base); | ||
| } | ||
| return Error::success(); | ||
| } | ||
| }; | ||
|
|
||
| // Creates executable stubs for Pointer32NB edges targeting external | ||
| // symbols whose image relative offset from __ImageBase would exceed | ||
| // 32 bits. | ||
| class COFFPointer32NBStubsManager : public llvm::jitlink::TableManager<COFFPointer32NBStubsManager> { | ||
| public: | ||
| // Returns section name for ADDR32NB stubs in the link graph. | ||
| static StringRef getSectionName() { return "$__STUBS_ADDR32NB"; } | ||
|
|
||
| // Constructs a stub manager using GOT for indirect jump targets. | ||
| COFFPointer32NBStubsManager(LinkGraph &, x86_64::GOTTableManager &GOT) : GOT(GOT) { | ||
| } | ||
|
|
||
| // Checks edge E on block B in graph G. If E is a Pointer32NB | ||
| // targeting an external, redirects it to a nearby stub. Returns | ||
| // true if handled. | ||
| bool visitEdge(LinkGraph &G, Block *B, Edge &E) { | ||
| if (E.getKind() == EdgeKind_coff_x86_64::Pointer32NB && !E.getTarget().isDefined()) { | ||
| DEBUG_WITH_TYPE("jitlink", { | ||
| dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " | ||
| << B->getFixupAddress(E) << " (" << B->getAddress() << " + " | ||
| << formatv("{0:x}", E.getOffset()) << ")\n"; | ||
| }); | ||
|
|
||
| E.setTarget(getEntryForTarget(G, E.getTarget())); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| // Creates an executable stub in G that jumps to Target via a GOT | ||
| // entry. Returns an anonymous symbol pointing to the stub. | ||
| Symbol &createEntry(LinkGraph &G, Symbol &Target) { | ||
| return x86_64::createAnonymousPointerJumpStub(G, getStubsSection(G), | ||
| GOT.getEntryForTarget(G, Target)); | ||
| } | ||
|
|
||
| public: | ||
| // Returns the executable stub section in G, creating it on first | ||
| // call. | ||
| Section &getStubsSection(LinkGraph &G) { | ||
| if (!StubsSection) | ||
| StubsSection = &G.createSection(getSectionName(), | ||
| orc::MemProt::Read | orc::MemProt::Exec); | ||
| return *StubsSection; | ||
| } | ||
|
|
||
| // Shared GOT for indirect jump targets. | ||
| x86_64::GOTTableManager &GOT; | ||
| // Lazily created stub section. | ||
| Section *StubsSection = nullptr; | ||
| }; | ||
|
|
||
| // Create stubs in G for external references: PLT stubs for calls (PCRel32) | ||
| // and ADDR32NB stubs for image-relative pointers (Pointer32NB). Always succeeds. | ||
| Error buildTables_COFF_x86_64(LinkGraph &G) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You may want to use llvm#203906 rather than this. If we decide to keep both (a case could be made for that) then both should be declared in |
||
| LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n"); | ||
|
|
||
| x86_64::GOTTableManager GOT(G); | ||
| x86_64::PLTTableManager PLT(G, GOT); | ||
| COFFPointer32NBStubsManager COFFPtr32NB(G, GOT); | ||
|
|
||
| // Mark calls to externals as BranchPCRel32 so PLTTableManager will create | ||
| // stubs for them. Without this, it ignores COFF's PCRel32 edge kind. | ||
| for (auto *B : G.blocks()) { | ||
| for (auto &E : B->edges()) { | ||
| if (E.getKind() == EdgeKind_coff_x86_64::PCRel32 && | ||
| !E.getTarget().isDefined()) { | ||
| E.setKind(x86_64::BranchPCRel32); | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+337
to
+344
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like the COFF/x86-64 backend is using its own Edge kind. We should update it to use EdgeKind_x86_64 from https://github.com/llvm/llvm-project/blob/a4e18dc2a2e300dd6b5e93225b050d852abb319d/llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h#L25, adding additional edges to that kind if necessary. |
||
|
|
||
| visitExistingEdges(G, PLT, COFFPtr32NB); | ||
| return Error::success(); | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| namespace llvm { | ||
|
|
@@ -303,6 +403,12 @@ void link_COFF_x86_64(std::unique_ptr<LinkGraph> G, | |
| } else | ||
| Config.PrePrunePasses.push_back(markAllSymbolsLive); | ||
|
|
||
| // Add an in place GOT/PLT stub build pass for external calls. | ||
| Config.PostPrunePasses.push_back(buildTables_COFF_x86_64); | ||
|
|
||
| // Add ImageBase resolution pass, needed by Lowering and other downstream passes. | ||
| Config.PreFixupPasses.push_back(COFFImageBaseResolution_x86_64()); | ||
|
|
||
| // Add COFF edge lowering passes. | ||
| Config.PreFixupPasses.push_back(COFFLinkGraphLowering_x86_64()); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been looking at how Windows handles
__ImageBaseand I think I've convinced myself that we should handle this via the memory manager (and maybe a custom materialisation unit), rather than a pass.E.g. the memory manager could define (and resolve)
__ImageBasein each object as it passes through theallocatemethod. @jaredwy -- what do you think?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thought was a postallocation pass. That way we can ensure that its in the right place and maybe +-2gb of relevant sections. But I guess same idea, just different approach, yours does give us a bit more control over the way things layout I suppose?