Skip to content

Commit

Permalink
WIN32: Linking with the CRT at runtime. (#570)
Browse files Browse the repository at this point in the history
Disclaimer: Forgive me if my format sucks, I've never submitted a PR before!

Fixes: #517 

I added a few things to allow zig to link with the CRT properly both statically and dynamically. In Visual Studio 2017, Microsoft changed how the c-runtime is factored again. With this change, they also added a COM interface to allow you to query the respective Visual Studio instance for two of them. This does that and also falls back on a registry query for 2015 support. If you're using a Visual Studio instance older than 2015, you'll have to use the existing options available with the zig compiler. Changes are listed below along with a general description of the changes.

all_types.cpp:

The separate variables for msvc/kern32 have been removed and all win32 libc directory paths have been combined into a ZigList since we're querying more than two directories and differentiating one from another doesn't matter to lld.

analyze.cpp:

The existing functions were extended to support querying libc libs & libc headers at runtime.

codegen.cpp/hpp:

Microsoft uses the new 'Universal C Runtime' name now. Doesn't matter from a functionality standpoint. I left the compiler switches as is to not introduce any breaking changes.

link.cpp:

We're linking 4 libs and generating another in order to support the UCRT.
Dynamic: msvcrt/d, vcruntime/d, ucrt/d, legacy_stdio_definitions.lib
Static: libcmt/d, libvcruntime/d libucrt/d, legacy_stdio_definitions.lib

main.cpp:

Update function call names.

os.cpp/hpp:

COM/Registry interface for querying Windows UCRT/SDK.

Sources:
[Windows CRT](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features)
[VS 2015 Breaking Changes](https://msdn.microsoft.com/en-us/library/bb531344.aspx)
  • Loading branch information
dimenus authored and andrewrk committed Nov 1, 2017
1 parent b35689b commit 38f05d4
Show file tree
Hide file tree
Showing 11 changed files with 1,316 additions and 86 deletions.
5 changes: 3 additions & 2 deletions src/all_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct IrInstruction;
struct IrInstructionCast;
struct IrBasicBlock;
struct ScopeDecls;
struct ZigWindowsSDK;

struct IrGotoItem {
AstNode *source_node;
Expand Down Expand Up @@ -1461,17 +1462,17 @@ struct CodeGen {
bool have_winmain_crt_startup;
bool have_dllmain_crt_startup;
bool have_pub_panic;
ZigList<Buf*> libc_lib_dirs_list;
Buf *libc_lib_dir;
Buf *libc_static_lib_dir;
Buf *libc_include_dir;
Buf *msvc_lib_dir;
Buf *kernel32_lib_dir;
Buf *zig_lib_dir;
Buf *zig_std_dir;
Buf *zig_c_headers_dir;
Buf *zig_std_special_dir;
Buf *dynamic_linker;
Buf *ar_path;
ZigWindowsSDK *win_sdk;
Buf triple_str;
BuildMode build_mode;
bool is_test_build;
Expand Down
86 changes: 39 additions & 47 deletions src/analyze.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
Expand Down Expand Up @@ -3371,65 +3371,57 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
}

void find_libc_include_path(CodeGen *g) {
#ifdef ZIG_OS_WINDOWS
if (!g->libc_include_dir || buf_len(g->libc_include_dir) == 0) {
if (g->win_sdk == nullptr) {
if (os_find_windows_sdk(&g->win_sdk)) {
zig_panic("Unable to determine Windows SDK path.");
}
}

if (g->zig_target.os == ZigLLVM_Win32) {
if (os_get_win32_ucrt_include_path(g->win_sdk, g->libc_include_dir)) {
zig_panic("Unable to determine libc include path.");
}
}
}
return;
#endif
// TODO find libc at runtime for other operating systems
if(!g->libc_include_dir || buf_len(g->libc_include_dir) == 0) {
zig_panic("Unable to determine libc include path.");
}
}

void find_libc_lib_path(CodeGen *g) {
#ifdef ZIG_OS_WINDOWS
if (!g->msvc_lib_dir && g->zig_target.os == ZigLLVM_Win32) {
Buf *msvc_lib_dir;
if (g->zig_target.arch.arch == ZigLLVM_arm) {
msvc_lib_dir = buf_create_from_str("C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\lib\\arm");
} else if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
msvc_lib_dir = buf_create_from_str("C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\lib\\amd64");
} else if (g->zig_target.arch.arch == ZigLLVM_x86) {
msvc_lib_dir = buf_create_from_str("C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\lib");
} else {
zig_panic("unable to determine msvc lib path");
}
Buf *test_path = buf_alloc();
os_path_join(msvc_lib_dir, buf_create_from_str("vcruntime.lib"), test_path);
bool result;
int err;
if ((err = os_file_exists(test_path, &result))) {
result = false;
}
if (result) {
g->msvc_lib_dir = msvc_lib_dir;
} else {
zig_panic("Unable to determine msvc lib path.");
if (g->zig_target.os == ZigLLVM_Win32) {
if (g->win_sdk == nullptr) {
if (os_find_windows_sdk(&g->win_sdk)) {
zig_panic("Unable to determine Windows SDK path.");
}
}
}

if (!g->kernel32_lib_dir && g->zig_target.os == ZigLLVM_Win32) {
Buf *kernel32_lib_dir;
if (g->zig_target.arch.arch == ZigLLVM_arm) {
kernel32_lib_dir = buf_create_from_str(
"C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\arm");
} else if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
kernel32_lib_dir = buf_create_from_str(
"C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x64");
} else if (g->zig_target.arch.arch == ZigLLVM_x86) {
kernel32_lib_dir = buf_create_from_str(
"C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x86");
} else {
zig_panic("unable to determine kernel32 lib path");
Buf* vc_lib_dir = buf_alloc();
if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {
zig_panic("Unable to determine vcruntime path.");
}
Buf *test_path = buf_alloc();
os_path_join(kernel32_lib_dir, buf_create_from_str("kernel32.lib"), test_path);
bool result;
int err;
if ((err = os_file_exists(test_path, &result))) {
result = false;

Buf* ucrt_lib_path = buf_alloc();
if (os_get_win32_ucrt_lib_path(g->win_sdk, ucrt_lib_path, g->zig_target.arch.arch)) {
zig_panic("Unable to determine ucrt path.");
}
if (result) {
g->kernel32_lib_dir = kernel32_lib_dir;
} else {
zig_panic("Unable to determine kernel32 lib path.");

Buf* kern_lib_path = buf_alloc();
if (os_get_win32_kern32_path(g->win_sdk, kern_lib_path, g->zig_target.arch.arch)) {
zig_panic("Unable to determine kernel32 path.");
}

g->libc_lib_dirs_list.append(vc_lib_dir);
g->libc_lib_dirs_list.append(ucrt_lib_path);
g->libc_lib_dirs_list.append(kern_lib_path);
}
return;
#endif

// later we can handle this better by reporting an error via the normal mechanism
Expand Down
25 changes: 9 additions & 16 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
g->external_prototypes.init(8);
g->is_test_build = false;
g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib);

buf_resize(&g->global_asm, 0);

// reserve index 0 to indicate no error
Expand All @@ -106,31 +105,25 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
g->zig_std_special_dir = buf_alloc();
os_path_join(g->zig_std_dir, buf_sprintf("special"), g->zig_std_special_dir);


if (target) {
// cross compiling, so we can't rely on all the configured stuff since
// that's for native compilation
g->zig_target = *target;
resolve_target_object_format(&g->zig_target);

g->dynamic_linker = buf_create_from_str("");
g->libc_lib_dir = buf_create_from_str("");
g->libc_static_lib_dir = buf_create_from_str("");
g->libc_include_dir = buf_create_from_str("");
g->msvc_lib_dir = nullptr;
g->kernel32_lib_dir = nullptr;
g->each_lib_rpath = false;
} else {
// native compilation, we can rely on the configuration stuff
g->is_native_target = true;
get_native_target(&g->zig_target);

g->dynamic_linker = buf_create_from_str(ZIG_DYNAMIC_LINKER);
g->libc_lib_dir = buf_create_from_str(ZIG_LIBC_LIB_DIR);
g->libc_static_lib_dir = buf_create_from_str(ZIG_LIBC_STATIC_LIB_DIR);
g->libc_include_dir = buf_create_from_str(ZIG_LIBC_INCLUDE_DIR);
g->msvc_lib_dir = nullptr; // find it at runtime
g->kernel32_lib_dir = nullptr; // find it at runtime

#ifdef ZIG_EACH_LIB_RPATH
g->each_lib_rpath = true;
#endif
Expand Down Expand Up @@ -228,14 +221,6 @@ void codegen_set_libc_include_dir(CodeGen *g, Buf *libc_include_dir) {
g->libc_include_dir = libc_include_dir;
}

void codegen_set_msvc_lib_dir(CodeGen *g, Buf *msvc_lib_dir) {
g->msvc_lib_dir = msvc_lib_dir;
}

void codegen_set_kernel32_lib_dir(CodeGen *g, Buf *kernel32_lib_dir) {
g->kernel32_lib_dir = kernel32_lib_dir;
}

void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker) {
g->dynamic_linker = dynamic_linker;
}
Expand All @@ -244,6 +229,14 @@ void codegen_add_lib_dir(CodeGen *g, const char *dir) {
g->lib_dirs.append(dir);
}

void codegen_set_ucrt_lib_dir(CodeGen *g, Buf *ucrt_lib_dir) {
g->libc_lib_dirs_list.append(ucrt_lib_dir);
}

void codegen_set_kernel32_lib_dir(CodeGen *g, Buf *kernel32_lib_dir) {
g->libc_lib_dirs_list.append(kernel32_lib_dir);
}

void codegen_add_rpath(CodeGen *g, const char *name) {
g->rpath_list.append(buf_create_from_str(name));
}
Expand Down
2 changes: 1 addition & 1 deletion src/codegen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ void codegen_set_out_name(CodeGen *codegen, Buf *out_name);
void codegen_set_libc_lib_dir(CodeGen *codegen, Buf *libc_lib_dir);
void codegen_set_libc_static_lib_dir(CodeGen *g, Buf *libc_static_lib_dir);
void codegen_set_libc_include_dir(CodeGen *codegen, Buf *libc_include_dir);
void codegen_set_msvc_lib_dir(CodeGen *codegen, Buf *msvc_lib_dir);
void codegen_set_ucrt_lib_dir(CodeGen *g, Buf *ucrt_lib_dir);
void codegen_set_kernel32_lib_dir(CodeGen *codegen, Buf *kernel32_lib_dir);
void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker);
void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole);
Expand Down
23 changes: 16 additions & 7 deletions src/link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,26 +402,35 @@ static void construct_linker_job_coff(LinkJob *lj) {
lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&lj->out_file))));

if (g->libc_link_lib != nullptr) {
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->msvc_lib_dir))));
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->kernel32_lib_dir))));

lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_lib_dir))));
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_static_lib_dir))));
if (g->libc_link_lib != nullptr) {
for (uint32_t i = 0; i < g->libc_lib_dirs_list.length; ++i) {
lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_lib_dirs_list.items[i]))));
}
}
}

if (lj->link_in_crt) {
const char *lib_str = g->is_static ? "lib" : "";
const char *d_str = (g->build_mode == BuildModeDebug) ? "d" : "";

Buf *cmt_lib_name = buf_sprintf("libcmt%s.lib", d_str);
lj->args.append(buf_ptr(cmt_lib_name));
if (g->is_static) {
Buf *cmt_lib_name = buf_sprintf("libcmt%s.lib", d_str);
lj->args.append(buf_ptr(cmt_lib_name));
}
else {
Buf *msvcrt_lib_name = buf_sprintf("msvcrt%s.lib", d_str);
lj->args.append(buf_ptr(msvcrt_lib_name));
}

Buf *vcruntime_lib_name = buf_sprintf("%svcruntime%s.lib", lib_str, d_str);
lj->args.append(buf_ptr(vcruntime_lib_name));

Buf *crt_lib_name = buf_sprintf("%sucrt%s.lib", lib_str, d_str);
lj->args.append(buf_ptr(crt_lib_name));

//Visual C++ 2015 Conformance Changes
//https://msdn.microsoft.com/en-us/library/bb531344.aspx
lj->args.append("legacy_stdio_definitions.lib");

//if (shared || dll) {
// lj->args.append(get_libc_file(g, "dllcrt2.o"));
Expand Down
2 changes: 1 addition & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ int main(int argc, char **argv) {
if (libc_include_dir)
codegen_set_libc_include_dir(g, buf_create_from_str(libc_include_dir));
if (msvc_lib_dir)
codegen_set_msvc_lib_dir(g, buf_create_from_str(msvc_lib_dir));
codegen_set_ucrt_lib_dir(g, buf_create_from_str(msvc_lib_dir));
if (kernel32_lib_dir)
codegen_set_kernel32_lib_dir(g, buf_create_from_str(kernel32_lib_dir));
if (dynamic_linker)
Expand Down
Loading

0 comments on commit 38f05d4

Please sign in to comment.