Browse Source

Fix -build-mode:shared type table on Unix

Fixes #527.

Previously on Linux, '__$startup_runtime', the procedure that
initializes the type table for runtime typeids, was NOT called when
a shared library was loaded by the dynamic loader.

This caused the library to not have its type table populated, which
caused an assert to fail if you tried to use runtime typeids - like
core:fmt, for example.

This commit fixes this by calling ld instead of clang, when building a
shared library, so that we can pass "-init '__$startup_runtime'" to it,
when building a shared library.

Try as I might, I could not get clang to correctly pass through
the linker flags that I wanted.
Tetralux 4 years ago
parent
commit
140bb3ebfc
1 changed files with 45 additions and 37 deletions
  1. 45 37
      src/main.cpp

+ 45 - 37
src/main.cpp

@@ -342,9 +342,9 @@ i32 linker_stage(lbGenerator *gen) {
 					lib_name = remove_extension_from_path(lib_name);
 					lib_name = remove_extension_from_path(lib_name);
 					lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
 					lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
 				} else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
 				} else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
-          				// For:
-          				// object 
-          				// dynamic lib
+					// For:
+					// object
+					// dynamic lib
 					// static libs, absolute full path relative to the file in which the lib was imported from
 					// static libs, absolute full path relative to the file in which the lib was imported from
 					lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
 					lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
 				} else {
 				} else {
@@ -382,20 +382,33 @@ i32 linker_stage(lbGenerator *gen) {
 		// Unlike the Win32 linker code, the output_ext includes the dot, because
 		// Unlike the Win32 linker code, the output_ext includes the dot, because
 		// typically executable files on *NIX systems don't have extensions.
 		// typically executable files on *NIX systems don't have extensions.
 		String output_ext = {};
 		String output_ext = {};
-		char const *link_settings = "";
+		gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
 		char const *linker;
 		char const *linker;
 		if (build_context.build_mode == BuildMode_DynamicLibrary) {
 		if (build_context.build_mode == BuildMode_DynamicLibrary) {
+			// NOTE(tetra, 2020-11-06): __$startup_runtime must be called at DLL load time.
+			// Clang, for some reason, won't let us pass the '-init' flag that lets us do this,
+			// so use ld instead.
+			// :UseLDForShared
+			linker = "ld";
+			link_settings = gb_string_appendc(link_settings, "-init '__$startup_runtime' ");
 			// Shared libraries are .dylib on MacOS and .so on Linux.
 			// Shared libraries are .dylib on MacOS and .so on Linux.
 			#if defined(GB_SYSTEM_OSX)
 			#if defined(GB_SYSTEM_OSX)
 				output_ext = STR_LIT(".dylib");
 				output_ext = STR_LIT(".dylib");
-				link_settings = "-dylib -dynamic";
+				link_settings = gb_string_appendc(link_settings, "-dylib -dynamic ");
 			#else
 			#else
 				output_ext = STR_LIT(".so");
 				output_ext = STR_LIT(".so");
-				link_settings = "-shared";
+				link_settings = gb_string_appendc(link_settings, "-shared ");
 			#endif
 			#endif
 		} else {
 		} else {
-			// TODO: Do I need anything here?
-			link_settings = "";
+			#if defined(GB_SYSTEM_OSX)
+				linker = "ld";
+			#else
+				// TODO(zangent): Figure out how to make ld work on Linux.
+				//   It probably has to do with including the entire CRT, but
+				//   that's quite a complicated issue to solve while remaining distro-agnostic.
+				//   Clang can figure out linker flags for us, and that's good enough _for now_.
+				linker = "clang -Wno-unused-command-line-argument";
+			#endif
 		}
 		}
 
 
 		if (build_context.out_filepath.len > 0) {
 		if (build_context.out_filepath.len > 0) {
@@ -406,16 +419,6 @@ i32 linker_stage(lbGenerator *gen) {
 			}
 			}
 		}
 		}
 
 
-		#if defined(GB_SYSTEM_OSX)
-			linker = "ld";
-		#else
-			// TODO(zangent): Figure out how to make ld work on Linux.
-			//   It probably has to do with including the entire CRT, but
-			//   that's quite a complicated issue to solve while remaining distro-agnostic.
-			//   Clang can figure out linker flags for us, and that's good enough _for now_.
-			linker = "clang -Wno-unused-command-line-argument";
-		#endif
-
 		exit_code = system_exec_command_line_app("ld-link",
 		exit_code = system_exec_command_line_app("ld-link",
 			"%s %s -o \"%.*s%.*s\" %s "
 			"%s %s -o \"%.*s%.*s\" %s "
 			" %s "
 			" %s "
@@ -443,7 +446,7 @@ i32 linker_stage(lbGenerator *gen) {
 		if (exit_code != 0) {
 		if (exit_code != 0) {
 			return exit_code;
 			return exit_code;
 		}
 		}
-    
+
 	#if defined(GB_SYSTEM_OSX)
 	#if defined(GB_SYSTEM_OSX)
 		if (build_context.ODIN_DEBUG) {
 		if (build_context.ODIN_DEBUG) {
 			// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
 			// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
@@ -2136,11 +2139,11 @@ int main(int arg_count, char const **arg_ptr) {
 						String lib_name = lib;
 						String lib_name = lib;
 						lib_name = remove_extension_from_path(lib_name);
 						lib_name = remove_extension_from_path(lib_name);
 						lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
 						lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
-						
+
 				} else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
 				} else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
-          				// For:
-          				// object 
-          				// dynamic lib
+					// For:
+					// object
+					// dynamic lib
 					// static libs, absolute full path relative to the file in which the lib was imported from
 					// static libs, absolute full path relative to the file in which the lib was imported from
 					lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
 					lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
 				} else {
 				} else {
@@ -2171,22 +2174,36 @@ int main(int arg_count, char const **arg_ptr) {
 			// Unlike the Win32 linker code, the output_ext includes the dot, because
 			// Unlike the Win32 linker code, the output_ext includes the dot, because
 			// typically executable files on *NIX systems don't have extensions.
 			// typically executable files on *NIX systems don't have extensions.
 			String output_ext = {};
 			String output_ext = {};
-			char const *link_settings = "";
+			gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
 			char const *linker;
 			char const *linker;
 			if (build_context.build_mode == BuildMode_DynamicLibrary) {
 			if (build_context.build_mode == BuildMode_DynamicLibrary) {
+				// NOTE(tetra, 2020-11-06): __$startup_runtime must be called at DLL load time.
+				// Clang, for some reason, won't let us pass the '-init' flag that lets us do this,
+				// so use ld instead.
+				// :UseLDForShared
+				linker = "ld";
+				link_settings = gb_string_appendc(link_settings, "-init '__$startup_runtime' ");
 				// Shared libraries are .dylib on MacOS and .so on Linux.
 				// Shared libraries are .dylib on MacOS and .so on Linux.
 				#if defined(GB_SYSTEM_OSX)
 				#if defined(GB_SYSTEM_OSX)
 					output_ext = STR_LIT(".dylib");
 					output_ext = STR_LIT(".dylib");
-					link_settings = "-dylib -dynamic";
+					link_settings = gb_string_appendc(link_settings, "-dylib -dynamic ");
 				#else
 				#else
 					output_ext = STR_LIT(".so");
 					output_ext = STR_LIT(".so");
-					link_settings = "-shared";
+					link_settings = gb_string_appendc(link_settings, "-shared ");
 				#endif
 				#endif
 			} else {
 			} else {
-				// TODO: Do I need anything here?
-				link_settings = "";
+				#if defined(GB_SYSTEM_OSX)
+					linker = "ld";
+				#else
+					// TODO(zangent): Figure out how to make ld work on Linux.
+					//   It probably has to do with including the entire CRT, but
+					//   that's quite a complicated issue to solve while remaining distro-agnostic.
+					//   Clang can figure out linker flags for us, and that's good enough _for now_.
+					linker = "clang -Wno-unused-command-line-argument";
+				#endif
 			}
 			}
 
 
+
 			if (build_context.out_filepath.len > 0) {
 			if (build_context.out_filepath.len > 0) {
 				//NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that
 				//NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that
 				isize pos = string_extension_position(build_context.out_filepath);
 				isize pos = string_extension_position(build_context.out_filepath);
@@ -2195,15 +2212,6 @@ int main(int arg_count, char const **arg_ptr) {
 				}
 				}
 			}
 			}
 
 
-			#if defined(GB_SYSTEM_OSX)
-				linker = "ld";
-			#else
-				// TODO(zangent): Figure out how to make ld work on Linux.
-				//   It probably has to do with including the entire CRT, but
-				//   that's quite a complicated issue to solve while remaining distro-agnostic.
-				//   Clang can figure out linker flags for us, and that's good enough _for now_.
-				linker = "clang -Wno-unused-command-line-argument";
-			#endif
 
 
 			exit_code = system_exec_command_line_app("ld-link",
 			exit_code = system_exec_command_line_app("ld-link",
 				"%s \"%.*s.o\" -o \"%.*s%.*s\" %s "
 				"%s \"%.*s.o\" -o \"%.*s%.*s\" %s "