Skip to content

Commit 8873a15

Browse files
committed
handle GNU properties and build attributes after loop
1 parent 64a45ed commit 8873a15

File tree

1 file changed

+117
-126
lines changed

1 file changed

+117
-126
lines changed

lld/ELF/InputFiles.cpp

Lines changed: 117 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,89 @@ static gnuPropertiesInfo readGnuProperty(Ctx &ctx, const InputSection &sec,
682682
return gnuPropertiesInfo{f.andFeatures, f.aarch64PauthAbiCoreInfo};
683683
}
684684

685+
template <class ELFT>
686+
static void
687+
handleAArch64BAAndGnuProperties(const ELFT &tPointer, Ctx &ctx, bool isBE,
688+
bool hasBA, bool hasGP,
689+
const AArch64BuildAttrSubsections &baInfo,
690+
const gnuPropertiesInfo &gpInfo) {
691+
if (hasBA && hasGP) {
692+
if (!gpInfo.aarch64PauthAbiCoreInfo.empty()) {
693+
// check for a mismatch
694+
auto deserializeArray = [&](size_t offset, bool isBE) {
695+
unsigned value = 0;
696+
for (size_t i = 0; i < 8; ++i) {
697+
if (isBE)
698+
value = (value << 8) | gpInfo.aarch64PauthAbiCoreInfo[i + offset];
699+
else
700+
value |= static_cast<uint64_t>(
701+
gpInfo.aarch64PauthAbiCoreInfo[i + offset])
702+
<< (8 * i);
703+
};
704+
return value;
705+
};
706+
unsigned gnuPropPauthPlatform = deserializeArray(0, isBE);
707+
if (baInfo.pauth.tagPlatform != gnuPropPauthPlatform)
708+
ErrAlways(ctx)
709+
<< tPointer
710+
<< "Pauth Platform mismatch: file contains both GNU properties and "
711+
"AArch64 build attributes sections\nGNU properties: "
712+
<< gnuPropPauthPlatform
713+
<< "\nAArch64 build attributes: " << baInfo.pauth.tagPlatform;
714+
unsigned gnuPropPauthScheme = deserializeArray(8, isBE);
715+
if (baInfo.pauth.tagSchema != gnuPropPauthScheme)
716+
ErrAlways(ctx)
717+
<< tPointer
718+
<< "Pauth Schema mismatch: file contains both GNU properties and "
719+
"AArch64 build attributes sections\nGNU properties: "
720+
<< gnuPropPauthScheme
721+
<< "\nAArch64 build attributes: " << baInfo.pauth.tagSchema;
722+
}
723+
if (baInfo.fAndB.tagBTI != (gpInfo.andFeatures & 0x01))
724+
ErrAlways(ctx)
725+
<< tPointer
726+
<< "Features BTI mismatch: file contains both GNU properties and "
727+
"AArch64 build attributes sections\nGNU properties: "
728+
<< (gpInfo.andFeatures & 0x01)
729+
<< "\nAArch64 build attributes: " << baInfo.fAndB.tagBTI;
730+
if (baInfo.fAndB.tagPAC != ((gpInfo.andFeatures >> 1) & 0x01))
731+
ErrAlways(ctx)
732+
<< tPointer
733+
<< "Feature PAC mismatch: file contains both GNU properties and "
734+
"AArch64 build attributes sections\nGNU properties: "
735+
<< ((gpInfo.andFeatures >> 1) & 0x01)
736+
<< "\nAArch64 build attributes: " << baInfo.fAndB.tagPAC;
737+
if (baInfo.fAndB.tagGCS != ((gpInfo.andFeatures >> 2) & 0x01))
738+
ErrAlways(ctx)
739+
<< tPointer
740+
<< "Feature GCS mismatch: file contains both GNU properties and "
741+
"AArch64 build attributes sections\nGNU properties: "
742+
<< ((gpInfo.andFeatures >> 2) & 0x01)
743+
<< "\nAArch64 build attributes: " << baInfo.fAndB.tagGCS;
744+
}
745+
746+
if (hasBA && !hasGP) {
747+
if (baInfo.pauth.tagPlatform || baInfo.pauth.tagSchema) {
748+
auto serializeUnsigned = [&](unsigned value, size_t offset, bool isBE) {
749+
for (size_t i = 0; i < 8; ++i) {
750+
tPointer->aarch64PauthAbiCoreInfoStorage[i + offset] =
751+
static_cast<uint8_t>(
752+
(static_cast<uint64_t>(value) >> (8 * (isBE ? (7 - i) : i))) &
753+
0xFF);
754+
};
755+
};
756+
serializeUnsigned(baInfo.pauth.tagPlatform, 0, isBE);
757+
serializeUnsigned(baInfo.pauth.tagSchema, 8, isBE);
758+
tPointer->aarch64PauthAbiCoreInfo =
759+
tPointer->aarch64PauthAbiCoreInfoStorage;
760+
}
761+
tPointer->andFeatures = 0;
762+
tPointer->andFeatures |= (baInfo.fAndB.tagBTI) << 0;
763+
tPointer->andFeatures |= (baInfo.fAndB.tagPAC) << 1;
764+
tPointer->andFeatures |= (baInfo.fAndB.tagGCS) << 2;
765+
}
766+
}
767+
685768
template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
686769
object::ELFFile<ELFT> obj = this->getObj();
687770
// Read a section table. justSymbols is usually false.
@@ -690,21 +773,27 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
690773
initializeSymbols(obj);
691774
return;
692775
}
693-
694776
// Handle dependent libraries and selection of section groups as these are not
695777
// done in parallel.
696778
ArrayRef<Elf_Shdr> objSections = getELFShdrs<ELFT>();
697779
StringRef shstrtab = CHECK2(obj.getSectionStringTable(objSections), this);
698780
uint64_t size = objSections.size();
699781
sections.resize(size);
700782

