Skip to content
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

int.str() causes Unhandled page fault when built with -shared on Windows #23496

Closed
syobocat opened this issue Jan 17, 2025 · 9 comments
Closed
Labels
Bug This tag is applied to issues which reports bugs. OS: Windows Bugs/feature requests, that are specific to Windows OS.

Comments

@syobocat
Copy link
Contributor

syobocat commented Jan 17, 2025

Describe the bug

Calling int.str() causes Unhandled page fault on Windows, when the code is built with -shared

Reproduction Steps

I have created a repository for demonstration, and prepared sample binaries built on GitHub Actions.

code.v:

@[export: 'func']
fn func() {
	println('Begin DLL')
	for i in 0 .. 10 {
		println('${i}')
	}
	println('End DLL')
}

loader.c:

#include <stdio.h>
#include <windows.h>

typedef void(*FUNC)();

int main(void) {
	HMODULE hModule = LoadLibraryW(L"code.dll");
	if (hModule == NULL) {
		DWORD e = GetLastError();
		printf("Failed: %d\r\n", e);
		return 1;
	}
	FUNC func = (FUNC)GetProcAddress(hModule, "func");
	printf("Begin\r\n");
	func();
	printf("End\r\n");
	return 0;
}

Expected Behavior

The following should be printed:

Begin
Begin DLL
0
1
2
3
4
5
6
7
8
9
End DLL
End

Current Behavior

On Windows:

Begin
Begin DLL
0

On Wine:

Begin
Begin DLL
0
wine: Unhandled page fault on read access to 0000000000000002 at address 000000001001CCFE (thread 0024), starting debugger...

Possible Solution

No response

Additional Information/Context

No response

V version

V 0.4.9 d5aa37d

Environment details (OS name and version, etc.)

GitHub Actions:

V full version V 0.4.9 d5aa37d
OS windows, Microsoft Windows Server 2022 Datacenter 20348 64-bit
Processor 4 cpus, 64bit, little endian, AMD EPYC 7763 64-Core Processor
Memory 13.57GB/16GB
V executable C:\hostedtoolcache\windows\v\0.4.9\x64\v.exe
V last modified time 2025-01-17 10:34:01
V home dir OK, value: C:\hostedtoolcache\windows\v\0.4.9\x64
VMODULES OK, value: C:\Users\runneradmin.vmodules
VTMP OK, value: C:\Users\runneradmin\AppData\Local\Temp\v_0
Current working dir OK, value: D:\a\demo\demo
Git version git version 2.47.1.windows.1
V git status Error: fatal: not a git repository (or any of the parent directories): .git
.git/config present false
cc version N/A
gcc version gcc (x86_64-posix-seh-rev2, Built by MinGW-W64 project) 12.2.0
clang version clang version 18.1.8
msvc version N/A
tcc version tcc version 0.9.27 (x86_64 Windows)
tcc git status thirdparty-windows-amd64 b425ac82
emcc version N/A
glibc version ldd (cygwin) 3.5.4

Also tested on a real machine:

V full version V 0.4.9 d5aa37d
OS windows,
Processor 4 cpus, 64bit, little endian, N/A
Memory 4.01GB/7.89GB
V executable C:\Users\syobon\v\v.exe
V last modified time 2025-01-17 09:51:49
V home dir OK, value: C:\Users\syobon\v
VMODULES OK, value: C:\Users\syobon.vmodules
VTMP OK, value: C:\Users\syobon\AppData\Local\Temp\v_0
Current working dir OK, value: C:\Users\syobon\Desktop
Git version git version 2.47.1.windows.2
V git status weekly.2025.1-69-gd5aa37d8
.git/config present true
cc version N/A
gcc version N/A
clang version N/A
msvc version N/A
tcc version tcc version 0.9.27 (x86_64 Windows)
tcc git status thirdparty-windows-amd64 b425ac82
emcc version N/A
glibc version N/A

Note

You can use the 👍 reaction to increase the issue's priority for developers.

Please note that only the 👍 reaction to the issue itself counts as a vote.
Other reactions and those to comments will not be taken into account.

@syobocat syobocat added the Bug This tag is applied to issues which reports bugs. label Jan 17, 2025
Copy link

Connected to Huly®: V_0.6-21925

@kbkpbot
Copy link
Contributor

kbkpbot commented Jan 17, 2025

This is because V's dll need to call _vinit_caller before any other call, and call _vcleanup_caller before you release the dll.

A modified version of your code:

#include <stdio.h>
#include <windows.h>

typedef void(*FUNC)();

typedef void (*fn_vinit_caller)();
typedef void (*fn_vcleanup_caller)();

int main(void) {
	HMODULE hModule = LoadLibraryW(L"code.dll");
	if (hModule == NULL) {
		DWORD e = GetLastError();
		printf("Failed: %d\r\n", e);
		return 1;
	}
	fn_vinit_caller _vinit_caller = (fn_vinit_caller)GetProcAddress(hModule, "_vinit_caller");
	fn_vcleanup_caller _vcleanup_caller = (fn_vcleanup_caller)GetProcAddress(hModule, "_vcleanup_caller");
	_vinit_caller(); // call this before any other call
	FUNC func = (FUNC)GetProcAddress(hModule, "func");
	printf("Begin\r\n");
	func();
	printf("End\r\n");
	_vcleanup_caller(); // call this before you release the dll
	return 0;
}

@kbkpbot
Copy link
Contributor

kbkpbot commented Jan 17, 2025

Or we need to create a V version of LoadLibrary and FreeLibrary?

Oh, we already have dl.open....

@felipensp felipensp added the OS: Windows Bugs/feature requests, that are specific to Windows OS. label Jan 17, 2025
@syobocat
Copy link
Contributor Author

This is because V's dll need to call _vinit_caller before any other call, and call _vcleanup_caller before you release the dll.

I want to load my DLL from an external program, how can I handle that?

@spytheman
Copy link
Member

I want to load my DLL from an external program, how can I handle that?

See the modified example, that @kbkpbot posted.
It shows what to do.

@syobocat
Copy link
Contributor Author

syobocat commented Jan 18, 2025

It shows what to do.

The "external program" I want to use is proprietary and I have no access to the source, so is there any way to call _vinit_caller and _vcleanup_caller from DLL side?

@kbkpbot
Copy link
Contributor

kbkpbot commented Jan 18, 2025

It shows what to do.

The "external program" I want to use is proprietary and I have no access to the source, so is there any way to call _vinit_caller and _vcleanup_caller from DLL side?

  1. You can make a LoadLibrary hook, in which you can LoadLibrary first and then call the _vinit_caller, or
  2. I will make a PR which will let your v function can call the _vinit_caller.

@kbkpbot
Copy link
Contributor

kbkpbot commented Jan 18, 2025

After apply the PR #23507, you can modify your DLL code as follow:

fn C._vinit_caller()
fn C._vcleanup_caller()

@[export: 'func']
fn func() {
	C._vinit_caller()          // must call first
	println('Begin DLL')
	for i in 0 .. 10 {
		println('${i}')
	}
	println('End DLL')
	C._vcleanup_caller()    // must call last,  note, currently , it is an empty function....
}

Every your export function, should call _vinit_caller()/_vcleanup_caller()...
This is a workaround.

@jorgeluismireles
Copy link

jorgeluismireles commented Jan 20, 2025

Question. Is possible to make a specific module where to put _vinit_caller and _vcleanup_caller like this:

module dll // xxx.xxx.dll

fn C._vinit_caller()
fn C._vcleanup_caller()

fn init() {
    C._vinint_caller()
}

fn cleanup() {
    C._vcleanup_caller()
}

And then use the above by importing xxx.xxx.xxx.dll as _ ?

import xxx.xxx.dll as _

@[export: 'func']
fn func() {
	println('Begin DLL')
	for i in 0 .. 10 {
		println('${i}')
	}
	println('End DLL')
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug This tag is applied to issues which reports bugs. OS: Windows Bugs/feature requests, that are specific to Windows OS.
Projects
None yet
Development

No branches or pull requests

5 participants