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, @@ -170,9 +172,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 { @@ -218,6 +226,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 @@ -240,6 +263,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, @@ -139,8 +141,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 { @@ -160,6 +169,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 @@ -172,6 +196,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,6 +184,7 @@ bool WarnMissingEntry; bool WarnSymbolOrdering; bool WriteAddends; + bool SPltSectionEnable = false; bool ZCombreloc; bool ZCopyreloc; bool ZExecstack; @@ -214,6 +215,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]); @@ -1439,6 +1441,403 @@ #undef HANDLE_LIBCALL }; +enum X86F1ANDForm { + X86F1ANDSingle, // The section just contains X86F1AND info. + X86F1ANDMerged, // The section contains X86F1AND info and other info. + X86F1ANDNone // There is no X86F1AND info in this section. +}; + +// X86Feature1AND information would be merged with other .note.gnu.property +// sections or other 'desc'. So, if we need to read or delete them, we need +// to know where and how long it is. The structure of X86F1AND section can +// be refer to the comment of findGNUPropertyX86Feature1AND. +struct X86F1ANDLocType { + // The X86F1AND section's (or subsection's) position in the + // .note.gnu.property section. + unsigned SecPos; + + // In format A it is the distance of X86F1AND section head's starting point + // from the whole section head's, but in format B it is distance of X86F1AND + // section desc from the whole section starting point. + // * Form A: Form B: + // * ___________________________ _________________________________ + // * | | | | | | |(F1AND)| | + // * | ... | head | desc | | ... | head | desc1 | desc2 | + // * |________|_______|________| |________|______|_______|_______| + // * ^ ^ + // * I-----StartPos I------------StartPos + // * I------Size------I I-Size--I + unsigned StartPos; + + // The size of X86F1AND info, It should contains the size of X86F1AND section + // head if IsHeadMerge = false (format A). + unsigned Size; + bool IsHeadMerge; // One section head with more than one desc(format B). +}; + +// 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, + X86F1ANDLocType &X86F1ANDLoca) { + // TODO: for now, we assume the section is well-formed with one note array. + using ElfWordT = typename ELFT::Word; + auto Data = S->getDataAs(); + + unsigned pr_typeIndex = 4; // Offset of pr_type from start of 'head' + unsigned pr_dataIndex = 6; // Offset of pr_data from start of 'head' + + unsigned Offset = X86F1ANDLoca.StartPos; + + if (X86F1ANDLoca.IsHeadMerge) { + // Now we get the pr_type and pr_data offset from 'desc'. + pr_typeIndex = 0; + pr_dataIndex = 2; + } + + // 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; +} + +// When we find the X86Feature1AND info, we collect it into X86F1ANDLoca. +// CurSecOffset: The current section head offset in the .note. +// DescOffset: The offset of current desc in current section. +// IsHeadMerge: is format B or not. (Refer findGNUPropertyX86Feature1AND.) +template +static X86F1ANDLocType getX86Feature1ANDLocation(InputSectionBase *S, + unsigned CurSecOffset, + unsigned DescOffset, + bool IsHeadMerge) +{ + using ElfWordT = typename ELFT::Word; + auto Data = S->getDataAs(); + X86F1ANDLocType X86F1ANDLoca; + + // Offset of pr_datasz from start of 'desc' + 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' + + X86F1ANDLoca.IsHeadMerge = IsHeadMerge; + X86F1ANDLoca.SecPos = CurSecOffset; + unsigned Size = 0; + if (IsHeadMerge) { + X86F1ANDLoca.StartPos = CurSecOffset + DescOffset; + Size = Data[X86F1ANDLoca.StartPos + pr_dataszInDescIndex]; + Size += X86F1ANDLoca.StartPos * 4 + DescFixedSize; + } else { + X86F1ANDLoca.StartPos = CurSecOffset; + Size = Data[CurSecOffset + DescOffset + pr_dataszInDescIndex]; + Size += X86F1ANDLoca.StartPos * 4 + HeadSize + DescFixedSize; + } + Size = ALIGN_UP (Size, S->Alignment); + X86F1ANDLoca.Size = Size - X86F1ANDLoca.StartPos * 4; + return X86F1ANDLoca; +} + +// This function check if there is X86Feature1AND section or '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 X86F1ANDLoca. +// +// 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 | +// |_______|________| |______|_______|_______| +// +// I-not headMerged-I I------headMerged------I +// I-X86F1ANDSingle-I I----X86F1ANDMerged----I +// +// (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----- +// I-not headMerged-I--------headMerged----- +// I-------------X86F1ANDMergedd------------ +// +// Function return: +// 1 Return X86F1ANDSingle if the .noto.gnu.property section just contains +// one section with Form A. +// 2 Return X86F1ANDMerged if the .note.gnu.property section contains +// X86Feature1AND section with form B or form MIX. +// 3 Return X86F1ANDNone if the .note.gnu.property section do not contains +// X86Feature1AND info. +template +static X86F1ANDForm findGNUPropertyX86Feature1AND(InputSectionBase *S, + X86F1ANDLocType &X86F1ANDLoca) { + 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) { + unsigned MaxDescSize = DescFixedSize + + Data[CurSecOffset + DescOffset + pr_dataszInDescIndex]; + MaxDescSize = ALIGN_UP (MaxDescSize, S->Alignment); + + // This is a data merged section. (format B) + if (MaxDescSize < Data[CurSecOffset + n_descszIndex]) { + X86F1ANDLoca = getX86Feature1ANDLocation(S, CurSecOffset, + DescOffset, true); + return X86F1ANDMerged; + } else { // This is not data merged section. (format A) + X86F1ANDLoca = getX86Feature1ANDLocation(S, CurSecOffset, + DescOffset, false); + if (Data[n_descszIndex] + HeadSize < SecSize) + // X86F1ANDMerged does not equal with data merged, it just means the + // .note.gnu.property section also contain more than one subsection + // or one head tail with more than one desc. + return X86F1ANDMerged; + else + return X86F1ANDSingle; + } + } 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)) { + X86F1ANDLoca = getX86Feature1ANDLocation(S, CurSecOffset, + NextDescOffset, true); + if (Data[n_descszIndex] + HeadSize < SecSize) + return X86F1ANDMerged; + else + return X86F1ANDSingle; + } + 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 X86F1ANDNone; +} + +// Remove the old X86F1AND info in a merged section. +template +static void eraseGNUProperty(InputSectionBase *S, X86F1ANDLocType &X86F1ANDLoca, + std::vector&DataAllocated) { + // The offset of n_descsz is fixed in .note sections by ABI. + const unsigned n_descszIndex = 1; + size_t SecSize = S->data().size(); + uint8_t *NewData = new uint8_t[SecSize - X86F1ANDLoca.Size]; + if (NewData == nullptr) { + error("NewData allocated failled!"); + return; + } + DataAllocated.push_back(NewData); + + // Now we know the position and size of X86F1AND from X86F1ANDLoca, it's + // time to delete. + for (size_t i = 0; i < X86F1ANDLoca.StartPos*4; i++) + NewData[i] = S->data()[i]; + for (size_t i = X86F1ANDLoca.StartPos*4; i + X86F1ANDLoca.Size < SecSize; i++) + NewData[i] = S->data()[i + X86F1ANDLoca.Size]; + + ArrayRef DataArr = ArrayRef(NewData, + SecSize - X86F1ANDLoca.Size); + S->setData(DataArr); + + // Update the size of n_descsz in the data merged section. + if (X86F1ANDLoca.IsHeadMerge) { + uint8_t *PtrDescsz = const_cast(S->data().begin()); + PtrDescsz += (X86F1ANDLoca.SecPos + n_descszIndex) * 4; + + // We can make sure the PtrDescsz is align to 4. + *((uint32_t *)(PtrDescsz)) -= X86F1ANDLoca.Size; + } +} + +template +inline static X86F1ANDForm checkX86Feature1ANDSection(InputSectionBase *S, + X86F1ANDLocType &X86F1ANDLoca){ + if (S->Type != SHT_NOTE) + return X86F1ANDNone; + X86F1ANDForm Form; + Form = findGNUPropertyX86Feature1AND(S, X86F1ANDLoca); + return Form; +} + +// 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; + std::vector< std::pair< InputSectionBase *, X86F1ANDLocType > > + X86F1ANDMergedSections; + bool IsAllFileF1AND = true; + Config->SPltSectionEnable = true; + Config->X86Feature1AND = 0xFFFFFFFF; + for (InputFile *F : ObjectFiles) { + X86F1ANDLocType X86F1ANDLoca = {0,0,0,false}; + X86F1ANDForm form = X86F1ANDNone; + 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) + return false; + form = checkX86Feature1ANDSection(S, X86F1ANDLoca); + return form != X86F1ANDNone; + }); + 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; + } else { + readGNUPropertyX86Feature1AND(*I, Config, X86F1ANDLoca); + if (form == X86F1ANDSingle) + X86F1ANDSections.push_back(*I); + else if (form == X86F1ANDMerged) + X86F1ANDMergedSections.push_back(std::make_pair(*I, X86F1ANDLoca)); + } + } + if (!IsAllFileF1AND) { + Config->SPltSectionEnable = false; + Config->X86Feature1AND = 0x0; + } + llvm::erase_if(InputSections, [=, &X86F1ANDSections](InputSectionBase *S) { + if (llvm::find (X86F1ANDSections, S) != X86F1ANDSections.end()) + return true; + else + return false; + }); + + // X86Feature1AND info would be merged with other .note.gnu.property sections + // we should 'delete' it manully. + for (auto MapIter : X86F1ANDMergedSections){ + eraseGNUProperty(MapIter.first, MapIter.second, DataAllocated); + } + + // The resulting X86Feature1AND section will be created by the Writer + // if necessary. +} + +// We may allocate the mem for the section in function eraseGNUProperty. +// Now release them. +template void LinkerDriver::releaseResources() { + for (uint8_t *Ptr : DataAllocated) { + assert(Ptr); + delete[] Ptr; + } +} + // 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 +2010,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) { @@ -1648,4 +2054,5 @@ // Write the result to the file. writeResult(); + releaseResources(); } 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 @@ -707,10 +707,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 @@ -756,9 +756,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}); @@ -957,7 +960,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; @@ -1033,6 +1036,12 @@ getLocation(Sec, Sym, Offset)); } Expr = toPlt(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. + Config->X86Feature1AND &= ~GNU_PROPERTY_X86_FEATURE_1_IBT; + Config->SPltSectionEnable = false; } else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) { Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr); } else if (!Sym.IsPreemptible) { @@ -1059,10 +1068,11 @@ // If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol. if (needsPlt(Expr) && !Sym.isInPlt()) { if (Sym.isGnuIFunc() && !Sym.IsPreemptible) - addPltEntry(In.Iplt, In.IgotPlt, In.RelaIplt, Target->IRelativeRel, - Sym); + addPltEntry(In.Iplt, In.Splt, In.IgotPlt, In.RelaIplt, + Target->IRelativeRel, Sym); else - addPltEntry(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. 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 @@ -148,6 +148,10 @@ return Plt->getVA() + Plt->HeaderSize + PltIndex * Target->PltEntrySize; } +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 { @@ -1017,6 +1032,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 @@ -2374,6 +2374,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; @@ -3181,6 +3207,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(); @@ -83,6 +84,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 @@ -412,6 +420,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 @@ -446,6 +458,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(); @@ -1630,6 +1648,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"); @@ -1795,6 +1840,7 @@ finalizeSynthetic(In.RelaPlt); finalizeSynthetic(In.Plt); finalizeSynthetic(In.Iplt); + finalizeSynthetic(In.Splt); finalizeSynthetic(In.EhFrameHdr); finalizeSynthetic(InX::VerSym); finalizeSynthetic(InX::VerNeed); 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