mirror of
https://github.com/rui314/mold.git
synced 2026-11-07 09:16:04 -10:00
Support -z force-bti and -z bti-report
-z force-bti turns on the BTI bit of the output's GNU_PROPERTY_AARCH64_FEATURE_1_AND, and -z bti-report diagnoses input files lacking it. As in lld, -z force-bti implies -z bti-report=warning. CetReportKind is renamed ReportKind because -z bti-report shares it.
This commit is contained in:
1 parent
5252daa5d8
commit
eec9e37a95
6 files changed
+112
-10
No files matched your search
@@ -853,6 +853,24 @@ point symbol in a script.
|
||||
`GNU_PROPERTY_X86_FEATURE_1_SHSTK` bit in `.note.gnu.property` section to
|
||||
determine whether or not an object file was compiled with `-fcf-protection`.
|
||||
|
||||
* `-z bti-report`=[ `warning` | `error` | `none` ]:
|
||||
Branch Target Identification (BTI) is an ARM64 feature to protect programs
|
||||
from control hijacking attacks by requiring indirect branches to land on
|
||||
`bti` instructions. You can tell the compiler to use the feature by
|
||||
specifying the `-mbranch-protection=bti` or `-mbranch-protection=standard`
|
||||
flag. An output file is marked as BTI-enabled only if all object files are.
|
||||
|
||||
`-z bti-report` flag is used to make sure that all object files were
|
||||
compiled with BTI. If `warning` or `error` are given, `mold` prints out a
|
||||
warning or an error message if an object file lacks
|
||||
`GNU_PROPERTY_AARCH64_FEATURE_1_BTI` bit in its `.note.gnu.property`
|
||||
section.
|
||||
|
||||
* `-z force-bti`:
|
||||
Turn on `GNU_PROPERTY_AARCH64_FEATURE_1_BTI` bit in `.note.gnu.property`
|
||||
section even if some object files were not compiled with BTI, and generate
|
||||
a PLT with BTI landing pads. This option implies `-z bti-report=warning`.
|
||||
|
||||
* `-z now`, `-z lazy`:
|
||||
By default, functions referring to other ELF modules are resolved by the
|
||||
dynamic linker when they are called for the first time. `-z now` marks an
|
||||
|
||||
@@ -101,6 +101,13 @@ pub fn construct<E: Target>(ctx: &mut Context<E>) {
|
||||
*map.entry(GNU_PROPERTY_X86_ISA_1_NEEDED).or_insert(0) |= ctx.args.z_x86_64_isa_level;
|
||||
}
|
||||
|
||||
if E::FAMILY == Family::Arm64 {
|
||||
let features = map.entry(GNU_PROPERTY_AARCH64_FEATURE_1_AND).or_insert(0);
|
||||
if ctx.args.z_force_bti {
|
||||
*features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.note_property.as_mut().unwrap().contents =
|
||||
map.into_iter().filter(|&(_, v)| v != 0).collect();
|
||||
}
|
||||
|
||||
+23
-6
@@ -286,7 +286,7 @@ pub enum SeparateCodeKind {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
pub enum CetReportKind {
|
||||
pub enum ReportKind {
|
||||
#[default]
|
||||
None,
|
||||
Warning,
|
||||
@@ -429,7 +429,8 @@ pub struct ReaderJob {
|
||||
pub struct Args {
|
||||
pub bsymbolic: BsymbolicKind,
|
||||
pub build_id: BuildId,
|
||||
pub z_cet_report: CetReportKind,
|
||||
pub z_cet_report: ReportKind,
|
||||
pub z_bti_report: ReportKind,
|
||||
pub undefined_glob: Glob,
|
||||
pub unique: Glob,
|
||||
pub z_separate_code: SeparateCodeKind,
|
||||
@@ -494,6 +495,7 @@ pub struct Args {
|
||||
pub z_dynamic_undefined_weak: bool,
|
||||
pub z_execstack: bool,
|
||||
pub z_execstack_if_needed: bool,
|
||||
pub z_force_bti: bool,
|
||||
pub z_global: bool,
|
||||
pub z_ibt: bool,
|
||||
pub z_initfirst: bool,
|
||||
@@ -568,7 +570,8 @@ impl Default for Args {
|
||||
Self {
|
||||
bsymbolic: BsymbolicKind::None,
|
||||
build_id: BuildId::default(),
|
||||
z_cet_report: CetReportKind::None,
|
||||
z_cet_report: ReportKind::None,
|
||||
z_bti_report: ReportKind::None,
|
||||
undefined_glob: Glob::new(),
|
||||
unique: Glob::new(),
|
||||
z_separate_code: SeparateCodeKind::NoSeparateCode,
|
||||
@@ -633,6 +636,7 @@ impl Default for Args {
|
||||
z_dynamic_undefined_weak: true,
|
||||
z_execstack: false,
|
||||
z_execstack_if_needed: false,
|
||||
z_force_bti: false,
|
||||
z_global: false,
|
||||
z_ibt: false,
|
||||
z_initfirst: false,
|
||||
@@ -1221,6 +1225,7 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
|
||||
let mut report_undefined: Option<bool> = None;
|
||||
let mut z_relro: Option<bool> = None;
|
||||
let mut z_dynamic_undefined_weak: Option<bool> = None;
|
||||
let mut z_bti_report: Option<ReportKind> = None;
|
||||
let mut separate_debug_file: Option<PathBuf> = None;
|
||||
// An explicit seed survives intervening --reverse-sections options.
|
||||
let mut shuffle_sections_seed: Option<u64> = None;
|
||||
@@ -1621,11 +1626,17 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
|
||||
} else if let Some(value) = cursor.read_z_switch("now", "lazy") {
|
||||
a.z_now = value;
|
||||
} else if cursor.read_z_flag("cet-report=none") {
|
||||
a.z_cet_report = CetReportKind::None;
|
||||
a.z_cet_report = ReportKind::None;
|
||||
} else if cursor.read_z_flag("cet-report=warning") {
|
||||
a.z_cet_report = CetReportKind::Warning;
|
||||
a.z_cet_report = ReportKind::Warning;
|
||||
} else if cursor.read_z_flag("cet-report=error") {
|
||||
a.z_cet_report = CetReportKind::Error;
|
||||
a.z_cet_report = ReportKind::Error;
|
||||
} else if cursor.read_z_flag("bti-report=none") {
|
||||
z_bti_report = Some(ReportKind::None);
|
||||
} else if cursor.read_z_flag("bti-report=warning") {
|
||||
z_bti_report = Some(ReportKind::Warning);
|
||||
} else if cursor.read_z_flag("bti-report=error") {
|
||||
z_bti_report = Some(ReportKind::Error);
|
||||
} else if cursor.read_z_flag("execstack") {
|
||||
a.z_execstack = true;
|
||||
} else if cursor.read_z_flag("execstack-if-needed") {
|
||||
@@ -1666,6 +1677,8 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
|
||||
} else if cursor.read_z_flag("ibt") {
|
||||
a.z_ibt = true;
|
||||
} else if cursor.read_z_flag("ibtplt") {
|
||||
} else if cursor.read_z_flag("force-bti") {
|
||||
a.z_force_bti = true;
|
||||
} else if cursor.read_z_flag("muldefs") {
|
||||
a.allow_multiple_definition = true;
|
||||
} else if let Some(value) =
|
||||
@@ -2037,6 +2050,10 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
|
||||
// `-z dynamic-undefined-weak` is enabled by default for DSOs.
|
||||
a.z_dynamic_undefined_weak = z_dynamic_undefined_weak.unwrap_or(a.shared);
|
||||
|
||||
// `-z force-bti` implies `-z bti-report=warning`.
|
||||
a.z_bti_report =
|
||||
z_bti_report.unwrap_or(if a.z_force_bti { ReportKind::Warning } else { ReportKind::None });
|
||||
|
||||
// --section-order implies `-z norelro`
|
||||
a.z_relro = z_relro.unwrap_or(a.section_order.is_empty());
|
||||
if a.nmagic || a.omagic {
|
||||
|
||||
+5
-1
@@ -316,9 +316,13 @@ pub fn link<E: Target>(cmdline: Cmdline) -> LinkResult {
|
||||
// or ctx.dsos.
|
||||
|
||||
// Handle `-z cet-report`.
|
||||
if ctx.args.z_cet_report != cmdline::CetReportKind::None {
|
||||
if ctx.args.z_cet_report != cmdline::ReportKind::None {
|
||||
passes::check_cet_errors(&ctx);
|
||||
}
|
||||
// Handle `-z bti-report`.
|
||||
if E::FAMILY == Family::Arm64 && ctx.args.z_bti_report != cmdline::ReportKind::None {
|
||||
passes::check_arm64_feature_errors(&ctx);
|
||||
}
|
||||
// Handle `-z execstack-if-needed`.
|
||||
if ctx.args.z_execstack_if_needed && ctx.objs.iter().any(|f| f.needs_executable_stack) {
|
||||
ctx.args.z_execstack = true;
|
||||
|
||||
+25
-3
@@ -31,8 +31,8 @@ use crate::chunks::{
|
||||
self, ChunkHeader, ChunkId, OutputPhdr, OutputSectionId, compressed, copyrel, dynsym, reloc,
|
||||
};
|
||||
use crate::cmdline::{
|
||||
BsymbolicKind, BuildId, CetReportKind, DebugCompression, DefsymValue, ReportOutput,
|
||||
SectionOrder, SeparateCodeKind, ShuffleSections, UnresolvedKind,
|
||||
BsymbolicKind, BuildId, DebugCompression, DefsymValue, ReportKind, ReportOutput, SectionOrder,
|
||||
SeparateCodeKind, ShuffleSections, UnresolvedKind,
|
||||
};
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
@@ -1524,7 +1524,7 @@ pub fn apply_section_align<E: Target>(ctx: &mut Context<E>) {
|
||||
}
|
||||
|
||||
pub fn check_cet_errors<E: Target>(ctx: &Context<E>) {
|
||||
let warning = ctx.args.z_cet_report == CetReportKind::Warning;
|
||||
let warning = ctx.args.z_cet_report == ReportKind::Warning;
|
||||
let has_feature = |file: &ObjectFile<E>, feature: u32| {
|
||||
file.gnu_properties.get(&GNU_PROPERTY_X86_FEATURE_1_AND).is_some_and(|v| v & feature != 0)
|
||||
};
|
||||
@@ -1546,6 +1546,28 @@ pub fn check_cet_errors<E: Target>(ctx: &Context<E>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_arm64_feature_errors<E: Target>(ctx: &Context<E>) {
|
||||
for file in &ctx.objs {
|
||||
if ctx.is_internal(file.id())
|
||||
|| file
|
||||
.gnu_properties
|
||||
.get(&GNU_PROPERTY_AARCH64_FEATURE_1_AND)
|
||||
.is_some_and(|v| v & GNU_PROPERTY_AARCH64_FEATURE_1_BTI != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
match ctx.args.z_bti_report {
|
||||
ReportKind::None => {}
|
||||
ReportKind::Warning => {
|
||||
warn!("{file}: -z bti-report=warning: missing GNU_PROPERTY_AARCH64_FEATURE_1_BTI")
|
||||
}
|
||||
ReportKind::Error => {
|
||||
error!("{file}: -z bti-report=error: missing GNU_PROPERTY_AARCH64_FEATURE_1_BTI")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_dependencies<E: Target>(ctx: &Context<E>) {
|
||||
ReportOutput::Stdout.with_writer("--print-dependencies", |out| {
|
||||
writeln!(
|
||||
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
test_cflags -mbranch-protection=bti || skip
|
||||
|
||||
cat <<EOF | $CC -fPIC -mbranch-protection=none -c -o $t/a.o -xc -
|
||||
void foo() {}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -mbranch-protection=bti -c -o $t/b.o -xc -
|
||||
void foo();
|
||||
void bar() { foo(); }
|
||||
EOF
|
||||
|
||||
./mold -shared -o $t/c.so $t/a.o $t/b.o
|
||||
readelf -n $t/c.so | not grep 'AArch64 feature'
|
||||
$OBJDUMP -d -j .plt $t/c.so | grep -A1 '<_PROCEDURE_LINKAGE_TABLE_>:' | not grep -w bti
|
||||
|
||||
./mold -shared -o $t/c.so $t/a.o $t/b.o -z force-bti 2> $t/log
|
||||
readelf -n $t/c.so | grep 'AArch64 feature: BTI'
|
||||
readelf --dynamic $t/c.so | grep AARCH64_BTI_PLT
|
||||
$OBJDUMP -d -j .plt $t/c.so | grep -A1 '<_PROCEDURE_LINKAGE_TABLE_>:' | grep -w bti
|
||||
grep 'a.o: -z bti-report=warning: missing GNU_PROPERTY_AARCH64_FEATURE_1_BTI' $t/log
|
||||
not grep b.o: $t/log
|
||||
|
||||
./mold -shared -o $t/c.so $t/a.o $t/b.o -z force-bti -z bti-report=none 2> $t/log
|
||||
not grep a.o: $t/log
|
||||
|
||||
./mold -shared -o $t/c.so $t/a.o $t/b.o -z bti-report=warning 2> $t/log
|
||||
readelf -n $t/c.so | not grep 'AArch64 feature'
|
||||
grep 'a.o: -z bti-report=warning: missing GNU_PROPERTY_AARCH64_FEATURE_1_BTI' $t/log
|
||||
|
||||
not ./mold -shared -o $t/c.so $t/a.o $t/b.o -z bti-report=error 2> $t/log
|
||||
grep 'a.o: -z bti-report=error: missing GNU_PROPERTY_AARCH64_FEATURE_1_BTI' $t/log
|
||||
Reference in new issue
Block a user