}
elf_aarch64_tdata (output_bfd)->sw_protections = *sw_protections;
+ /* Inherit the value from '-z gcs-report' if the option '-z gcs-report-dynamic'
+ was not set on the command line. However, the inheritance mechanism is
+ capped to avoid inheriting the error level from -g gcs-report as the user
+ might want to continue to build a module without rebuilding all the shared
+ libraries. If a user also wants to error GCS issues in the shared
+ libraries, '-z gcs-report-dynamic=error' will have to be specified
+ explicitly. */
+ if (sw_protections->gcs_report_dynamic == MARKING_UNSET)
+ elf_aarch64_tdata (output_bfd)->sw_protections.gcs_report_dynamic
+ = (sw_protections->gcs_report == MARKING_ERROR)
+ ? MARKING_WARN
+ : sw_protections->gcs_report;
+
elf_aarch64_tdata (output_bfd)->n_bti_issues = 0;
elf_aarch64_tdata (output_bfd)->n_gcs_issues = 0;
+ elf_aarch64_tdata (output_bfd)->n_gcs_dynamic_issues = 0;
setup_plt_values (link_info, sw_protections->plt_type);
}
{
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)
+ && tdata->sw_protections.bti_report != MARKING_NONE)
{
const char *msg
= (tdata->sw_protections.bti_report == MARKING_ERROR)
}
if (tdata->n_gcs_issues > GNU_PROPERTY_ISSUES_MAX
- && gcs_report != MARKING_NONE)
+ && tdata->sw_protections.gcs_report != MARKING_NONE)
{
const char *msg
= (tdata->sw_protections.gcs_report == MARKING_ERROR)
"GCS requirements.\n");
info->callbacks->einfo (msg, tdata->n_gcs_issues);
}
+
+ if (tdata->n_gcs_dynamic_issues > GNU_PROPERTY_ISSUES_MAX
+ && tdata->sw_protections.gcs_report_dynamic != MARKING_NONE)
+ {
+ const char *msg
+ = (tdata->sw_protections.gcs_report_dynamic == MARKING_ERROR)
+ ? _("%Xerror: found a total of %d dynamically-linked objects "
+ "incompatible with GCS requirements.\n")
+ : _("warning: found a total of %d dynamically-linked objects "
+ "incompatible with GCS requirements.\n");
+ info->callbacks->einfo (msg, tdata->n_gcs_dynamic_issues);
+ }
+}
+
+/* Perform a look-up of a property in an unsorted list of properties. This is
+ useful when the list of properties of an object has not been sorted yet. */
+static uint32_t
+_bfd_aarch64_prop_linear_lookup (elf_property_list *properties,
+ unsigned int pr_type)
+{
+ for (elf_property_list *p = properties; p != NULL; p = p->next)
+ if (p->property.pr_type == pr_type)
+ return p->property.u.number;
+ return 0;
+}
+
+/* Compare the GNU properties of the current dynamic object, with the ones of
+ the output BFD. Today, we only care about GCS feature stored in
+ GNU_PROPERTY_AARCH64_FEATURE_1. */
+static void
+_bfd_aarch64_compare_dynamic_obj_prop_against_outprop(
+ struct bfd_link_info *info,
+ const uint32_t outprop,
+ bfd *pbfd)
+{
+ if (!(outprop & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
+ return;
+
+ const uint32_t dyn_obj_aarch64_feature_prop =
+ _bfd_aarch64_prop_linear_lookup (elf_properties (pbfd),
+ GNU_PROPERTY_AARCH64_FEATURE_1_AND);
+ if (!(dyn_obj_aarch64_feature_prop & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
+ _bfd_aarch64_elf_check_gcs_report (info, pbfd);
+}
+
+/* Check compatibility between the GNU properties of the ouput BFD and the
+ linked dynamic objects. */
+static void
+_bfd_aarch64_elf_check_gnu_properties_linked_dynamic_objects (
+ struct bfd_link_info * info,
+ const uint32_t outprop)
+{
+ const struct elf_backend_data *bed = get_elf_backend_data (info->output_bfd);
+ const int elf_machine_code = bed->elf_machine_code;
+ const unsigned int elfclass = bed->s->elfclass;
+
+ for (bfd *pbfd = info->input_bfds; pbfd != NULL; pbfd = pbfd->link.next)
+ /* Ignore GNU properties from non-ELF objects or ELF objects with different
+ machine code. */
+ if ((pbfd->flags & DYNAMIC) != 0
+ && (bfd_get_flavour (pbfd) == bfd_target_elf_flavour)
+ && (get_elf_backend_data (pbfd)->elf_machine_code == elf_machine_code)
+ && (get_elf_backend_data (pbfd)->s->elfclass == elfclass))
+ _bfd_aarch64_compare_dynamic_obj_prop_against_outprop(info, outprop,
+ pbfd);
}
/* Find the first input bfd with GNU property and merge it with GPROP. If no
/* The property list is sorted in order of type. */
for (elf_property_list *p = elf_properties (pbfd);
(p != NULL)
- && (GNU_PROPERTY_AARCH64_FEATURE_1_AND <= p->property.pr_type);
+ && (GNU_PROPERTY_AARCH64_FEATURE_1_AND <= p->property.pr_type);
p = p->next)
{
/* This merge of features should happen only once as all the identical
}
}
+ tdata->gnu_property_aarch64_feature_1_and = outprop;
+
+ _bfd_aarch64_elf_check_gnu_properties_linked_dynamic_objects (info, outprop);
+
_bfd_aarch64_report_summary_merge_issues (info);
- tdata->gnu_property_aarch64_feature_1_and = outprop;
return pbfd;
}
_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);
+ bool dynamic_obj = (ebfd->flags & DYNAMIC) != 0;
- if (tdata->sw_protections.gcs_report == MARKING_NONE)
- return;
-
- ++tdata->n_gcs_issues;
-
- if (tdata->n_gcs_issues > GNU_PROPERTY_ISSUES_MAX)
- return;
+ if (dynamic_obj)
+ {
+ if (tdata->sw_protections.gcs_report_dynamic == MARKING_NONE)
+ return;
+ ++tdata->n_gcs_dynamic_issues;
+ if (tdata->n_gcs_dynamic_issues > GNU_PROPERTY_ISSUES_MAX)
+ return;
+ }
+ else
+ {
+ 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");
+ const char *msg;
+ if (dynamic_obj)
+ msg = (tdata->sw_protections.gcs_report_dynamic == MARKING_WARN)
+ ? _("%pB: warning: GCS is required by -z gcs, but this shared library "
+ "lacks the necessary property note. The dynamic loader might not "
+ "enable GCS or refuse to load the program unless all the shared "
+ "library dependencies have the GCS marking.\n")
+ : _("%X%pB: error: GCS is required by -z gcs, but this shared library "
+ "lacks the necessary property note. The dynamic loader might not "
+ "enable GCS or refuse to load the program unless all the shared "
+ "library dependencies have the GCS marking.\n");
+ else
+ 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);
}
MARKING_ERROR = 2, /* Emit error when the input objects are missing GNU
feature property markings, and the output has the
markings. */
+ MARKING_UNSET = 3, /* The only purpose of this value is to simulate an
+ optional to detect when the value was not initialized
+ from the command line. */
} aarch64_feature_marking_report;
/* To indicate whether GNU_PROPERTY_AARCH64_FEATURE_1_GCS bit is
} aarch64_gcs_type;
/* A structure to encompass all information about software protections coming
- from BTI or PAC related command line options. */
+ from BTI, PAC and GCS related command line options. */
struct aarch64_protection_opts
{
/* PLT type to use depending on the selected software proctections. */
/* Report level for GCS issues. */
aarch64_feature_marking_report gcs_report;
+
+ /* Report level for GCS issues with dynamic inputs. */
+ aarch64_feature_marking_report gcs_report_dynamic;
};
typedef struct aarch64_protection_opts aarch64_protection_opts;
/* Number of reported BTI issues. */
int n_bti_issues;
- /* Number of reported GCS issues. */
+ /* Number of reported GCS issues for non-dynamic objects. */
int n_gcs_issues;
+
+ /* Number of reported GCS issues for dynamic objects. */
+ int n_gcs_dynamic_issues;
};
#define elf_aarch64_tdata(bfd) \
.bti_report = MARKING_WARN,
.gcs_type = GCS_IMPLICIT,
.gcs_report = MARKING_WARN,
+ .gcs_report_dynamic = MARKING_UNSET,
};
#define COMPILE_TIME_STRLEN(s) \
aarch64_parse_feature_report_option (const char *_optarg,
const char *report_opt,
const size_t report_opt_len,
+ bool allow_empty_value,
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)
+ if (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 if (allow_empty_value && strlen (_optarg) == report_opt_len)
+ *level = MARKING_WARN;
else
einfo (_("%X%P: error: unrecognized value '-z %s'\n"), _optarg);
#define BTI_REPORT_LEN COMPILE_TIME_STRLEN (BTI_REPORT)
return aarch64_parse_feature_report_option (_optarg, BTI_REPORT,
- BTI_REPORT_LEN, &sw_protections.bti_report);
+ BTI_REPORT_LEN, true, &sw_protections.bti_report);
#undef BTI_REPORT
#undef BTI_REPORT_LEN
#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);
+ GCS_REPORT_LEN, true, &sw_protections.gcs_report);
#undef GCS_REPORT
#undef GCS_REPORT_LEN
}
+static bool
+aarch64_parse_gcs_report_dynamic_option (const char *_optarg)
+{
+ #define GCS_REPORT_DYNAMIC "gcs-report-dynamic"
+ #define GCS_REPORT_DYNAMIC_LEN COMPILE_TIME_STRLEN (GCS_REPORT_DYNAMIC)
+
+ return aarch64_parse_feature_report_option (_optarg, GCS_REPORT_DYNAMIC,
+ GCS_REPORT_DYNAMIC_LEN, false, &sw_protections.gcs_report_dynamic);
+
+ #undef GCS_REPORT_DYNAMIC
+ #undef GCS_REPORT_DYNAMIC_LEN
+}
+
static bool
aarch64_parse_gcs_option (const char *_optarg)
{
and output have GCS marking.\n\
error: Emit error when the input objects are missing GCS markings\n\
and output have GCS marking.\n"));
+ fprintf (file, _("\
+ -z gcs-report-dynamic=none|warning|error Emit warning/error on mismatch of GCS marking between the current link\n\
+ unit and input dynamic objects.\n\
+ none: Does not emit any warning/error messages.\n\
+ warning: 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='
{}
else if (strcmp (optarg, "pac-plt") == 0)
sw_protections.plt_type |= PLT_PAC;
+ else if (aarch64_parse_gcs_report_dynamic_option (optarg))
+ {}
else if (aarch64_parse_gcs_report_option (optarg))
{}
else if (aarch64_parse_gcs_option (optarg))
@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.
+The @samp{-z gcs-report[=none|warning|error]} option 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.
+composing the link unit are missing GCS markings.
+@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.
+
+@kindex -z gcs-report-dynamic=none|warning|error
+@cindex Control warnings for missing GCS markings on dynamic input objects.
+The @samp{-z gcs-report-dynamic=none|warning|error} option specifies how to
+report the missing GCS markings on dynamic input objects, i.e. the
+GNU_PROPERTY_AARCH64_FEATURE_1_GCS property. By default, if the option is
+omitted, it inherits the value of @samp{-z gcs-report}. However, the inherited
+value is capped to @samp{warning} as some user might want to only report errors
+in the currently built module, and not the shared dependencies. It is therefore
+necessary to use an explicit @samp{-z gcs-report-dynamic=error} option if you
+want the linker to error on GCS issues in the shared libraries.
+@itemize
+@item @samp{none} disables any warning messages.
+@item @samp{warning} emits warning messages when dynamic objects are missing
+GCS markings.
@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