elf_aarch64_tdata (output_bfd)->no_enum_size_warning = no_enum_warn;
elf_aarch64_tdata (output_bfd)->no_wchar_size_warning = no_wchar_warn;
+ /* The global list of object attributes used to save requested features from
+ the command-line options. */
+ obj_attr_subsection_v2_t *attrs_subsection
+ = bfd_elf_obj_attr_subsection_v2_init (xstrdup ("aeabi_feature_and_bits"),
+ OA_SUBSEC_PUBLIC, true,
+ OA_ENC_ULEB128);
+
uint32_t gnu_property_aarch64_feature_1_and = 0;
aarch64_feature_marking_report gcs_report;
aarch64_feature_marking_report gcs_report_dynamic;
if (sw_protections->plt_type & PLT_BTI)
- gnu_property_aarch64_feature_1_and |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+ {
+ gnu_property_aarch64_feature_1_and |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+ _bfd_aarch64_oav2_record (attrs_subsection, Tag_Feature_BTI, true);
+ }
+
+ /* Note: Contrarilly to PLT_BTI, (sw_protections->plt_type & PLT_PAC) == true
+ does not mean that Tag_Feature_PAC should also be set to true. The PAC
+ object attribute is only there to mirror the existing GNU properties.
+ Adding a property for PAC was in retrospect a mistake as it does not carry
+ valuable information. The only use it does have is informational: if the
+ property is set on the output ELF object, then someone went to the trouble
+ of enabling it on all the input objects. */
switch (sw_protections->gcs_type)
{
case GCS_ALWAYS:
gnu_property_aarch64_feature_1_and |= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+ _bfd_aarch64_oav2_record (attrs_subsection, Tag_Feature_GCS, true);
/* The default diagnostic level with '-z gcs=always' is 'warning'. */
if (sw_protections->gcs_report == MARKING_UNSET)
case GCS_NEVER:
gnu_property_aarch64_feature_1_and &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+ _bfd_aarch64_oav2_record (attrs_subsection, Tag_Feature_GCS, false);
/* Markings are ignored, so no diagnostic messages can be emitted. */
gcs_report = MARKING_NONE;
abort ();
}
+ if (attrs_subsection->size > 0)
+ LINKED_LIST_APPEND (obj_attr_subsection_v2_t)
+ (&elf_obj_attr_subsections (output_bfd), attrs_subsection);
+
elf_aarch64_tdata (output_bfd)->gnu_property_aarch64_feature_1_and
= gnu_property_aarch64_feature_1_and;
return true;
}
-/* Check if BTI enabled PLTs are needed. Returns the type needed. */
+/* Check if BTI-enabled (and/or PAC-enabled) PLTs are needed.
+ Returns the type needed. */
static aarch64_plt_type
get_plt_type (bfd *abfd)
{
sym->flags |= BSF_KEEP;
}
+/* Implement elf_backend_setup_object_attributes for AArch64. */
+static bfd *
+elfNN_aarch64_link_setup_object_attributes (struct bfd_link_info *info)
+{
+ bfd *pbfd = _bfd_aarch64_elf_link_setup_object_attributes (info);
+
+ struct elf_aarch64_obj_tdata *tdata = elf_aarch64_tdata (info->output_bfd);
+
+ /* When BTI is forced on the command line, information flows from plt_type to
+ the frozen object attributes (a.k.a FROZEN), so plt_type has already been
+ set and FROZEN doesn't have any effect on plt_type.
+ Whereas if BTI is inferred from the input bfds, information flows from
+ output object attributes to plt_type. If the property GNU_PROPERTY_AARCH64
+ _FEATURE_1_BTI has been set on all the input bfds, then BTI is set on the
+ output bfd and plt_type is updated accordingly.
+
+ Important note: this is not true for GNU_PROPERTY_AARCH64_FEATURE_1_PAC.
+ See more explanation in bfd_elfNN_aarch64_set_options. */
+ const obj_attr_subsection_v2_t *aeabi_feature_and_bits_subsec
+ = bfd_obj_attr_subsection_v2_find_by_name
+ (elf_obj_attr_subsections (info->output_bfd).first,
+ "aeabi_feature_and_bits", true);
+ if (aeabi_feature_and_bits_subsec != NULL)
+ {
+ const obj_attr_v2_t *attr_bti
+ = _bfd_obj_attr_v2_find_by_tag (aeabi_feature_and_bits_subsec,
+ Tag_Feature_BTI, true);
+ if (attr_bti && attr_bti->val.uint == 1)
+ tdata->sw_protections.plt_type |= PLT_BTI;
+ }
+
+ setup_plt_values (info, tdata->sw_protections.plt_type);
+
+ return pbfd;
+}
+
/* Implement elf_backend_setup_gnu_properties for AArch64. It serves as a
wrapper function for _bfd_aarch64_elf_link_setup_gnu_properties to account
for the effect of GNU properties of the output_bfd. */
outprop to plt_type. If the property GNU_PROPERTY_AARCH64_FEATURE_1_BTI
has been set on all the input bfds, then BTI is set on the output bfd and
plt_type is updated accordingly. */
- struct elf_aarch64_obj_tdata * tdata = elf_aarch64_tdata (info->output_bfd);
+ struct elf_aarch64_obj_tdata *tdata = elf_aarch64_tdata (info->output_bfd);
uint32_t outprop = tdata->gnu_property_aarch64_feature_1_and;
if (outprop & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
tdata->sw_protections.plt_type |= PLT_BTI;
#define elf_backend_symbol_processing \
elfNN_aarch64_backend_symbol_processing
+#define elf_backend_setup_object_attributes \
+ elfNN_aarch64_link_setup_object_attributes
+
#define elf_backend_setup_gnu_properties \
elfNN_aarch64_link_setup_gnu_properties
+#define elf_backend_translate_gnu_props_to_obj_attrs \
+ _bfd_aarch64_translate_gnu_props_to_obj_attrs
+
+#define elf_backend_translate_obj_attrs_to_gnu_props \
+ _bfd_aarch64_translate_obj_attrs_to_gnu_props
+
+#define elf_backend_obj_attr_v2_default_value \
+ _bfd_aarch64_oav2_default_value
+
+#define elf_backend_obj_attr_v2_merge \
+ _bfd_aarch64_oav2_attr_merge
+
#define elf_backend_merge_gnu_properties \
elfNN_aarch64_merge_gnu_properties
},
};
+/* Record the pair (TAG, VALUE) into SUBSEC. */
+void
+_bfd_aarch64_oav2_record (obj_attr_subsection_v2_t *subsec,
+ Tag_Feature_Set feature_tag,
+ uint32_t value)
+{
+ union obj_attr_value_v2 data;
+ data.uint = value;
+ obj_attr_v2_t *attr = bfd_elf_obj_attr_v2_init (feature_tag, data);
+ LINKED_LIST_APPEND (obj_attr_v2_t) (subsec, attr);
+}
+
+/* Wrapper around the recording of the pair (TAG, VALUE) into SUBSEC called from
+ a context of translation from GNU properties. */
+static void
+obj_attr_v2_record_tag_value (obj_attr_subsection_v2_t *subsec,
+ Tag_Feature_Set tag,
+ bool value)
+{
+ obj_attr_v2_t *attr;
+ attr = _bfd_obj_attr_v2_find_by_tag (subsec, tag, false);
+ if (attr != NULL)
+ {
+ if (attr->val.uint != value)
+ {
+ /* If we find an existing value for the given object attributes and
+ this value is different from the new one, it can mean two things:
+ - either the values are conflicting, and we need to raise an
+ error.
+ - either there are several GNU properties AARCH64_FEATURE_1_AND
+ which were recorded, but its final value is the result of the
+ merge of those separate values.
+ For now, only the second case occurs. */
+ uint32_t merged_val = attr->val.uint | value;
+ _bfd_aarch64_oav2_record (subsec, tag, merged_val);
+ }
+ /* else: nothing to do. */
+ }
+ else
+ _bfd_aarch64_oav2_record (subsec, tag, value);
+}
+
+/* Translate the relevant GNU properties in P to their Object Attributes v2
+ equivalents. */
+void
+_bfd_aarch64_translate_gnu_props_to_obj_attrs
+ (const bfd *abfd, const elf_property_list *p)
+{
+ if (p->property.pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND)
+ {
+ const elf_property *prop = &p->property;
+ BFD_ASSERT (prop->pr_kind == property_number);
+
+ obj_attr_subsection_v2_t *subsec = bfd_obj_attr_subsection_v2_find_by_name
+ (elf_obj_attr_subsections (abfd).first, "aeabi_feature_and_bits", false);
+
+ bool new_subsec = false;
+ if (subsec == NULL)
+ {
+ subsec = bfd_elf_obj_attr_subsection_v2_init
+ (xstrdup ("aeabi_feature_and_bits"), OA_SUBSEC_PUBLIC, true,
+ OA_ENC_ULEB128);
+ new_subsec = true;
+ }
+
+ bool bti_bit = prop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+ bool pac_bit = prop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
+ bool gcs_bit = prop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+
+ obj_attr_v2_record_tag_value (subsec, Tag_Feature_BTI, bti_bit);
+ obj_attr_v2_record_tag_value (subsec, Tag_Feature_PAC, pac_bit);
+ obj_attr_v2_record_tag_value (subsec, Tag_Feature_GCS, gcs_bit);
+
+ if (new_subsec)
+ LINKED_LIST_APPEND (obj_attr_subsection_v2_t)
+ (&elf_obj_attr_subsections (abfd), subsec);
+ }
+}
+
+/* Translate relevant Object Attributes v2 in SUBSEC to GNU properties. */
+void
+_bfd_aarch64_translate_obj_attrs_to_gnu_props
+ (bfd *abfd, const obj_attr_subsection_v2_t *subsec)
+{
+ /* Note: there is no need to create the GNU properties section here. It will
+ be handled later by setup_gnu_properties. */
+
+ if (strcmp (subsec->name, "aeabi_feature_and_bits") == 0)
+ {
+ uint32_t gnu_property_aarch64_features = 0;
+
+ for (const obj_attr_v2_t *attr = subsec->first;
+ attr != NULL;
+ attr = attr->next)
+ {
+ if (attr->tag == Tag_Feature_BTI && attr->val.uint == 1)
+ gnu_property_aarch64_features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+ else if (attr->tag == Tag_Feature_PAC && attr->val.uint == 1)
+ gnu_property_aarch64_features |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
+ else if (attr->tag == Tag_Feature_GCS && attr->val.uint == 1)
+ gnu_property_aarch64_features |= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+ }
+
+ /* Note: _bfd_elf_get_property find the existing property, or create one.
+ The insertion is already done by it. */
+ elf_property *prop
+ = _bfd_elf_get_property (abfd, GNU_PROPERTY_AARCH64_FEATURE_1_AND, 4);
+ prop->u.number |= gnu_property_aarch64_features;
+ prop->pr_kind = property_number;
+ }
+}
+
+/* Check whether the given ATTR is managed by the backend, and if that is the
+ case, initialize ATTR with its default value coming from the known tag
+ registry.
+ True if the default value for the tag is managed by the backend, and was
+ initialized. False otherwise. */
+bool
+_bfd_aarch64_oav2_default_value
+ (const struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ const obj_attr_info_t *tag_info ATTRIBUTE_UNUSED,
+ const obj_attr_subsection_v2_t *subsec ATTRIBUTE_UNUSED,
+ obj_attr_v2_t *attr ATTRIBUTE_UNUSED)
+{
+ /* For now, there is no default value set by the backend. The default BTI and
+ GCS values are set by the respective command-line options '-z force-bti'
+ and '-z gcs'. */
+
+ return false;
+}
+
+/* Merge the values from LHS, RHS, FROZEN, and return the merge result. LHS,
+ RHS and FROZEN correspond to an attribute from SUBSEC with the same tag, but
+ from three different contexts:
+ - LHS corresponds to the global merge result.
+ - RHS corresponds to the new value that is merged into the global merge
+ result.
+ - FROZEN corresponds to the value coming from some configuration context
+ (usually a command-line option) and which is immutable for the whole
+ merge process. */
+obj_attr_v2_merge_result_t
+_bfd_aarch64_oav2_attr_merge (const struct bfd_link_info *info,
+ const bfd *abfd,
+ const obj_attr_subsection_v2_t *subsec,
+ const obj_attr_v2_t *lhs, const obj_attr_v2_t *rhs,
+ const obj_attr_v2_t *frozen)
+{
+ obj_attr_v2_merge_result_t res = {
+ .merge = false,
+ .val.uint = 0,
+ .reason = OAv2_MERGE_OK,
+ };
+
+ /* No need to list required sections here, they are handled separately as
+ they require a perfect one-to-one match for all the tag values. */
+ if (strcmp (subsec->name, "aeabi_feature_and_bits") == 0)
+ {
+ BFD_ASSERT (subsec->encoding == OA_ENC_ULEB128 && subsec->optional);
+ const obj_attr_info_t *attr_info
+ = _bfd_obj_attr_v2_find_known_by_tag (get_elf_backend_data (abfd),
+ subsec->name, lhs->tag);
+ if (attr_info == NULL)
+ {
+ info->callbacks->einfo
+ (_("%pB: warning: cannot merge unknown tag 'Tag_unknown_%u' "
+ "(=0x%x) in subsection '%s'.\n"),
+ abfd, rhs->tag, rhs->val.uint, subsec->name);
+ res.reason = OAv2_MERGE_UNSUPPORTED;
+ return res;
+ }
+
+ /* For now, there is no different between the tags of this section, all
+ will be merged in the same way. */
+ res = _bfd_obj_attr_v2_merge_AND (info, abfd, subsec, lhs, rhs, frozen);
+
+ const aarch64_protection_opts *sw_protections
+ = &elf_aarch64_tdata (info->output_bfd)->sw_protections;
+ aarch64_feature_marking_report bti_report = sw_protections->bti_report;
+ aarch64_feature_marking_report gcs_report = sw_protections->gcs_report;
+
+ if (rhs->tag == Tag_Feature_BTI
+ && bti_report != MARKING_NONE
+ && (sw_protections->plt_type & PLT_BTI)
+ && rhs->val.uint == 0)
+ _bfd_aarch64_elf_check_bti_report (info, abfd);
+
+ if ((rhs->tag == Tag_Feature_GCS) && (gcs_report != MARKING_NONE)
+ && (sw_protections->gcs_type == GCS_ALWAYS) && (rhs->val.uint == 0))
+ _bfd_aarch64_elf_check_gcs_report (info, abfd);
+
+ /* Make sure that frozen bits don't disappear from REF when it will be
+ compared to the next file. */
+ if (frozen != NULL)
+ res.val.uint |= frozen->val.uint;
+ }
+ else
+ res.reason = OAv2_MERGE_UNSUPPORTED;
+
+ return res;
+}
+
+/* Check for incompatibilities with PAuthABI attributes. */
+static bool
+aarch64_check_pauthabi_attributes (const struct bfd_link_info *info)
+{
+ /* The subsection "aeabi_pauthabi" contains information about the Pointer
+ Authentication Signing schema when the object uses an extension to ELF,
+ PAUTHABI64, which is today only supported by LLVM, and not supported by
+ GCC 15 toolchain or ealier ones. There is no plan to add support for it
+ in the future. The pointers that are signed as well as the modifiers and
+ key used for each type of pointer are known as the signing schema.
+ The AEABI Build attributes specification defines the following tuple values
+ of (Tag_Pauth_Platform, Tag_Pauth_Schema):
+ - The tuple (0, 0) is obtained when both attributes are explicitly set to
+ 0 or are implicitly set to 0 due to the rules for setting default values
+ for public tags. This represents an ELF file which makes no use of the
+ PAuthABI extension.
+ - The tuple (0, 1) is reserved for the "Invalid" platform. ELF files with
+ an "Invalid" platform are incompatible with the PAuth ABI Extension.
+ - The tuples (0, N) where N > 1 are reserved.
+ - The tuples (M, N) where M is the id of one of the registered platforms
+ defined in PAuthABI64, represents a valid signing schema. (M, 0)
+ represents a schema version of 0 for platform M.
+ Given that the GNU linker does not support PAuthABI, it cannot do anything
+ with values others than (0, 0) or (0, 1).
+ The check below enforces either that the output object has either no
+ subsection "aeabi_pauthabi", or the tuple is set to (0, 0) and (0, 1). */
+
+ obj_attr_subsection_v2_t *subsecs
+ = elf_obj_attr_subsections (info->output_bfd).first;
+ const obj_attr_subsection_v2_t *subsec
+ = bfd_obj_attr_subsection_v2_find_by_name (subsecs, "aeabi_pauthabi", true);
+ if (subsec == NULL)
+ return true;
+
+ int platform_id = 0;
+ int version_id = 0;
+
+ const obj_attr_v2_t *attr
+ = _bfd_obj_attr_v2_find_by_tag (subsec, Tag_PAuth_Platform, true);
+ if (attr != NULL)
+ platform_id = attr->val.uint;
+
+ attr = _bfd_obj_attr_v2_find_by_tag (subsec, Tag_PAuth_Schema, true);
+ if (attr != NULL)
+ version_id = attr->val.uint;
+
+ if (! ((platform_id == 0 && version_id == 0)
+ || (platform_id == 0 && version_id == 1)))
+ {
+ info->callbacks->einfo
+ (_("%Xerror: the GNU linker does not support PAuthABI. Any value "
+ "different from (platform = 0, schema = 0) or (platform = 0, schema "
+ "= 1) is not supported.\n"));
+ return false;
+ }
+
+ return true;
+}
+
+/* Merge the AEABI Build Attributes present in the input BFDs, raise any
+ compatibility issue, and write the merge result to OBFD.
+
+ AArch64 backend declares two vendor subsections, and their associated tags:
+ - aeabi_feature_and_bits: contains tags that describe the same optional
+ bits as the GNU_PROPERTY_AARCH64_FEATURE_1_AND. For now, the following
+ attributes are recognized:
+ - Tag_Feature_BTI: means that all the executable sections are
+ compatible with Branch Target Identification (BTI) mechanism.
+ - Tag_Feature_PAC: means that all the executable sections have been
+ protected with Return Address Signing.
+ - Tag_Feature_GCS: means that all the executable sections are
+ compatible with the Guarded Control Stack (GCS) extension.
+ - aeabi_pauthabi: contains information about the Pointer Authentication
+ Signing schema when the object uses an extension to ELF, PAUTHABI64,
+ which is currently not supported by GCC toolchain. The pointers that
+ are signed as well as the modifiers and key used for each type of pointer
+ are known as the signing schema. The support of this subsection is there
+ for completeness with the AEABI Build Attributes document, and allows
+ readelf to dump the data nicely, and the linker to detect a use of a
+ signing schema, and error.
+ - Tag_PAuth_Paltform: the platform vendor id.
+ - Tag_PAuth_Schema: the version numner of the schema.
+
+ For backward-compatibilty purpose, AArch64 backend translates
+ GNU_PROPERTY_AARCH64_FEATURE_1_AND in input files to its OAv2 equivalents.
+ The frozen set of OAv2 is populated with values derived from command-line
+ options for BTI (-z force-bti) and GCS (-z gcs=*).
+ It also reports incompatibilities for BTI and GCS, and set BTI PLT type
+ depending on the OAv2 merge result.
+ Regarding incompatibilities, only the ones detected in objects constituting
+ the output link unit will be reported. Supports for detecting incompatibi-
+ -lities in shared objects might be a future work to bring it in pair with
+ thenGNU properties merge. However, since OAv2 are translated to GNU
+ properties, detection will still happen so this feature seems redundant and
+ of little value given the backward compatibility support for GNU properties
+ is required (see next paragraph).
+ Finally, it translates OAv2s in subsection "aeabi_feature_and_bits" to
+ GNU_PROPERTY_AARCH64_FEATURE_1_AND as GNU properties are required for
+ the dynamic linker (it does not understand OAv2s yet). */
+bfd *
+_bfd_aarch64_elf_link_setup_object_attributes (struct bfd_link_info *info)
+{
+ bfd *pbfd = _bfd_elf_link_setup_object_attributes (info);
+
+ /* Check PAuthABI compatibility. */
+ if (! aarch64_check_pauthabi_attributes (info))
+ return NULL;
+
+ /* Set the flag marking whether the merge of object attributes was done so
+ that setup_gnu_properties does not raise the same errors/warning again. */
+ elf_aarch64_tdata (info->output_bfd)->oa_merge_done = true;
+
+ return pbfd;
+}
+
/* Find the first input bfd with GNU property and merge it with GPROP. If no
such input is found, add it to a new section at the last input. Update
GPROP accordingly. */
Note: If there is no .gnu.note.property section, we might think that
elf_properties (res.pbfd) is always NULL. However, this is not always
- true. In PR23900: old linkers were treating .note.gnu.property as a
- generic note section, so old objects might contain properties inside
- .note instead of .note.gnu.property. In this case, the section won't be
- detected but the properties are still parsed. Consequently,
- elf_properties (res.pbfd) is populated and different from NULL (see
- https://sourceware.org/bugzilla/show_bug.cgi?id=23900 for more
- details). */
- if (res.sec == NULL && elf_properties (res.pbfd) == NULL)
+ true for the following reasons:
+ - PR23900: old linkers were treating .note.gnu.property as a generic
+ note section, so old objects might contain properties inside .note
+ instead of .note.gnu.property. In this case, the section won't be
+ detected but the properties are still parsed. Consequently,
+ elf_properties (res.pbfd) is populated and different from NULL (see
+ https://sourceware.org/bugzilla/show_bug.cgi?id=23900 for more
+ details).
+ - since the introduction of the object attributes, once the merge
+ of the OAs is done, some of the OAs can be translated to GNU
+ properties like GNU_PROPERTY_AARCH64_FEATURE_1_AND. In this case,
+ we need to check explicitly for the presence of the GNU properties
+ that might be added by the BAs merge. */
+ if (res.sec == NULL
+ && (elf_properties (res.pbfd) == NULL
+ || _bfd_elf_find_property (elf_properties (res.pbfd),
+ GNU_PROPERTY_AARCH64_FEATURE_1_AND,
+ NULL)))
_bfd_aarch64_elf_create_gnu_property_section (info, res.pbfd);
/* Merge the found input property with output properties. Note: if no
property was found, _bfd_elf_get_property will create one. */
- elf_property *prop =
- _bfd_elf_get_property (res.pbfd,
- GNU_PROPERTY_AARCH64_FEATURE_1_AND,
- 4);
+ elf_property *prop
+ = _bfd_elf_get_property (res.pbfd,
+ GNU_PROPERTY_AARCH64_FEATURE_1_AND,
+ 4);
/* Check for a feature mismatch and report issue (if any) before this
information get lost as the value of ebfd will be overriden with
{
struct elf_aarch64_obj_tdata *tdata = elf_aarch64_tdata (info->output_bfd);
- if (tdata->sw_protections.bti_report == MARKING_NONE)
+ if (elf_aarch64_tdata (info->output_bfd)->oa_merge_done
+ || tdata->sw_protections.bti_report == MARKING_NONE)
return;
++tdata->n_bti_issues;
}
else
{
- if (tdata->sw_protections.gcs_report == MARKING_NONE)
+ if (elf_aarch64_tdata (info->output_bfd)->oa_merge_done
+ || tdata->sw_protections.gcs_report == MARKING_NONE)
return;
++tdata->n_gcs_issues;
if (tdata->n_gcs_issues > GNU_PROPERTY_ISSUES_MAX)