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

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

This comment has been minimized.

Copy link
Contributor Author

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

This comment has been minimized.

Copy link
Member

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

This comment has been minimized.

Copy link
Contributor Author

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 Feb 18, 2015

@andlabs

This comment has been minimized.

Copy link
Contributor Author

commented Feb 18, 2015

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

@alexbrainman

This comment has been minimized.

Copy link
Member

commented Feb 18, 2015

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

Alex

@mattn

This comment has been minimized.

Copy link
Member

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

This comment has been minimized.

Copy link
Member

commented Feb 18, 2015

Anyone, could you please re-open this?

@mikioh mikioh reopened this Feb 18, 2015

@mattn

This comment has been minimized.

Copy link
Member

commented Feb 19, 2015

@andlabs

This comment has been minimized.

Copy link
Contributor Author

commented Feb 19, 2015

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

@mattn ah, thanks for the clarification :)

@alexbrainman

This comment has been minimized.

Copy link
Member

commented Feb 20, 2015

@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

This comment has been minimized.

Copy link
Contributor Author

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

This comment has been minimized.

Copy link
Member

commented Mar 20, 2015

@mattn

This comment has been minimized.

Copy link
Member

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

This comment has been minimized.

Copy link
Member

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

This comment has been minimized.

Copy link
Member

commented Mar 24, 2015

@mattn

This comment has been minimized.

Copy link
Member

commented Mar 24, 2015

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

@minux

This comment has been minimized.

Copy link
Member

commented Mar 24, 2015

Thanks for confirmation!

@minux minux closed this 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
6 participants
You can’t perform that action at this time.