Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions libwild/src/args/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub struct ElfArgs {
pub(crate) lib_search_path: Vec<Box<Path>>,
pub(crate) dynamic_linker: Option<Box<Path>>,
pub(crate) strip: Strip,
pub(crate) only_keep_debug: bool,
pub(crate) merge_sections: bool,
pub(crate) version_script_path: Option<PathBuf>,
pub(crate) should_write_eh_frame_hdr: bool,
Expand Down Expand Up @@ -274,6 +275,7 @@ impl Default for ElfArgs {
should_output_partial_object: false,
dynamic_linker: None,
strip: Strip::Nothing,
only_keep_debug: false,
// For now, we default to --gc-sections. This is different to other linkers, but other
// than being different, there doesn't seem to be any downside to doing
// this. We don't currently do any less work if we're not GCing sections,
Expand Down Expand Up @@ -791,6 +793,15 @@ fn setup_argument_parser() -> ArgumentParser<ElfArgs> {
Ok(())
});

parser
.declare()
.long("only-keep-debug")
.help("Keep only debug sections, discarding content of others")
.execute(|args, _modifier_stack| {
args.only_keep_debug = true;
Ok(())
});

parser
.declare()
.long("gc-sections")
Expand Down Expand Up @@ -1869,6 +1880,10 @@ impl platform::Args for ElfArgs {
&mut self.common
}

fn should_only_keep_debug(&self) -> bool {
self.only_keep_debug
}

// TODO: Some linkers like ld and mold cleanup debug symbols when linking with -r. For now, we
// ignore --strip-all and --strip-debug in partial link mode.
fn should_strip_debug(&self) -> bool {
Expand Down
34 changes: 25 additions & 9 deletions libwild/src/elf_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1991,6 +1991,9 @@ fn write_object_section<'data, A: Arch<Platform = Elf>>(
}

let out = write_section_raw::<A>(object, layout, section, section_index, buffers)?;
if out.is_empty() {
return Ok(());
}

// We need to reverse the contents and adjust relocations because .ctors/.dtors are executed in
// reverse order while .init_array/.fini_array are executed in forward order.
Expand Down Expand Up @@ -2159,10 +2162,10 @@ fn write_section_raw<'out, 'data, A: Arch<Platform = Elf>>(
buffers: &'out mut OutputSectionPartMap<&mut [u8]>,
) -> Result<&'out mut [u8]> {
let part_id = object.section_part_id(section_index, &layout.symbol_db.section_part_ids);
if layout
.output_sections
.has_data_in_file(part_id.output_section_id())
{
if layout.output_sections.has_data_in_file(
part_id.output_section_id(),
layout.args().should_only_keep_debug(),
) {
let section_buffer = buffers.get_mut(part_id);
let allocation_size = sec.capacity(part_id, &layout.output_sections) as usize;
if section_buffer.len() < allocation_size {
Expand Down Expand Up @@ -3835,14 +3838,22 @@ fn write_merged_strings(
layout: &ElfLayout,
) {
layout.merged_strings.for_each(|section_id, merged| {
if merged.len() > 0 {
if merged.len() > 0
&& layout
.output_sections
.has_data_in_file(section_id, layout.args().should_only_keep_debug())
{
let buffer = buffers.get_mut(section_id.part_id_with_alignment(crate::alignment::MIN));

write_merged_strings_to_buffer(merged, buffer);
}
});

if layout.args().should_write_linker_identity {
if layout.args().should_write_linker_identity
&& layout.output_sections.has_data_in_file(
output_section_id::COMMENT,
layout.args().should_only_keep_debug(),
)
{
// Write linker identity into .comment section.
let comment_buffer =
buffers.get_mut(output_section_id::COMMENT.part_id_with_alignment(alignment::MIN));
Expand Down Expand Up @@ -5539,8 +5550,13 @@ fn write_section_headers(out: &mut [u8], layout: &ElfLayout) -> Result {
let entry = entries.next().unwrap();
let e = LittleEndian;
entry.sh_name.set(e, name_offset);

let sh_type = if layout.args().use_android_relr_tags && section_type == sht::RELR {
let sh_type = if layout.args().should_only_keep_debug()
&& section_type != sht::NULL
&& section_id.is_regular()
&& !layout.output_sections.is_debug_section(section_id)
{
sht::NOBITS
} else if layout.args().use_android_relr_tags && section_type == sht::RELR {
object::elf::SHT_ANDROID_RELR
} else {
section_type
Expand Down
20 changes: 15 additions & 5 deletions libwild/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2486,7 +2486,11 @@ impl<'data, P: Platform> GroupState<'data, P> {
dynstr_start_offset,
symtab_local_start_index,
symtab_global_start_index,
file_sizes: compute_file_sizes(&self.common.mem_sizes, resources.output_sections),
file_sizes: compute_file_sizes(
&self.common.mem_sizes,
resources.output_sections,
resources.symbol_db.args.should_only_keep_debug(),
),
mem_sizes: self.common.mem_sizes,
format_specific,
})
Expand Down Expand Up @@ -2813,9 +2817,10 @@ impl<'data, P: Platform> FileLayoutState<'data, P> {
fn compute_file_sizes<P: Platform>(
mem_sizes: &OutputSectionPartMap<u64>,
output_sections: &OutputSections<'_, P>,
only_keep_debug: bool,
) -> OutputSectionPartMap<usize> {
mem_sizes.map(|part_id, size| {
if output_sections.has_data_in_file(part_id.output_section_id()) {
if output_sections.has_data_in_file(part_id.output_section_id(), only_keep_debug) {
*size as usize
} else {
0
Expand Down Expand Up @@ -5333,7 +5338,8 @@ fn compute_layout_sections<'data, P: Platform>(
.is_some_and(|info| info.is_top_level);
if (section_id == merge_target || !is_top_level)
&& section_flags.is_alloc()
&& output_sections.has_data_in_file(merge_target)
&& output_sections
.has_data_in_file(merge_target, args.should_only_keep_debug())
{
let new_offset = offset
.checked_sub(mem_offset)
Expand Down Expand Up @@ -5367,7 +5373,9 @@ fn compute_layout_sections<'data, P: Platform>(

if section_flags.is_alloc() {
if args.should_output_partial_object() {
let file_size = if output_sections.has_data_in_file(merge_target) {
let file_size = if output_sections
.has_data_in_file(merge_target, args.should_only_keep_debug())
{
mem_size as usize
} else {
0
Expand All @@ -5393,7 +5401,9 @@ fn compute_layout_sections<'data, P: Platform>(
mem_offset = alignment.align_up(mem_offset);
lma_offset = alignment.align_up(lma_offset);

let file_size = if output_sections.has_data_in_file(merge_target) {
let file_size = if output_sections
.has_data_in_file(merge_target, args.should_only_keep_debug())
{
mem_size as usize
} else {
0
Expand Down
8 changes: 4 additions & 4 deletions libwild/src/macho_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,10 +681,10 @@ fn write_section_raw<'out, 'data>(
buffers: &'out mut OutputSectionPartMap<&mut [u8]>,
) -> Result<&'out mut [u8]> {
let part_id = object.section_part_id(section_index, &layout.symbol_db.section_part_ids);
if layout
.output_sections
.has_data_in_file(part_id.output_section_id())
{
if layout.output_sections.has_data_in_file(
part_id.output_section_id(),
layout.args().should_only_keep_debug(),
) {
let section_buffer = buffers.get_mut(part_id);
let allocation_size = sec.capacity(part_id, &layout.output_sections) as usize;
if section_buffer.len() < allocation_size {
Expand Down
22 changes: 19 additions & 3 deletions libwild/src/output_section_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -981,11 +981,27 @@ impl<'data, P: Platform> OutputSections<'data, P> {
self.section_infos.len() - NUM_SINGLE_PART_SECTIONS as usize
}

pub(crate) fn has_data_in_file(&self, section_id: OutputSectionId) -> bool {
let attributes = self.output_info(section_id).section_attributes;
!attributes.is_no_bits()
pub(crate) fn is_debug_section(&self, section_id: OutputSectionId) -> bool {
match self.output_info(section_id).kind {
SectionKind::Primary(section_name) => section_name.0.starts_with(b".debug_"),
SectionKind::Secondary(primary_id) => self.is_debug_section(primary_id),
}
}

pub(crate) fn has_data_in_file(
&self,
section_id: OutputSectionId,
only_keep_debug: bool,
) -> bool {
let attributes = self.output_info(section_id).section_attributes;
if attributes.is_no_bits() {
return false;
}
if only_keep_debug && section_id.is_regular() && !self.is_debug_section(section_id) {
return false;
}
true
}
pub(crate) fn output_info(&self, id: OutputSectionId) -> &SectionOutputInfo<'data, P> {
self.section_infos.get(id)
}
Expand Down
4 changes: 4 additions & 0 deletions libwild/src/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,10 @@ pub(crate) trait Args: std::fmt::Debug + Send + Sync + 'static {

fn should_strip_all(&self) -> bool;

fn should_only_keep_debug(&self) -> bool {
false
}

/// Returns whether a symbol with the specified name should be stripped. Should return false if
/// name-based stripping is not being applied.
fn should_strip_symbol_named(&self, _name: &[u8]) -> bool {
Expand Down
Loading