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

cmd/cgo: link failure when referencing standard COM GUID (non-function external symbol in a DLL) #9916

Closed
andlabs opened this issue Feb 18, 2015 · 17 comments

Comments

@andlabs
Copy link
Contributor

andlabs commented Feb 18, 2015

package main
import "fmt"
// #define CINTERFACE
// #include <windows.h>
// #cgo LDFLAGS: -luuid
import "C"
func main() {
    fmt.Printf("IID_IUnknown is at %p", &C.IID_IUnknown)
}

IID_IUnknown is a GUID that COM uses to refer to the IUnknown interface (IUnknown is to COM as interface{} is to Go). These GUIDs are defined in uuid.dll, a system DLL that you link against programs that want to use COM. This confuses Go's linker:

$ CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -x
WORK=/tmp/go-build692260167
mkdir -p $WORK/_/tmp/comtest/_obj/
cd /tmp/comtest
CGO_LDFLAGS="-g" "-O2" "-luuid" /home/pietro/go/pkg/tool/linux_amd64/cgo -objdir $WORK/_/tmp/comtest/_obj/ -- -I $WORK/_/tmp/comtest/_obj/ test.go
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -print-libgcc-file-name
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -I $WORK/_/tmp/comtest/_obj/ -g -O2 -o $WORK/_/tmp/comtest/_obj/_cgo_main.o -c $WORK/_/tmp/comtest/_obj/_cgo_main.c
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -I $WORK/_/tmp/comtest/_obj/ -g -O2 -o $WORK/_/tmp/comtest/_obj/_cgo_export.o -c $WORK/_/tmp/comtest/_obj/_cgo_export.c
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -I $WORK/_/tmp/comtest/_obj/ -g -O2 -o $WORK/_/tmp/comtest/_obj/test.cgo2.o -c $WORK/_/tmp/comtest/_obj/test.cgo2.c
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -o $WORK/_/tmp/comtest/_obj/_cgo_.o $WORK/_/tmp/comtest/_obj/_cgo_main.o $WORK/_/tmp/comtest/_obj/_cgo_export.o $WORK/_/tmp/comtest/_obj/test.cgo2.o -g -O2 -luuid
/home/pietro/go/pkg/tool/linux_amd64/cgo -objdir $WORK/_/tmp/comtest/_obj/ -dynpackage main -dynimport $WORK/_/tmp/comtest/_obj/_cgo_.o -dynout $WORK/_/tmp/comtest/_obj/_cgo_import.go
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -o $WORK/_/tmp/comtest/_obj/_all.o $WORK/_/tmp/comtest/_obj/_cgo_export.o $WORK/_/tmp/comtest/_obj/test.cgo2.o -g -O2 -Wl,-r -nostdlib -Wl,--start-group -lmingwex -lmingw32 -Wl,--end-group /usr/lib/gcc/i686-w64-mingw32/4.9-win32/libgcc.a
/home/pietro/go/pkg/tool/linux_amd64/8g -o $WORK/_/tmp/comtest.a -trimpath $WORK -p _/tmp/comtest -D _/tmp/comtest -I $WORK -pack $WORK/_/tmp/comtest/_obj/_cgo_gotypes.go $WORK/_/tmp/comtest/_obj/test.cgo1.go $WORK/_/tmp/comtest/_obj/_cgo_import.go
pack r $WORK/_/tmp/comtest.a $WORK/_/tmp/comtest/_obj/_all.o # internal
cd .
/home/pietro/go/pkg/tool/linux_amd64/8l -o comtest.exe -L $WORK -extld=i686-w64-mingw32-gcc $WORK/_/tmp/comtest.a
# _/tmp/comtest
main.init: IID_IUnknown: not defined
main.init: undefined: IID_IUnknown

I'm not sure if this specifically happens for these UUIDs or for any symbol in a DLL (or other shared object) hat isn't a function.

$ go version
go version devel +3ad906b Wed Feb 18 03:29:47 2015 +0000 linux/amd64
$ i686-w64-mingw32-gcc --version
i686-w64-mingw32-gcc (GCC) 4.9.1
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ # MinGW-w64 3.1.0
@andlabs
Copy link
Contributor Author

andlabs commented Feb 18, 2015

