Index: ELF/Arch/X86.cpp =================================================================== --- ELF/Arch/X86.cpp +++ ELF/Arch/X86.cpp @@ -34,6 +34,8 @@ void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; + void writeSPlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr) const override; void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, @@ -172,9 +174,15 @@ } void X86::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - // Entries in .got.plt initially points back to the corresponding - // PLT entries with a fixed offset to skip the first instruction. - write32le(Buf, S.getPltVA() + 6); + if (Config->SPltSectionEnable) + // If the PLT is IBT-enabled, the entries in .got.plt initially point to + // the entries in the secondary PLT. + write32le(Buf, S.getSPltVA()); + else + // Otherwise the entries in .got.plt initially point back to the + // corresponding PLT entries with a fixed offset to skip the first + // instruction. + write32le(Buf, S.getPltVA() + 6); } void X86::writeIgotPlt(uint8_t *Buf, const Symbol &S) const { @@ -220,6 +228,21 @@ void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { + // In the IBT-enabled PLT, the instruction sequence is different. + if (Config->SPltSectionEnable) { + const uint8_t Inst[] = { + 0xf3, 0x0f, 0x1e, 0xfb, // endbr32 + 0x68, 0, 0, 0, 0, // pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // jmpq .PLT0@PC + 0x66, 0x90, // nop + }; + memcpy(Buf, Inst, sizeof(Inst)); + + write32le(Buf + 5, RelOff); + write32le(Buf + 10, -getPltEntryOffset(Index) - 14); + return; + } + const uint8_t Inst[] = { 0xff, 0x00, 0, 0, 0, 0, // jmp *foo_in_GOT or jmp *foo@GOT(%ebx) 0x68, 0, 0, 0, 0, // pushl $reloc_offset @@ -242,6 +265,18 @@ write32le(Buf + 12, -getPltEntryOffset(Index) - 16); } +void X86::writeSPlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr) const { + const uint8_t Inst[] = { + 0xf3, 0x0f, 0x1e, 0xfb, // endbr32 + 0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip) + 0x66, 0x0f, 0x1f, 0x44, 0, 0, // nop + }; + memcpy(Buf, Inst, sizeof(Inst)); + + write32le(Buf + 6, GotPltEntryAddr - PltEntryAddr - 10); +} + int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const { switch (Type) { case R_386_8: Index: ELF/Arch/X86_64.cpp =================================================================== --- ELF/Arch/X86_64.cpp +++ ELF/Arch/X86_64.cpp @@ -34,6 +34,8 @@ void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; + void writeSPlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr) const override; void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, @@ -141,8 +143,15 @@ template void X86_64::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - // See comments in X86::writeGotPlt. - write64le(Buf, S.getPltVA() + 6); + if (Config->SPltSectionEnable) + // If the PLT is IBT-enabled, the entries in .got.plt also initially point to + // the entries in the first PLT not the second PLT. + write64le(Buf, S.getPltVA()); + else + // Otherwise the entries in .got.plt initially point back to the + // corresponding PLT entries with a fixed offset to skip the first + // instruction. + write64le(Buf, S.getPltVA() + 6); } template void X86_64::writePltHeader(uint8_t *Buf) const { @@ -162,6 +171,21 @@ void X86_64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { + // In the IBT-enabled PLT, the instruction sequence is different. + if (Config->SPltSectionEnable) { + const uint8_t Inst[] = { + 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 + 0x68, 0, 0, 0, 0, // pushq + 0xe9, 0, 0, 0, 0, // jmpq plt[0] + 0x66, 0x90, // nop + }; + memcpy(Buf, Inst, sizeof(Inst)); + write32le(Buf + 5, Index); + // Distance between nop and PLT's entry + write32le(Buf + 10, -getPltEntryOffset(Index) - 14); + return; + } + const uint8_t Inst[] = { 0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip) 0x68, 0, 0, 0, 0, // pushq @@ -174,6 +198,19 @@ write32le(Buf + 12, -getPltEntryOffset(Index) - 16); } +template +void X86_64::writeSPlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr) const { + const uint8_t Inst[] = { + 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 + 0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip) + 0x66, 0x0f, 0x1f, 0x44, 0, 0, // nop + }; + memcpy(Buf, Inst, sizeof(Inst)); + // Distance between nop and GOT PLT's entry + write32le(Buf + 6, GotPltEntryAddr - PltEntryAddr - 10); +} + template RelType X86_64::getDynRel(RelType Type) const { if (Type == R_X86_64_64 || Type == R_X86_64_PC64 || Type == R_X86_64_SIZE32 || Type == R_X86_64_SIZE64) Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -184,9 +184,11 @@ bool WarnMissingEntry; bool WarnSymbolOrdering; bool WriteAddends; + bool SPltSectionEnable = false; bool ZCombreloc; bool ZCopyreloc; bool ZExecstack; + bool ZForceCET; bool ZGlobal; bool ZHazardplt; bool ZInitfirst; @@ -214,6 +216,11 @@ ELFKind EKind = ELFNoneKind; uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL; uint16_t EMachine = llvm::ELF::EM_NONE; + + // GNU_PROPERTY_X86_FEATURE_1_AND: Its pr_data field contains a 4-byte + // integer. A bit in the output pr_data field is set only if it is set + // in all relocatable input pr_data fields. + uint32_t X86Feature1AND= 0x0; llvm::Optional ImageBase; uint64_t MaxPageSize; uint64_t MipsGotSize; Index: ELF/Driver.h =================================================================== --- ELF/Driver.h +++ ELF/Driver.h @@ -33,7 +33,9 @@ void readConfigs(llvm::opt::InputArgList &Args); void createFiles(llvm::opt::InputArgList &Args); void inferMachineType(); + template void mergeAggregateMetadata(); template void link(llvm::opt::InputArgList &Args); + template void releaseResources(); // True if we are in --whole-archive and --no-whole-archive. bool InWholeArchive = false; @@ -42,6 +44,7 @@ bool InLib = false; std::vector Files; + std::vector DataAllocated; }; // Parses command line options. Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -72,6 +72,8 @@ static void setConfigs(opt::InputArgList &Args); +#define ALIGN_UP(size, alignment) ((size) + (alignment) - 1)&(-(alignment)) + bool elf::link(ArrayRef Args, bool CanExitEarly, raw_ostream &Error) { errorHandler().LogName = args::getFilenameWithoutExe(Args[0]); @@ -346,8 +348,8 @@ static bool isKnownZFlag(StringRef S) { return S == "combreloc" || S == "copyreloc" || S == "defs" || - S == "execstack" || S == "global" || S == "hazardplt" || - S == "initfirst" || S == "interpose" || + S == "execstack" || S == "force-cet" || S == "global" || + S == "hazardplt" || S == "initfirst" || S == "interpose" || S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" || S == "nocombreloc" || S == "nocopyreloc" || S == "nodefaultlib" || S == "nodelete" || S == "nodlopen" || S == "noexecstack" || @@ -871,6 +873,7 @@ Config->ZCombreloc = getZFlag(Args, "combreloc", "nocombreloc", true); Config->ZCopyreloc = getZFlag(Args, "copyreloc", "nocopyreloc", true); Config->ZExecstack = getZFlag(Args, "execstack", "noexecstack", false); + Config->ZForceCET = hasZOption(Args, "force-cet"); Config->ZGlobal = hasZOption(Args, "global"); Config->ZHazardplt = hasZOption(Args, "hazardplt"); Config->ZInitfirst = hasZOption(Args, "initfirst"); @@ -1439,6 +1442,258 @@ #undef HANDLE_LIBCALL }; +// Get the CFProtection properties from a X86F1AND section. The X86F1AND section +// info can refert the comment of function findGNUPropertyX86Feature1AND. +template +static void readGNUPropertyX86Feature1AND(InputSectionBase *S, + Configuration *Config, + unsigned X86F1ANDDescLoca) { + // TODO: for now, we assume the section is well-formed with one note array. + using ElfWordT = typename ELFT::Word; + auto Data = S->getDataAs(); + unsigned &Offset = X86F1ANDDescLoca; + + unsigned pr_typeIndex = 0; // Offset of pr_type from start of 'desc' + unsigned pr_dataIndex = 2; // Offset of pr_data from start of 'desc' + + // There must be GNU_PROPERTY_X86_FEATURE_1_AND note according to the ABI. + if (Data[Offset + pr_typeIndex] != GNU_PROPERTY_X86_FEATURE_1_AND) + error("There must be GNU_PROPERTY_X86_FEATURE_1_AND note!"); + ElfWordT Flags = Data[Offset + pr_dataIndex]; + + // GNU_PROPERTY_X86_FEATURE_1_AND: Its pr_data field contains a 4-byte + // integer. A bit in the output pr_data field is set only if it is set + // in all relocatable input pr_data fields. + Config->X86Feature1AND &= Flags; + + // To Be Done. + // Now the IBT can't work with retpolineplt feature, becasue they may conflict + // in changing the PLT. So we disable the IBT when the IFUNC enabled. + if (Config->ZRetpolineplt) + Config->X86Feature1AND &= ~GNU_PROPERTY_X86_FEATURE_1_IBT; + + Config->SPltSectionEnable = Config->X86Feature1AND + & GNU_PROPERTY_X86_FEATURE_1_IBT; +} + +// Get the position of desc with GNU_PROPERTY_X86_FEATURE_1_AND. +// CurSecOffset: The current section head offset in the .note. +// DescOffset: The offset of current desc in current section. +inline static unsigned getX86Feature1ANDLocation(unsigned CurSecOffset, + unsigned DescOffset) +{ + return CurSecOffset + DescOffset; +} + +// This function check if there is X86Feature1AND 'desc' in a .note.gnu +// .property, becase the X86Feature1AND info is always stored in a note.gnu +// .property whose section's n_type = NT_GNU_PROPERTY_TYPE_0 and pr_type = +// GNU_PROPERTY_X86_FEATURE_1_AND. So we just use these 2 flags to check if it +// is X86Feature1AND. But we can not make sure the X86Feature1AND info such as +// IBT SHSTK is existed in the section, it should be checked later. +// The .note.gnu.property context is 'compressed' into the DATA array of section +// mapped from the objdect files. There are 3 forms of the merged of sections in +// object files. Here we should parse it from section DATA. +// And then collect the X86Feature1AND infomation into X86F1ANDDescLoca. +// +// Data struct of .note.gnu.property section (X86Feature1AND section) in ABI: +// System V Application Binary Interface Linux Extensions Version 0.1 Edited +// by H.J.Lu1 September 18,2018. Prease refer +// https://github.com/hjl-tools/linux-abi/wiki/linux-abi-draft.pdf +// Field Length Contents _ +// n_namsz 4 4 | +// n_descsz 4 The note descriptor size | We called these data 'head' +// n_type 4 NT_GNU_PROPERTY_TYPE_0 | +// n_name 4 GNU _| +// n_desc n_descsz The program property array +// +// Data struct in n_desc: +// typedef struct { _ +// ElfWord pr_type; //A 4-byte integer | +// ElfWord pr_datasz; //A 4-byte integer | We called these data 'desc' +// unsigned char pr_data[PR_DATASZ]; | +// unsigned char pr_padding[PR_PADDING]; _| +// } +// +// pr_type The type of program property. A 4-byte integer in the format of +// the target processor. +// pr_datasz The size of the pr_data. A 4-byte integer in the format of the +// target processor. +// pr_data The program property descriptor which is aligned to 4 bytes in +// 32-bit objects and 8 bytes in 64-bit objects. +// pr_padding The padding. If necessary, it aligns the array element to 8 or +// 4-byte alignment (depending on whether the target is a 64-biti +// or 32-bit object). +// PR_DATASZ The value in the pr_datasz. A constant. +// PR_PADDING The size of the pr_padding. A constant. +// +// ********* we call the upper data 'head' and the following data 'desc' in +// ********* the code. There are 3 formats for the merged note.gnu.property +// ********* sections: A) one head with one desc. B) pairs of one head with +// ********* mutil desc and the mix of A and B. ********* +// +// Form A: Form B: +// __________________ ________________________ +// | | | | | | | +// | head | desc | | head | desc1 | desc2 | +// |_______|________| |______|_______|_______| +// +// (As means more than one A) +// Form MIX of all As, all Bs, or As+Bs: +// ____________________________________________ +// | | | | | | +// | head1 | desc1 |head2 | desc1 | desc2 |... +// |_______|________|______|_______|_______|___ +// I---subsection---I--------subsection----- +// +// Function return: +// Return true if the .noto.gnu.property section contains X86Feature1ANDi, +// else return false; +template +static bool findGNUPropertyX86Feature1AND(InputSectionBase *S, + unsigned &X86F1ANDDescLoca) { + using ElfWordT = typename ELFT::Word; + auto Data = S->getDataAs(); + auto SecSize = Data.size() * 4; + decltype(SecSize) CurSize = 0; + + // The current section's (or subsection's) 'head' offset in the .note. + unsigned CurSecOffset = 0; + + const unsigned n_descszIndex = 1; // Offset of n_descsz from 'head' start. + const unsigned n_typeIndex = 2; // Offset of n_type from 'head' start. + const unsigned pr_typeInDescIndex = 0;// Offset of pr_type from 'head' start. + // Offset of pr_datasz from 'head' start. + const unsigned pr_dataszInDescIndex = 1; + const unsigned HeadSize = 4 * 4; // Size of 'head' + const unsigned DescFixedSize = 4 * 2; // Size of pr_type + pr_datasz in 'desc' + + // There are 3 key offset in the funtion. If we suppose the current checked + // section is subSection2 and the its current checked desc is desc2, the 3 + // offset can be show as following in DATA array: + // _____________________________________________ + // | | | * | | * | + // | head1 | desc1 | head2 | desc1 | desc2 |... + // |_______|________|_______|_______|_______|___ + // I--curSecOffect--I--descOffect---I + // I-----nextDescOffect----I + // I--subSection1---I------subSection2------? + // + // Now we begin to parse the section's DATA array. + while (CurSize < SecSize) { + // The offset of current desc in current section. + unsigned DescOffset = 4; + + // It is a X86Feature1AND section when n_type is NT_GNU_PROPERTY_TYPE_0 and + // pr_type is GNU_PROPERTY_X86_FEATURE_1_AND. + if (Data[CurSecOffset + n_typeIndex] == NT_GNU_PROPERTY_TYPE_0) { + if (Data[CurSecOffset + DescOffset + pr_typeInDescIndex] + == GNU_PROPERTY_X86_FEATURE_1_AND) { + X86F1ANDDescLoca = getX86Feature1ANDLocation(CurSecOffset, DescOffset); + return true; + } else { + bool IsOutBoundary = true; + // Now the current desc has no X86Feature1AND info, we try to find if + // there is another desc in current section and is it contain the + // X86Feature1AND info. + unsigned Next_pr_typeIndex, NextDescOffset; + do { + unsigned Size = Data[CurSecOffset + DescOffset + pr_dataszInDescIndex]; + Size += CurSecOffset * 4 + DescOffset*4 + DescFixedSize; + Size = ALIGN_UP (Size, S->Alignment); + NextDescOffset = Size / 4 - CurSecOffset; //next DescOffset + Next_pr_typeIndex = CurSecOffset + NextDescOffset + pr_typeInDescIndex; + + // Check if the next pr_type offset go out of the current section. + IsOutBoundary = Next_pr_typeIndex + >= CurSecOffset + 4 + + Data[CurSecOffset + n_descszIndex]/4; + + // Find X86Feature1AND info! + if (!IsOutBoundary && (Data[Next_pr_typeIndex] + == GNU_PROPERTY_X86_FEATURE_1_AND)) { + X86F1ANDDescLoca = getX86Feature1ANDLocation(CurSecOffset, + NextDescOffset); + return true; + } + DescOffset = NextDescOffset; + } while (!IsOutBoundary); + } + } + auto Size = Data[CurSecOffset + n_descszIndex]; + CurSize += HeadSize + Size; + // Section and pr_data both have the align requestment: 8 in 64bit cpu + // and 4 in 32bit cpu. + CurSize = ALIGN_UP (CurSize, S->Alignment); + CurSecOffset = CurSize / 4; + } + return false; +} + +// Iterate over metadata sections that should be aggregated and processed for +// the final result. But till now, we just deal with X86Feature1AND info here. +// In most cases the X86Feature1AND info was written in .note.gnu.propery +// section with one 'desc' but it can be also written in 'desc' combined with +// other 'desc' in the .note.gnu.propery section. Here we first find all the +// X86Feature1AND info section, then delete them, and finally rebuild the +// X86Feature1AND section in the Writer if necessary. +template void LinkerDriver::mergeAggregateMetadata() { + decltype (InputSections) X86F1ANDSections; + bool IsAllFileF1AND = true; + Config->SPltSectionEnable = true; + Config->X86Feature1AND = 0xFFFFFFFF; + for (InputFile *F : ObjectFiles) { + unsigned X86F1ANDDescLoca = 0; + auto I = llvm::find_if(InputSections, [&](InputSectionBase *S) { + // X86Feature1AND info must be setten in .note.gnu.property sections. + if (S->Name != ".note.gnu.property" || S->File != F + || S->Type != SHT_NOTE) + return false; + X86F1ANDSections.push_back(S); + bool Found = findGNUPropertyX86Feature1AND(S, X86F1ANDDescLoca); + return Found; + }); + if (I == InputSections.end()) { + // There is no X86Feature1AND .note.gnu.property section in this file. Set + // all CFProtection properties to false and discard other such sections. + IsAllFileF1AND = false; + + // If used -z force-cet option, all the input files must be CET marked. + if (Config->ZForceCET) { + error(toString(F) + "must enable CET when using -z force-cet " + + "linker option."); + break; + } + } else { + readGNUPropertyX86Feature1AND(*I, Config, X86F1ANDDescLoca); + } + } + if (!IsAllFileF1AND) { + Config->SPltSectionEnable = false; + Config->X86Feature1AND = 0x0; + } + + // The resulting .note.gnu.property section will be created by the Writer + // if necessary. + // ******************************* To Be Done ******************************** + // We rudely erase the .note.gnu.property sections in the input files. Because + // till now we just implemented GNU_PROPERTY_X86_FEATURE_1_AND property, the + // other unhandled properties in the sections may be discard too. It seems not + // reasonable. But till now it's reasonable, because the .note.gnu.property + // is a special section, all the properties in this section need to be checked + // and handled. If they are simply passed to output file without being handled + // there may be a bug or exception in running the output file. For example, + // the GNU_PROPERTY_X86_ISA_1_USED also need all input file contain its flags, + // it can't be simply passed to the output file without checking other files. + // So we erase these informations before we implement the function of them. + llvm::erase_if(InputSections, [=, &X86F1ANDSections](InputSectionBase *S) { + if (llvm::find (X86F1ANDSections, S) != X86F1ANDSections.end()) + return true; + else + return false; + }); +} + // Do actual linking. Note that when this function is called, // all linker scripts have already been parsed. template void LinkerDriver::link(opt::InputArgList &Args) { @@ -1611,6 +1866,13 @@ return S->Name.startswith(".debug") || S->Name.startswith(".zdebug"); }); + // Merge aggregate metadata sections like .note.gnu.property that should be + // found across all relocated files and reproduced once. But Until now, the + // emitAggregateMetadata just deal with the X86Feature1AND info, and only X86_64, i386 + // support the X86Feature1AND. + if (Config->EMachine == EM_X86_64 || Config->EMachine == EM_386) + mergeAggregateMetadata(); + Config->EFlags = Target->calcEFlags(); if (Config->EMachine == EM_ARM) { Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -120,6 +120,10 @@ return RawData; } + void setData(ArrayRef Data){ + RawData = Data; + } + uint64_t getOffsetInFile() const; // True if this section has already been placed to a linker script Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -705,10 +705,20 @@ return Dest - P; } case R_PLT: - return Sym.getPltVA() + A; + // When the CET Branch protection is enabled, the second PLT + // should be call to. + if (Config->SPltSectionEnable) + return Sym.getSPltVA() + A; + else + return Sym.getPltVA() + A; case R_PLT_PC: case R_PPC_CALL_PLT: - return Sym.getPltVA() + A - P; + // When the CET Branch protection is enabled, the second PLT + // should be call to. + if (Config->SPltSectionEnable) + return Sym.getSPltVA() + A - P; + else + return Sym.getPltVA() + A - P; case R_PPC_CALL: { uint64_t SymVA = Sym.getVA(A); // If we have an undefined weak symbol, we might get here with a symbol Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -778,9 +778,12 @@ } template -static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt, - RelocationBaseSection *Rel, RelType Type, Symbol &Sym) { +static void addPltEntry(PltSection *Plt, SPltSection *Splt, + GotPltSection *GotPlt, RelocationBaseSection *Rel, + RelType Type, Symbol &Sym) { Plt->addEntry(Sym); + if (Config->SPltSectionEnable) + Splt->addEntry(Sym); GotPlt->addEntry(Sym); Rel->addReloc( {Type, GotPlt, Sym.getGotPltOffset(), !Sym.IsPreemptible, &Sym, 0}); @@ -972,7 +975,7 @@ "' cannot be preempted; recompile with -fPIE" + getLocation(Sec, Sym, Offset)); if (!Sym.isInPlt()) - addPltEntry(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym); + addPltEntry(In.Plt, In.Splt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym); if (!Sym.isDefined()) replaceWithDefined(Sym, In.Plt, getPltEntryOffset(Sym.PltIndex), 0); Sym.NeedsPltAddr = true; @@ -1060,6 +1063,14 @@ Expr = fromPlt(Expr); } + // To Be Done. + // Now the IBT can't work with IFUNC feature, becasue they may conflict in + // changing the PLT. So we disable the IBT when the IFUNC enabled. + if (Sym.isGnuIFunc()) { + Config->X86Feature1AND &= ~GNU_PROPERTY_X86_FEATURE_1_IBT; + Config->SPltSectionEnable = false; + } + // This relocation does not require got entry, but it is relative to got and // needs it to be created. Here we request for that. if (oneof(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym); + addPltEntry(In.Plt, In.Splt, In.GotPlt, In.RelaPlt, + Target->PltRel, Sym); // Create a GOT slot if a relocation needs GOT. if (needsGot(Expr)) { @@ -1158,8 +1170,8 @@ // that's really needed to create the IRELATIVE is the section and value, // so ideally we should just need to copy those. auto *DirectSym = make(cast(Sym)); - addPltEntry(In.Iplt, In.IgotPlt, In.RelaIplt, Target->IRelativeRel, - *DirectSym); + addPltEntry(In.Iplt, In.Splt, In.IgotPlt, In.RelaIplt, + Target->IRelativeRel, *DirectSym); Sym.PltIndex = DirectSym->PltIndex; } if (Expr == R_ABS && Addend == 0 && (Sec.Flags & SHF_WRITE)) { Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -171,6 +171,7 @@ uint64_t getGotPltOffset() const; uint64_t getGotPltVA() const; uint64_t getPltVA() const; + uint64_t getSPltVA() const; uint64_t getPPC64LongBranchTableVA() const; uint64_t getPPC64LongBranchOffset() const; uint64_t getSize() const; Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -172,6 +172,10 @@ return OutVA; } +uint64_t Symbol::getSPltVA() const { + return In.Splt->getVA() + PltIndex * Target->PltEntrySize; +} + uint64_t Symbol::getPPC64LongBranchTableVA() const { assert(PPC64BranchltIndex != 0xffff); return In.PPC64LongBranchTarget->getVA() + Index: ELF/SyntheticSections.h =================================================================== --- ELF/SyntheticSections.h +++ ELF/SyntheticSections.h @@ -652,7 +652,8 @@ // The PltSection is used for both the Plt and Iplt. The former usually has a // header as its first entry that is used at run-time to resolve lazy binding. // The latter is used for GNU Ifunc symbols, that will be subject to a -// Target->IRelativeRel. +// Target->IRelativeRel. It is also used for the primary part of the IBT-enabled +// PLT, also containing the lazy binding header. class PltSection : public SyntheticSection { public: PltSection(bool IsIplt); @@ -670,6 +671,20 @@ bool IsIplt; }; +// The SPltSection is used in IBT-enabled PLTs to represent the second table +// that is referenced by the GOT. +class SPltSection : public SyntheticSection { +public: + SPltSection(); + void writeTo(uint8_t *Buf) override; + size_t getSize() const override; + bool empty() const override { return Entries.empty(); } + + template void addEntry(Symbol &Sym); +private: + std::vector> Entries; +}; + class GdbIndexSection final : public SyntheticSection { public: struct AddressEntry { @@ -1015,6 +1030,7 @@ MipsRldMapSection *MipsRldMap; PltSection *Plt; PltSection *Iplt; + SPltSection *Splt; RelocationBaseSection *RelaDyn; RelrBaseSection *RelrDyn; RelocationBaseSection *RelaPlt; Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -2375,6 +2375,32 @@ return IsIplt ? In.Plt->getSize() : 0; } +// The SPLT is only used by X86_64. +SPltSection::SPltSection() + : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, ".splt") {} + +void SPltSection::writeTo(uint8_t *Buf) { + size_t Off = 0; + for (auto &I : Entries) { + const Symbol *B = I.first; + uint64_t Got = B->getGotPltVA(); + uint64_t Plt = this->getVA() + Off; + Target->writeSPlt(Buf + Off, Got, Plt); + Off += Target->PltEntrySize; // The Splt size is the same with Plt size + } +} + +template void SPltSection::addEntry(Symbol &Sym) { + RelocationBaseSection *PltRelocSection = In.RelaPlt; + unsigned RelOff = + static_cast *>(PltRelocSection)->getRelocOffset(); + Entries.push_back(std::make_pair(&Sym, RelOff)); +} + +size_t SPltSection::getSize() const { + return Entries.size() * Target->PltEntrySize; +} + // The string hash function for .gdb_index. static uint32_t computeGdbHash(StringRef S) { uint32_t H = 0; @@ -3182,6 +3208,11 @@ template void PltSection::addEntry(Symbol &Sym); template void PltSection::addEntry(Symbol &Sym); +template void SPltSection::addEntry(Symbol &Sym); +template void SPltSection::addEntry(Symbol &Sym); +template void SPltSection::addEntry(Symbol &Sym); +template void SPltSection::addEntry(Symbol &Sym); + template void MipsGotSection::build(); template void MipsGotSection::build(); template void MipsGotSection::build(); Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -42,6 +42,8 @@ virtual void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const {} + virtual void writeSPlt(uint8_t *Buf, uint64_t GotEntryAddr, + uint64_t PltEntryAddr) const {} virtual void addPltHeaderSymbols(InputSection &IS) const {} virtual void addPltSymbols(InputSection &IS, uint64_t Off) const {} @@ -205,6 +207,10 @@ return Target->PltHeaderSize + Target->PltEntrySize * Idx; } +inline unsigned getSPltEntryOffset(unsigned Idx) { + return Target->PltEntrySize * Idx; +} + // Make sure that V can be represented as an N bit signed integer. inline void checkInt(uint8_t *Loc, int64_t V, int N, RelType Type) { if (V != llvm::SignExtend64(V, N)) Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -54,6 +54,7 @@ void resolveShfLinkOrder(); void maybeAddThunks(); void sortInputSections(); + void emitAggregateMetadata(); void finalizeSections(); void checkExecuteOnly(); void setReservedSymbolSections(); @@ -84,6 +85,13 @@ uint64_t FileSize; uint64_t SectionHeaderOff; + + // The Writer will deal with the InputSection's data array which is just a + // point in class ArrayRef, so the caller should initialize the data array + // or there may be an overwite to data array if it's initialized in + // InputSection's method locally. + using ElfWordT = typename ELFT::Word; + SmallVector DataNote; }; } // anonymous namespace @@ -409,6 +417,10 @@ Add(In.Plt); In.Iplt = make(true); Add(In.Iplt); + if (Config->SPltSectionEnable) { + In.Splt = make(); + Add(In.Splt); + } // .note.GNU-stack is always added when we are creating a re-linkable // object file. Other linkers are using the presence of this marker @@ -443,6 +455,12 @@ // The main function of the writer. template void Writer::run() { + // Emit the custom-aggregated metadata sections from the Config values. But + // Until now, the emitAggregateMetadata just deal with the CET info, and + // only X86_64, i386 support the CET. + if (Config->EMachine == EM_X86_64 || Config->EMachine == EM_386) + emitAggregateMetadata(); + // Create linker-synthesized sections such as .got or .plt. // Such sections are of type input section. createSyntheticSections(); @@ -1627,6 +1645,33 @@ return true; } +// Emit aggregated metadata sections like .note.gnu.property. +template void Writer::emitAggregateMetadata() { + using ElfWordT = typename ELFT::Word; + if (!Config->X86Feature1AND) + return; + + int EntrySize = Config->Is64 ? 32 : 28; + DataNote.resize(Config->Is64 ? 8 : 7); + DataNote[0] = 4; + DataNote[1] = 8 + (Config->Is64 ? 8 : 4); + DataNote[2] = ELF::NT_GNU_PROPERTY_TYPE_0; + if (!sys::IsBigEndianHost) + DataNote[3] = 'G' + ('N' << 8) + ('U' << 16); + else + DataNote[3] = ('G' << 24) + ('N' << 16) + ('U' << 8); + DataNote[4] = ELF::GNU_PROPERTY_X86_FEATURE_1_AND; + DataNote[5] = 4; + DataNote[6] = Config->X86Feature1AND; + ArrayRef DataArr = ArrayRef(DataNote); + ArrayRef SecData = + llvm::makeArrayRef((const uint8_t *)DataArr.data(), EntrySize); + auto *Sec = make(nullptr, SHF_ALLOC, SHT_NOTE, Config->Is64 ? 8 : 4, + SecData, ".note.gnu.property"); + Sec->Live = true; + InputSections.push_back(Sec); +} + // Create output section objects and add them to OutputSections. template void Writer::finalizeSections() { Out::PreinitArray = findSection(".preinit_array"); @@ -1803,6 +1848,7 @@ finalizeSynthetic(In.RelaPlt); finalizeSynthetic(In.Plt); finalizeSynthetic(In.Iplt); + finalizeSynthetic(In.Splt); finalizeSynthetic(In.EhFrameHdr); finalizeSynthetic(InX::VerSym); finalizeSynthetic(InX::VerNeed); Index: docs/ld.lld.1 =================================================================== --- docs/ld.lld.1 +++ docs/ld.lld.1 @@ -480,6 +480,8 @@ Stack permissions are recorded in the .Dv PT_GNU_STACK segment. +.It Cm force-cet +Require all the input files must enabled CET before link. .It Cm global Sets the .Dv DF_1_GLOBAL flag in the Index: test/ELF/Inputs/i386-feature-1-and0.s =================================================================== --- /dev/null +++ test/ELF/Inputs/i386-feature-1-and0.s @@ -0,0 +1,5 @@ + .text + .globl func2 + .type func2,@function +func2: + ret Index: test/ELF/Inputs/i386-feature-1-and1.s =================================================================== --- /dev/null +++ test/ELF/Inputs/i386-feature-1-and1.s @@ -0,0 +1,28 @@ + .section ".note.gnu.property", "a" + + .p2align 2 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 2 + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x00000003 +3: + + .p2align 2 + +4: + + .text + .globl func2 + .type func2,@function +func2: + ret Index: test/ELF/Inputs/i386-feature-1-and1x.s =================================================================== --- /dev/null +++ test/ELF/Inputs/i386-feature-1-and1x.s @@ -0,0 +1,28 @@ + .section ".note.gnu.property", "a" + + .p2align 2 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 2 + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0xfffffffd +3: + + .p2align 2 + +4: + + .text + .globl func2 + .type func2,@function +func2: + ret Index: test/ELF/Inputs/i386-feature-1-and2.s =================================================================== --- /dev/null +++ test/ELF/Inputs/i386-feature-1-and2.s @@ -0,0 +1,37 @@ + .section ".note.gnu.property", "a" + + .p2align 2 + + .long 1f - 0f + .long 7f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 2 + + .long 0xc0000000 + .long 3f - 2f +2: + .long 0x00000000 +3: + + .p2align 2 + +4: + .long 0xc0000002 + .long 6f - 5f +5: + .long 0x00000003 +6: + + .p2align 2 + +7: + + .text + .globl func2 + .type func2,@function +func2: + ret Index: test/ELF/Inputs/i386-feature-1-and2s.s =================================================================== --- /dev/null +++ test/ELF/Inputs/i386-feature-1-and2s.s @@ -0,0 +1,51 @@ + .section ".note.gnu.property", "a" + + .p2align 2 + + .long 1f - 0f + .long 4f -1f + .long 0x4 +0: + .asciz "GNU" +1: + + .p2align 2 + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x00000003 +3: + + .p2align 2 + +4: + .section ".note.gnu.property", "a" + + .p2align 2 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 2 + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x00000002 +3: + + .p2align 2 + +4: + + + .text + .globl func2 + .type func2,@function +func2: + ret Index: test/ELF/Inputs/x86-64-feature-1-and0.s =================================================================== --- /dev/null +++ test/ELF/Inputs/x86-64-feature-1-and0.s @@ -0,0 +1,5 @@ + .text + .globl func2 + .type func2,@function +func2: + retq Index: test/ELF/Inputs/x86-64-feature-1-and1.s =================================================================== --- /dev/null +++ test/ELF/Inputs/x86-64-feature-1-and1.s @@ -0,0 +1,27 @@ + .section ".note.gnu.property", "a" + + .p2align 3 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 3 + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x00000003 +3: + + .p2align 3 + +4: + .text + .globl func2 + .type func2,@function +func2: + retq Index: test/ELF/Inputs/x86-64-feature-1-and1x.s =================================================================== --- /dev/null +++ test/ELF/Inputs/x86-64-feature-1-and1x.s @@ -0,0 +1,27 @@ + .section ".note.gnu.property", "a" + + .p2align 3 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 3 + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0xfffffffd +3: + + .p2align 3 + +4: + .text + .globl func2 + .type func2,@function +func2: + retq Index: test/ELF/Inputs/x86-64-feature-1-and2.s =================================================================== --- /dev/null +++ test/ELF/Inputs/x86-64-feature-1-and2.s @@ -0,0 +1,32 @@ + .section ".note.gnu.property", "a" + + .p2align 3 + + .long 1f - 0f + .long 7f -1f + .long 0x5 +0: + .asciz "GNU" +1: + .p2align 3 + .long 0xc0000000 + .long 3f - 2f +2: + .long 0x00000001 +3: + .p2align 3 + +4: + .long 0xc0000002 + .long 6f - 5f +5: + .long 0x00000003 +6: + .p2align 3 + +7: + .text + .globl func2 + .type func2,@function +func2: + retq Index: test/ELF/Inputs/x86-64-feature-1-and2s.s =================================================================== --- /dev/null +++ test/ELF/Inputs/x86-64-feature-1-and2s.s @@ -0,0 +1,47 @@ + .section ".note.gnu.property", "a" + + .p2align 3 + + .long 1f - 0f + .long 4f -1f + .long 0x4 +0: + .asciz "GNU" +1: + .p2align 3 + .long 0xc0000000 + .long 3f - 2f +2: + .long 0x00000001 +3: + .p2align 3 + +4: + + .section ".note.gnu.property", "a" + + .p2align 3 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + .p2align 3 + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x00000002 +3: + .p2align 3 + +4: + + + + .text + .globl func2 + .type func2,@function +func2: + retq Index: test/ELF/i386-feature-1-and.s =================================================================== --- /dev/null +++ test/ELF/i386-feature-1-and.s @@ -0,0 +1,102 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-feature-1-and1.s -o %t2.o +# RUN: ld.lld -e func1 %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP1 %s +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC1 %s + +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-feature-1-and0.s -o %t2.o +# RUN: ld.lld -e func1 %t1.o %t2.o -o %t +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC0 %s + +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-feature-1-and1x.s -o %t2.o +# RUN: ld.lld -e func1 %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP1X %s +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC1X %s + +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-feature-1-and2.s -o %t2.o +# RUN: ld.lld -e func1 %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP2 %s +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC2 %s + +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-feature-1-and2s.s -o %t2.o +# RUN: ld.lld -e func1 %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP2S %s +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC2S %s + +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-feature-1-and1.s -o %t2.o +# RUN: ld.lld -shared %t2.o -o %t2.so +# RUN: ld.lld -e func1 %t1.o %t2.so -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGPSPLT %s +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SECSPLT %s +# RUN: llvm-objdump -d %t | FileCheck --check-prefix=DMPSPLT %s + +# Check .note.gnu.protery +# SEC1: .note.gnu.property +# NGP1: NT_GNU_PROPERTY_TYPE_0 +# NGP1: IBT +# NGP1: SHSTK + +# Check no .note.gnu.protery +# SEC0: Section Headers +# SEC0-NOT: .note.gnu.property + +# Check .note.gnu.protery without property +# SEC1X: .note.gnu.property +# NGP1X: NT_GNU_PROPERTY_TYPE_0 +# NGP1X: IBT +# NGP1X-NOT: SHSTK + +# Check .note.gnu.protery +# SEC2: .note.gnu.property +# NGP2: NT_GNU_PROPERTY_TYPE_0 +# NGP2: IBT +# NGP2: SHSTK + +# Check .note.gnu.protery +# SEC2S: .note.gnu.property +# NGP2S: NT_GNU_PROPERTY_TYPE_0 +# NGP2S-NOT: IBT +# NGP2S: SHSTK + +# Check .note.gnu.protery and splt +# SECSPLT: .note.gnu.property +# NGPSPLT: NT_GNU_PROPERTY_TYPE_0 +# NGPSPLT: IBT +# NGPSPLT: SHSTK +# DMPSPLT: splt + + .section ".note.gnu.property", "a" + + .p2align 2 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 2 + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x00000003 +3: + + .p2align 2 + +4: + + .text + .globl func1 + .type func1,@function +func1: + call func2 + ret Index: test/ELF/x86-64-feature-1-and.s =================================================================== --- /dev/null +++ test/ELF/x86-64-feature-1-and.s @@ -0,0 +1,105 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-feature-1-and1.s -o %t2.o +# RUN: ld.lld -e func1 %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP1 %s +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC1 %s + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-feature-1-and0.s -o %t2.o +# RUN: ld.lld -e func1 %t1.o %t2.o -o %t +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC0 %s + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-feature-1-and1x.s -o %t2.o +# RUN: ld.lld -e func1 %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP1X %s +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC1X %s + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-feature-1-and2.s -o %t2.o +# RUN: ld.lld -e func1 %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP2 %s +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC2 %s + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-feature-1-and2s.s -o %t2.o +# RUN: ld.lld -e func1 %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP2S %s +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC2S %s + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-feature-1-and1.s -o %t2.o +# RUN: ld.lld -shared %t2.o -o %t2.so +# RUN: ld.lld -e func1 %t1.o %t2.so -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGPSPLT %s +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SECSPLT %s +# RUN: llvm-objdump -d %t | FileCheck --check-prefix=DMPSPLT %s + +# Check .note.gnu.protery +# SEC1: .note.gnu.property +# NGP1: NT_GNU_PROPERTY_TYPE_0 +# NGP1: IBT +# NGP1: SHSTK + +# Check no .note.gnu.protery +# SEC0: Section Headers +# SEC0-NOT: .note.gnu.property + +# Check .note.gnu.protery without property +# SEC1X: .note.gnu.property +# NGP1X: NT_GNU_PROPERTY_TYPE_0 +# NGP1X: IBT +# NGP1X-NOT: SHSTK + +# Check .note.gnu.protery +# SEC2: .note.gnu.property +# NGP2: NT_GNU_PROPERTY_TYPE_0 +# NGP2: IBT +# NGP2: SHSTK + +# Check .note.gnu.protery +# SEC2S: .note.gnu.property +# NGP2S: NT_GNU_PROPERTY_TYPE_0 +# NGP2S-NOT: IBT +# NGP2S: SHSTK + +# Check .note.gnu.protery and splt +# SECSPLT: .note.gnu.property +# NGPSPLT: NT_GNU_PROPERTY_TYPE_0 +# NGPSPLT: IBT +# NGPSPLT: SHSTK +# DMPSPLT: splt + + .section ".note.gnu.property", "a" + + .p2align 3 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 3 + + + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x00000003 +3: + + .p2align 3 + + + +4: + .text + .globl func1 + .type func1,@function +func1: + callq func2 + retq