@@ -4508,6 +4508,85 @@ fn linker_run_safe_thunks_do_not_grow_small_programs() { |
| 4508 | let _ = fs::remove_file(out); | 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 | #[test] | 4590 | #[test] |
| 4512 | fn linker_run_places_thunks_in_caller_segment() { | 4591 | fn linker_run_places_thunks_in_caller_segment() { |
| 4513 | if !have_xcrun() || !have_tool("codesign") { | 4592 | if !have_xcrun() || !have_tool("codesign") { |