1*753f127fSDimitry Andric //===-------------- COFF.cpp - JIT linker function for COFF -------------===//
2*753f127fSDimitry Andric //
3*753f127fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*753f127fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*753f127fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*753f127fSDimitry Andric //
7*753f127fSDimitry Andric //===----------------------------------------------------------------------===//
8*753f127fSDimitry Andric //
9*753f127fSDimitry Andric // COFF jit-link function.
10*753f127fSDimitry Andric //
11*753f127fSDimitry Andric //===----------------------------------------------------------------------===//
12*753f127fSDimitry Andric 
13*753f127fSDimitry Andric #include "llvm/ExecutionEngine/JITLink/COFF.h"
14*753f127fSDimitry Andric 
15*753f127fSDimitry Andric #include "llvm/BinaryFormat/COFF.h"
16*753f127fSDimitry Andric #include "llvm/ExecutionEngine/JITLink/COFF_x86_64.h"
17*753f127fSDimitry Andric #include "llvm/Object/COFF.h"
18*753f127fSDimitry Andric #include "llvm/Support/Format.h"
19*753f127fSDimitry Andric #include "llvm/Support/MemoryBuffer.h"
20*753f127fSDimitry Andric #include <cstring>
21*753f127fSDimitry Andric 
22*753f127fSDimitry Andric using namespace llvm;
23*753f127fSDimitry Andric 
24*753f127fSDimitry Andric #define DEBUG_TYPE "jitlink"
25*753f127fSDimitry Andric 
26*753f127fSDimitry Andric namespace llvm {
27*753f127fSDimitry Andric namespace jitlink {
28*753f127fSDimitry Andric 
getMachineName(uint16_t Machine)29*753f127fSDimitry Andric static StringRef getMachineName(uint16_t Machine) {
30*753f127fSDimitry Andric   switch (Machine) {
31*753f127fSDimitry Andric   case COFF::IMAGE_FILE_MACHINE_I386:
32*753f127fSDimitry Andric     return "i386";
33*753f127fSDimitry Andric   case COFF::IMAGE_FILE_MACHINE_AMD64:
34*753f127fSDimitry Andric     return "x86_64";
35*753f127fSDimitry Andric   case COFF::IMAGE_FILE_MACHINE_ARMNT:
36*753f127fSDimitry Andric     return "ARM";
37*753f127fSDimitry Andric   case COFF::IMAGE_FILE_MACHINE_ARM64:
38*753f127fSDimitry Andric     return "ARM64";
39*753f127fSDimitry Andric   default:
40*753f127fSDimitry Andric     return "unknown";
41*753f127fSDimitry Andric   }
42*753f127fSDimitry Andric }
43*753f127fSDimitry Andric 
44*753f127fSDimitry Andric Expected<std::unique_ptr<LinkGraph>>
createLinkGraphFromCOFFObject(MemoryBufferRef ObjectBuffer)45*753f127fSDimitry Andric createLinkGraphFromCOFFObject(MemoryBufferRef ObjectBuffer) {
46*753f127fSDimitry Andric   StringRef Data = ObjectBuffer.getBuffer();
47*753f127fSDimitry Andric 
48*753f127fSDimitry Andric   // Check magic
49*753f127fSDimitry Andric   auto Magic = identify_magic(ObjectBuffer.getBuffer());
50*753f127fSDimitry Andric   if (Magic != file_magic::coff_object)
51*753f127fSDimitry Andric     return make_error<JITLinkError>("Invalid COFF buffer");
52*753f127fSDimitry Andric 
53*753f127fSDimitry Andric   if (Data.size() < sizeof(object::coff_file_header))
54*753f127fSDimitry Andric     return make_error<JITLinkError>("Truncated COFF buffer");
55*753f127fSDimitry Andric 
56*753f127fSDimitry Andric   uint64_t CurPtr = 0;
57*753f127fSDimitry Andric   bool IsPE = false;
58*753f127fSDimitry Andric 
59*753f127fSDimitry Andric   // Check if this is a PE/COFF file.
60*753f127fSDimitry Andric   if (Data.size() >= sizeof(object::dos_header) + sizeof(COFF::PEMagic)) {
61*753f127fSDimitry Andric     const auto *DH =
62*753f127fSDimitry Andric         reinterpret_cast<const object::dos_header *>(Data.data() + CurPtr);
63*753f127fSDimitry Andric     if (DH->Magic[0] == 'M' && DH->Magic[1] == 'Z') {
64*753f127fSDimitry Andric       // Check the PE magic bytes. ("PE\0\0")
65*753f127fSDimitry Andric       CurPtr = DH->AddressOfNewExeHeader;
66*753f127fSDimitry Andric       if (memcmp(Data.data() + CurPtr, COFF::PEMagic, sizeof(COFF::PEMagic)) !=
67*753f127fSDimitry Andric           0) {
68*753f127fSDimitry Andric         return make_error<JITLinkError>("Incorrect PE magic");
69*753f127fSDimitry Andric       }
70*753f127fSDimitry Andric       CurPtr += sizeof(COFF::PEMagic);
71*753f127fSDimitry Andric       IsPE = true;
72*753f127fSDimitry Andric     }
73*753f127fSDimitry Andric   }
74*753f127fSDimitry Andric   if (Data.size() < CurPtr + sizeof(object::coff_file_header))
75*753f127fSDimitry Andric     return make_error<JITLinkError>("Truncated COFF buffer");
76*753f127fSDimitry Andric 
77*753f127fSDimitry Andric   const object::coff_file_header *COFFHeader =
78*753f127fSDimitry Andric       reinterpret_cast<const object::coff_file_header *>(Data.data() + CurPtr);
79*753f127fSDimitry Andric   const object::coff_bigobj_file_header *COFFBigObjHeader = nullptr;
80*753f127fSDimitry Andric 
81*753f127fSDimitry Andric   // Deal with bigobj file
82*753f127fSDimitry Andric   if (!IsPE && COFFHeader->Machine == COFF::IMAGE_FILE_MACHINE_UNKNOWN &&
83*753f127fSDimitry Andric       COFFHeader->NumberOfSections == uint16_t(0xffff) &&
84*753f127fSDimitry Andric       Data.size() >= sizeof(object::coff_bigobj_file_header)) {
85*753f127fSDimitry Andric     if (Data.size() < sizeof(object::coff_file_header)) {
86*753f127fSDimitry Andric       return make_error<JITLinkError>("Truncated COFF buffer");
87*753f127fSDimitry Andric     }
88*753f127fSDimitry Andric     COFFBigObjHeader =
89*753f127fSDimitry Andric         reinterpret_cast<const object::coff_bigobj_file_header *>(Data.data() +
90*753f127fSDimitry Andric                                                                   CurPtr);
91*753f127fSDimitry Andric 
92*753f127fSDimitry Andric     // Verify that we are dealing with bigobj.
93*753f127fSDimitry Andric     if (COFFBigObjHeader->Version >= COFF::BigObjHeader::MinBigObjectVersion &&
94*753f127fSDimitry Andric         std::memcmp(COFFBigObjHeader->UUID, COFF::BigObjMagic,
95*753f127fSDimitry Andric                     sizeof(COFF::BigObjMagic)) == 0) {
96*753f127fSDimitry Andric       COFFHeader = nullptr;
97*753f127fSDimitry Andric       CurPtr += sizeof(object::coff_bigobj_file_header);
98*753f127fSDimitry Andric     } else
99*753f127fSDimitry Andric       COFFBigObjHeader = nullptr;
100*753f127fSDimitry Andric   }
101*753f127fSDimitry Andric 
102*753f127fSDimitry Andric   uint16_t Machine =
103*753f127fSDimitry Andric       COFFHeader ? COFFHeader->Machine : COFFBigObjHeader->Machine;
104*753f127fSDimitry Andric   LLVM_DEBUG({
105*753f127fSDimitry Andric     dbgs() << "jitLink_COFF: PE = " << (IsPE ? "yes" : "no")
106*753f127fSDimitry Andric            << ", bigobj = " << (COFFBigObjHeader ? "yes" : "no")
107*753f127fSDimitry Andric            << ", identifier = \"" << ObjectBuffer.getBufferIdentifier() << "\" "
108*753f127fSDimitry Andric            << "machine = " << getMachineName(Machine) << "\n";
109*753f127fSDimitry Andric   });
110*753f127fSDimitry Andric 
111*753f127fSDimitry Andric   switch (Machine) {
112*753f127fSDimitry Andric   case COFF::IMAGE_FILE_MACHINE_AMD64:
113*753f127fSDimitry Andric     return createLinkGraphFromCOFFObject_x86_64(ObjectBuffer);
114*753f127fSDimitry Andric   default:
115*753f127fSDimitry Andric     return make_error<JITLinkError>(
116*753f127fSDimitry Andric         "Unsupported target machine architecture in COFF object " +
117*753f127fSDimitry Andric         ObjectBuffer.getBufferIdentifier() + ": " + getMachineName(Machine));
118*753f127fSDimitry Andric   }
119*753f127fSDimitry Andric }
120*753f127fSDimitry Andric 
link_COFF(std::unique_ptr<LinkGraph> G,std::unique_ptr<JITLinkContext> Ctx)121*753f127fSDimitry Andric void link_COFF(std::unique_ptr<LinkGraph> G,
122*753f127fSDimitry Andric                std::unique_ptr<JITLinkContext> Ctx) {
123*753f127fSDimitry Andric   switch (G->getTargetTriple().getArch()) {
124*753f127fSDimitry Andric   case Triple::x86_64:
125*753f127fSDimitry Andric     link_COFF_x86_64(std::move(G), std::move(Ctx));
126*753f127fSDimitry Andric     return;
127*753f127fSDimitry Andric   default:
128*753f127fSDimitry Andric     Ctx->notifyFailed(make_error<JITLinkError>(
129*753f127fSDimitry Andric         "Unsupported target machine architecture in COFF link graph " +
130*753f127fSDimitry Andric         G->getName()));
131*753f127fSDimitry Andric     return;
132*753f127fSDimitry Andric   }
133*753f127fSDimitry Andric }
134*753f127fSDimitry Andric 
135*753f127fSDimitry Andric } // end namespace jitlink
136*753f127fSDimitry Andric } // end namespace llvm
137