| 1 | use std::process::ExitCode; |
| 2 | |
| 3 | use afs_ld::{args, diag, dump, LinkError, Linker}; |
| 4 | |
| 5 | fn usage() -> &'static str { |
| 6 | "\ |
| 7 | Usage: afs-ld [options] <inputs...> |
| 8 | |
| 9 | Options: |
| 10 | -o <path> Write output to <path> |
| 11 | -dylib Emit a dylib instead of an executable |
| 12 | -e <symbol> Set the entry symbol |
| 13 | -arch arm64 Select the arm64 target |
| 14 | -map <path> Emit text link map |
| 15 | -why_live <symbol> Print a reachability chain for <symbol> |
| 16 | -l<name> / -l <name> Search for library |
| 17 | -L <dir> Add library search path |
| 18 | -framework <name> Link framework |
| 19 | -weak_framework <name> Link weak framework |
| 20 | -ObjC Objective-C archive loading mode (currently a no-op warning) |
| 21 | -syslibroot <path> Prefix SDK search roots |
| 22 | -platform_version macos <min> <sdk> |
| 23 | Set LC_BUILD_VERSION payload |
| 24 | -r Relocatable output (deferred; errors) |
| 25 | -bundle Bundle output (deferred; errors) |
| 26 | -undefined <error|warning|suppress|dynamic_lookup> |
| 27 | Control unresolved-symbol treatment |
| 28 | -rpath <path> Add LC_RPATH |
| 29 | -install_name <path> Override dylib install name |
| 30 | -current_version <v> Override dylib current version |
| 31 | -compatibility_version <v> Override dylib compatibility version |
| 32 | -exported_symbols_list <file> Export only symbols matching file patterns |
| 33 | -unexported_symbols_list <file> Hide symbols matching file patterns |
| 34 | -exported_symbol <sym> Export one symbol/pattern |
| 35 | -unexported_symbol <sym> Hide one symbol/pattern |
| 36 | -x Strip local symbols |
| 37 | -S Strip debug symbols (currently a no-op warning) |
| 38 | -no_uuid Omit LC_UUID |
| 39 | -no_loh Accepted for compatibility (currently warns; no effect) |
| 40 | -thunks=<none|safe|all> Configure branch thunks |
| 41 | -dead_strip Dead-strip unreferenced code/data |
| 42 | -icf=safe | -icf=none | -icf=all |
| 43 | Configure identical code folding (`all` currently errors) |
| 44 | -fixup_chains | -no_fixup_chains |
| 45 | Select chained fixups vs classic dyld info |
| 46 | -all_load Force-load every archive member |
| 47 | -force_load <archive> Force-load one archive |
| 48 | -j <jobs> Limit parallel worker jobs (`1` disables parallelism) |
| 49 | -Wl,<arg,arg,...> Normalize comma-separated driver flags |
| 50 | --dump <path> Dump a Mach-O file summary |
| 51 | --dump-archive <path> Dump an archive summary |
| 52 | --dump-dylib <path> Dump a dylib summary |
| 53 | --dump-tbd <path> Dump a TBD summary |
| 54 | -t, -trace Print input paths as they are loaded |
| 55 | -h, --help Show this help |
| 56 | -v, --version Show afs-ld version |
| 57 | " |
| 58 | } |
| 59 | |
| 60 | fn main() -> ExitCode { |
| 61 | let argv: Vec<String> = std::env::args().collect(); |
| 62 | |
| 63 | let opts = match args::parse(&argv[1..]) { |
| 64 | Ok(opts) => opts, |
| 65 | Err(e) => { |
| 66 | diag::error(&e.to_string()); |
| 67 | return ExitCode::from(2); |
| 68 | } |
| 69 | }; |
| 70 | |
| 71 | if opts.show_help { |
| 72 | print!("{}", usage()); |
| 73 | return ExitCode::SUCCESS; |
| 74 | } |
| 75 | |
| 76 | if opts.show_version { |
| 77 | println!("afs-ld {}", env!("CARGO_PKG_VERSION")); |
| 78 | return ExitCode::SUCCESS; |
| 79 | } |
| 80 | |
| 81 | if let Some(path) = &opts.dump { |
| 82 | return match dump::dump_file(path) { |
| 83 | Ok(()) => ExitCode::SUCCESS, |
| 84 | Err(e) => { |
| 85 | diag::error(&format!("{}: {}", path.display(), e)); |
| 86 | ExitCode::from(1) |
| 87 | } |
| 88 | }; |
| 89 | } |
| 90 | |
| 91 | if let Some(path) = &opts.dump_archive { |
| 92 | return match dump::dump_archive_file(path) { |
| 93 | Ok(()) => ExitCode::SUCCESS, |
| 94 | Err(e) => { |
| 95 | diag::error(&format!("{}: {}", path.display(), e)); |
| 96 | ExitCode::from(1) |
| 97 | } |
| 98 | }; |
| 99 | } |
| 100 | |
| 101 | if let Some(path) = &opts.dump_dylib { |
| 102 | return match dump::dump_dylib_file(path) { |
| 103 | Ok(()) => ExitCode::SUCCESS, |
| 104 | Err(e) => { |
| 105 | diag::error(&format!("{}: {}", path.display(), e)); |
| 106 | ExitCode::from(1) |
| 107 | } |
| 108 | }; |
| 109 | } |
| 110 | |
| 111 | if let Some(path) = &opts.dump_tbd { |
| 112 | return match dump::dump_tbd_file(path) { |
| 113 | Ok(()) => ExitCode::SUCCESS, |
| 114 | Err(e) => { |
| 115 | diag::error(&format!("{}: {}", path.display(), e)); |
| 116 | ExitCode::from(1) |
| 117 | } |
| 118 | }; |
| 119 | } |
| 120 | |
| 121 | match Linker::run(&opts) { |
| 122 | Ok(()) => ExitCode::SUCCESS, |
| 123 | Err(LinkError::NoInputs) => { |
| 124 | diag::error("no input files"); |
| 125 | ExitCode::from(2) |
| 126 | } |
| 127 | Err(LinkError::DuplicateSymbols(msg)) | Err(LinkError::UndefinedSymbols(msg)) => { |
| 128 | diag::error_verbatim(&msg); |
| 129 | ExitCode::from(1) |
| 130 | } |
| 131 | Err(e) => { |
| 132 | diag::error(&e.to_string()); |
| 133 | ExitCode::from(1) |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 |