The same/a similar thing happens if the reference to IID_IUnknown is in a C file (this is a separate comment to keep the two examples separate):

package main
import "fmt"
// #cgo LDFLAGS: -luuid
// extern void *getIUnknown(void);
import "C"
func main() {
    fmt.Printf("IID_IUnknown is at %p", C.getIUnknown())
}
#define CINTERFACE
#include <windows.h>
void *getIUnknown(void)
{
    return (void *) (&IID_IUnknown);
}
$ CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -x
WORK=/tmp/go-build303968351
mkdir -p $WORK/_/tmp/comtest/_obj/
cd /tmp/comtest
CGO_LDFLAGS="-g" "-O2" "-luuid" /home/pietro/go/pkg/tool/linux_amd64/cgo -objdir $WORK/_/tmp/comtest/_obj/ -- -I $WORK/_/tmp/comtest/_obj/ test.go
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -print-libgcc-file-name
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -I $WORK/_/tmp/comtest/_obj/ -g -O2 -o $WORK/_/tmp/comtest/_obj/_cgo_main.o -c $WORK/_/tmp/comtest/_obj/_cgo_main.c
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -I $WORK/_/tmp/comtest/_obj/ -g -O2 -o $WORK/_/tmp/comtest/_obj/_cgo_export.o -c $WORK/_/tmp/comtest/_obj/_cgo_export.c
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -I $WORK/_/tmp/comtest/_obj/ -g -O2 -o $WORK/_/tmp/comtest/_obj/test.cgo2.o -c $WORK/_/tmp/comtest/_obj/test.cgo2.c
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -I $WORK/_/tmp/comtest/_obj/ -g -O2 -o $WORK/_/tmp/comtest/_obj/test.o -c ./test.c
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -o $WORK/_/tmp/comtest/_obj/_cgo_.o $WORK/_/tmp/comtest/_obj/_cgo_main.o $WORK/_/tmp/comtest/_obj/_cgo_export.o $WORK/_/tmp/comtest/_obj/test.cgo2.o $WORK/_/tmp/comtest/_obj/test.o -g -O2 -luuid
/home/pietro/go/pkg/tool/linux_amd64/cgo -objdir $WORK/_/tmp/comtest/_obj/ -dynpackage main -dynimport $WORK/_/tmp/comtest/_obj/_cgo_.o -dynout $WORK/_/tmp/comtest/_obj/_cgo_import.go
i686-w64-mingw32-gcc -I . -m32 -mthreads -fmessage-length=0 -o $WORK/_/tmp/comtest/_obj/_all.o $WORK/_/tmp/comtest/_obj/_cgo_export.o $WORK/_/tmp/comtest/_obj/test.cgo2.o $WORK/_/tmp/comtest/_obj/test.o -g -O2 -Wl,-r -nostdlib -Wl,--start-group -lmingwex -lmingw32 -Wl,--end-group /usr/lib/gcc/i686-w64-mingw32/4.9-win32/libgcc.a
/home/pietro/go/pkg/tool/linux_amd64/8g -o $WORK/_/tmp/comtest.a -trimpath $WORK -p _/tmp/comtest -D _/tmp/comtest -I $WORK -pack $WORK/_/tmp/comtest/_obj/_cgo_gotypes.go $WORK/_/tmp/comtest/_obj/test.cgo1.go $WORK/_/tmp/comtest/_obj/_cgo_import.go
pack r $WORK/_/tmp/comtest.a $WORK/_/tmp/comtest/_obj/_all.o # internal
cd .
/home/pietro/go/pkg/tool/linux_amd64/8l -o comtest.exe -L $WORK -extld=i686-w64-mingw32-gcc $WORK/_/tmp/comtest.a
# _/tmp/comtest
main(.text): IID_IUnknown: not defined
main(.text): undefined: IID_IUnknown

@mikioh mikioh changed the title cgo: link failure when referencing standard COM GUID (non-function external symbol in a DLL) cmd/cgo: link failure when referencing standard COM GUID (non-function external symbol in a DLL) Feb 18, 2015
@mattn
Copy link
Member

mattn commented Feb 18, 2015

You need to set INITGUID before include.

package main

import "fmt"

// #define INITGUID
// #include <objidl.h>
// #cgo LDFLAGS: -luuid
import "C"

