@@ -4508,6 +4508,85 @@ fn linker_run_safe_thunks_do_not_grow_small_programs() { |
| 4508 | 4508 | let _ = fs::remove_file(out); |
| 4509 | 4509 | } |
| 4510 | 4510 | |
| 4511 | +#[test] |
| 4512 | +fn linker_run_thunks_all_forces_shared_thunk_for_in_range_calls() { |
| 4513 | + if !have_xcrun() || !have_tool("codesign") { |
| 4514 | + eprintln!("skipping: xcrun or codesign unavailable"); |
| 4515 | + return; |
| 4516 | + } |
| 4517 | + |
| 4518 | + let obj = scratch("branch26-thunks-all.o"); |
| 4519 | + let out = scratch("branch26-thunks-all.out"); |
| 4520 | + let src = r#" |
| 4521 | + .section __TEXT,__text,regular,pure_instructions |
| 4522 | + .globl _main |
| 4523 | + _main: |
| 4524 | + stp x29, x30, [sp, #-16]! |
| 4525 | + mov x29, sp |
| 4526 | + bl _helper |
| 4527 | + bl _helper |
| 4528 | + ldp x29, x30, [sp], #16 |
| 4529 | + ret |
| 4530 | + |
| 4531 | + _helper: |
| 4532 | + mov w0, #0 |
| 4533 | + ret |
| 4534 | + .subsections_via_symbols |
| 4535 | + "#; |
| 4536 | + if let Err(e) = assemble(src, &obj) { |
| 4537 | + eprintln!("skipping: assemble failed: {e}"); |
| 4538 | + return; |
| 4539 | + } |
| 4540 | + |
| 4541 | + let opts = LinkOptions { |
| 4542 | + inputs: vec![obj.clone()], |
| 4543 | + output: Some(out.clone()), |
| 4544 | + kind: OutputKind::Executable, |
| 4545 | + thunks: afs_ld::ThunkMode::All, |
| 4546 | + ..LinkOptions::default() |
| 4547 | + }; |
| 4548 | + Linker::run(&opts).unwrap(); |
| 4549 | + |
| 4550 | + let bytes = fs::read(&out).unwrap(); |
| 4551 | + let (text_addr, text) = output_section(&bytes, "__TEXT", "__text").unwrap(); |
| 4552 | + let (thunks_addr, thunks) = output_section(&bytes, "__TEXT", "__thunks").unwrap(); |
| 4553 | + assert_eq!( |
| 4554 | + thunks.len(), |
| 4555 | + 12, |
| 4556 | + "expected both in-range calls to share one forced thunk" |
| 4557 | + ); |
| 4558 | + assert_eq!( |
| 4559 | + decode_branch_target(&text, text_addr, 8).unwrap(), |
| 4560 | + thunks_addr, |
| 4561 | + "expected first BL to route through __thunks under -thunks=all" |
| 4562 | + ); |
| 4563 | + assert_eq!( |
| 4564 | + decode_branch_target(&text, text_addr, 12).unwrap(), |
| 4565 | + thunks_addr, |
| 4566 | + "expected second BL to share the same thunk target" |
| 4567 | + ); |
| 4568 | + |
| 4569 | + let verify = Command::new("codesign") |
| 4570 | + .arg("-v") |
| 4571 | + .arg(&out) |
| 4572 | + .output() |
| 4573 | + .unwrap(); |
| 4574 | + assert!( |
| 4575 | + verify.status.success(), |
| 4576 | + "codesign verify failed: {}", |
| 4577 | + String::from_utf8_lossy(&verify.stderr) |
| 4578 | + ); |
| 4579 | + let status = Command::new(&out).status().unwrap(); |
| 4580 | + assert_eq!( |
| 4581 | + status.code(), |
| 4582 | + Some(0), |
| 4583 | + "expected -thunks=all executable to exit 0" |
| 4584 | + ); |
| 4585 | + |
| 4586 | + let _ = fs::remove_file(obj); |
| 4587 | + let _ = fs::remove_file(out); |
| 4588 | +} |
| 4589 | + |
| 4511 | 4590 | #[test] |
| 4512 | 4591 | fn linker_run_places_thunks_in_caller_segment() { |
| 4513 | 4592 | if !have_xcrun() || !have_tool("codesign") { |