linker.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995
  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. bool needs_system_library_linked;
  10. };
  11. gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...);
  12. gb_internal bool system_exec_command_line_app_output(char const *command, gbString *output);
  13. gb_internal void linker_enable_system_library_linking(LinkerData *ld) {
  14. ld->needs_system_library_linked = true;
  15. }
  16. gb_internal void linker_data_init(LinkerData *ld, CheckerInfo *info, String const &init_fullpath) {
  17. gbAllocator ha = heap_allocator();
  18. array_init(&ld->output_object_paths, ha);
  19. array_init(&ld->output_temp_paths, ha);
  20. array_init(&ld->foreign_libraries, ha, 0, 1024);
  21. ptr_set_init(&ld->foreign_libraries_set, 1024);
  22. ld->needs_system_library_linked = false;
  23. if (build_context.out_filepath.len == 0) {
  24. ld->output_name = remove_directory_from_path(init_fullpath);
  25. ld->output_name = remove_extension_from_path(ld->output_name);
  26. ld->output_name = string_trim_whitespace(ld->output_name);
  27. if (ld->output_name.len == 0) {
  28. ld->output_name = info->init_scope->pkg->name;
  29. }
  30. ld->output_base = ld->output_name;
  31. } else {
  32. ld->output_name = build_context.out_filepath;
  33. ld->output_name = string_trim_whitespace(ld->output_name);
  34. if (ld->output_name.len == 0) {
  35. ld->output_name = info->init_scope->pkg->name;
  36. }
  37. isize pos = string_extension_position(ld->output_name);
  38. if (pos < 0) {
  39. ld->output_base = ld->output_name;
  40. } else {
  41. ld->output_base = substring(ld->output_name, 0, pos);
  42. }
  43. }
  44. ld->output_base = path_to_full_path(ha, ld->output_base);
  45. }
  46. gb_internal i32 linker_stage(LinkerData *gen) {
  47. i32 result = 0;
  48. Timings *timings = &global_timings;
  49. String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
  50. debugf("Linking %.*s\n", LIT(output_filename));
  51. // TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`.
  52. if (is_arch_wasm()) {
  53. timings_start_section(timings, str_lit("wasm-ld"));
  54. gbString lib_str = gb_string_make(heap_allocator(), "");
  55. gbString extra_orca_flags = gb_string_make(temporary_allocator(), "");
  56. gbString inputs = gb_string_make(temporary_allocator(), "");
  57. inputs = gb_string_append_fmt(inputs, "\"%.*s.o\"", LIT(output_filename));
  58. for (Entity *e : gen->foreign_libraries) {
  59. GB_ASSERT(e->kind == Entity_LibraryName);
  60. // NOTE(bill): Add these before the linking values
  61. String extra_linker_flags = string_trim_whitespace(e->LibraryName.extra_linker_flags);
  62. if (extra_linker_flags.len != 0) {
  63. lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags));
  64. }
  65. for_array(i, e->LibraryName.paths) {
  66. String lib = e->LibraryName.paths[i];
  67. if (lib.len == 0) {
  68. continue;
  69. }
  70. if (!string_ends_with(lib, str_lit(".o"))) {
  71. continue;
  72. }
  73. inputs = gb_string_append_fmt(inputs, " \"%.*s\"", LIT(lib));
  74. }
  75. }
  76. if (build_context.metrics.os == TargetOs_orca) {
  77. gbString orca_sdk_path = gb_string_make(temporary_allocator(), "");
  78. if (!system_exec_command_line_app_output("orca sdk-path", &orca_sdk_path)) {
  79. gb_printf_err("executing `orca sdk-path` failed, make sure Orca is installed and added to your path\n");
  80. return 1;
  81. }
  82. if (gb_string_length(orca_sdk_path) == 0) {
  83. gb_printf_err("executing `orca sdk-path` did not produce output\n");
  84. return 1;
  85. }
  86. inputs = gb_string_append_fmt(inputs, " \"%s/orca-libc/lib/crt1.o\" \"%s/orca-libc/lib/libc.o\"", orca_sdk_path, orca_sdk_path);
  87. extra_orca_flags = gb_string_append_fmt(extra_orca_flags, " -L \"%s/bin\" -lorca_wasm --export-dynamic", orca_sdk_path);
  88. }
  89. #if defined(GB_SYSTEM_WINDOWS)
  90. result = system_exec_command_line_app("wasm-ld",
  91. "\"%.*s\\bin\\wasm-ld\" %s -o \"%.*s\" %.*s %.*s %s %s",
  92. LIT(build_context.ODIN_ROOT),
  93. inputs, LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags),
  94. lib_str,
  95. extra_orca_flags);
  96. #else
  97. result = system_exec_command_line_app("wasm-ld",
  98. "wasm-ld %s -o \"%.*s\" %.*s %.*s %s %s",
  99. inputs, LIT(output_filename),
  100. LIT(build_context.link_flags),
  101. LIT(build_context.extra_linker_flags),
  102. lib_str,
  103. extra_orca_flags);
  104. #endif
  105. return result;
  106. }
  107. bool is_cross_linking = false;
  108. bool is_android = false;
  109. if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
  110. #if defined(GB_SYSTEM_UNIX)
  111. result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
  112. LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
  113. #else
  114. gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
  115. LIT(target_os_names[build_context.metrics.os]),
  116. LIT(target_arch_names[build_context.metrics.arch])
  117. );
  118. #endif
  119. } else if (build_context.cross_compiling && (build_context.different_os || selected_subtarget != Subtarget_Default)) {
  120. switch (selected_subtarget) {
  121. case Subtarget_Android:
  122. is_cross_linking = true;
  123. is_android = true;
  124. goto try_cross_linking;
  125. default:
  126. gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
  127. LIT(target_os_names[build_context.metrics.os]),
  128. LIT(target_arch_names[build_context.metrics.arch])
  129. );
  130. build_context.keep_object_files = true;
  131. break;
  132. }
  133. } else {
  134. try_cross_linking:;
  135. #if defined(GB_SYSTEM_WINDOWS)
  136. String section_name = str_lit("msvc-link");
  137. bool is_windows = build_context.metrics.os == TargetOs_windows;
  138. #else
  139. String section_name = str_lit("lld-link");
  140. bool is_windows = false;
  141. #endif
  142. bool is_osx = build_context.metrics.os == TargetOs_darwin;
  143. switch (build_context.linker_choice) {
  144. case Linker_Default: break;
  145. case Linker_lld: section_name = str_lit("lld-link"); break;
  146. #if defined(GB_SYSTEM_LINUX)
  147. case Linker_mold: section_name = str_lit("mold-link"); break;
  148. #endif
  149. #if defined(GB_SYSTEM_WINDOWS)
  150. case Linker_radlink: section_name = str_lit("rad-link"); break;
  151. #endif
  152. default:
  153. gb_printf_err("'%.*s' linker is not support for this platform\n", LIT(linker_choices[build_context.linker_choice]));
  154. return 1;
  155. }
  156. if (is_windows) {
  157. timings_start_section(timings, section_name);
  158. gbString lib_str = gb_string_make(heap_allocator(), "");
  159. defer (gb_string_free(lib_str));
  160. gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
  161. defer (gb_string_free(link_settings));
  162. // Add library search paths.
  163. if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
  164. String path = {};
  165. auto add_path = [&](String path) {
  166. if (path[path.len-1] == '\\') {
  167. path.len -= 1;
  168. }
  169. link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
  170. };
  171. add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename);
  172. add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename);
  173. add_path(build_context.build_paths[BuildPath_VS_LIB].basename);
  174. }
  175. StringSet min_libs_set = {};
  176. string_set_init(&min_libs_set, 64);
  177. defer (string_set_destroy(&min_libs_set));
  178. String prev_lib = {};
  179. StringSet asm_files = {};
  180. string_set_init(&asm_files, 64);
  181. defer (string_set_destroy(&asm_files));
  182. for (Entity *e : gen->foreign_libraries) {
  183. GB_ASSERT(e->kind == Entity_LibraryName);
  184. // NOTE(bill): Add these before the linking values
  185. String extra_linker_flags = string_trim_whitespace(e->LibraryName.extra_linker_flags);
  186. if (extra_linker_flags.len != 0) {
  187. lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags));
  188. }
  189. for_array(i, e->LibraryName.paths) {
  190. String lib = string_trim_whitespace(e->LibraryName.paths[i]);
  191. // IMPORTANT NOTE(bill): calling `string_to_lower` here is not an issue because
  192. // we will never uses these strings afterwards
  193. string_to_lower(&lib);
  194. if (lib.len == 0) {
  195. continue;
  196. }
  197. if (has_asm_extension(lib)) {
  198. if (!string_set_update(&asm_files, lib)) {
  199. String asm_file = lib;
  200. String obj_file = {};
  201. String temp_dir = temporary_directory(temporary_allocator());
  202. if (temp_dir.len != 0) {
  203. String filename = filename_without_directory(asm_file);
  204. gbString str = gb_string_make(heap_allocator(), "");
  205. str = gb_string_append_length(str, temp_dir.text, temp_dir.len);
  206. str = gb_string_appendc(str, "/");
  207. str = gb_string_append_length(str, filename.text, filename.len);
  208. str = gb_string_append_fmt(str, "-%p.obj", asm_file.text);
  209. obj_file = make_string_c(str);
  210. } else {
  211. obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
  212. }
  213. String obj_format = str_lit("win64");
  214. #if defined(GB_ARCH_32_BIT)
  215. obj_format = str_lit("win32");
  216. #endif
  217. result = system_exec_command_line_app("nasm",
  218. "\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" "
  219. "-f \"%.*s\" "
  220. "-o \"%.*s\" "
  221. "%.*s "
  222. "",
  223. LIT(build_context.ODIN_ROOT), LIT(asm_file),
  224. LIT(obj_format),
  225. LIT(obj_file),
  226. LIT(build_context.extra_assembler_flags)
  227. );
  228. if (result) {
  229. return result;
  230. }
  231. array_add(&gen->output_object_paths, obj_file);
  232. }
  233. } else if (!string_set_update(&min_libs_set, lib) ||
  234. !build_context.min_link_libs) {
  235. if (prev_lib != lib) {
  236. lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
  237. }
  238. prev_lib = lib;
  239. }
  240. }
  241. }
  242. if (build_context.build_mode == BuildMode_DynamicLibrary) {
  243. link_settings = gb_string_append_fmt(link_settings, " /DLL");
  244. if (build_context.no_entry_point) {
  245. link_settings = gb_string_append_fmt(link_settings, " /NOENTRY");
  246. }
  247. } else {
  248. // For i386 with CRT, libcmt provides the entry point
  249. // For other cases or no_crt, we need to specify the entry point
  250. if (!(build_context.metrics.arch == TargetArch_i386 && !build_context.no_crt)) {
  251. link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
  252. }
  253. }
  254. if (build_context.build_paths[BuildPath_Symbols].name != "") {
  255. String symbol_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Symbols]);
  256. link_settings = gb_string_append_fmt(link_settings, " /PDB:\"%.*s\"", LIT(symbol_path));
  257. }
  258. if (build_context.build_mode != BuildMode_StaticLibrary) {
  259. if (build_context.no_crt) {
  260. link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
  261. } else {
  262. link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
  263. }
  264. }
  265. if (build_context.ODIN_DEBUG) {
  266. link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
  267. }
  268. gbString object_files = gb_string_make(heap_allocator(), "");
  269. defer (gb_string_free(object_files));
  270. for (String const &object_path : gen->output_object_paths) {
  271. object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
  272. }
  273. String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
  274. defer (gb_free(heap_allocator(), vs_exe_path.text));
  275. String windows_sdk_bin_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Win_SDK_Bin_Path]);
  276. defer (gb_free(heap_allocator(), windows_sdk_bin_path.text));
  277. switch (build_context.linker_choice) {
  278. case Linker_lld:
  279. result = system_exec_command_line_app("msvc-lld-link",
  280. "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
  281. "/nologo /incremental:no /opt:ref /subsystem:%.*s "
  282. "%.*s "
  283. "%.*s "
  284. "%s "
  285. "",
  286. LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
  287. link_settings,
  288. LIT(windows_subsystem_names[build_context.ODIN_WINDOWS_SUBSYSTEM]),
  289. LIT(build_context.link_flags),
  290. LIT(build_context.extra_linker_flags),
  291. lib_str
  292. );
  293. if (result) {
  294. return result;
  295. }
  296. break;
  297. case Linker_radlink:
  298. result = system_exec_command_line_app("msvc-rad-link",
  299. "\"%.*s\\bin\\radlink\" %s -OUT:\"%.*s\" %s "
  300. "/nologo /incremental:no /opt:ref /subsystem:%.*s "
  301. "%.*s "
  302. "%.*s "
  303. "%s "
  304. "",
  305. LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
  306. link_settings,
  307. LIT(windows_subsystem_names[build_context.ODIN_WINDOWS_SUBSYSTEM]),
  308. LIT(build_context.link_flags),
  309. LIT(build_context.extra_linker_flags),
  310. lib_str
  311. );
  312. if (result) {
  313. return result;
  314. }
  315. break;
  316. default: { // msvc
  317. String res_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RES]);
  318. String rc_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RC]);
  319. defer (gb_free(heap_allocator(), res_path.text));
  320. defer (gb_free(heap_allocator(), rc_path.text));
  321. if (build_context.has_resource) {
  322. if (build_context.build_paths[BuildPath_RC].basename == "") {
  323. debugf("Using precompiled resource %.*s\n", LIT(res_path));
  324. } else {
  325. debugf("Compiling resource %.*s\n", LIT(res_path));
  326. result = system_exec_command_line_app("msvc-link",
  327. "\"%.*src.exe\" /nologo /fo %.*s %.*s",
  328. LIT(windows_sdk_bin_path),
  329. LIT(res_path),
  330. LIT(rc_path)
  331. );
  332. if (result) {
  333. return result;
  334. }
  335. }
  336. } else {
  337. res_path = {};
  338. }
  339. String linker_name = str_lit("link.exe");
  340. switch (build_context.build_mode) {
  341. case BuildMode_Executable:
  342. link_settings = gb_string_append_fmt(link_settings, " /NOIMPLIB /NOEXP");
  343. break;
  344. }
  345. switch (build_context.build_mode) {
  346. case BuildMode_StaticLibrary:
  347. linker_name = str_lit("lib.exe");
  348. break;
  349. default:
  350. link_settings = gb_string_append_fmt(link_settings, " /incremental:no /opt:ref");
  351. break;
  352. }
  353. result = system_exec_command_line_app("msvc-link",
  354. "\"%.*s%.*s\" %s %.*s -OUT:\"%.*s\" %s "
  355. "/nologo /subsystem:%.*s "
  356. "%.*s "
  357. "%.*s "
  358. "%s "
  359. "",
  360. LIT(vs_exe_path), LIT(linker_name), object_files, LIT(res_path), LIT(output_filename),
  361. link_settings,
  362. LIT(windows_subsystem_names[build_context.ODIN_WINDOWS_SUBSYSTEM]),
  363. LIT(build_context.link_flags),
  364. LIT(build_context.extra_linker_flags),
  365. lib_str
  366. );
  367. if (result) {
  368. return result;
  369. }
  370. break;
  371. }
  372. }
  373. } else {
  374. timings_start_section(timings, section_name);
  375. int const ODIN_ANDROID_API_LEVEL = build_context.ODIN_ANDROID_API_LEVEL;
  376. String ODIN_ANDROID_NDK = build_context.ODIN_ANDROID_NDK;
  377. String ODIN_ANDROID_NDK_TOOLCHAIN = build_context.ODIN_ANDROID_NDK_TOOLCHAIN;
  378. String ODIN_ANDROID_NDK_TOOLCHAIN_LIB = build_context.ODIN_ANDROID_NDK_TOOLCHAIN_LIB;
  379. String ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL = build_context.ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL;
  380. String ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT = build_context.ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT;
  381. // Link using `clang`, unless overridden by `ODIN_CLANG_PATH` environment variable.
  382. const char* clang_path = gb_get_env("ODIN_CLANG_PATH", permanent_allocator());
  383. bool has_odin_clang_path_env = true;
  384. if (clang_path == NULL) {
  385. clang_path = "clang";
  386. has_odin_clang_path_env = false;
  387. }
  388. // NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
  389. // files can be passed with -l:
  390. gbString lib_str = gb_string_make(heap_allocator(), "");
  391. defer (gb_string_free(lib_str));
  392. #if !defined(GB_SYSTEM_WINDOWS)
  393. lib_str = gb_string_appendc(lib_str, "-L/ ");
  394. #endif
  395. StringSet asm_files = {};
  396. string_set_init(&asm_files, 64);
  397. defer (string_set_destroy(&asm_files));
  398. StringSet min_libs_set = {};
  399. string_set_init(&min_libs_set, 64);
  400. defer (string_set_destroy(&min_libs_set));
  401. String prev_lib = {};
  402. for (Entity *e : gen->foreign_libraries) {
  403. GB_ASSERT(e->kind == Entity_LibraryName);
  404. // NOTE(bill): Add these before the linking values
  405. String extra_linker_flags = string_trim_whitespace(e->LibraryName.extra_linker_flags);
  406. if (extra_linker_flags.len != 0) {
  407. lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags));
  408. }
  409. if (build_context.metrics.os == TargetOs_darwin) {
  410. // Print frameworks first
  411. for (String lib : e->LibraryName.paths) {
  412. lib = string_trim_whitespace(lib);
  413. if (lib.len == 0) {
  414. continue;
  415. }
  416. if (string_ends_with(lib, str_lit(".framework"))) {
  417. if (string_set_update(&min_libs_set, lib)) {
  418. continue;
  419. }
  420. String lib_name = lib;
  421. lib_name = remove_extension_from_path(lib_name);
  422. lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
  423. }
  424. }
  425. }
  426. for (String lib : e->LibraryName.paths) {
  427. lib = string_trim_whitespace(lib);
  428. if (lib.len == 0) {
  429. continue;
  430. }
  431. if (has_asm_extension(lib)) {
  432. if (string_set_update(&asm_files, lib)) {
  433. continue; // already handled
  434. }
  435. String asm_file = lib;
  436. String obj_file = {};
  437. String temp_dir = temporary_directory(temporary_allocator());
  438. if (temp_dir.len != 0) {
  439. String filename = filename_without_directory(asm_file);
  440. gbString str = gb_string_make(heap_allocator(), "");
  441. str = gb_string_append_length(str, temp_dir.text, temp_dir.len);
  442. str = gb_string_appendc(str, "/");
  443. str = gb_string_append_length(str, filename.text, filename.len);
  444. str = gb_string_append_fmt(str, "-%p.o", asm_file.text);
  445. obj_file = make_string_c(str);
  446. } else {
  447. obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".o"));
  448. }
  449. String obj_format;
  450. if (build_context.metrics.ptr_size == 8) {
  451. if (is_osx) {
  452. obj_format = str_lit("macho64");
  453. } else {
  454. obj_format = str_lit("elf64");
  455. }
  456. } else {
  457. GB_ASSERT(build_context.metrics.ptr_size == 4);
  458. if (is_osx) {
  459. obj_format = str_lit("macho32");
  460. } else {
  461. obj_format = str_lit("elf32");
  462. }
  463. }
  464. if (build_context.metrics.arch == TargetArch_riscv64) {
  465. result = system_exec_command_line_app("clang",
  466. "%s \"%.*s\" "
  467. "-c -o \"%.*s\" "
  468. "-target %.*s -march=rv64gc "
  469. "%.*s "
  470. "",
  471. clang_path,
  472. LIT(asm_file),
  473. LIT(obj_file),
  474. LIT(build_context.metrics.target_triplet),
  475. LIT(build_context.extra_assembler_flags)
  476. );
  477. } else if (is_osx) {
  478. // `as` comes with MacOS.
  479. result = system_exec_command_line_app("as",
  480. "as \"%.*s\" "
  481. "-o \"%.*s\" "
  482. "%.*s "
  483. "",
  484. LIT(asm_file),
  485. LIT(obj_file),
  486. LIT(build_context.extra_assembler_flags)
  487. );
  488. } else {
  489. // Note(bumbread): I'm assuming nasm is installed on the host machine.
  490. // Shipping binaries on unix-likes gets into the weird territorry of
  491. // "which version of glibc" is it linked with.
  492. result = system_exec_command_line_app("nasm",
  493. "nasm \"%.*s\" "
  494. "-f \"%.*s\" "
  495. "-o \"%.*s\" "
  496. "%.*s "
  497. "",
  498. LIT(asm_file),
  499. LIT(obj_format),
  500. LIT(obj_file),
  501. LIT(build_context.extra_assembler_flags)
  502. );
  503. if (result) {
  504. 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));
  505. return result;
  506. }
  507. }
  508. array_add(&gen->output_object_paths, obj_file);
  509. } else {
  510. bool short_circuit = false;
  511. if (string_ends_with(lib, str_lit(".framework"))) {
  512. short_circuit = true;
  513. } else if (string_ends_with(lib, str_lit(".dylib"))) {
  514. short_circuit = true;
  515. } else if (string_ends_with(lib, str_lit(".so"))) {
  516. short_circuit = true;
  517. } else if (e->LibraryName.ignore_duplicates) {
  518. short_circuit = true;
  519. }
  520. if (string_set_update(&min_libs_set, lib) && (build_context.min_link_libs || short_circuit)) {
  521. continue;
  522. }
  523. if (prev_lib == lib) {
  524. continue;
  525. }
  526. prev_lib = lib;
  527. // Do not add libc again, this is added later already, and omitted with
  528. // the `-no-crt` flag, not skipping here would cause duplicate library
  529. // warnings when linking on darwin and might link libc silently even with `-no-crt`.
  530. if (lib == str_lit("System.framework") || lib == str_lit("System") || lib == str_lit("c")) {
  531. continue;
  532. }
  533. if (build_context.metrics.os == TargetOs_darwin) {
  534. if (string_ends_with(lib, str_lit(".framework"))) {
  535. // framework thingie
  536. String lib_name = lib;
  537. lib_name = remove_extension_from_path(lib_name);
  538. lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
  539. } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
  540. // For:
  541. // object
  542. // dynamic lib
  543. // static libs, absolute full path relative to the file in which the lib was imported from
  544. lib_str = gb_string_append_fmt(lib_str, " \"%.*s\" ", LIT(lib));
  545. } else {
  546. // dynamic or static system lib, just link regularly searching system library paths
  547. lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
  548. }
  549. } else {
  550. // NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
  551. // since those are statically linked to at link time. shared libraries (.so) has to be
  552. // available at runtime wherever the executable is run, so we make require those to be
  553. // local to the executable (unless the system collection is used, in which case we search
  554. // the system library paths for the library file).
  555. 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."))) {
  556. lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
  557. } else {
  558. // dynamic or static system lib, just link regularly searching system library paths
  559. lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
  560. }
  561. }
  562. }
  563. }
  564. }
  565. gbString object_files = gb_string_make(heap_allocator(), "");
  566. defer (gb_string_free(object_files));
  567. if (is_android) { // NOTE(bill): glue code needed for Android
  568. TIME_SECTION("Android Native App Glue Compile");
  569. String android_glue_object = {};
  570. String android_glue_static_lib = {};
  571. char hash_buf[64] = {};
  572. gb_snprintf(hash_buf, gb_size_of(hash_buf), "%p", &hash_buf);
  573. String hash = make_string_c(hash_buf);
  574. String temp_dir = normalize_path(temporary_allocator(), temporary_directory(temporary_allocator()), NIX_SEPARATOR_STRING);
  575. android_glue_object = concatenate4_strings(temporary_allocator(), temp_dir, str_lit("android_native_app_glue-"), hash, str_lit(".o"));
  576. android_glue_static_lib = concatenate4_strings(permanent_allocator(), temp_dir, str_lit("libandroid_native_app_glue-"), hash, str_lit(".a"));
  577. gbString glue = gb_string_make_length(heap_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
  578. defer (gb_string_free(glue));
  579. glue = gb_string_append_fmt(glue, "bin/clang");
  580. glue = gb_string_append_fmt(glue, " --target=aarch64-linux-android%d ", ODIN_ANDROID_API_LEVEL);
  581. glue = gb_string_appendc(glue, "-c \"");
  582. glue = gb_string_append_length(glue, ODIN_ANDROID_NDK.text, ODIN_ANDROID_NDK.len);
  583. glue = gb_string_appendc(glue, "sources/android/native_app_glue/android_native_app_glue.c");
  584. glue = gb_string_appendc(glue, "\" ");
  585. glue = gb_string_appendc(glue, "-o \"");
  586. glue = gb_string_append_length(glue, android_glue_object.text, android_glue_object.len);
  587. glue = gb_string_appendc(glue, "\" ");
  588. glue = gb_string_appendc(glue, "--sysroot \"");
  589. glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
  590. glue = gb_string_appendc(glue, "sysroot");
  591. glue = gb_string_appendc(glue, "\" ");
  592. glue = gb_string_appendc(glue, "\"-I");
  593. glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
  594. glue = gb_string_appendc(glue, "sysroot/usr/include/");
  595. glue = gb_string_appendc(glue, "\" ");
  596. glue = gb_string_appendc(glue, "\"-I");
  597. glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
  598. glue = gb_string_appendc(glue, "sysroot/usr/include/aarch64-linux-android/");
  599. glue = gb_string_appendc(glue, "\" ");
  600. glue = gb_string_appendc(glue, "-Wno-macro-redefined ");
  601. result = system_exec_command_line_app("android-native-app-glue-compile", glue);
  602. if (result) {
  603. return result;
  604. }
  605. TIME_SECTION("Android Native App Glue ar");
  606. gbString ar = gb_string_make_length(heap_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
  607. defer (gb_string_free(ar));
  608. ar = gb_string_appendc(ar, "bin/llvm-ar");
  609. ar = gb_string_appendc(ar, " rcs ");
  610. ar = gb_string_appendc(ar, "\"");
  611. ar = gb_string_append_length(ar, android_glue_static_lib.text, android_glue_static_lib.len);
  612. ar = gb_string_appendc(ar, "\" ");
  613. ar = gb_string_appendc(ar, "\"");
  614. ar = gb_string_append_length(ar, android_glue_object.text, android_glue_object.len);
  615. ar = gb_string_appendc(ar, "\" ");
  616. result = system_exec_command_line_app("android-native-app-glue-ar", ar);
  617. if (result) {
  618. return result;
  619. }
  620. object_files = gb_string_append_fmt(object_files, "\'%.*s\' ", LIT(android_glue_static_lib));
  621. }
  622. for (String object_path : gen->output_object_paths) {
  623. object_files = gb_string_append_fmt(object_files, "\'%.*s\' ", LIT(object_path));
  624. }
  625. gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
  626. if (build_context.no_crt) {
  627. link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
  628. }
  629. if (build_context.build_mode == BuildMode_StaticLibrary) {
  630. compiler_error("TODO(bill): -build-mode:static on non-windows targets");
  631. }
  632. // NOTE(dweiler): We use clang as a frontend for the linker as there are
  633. // other runtime and compiler support libraries that need to be linked in
  634. // very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
  635. // These are not always typically inside /lib, /lib64, or /usr versions
  636. // of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on
  637. // the distribution of Linux even. The gcc or clang specs is the only
  638. // reliable way to query this information to call ld directly.
  639. if (build_context.build_mode == BuildMode_DynamicLibrary) {
  640. // NOTE(dweiler): Let the frontend know we're building a shared library
  641. // so it doesn't generate symbols which cannot be relocated.
  642. link_settings = gb_string_appendc(link_settings, "-shared ");
  643. // NOTE(dweiler): _odin_entry_point must be called at initialization
  644. // time of the shared object, similarly, _odin_exit_point must be called
  645. // at deinitialization. We can pass both -init and -fini to the linker by
  646. // using a comma separated list of arguments to -Wl.
  647. //
  648. // This previously used ld but ld cannot actually build a shared library
  649. // correctly this way since all the other dependencies provided implicitly
  650. // by the compiler frontend are still needed and most of the command
  651. // line arguments prepared previously are incompatible with ld.
  652. if (build_context.metrics.os == TargetOs_darwin) {
  653. link_settings = gb_string_appendc(link_settings, "-Wl,-init,'__odin_entry_point' ");
  654. // NOTE(weshardee): __odin_exit_point should also be added, but -fini
  655. // does not exist on MacOS
  656. } else {
  657. link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
  658. link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
  659. }
  660. } else if (is_android) {
  661. // Always shared even in android!
  662. link_settings = gb_string_appendc(link_settings, "-shared ");
  663. }
  664. if (build_context.build_mode == BuildMode_Executable && build_context.reloc_mode == RelocMode_PIC) {
  665. // Do not disable PIE, let the linker choose. (most likely you want it enabled)
  666. } else if (build_context.build_mode != BuildMode_DynamicLibrary) {
  667. if (build_context.metrics.os != TargetOs_openbsd
  668. && build_context.metrics.os != TargetOs_haiku
  669. && build_context.metrics.arch != TargetArch_riscv64
  670. && !is_android
  671. ) {
  672. // OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it.
  673. link_settings = gb_string_appendc(link_settings, "-no-pie ");
  674. }
  675. }
  676. gbString platform_lib_str = gb_string_make(heap_allocator(), "");
  677. defer (gb_string_free(platform_lib_str));
  678. if (build_context.metrics.os == TargetOs_darwin) {
  679. // Get the SDK path.
  680. gbString darwin_sdk_path = gb_string_make(temporary_allocator(), "");
  681. char const* darwin_platform_name = "MacOSX";
  682. char const* darwin_xcrun_sdk_name = "macosx";
  683. char const* darwin_min_version_id = "macosx";
  684. const char* original_clang_path = clang_path;
  685. // NOTE(harold): We set the clang_path to run through xcrun because otherwise it complaints about the the sysroot
  686. // being set to 'MacOSX' even though we've set the sysroot to the correct SDK (-Wincompatible-sysroot).
  687. // This is because it is likely not using the SDK's toolchain Apple Clang but another one installed in the system.
  688. switch (selected_subtarget) {
  689. case Subtarget_iPhone:
  690. darwin_platform_name = "iPhoneOS";
  691. darwin_xcrun_sdk_name = "iphoneos";
  692. darwin_min_version_id = "ios";
  693. if (!has_odin_clang_path_env) {
  694. clang_path = "xcrun --sdk iphoneos clang";
  695. }
  696. break;
  697. case Subtarget_iPhoneSimulator:
  698. darwin_platform_name = "iPhoneSimulator";
  699. darwin_xcrun_sdk_name = "iphonesimulator";
  700. darwin_min_version_id = "ios-simulator";
  701. if (!has_odin_clang_path_env) {
  702. clang_path = "xcrun --sdk iphonesimulator clang";
  703. }
  704. break;
  705. }
  706. gbString darwin_find_sdk_cmd = gb_string_make(temporary_allocator(), "");
  707. darwin_find_sdk_cmd = gb_string_append_fmt(darwin_find_sdk_cmd, "xcrun --sdk %s --show-sdk-path", darwin_xcrun_sdk_name);
  708. if (!system_exec_command_line_app_output(darwin_find_sdk_cmd, &darwin_sdk_path)) {
  709. // Fallback to default clang, since `xcrun --sdk` did not work.
  710. clang_path = original_clang_path;
  711. // Best-effort fallback to known locations
  712. gbString darwin_sdk_path = gb_string_make(temporary_allocator(), "");
  713. darwin_sdk_path = gb_string_append_fmt(darwin_sdk_path, "/Library/Developer/CommandLineTools/SDKs/%s.sdk", darwin_platform_name);
  714. if (!path_is_directory(make_string_c(darwin_sdk_path))) {
  715. gb_string_clear(darwin_sdk_path);
  716. darwin_sdk_path = gb_string_append_fmt(darwin_sdk_path, "/Applications/Xcode.app/Contents/Developer/Platforms/%s.platform/Developer/SDKs/%s.sdk", darwin_platform_name);
  717. if (!path_is_directory(make_string_c(darwin_sdk_path))) {
  718. gb_printf_err("Failed to find %s SDK\n", darwin_platform_name);
  719. return -1;
  720. }
  721. }
  722. } else {
  723. // Trim the trailing newline.
  724. darwin_sdk_path = gb_string_trim_space(darwin_sdk_path);
  725. }
  726. platform_lib_str = gb_string_append_fmt(platform_lib_str, "--sysroot %s ", darwin_sdk_path);
  727. platform_lib_str = gb_string_appendc(platform_lib_str, "-L/usr/local/lib ");
  728. // Homebrew's default library path, checking if it exists to avoid linking warnings.
  729. if (gb_file_exists("/opt/homebrew/lib")) {
  730. platform_lib_str = gb_string_appendc(platform_lib_str, "-L/opt/homebrew/lib ");
  731. }
  732. // MacPort's default library path, checking if it exists to avoid linking warnings.
  733. if (gb_file_exists("/opt/local/lib")) {
  734. platform_lib_str = gb_string_appendc(platform_lib_str, "-L/opt/local/lib ");
  735. }
  736. // Only specify this flag if the user has given a minimum version to target.
  737. // This will cause warnings to show up for mismatched libraries.
  738. // NOTE(harold): For device subtargets we have to explicitly set the default version to
  739. // avoid the same warning since we configure our own minimum version when compiling for devices.
  740. if (build_context.minimum_os_version_string_given || selected_subtarget != Subtarget_Default) {
  741. link_settings = gb_string_append_fmt(link_settings, "-m%s-version-min=%.*s ", darwin_min_version_id, LIT(build_context.minimum_os_version_string));
  742. }
  743. if (build_context.build_mode != BuildMode_DynamicLibrary) {
  744. // This points the linker to where the entry point is
  745. link_settings = gb_string_appendc(link_settings, "-e _main ");
  746. }
  747. } else if (build_context.metrics.os == TargetOs_freebsd) {
  748. if (build_context.sanitizer_flags & (SanitizerFlag_Address | SanitizerFlag_Memory)) {
  749. // It's imperative that `pthread` is linked before `libc`,
  750. // otherwise ASan/MSan will be unable to call `pthread_key_create`
  751. // because FreeBSD's `libthr` implementation of `pthread`
  752. // needs to replace the relevant stubs first.
  753. //
  754. // (Presumably TSan implements its own `pthread` interface,
  755. // which is why it isn't required.)
  756. //
  757. // See: https://reviews.llvm.org/D39254
  758. platform_lib_str = gb_string_appendc(platform_lib_str, "-lpthread ");
  759. }
  760. // FreeBSD pkg installs third-party shared libraries in /usr/local/lib.
  761. platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-L/usr/local/lib ");
  762. } else if (build_context.metrics.os == TargetOs_openbsd) {
  763. // OpenBSD ports install shared libraries in /usr/local/lib. Also, we must explicitly link libpthread.
  764. platform_lib_str = gb_string_appendc(platform_lib_str, "-lpthread -Wl,-L/usr/local/lib ");
  765. // Until the LLVM back-end can be adapted to emit endbr64 instructions on amd64, we
  766. // need to pass -z nobtcfi in order to allow the resulting program to run under
  767. // OpenBSD 7.4 and newer. Once support is added at compile time, this can be dropped.
  768. platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-z,nobtcfi ");
  769. }
  770. if (is_android) {
  771. GB_ASSERT(ODIN_ANDROID_NDK_TOOLCHAIN_LIB.len != 0);
  772. GB_ASSERT(ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL.len != 0);
  773. GB_ASSERT(ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT.len != 0);
  774. platform_lib_str = gb_string_appendc(platform_lib_str, "\"-L");
  775. platform_lib_str = gb_string_append_length(platform_lib_str, ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL.text, ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL.len);
  776. platform_lib_str = gb_string_appendc(platform_lib_str, "\" ");
  777. platform_lib_str = gb_string_appendc(platform_lib_str, "-landroid ");
  778. platform_lib_str = gb_string_appendc(platform_lib_str, "-llog ");
  779. platform_lib_str = gb_string_appendc(platform_lib_str, "\"--sysroot=");
  780. platform_lib_str = gb_string_append_length(platform_lib_str, ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT.text, ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT.len);
  781. platform_lib_str = gb_string_appendc(platform_lib_str, "\" ");
  782. link_settings = gb_string_appendc(link_settings, "-u ANativeActivity_onCreate ");
  783. }
  784. if (!build_context.no_rpath) {
  785. // Set the rpath to the $ORIGIN/@loader_path (the path of the executable),
  786. // so that dynamic libraries are looked for at that path.
  787. if (build_context.metrics.os == TargetOs_darwin) {
  788. link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,@loader_path ");
  789. } else {
  790. if (is_android) {
  791. // ignore
  792. } else {
  793. link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,\\$ORIGIN ");
  794. }
  795. }
  796. }
  797. if (!build_context.no_crt) {
  798. lib_str = gb_string_appendc(lib_str, "-lm ");
  799. if (build_context.metrics.os == TargetOs_darwin) {
  800. // NOTE: adding this causes a warning about duplicate libraries, I think it is
  801. // automatically assumed/added by clang when you don't do `-nostdlib`.
  802. // lib_str = gb_string_appendc(lib_str, "-lSystem ");
  803. } else {
  804. lib_str = gb_string_appendc(lib_str, "-lc ");
  805. }
  806. }
  807. gbString link_command_line = gb_string_make(heap_allocator(), "");
  808. defer (gb_string_free(link_command_line));
  809. if (is_android) {
  810. gbString ndk_bin_directory = gb_string_make_length(temporary_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
  811. link_command_line = gb_string_appendc(link_command_line, ndk_bin_directory);
  812. link_command_line = gb_string_appendc(link_command_line, "bin/clang");
  813. link_command_line = gb_string_append_fmt(link_command_line, " --target=aarch64-linux-android%d ", ODIN_ANDROID_API_LEVEL);
  814. } else {
  815. link_command_line = gb_string_appendc(link_command_line, clang_path);
  816. }
  817. link_command_line = gb_string_appendc(link_command_line, " -Wno-unused-command-line-argument ");
  818. link_command_line = gb_string_appendc(link_command_line, object_files);
  819. link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
  820. link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
  821. link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
  822. link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
  823. link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
  824. link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
  825. if (is_android) {
  826. TIME_SECTION("Linking");
  827. }
  828. if (build_context.linker_choice == Linker_lld) {
  829. link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=lld");
  830. result = system_exec_command_line_app("lld-link", link_command_line);
  831. } else if (build_context.linker_choice == Linker_mold) {
  832. link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=mold");
  833. result = system_exec_command_line_app("mold-link", link_command_line);
  834. } else {
  835. result = system_exec_command_line_app("ld-link", link_command_line);
  836. }
  837. if (result) {
  838. return result;
  839. }
  840. if (is_osx && build_context.ODIN_DEBUG) {
  841. // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
  842. // to the symbols in the object file
  843. result = system_exec_command_line_app("dsymutil", "dsymutil \"%.*s\"", LIT(output_filename));
  844. if (result) {
  845. return result;
  846. }
  847. }
  848. }
  849. }
  850. return result;
  851. }