mirror of
https://github.com/rui314/mold.git
synced 2026-11-09 04:49:33 -10:00
Support -z force-bti, pac-plt, gcs, bti-report and gcs-report
-z force-bti and -z pac-plt force the BTI and PAC bits of the output's GNU_PROPERTY_AARCH64_FEATURE_1_AND, and -z pac-plt also makes PLT entries authenticate .got.plt entries with autia1716 and sets DT_AARCH64_PAC_PLT. -z gcs=implicit|never|always controls the GCS bit. -z bti-report and -z gcs-report diagnose input files lacking the features, and as in lld, -z force-bti and -z gcs=always imply the warning level.
This commit is contained in:
1 parent
f03b745952
commit
48428d3890
12 files changed
+306
-15
No files matched your search
@@ -847,6 +847,42 @@ 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 gcs`=[ `implicit` | `always` | `never` ]:
|
||||
Guarded Control Stack (GCS) is an ARM64 shadow stack feature. With
|
||||
`implicit`, which is the default, `GNU_PROPERTY_AARCH64_FEATURE_1_GCS` bit
|
||||
in `.note.gnu.property` section is turned on only if all object files have
|
||||
the bit. `always` turns it on regardless of object files and implies `-z
|
||||
gcs-report=warning`. `never` turns it off.
|
||||
|
||||
* `-z gcs-report`=[ `warning` | `error` | `none` ]:
|
||||
Print out a warning or an error message if an object file lacks
|
||||
`GNU_PROPERTY_AARCH64_FEATURE_1_GCS` bit in its `.note.gnu.property`
|
||||
section.
|
||||
|
||||
* `-z pac-plt`:
|
||||
Generate an ARM64 PLT that authenticates function addresses read from
|
||||
`.got.plt` using Pointer Authentication (PAC), and turn on
|
||||
`GNU_PROPERTY_AARCH64_FEATURE_1_PAC` bit in `.note.gnu.property` section.
|
||||
The output works only with a dynamic linker that signs `.got.plt` entries.
|
||||
|
||||
* `-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
|
||||
|
||||
@@ -233,6 +233,9 @@ fn for_each_entry<E: Target>(ctx: &Context<E>, mut define: impl FnMut(u32, u64))
|
||||
if note_property::is_bti(ctx) {
|
||||
define(DT_AARCH64_BTI_PLT, 0);
|
||||
}
|
||||
if E::FAMILY == Family::Arm64 && ctx.args.z_pac_plt {
|
||||
define(DT_AARCH64_PAC_PLT, 0);
|
||||
}
|
||||
// RISC-V has the same feature but with a different name.
|
||||
if E::IS_RISCV && plt.symbols.iter().any(|&id| ctx.symbols[id].esym(ctx).riscv_variant_cc()) {
|
||||
define(DT_RISCV_VARIANT_CC, 0);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use crate::chunks::ChunkHeader;
|
||||
use crate::cmdline::GcsKind;
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
use crate::target::{Family, Target};
|
||||
@@ -101,6 +102,21 @@ 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;
|
||||
}
|
||||
if ctx.args.z_pac_plt {
|
||||
*features |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
|
||||
}
|
||||
match ctx.args.z_gcs {
|
||||
GcsKind::Implicit => {}
|
||||
GcsKind::Never => *features &= !GNU_PROPERTY_AARCH64_FEATURE_1_GCS,
|
||||
GcsKind::Always => *features |= GNU_PROPERTY_AARCH64_FEATURE_1_GCS,
|
||||
}
|
||||
}
|
||||
|
||||
ctx.note_property.as_mut().unwrap().contents =
|
||||
map.into_iter().filter(|&(_, v)| v != 0).collect();
|
||||
}
|
||||
|
||||
+7
-2
@@ -47,8 +47,13 @@ pub const SPARC_NUM_SMALL_PLT: u64 = (0x100000 - 128) / 32;
|
||||
/// The size of a PLT entry, for targets whose entries are uniformly sized.
|
||||
pub fn entry_size<E: Target>(ctx: &Context<E>) -> u64 {
|
||||
// ARM64 PLT entries grow from 16 to 24 bytes to make room for a `bti c`
|
||||
// landing pad if BTI is enabled.
|
||||
if note_property::is_bti(ctx) { 24 } else { E::PLT_SIZE }
|
||||
// landing pad if BTI is enabled and for an `autia1716` if -z pac-plt is
|
||||
// given.
|
||||
if E::FAMILY == Family::Arm64 && (note_property::is_bti(ctx) || ctx.args.z_pac_plt) {
|
||||
24
|
||||
} else {
|
||||
E::PLT_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
/// The offset of a PLT entry within `.plt`.
|
||||
|
||||
+58
-6
@@ -283,13 +283,21 @@ pub enum SeparateCodeKind {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
pub enum CetReportKind {
|
||||
pub enum ReportKind {
|
||||
#[default]
|
||||
None,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
pub enum GcsKind {
|
||||
#[default]
|
||||
Implicit,
|
||||
Never,
|
||||
Always,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
pub enum ShuffleSections {
|
||||
#[default]
|
||||
@@ -426,7 +434,10 @@ 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 z_gcs_report: ReportKind,
|
||||
pub z_gcs: GcsKind,
|
||||
pub undefined_glob: Glob,
|
||||
pub unique: Glob,
|
||||
pub z_separate_code: SeparateCodeKind,
|
||||
@@ -490,6 +501,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_ibt: bool,
|
||||
pub z_initfirst: bool,
|
||||
pub z_interpose: bool,
|
||||
@@ -497,6 +509,7 @@ pub struct Args {
|
||||
pub z_nodefaultlib: bool,
|
||||
pub z_now: bool,
|
||||
pub z_origin: bool,
|
||||
pub z_pac_plt: bool,
|
||||
pub z_relro: bool,
|
||||
pub z_rewrite_endbr: bool,
|
||||
pub z_rodynamic: bool,
|
||||
@@ -563,7 +576,10 @@ 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,
|
||||
z_gcs_report: ReportKind::None,
|
||||
z_gcs: GcsKind::Implicit,
|
||||
undefined_glob: Glob::new(),
|
||||
unique: Glob::new(),
|
||||
z_separate_code: SeparateCodeKind::NoSeparateCode,
|
||||
@@ -627,6 +643,7 @@ impl Default for Args {
|
||||
z_dynamic_undefined_weak: true,
|
||||
z_execstack: false,
|
||||
z_execstack_if_needed: false,
|
||||
z_force_bti: false,
|
||||
z_ibt: false,
|
||||
z_initfirst: false,
|
||||
z_interpose: false,
|
||||
@@ -634,6 +651,7 @@ impl Default for Args {
|
||||
z_nodefaultlib: false,
|
||||
z_now: false,
|
||||
z_origin: false,
|
||||
z_pac_plt: false,
|
||||
z_relro: true,
|
||||
z_rewrite_endbr: false,
|
||||
z_rodynamic: false,
|
||||
@@ -1214,6 +1232,8 @@ 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 z_gcs_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;
|
||||
@@ -1615,11 +1635,23 @@ 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("gcs-report=none") {
|
||||
z_gcs_report = Some(ReportKind::None);
|
||||
} else if cursor.read_z_flag("gcs-report=warning") {
|
||||
z_gcs_report = Some(ReportKind::Warning);
|
||||
} else if cursor.read_z_flag("gcs-report=error") {
|
||||
z_gcs_report = Some(ReportKind::Error);
|
||||
} else if cursor.read_z_flag("execstack") {
|
||||
a.z_execstack = true;
|
||||
} else if cursor.read_z_flag("execstack-if-needed") {
|
||||
@@ -1658,6 +1690,16 @@ 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("pac-plt") {
|
||||
a.z_pac_plt = true;
|
||||
} else if cursor.read_z_flag("gcs=implicit") {
|
||||
a.z_gcs = GcsKind::Implicit;
|
||||
} else if cursor.read_z_flag("gcs=never") {
|
||||
a.z_gcs = GcsKind::Never;
|
||||
} else if cursor.read_z_flag("gcs=always") {
|
||||
a.z_gcs = GcsKind::Always;
|
||||
} else if cursor.read_z_flag("muldefs") {
|
||||
a.allow_multiple_definition = true;
|
||||
} else if let Some(value) =
|
||||
@@ -2025,6 +2067,16 @@ 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`, and `-z gcs=always`
|
||||
// implies `-z gcs-report=warning`.
|
||||
a.z_bti_report =
|
||||
z_bti_report.unwrap_or(if a.z_force_bti { ReportKind::Warning } else { ReportKind::None });
|
||||
a.z_gcs_report = z_gcs_report.unwrap_or(if a.z_gcs == GcsKind::Always {
|
||||
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 {
|
||||
|
||||
+8
-1
@@ -327,9 +327,16 @@ 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` and `-z gcs-report`.
|
||||
if E::FAMILY == Family::Arm64
|
||||
&& (ctx.args.z_bti_report != cmdline::ReportKind::None
|
||||
|| ctx.args.z_gcs_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;
|
||||
|
||||
@@ -220,6 +220,7 @@ pub const DT_PPC_GOT: u32 = 0x70000000;
|
||||
pub const DT_PPC64_GLINK: u32 = 0x70000000;
|
||||
pub const DT_RISCV_VARIANT_CC: u32 = 0x70000001;
|
||||
pub const DT_AARCH64_BTI_PLT: u32 = 0x70000001;
|
||||
pub const DT_AARCH64_PAC_PLT: u32 = 0x70000003;
|
||||
pub const DT_AARCH64_VARIANT_PCS: u32 = 0x70000005;
|
||||
pub const DT_AUXILIARY: u32 = 0x7ffffffd;
|
||||
pub const DT_FILTER: u32 = 0x7fffffff;
|
||||
@@ -266,6 +267,8 @@ pub const GNU_PROPERTY_X86_ISA_1_V4: u32 = 8;
|
||||
|
||||
pub const GNU_PROPERTY_AARCH64_FEATURE_1_AND: u32 = 0xc0000000;
|
||||
pub const GNU_PROPERTY_AARCH64_FEATURE_1_BTI: u32 = 1;
|
||||
pub const GNU_PROPERTY_AARCH64_FEATURE_1_PAC: u32 = 2;
|
||||
pub const GNU_PROPERTY_AARCH64_FEATURE_1_GCS: u32 = 4;
|
||||
|
||||
pub const ELFCOMPRESS_NONE: u32 = 0;
|
||||
pub const ELFCOMPRESS_ZLIB: u32 = 1;
|
||||
|
||||
+33
-3
@@ -21,8 +21,8 @@ use crate::chunks::{
|
||||
self, ChunkHeader, ChunkId, OutputPhdr, OutputSectionId, compressed, copyrel, dynsym, reloc,
|
||||
};
|
||||
use crate::cmdline::{
|
||||
BsymbolicKind, BuildId, CetReportKind, DefsymValue, ReportOutput, SectionOrder,
|
||||
SeparateCodeKind, ShuffleSections, UnresolvedKind,
|
||||
BsymbolicKind, BuildId, DefsymValue, ReportKind, ReportOutput, SectionOrder, SeparateCodeKind,
|
||||
ShuffleSections, UnresolvedKind,
|
||||
};
|
||||
use crate::context::Context;
|
||||
use crate::elf::*;
|
||||
@@ -1510,7 +1510,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)
|
||||
};
|
||||
@@ -1532,6 +1532,36 @@ pub fn check_cet_errors<E: Target>(ctx: &Context<E>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_arm64_feature_errors<E: Target>(ctx: &Context<E>) {
|
||||
let has_feature = |file: &ObjectFile<E>, feature: u32| {
|
||||
file.gnu_properties
|
||||
.get(&GNU_PROPERTY_AARCH64_FEATURE_1_AND)
|
||||
.is_some_and(|v| v & feature != 0)
|
||||
};
|
||||
for file in &ctx.objs {
|
||||
if ctx.is_internal(file.id()) {
|
||||
continue;
|
||||
}
|
||||
for (kind, option, feature, name) in [
|
||||
(ctx.args.z_bti_report, "bti-report", GNU_PROPERTY_AARCH64_FEATURE_1_BTI, "BTI"),
|
||||
(ctx.args.z_gcs_report, "gcs-report", GNU_PROPERTY_AARCH64_FEATURE_1_GCS, "GCS"),
|
||||
] {
|
||||
if has_feature(file, feature) {
|
||||
continue;
|
||||
}
|
||||
match kind {
|
||||
ReportKind::None => {}
|
||||
ReportKind::Warning => warn!(
|
||||
"{file}: -z {option}=warning: missing GNU_PROPERTY_AARCH64_FEATURE_1_{name}"
|
||||
),
|
||||
ReportKind::Error => error!(
|
||||
"{file}: -z {option}=error: missing GNU_PROPERTY_AARCH64_FEATURE_1_{name}"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_dependencies<E: Target>(ctx: &Context<E>) {
|
||||
ReportOutput::Stdout.with_writer("--print-dependencies", |out| {
|
||||
writeln!(
|
||||
|
||||
+17
-3
@@ -225,8 +225,13 @@ impl<const LE: bool> Target for Arm64Target<LE> {
|
||||
}
|
||||
|
||||
// A PLT entry is 16 bytes long by default and 24 bytes long if BTI is
|
||||
// enabled. The extra space holds a `bti c` if the entry needs one, and
|
||||
// the rest of the entry is filled with brks.
|
||||
// enabled or -z pac-plt is given. The extra space is used for `bti c` if
|
||||
// the entry needs one and for `autia1716` if -z pac-plt is given, and the
|
||||
// rest is filled with brks.
|
||||
//
|
||||
// With -z pac-plt, the dynamic linker is expected to sign .got.plt entries
|
||||
// with `pacia1716` using the entry's address as a modifier, and the PLT
|
||||
// entry authenticates the address before jumping to it.
|
||||
fn write_plt_entry(ctx: &Context<Self>, buf: &mut [u8], sym: &Symbol) {
|
||||
const INSN: [u32; 6] = [
|
||||
0x9000_0010, // adrp x16, .got.plt[n]
|
||||
@@ -236,9 +241,18 @@ impl<const LE: bool> Target for Arm64Target<LE> {
|
||||
0xd420_7d00, // brk
|
||||
0xd420_7d00, // brk
|
||||
];
|
||||
const PAC_INSN: [u32; 6] = [
|
||||
0x9000_0010, // adrp x16, .got.plt[n]
|
||||
0xf940_0211, // ldr x17, [x16, .got.plt[n]]
|
||||
0x9100_0210, // add x16, x16, .got.plt[n]
|
||||
0xd503_219f, // autia1716
|
||||
0xd61f_0220, // br x17
|
||||
0xd420_7d00, // brk
|
||||
];
|
||||
let insn = if ctx.args.z_pac_plt { &PAC_INSN } else { &INSN };
|
||||
let off = write_landing_pad(buf, Self::plt_needs_landing_pad(ctx, sym));
|
||||
let buf = &mut buf[off..plt::entry_size(ctx) as usize];
|
||||
for (loc, &v) in buf.chunks_exact_mut(4).zip(&INSN) {
|
||||
for (loc, &v) in buf.chunks_exact_mut(4).zip(insn) {
|
||||
write_insn(loc, v);
|
||||
}
|
||||
let gotplt = sym.gotplt_addr(ctx);
|
||||
|
||||
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
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
# a.o is marked with BTI and GCS, and b.o only with BTI.
|
||||
cat <<EOF | $CC -c -o $t/a.o -xassembler-with-cpp -
|
||||
#define NT_GNU_PROPERTY_TYPE_0 5
|
||||
#define GNU_PROPERTY_AARCH64_FEATURE_1_AND 0xc0000000
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
ret
|
||||
|
||||
.section .note.gnu.property, "a"
|
||||
.p2align 3
|
||||
.long 4
|
||||
.long 1f - 0f
|
||||
.long NT_GNU_PROPERTY_TYPE_0
|
||||
.asciz "GNU"
|
||||
0:.long GNU_PROPERTY_AARCH64_FEATURE_1_AND
|
||||
.long 4
|
||||
.long 5
|
||||
.p2align 3
|
||||
1:
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -c -o $t/b.o -xassembler-with-cpp -
|
||||
#define NT_GNU_PROPERTY_TYPE_0 5
|
||||
#define GNU_PROPERTY_AARCH64_FEATURE_1_AND 0xc0000000
|
||||
|
||||
.globl foo
|
||||
foo:
|
||||
ret
|
||||
|
||||
.section .note.gnu.property, "a"
|
||||
.p2align 3
|
||||
.long 4
|
||||
.long 1f - 0f
|
||||
.long NT_GNU_PROPERTY_TYPE_0
|
||||
.asciz "GNU"
|
||||
0:.long GNU_PROPERTY_AARCH64_FEATURE_1_AND
|
||||
.long 4
|
||||
.long 1
|
||||
.p2align 3
|
||||
1:
|
||||
EOF
|
||||
|
||||
# Older readelf doesn't know the name of the GCS bit.
|
||||
./mold -o $t/exe1 $t/a.o
|
||||
readelf -n $t/exe1 | grep -E 'AArch64 feature: BTI, (GCS|<unknown: 4>)$'
|
||||
|
||||
./mold -o $t/exe2 $t/a.o -z gcs=never
|
||||
readelf -n $t/exe2 | grep 'AArch64 feature: BTI$'
|
||||
|
||||
./mold -o $t/exe3 $t/a.o $t/b.o 2> $t/log
|
||||
readelf -n $t/exe3 | grep 'AArch64 feature: BTI$'
|
||||
not grep . $t/log
|
||||
|
||||
./mold -o $t/exe4 $t/a.o $t/b.o -z gcs=always 2> $t/log
|
||||
readelf -n $t/exe4 | grep -E 'AArch64 feature: BTI, (GCS|<unknown: 4>)$'
|
||||
grep 'b.o: -z gcs-report=warning: missing GNU_PROPERTY_AARCH64_FEATURE_1_GCS' $t/log
|
||||
not grep a.o: $t/log
|
||||
|
||||
./mold -o $t/exe5 $t/a.o $t/b.o -z gcs=always -z gcs-report=none 2> $t/log
|
||||
not grep . $t/log
|
||||
|
||||
not ./mold -o $t/exe6 $t/a.o $t/b.o -z gcs-report=error 2> $t/log
|
||||
grep 'b.o: -z gcs-report=error: missing GNU_PROPERTY_AARCH64_FEATURE_1_GCS' $t/log
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
|
||||
void foo();
|
||||
void bar() { foo(); }
|
||||
EOF
|
||||
|
||||
./mold -shared -o $t/b.so $t/a.o -z pac-plt
|
||||
readelf -n $t/b.so | grep 'AArch64 feature: PAC'
|
||||
readelf --dynamic $t/b.so | grep AARCH64_PAC_PLT
|
||||
readelf --dynamic $t/b.so | not grep AARCH64_BTI_PLT
|
||||
|
||||
# A PLT entry authenticates the address in x17 before jumping to it.
|
||||
$OBJDUMP -d -j .plt $t/b.so | grep -A5 -E '<foo[@$]plt>:' > $t/log
|
||||
grep -A1 -w autia1716 $t/log | grep 'br.*x17'
|
||||
not grep -w bti $t/log
|
||||
|
||||
./mold -shared -o $t/b.so $t/a.o -z pac-plt -z force-bti
|
||||
readelf -n $t/b.so | grep 'AArch64 feature: BTI, PAC'
|
||||
readelf --dynamic $t/b.so | grep AARCH64_PAC_PLT
|
||||
readelf --dynamic $t/b.so | grep AARCH64_BTI_PLT
|
||||
$OBJDUMP -d -j .plt $t/b.so | grep -A5 -E '<foo[@$]plt>:' > $t/log
|
||||
grep -A1 -w autia1716 $t/log | grep 'br.*x17'
|
||||
Reference in new issue
Block a user