func main() {
    fmt.Printf("IID_IUnknown is at %p", &C.IID_IUnknown)
}

Btw, If you want to call COM/OLE from go, see also https://github.com/mattn/go-ole

@andlabs
Copy link
Contributor Author

andlabs commented Feb 18, 2015

Well that fixed the build; thanks. What exactly does INITGUID do? From what I can tell it bypasses uuid.dll entirely and includes the GUIDs in the binary... in which case I'm not sure if this counts as a fix because I don't know if some other thing could cause this specific problem.

I don't need to use COM from Go; I wrote a new Table control for package ui and its accessibility code uses COM (as that's how accessibility is exposed on Windows) but that's all on the C side.

@mikioh mikioh closed this as completed Feb 18, 2015
@andlabs
Copy link
Contributor Author

andlabs commented Feb 18, 2015

Wait, so is this really the only scenario in which the above error will happen?

@alexbrainman
Copy link
Member

@andlabs when you use gcc, you have to play by its rules. If you have a problem will reopen this issue.

Alex

@mattn
Copy link
Member

mattn commented Feb 18, 2015

IID_IUnknown is imported from dll. So it shouldn't be specified as cgo_import_static.

https://go-review.googlesource.com/#/c/5160/

@mattn
Copy link
Member

mattn commented Feb 18, 2015

Anyone, could you please re-open this?

@mikioh mikioh reopened this Feb 18, 2015
@mattn
Copy link
Member

mattn commented Feb 19, 2015

Sorry, I updated CL.

https://go-review.googlesource.com/#/c/5253/

@andlabs
Copy link
Contributor Author

andlabs commented Feb 19, 2015

@alexbrainman I'm sorry, could you explain what you mean?

@mattn ah, thanks for the clarification :)

@alexbrainman
Copy link
Member

@andlabs gcc is not part of Go. gcc is another big world. If you want to go there, you need to understand how it works. I don't know how it works. Your original issue was about IID_IUnknown structure. You don't really need to use gcc to access IID_IUnknown.

@andlabs
Copy link
Contributor Author

andlabs commented Feb 20, 2015

I know that; I don't know how my question was about gcc, as this happens with cgo and it clearly happens at the 8l step, not at gcc...

@minux
Copy link
Member

minux commented Mar 20, 2015 via email

@mattn
Copy link
Member

mattn commented Mar 21, 2015

I'm not sure, but if you didn't fix link phase for external library to use -luuid, this issue may not be fixed. I'll try again in next week.

@mattn
Copy link
Member

mattn commented Mar 23, 2015

package main

/*
#include <stdio.h>
#include <objbase.h>
#cgo LDFLAGS: -luuid
int ccc = 3;
*/
import "C"
import "log"
import "os"

func test9916(t *log.Logger) {
    v := C.IID_IUnknown
    log.Println(v)
    if v.Data1 != 0 {
        t.Fatalf("Data1 %v", v)
    }
    if v.Data2 != 0 {
        t.Fatalf("Data2 %v", v)
    }
    if v.Data3 != 0 {
        t.Fatalf("Data3 %v", v)
    }
    if v.Data4[0] != 192 {
        t.Fatalf("Data4[0] %v", v)
    }
    if v.Data4[7] != 70 {
        t.Fatalf("Data4[7] %v", v)
    }
    println(C.ccc)
}

func main() {
    test9916(log.New(os.Stdout, "", 0))
}
C:\temp>go build test9916.go
# command-line-arguments
main.init: IID_IUnknown: not defined
main.init: undefined: IID_IUnknown

See https://go-review.googlesource.com/#/c/5253/3/src/cmd/go/build.go

On windows, .a file doesn't contains object files in libuuid.a. So libuuid.a should be linked at link phase of executable file.

@minux
Copy link
Member

minux commented Mar 24, 2015 via email

@mattn
Copy link
Member

mattn commented Mar 24, 2015

Ah, sorry, #4069 wasn't merged when I checked. I thought it is already merged. #4069 fixes this issue.

@minux
Copy link
Member

minux commented Mar 24, 2015

Thanks for confirmation!

@minux minux closed this as completed Mar 24, 2015
@golang golang locked and limited conversation to collaborators Jun 25, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants