Skip to content

Commit

Permalink
[gnustep-objc] Make the GNUstep v2 ABI work for Windows DLLs.
Browse files Browse the repository at this point in the history
Summary:
Based on a patch by Dustin Howett, modified to not change the ABI for
ELF platforms.

Use more Windows-like section names.

This also makes things more readable by PE/COFF debug tools that assume
sections fit in the first header.

With these changes in, it is now possible to build a working WinObjC
with clang and the WinObjC version of GNUstep libobjc (upstream GNUstep
libobjc + a work around for incremental linking, which can be removed
once LINK.EXE gains a feature to opt sections out of receiving extra
padding during an incremental link).

Patch by Dustin Howett!

Reviewers: DHowett-MSFT

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D58724

llvm-svn: 357364
  • Loading branch information
David Chisnall committed Mar 31, 2019
1 parent 17d4295 commit 7b36a86
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 47 deletions.
163 changes: 134 additions & 29 deletions clang/lib/CodeGen/CGObjCGNU.cpp
Expand Up @@ -185,12 +185,16 @@ class CGObjCGNU : public CGObjCRuntime {
(R.getVersion() >= VersionTuple(major, minor));
}

std::string SymbolForProtocol(StringRef Name) {
return (StringRef("._OBJC_PROTOCOL_") + Name).str();
std::string ManglePublicSymbol(StringRef Name) {
return (StringRef(CGM.getTriple().isOSBinFormatCOFF() ? "$_" : "._") + Name).str();
}

std::string SymbolForProtocol(Twine Name) {
return (ManglePublicSymbol("OBJC_PROTOCOL_") + Name).str();
}

std::string SymbolForProtocolRef(StringRef Name) {
return (StringRef("._OBJC_REF_PROTOCOL_") + Name).str();
return (ManglePublicSymbol("OBJC_REF_PROTOCOL_") + Name).str();
}


Expand Down Expand Up @@ -906,12 +910,15 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
ConstantStringSection
};
static const char *const SectionsBaseNames[8];
static const char *const PECOFFSectionsBaseNames[8];
template<SectionKind K>
std::string sectionName() {
std::string name(SectionsBaseNames[K]);
if (CGM.getTriple().isOSBinFormatCOFF())
if (CGM.getTriple().isOSBinFormatCOFF()) {
std::string name(PECOFFSectionsBaseNames[K]);
name += "$m";
return name;
return name;
}
return SectionsBaseNames[K];
}
/// The GCC ABI superclass message lookup function. Takes a pointer to a
/// structure describing the receiver and the class, and a selector as
Expand All @@ -932,15 +939,19 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
bool EmittedClass = false;
/// Generate the name of a symbol for a reference to a class. Accesses to
/// classes should be indirected via this.

typedef std::pair<std::string, std::pair<llvm::Constant*, int>> EarlyInitPair;
std::vector<EarlyInitPair> EarlyInitList;

std::string SymbolForClassRef(StringRef Name, bool isWeak) {
if (isWeak)
return (StringRef("._OBJC_WEAK_REF_CLASS_") + Name).str();
return (ManglePublicSymbol("OBJC_WEAK_REF_CLASS_") + Name).str();
else
return (StringRef("._OBJC_REF_CLASS_") + Name).str();
return (ManglePublicSymbol("OBJC_REF_CLASS_") + Name).str();
}
/// Generate the name of a class symbol.
std::string SymbolForClass(StringRef Name) {
return (StringRef("._OBJC_CLASS_") + Name).str();
return (ManglePublicSymbol("OBJC_CLASS_") + Name).str();
}
void CallRuntimeFunction(CGBuilderTy &B, StringRef FunctionName,
ArrayRef<llvm::Value*> Args) {
Expand Down Expand Up @@ -994,10 +1005,13 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {

llvm::Constant *isa = TheModule.getNamedGlobal(Sym);

if (!isa)
if (!isa) {
isa = new llvm::GlobalVariable(TheModule, IdTy, /* isConstant */false,
llvm::GlobalValue::ExternalLinkage, nullptr, Sym);
else if (isa->getType() != PtrToIdTy)
if (CGM.getTriple().isOSBinFormatCOFF()) {
cast<llvm::GlobalValue>(isa)->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass);
}
} else if (isa->getType() != PtrToIdTy)
isa = llvm::ConstantExpr::getBitCast(isa, PtrToIdTy);

// struct
Expand All @@ -1012,7 +1026,11 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {

ConstantInitBuilder Builder(CGM);
auto Fields = Builder.beginStruct();
Fields.add(isa);
if (!CGM.getTriple().isOSBinFormatCOFF()) {
Fields.add(isa);
} else {
Fields.addNullPointer(PtrTy);
}
// For now, all non-ASCII strings are represented as UTF-16. As such, the
// number of bytes is simply double the number of UTF-16 codepoints. In
// ASCII strings, the number of bytes is equal to the number of non-ASCII
Expand Down Expand Up @@ -1083,6 +1101,10 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
ObjCStrGV->setComdat(TheModule.getOrInsertComdat(StringName));
ObjCStrGV->setVisibility(llvm::GlobalValue::HiddenVisibility);
}
if (CGM.getTriple().isOSBinFormatCOFF()) {
std::pair<llvm::Constant*, int> v{ObjCStrGV, 0};
EarlyInitList.emplace_back(Sym, v);
}
llvm::Constant *ObjCStr = llvm::ConstantExpr::getBitCast(ObjCStrGV, IdTy);
ObjCStrings[Str] = ObjCStr;
ConstantStrings.push_back(ObjCStr);
Expand Down Expand Up @@ -1196,6 +1218,33 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
ClassSymbol->setInitializer(new llvm::GlobalVariable(TheModule,
Int8Ty, false, llvm::GlobalValue::ExternalWeakLinkage,
nullptr, SymbolForClass(Name)));
else {
if (CGM.getTriple().isOSBinFormatCOFF()) {
IdentifierInfo &II = CGM.getContext().Idents.get(Name);
TranslationUnitDecl *TUDecl = CGM.getContext().getTranslationUnitDecl();
DeclContext *DC = TranslationUnitDecl::castToDeclContext(TUDecl);

const ObjCInterfaceDecl *OID = nullptr;
for (const auto &Result : DC->lookup(&II))
if ((OID = dyn_cast<ObjCInterfaceDecl>(Result)))
break;

// The first Interface we find may be a @class,
// which should only be treated as the source of
// truth in the absence of a true declaration.
const ObjCInterfaceDecl *OIDDef = OID->getDefinition();
if (OIDDef != nullptr)
OID = OIDDef;

auto Storage = llvm::GlobalValue::DefaultStorageClass;
if (OID->hasAttr<DLLImportAttr>())
Storage = llvm::GlobalValue::DLLImportStorageClass;
else if (OID->hasAttr<DLLExportAttr>())
Storage = llvm::GlobalValue::DLLExportStorageClass;

cast<llvm::GlobalValue>(ClassSymbol)->setDLLStorageClass(Storage);
}
}
assert(ClassSymbol->getName() == SymbolName);
return ClassSymbol;
}
Expand Down Expand Up @@ -1448,7 +1497,7 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
Sym->setSection((Section + SecSuffix).str());
Sym->setComdat(TheModule.getOrInsertComdat((Prefix +
Section).str()));
Sym->setAlignment(1);
Sym->setAlignment(CGM.getPointerAlign().getQuantity());
return Sym;
};
return { Sym("__start_", "$a"), Sym("__stop", "$z") };
Expand Down Expand Up @@ -1483,11 +1532,12 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
ConstantInitBuilder builder(CGM);
auto InitStructBuilder = builder.beginStruct();
InitStructBuilder.addInt(Int64Ty, 0);
for (auto *s : SectionsBaseNames) {
auto &sectionVec = CGM.getTriple().isOSBinFormatCOFF() ? PECOFFSectionsBaseNames : SectionsBaseNames;
for (auto *s : sectionVec) {
auto bounds = GetSectionBounds(s);
InitStructBuilder.add(bounds.first);
InitStructBuilder.add(bounds.second);
};
}
auto *InitStruct = InitStructBuilder.finishAndCreateGlobal(".objc_init",
CGM.getPointerAlign(), false, llvm::GlobalValue::LinkOnceODRLinkage);
InitStruct->setVisibility(llvm::GlobalValue::HiddenVisibility);
Expand Down Expand Up @@ -1582,6 +1632,29 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
ConstantStrings.clear();
Categories.clear();
Classes.clear();

if (EarlyInitList.size() > 0) {
auto *Init = llvm::Function::Create(llvm::FunctionType::get(CGM.VoidTy,
{}), llvm::GlobalValue::InternalLinkage, ".objc_early_init",
&CGM.getModule());
llvm::IRBuilder<> b(llvm::BasicBlock::Create(CGM.getLLVMContext(), "entry",
Init));
for (const auto &lateInit : EarlyInitList) {
auto *global = TheModule.getGlobalVariable(lateInit.first);
if (global) {
b.CreateAlignedStore(global,
b.CreateStructGEP(lateInit.second.first, lateInit.second.second), CGM.getPointerAlign().getQuantity());
}
}
b.CreateRetVoid();
// We can't use the normal LLVM global initialisation array, because we
// need to specify that this runs early in library initialisation.
auto *InitVar = new llvm::GlobalVariable(CGM.getModule(), Init->getType(),
/*isConstant*/true, llvm::GlobalValue::InternalLinkage,
Init, ".objc_early_init_ptr");
InitVar->setSection(".CRT$XCLb");
CGM.addUsedGlobal(InitVar);
}
return nullptr;
}
/// In the v2 ABI, ivar offset variables use the type encoding in their name
Expand Down Expand Up @@ -1613,6 +1686,7 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
}
void GenerateClass(const ObjCImplementationDecl *OID) override {
ASTContext &Context = CGM.getContext();
bool IsCOFF = CGM.getTriple().isOSBinFormatCOFF();

// Get the class name
ObjCInterfaceDecl *classDecl =
Expand Down Expand Up @@ -1671,8 +1745,9 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
// struct objc_property_list *properties
metaclassFields.add(GeneratePropertyList(OID, classDecl, /*isClassProperty*/true));

auto *metaclass = metaclassFields.finishAndCreateGlobal("._OBJC_METACLASS_"
+ className, CGM.getPointerAlign());
auto *metaclass = metaclassFields.finishAndCreateGlobal(
ManglePublicSymbol("OBJC_METACLASS_") + className,
CGM.getPointerAlign());

auto classFields = builder.beginStruct();
// struct objc_class *isa;
Expand All @@ -1681,15 +1756,28 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
// Get the superclass name.
const ObjCInterfaceDecl * SuperClassDecl =
OID->getClassInterface()->getSuperClass();
llvm::Constant *SuperClass = nullptr;
if (SuperClassDecl) {
auto SuperClassName = SymbolForClass(SuperClassDecl->getNameAsString());
llvm::Constant *SuperClass = TheModule.getNamedGlobal(SuperClassName);
SuperClass = TheModule.getNamedGlobal(SuperClassName);
if (!SuperClass)
{
SuperClass = new llvm::GlobalVariable(TheModule, PtrTy, false,
llvm::GlobalValue::ExternalLinkage, nullptr, SuperClassName);
if (IsCOFF) {
auto Storage = llvm::GlobalValue::DefaultStorageClass;
if (SuperClassDecl->hasAttr<DLLImportAttr>())
Storage = llvm::GlobalValue::DLLImportStorageClass;
else if (SuperClassDecl->hasAttr<DLLExportAttr>())
Storage = llvm::GlobalValue::DLLExportStorageClass;

cast<llvm::GlobalValue>(SuperClass)->setDLLStorageClass(Storage);
}
}
classFields.add(llvm::ConstantExpr::getBitCast(SuperClass, PtrTy));
if (!IsCOFF)
classFields.add(llvm::ConstantExpr::getBitCast(SuperClass, PtrTy));
else
classFields.addNullPointer(PtrTy);
} else
classFields.addNullPointer(PtrTy);
// const char *name;
Expand Down Expand Up @@ -1837,19 +1925,24 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
classFields.finishAndCreateGlobal(SymbolForClass(className),
CGM.getPointerAlign(), false, llvm::GlobalValue::ExternalLinkage);

if (CGM.getTriple().isOSBinFormatCOFF()) {
auto Storage = llvm::GlobalValue::DefaultStorageClass;
if (OID->getClassInterface()->hasAttr<DLLImportAttr>())
Storage = llvm::GlobalValue::DLLImportStorageClass;
else if (OID->getClassInterface()->hasAttr<DLLExportAttr>())
Storage = llvm::GlobalValue::DLLExportStorageClass;
cast<llvm::GlobalValue>(classStruct)->setDLLStorageClass(Storage);
}

auto *classRefSymbol = GetClassVar(className);
classRefSymbol->setSection(sectionName<ClassReferenceSection>());
classRefSymbol->setInitializer(llvm::ConstantExpr::getBitCast(classStruct, IdTy));

if (IsCOFF) {
// we can't import a class struct.
if (OID->getClassInterface()->hasAttr<DLLExportAttr>()) {
cast<llvm::GlobalValue>(classStruct)->setDLLStorageClass(llvm::GlobalValue::DLLExportStorageClass);
cast<llvm::GlobalValue>(classRefSymbol)->setDLLStorageClass(llvm::GlobalValue::DLLExportStorageClass);
}

if (SuperClass) {
std::pair<llvm::Constant*, int> v{classStruct, 1};
EarlyInitList.emplace_back(SuperClass->getName(), std::move(v));
}

}


// Resolve the class aliases, if they exist.
// FIXME: Class pointer aliases shouldn't exist!
Expand Down Expand Up @@ -1877,7 +1970,7 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {

auto classInitRef = new llvm::GlobalVariable(TheModule,
classStruct->getType(), false, llvm::GlobalValue::ExternalLinkage,
classStruct, "._OBJC_INIT_CLASS_" + className);
classStruct, ManglePublicSymbol("OBJC_INIT_CLASS_") + className);
classInitRef->setSection(sectionName<ClassSection>());
CGM.addUsedGlobal(classInitRef);

Expand Down Expand Up @@ -1914,6 +2007,18 @@ const char *const CGObjCGNUstep2::SectionsBaseNames[8] =
"__objc_constant_string"
};

const char *const CGObjCGNUstep2::PECOFFSectionsBaseNames[8] =
{
".objcrt$SEL",
".objcrt$CLS",
".objcrt$CLR",
".objcrt$CAT",
".objcrt$PCL",
".objcrt$PCR",
".objcrt$CAL",
".objcrt$STR"
};

/// Support for the ObjFW runtime.
class CGObjCObjFW: public CGObjCGNU {
protected:
Expand Down

0 comments on commit 7b36a86

Please sign in to comment.