Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Comments

[WIP] consider order .init_array .fini_array#1253

Closed
YamasouA wants to merge 10 commits intodavidlattimore:mainfrom
YamasouA:ft/consider_order
Closed

[WIP] consider order .init_array .fini_array#1253
YamasouA wants to merge 10 commits intodavidlattimore:mainfrom
YamasouA:ft/consider_order

Conversation

@YamasouA
Copy link
Contributor

@YamasouA YamasouA commented Nov 1, 2025

Closes #588

@davidlattimore
Copy link
Owner

Great to see this progressing! Let me know if want any feedback yet or need any help

@YamasouA
Copy link
Contributor Author

YamasouA commented Nov 9, 2025

@davidlattimore
Thanks! A quick status update:

I’ve wired up the basic priority handling for .init_array / .fini_array and the legacy .ctors / .dtors, and all the unit tests are passing. I’m currently down to 6 failing integration tests, and in all of them the only remaining difference between wild and ld/lld is the order of entries in the final .init_array section (e.g. the ordering of frame_dummy, init_have_lse_atomics, ARGV_INIT_ARRAY, etc.).

Right now I’m trying to match GNU ld’s behavior more precisely here – especially how it derives init priorities from section names and how it handles legacy .ctors when converting them into .init_array (including the reversal of the ctor list). I have a rough plan, but I’m still figuring out the cleanest way to express this in wild’s codebase, so if you have any suggestions or pointers on how you’d structure this logic, I’d really appreciate your feedback.

@@ -191,8 +191,6 @@ impl Config {
".dynamic.DT_BIND_NOW",
".dynamic.DT_FLAGS.BIND_NOW",
// TODO: Implement proper ordering of .init .ctors etc
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can probably delete this TODO as well :)

&mut buffers,
group.eh_frame_start_address,
);
let mut sorted_sections = SortedSectionCollector::default();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to sort all the .init_array sections over all the input files, but here, a new SortedSectionCollector is being created for each group. See the groups_and_buffers.into_par_iter() above. So at the moment, we'll only be sorting within a group. Which files go in which groups depends on things like the number of threads. You can force each file to be in a separate group for testing purposes by setting the environment variable WILD_FILES_PER_GROUP=1. You might find that an init-order test that passes at the moment, fails if run with this variable set.

So I think this line will need to go in the outer level of write_file_contents. That would mean that it'd need to be shareable between threads. It could either be wrapped in a Mutex or the collection could be changed to a concurrent collection like crossbeam_queue::SeqQueue. However, even that, I don't think will work because the buffers are per-group, so when you try to call sorted_sections.flush after the try_for_each has finished, you won't be able to because you don't have access to a buffer. The problem is that the buffers for .init_array etc have been allocated to each group. I think we really want one buffer for the whole .init_array section that's the right size for all of the contributions coming from different input files. Most likely that one buffer would need to be allocated to the epilogue.

One way this might be achieved is during layout, the groups from which the .init_array sections originate, rather than requesting space in the .init_array output section, could ask the epilogue or prelude to do so. One way to do this might be to add a new variant to layout::WorkItem. The epilogue when it receives a request to handle a sorted section can record information about that section, allocate space for it etc. In Epilogue::finalise_layout, we can then sort those sections by their priority. Writing the output section would then be done entirely by the epilogue.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To add a little more detail and alternatives...

The new variant of WorkItem can probably be similar to WorkItem::LoadSection. It can probably even reuse SectionLoadRequest. e.g.`

LoadOrderedSection(SectionLoadRequest),

Such requests should only be sent to the epilogue, so if a non-epilogue group receives such a request, it's fine to panic.

An alternative that might be simpler would be to add an extra field to GraphResources. That field could hold for example an OutputSectionMap<crossbeam_queue::SegQueue<SectionToSort>> or something like that. Each input file, upon finding an input section that it wants sorted could add to that queue for the relevant output section. The epilogue, probably in finalise_sizes could take those queues and allocate whatever space is needed in each sorted output section. It could do the actual sorting either in finalise_sizes or finalise_layout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidlattimore
Thank you for your advice.!!
I try to Implement GraphResources field.

)? {
Some(x) => x,
None => {
// Allow symbols in epilogue-owned sorted sections to be resolved later.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this is tricky. I hadn't given much thought to symbols. The symbols are still owned by the object, it's just the section that is being written by the epilogue. I suspect we'd need the epilogue to determine the resolutions for the sections, then populate the relevant resolutions on the object so that the object can then resolve symbols that refer to that section. This is making me wonder if my suggestion to transfer ownership to the epilogue might have been a bad idea.

An alternative to transferring ownership would be to have multiple OutputSectionIds for .init_array, one for each used priority level. These would be merged together - see SectionKind::Secondary.

I've made a couple of refactorings that I hope would make that approach easier. It's possible that more refactorings still should be done. e.g. currently it might be a bit annoying to put __init_array_end in the correct place, although I think it should still be doable. It'd just need to be attached to the last OutputSectionId that merges into .init_array. Alternatively, we could add a second field similar to end_symbol_name, but which places the relevant symbol after any other secondary sections.

I'd be happy to go into more details if you'd like. I'm also happy to make more refactorings to make it easier.

Anyway, just know that there is an alternative if you decide that moving stuff to the epilogue is proving too difficult and sorry if I possibly sent you down the wrong path.

Copy link
Owner

@davidlattimore davidlattimore left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've only skimmed over the changes so far. One thing that I wanted to check is are the tests passing for you, but failing in CI? If so, make sure you have run_all_diffs = true in your test-config.toml.

});
}

#[tracing::instrument(skip_all, name = "Sort .eh_frame_hdr")]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line was removed in a separate PR. It's replaced by the timing_phase! line inside the function.

@davidlattimore
Copy link
Owner

If you're stuck, let me know and I'll take a look through what you've got and see if I've got ideas.

@YamasouA
Copy link
Contributor Author

YamasouA commented Dec 7, 2025

@davidlattimore
Hi!
All tests passed on my local environment.
I also confirmed that run_all_diffs = true is set.
However, the tests are still failing on CI.
What should I do to resolve this?

Details $ cargo test --profile ci --workspac Compiling libwild v0.7.0 (/workspaces/wild/libwild) Compiling wild-linker v0.7.0 (/workspaces/wild/wild) Finished `ci` profile [unoptimized] target(s) in 13.35s Running unittests src/lib.rs (target/ci/deps/libwild-05bee48f94dc9a2b)

running 47 tests
test alignment::test_align_down ... ok
test alignment::test_align_modulo ... ok
test alignment::test_align_up ... ok
test archive::tests::test_parse_decimal_int ... ok
test args::tests::test_arguments_from_string ... ok
test args::tests::test_ignored_flags ... ok
test archive::tests::test_ar_consistency ... ok
test args::tests::test_parse_file_only_options ... ok
test args::tests::test_parse_inline_only_options ... ok
test args::tests::test_parse_mixed_file_and_inline_options ... ok
test args::tests::test_parse_overlapping_file_and_inline_options ... ok
test export_list::tests::externs ... ok
test export_list::tests::parse_inline ... ok
test export_list::tests::parse_multiline_with_comments ... ok
test layout::test_no_disallowed_overlaps ... ok
test args::tests::test_parse_recursive_file_option ... ok
test layout_rules::test_section_mapping ... ok
test linker_script::tests::test_basic_linker_script ... ok
test linker_script::tests::test_inputs_from_script ... ok
test linker_script::tests::test_section_command ... ok
test linker_script::tests::test_sysroot_application ... ok
test linker_script::tests::test_test_inputs_from_script ... ok
test linker_script::tests::test_version_command ... ok
test linker_script::tests::test_version_command_with_nested_braces ... ok
test linker_script::tests::test_version_command_with_other_commands ... ok
test output_section_id::test_constant_ids ... ok
test linker_script::tests::test_version_script_parsing_from_version_command ... ok
test output_section_part_map::test_max_alignment ... ok
test output_section_part_map::test_merge ... ok
test output_section_part_map::test_merge_with_custom_sections ... ok
test output_section_part_map::test_merge_parts ... ok
test output_section_part_map::test_mut_with_map ... ok
test output_section_part_map::test_output_order_map ... ok
test part_id::tests::test_conversion_consistency ... ok
test resolution::section_slot_is_copy ... ok
test output_section_part_map::test_output_order_map_consistent ... ok
test version_script::tests::extern_c_version_script ... ok
test version_script::tests::extern_without_semicolon_version_script ... ok
test version_script::tests::invalid_version_scripts ... ok
test version_script::tests::extern_cxx_version_script ... ok
test version_script::tests::single_line_version_script ... ok
test version_script::tests::test_escape_sequences ... ok
test version_script::tests::test_negation ... ok
test version_script::tests::test_parse_simple_version_script ... ok
test version_script::tests::test_parse_version_script ... ok
test version_script::tests::test_version_order ... ok
test x86_64::test_relaxation ... ok

test result: ok. 47 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s

 Running unittests src/lib.rs (target/ci/deps/linker_diff-d6abcbf351d065b8)

running 1 test
test utils::test_align_up ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

 Running unittests src/bin/linker-diff.rs (target/ci/deps/linker_diff-c83d7018c4b689bc)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

 Running unittests src/lib.rs (target/ci/deps/linker_layout-fd40108ae1e06ec0)

running 1 test
test tests::test_round_trip ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

 Running unittests src/lib.rs (target/ci/deps/linker_trace-26531edaae0e0f02)

running 1 test
test tests::test_round_trip ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

 Running unittests src/lib.rs (target/ci/deps/linker_utils-13a92d3228d8928c)

running 8 tests
test bit_misc::tests::test_bit_operations ... ok
test bit_misc::tests::test_extract_bits_too_large - should panic ... ok
test bit_misc::tests::test_extract_bits_wrong_range - should panic ... ok
test bit_misc::tests::test_sign_extend ... ok
test elf::tests::test_range_from_byte_size ... ok
test elf::tests::test_rel_type_to_string ... ok
test riscv64::test_riscv_insn_immediate_mask ... ok
test riscv64::test_riscv_insn_rvcimmediate_mask ... ok

test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

 Running unittests src/main.rs (target/ci/deps/wild-c4c92bdd923d10eb)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

 Running tests/integration_tests.rs (target/ci/deps/integration_tests-aecb11bce6d7d44d)

running 88 tests
test integration_test::program_name_01___trivial_c__ ... ok
test integration_test::program_name_03___trivial_dynamic_c__ ... ok
test integration_test::program_name_04___link_args_c__ ... ok
test integration_test::program_name_05___global_definitions_c__ ... ok
test integration_test::program_name_06___data_c__ ... ok
test integration_test::program_name_07___data_pointers_c__ ... ok
test integration_test::program_name_08___weak_vars_c__ ... ok
test integration_test::program_name_09___weak_vars_archive_c__ ... ok
test integration_test::program_name_10___weak_fns_c__ ... ok
test integration_test::program_name_11___weak_fns_archive_c__ ... ok
test integration_test::program_name_12___init_test_c__ ... ok
test integration_test::program_name_13___ifunc_c__ ... ok
test integration_test::program_name_14___internal_syms_c__ ... ok
test integration_test::program_name_02___trivial_main_c__ ... ok
test integration_test::program_name_15___tls_c__ ... ok
test integration_test::program_name_17___tls_variant_c__ ... ok
test integration_test::program_name_18___no_start_c__ ... ok
test integration_test::program_name_19___old_init_c__ ... ok
test integration_test::program_name_20___custom_section_c__ ... ok
test integration_test::program_name_21___stack_alignment_s__ ... ok
test integration_test::program_name_22___got_ref_to_local_c__ ... ok
test integration_test::program_name_23___local_symbol_refs_s__ ... ok
test integration_test::program_name_24___archive_activation_c__ ... ok
test integration_test::program_name_25___relocation_in_non_alloc_section_s__ ... ok
test integration_test::program_name_26___exclude_libs_c__ ... ok
test integration_test::program_name_27___exclude_section_s__ ... ok
test integration_test::program_name_28___common_section_c__ ... ok
test integration_test::program_name_29___string_merging_c__ ... ok
test integration_test::program_name_30___non_string_merging_c__ ... ok
test integration_test::program_name_31___string_merge_missing_null_c__ ... ok
test integration_test::program_name_32___comments_c__ ... ok
test integration_test::program_name_33___custom_note_s__ ... ok
test integration_test::program_name_34___eh_frame_c__ ... ok
test integration_test::program_name_35___symbol_priority_c__ ... ok
test integration_test::program_name_36___hidden_ref_c__ ... ok
test integration_test::program_name_37___trivial_asm_s__ ... ok
test integration_test::program_name_38___non_alloc_s__ ... ok
test integration_test::program_name_39___gnu_unique_c__ ... ok
test integration_test::program_name_40___symbol_versions_c__ ... ok
test integration_test::program_name_41___mixed_verdef_verneed_c__ ... ok
test integration_test::program_name_42___copy_relocations_c__ ... ok
test integration_test::program_name_43___relocation_overflow_c__ ... ok
test integration_test::program_name_44___force_undefined_c__ ... ok
test integration_test::program_name_16___tlsdesc_c__ ... ok
test integration_test::program_name_45___wrap_c__ ... ok
test integration_test::program_name_47___linker_script_c__ ... ok
test integration_test::program_name_46___shlib_archive_activation_c__ ... ok
test integration_test::program_name_48___linker_script_executable_c__ ... ok
test integration_test::program_name_49___libc_ifunc_c__ ... ok
test integration_test::program_name_51___rust_integration_rs__ ... ok
test integration_test::program_name_52___rust_integration_dynamic_rs__ ... ok
test integration_test::program_name_53___tls_custom_c__ ... ok
test integration_test::program_name_54___cpp_integration_cc__ ... ok
test integration_test::program_name_55___rust_tls_rs__ ... ok
test integration_test::program_name_56___basic_comdat_s__ ... ok
test integration_test::program_name_57___input_does_not_exist_c__ ... ok
test integration_test::program_name_58___ifunc2_c__ ... ok
test integration_test::program_name_59___ctors_c__ ... ok
test integration_test::program_name_60___visibility_merging_c__ ... ok
test integration_test::program_name_61___tls_local_exec_c__ ... ok
test integration_test::program_name_50___libc_integration_c__ ... ok
test integration_test::program_name_63___undefined_symbols_c__ ... ok
test integration_test::program_name_62___tls_local_dynamic_c__ ... ok
test integration_test::program_name_65___whole_archive_c__ ... ok
test integration_test::program_name_66___entry_arg_c__ ... ok
test integration_test::program_name_67___dynamic_bss_only_c__ ... ok
test integration_test::program_name_68___shared_priority_c__ ... ok
test integration_test::program_name_64___shlib_undefined_c__ ... ok
test integration_test::program_name_70___duplicate_strong_symbols_c__ ... ok
test integration_test::program_name_69___shared_c__ ... ok
test integration_test::program_name_72___preinit_array_c__ ... ok
test integration_test::program_name_71___linker_plugin_lto_c__ ... ok
test integration_test::program_name_74___z_defs_c__ ... ok
test integration_test::program_name_75___export_dynamic_c__ ... ok
test integration_test::program_name_73___exception_cc__ ... ok
test integration_test::program_name_77___unresolved_symbols_shared_c__ ... ok
test integration_test::program_name_76___unresolved_symbols_object_c__ ... ok
test integration_test::program_name_79___symbol_version_symver_c__ ... ok
test integration_test::program_name_80___symbol_version_symver_error_c__ ... ok
test integration_test::program_name_78___lto_undefined_c__ ... ok
test integration_test::program_name_82___entry_in_shared_c__ ... ok
test integration_test::program_name_83___alignment_c__ ... ok
test integration_test::program_name_84___hash_style_c__ ... ok
test integration_test::program_name_81___output_kind_c__ ... ok
test integration_test::program_name_86___linker_script_defsym_notfound_c__ ... ok
test integration_test::program_name_85___defsym_c__ ... ok
test integration_test::program_name_87___tls_common_c__ ... ok
test tidy::check_sources_format ... ok

test result: ok. 88 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 21.17s

Doc-tests libwild

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Doc-tests linker_diff

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Doc-tests linker_layout

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Doc-tests linker_trace

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Doc-tests linker_utils

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

@lapla-cogito
Copy link
Collaborator

Have you confirmed that the test configuration is specified in test-config.toml? While Wild's repository includes test-config.toml.sample by default, simply modifying the settings within this file won't take effect (we assume you'll copy test-config.toml.sample to test-config.toml to actually update the contents).
In fact, when I synchronized the contents of test-config.toml with test-config-ci.toml, I could confirm that the test would fail with the following errors:

test output
running 88 tests
test integration_test::program_name_12___init_test_c__ ... ok
test integration_test::program_name_23___local_symbol_refs_s__ ... ok
test integration_test::program_name_14___internal_syms_c__ ... ok
test integration_test::program_name_21___stack_alignment_s__ ... ok
test integration_test::program_name_22___got_ref_to_local_c__ ... ok
test integration_test::program_name_05___global_definitions_c__ ... ok
test integration_test::program_name_19___old_init_c__ ... ok
test integration_test::program_name_06___data_c__ ... ok
test integration_test::program_name_31___string_merge_missing_null_c__ ... ok
test integration_test::program_name_25___relocation_in_non_alloc_section_s__ ... ok
test integration_test::program_name_33___custom_note_s__ ... ok
test integration_test::program_name_18___no_start_c__ ... ok
test integration_test::program_name_26___exclude_libs_c__ ... ok
test integration_test::program_name_32___comments_c__ ... ok
test integration_test::program_name_27___exclude_section_s__ ... ok
test integration_test::program_name_28___common_section_c__ ... ok
test integration_test::program_name_20___custom_section_c__ ... ok
test integration_test::program_name_34___eh_frame_c__ ... ok
test integration_test::program_name_39___gnu_unique_c__ ... ok
test integration_test::program_name_38___non_alloc_s__ ... ok
test integration_test::program_name_07___data_pointers_c__ ... ok
test integration_test::program_name_43___relocation_overflow_c__ ... ok
test integration_test::program_name_01___trivial_c__ ... ok
test integration_test::program_name_37___trivial_asm_s__ ... ok
test integration_test::program_name_30___non_string_merging_c__ ... ok
test integration_test::program_name_41___mixed_verdef_verneed_c__ ... ok
test integration_test::program_name_45___wrap_c__ ... ok
test integration_test::program_name_47___linker_script_c__ ... ok
test integration_test::program_name_35___symbol_priority_c__ ... ok
test integration_test::program_name_29___string_merging_c__ ... ok
test integration_test::program_name_42___copy_relocations_c__ ... ok
test integration_test::program_name_48___linker_script_executable_c__ ... ok
test integration_test::program_name_46___shlib_archive_activation_c__ ... ok
test integration_test::program_name_44___force_undefined_c__ ... ok
test integration_test::program_name_13___ifunc_c__ ... ok
test integration_test::program_name_56___basic_comdat_s__ ... ok
test integration_test::program_name_36___hidden_ref_c__ ... ok
test integration_test::program_name_57___input_does_not_exist_c__ ... ok
test integration_test::program_name_24___archive_activation_c__ ... ok
test integration_test::program_name_60___visibility_merging_c__ ... ok
test integration_test::program_name_04___link_args_c__ ... ok
test integration_test::program_name_03___trivial_dynamic_c__ ... ok
test integration_test::program_name_15___tls_c__ ... ok
test integration_test::program_name_65___whole_archive_c__ ... ok
test integration_test::program_name_40___symbol_versions_c__ ... ok
test integration_test::program_name_63___undefined_symbols_c__ ... ok
test integration_test::program_name_11___weak_fns_archive_c__ ... ok
test integration_test::program_name_10___weak_fns_c__ ... ok
test integration_test::program_name_67___dynamic_bss_only_c__ ... ok
test integration_test::program_name_66___entry_arg_c__ ... ok
test integration_test::program_name_08___weak_vars_c__ ... ok
test integration_test::program_name_09___weak_vars_archive_c__ ... ok
test integration_test::program_name_53___tls_custom_c__ ... ok
test integration_test::program_name_72___preinit_array_c__ ... ok
test integration_test::program_name_70___duplicate_strong_symbols_c__ ... ok
test integration_test::program_name_74___z_defs_c__ ... ok
test integration_test::program_name_68___shared_priority_c__ ... ok
test integration_test::program_name_49___libc_ifunc_c__ ... ok
test integration_test::program_name_59___ctors_c__ ... FAILED
test integration_test::program_name_80___symbol_version_symver_error_c__ ... ok
test integration_test::program_name_61___tls_local_exec_c__ ... ok
test integration_test::program_name_79___symbol_version_symver_c__ ... ok
test integration_test::program_name_58___ifunc2_c__ ... ok
test integration_test::program_name_86___linker_script_defsym_notfound_c__ ... ok
test integration_test::program_name_77___unresolved_symbols_shared_c__ ... ok
test integration_test::program_name_83___alignment_c__ ... ok
test integration_test::program_name_82___entry_in_shared_c__ ... ok
test integration_test::program_name_71___linker_plugin_lto_c__ ... ok
test integration_test::program_name_17___tls_variant_c__ ... ok
test integration_test::program_name_87___tls_common_c__ ... ok
test integration_test::program_name_85___defsym_c__ ... ok
test integration_test::program_name_84___hash_style_c__ ... ok
test integration_test::program_name_78___lto_undefined_c__ ... ok
test integration_test::program_name_75___export_dynamic_c__ ... ok
test integration_test::program_name_69___shared_c__ ... ok
test integration_test::program_name_62___tls_local_dynamic_c__ ... ok
test integration_test::program_name_76___unresolved_symbols_object_c__ ... ok
test tidy::check_sources_format ... ok
test integration_test::program_name_81___output_kind_c__ ... ok
test integration_test::program_name_64___shlib_undefined_c__ ... ok
test integration_test::program_name_73___exception_cc__ ... ok
test integration_test::program_name_55___rust_tls_rs__ ... FAILED
test integration_test::program_name_54___cpp_integration_cc__ ... ok
test integration_test::program_name_16___tlsdesc_c__ ... ok
test integration_test::program_name_52___rust_integration_dynamic_rs__ ... FAILED
test integration_test::program_name_02___trivial_main_c__ ... ok
test integration_test::program_name_51___rust_integration_rs__ ... FAILED
test integration_test::program_name_50___libc_integration_c__ ... ok

failures:

---- integration_test::program_name_59___ctors_c__ stdout ----
wild: /home/lapla/zatsu/wild_playground/wild/wild/tests/build/ctors.c-default-host.wild
ld: /home/lapla/zatsu/wild_playground/wild/wild/tests/build/ctors.c-default-host.ld
init_array
  ┌─────────────┬─────────────┐
  │ wild        │ ld          │
  ├─────────────┼─────────────┤
  │ frame_dummy │ frame_dummy │
  │ init1       │ init3       │
  │ init2       │ init2       │
  │ init3       │ init1       │
  └─────────────┴─────────────┘


Error: Validation failed.
Binary `/home/lapla/zatsu/wild_playground/wild/wild/tests/build/ctors.c-default-host.wild`. Relink with:
WILD_WRITE_LAYOUT=1 WILD_WRITE_TRACE=1 OUT=/home/lapla/zatsu/wild_playground/wild/wild/tests/build/ctors.c-default-host.wild /home/lapla/zatsu/wild_playground/wild/wild/tests/build/ctors.c-default-host.save/run-with cargo run --bin wild --
 To revalidate:
cargo run --bin linker-diff -- --wild-defaults --ignore 'section.data,section.rodata' --ref /home/lapla/zatsu/wild_playground/wild/wild/tests/build/ctors.c-default-host.ld /home/lapla/zatsu/wild_playground/wild/wild/tests/build/ctors.c-default-host.wild
To disable diff checking, set run_all_diffs=false in test config (see CONTRIBUTING.md)

---- integration_test::program_name_55___rust_tls_rs__ stdout ----
wild: /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-tls.rs-global-dynamic-host.wild
ld: /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-tls.rs-global-dynamic-host.ld
init_array
  ┌───────────────────────────────────────────────┬───────────────────────────────────────────────┐
  │ wild                                          │ ld                                            │
  ├───────────────────────────────────────────────┼───────────────────────────────────────────────┤
  │ frame_dummy                                   │ _RNvNvNtNtNtNtCsbEJXP1FjVBr_3std3sys4args4un+ │
  │ _RNvNvNtNtNtNtCsbEJXP1FjVBr_3std3sys4args4un+ │ frame_dummy                                   │
  └───────────────────────────────────────────────┴───────────────────────────────────────────────┘


Error: Validation failed.
Binary `/home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-tls.rs-global-dynamic-host.wild`. Relink with:
        OUT=/home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-tls.rs-global-dynamic-host.wild /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-tls.global-dynamic-host-58e7452a20a06237.d/run-with cargo run --bin wild -- --validate-output --write-layout --write-trace
 To revalidate:
cargo run --bin linker-diff -- --wild-defaults --ignore 'dynsym.*,.dynamic.DT_FLAGS.TEXTREL,.dynamic.DT_TEXTREL' --ref /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-tls.rs-global-dynamic-host.ld /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-tls.rs-global-dynamic-host.wild
To disable diff checking, set run_all_diffs=false in test config (see CONTRIBUTING.md)

---- integration_test::program_name_52___rust_integration_dynamic_rs__ stdout ----
wild: /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rdyn1.default-host-42b8612bee51627e.wild.so
ld: /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rdyn1.default-host-42b8612bee51627e.ld.so
init_array
  ┌───────────────────────────────────────────────┬───────────────────────────────────────────────┐
  │ wild                                          │ ld                                            │
  ├───────────────────────────────────────────────┼───────────────────────────────────────────────┤
  │ frame_dummy                                   │ _RNvNvNtNtNtNtCsbEJXP1FjVBr_3std3sys4args4un+ │
  │ _RNvNvNtNtNtNtCsbEJXP1FjVBr_3std3sys4args4un+ │ frame_dummy                                   │
  └───────────────────────────────────────────────┴───────────────────────────────────────────────┘


Error: Validation failed.
        OUT=/home/lapla/zatsu/wild_playground/wild/wild/tests/build/rdyn1.default-host-42b8612bee51627e.wild.so /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rdyn1.default-host-42b8612bee51627e.d/run-with cargo run --bin wild -- --validate-output --write-layout --write-trace
 To revalidate:
cargo run --bin linker-diff -- --wild-defaults --ignore '.dynamic.*,section.tdata.alignment,dynsym.*' --ref /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rdyn1.default-host-42b8612bee51627e.ld.so /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rdyn1.default-host-42b8612bee51627e.wild.so
To disable diff checking, set run_all_diffs=false in test config (see CONTRIBUTING.md)

---- integration_test::program_name_51___rust_integration_rs__ stdout ----
wild: /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-integration.rs-llvm-dynamic-host.wild
ld: /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-integration.rs-llvm-dynamic-host.ld
init_array
  ┌───────────────────────────────────────────────┬───────────────────────────────────────────────┐
  │ wild                                          │ ld                                            │
  ├───────────────────────────────────────────────┼───────────────────────────────────────────────┤
  │ frame_dummy                                   │ _RNvNvNtNtNtNtCsbEJXP1FjVBr_3std3sys4args4un+ │
  │ _RNvNvNtNtNtNtCsbEJXP1FjVBr_3std3sys4args4un+ │ frame_dummy                                   │
  └───────────────────────────────────────────────┴───────────────────────────────────────────────┘


Error: Validation failed.
Binary `/home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-integration.rs-llvm-dynamic-host.wild`. Relink with:
        OUT=/home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-integration.rs-llvm-dynamic-host.wild /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-integration.llvm-dynamic-host-211c857e7af44eae.d/run-with cargo run --bin wild -- --validate-output --write-layout --write-trace
 To revalidate:
cargo run --bin linker-diff -- --wild-defaults --ignore 'section.tdata.alignment,debug_info.missing_unit,dynsym.*,.dynamic.DT_JMPREL,.dynamic.DT_PLTGOT,.dynamic.DT_PLTREL' --ref /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-integration.rs-llvm-dynamic-host.ld /home/lapla/zatsu/wild_playground/wild/wild/tests/build/rust-integration.rs-llvm-dynamic-host.wild
To disable diff checking, set run_all_diffs=false in test config (see CONTRIBUTING.md)


failures:
    integration_test::program_name_51___rust_integration_rs__
    integration_test::program_name_52___rust_integration_dynamic_rs__
    integration_test::program_name_55___rust_tls_rs__
    integration_test::program_name_59___ctors_c__

test result: FAILED. 84 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in 4.78s

error: test failed, to rerun pass `-p wild-linker --test integration_tests`

@YamasouA
Copy link
Contributor Author

YamasouA commented Dec 7, 2025

@lapla-cogito
Thank you so much for the information.
Your advice helped me resolve the issue!

@YamasouA
Copy link
Contributor Author

YamasouA commented Dec 7, 2025

Right now the tests are failing in cases where multiple entries have the same priority.

In the GCC bug below, there is a comment that says:

You may also be able to play games
with object file order to control the order of constructors with the
same priority, but we don't document that anywhere, as far as I know.

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=46770

To me this suggests that the behaviour for constructors with the same priority isn’t really specified.

Given that, how would you prefer this to be implemented in Wild? Should we just pick a simple, deterministic rule (for example, based on object file order), or do you have a particular ordering in mind that we should try to match?

@davidlattimore
Copy link
Owner

Within a section, wild will write the input sections in order. That's not quite true if the input sections have different alignment, but I don't think any .init_array sections should have alignment other than 8. So for example, if we have two files that both have .init_array.1000, the contents of that section from the first file should be placed first.

I did some experiments to see what the linkers were doing. Firstly, I'd suggest not worrying about order of .ctors and .dtors. It looks like GNU ld is actually reversing not just the priority, but also the input order. So for .ctors, a .ctors.1000 in the first file would be placed after a .ctors.1000 in the second file. This would be pretty annoying to try to implement, so, I'd suggest saying that matching the ordering of .ctors and .dtors should be out of scope. I think ordering is much more important for .init_array and .fini_array, since .ctors and .dtors are long deprecated. LLD doesn't even run .ctors and .dtors if any files provide the new sections, so the fact that we run them, but don't guarantee the order should be enough.

Now, if we turn to .init_array* and .fini_array*, I think it'd be helpful to have an extra test. I've written one here. Feel free to copy it to add to your PR.

When we run that test with your PR, we get this:

    init_array
      ┌─────────────┬─────────────┐
      │ wild        │ ld          │
      ├─────────────┼─────────────┤
      │ init_a      │ init_1000a  │
      │ init_b      │ init_1000b  │
      │ init_65535a │ init_1000c  │
      │ init_1000a  │ init_1000d  │
      │ init_1000b  │ init_2000a  │
      │ init_2000a  │ init_2000b  │
      │ init_2000b  │ init_2000c  │
      │ init_2000c  │ init_2000d  │
      │ init_2000d  │ init_a      │
      │ init_1000c  │ init_b      │
      │ init_1000d  │ init_65535a │
      │ init_c      │ init_c      │
      │ init_d      │ init_d      │
      └─────────────┴─────────────┘

    fini_array
      ┌─────────────┬─────────────┐
      │ wild        │ ld          │
      ├─────────────┼─────────────┤
      │ fini_a      │ fini_1000a  │
      │ fini_b      │ fini_1000b  │
      │ fini_65535a │ fini_1000c  │
      │ fini_1000a  │ fini_1000d  │
      │ fini_1000b  │ fini_2000a  │
      │ fini_2000a  │ fini_2000b  │
      │ fini_2000b  │ fini_2000c  │
      │ fini_2000c  │ fini_2000d  │
      │ fini_2000d  │ fini_a      │
      │ fini_1000c  │ fini_b      │
      │ fini_1000d  │ fini_65535a │
      │ fini_c      │ fini_c      │
      │ fini_d      │ fini_d      │
      └─────────────┴─────────────┘

I was somewhat surprised to learn that the default priority is 65535 not 0, but I guess in retrospect that actually makes sense.

It looks like currently your PR isn't grouping together sections with the same priority. For example, init_1000* should all be together, but they're not at present.

@davidlattimore
Copy link
Owner

Note that if .ctor order is out-of-scope, then you'll need to add a couple of diff-ignore rules to the test ctors.c.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than changing merging_secondary_parts, would it work to sort the secondary sections by priority here?

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct InitFiniOrder {
pub(crate) priority: u16,
pub(crate) file_index: u32,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't thing file_index should be necessary. If we just have one secondary section per used priority level, then stuff within that secondary section should naturally end up in file order.

&non_dynamic.custom_sections,
non_dynamic.sections.as_mut_slice(),
);
assign_init_fini_secondaries(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assign_init_fini_secondaries looks like it might be expensive. It iterates over all sections, which means we'd be iterating over all sections of all objects from a single thread.

We might be able to do something similar to how custom sections are handled. i.e. add an extra field to non_dynamic that is similar to custom_sections, but instead of a Vec<CustomSectionDetails>, a Vec of the details that we need to generate the secondary sections. i.e. the primary section ID and the priority.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement proper ordering of .init_array, .ctors etc

3 participants