aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSrinath Parvathaneni <srinath.parvathaneni@arm.com>2024-10-28 18:13:08 +0000
committerMatthieu Longo <matthieu.longo@arm.com>2024-12-02 15:18:41 +0000
commitb75ce33f0cd3a4720c252969bfffdac4f3f998a7 (patch)
treec6b6bc86e2d9ec80faf89d4681dba660080a4830
parentf1cd84f4c55f3b361cb219f625a397ee039ad0d2 (diff)
downloadbinutils-b75ce33f0cd3a4720c252969bfffdac4f3f998a7.zip
binutils-b75ce33f0cd3a4720c252969bfffdac4f3f998a7.tar.gz
binutils-b75ce33f0cd3a4720c252969bfffdac4f3f998a7.tar.bz2
aarch64: GCS feature check in GNU note properties for input objects
This patch adds support for Guarded Control Stack in AArch64 linker. This patch implements the following: 1) Defines GNU_PROPERTY_AARCH64_FEATURE_1_GCS bit for GCS in GNU_PROPERTY_AARCH64_FEATURE_1_AND macro. 2) Adds readelf support to read and print the GCS feature in GNU properties in AArch64. Displaying notes found in: .note.gnu.property [ ]+Owner[ ]+Data size[ ]+Description GNU 0x00000010 NT_GNU_PROPERTY_TYPE_0 Properties: AArch64 feature: GCS 3) Adds support for the "-z gcs" linker option and document all the values allowed with this option (-z gcs[=always|never|implicit]) where "-z gcs" is equivalent to "-z gcs=always". When '-z gcs' option is omitted from the command line, it defaults to "implicit" and relies on the GCS feature marking in GNU properties. 4) Adds support for the "-z gcs-report" linker option and document all the values allowed with this option (-z gcs-report[=none|warning|error]) where "-z gcs-report" is equivalent to "-z gcs-report=warning". When this option is omitted from the command line, it defaults to "warning". The ABI changes adding GNU_PROPERTY_AARCH64_FEATURE_1_GCS to the GNU property GNU_PROPERTY_AARCH64_FEATURE_1_AND is merged into main and can be found in [1]. [1] https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst Co-authored-by: Matthieu Longo <matthieu.longo@arm.com> Co-authored-by: Yury Khrustalev <yury.khrustalev@arm.com>
-rw-r--r--bfd/elfnn-aarch64.c29
-rw-r--r--bfd/elfxx-aarch64.c67
-rw-r--r--bfd/elfxx-aarch64.h22
-rw-r--r--binutils/readelf.c4
-rw-r--r--include/elf/common.h1
-rw-r--r--ld/emultempl/aarch64elf.em85
-rw-r--r--ld/ld.texi30
7 files changed, 224 insertions, 14 deletions
diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
index 3a364e9..7011914 100644
--- a/bfd/elfnn-aarch64.c
+++ b/bfd/elfnn-aarch64.c
@@ -5020,8 +5020,24 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
elf_aarch64_tdata (output_bfd)->gnu_property_aarch64_feature_1_and
|= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+ switch (sw_protections->gcs_type)
+ {
+ case GCS_ALWAYS:
+ elf_aarch64_tdata (output_bfd)->gnu_property_aarch64_feature_1_and
+ |= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+ break;
+ case GCS_NEVER:
+ elf_aarch64_tdata (output_bfd)->gnu_property_aarch64_feature_1_and
+ &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+ break;
+ case GCS_IMPLICIT:
+ /* GCS feature on the output bfd will be deduced from input objects. */
+ break;
+ }
+
elf_aarch64_tdata (output_bfd)->sw_protections = *sw_protections;
elf_aarch64_tdata (output_bfd)->n_bti_issues = 0;
+ elf_aarch64_tdata (output_bfd)->n_gcs_issues = 0;
setup_plt_values (link_info, sw_protections->plt_type);
}
@@ -10630,6 +10646,7 @@ elfNN_aarch64_merge_gnu_properties (struct bfd_link_info *info,
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 output has been marked with BTI using command line argument, give
out warning if necessary. */
@@ -10641,6 +10658,18 @@ elfNN_aarch64_merge_gnu_properties (struct bfd_link_info *info,
if (!bprop || !(bprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
_bfd_aarch64_elf_check_bti_report (info, bbfd);
}
+
+ /* If the output has been marked with GCS using '-z gcs' and the input is
+ missing GCS feature tag, throw a warning/error in accordance with
+ -z gcs-report=warning/error. */
+ if ((outprop & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
+ && gcs_report != MARKING_NONE)
+ {
+ if (!aprop || !(aprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
+ _bfd_aarch64_elf_check_gcs_report (info, abfd);
+ if (!bprop || !(bprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
+ _bfd_aarch64_elf_check_gcs_report (info, bbfd);
+ }
}
return _bfd_aarch64_elf_merge_gnu_properties (info, abfd, aprop,
diff --git a/bfd/elfxx-aarch64.c b/bfd/elfxx-aarch64.c
index 41ff2ca..e4d7fa5 100644
--- a/bfd/elfxx-aarch64.c
+++ b/bfd/elfxx-aarch64.c
@@ -765,6 +765,7 @@ _bfd_aarch64_report_summary_merge_issues (struct bfd_link_info *info)
const struct elf_aarch64_obj_tdata * tdata
= elf_aarch64_tdata (info->output_bfd);
aarch64_feature_marking_report bti_report = tdata->sw_protections.bti_report;
+ aarch64_feature_marking_report gcs_report = tdata->sw_protections.gcs_report;
if (tdata->n_bti_issues > GNU_PROPERTY_ISSUES_MAX
&& bti_report != MARKING_NONE)
@@ -777,6 +778,18 @@ _bfd_aarch64_report_summary_merge_issues (struct bfd_link_info *info)
"BTI requirements.\n");
info->callbacks->einfo (msg, tdata->n_bti_issues);
}
+
+ if (tdata->n_gcs_issues > GNU_PROPERTY_ISSUES_MAX
+ && gcs_report != MARKING_NONE)
+ {
+ const char *msg
+ = (tdata->sw_protections.gcs_report == MARKING_ERROR)
+ ? _("%Xerror: found a total of %d inputs incompatible with "
+ "GCS requirements.\n")
+ : _("warning: found a total of %d inputs incompatible with "
+ "GCS requirements.\n");
+ info->callbacks->einfo (msg, tdata->n_gcs_issues);
+ }
}
/* Find the first input bfd with GNU property and merge it with GPROP. If no
@@ -796,7 +809,7 @@ _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info)
/* If ebfd != NULL it is either an input with property note or the last input.
Either way if we have an output GNU property that was provided, we should
add it (by creating a section if needed). */
- if (ebfd != NULL && outprop)
+ if (ebfd != NULL)
{
/* If no GNU property node was found, create the GNU property note
section. */
@@ -817,8 +830,17 @@ _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info)
&& !(prop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
_bfd_aarch64_elf_check_bti_report (info, ebfd);
+ if (tdata->sw_protections.gcs_type == GCS_NEVER)
+ prop->u.number &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+ else if ((outprop & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
+ && !(prop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
+ _bfd_aarch64_elf_check_gcs_report (info, ebfd);
+
prop->u.number |= outprop;
- prop->pr_kind = property_number;
+ if (prop->u.number == 0)
+ prop->pr_kind = property_remove;
+ else
+ prop->pr_kind = property_number;
}
/* Set up generic GNU properties, and merge them with the backend-specific
@@ -843,7 +865,8 @@ _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info)
{
outprop = (p->property.u.number
& (GNU_PROPERTY_AARCH64_FEATURE_1_BTI
- | GNU_PROPERTY_AARCH64_FEATURE_1_PAC));
+ | GNU_PROPERTY_AARCH64_FEATURE_1_PAC
+ | GNU_PROPERTY_AARCH64_FEATURE_1_GCS));
break;
}
}
@@ -906,6 +929,19 @@ _bfd_aarch64_elf_merge_gnu_properties (struct bfd_link_info *info
{
case GNU_PROPERTY_AARCH64_FEATURE_1_AND:
{
+ aarch64_gcs_type gcs_type
+ = elf_aarch64_tdata (info->output_bfd)->sw_protections.gcs_type;
+ /* OUTPROP does not contain GCS for GCS_NEVER. We only need to make sure
+ that APROP does not contain GCS as well.
+ Notes:
+ - if BPROP contains GCS and APROP is not null, it is zeroed by the
+ AND with APROP.
+ - if BPROP contains GCS and APROP is null, it is overwritten with
+ OUTPROP as the AND with APROP would have been equivalent to zeroing
+ BPROP. */
+ if (gcs_type == GCS_NEVER && aprop != NULL)
+ aprop->u.number &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+
if (aprop != NULL && bprop != NULL)
{
orig_number = aprop->u.number;
@@ -1005,4 +1041,27 @@ _bfd_aarch64_elf_check_bti_report (struct bfd_link_info *info, bfd *ebfd)
"file lacks the necessary property note.\n");
info->callbacks->einfo (msg, ebfd);
-} \ No newline at end of file
+}
+
+void
+_bfd_aarch64_elf_check_gcs_report (struct bfd_link_info *info, bfd *ebfd)
+{
+ struct elf_aarch64_obj_tdata *tdata = elf_aarch64_tdata (info->output_bfd);
+
+ if (tdata->sw_protections.gcs_report == MARKING_NONE)
+ return;
+
+ ++tdata->n_gcs_issues;
+
+ if (tdata->n_gcs_issues > GNU_PROPERTY_ISSUES_MAX)
+ return;
+
+ const char *msg
+ = (tdata->sw_protections.gcs_report == MARKING_WARN)
+ ? _("%pB: warning: GCS is required by -z gcs, but this input object file "
+ "lacks the necessary property note.\n")
+ : _("%X%pB: error: GCS is required by -z gcs, but this input object file "
+ "lacks the necessary property note.\n");
+
+ info->callbacks->einfo (msg, ebfd);
+}
diff --git a/bfd/elfxx-aarch64.h b/bfd/elfxx-aarch64.h
index 48a2847..4f8b2be 100644
--- a/bfd/elfxx-aarch64.h
+++ b/bfd/elfxx-aarch64.h
@@ -53,6 +53,16 @@ typedef enum
markings. */
} aarch64_feature_marking_report;
+/* To indicate whether GNU_PROPERTY_AARCH64_FEATURE_1_GCS bit is
+ enabled/disabled on the output when -z gcs linker
+ command line option is passed. */
+typedef enum
+{
+ GCS_NEVER = 0, /* gcs is disabled on output. */
+ GCS_IMPLICIT = 1, /* gcs is deduced from input object. */
+ GCS_ALWAYS = 2, /* gsc is enabled on output. */
+} aarch64_gcs_type;
+
/* A structure to encompass all information about software protections coming
from BTI or PAC related command line options. */
struct aarch64_protection_opts
@@ -62,6 +72,12 @@ struct aarch64_protection_opts
/* Report level for BTI issues. */
aarch64_feature_marking_report bti_report;
+
+ /* Look-up mode for GCS property. */
+ aarch64_gcs_type gcs_type;
+
+ /* Report level for GCS issues. */
+ aarch64_feature_marking_report gcs_report;
};
typedef struct aarch64_protection_opts aarch64_protection_opts;
@@ -87,6 +103,9 @@ struct elf_aarch64_obj_tdata
/* Number of reported BTI issues. */
int n_bti_issues;
+
+ /* Number of reported GCS issues. */
+ int n_gcs_issues;
};
#define elf_aarch64_tdata(bfd) \
@@ -197,6 +216,9 @@ extern void
_bfd_aarch64_elf_check_bti_report (struct bfd_link_info *, bfd *);
extern void
+_bfd_aarch64_elf_check_gcs_report (struct bfd_link_info *, bfd *);
+
+extern void
_bfd_aarch64_elf_link_fixup_gnu_properties (struct bfd_link_info *,
elf_property_list **);
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 73163e0..64e461d 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -21239,6 +21239,10 @@ decode_aarch64_feature_1_and (unsigned int bitmask)
printf ("PAC");
break;
+ case GNU_PROPERTY_AARCH64_FEATURE_1_GCS:
+ printf ("GCS");
+ break;
+
default:
printf (_("<unknown: %x>"), bit);
break;
diff --git a/include/elf/common.h b/include/elf/common.h
index c9920e7..c4eb33b 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -1037,6 +1037,7 @@
#define GNU_PROPERTY_AARCH64_FEATURE_1_BTI (1U << 0)
#define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1U << 1)
+#define GNU_PROPERTY_AARCH64_FEATURE_1_GCS (1U << 2)
/* Values used in GNU .note.ABI-tag notes (NT_GNU_ABI_TAG). */
#define GNU_ABI_TAG_LINUX 0
diff --git a/ld/emultempl/aarch64elf.em b/ld/emultempl/aarch64elf.em
index 726f356..6dcb296 100644
--- a/ld/emultempl/aarch64elf.em
+++ b/ld/emultempl/aarch64elf.em
@@ -37,6 +37,8 @@ static int no_apply_dynamic_relocs = 0;
static aarch64_protection_opts sw_protections = {
.plt_type = PLT_NORMAL,
.bti_report = MARKING_WARN,
+ .gcs_type = GCS_IMPLICIT,
+ .gcs_report = MARKING_WARN,
};
#define COMPILE_TIME_STRLEN(s) \
@@ -352,28 +354,75 @@ aarch64_elf_create_output_section_statements (void)
}
static bool
+aarch64_parse_feature_report_option (const char *optarg,
+ const char *report_opt,
+ const size_t report_opt_len,
+ aarch64_feature_marking_report *level)
+{
+ if (strncmp (optarg, report_opt, report_opt_len) != 0)
+ return false;
+
+ if (strlen (optarg) == report_opt_len
+ || strcmp (optarg + report_opt_len, "=warning") == 0)
+ *level = MARKING_WARN;
+ else if (strcmp (optarg + report_opt_len, "=none") == 0)
+ *level = MARKING_NONE;
+ else if (strcmp (optarg + report_opt_len, "=error") == 0)
+ *level = MARKING_ERROR;
+ else
+ einfo (_("%X%P: error: unrecognized value '-z %s'\n"), optarg);
+
+ return true;
+}
+
+static bool
aarch64_parse_bti_report_option (const char *optarg)
{
#define BTI_REPORT "bti-report"
#define BTI_REPORT_LEN COMPILE_TIME_STRLEN (BTI_REPORT)
- if (strncmp (optarg, BTI_REPORT, BTI_REPORT_LEN) != 0)
+ return aarch64_parse_feature_report_option (optarg, BTI_REPORT,
+ BTI_REPORT_LEN, &sw_protections.bti_report);
+
+ #undef BTI_REPORT
+ #undef BTI_REPORT_LEN
+}
+
+static bool
+aarch64_parse_gcs_report_option (const char *optarg)
+{
+ #define GCS_REPORT "gcs-report"
+ #define GCS_REPORT_LEN COMPILE_TIME_STRLEN (GCS_REPORT)
+
+ return aarch64_parse_feature_report_option (optarg, GCS_REPORT,
+ GCS_REPORT_LEN, &sw_protections.gcs_report);
+
+ #undef GCS_REPORT
+ #undef GCS_REPORT_LEN
+}
+
+static bool
+aarch64_parse_gcs_option (const char *optarg)
+{
+ #define GCS "gcs"
+ #define GCS_LEN COMPILE_TIME_STRLEN (GCS)
+
+ if (strncmp (optarg, GCS, GCS_LEN) != 0)
return false;
- if (strlen (optarg) == BTI_REPORT_LEN
- || strcmp (optarg + BTI_REPORT_LEN, "=warning") == 0)
- sw_protections.bti_report = MARKING_WARN;
- else if (strcmp (optarg + BTI_REPORT_LEN, "=none") == 0)
- sw_protections.bti_report = MARKING_NONE;
- else if (strcmp (optarg + BTI_REPORT_LEN, "=error") == 0)
- sw_protections.bti_report = MARKING_ERROR;
+ if (strcmp (optarg + GCS_LEN, "=always") == 0)
+ sw_protections.gcs_type = GCS_ALWAYS;
+ else if (strcmp (optarg + GCS_LEN, "=never") == 0)
+ sw_protections.gcs_type = GCS_NEVER;
+ else if (strcmp (optarg + GCS_LEN, "=implicit") == 0)
+ sw_protections.gcs_type = GCS_IMPLICIT;
else
einfo (_("%X%P: error: unrecognized value '-z %s'\n"), optarg);
return true;
- #undef BTI_REPORT
- #undef BTI_REPORT_LEN
+ #undef GCS
+ #undef GCS_LEN
}
EOF
@@ -433,6 +482,18 @@ PARSE_AND_LIST_OPTIONS='
and output has BTI marking.\n"));
fprintf (file, _("\
-z pac-plt Protect PLTs with Pointer Authentication.\n"));
+ fprintf (file, _("\
+ -z gcs=[always|never|implicit] Controls whether the output supports the Guarded Control Stack (GCS) mechanism.\n\
+ implicit (default if '\''-z gcs'\'' is omitted): deduce GCS from input objects.\n\
+ always: always marks the output with GCS.\n\
+ never: never marks the output with GCS.\n"));
+ fprintf (file, _("\
+ -z gcs-report[=none|warning|error] Emit warning/error on mismatch of GCS marking between input objects and ouput.\n\
+ none: Does not emit any warning/error messages.\n\
+ warning (default): Emit warning when the input objects are missing GCS markings\n\
+ and output have GCS marking.\n\
+ error: Emit error when the input objects are missing GCS markings\n\
+ and output have GCS marking.\n"));
'
PARSE_AND_LIST_ARGS_CASE_Z_AARCH64='
@@ -442,6 +503,10 @@ PARSE_AND_LIST_ARGS_CASE_Z_AARCH64='
{}
else if (strcmp (optarg, "pac-plt") == 0)
sw_protections.plt_type |= PLT_PAC;
+ else if (aarch64_parse_gcs_report_option (optarg))
+ {}
+ else if (aarch64_parse_gcs_option (optarg))
+ {}
'
PARSE_AND_LIST_ARGS_CASE_Z="$PARSE_AND_LIST_ARGS_CASE_Z $PARSE_AND_LIST_ARGS_CASE_Z_AARCH64"
diff --git a/ld/ld.texi b/ld/ld.texi
index ac6bd83..eb36eaf 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -8219,6 +8219,36 @@ with the total number of issues will be displayed at the end.
@cindex Protect PLTs with Returned Pointer Authentication
The @samp{-z pac-plt} option enables the usage of pointer authentication in PLTs.
+@kindex -z gcs=[always|never|implicit]
+@cindex Controls whether the output object supports the Guarded Control Stack (GCS) mechanism.
+The @samp{-z gcs} option controls the verification of Guarded Control Stack (GCS)
+markings on input objects and marks the output with GCS if all conditions are
+validated.
+@itemize
+@item@samp{implicit} (default if @samp{-z gcs} is omitted) enables GCS marking
+on the output if, and only if, all input objects composing the link unit are
+marked with GCS.
+@item@samp{always} forces the marking of the output with GCS.
+@item@samp{never} ignores any GCS marking on the input objects, and does not
+mark the output with GCS.
+@end itemize
+
+@kindex -z gcs-report[=none|warning|error]
+@cindex Control warnings for missing GCS markings.
+The @samp{-z gcs-report[=none|warning|error]} specifies how to report the missing
+GCS markings on inputs, i.e. the GNU_PROPERTY_AARCH64_FEATURE_1_GCS property.
+By default, if the option is omitted and @samp{-z gcs} is provided, warnings are
+emitted.
+@itemize
+@item@samp{none} disables any warning messages.
+@item@samp{warning} (the default value) emits warning messages when input objects
+composing the link unit are missing GCS markings, or dynamic objects containing
+external symbols used in the link unit.
+@item@samp{error} turns the warning messages into errors.
+@end itemize
+If issues are found, a maximum of 20 messages will be emitted, and then a summary
+with the total number of issues will be displayed at the end.
+
@ifclear GENERIC
@lowersections
@end ifclear