linker.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. struct LinkerData {
  2. BlockingMutex foreign_mutex;
  3. PtrSet<Entity *> foreign_libraries_set;
  4. Array<Entity *> foreign_libraries;
  5. Array<String> output_object_paths;
  6. Array<String> output_temp_paths;
  7. String output_base;
  8. String output_name;
  9. #if defined(GB_SYSTEM_OSX)
  10. b8 needs_system_library_linked;
  11. #endif
  12. };
  13. gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...);
  14. gb_internal bool system_exec_command_line_app_output(char const *command, gbString *output);
  15. #if defined(GB_SYSTEM_OSX)
  16. gb_internal void linker_enable_system_library_linking(LinkerData *ld) {
  17. ld->needs_system_library_linked = 1;
  18. }
  19. #endif
  20. gb_internal void linker_data_init(LinkerData *ld, CheckerInfo *info, String const &init_fullpath) {
  21. gbAllocator ha = heap_allocator();
  22. array_init(&ld->output_object_paths, ha);
  23. array_init(&ld->output_temp_paths, ha);
  24. array_init(&ld->foreign_libraries, ha, 0, 1024);
  25. ptr_set_init(&ld->foreign_libraries_set, 1024);
  26. #if defined(GB_SYSTEM_OSX)
  27. ld->needs_system_library_linked = 0;
  28. #endif
  29. if (build_context.out_filepath.len == 0) {
  30. ld->output_name = remove_directory_from_path(init_fullpath);
  31. ld->output_name = remove_extension_from_path(ld->output_name);
  32. ld->output_name = string_trim_whitespace(ld->output_name);
  33. if (ld->output_name.len == 0) {
  34. ld->output_name = info->init_scope->pkg->name;
  35. }
  36. ld->output_base = ld->output_name;
  37. } else {
  38. ld->output_name = build_context.out_filepath;
  39. ld->output_name = string_trim_whitespace(ld->output_name);
  40. if (ld->output_name.len == 0) {
  41. ld->output_name = info->init_scope->pkg->name;
  42. }
  43. isize pos = string_extension_position(ld->output_name);
  44. if (pos < 0) {
  45. ld->output_base = ld->output_name;
  46. } else {
  47. ld->output_base = substring(ld->output_name, 0, pos);
  48. }
  49. }
  50. ld->output_base = path_to_full_path(ha, ld->output_base);
  51. }
  52. gb_internal i32 linker_stage(LinkerData *gen) {
  53. i32 result = 0;
  54. Timings *timings = &global_timings;
  55. String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
  56. debugf("Linking %.*s\n", LIT(output_filename));
  57. // TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`.
  58. if (is_arch_wasm()) {
  59. timings_start_section(timings, str_lit("wasm-ld"));
  60. gbString lib_str = gb_string_make(heap_allocator(), "");
  61. gbString extra_orca_flags = gb_string_make(temporary_allocator(), "");
  62. gbString inputs = gb_string_make(temporary_allocator(), "");
  63. inputs = gb_string_append_fmt(inputs, "\"%.*s.o\"", LIT(output_filename));
  64. for (Entity *e : gen->foreign_libraries) {
  65. GB_ASSERT(e->kind == Entity_LibraryName);
  66. // NOTE(bill): Add these before the linking values
  67. String extra_linker_flags = string_trim_whitespace(e->LibraryName.extra_linker_flags);
  68. if (extra_linker_flags.len != 0) {
  69. lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags));
  70. }
  71. for_array(i, e->LibraryName.paths) {
  72. String lib = e->LibraryName.paths[i];
  73. if (lib.len == 0) {
  74. continue;
  75. }
  76. if (!string_ends_with(lib, str_lit(".o"))) {
  77. continue;
  78. }
  79. inputs = gb_string_append_fmt(inputs, " \"%.*s\"", LIT(lib));
  80. }
  81. }
  82. if (build_context.metrics.os == TargetOs_orca) {
  83. gbString orca_sdk_path = gb_string_make(temporary_allocator(), "");
  84. if (!system_exec_command_line_app_output("orca sdk-path", &orca_sdk_path)) {
  85. gb_printf_err("executing `orca sdk-path` failed, make sure Orca is installed and added to your path\n");
  86. return 1;
  87. }
  88. if (gb_string_length(orca_sdk_path) == 0) {
  89. gb_printf_err("executing `orca sdk-path` did not produce output\n");
  90. return 1;
  91. }
  92. inputs = gb_string_append_fmt(inputs, " \"%s/orca-libc/lib/crt1.o\" \"%s/orca-libc/lib/libc.o\"", orca_sdk_path, orca_sdk_path);
  93. extra_orca_flags = gb_string_append_fmt(extra_orca_flags, " -L \"%s/bin\" -lorca_wasm --export-dynamic", orca_sdk_path);
  94. }
  95. #if defined(GB_SYSTEM_WINDOWS)
  96. result = system_exec_command_line_app("wasm-ld",
  97. "\"%.*s\\bin\\wasm-ld\" %s -o \"%.*s\" %.*s %.*s %s %s",
  98. LIT(build_context.ODIN_ROOT),
  99. inputs, LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags),
  100. lib_str,
  101. extra_orca_flags);
  102. #else
  103. result = system_exec_command_line_app("wasm-ld",
  104. "wasm-ld %s -o \"%.*s\" %.*s %.*s %s %s",
  105. inputs, LIT(output_filename),
  106. LIT(build_context.link_flags),
  107. LIT(build_context.extra_linker_flags),
  108. lib_str,
  109. extra_orca_flags);
  110. #endif
  111. return result;
  112. }
  113. if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
  114. #if defined(GB_SYSTEM_UNIX)
  115. result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
  116. LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
  117. #else
  118. gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
  119. LIT(target_os_names[build_context.metrics.os]),
  120. LIT(target_arch_names[build_context.metrics.arch])
  121. );
  122. #endif
  123. } else if (build_context.cross_compiling && build_context.different_os) {
  124. gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
  125. LIT(target_os_names[build_context.metrics.os]),
  126. LIT(target_arch_names[build_context.metrics.arch])
  127. );
  128. build_context.keep_object_files = true;
  129. } else {
  130. #if defined(GB_SYSTEM_WINDOWS)
  131. bool is_windows = true;
  132. #else
  133. bool is_windows = false;
  134. #endif
  135. #if defined(GB_SYSTEM_OSX)
  136. bool is_osx = true;
  137. #else
  138. bool is_osx = false;
  139. #endif
  140. if (is_windows) {
  141. String section_name = str_lit("msvc-link");
  142. if (build_context.use_lld) {
  143. section_name = str_lit("lld-link");
  144. }
  145. timings_start_section(timings, section_name);
  146. gbString lib_str = gb_string_make(heap_allocator(), "");
  147. defer (gb_string_free(lib_str));
  148. gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
  149. defer (gb_string_free(link_settings));
  150. // Add library search paths.
  151. if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
  152. String path = {};
  153. auto add_path = [&](String path) {
  154. if (path[path.len-1] == '\\') {
  155. path.len -= 1;
  156. }
  157. link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
  158. };
  159. add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename);
  160. add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename);
  161. add_path(build_context.build_paths[BuildPath_VS_LIB].basename);
  162. }
  163. StringSet min_libs_set = {};
  164. string_set_init(&min_libs_set, 64);
  165. defer (string_set_destroy(&min_libs_set));
  166. String prev_lib = {};
  167. StringSet asm_files = {};
  168. string_set_init(&asm_files, 64);
  169. defer (string_set_destroy(&asm_files));
  170. for (Entity *e : gen->foreign_libraries) {
  171. GB_ASSERT(e->kind == Entity_LibraryName);
  172. // NOTE(bill): Add these before the linking values
  173. String extra_linker_flags = string_trim_whitespace(e->LibraryName.extra_linker_flags);
  174. if (extra_linker_flags.len != 0) {
  175. lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags));
  176. }
  177. for_array(i, e->LibraryName.paths) {
  178. String lib = string_trim_whitespace(e->LibraryName.paths[i]);
  179. // IMPORTANT NOTE(bill): calling `string_to_lower` here is not an issue because
  180. // we will never uses these strings afterwards
  181. string_to_lower(&lib);
  182. if (lib.len == 0) {
  183. continue;
  184. }
  185. if (has_asm_extension(lib)) {
  186. if (!string_set_update(&asm_files, lib)) {
  187. String asm_file = lib;
  188. String obj_file = {};
  189. String temp_dir = temporary_directory(temporary_allocator());
  190. if (temp_dir.len != 0) {
  191. String filename = filename_without_directory(asm_file);
  192. gbString str = gb_string_make(heap_allocator(), "");
  193. str = gb_string_append_length(str, temp_dir.text, temp_dir.len);
  194. str = gb_string_appendc(str, "/");
  195. str = gb_string_append_length(str, filename.text, filename.len);
  196. str = gb_string_append_fmt(str, "-%p.obj", asm_file.text);
  197. obj_file = make_string_c(str);
  198. } else {
  199. obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
  200. }
  201. String obj_format = str_lit("win64");
  202. #if defined(GB_ARCH_32_BIT)
  203. obj_format = str_lit("win32");
  204. #endif
  205. result = system_exec_command_line_app("nasm",
  206. "\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" "
  207. "-f \"%.*s\" "
  208. "-o \"%.*s\" "
  209. "%.*s "
  210. "",
  211. LIT(build_context.ODIN_ROOT), LIT(asm_file),
  212. LIT(obj_format),
  213. LIT(obj_file),
  214. LIT(build_context.extra_assembler_flags)
  215. );
  216. if (result) {
  217. return result;
  218. }
  219. array_add(&gen->output_object_paths, obj_file);
  220. }
  221. } else if (!string_set_update(&min_libs_set, lib) ||
  222. !build_context.min_link_libs) {
  223. if (prev_lib != lib) {
  224. lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
  225. }
  226. prev_lib = lib;
  227. }
  228. }
  229. }
  230. if (build_context.build_mode == BuildMode_DynamicLibrary) {
  231. link_settings = gb_string_append_fmt(link_settings, " /DLL");
  232. } else {
  233. link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
  234. }
  235. if (build_context.pdb_filepath != "") {
  236. String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
  237. link_settings = gb_string_append_fmt(link_settings, " /PDB:\"%.*s\"", LIT(pdb_path));
  238. }
  239. if (build_context.build_mode != BuildMode_StaticLibrary) {
  240. if (build_context.no_crt) {
  241. link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
  242. } else {
  243. link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
  244. }
  245. }
  246. if (build_context.ODIN_DEBUG) {
  247. link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
  248. }
  249. gbString object_files = gb_string_make(heap_allocator(), "");
  250. defer (gb_string_free(object_files));
  251. for (String const &object_path : gen->output_object_paths) {
  252. object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
  253. }
  254. String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
  255. defer (gb_free(heap_allocator(), vs_exe_path.text));
  256. String windows_sdk_bin_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Win_SDK_Bin_Path]);
  257. defer (gb_free(heap_allocator(), windows_sdk_bin_path.text));
  258. if (!build_context.use_lld) { // msvc
  259. String res_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RES]);
  260. String rc_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RC]);
  261. defer (gb_free(heap_allocator(), res_path.text));
  262. defer (gb_free(heap_allocator(), rc_path.text));
  263. if (build_context.has_resource) {
  264. if (build_context.build_paths[BuildPath_RC].basename == "") {
  265. debugf("Using precompiled resource %.*s\n", LIT(res_path));
  266. } else {
  267. debugf("Compiling resource %.*s\n", LIT(res_path));
  268. result = system_exec_command_line_app("msvc-link",
  269. "\"%.*src.exe\" /nologo /fo %.*s %.*s",
  270. LIT(windows_sdk_bin_path),
  271. LIT(res_path),
  272. LIT(rc_path)
  273. );
  274. if (result) {
  275. return result;
  276. }
  277. }
  278. } else {
  279. res_path = {};
  280. }
  281. String linker_name = str_lit("link.exe");
  282. switch (build_context.build_mode) {
  283. case BuildMode_Executable:
  284. link_settings = gb_string_append_fmt(link_settings, " /NOIMPLIB /NOEXP");
  285. break;
  286. }
  287. switch (build_context.build_mode) {
  288. case BuildMode_StaticLibrary:
  289. linker_name = str_lit("lib.exe");
  290. break;
  291. default:
  292. link_settings = gb_string_append_fmt(link_settings, " /incremental:no /opt:ref");
  293. break;
  294. }
  295. result = system_exec_command_line_app("msvc-link",
  296. "\"%.*s%.*s\" %s %.*s -OUT:\"%.*s\" %s "
  297. "/nologo /subsystem:%.*s "
  298. "%.*s "
  299. "%.*s "
  300. "%s "
  301. "",
  302. LIT(vs_exe_path), LIT(linker_name), object_files, LIT(res_path), LIT(output_filename),
  303. link_settings,
  304. LIT(build_context.ODIN_WINDOWS_SUBSYSTEM),
  305. LIT(build_context.link_flags),
  306. LIT(build_context.extra_linker_flags),
  307. lib_str
  308. );
  309. if (result) {
  310. return result;
  311. }
  312. } else { // lld
  313. result = system_exec_command_line_app("msvc-lld-link",
  314. "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
  315. "/nologo /incremental:no /opt:ref /subsystem:%.*s "
  316. "%.*s "
  317. "%.*s "
  318. "%s "
  319. "",
  320. LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
  321. link_settings,
  322. LIT(build_context.ODIN_WINDOWS_SUBSYSTEM),
  323. LIT(build_context.link_flags),
  324. LIT(build_context.extra_linker_flags),
  325. lib_str
  326. );
  327. if (result) {
  328. return result;
  329. }
  330. }
  331. } else {
  332. timings_start_section(timings, str_lit("ld-link"));
  333. // Link using `clang`, unless overridden by `ODIN_CLANG_PATH` environment variable.
  334. const char* clang_path = gb_get_env("ODIN_CLANG_PATH", permanent_allocator());
  335. if (clang_path == NULL) {
  336. clang_path = "clang";
  337. }
  338. // NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
  339. char cwd[256];
  340. #if !defined(GB_SYSTEM_WINDOWS)
  341. getcwd(&cwd[0], 256);
  342. #endif
  343. //printf("%s\n", cwd);
  344. // NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
  345. // files can be passed with -l:
  346. gbString lib_str = gb_string_make(heap_allocator(), "-L/");
  347. defer (gb_string_free(lib_str));
  348. StringSet asm_files = {};
  349. string_set_init(&asm_files, 64);
  350. defer (string_set_destroy(&asm_files));
  351. StringSet min_libs_set = {};
  352. string_set_init(&min_libs_set, 64);
  353. defer (string_set_destroy(&min_libs_set));
  354. String prev_lib = {};
  355. for (Entity *e : gen->foreign_libraries) {
  356. GB_ASSERT(e->kind == Entity_LibraryName);
  357. // NOTE(bill): Add these before the linking values
  358. String extra_linker_flags = string_trim_whitespace(e->LibraryName.extra_linker_flags);
  359. if (extra_linker_flags.len != 0) {
  360. lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags));
  361. }
  362. for (String lib : e->LibraryName.paths) {
  363. lib = string_trim_whitespace(lib);
  364. if (lib.len == 0) {
  365. continue;
  366. }
  367. if (has_asm_extension(lib)) {
  368. if (string_set_update(&asm_files, lib)) {
  369. continue; // already handled
  370. }
  371. String asm_file = lib;
  372. String obj_file = {};
  373. String temp_dir = temporary_directory(temporary_allocator());
  374. if (temp_dir.len != 0) {
  375. String filename = filename_without_directory(asm_file);
  376. gbString str = gb_string_make(heap_allocator(), "");
  377. str = gb_string_append_length(str, temp_dir.text, temp_dir.len);
  378. str = gb_string_appendc(str, "/");
  379. str = gb_string_append_length(str, filename.text, filename.len);
  380. str = gb_string_append_fmt(str, "-%p.o", asm_file.text);
  381. obj_file = make_string_c(str);
  382. } else {
  383. obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".o"));
  384. }
  385. String obj_format;
  386. #if defined(GB_ARCH_64_BIT)
  387. if (is_osx) {
  388. obj_format = str_lit("macho64");
  389. } else {
  390. obj_format = str_lit("elf64");
  391. }
  392. #elif defined(GB_ARCH_32_BIT)
  393. if (is_osx) {
  394. obj_format = str_lit("macho32");
  395. } else {
  396. obj_format = str_lit("elf32");
  397. }
  398. #endif // GB_ARCH_*_BIT
  399. if (build_context.metrics.arch == TargetArch_riscv64) {
  400. result = system_exec_command_line_app("clang",
  401. "%s \"%.*s\" "
  402. "-c -o \"%.*s\" "
  403. "-target %.*s -march=rv64gc "
  404. "%.*s "
  405. "",
  406. clang_path,
  407. LIT(asm_file),
  408. LIT(obj_file),
  409. LIT(build_context.metrics.target_triplet),
  410. LIT(build_context.extra_assembler_flags)
  411. );
  412. } else if (is_osx) {
  413. // `as` comes with MacOS.
  414. result = system_exec_command_line_app("as",
  415. "as \"%.*s\" "
  416. "-o \"%.*s\" "
  417. "%.*s "
  418. "",
  419. LIT(asm_file),
  420. LIT(obj_file),
  421. LIT(build_context.extra_assembler_flags)
  422. );
  423. } else {
  424. // Note(bumbread): I'm assuming nasm is installed on the host machine.
  425. // Shipping binaries on unix-likes gets into the weird territorry of
  426. // "which version of glibc" is it linked with.
  427. result = system_exec_command_line_app("nasm",
  428. "nasm \"%.*s\" "
  429. "-f \"%.*s\" "
  430. "-o \"%.*s\" "
  431. "%.*s "
  432. "",
  433. LIT(asm_file),
  434. LIT(obj_format),
  435. LIT(obj_file),
  436. LIT(build_context.extra_assembler_flags)
  437. );
  438. if (result) {
  439. gb_printf_err("executing `nasm` to assemble foreing import of %.*s failed.\n\tSuggestion: `nasm` does not ship with the compiler and should be installed with your system's package manager.\n", LIT(asm_file));
  440. return result;
  441. }
  442. }
  443. array_add(&gen->output_object_paths, obj_file);
  444. } else {
  445. if (string_set_update(&min_libs_set, lib) && build_context.min_link_libs) {
  446. continue;
  447. }
  448. if (prev_lib == lib) {
  449. continue;
  450. }
  451. prev_lib = lib;
  452. // Do not add libc again, this is added later already, and omitted with
  453. // the `-no-crt` flag, not skipping here would cause duplicate library
  454. // warnings when linking on darwin and might link libc silently even with `-no-crt`.
  455. if (lib == str_lit("System.framework") || lib == str_lit("c")) {
  456. continue;
  457. }
  458. if (build_context.metrics.os == TargetOs_darwin) {
  459. if (string_ends_with(lib, str_lit(".framework"))) {
  460. // framework thingie
  461. String lib_name = lib;
  462. lib_name = remove_extension_from_path(lib_name);
  463. lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
  464. } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
  465. // For:
  466. // object
  467. // dynamic lib
  468. // static libs, absolute full path relative to the file in which the lib was imported from
  469. lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
  470. } else {
  471. // dynamic or static system lib, just link regularly searching system library paths
  472. lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
  473. }
  474. } else {
  475. // NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
  476. // since those are statically linked to at link time. shared libraries (.so) has to be
  477. // available at runtime wherever the executable is run, so we make require those to be
  478. // local to the executable (unless the system collection is used, in which case we search
  479. // the system library paths for the library file).
  480. if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".so")) || string_contains_string(lib, str_lit(".so."))) {
  481. lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
  482. } else {
  483. // dynamic or static system lib, just link regularly searching system library paths
  484. lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
  485. }
  486. }
  487. }
  488. }
  489. }
  490. gbString object_files = gb_string_make(heap_allocator(), "");
  491. defer (gb_string_free(object_files));
  492. for (String object_path : gen->output_object_paths) {
  493. object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
  494. }
  495. gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
  496. if (build_context.no_crt) {
  497. link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
  498. }
  499. if (build_context.build_mode == BuildMode_StaticLibrary) {
  500. compiler_error("TODO(bill): -build-mode:static on non-windows targets");
  501. }
  502. // NOTE(dweiler): We use clang as a frontend for the linker as there are
  503. // other runtime and compiler support libraries that need to be linked in
  504. // very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
  505. // These are not always typically inside /lib, /lib64, or /usr versions
  506. // of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on
  507. // the distribution of Linux even. The gcc or clang specs is the only
  508. // reliable way to query this information to call ld directly.
  509. if (build_context.build_mode == BuildMode_DynamicLibrary) {
  510. // NOTE(dweiler): Let the frontend know we're building a shared library
  511. // so it doesn't generate symbols which cannot be relocated.
  512. link_settings = gb_string_appendc(link_settings, "-shared ");
  513. // NOTE(dweiler): _odin_entry_point must be called at initialization
  514. // time of the shared object, similarly, _odin_exit_point must be called
  515. // at deinitialization. We can pass both -init and -fini to the linker by
  516. // using a comma separated list of arguments to -Wl.
  517. //
  518. // This previously used ld but ld cannot actually build a shared library
  519. // correctly this way since all the other dependencies provided implicitly
  520. // by the compiler frontend are still needed and most of the command
  521. // line arguments prepared previously are incompatible with ld.
  522. if (build_context.metrics.os == TargetOs_darwin) {
  523. link_settings = gb_string_appendc(link_settings, "-Wl,-init,'__odin_entry_point' ");
  524. // NOTE(weshardee): __odin_exit_point should also be added, but -fini
  525. // does not exist on MacOS
  526. } else {
  527. link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
  528. link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
  529. }
  530. } else if (build_context.metrics.os != TargetOs_openbsd && build_context.metrics.os != TargetOs_haiku && build_context.metrics.arch != TargetArch_riscv64) {
  531. // OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it.
  532. link_settings = gb_string_appendc(link_settings, "-no-pie ");
  533. }
  534. gbString platform_lib_str = gb_string_make(heap_allocator(), "");
  535. defer (gb_string_free(platform_lib_str));
  536. if (build_context.metrics.os == TargetOs_darwin) {
  537. platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib ");
  538. // Homebrew's default library path, checking if it exists to avoid linking warnings.
  539. if (gb_file_exists("/opt/homebrew/lib")) {
  540. platform_lib_str = gb_string_appendc(platform_lib_str, "-L/opt/homebrew/lib ");
  541. }
  542. // MacPort's default library path, checking if it exists to avoid linking warnings.
  543. if (gb_file_exists("/opt/local/lib")) {
  544. platform_lib_str = gb_string_appendc(platform_lib_str, "-L/opt/local/lib ");
  545. }
  546. // Only specify this flag if the user has given a minimum version to target.
  547. // This will cause warnings to show up for mismatched libraries.
  548. if (build_context.minimum_os_version_string_given) {
  549. link_settings = gb_string_append_fmt(link_settings, "-mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
  550. }
  551. if (build_context.build_mode != BuildMode_DynamicLibrary) {
  552. // This points the linker to where the entry point is
  553. link_settings = gb_string_appendc(link_settings, "-e _main ");
  554. }
  555. }
  556. if (!build_context.no_rpath) {
  557. // Set the rpath to the $ORIGIN/@loader_path (the path of the executable),
  558. // so that dynamic libraries are looked for at that path.
  559. if (build_context.metrics.os == TargetOs_darwin) {
  560. link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,@loader_path ");
  561. } else {
  562. link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,\\$ORIGIN ");
  563. }
  564. }
  565. if (!build_context.no_crt) {
  566. platform_lib_str = gb_string_appendc(platform_lib_str, "-lm ");
  567. if (build_context.metrics.os == TargetOs_darwin) {
  568. // NOTE: adding this causes a warning about duplicate libraries, I think it is
  569. // automatically assumed/added by clang when you don't do `-nostdlib`.
  570. // platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem ");
  571. } else {
  572. platform_lib_str = gb_string_appendc(platform_lib_str, "-lc ");
  573. }
  574. }
  575. gbString link_command_line = gb_string_make(heap_allocator(), clang_path);
  576. defer (gb_string_free(link_command_line));
  577. link_command_line = gb_string_appendc(link_command_line, " -Wno-unused-command-line-argument ");
  578. link_command_line = gb_string_appendc(link_command_line, object_files);
  579. link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
  580. link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
  581. link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
  582. link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
  583. link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
  584. link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
  585. if (build_context.use_lld) {
  586. link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=lld");
  587. result = system_exec_command_line_app("lld-link", link_command_line);
  588. } else {
  589. result = system_exec_command_line_app("ld-link", link_command_line);
  590. }
  591. if (result) {
  592. return result;
  593. }
  594. if (is_osx && build_context.ODIN_DEBUG) {
  595. // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
  596. // to the symbols in the object file
  597. result = system_exec_command_line_app("dsymutil", "dsymutil \"%.*s\"", LIT(output_filename));
  598. if (result) {
  599. return result;
  600. }
  601. }
  602. }
  603. }
  604. return result;
  605. }