lld: [ELF][ARM] Implement support for Tag_ABI_VFP_args

The Tag_ABI_VFP_args build attribute controls the procedure call
standard used for floating point parameters on ARM. The values are:

0 - Base AAPCS (FP Parameters passed in Core (Integer) registers
1 - VFP AAPCS (FP Parameters passed in FP registers)
2 - Toolchain specific (Neither Base or VFP)
3 - Compatible with all (No use of floating point parameters)

If the Tag_ABI_VFP_args build attribute is missing it has an implicit
value of 0.

We use the attribute in two ways:

* Detect a clash in calling convention between Base, VFP and Toolchain.

we follow ld.bfd's lead and do not error if there is a clash between an
implicit Base AAPCS caused by a missing attribute. Many projects
including the hard-float (VFP AAPCS) version of glibc contain assembler
files that do not use floating point but do not have Tag_ABI_VFP_args.

* Set the EF_ARM_ABI_FLOAT_SOFT or EF_ARM_ABI_FLOAT_HARD ELF header flag

for Base or VFP AAPCS respectively. This flag is used by some ELF
loaders.

References:
* Addenda to, and Errata in, the ABI for the ARM Architecture for
  Tag_ABI_VFP_args
* Elf for the ARM Architecture for ELF header flags

Fixes LLVM PR36009

PR:		229050
Obtained from:	llvm r338377 by Peter Smith
This commit is contained in:
emaste 2018-07-31 15:25:03 +00:00
parent 3f006c3cbf
commit 930481b8f6
3 changed files with 56 additions and 1 deletions

View File

@ -97,10 +97,19 @@ ARM::ARM() {
}
uint32_t ARM::calcEFlags() const {
// The ABIFloatType is used by loaders to detect the floating point calling
// convention.
uint32_t ABIFloatType = 0;
if (Config->ARMVFPArgs == ARMVFPArgKind::Base ||
Config->ARMVFPArgs == ARMVFPArgKind::Default)
ABIFloatType = EF_ARM_ABI_FLOAT_SOFT;
else if (Config->ARMVFPArgs == ARMVFPArgKind::VFP)
ABIFloatType = EF_ARM_ABI_FLOAT_HARD;
// We don't currently use any features incompatible with EF_ARM_EABI_VER5,
// but we don't have any firm guarantees of conformance. Linux AArch64
// kernels (as of 2016) require an EABI version to be set.
return EF_ARM_EABI_VER5;
return EF_ARM_EABI_VER5 | ABIFloatType;
}
RelExpr ARM::getRelExpr(RelType Type, const Symbol &S,

View File

@ -54,6 +54,9 @@ enum class SortSectionPolicy { Default, None, Alignment, Name, Priority };
// For --target2
enum class Target2Policy { Abs, Rel, GotRel };
// For tracking ARM Float Argument PCS
enum class ARMVFPArgKind { Default, Base, VFP, ToolChain };
struct SymbolVersion {
llvm::StringRef Name;
bool IsExternCpp;
@ -169,6 +172,7 @@ struct Configuration {
StripPolicy Strip;
UnresolvedPolicy UnresolvedSymbols;
Target2Policy Target2;
ARMVFPArgKind ARMVFPArgs = ARMVFPArgKind::Default;
BuildIdKind BuildId = BuildIdKind::None;
ELFKind EKind = ELFNoneKind;
uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;

View File

@ -441,6 +441,46 @@ void ObjFile<ELFT>::initializeSections(
}
}
// For ARM only, to set the EF_ARM_ABI_FLOAT_SOFT or EF_ARM_ABI_FLOAT_HARD
// flag in the ELF Header we need to look at Tag_ABI_VFP_args to find out how
// the input objects have been compiled.
static void updateARMVFPArgs(const ARMAttributeParser &Attributes,
const InputFile *F) {
if (!Attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args))
// If an ABI tag isn't present then it is implicitly given the value of 0
// which maps to ARMBuildAttrs::BaseAAPCS. However many assembler files,
// including some in glibc that don't use FP args (and should have value 3)
// don't have the attribute so we do not consider an implicit value of 0
// as a clash.
return;
unsigned VFPArgs = Attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args);
ARMVFPArgKind Arg;
switch (VFPArgs) {
case ARMBuildAttrs::BaseAAPCS:
Arg = ARMVFPArgKind::Base;
break;
case ARMBuildAttrs::HardFPAAPCS:
Arg = ARMVFPArgKind::VFP;
break;
case ARMBuildAttrs::ToolChainFPPCS:
// Tool chain specific convention that conforms to neither AAPCS variant.
Arg = ARMVFPArgKind::ToolChain;
break;
case ARMBuildAttrs::CompatibleFPAAPCS:
// Object compatible with all conventions.
return;
default:
error(toString(F) + ": unknown Tag_ABI_VFP_args value: " + Twine(VFPArgs));
return;
}
// Follow ld.bfd and error if there is a mix of calling conventions.
if (Config->ARMVFPArgs != Arg && Config->ARMVFPArgs != ARMVFPArgKind::Default)
error(toString(F) + ": incompatible Tag_ABI_VFP_args");
else
Config->ARMVFPArgs = Arg;
}
// The ARM support in lld makes some use of instructions that are not available
// on all ARM architectures. Namely:
// - Use of BLX instruction for interworking between ARM and Thumb state.
@ -520,6 +560,8 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
ArrayRef<uint8_t> Contents = check(this->getObj().getSectionContents(&Sec));
Attributes.Parse(Contents, /*isLittle*/ Config->EKind == ELF32LEKind);
updateSupportedARMFeatures(Attributes);
updateARMVFPArgs(Attributes, this);
// FIXME: Retain the first attribute section we see. The eglibc ARM
// dynamic loaders require the presence of an attribute section for dlopen
// to work. In a full implementation we would merge all attribute sections.