701-
// Check whether GNU properties section present and store it's data.
702-
// This is done in order to compare the content of GNU properties section with
703-
// aarch64 build attributes section.
704-
bool hasGnuProperties = false;
783+
// For handling AArch64 Build attributes and GNU properties
784+
AArch64BuildAttrSubsections aarch64BAsubSections;
705785
gnuPropertiesInfo gnuPropertiesInformation;
786+
bool hasAArch64BuildAttributes = false;
787+
bool hasGNUProperties = false;
788+
706789
for (size_t i = 0; i != size; ++i) {
707790
const Elf_Shdr &sec = objSections[i];
791+
792+
// Collect GNU properties data.
793+
// GNU properties might be processed in this loop, or only later on (e.g. in
794+
// initializeSections) Therefore there is no guarantee that the GNU
795+
// properties data will be read after this loop end, so it is being
796+
// collected here.
708797
if (check(obj.getSectionName(sec, shstrtab)) == ".note.gnu.property") {
709798
if (0 == this->andFeatures && this->aarch64PauthAbiCoreInfo.empty()) {
710799
gnuPropertiesInformation = readGnuProperty(
@@ -719,13 +808,9 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
719808
gnuPropertiesInformation.aarch64PauthAbiCoreInfo =
720809
this->aarch64PauthAbiCoreInfo;
721810
}
722-
hasGnuProperties = true;
723-
break;
811+
hasGNUProperties = true;
724812
}
725-
}
726813

727-
for (size_t i = 0; i != size; ++i) {
728-
const Elf_Shdr &sec = objSections[i];
729814
if (LLVM_LIKELY(sec.sh_type == SHT_PROGBITS))
730815
continue;
731816
if (LLVM_LIKELY(sec.sh_type == SHT_GROUP)) {
@@ -809,130 +894,27 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
809894
}
810895
break;
811896
case EM_AARCH64: {
812-
// The specification states that if a file contains both GNU properties
813-
// and AArch64 build attributes, they can be assumed to be identical.
814-
// Therefore, if a file contains GNU properties, the AArch64 build
815-
// attributes are ignored. If a file does not contain GNU properties, we
816-
// leverage the existing GNU properties mechanism by populating the
817-
// corresponding data structures, which will later be handled by
818-
// Driver.cpp::readSecurityNotes. This ensures that AArch64 build
819-
// attributes are represented in the linked object file as GNU properties,
820-
// which are already supported by the Linux kernel and the dynamic
821-
// loader.
897+
// At this stage AArch64 Build Attributes does not replace GNU Properties.
898+
// When both exists, their values must match.
899+
// When both exists and contain different attributes, they complement each
900+
// other. Curently attributes are represented in the linked object file as
901+
// GNU properties, which are already supported by the Linux kernel and the
902+
// dynamic loader. In the future, when relocatable linking (`-r` flag) is
903+
// performed, a single merged AArch64 Build Attributes section will be
904+
// emitted.
905+
822906
if (sec.sh_type == SHT_AARCH64_ATTRIBUTES) {
823-
StringRef name = check(obj.getSectionName(sec, shstrtab));
824907
ArrayRef<uint8_t> contents = check(obj.getSectionContents(sec));
825908
AArch64AttributeParser attributes;
826-
// For functions that has to warn/err/report.
909+
StringRef name = check(obj.getSectionName(sec, shstrtab));
827910
InputSection isec(*this, sec, name);
828911
if (Error e = attributes.parse(contents, ELFT::Endianness)) {
829912
Warn(ctx) << &isec << ": " << std::move(e);
830913
// uint32_t andFeatures = 0;
831914
// std::array<uint8_t, 16> aarch64PauthAbiCoreInfoStorage;
832915
} else {
833-
bool writePauth = false;
834-
bool wrtieFeatures = false;
835-
AArch64BuildAttrSubsections subSections =
836-
extractBuildAttributesSubsections(attributes);
837-
if (hasGnuProperties) {
838-
if (!gnuPropertiesInformation.aarch64PauthAbiCoreInfo.empty()) {
839-
// check for mismatch error
840-
auto deserializeArray = [&](size_t offset, bool isBE) {
841-
unsigned value = 0;
842-
for (size_t i = 0; i < 8; ++i) {
843-
value = isBE ? (value << 8) |
844-
gnuPropertiesInformation
845-
.aarch64PauthAbiCoreInfo[i + offset]
846-
: value |=
847-
static_cast<uint64_t>(
848-
gnuPropertiesInformation
849-
.aarch64PauthAbiCoreInfo[i + offset])
850-
<< (8 * i);
851-
};
852-
return value;
853-
};
854-
unsigned gnuPropPauthPlatform = deserializeArray(
855-
0, ELFT::Endianness == llvm::endianness::big);
856-
if (subSections.pauth.tagPlatform != gnuPropPauthPlatform)
857-
ErrAlways(ctx)
858-
<< &isec
859-
<< "Pauth Platform mismatch: file contains both GNU "
860-
"properties and AArch64 build attributes sections\nGNU "
861-
"properties: "
862-
<< gnuPropPauthPlatform << "\nAArch64 build attributes: "
863-
<< subSections.pauth.tagPlatform;
864-
unsigned gnuPropPauthScheme = deserializeArray(
865-
8, ELFT::Endianness == llvm::endianness::big);
866-
if (subSections.pauth.tagSchema != gnuPropPauthScheme)
867-
ErrAlways(ctx)
868-
<< &isec
869-
<< "Pauth Schema mismatch: file contains both GNU "
870-
"properties and AArch64 build attributes sections\nGNU "
871-
"properties: "
872-
<< gnuPropPauthScheme << "\nAArch64 build attributes: "
873-
<< subSections.pauth.tagSchema;
874-
} else {
875-
writePauth = true;
876-
}
877-
if (subSections.fAndB.tagBTI !=
878-
(gnuPropertiesInformation.andFeatures & 0x01))
879-
ErrAlways(ctx)
880-
<< &isec
881-
<< "Features BTI mismatch: file contains both GNU "
882-
"properties and AArch64 build attributes sections\nGNU "
883-
"properties: "
884-
<< (gnuPropertiesInformation.andFeatures & 0x01)
885-
<< "\nAArch64 build attributes: " << subSections.fAndB.tagBTI;
886-
if (subSections.fAndB.tagPAC !=
887-
((gnuPropertiesInformation.andFeatures >> 1) & 0x01))
888-
ErrAlways(ctx)
889-
<< &isec
890-
<< "Feature PAC mismatch: file contains both GNU "
891-
"properties and AArch64 build attributes sections\nGNU "
892-
"properties: "
893-
<< ((gnuPropertiesInformation.andFeatures >> 1) & 0x01)
894-
<< "\nAArch64 build attributes: " << subSections.fAndB.tagBTI;
895-
if (subSections.fAndB.tagGCS !=
896-
((gnuPropertiesInformation.andFeatures >> 2) & 0x01))
897-
ErrAlways(ctx)
898-
<< &isec
899-
<< "Feature GCS mismatch: file contains both GNU "
900-
"properties and AArch64 build attributes sections\nGNU "
901-
"properties: "
902-
<< ((gnuPropertiesInformation.andFeatures >> 2) & 0x01)
903-
<< "\nAArch64 build attributes: " << subSections.fAndB.tagBTI;
904-
} else {
905-
// no GNU properties
906-
writePauth = true;
907-
wrtieFeatures = true;
908-
}
909-
if (writePauth) {
910-
// this condition is important in order not to emit section when
911-
// data does not exists, same as GNU properties.
912-
if (subSections.pauth.tagPlatform || subSections.pauth.tagSchema) {
913-
auto serializeUnsigned = [&](unsigned value, size_t offset,
914-
bool isBE) {
915-
for (size_t i = 0; i < 8; ++i) {
916-
this->aarch64PauthAbiCoreInfoStorage[i + offset] =
917-
static_cast<uint8_t>((static_cast<uint64_t>(value) >>
918-
(8 * (isBE ? (7 - i) : i))) &
919-
0xFF);
920-
};
921-
};
922-
serializeUnsigned(subSections.pauth.tagPlatform, 0,
923-
ELFT::Endianness == llvm::endianness::big);
924-
serializeUnsigned(subSections.pauth.tagSchema, 8,
925-
ELFT::Endianness == llvm::endianness::big);
926-
this->aarch64PauthAbiCoreInfo =
927-
this->aarch64PauthAbiCoreInfoStorage;
928-
}
929-
}
930-
if (wrtieFeatures) {
931-
this->andFeatures = 0;
932-
this->andFeatures |= (subSections.fAndB.tagBTI) << 0;
933-
this->andFeatures |= (subSections.fAndB.tagPAC) << 1;
934-
this->andFeatures |= (subSections.fAndB.tagGCS) << 2;
935-
}
916+
aarch64BAsubSections = extractBuildAttributesSubsections(attributes);
917+
hasAArch64BuildAttributes = true;
936918
}
937919
sections[i] = &InputSection::discarded;
938920
}
@@ -946,6 +928,15 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
946928
} break;
947929
}
948930
}
931+
932+
bool isBE = ELFT::Endianness == llvm::endianness::big;
933+
// Handle AArch64 Build Attributes and GNU properties:
934+
// - Err on mismatched values.
935+
// - Store missing values as GNU properties.
936+
handleAArch64BAAndGnuProperties(this, ctx, isBE, hasAArch64BuildAttributes,
937+
hasGNUProperties, aarch64BAsubSections,
938+
gnuPropertiesInformation);
939+
949940
// Read a symbol table.
950941
initializeSymbols(obj);
951942
}
@@ -1947,7 +1938,7 @@ BitcodeFile::BitcodeFile(Ctx &ctx, MemoryBufferRef mb, StringRef archiveName,
19471938

19481939
// ThinLTO assumes that all MemoryBufferRefs given to it have a unique
19491940
// name. If two archives define two members with the same name, this
1950-
// causes a collision which result in only one of the objects being taken
1941+
// causes a mismatch which result in only one of the objects being taken
19511942
// into consideration at LTO time (which very likely causes undefined
19521943
// symbols later in the link stage). So we append file offset to make
19531944
// filename unique.

0 commit comments

Comments
 (0)