Skip to content

Added DLL loading capability in windows to the std lib. Ensure that t… #616

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions src/link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
#include "codegen.hpp"
#include "analyze.hpp"

#ifdef ZIG_OS_WINDOWS
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pattern we have so far is to isolate windows API calls to os.cpp. That should be the only file that includes windows.h.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that makes sense, I'll move the verification code to there.

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#endif

struct LinkJob {
CodeGen *codegen;
Buf out_file;
Expand Down Expand Up @@ -488,18 +493,28 @@ static void construct_linker_job_coff(LinkJob *lj) {
if (lj->codegen->zig_target.env_type == ZigLLVM_GNU) {
Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
lj->args.append(buf_ptr(arg));
}
else {
} else {
lj->args.append(buf_ptr(link_lib->name));
}
} else {
buf_resize(def_contents, 0);
buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name));
#ifdef ZIG_OS_WINDOWS
Buf* dll_name = buf_create_from_buf(link_lib->name);
buf_append_str(dll_name, ".dll");
HMODULE hmod = GetModuleHandle(buf_ptr(dll_name));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From MSDN GetModuleHandle:

Retrieves a module handle for the specified module. The module must have been loaded by the calling process.

Based on this, I think we have to use LoadLibrary before GetModuleHandle. And LoadLibrary runs the code in the DLL, which can crash the compiler. So we should do it in a child process, and communicate back to zig compiler the results. If we're going to spawn a child process, we might as well write that child process in zig and build it lazily just like compiler_rt.o and builtin.o.

assert(hmod);
for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) {
Buf *symbol_name = link_lib->symbols.at(exp_i);
FARPROC symbol_addr = GetProcAddress(hmod, buf_ptr(symbol_name));
if (symbol_addr == NULL) {
fprintf(stderr, "error: extern symbol: %s does not exist in: %s.\n", buf_ptr(symbol_name), buf_ptr(dll_name));
exit(1);
}
buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name));
}
buf_appendf(def_contents, "\n");
#endif

Buf *def_path = buf_alloc();
os_path_join(g->cache_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path);
Expand Down
2 changes: 2 additions & 0 deletions std/os/index.zig
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ pub const windowsWaitSingle = windows_util.windowsWaitSingle;
pub const windowsWrite = windows_util.windowsWrite;
pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
pub const windowsOpen = windows_util.windowsOpen;
pub const windowsLoadDll = windows_util.windowsLoadDll;
pub const windowsUnloadDll = windows_util.windowsUnloadDll;
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;

pub const FileHandle = if (is_windows) windows.HANDLE else i32;
Expand Down
6 changes: 6 additions & 0 deletions std/os/windows/index.zig
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ pub extern "kernel32" stdcallcc fn WriteFile(in_hFile: HANDLE, in_lpBuffer: &con
in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?&DWORD,
in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL;

//TODO: call unicode versions instead of relying on ANSI code page
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

link to #534

pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) -> ?HMODULE;

pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) -> BOOL;

pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) -> c_int;

pub const PROV_RSA_FULL = 1;
Expand All @@ -97,6 +102,7 @@ pub const FLOAT = f32;
pub const HANDLE = &c_void;
pub const HCRYPTPROV = ULONG_PTR;
pub const HINSTANCE = &@OpaqueType();
pub const HMODULE = &@OpaqueType();
pub const INT = c_int;
pub const LPBYTE = &BYTE;
pub const LPCH = &CHAR;
Expand Down
17 changes: 17 additions & 0 deletions std/os/windows/util.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const windows = std.os.windows;
const assert = std.debug.assert;
const mem = std.mem;
const BufMap = std.BufMap;
const cstr = std.cstr;

error WaitAbandoned;
error WaitTimeOut;
Expand Down Expand Up @@ -149,3 +150,19 @@ pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap)
result[i] = 0;
return result;
}

error DllNotFound;
pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) -> %windows.HMODULE {
const padded_buff = %return cstr.addNullByte(allocator, dll_path);
defer allocator.free(padded_buff);
const rc = windows.LoadLibraryA(padded_buff.ptr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound

if(rc == null) {
return error.DllNotFound;
} else {
return @ptrCast(windows.HMODULE, rc);
}
}

pub fn windowsUnloadDll(hModule: windows.HMODULE) {
assert(windows.FreeLibrary(hModule)!= 0);
}
14 changes: 14 additions & 0 deletions test/compare_output.zig
Original file line number Diff line number Diff line change
Expand Up @@ -444,4 +444,18 @@ pub fn addCases(cases: &tests.CompareOutputContext) {

tc
});

cases.add("windowsLoadDll failure",
\\const std = @import("std");
\\const debug = std.debug;
\\const os = std.os;
\\const win = std.os.windows;
\\const allocator = std.debug.global_allocator;
\\pub fn main() -> %void {
\\ const handle = os.windowsLoadDll(allocator, "asdf.dll") %% |err| {
\\ debug.warn("{}: {}", @errorName(err), OpenGLDLL);
\\ return;
\\ };
\\}
, "DllNotFound: asdf.dll\n");
}