-
Notifications
You must be signed in to change notification settings - Fork 18k
runtime/cgo: missing -lmsvcrt causes sprintf misbehaving on Windows #12030
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
Comments
Please show us a complete standalone Go example. Thanks. I don't understand what Lua has to do with this. |
package main /* void my_print(float a) func main() { |
When I run that program it prints "20.5", which is what I expect. Are you saying that it prints something different on your system? |
same issue with go 1.5 rc1 and mingw64 on Win7-64. |
@lixin9311 What does the program print on your system? |
printf works well. sprintf is misbehaving. |
@lixin9311 Please provide a complete standalone program, please tell us what you expect it to do, and please tell us what it actually does do. Thanks. |
same code as @xicheng provided. |
What does the equivalent C program print on Windows? |
20.5 On Mon, Aug 10, 2015 at 4:12 PM, Ian Lance Taylor notifications@github.com
|
I have reproduced this on a Windows machine. Nice. |
This is kind of amazing. Here is a C program:
It prints:
Here is a very similar Go program:
It prints:
The use of a separate thread to run the code was meant to make sure that Go execution had not somehow poisoned that thread. The thread running the code in this case was created by Windows and has never been used for anything but C. Even so sprintf doesn't work. No matter what you try, %g formats as "g". Similarly, %e formats as "e" and %f formats as "f". I found that if you try to print 0 with %q (not valid) you get just "q" out, much like %g producing just "g". I wonder if the default case in this printf is to drop the %, as if %% were generalized. Then the question is just why %e, %f, %g are invalid like %q in a Go binary, but valid in a C binary. And why they are invalid in sprintf but valid in printf. On 32-bit x86 systems Go changes the FPU mode at startup, but there is no such change on 64-bit systems. And again printf works while sprintf does not. I also tried snprintf (fails the same way) and sprintf_s (returns -1, indicating some kind of error, but we knew that). It's quite the mystery. |
This appears to be caused by external linking. Compiling with -ldflags=-linkmode=internal makes sprintf start working again. It's hard to say how serious this is. It seems like something very fundamental must be wrong; the question is how much it affects. If it really is serious then we could change the default link mode on windows back to internal linking (external linking support is new in this release). How important is external linking? /cc @alexbrainman @minux |
Confirmed with
|
We have resolved a lot of windows cgo issues with external linking. I think external linking is good default. I think we need to understand what this problem is before deciding what to do about it. I am fighting with issue #10776 at this moment. I will try to debug this when I have time. I am sure Minux might have some ideas too. Alex |
The real cause is actually not that serious. ntdll.dll also defines a sprintf function, during internal linking, Try this to reproduce the issue with C: $ type b.c (XXX is certain random garbage, it probably is system dependent.) |
Good catch @minux . Do you know how to fix that? Alex |
The reason we link with ntdll is because we need NtWaitForSingleObject
to implement runtime.usleep2.
The fix is to explicitly link with -lmsvcrt and make sure it's before
-lntdl.
I will send a CL very soon.
|
And then something else will break. Can we supply dll names as part of obj file instead somehow? Alex |
I don't know if we can request a symbol from a specific dll in COFF.
Linking with msvcrt should be fine. The more complete solution might
be to remove the dependency on NtWaitForSingleObject. We use
the one in ntdll, which presumably is a thin wrapper around the actual
syscall, so that it could run on regular Go stack.
Linking msvcrt must be ok because runtime/cgo calls free and both
malloc and free are defined there.
|
How does ldpe function determines which dlls to call? Surely it is there in pe symbol table. Or maybe in import section.
As far as I remember rsc added call to NtWaitForSingleObject in usleep because he didn't have enough stack to call standard runtime.stdcall function (back in the days when runtime was written in C). It is also little bit quicker (because it does not need runtime.cgocall and friends). I wouldn't bother changing it. Alex |
CL https://golang.org/cl/14472 mentions this issue. |
CL https://golang.org/cl/16966 mentions this issue. |
It's because runtime links to ntdll, and ntdll exports a couple incompatible libc functions. We must link to msvcrt first and then try ntdll. Fixes #12030. Change-Id: I0105417bada108da55f5ae4482c2423ac7a92957 Reviewed-on: https://go-review.googlesource.com/14472 Reviewed-by: Alex Brainman <alex.brainman@gmail.com> Run-TryBot: Minux Ma <minux@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-on: https://go-review.googlesource.com/16966 Run-TryBot: Austin Clements <austin@google.com> Reviewed-by: Minux Ma <minux@golang.org> Reviewed-by: Russ Cox <rsc@golang.org>
CL https://golang.org/cl/30737 mentions this issue. |
CL 14472 solved issue #12030 by explicitly linking msvcrt.dll to every cgo executable we build. This CL achieves the same by manually loading ntdll.dll during startup. Updates #12030 Change-Id: I5d9cd925ef65cc34c5d4031c750f0f97794529b2 Reviewed-on: https://go-review.googlesource.com/30737 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Minux Ma <minux@golang.org>
print any number aways get 'g' when I use golua,
I found it is caused by sprinf.
in lua source code:
define LUA_NUMBER_SCAN "%lf"
define LUA_NUMBER_FMT "%.14g"
define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n))
define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */
so ,I wrote a test code:
/*
include <stdio.h>
void my_print(float a)
{
char buff[32];
sprintf(buff,"%.14g",a);
printf("%s\n",buff);
}
*/
C.my_print(20.5)
it output g
i use go1.5 beta3 on win7 64
The text was updated successfully, but these errors were encountered: