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

x/mobile: add bitcode support #22395

Closed
ashutosh-android opened this Issue Oct 23, 2017 · 50 comments

Comments

Projects
None yet
@ashutosh-android
Copy link

ashutosh-android commented Oct 23, 2017

I am able to successfully generate and use .framework file using gomobile bind command, however generated lib doesn't have bitcode enabled
Is there a way to enable bitcode in generated binary or are there are any plan to provide support in near future ?

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

go version go1.8.3 darwin/amd64
gomobile version +44a54e9

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

MacOS

What did you do?

Generated .framework lib from go source code using 'gomobile bind'

What did you expect to see?

bitcode should be enabled in generated framework

What did you see instead?

generated framework doesn't have bitcode enabled

@gbbr gbbr changed the title 'gomobile bind' generated framework file doesn't have bitcode support x/mobile: 'gomobile bind' generated framework file doesn't have bitcode support Oct 23, 2017

@gopherbot gopherbot added this to the Unreleased milestone Oct 23, 2017

@gopherbot gopherbot added the mobile label Oct 23, 2017

@ashutosh-android

This comment has been minimized.

Copy link
Author

ashutosh-android commented Oct 24, 2017

Can you please update on this. This is a blocker item for one of my project. It will be great if I get some visibility on release plan for this requirement. Thanks.

@timcooijmans

This comment has been minimized.

Copy link
Contributor

timcooijmans commented Oct 27, 2017

I'm having the same problem: Currently the Go compiler doesn't support compiling to BitCode intermediate representation. I think something has to be done for Gomobile to have a future. It's expected that Apple will mandate BitCode for iOS-apps in the near future.

However there seems to be some work going on on Google-side: https://go.googlesource.com/gollvm/

You could also try https://dragonegg.llvm.org/ but I have no idea if this is going to work.

@ashutosh-android

This comment has been minimized.

Copy link
Author

ashutosh-android commented Mar 12, 2018

Any update on this ?

@eliasnaur

This comment has been minimized.

Copy link
Contributor

eliasnaur commented Mar 16, 2018

I don't think there is any update other than gollvm is progressing. May I ask why this issue is blocking your project?

@ashutosh-android

This comment has been minimized.

Copy link
Author

ashutosh-android commented Mar 16, 2018

@eliasnaur thanks for looking into this query.
I am working on a mobile project where we will be distributing our service as library project. We are writing alot of business logic in go code so that we can reuse them for both android and iOS using go-mobile. Since framework generated by go-mobile doesn't have bitcode enabled so any developer who will be integrating our solution will not be able to enable bitcode for there application. Considering how apple is strongly recommending developers to enable bitcode it will impact adoption our product in a big way.

I hope this answers your question. Let me know if you have any suggestions/query. Thanks again.

@matti777

This comment has been minimized.

Copy link

matti777 commented Apr 26, 2018

Just ran into this. Yes, bitcode support would be a must. Any updates?

@jpop32

This comment has been minimized.

Copy link

jpop32 commented Apr 26, 2018

Yes, lack of bitcode support on iOS is a showstopper for library projects, and a big liability for others. We can safely assume that 'strong recommendation' to use bitcode is going to turn into 'requirement' sooner or later.

@adam-p

This comment has been minimized.

Copy link

adam-p commented Apr 26, 2018

Since it hasn't been mentioned here yet: Apple TV apps must support Bitcode. Not optional. Search for "bitcode" here: https://developer.apple.com/tvos/submit/

This has caused us problems when trying to support a client wanting to use our library on Apple TV.

@zboralski

This comment has been minimized.

Copy link

zboralski commented May 20, 2018

Just ran into this.

@drewpitchford

This comment has been minimized.

Copy link

drewpitchford commented May 22, 2018

I'll throw my hat in this ring too. We have iOS/Android sdks that use a go-mobile framework for some common encryption/decryption. Lack of bitcode is a major issue for us. I am attempting to replace the go-mobile framework in the iOS project with a js framework that does the same stuff, but it'd be nice to be able to keep the go framework

@pavlo

This comment has been minimized.

Copy link

pavlo commented May 24, 2018

It is a showstopper for us as well

@biscottigelato

This comment has been minimized.

Copy link

biscottigelato commented Jun 2, 2018

WWDC is coming soon, hopefully Bitcode will not become a requirement... The lack of any response from anyone in the know about how one an get Bitcode going for golang/gomobile is definitely concerning.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

ianlancetaylor commented Jun 5, 2018

As far as I know the only way to get the required Bitcode is to run LLVM. There is an active project to add Go support to LLVM; you can follow it at https://go.googlesource.com/gollvm/ . Until that project is complete and working, there will be no way to get Bitcode.

That is essentially what @eliasnaur said on March 16. While I understand that the pace of these things is frustrating, I don't think it's entirely fair to say that there has been no response. Go is an open source project; if you want this to happen faster, try building GoLLVM, report bugs, and send in fixes.

@ashutosh-android

This comment has been minimized.

Copy link
Author

ashutosh-android commented Aug 8, 2018

Hi,

Checking to see if there is any new development on this ?

Thanks,
Ashutosh

@ncastagnoli

This comment has been minimized.

Copy link

ncastagnoli commented Sep 21, 2018

Hi,

To add to the need, if that's important.

I'm working on a project that started cloud based, and elected to write the code in "go." It turns out we also need to run the code on the client, both iOS and Android. But, our partners are demanding bit-code compliance. We are at a decision point to decide whether to rewrite the entire set of code so we can have a single code base. Curious about the thinking regarding bit code compliance for "go."

Is it a project? If so, what is the time-frame? Go is perfect for our application, incidentally.

@ashutosh-android

This comment has been minimized.

Copy link
Author

ashutosh-android commented Dec 18, 2018

Hi,

Checking again on this. Is there any roadmap for this support ?

Thanks,
Ashutosh

@eliasnaur

This comment has been minimized.

Copy link
Contributor

eliasnaur commented Dec 18, 2018

Please refrain from asking about updates on this issue; fixing it is a lot of work and only benefits niche platforms for Go: tvOS and watchOS. @ianlancetaylor suggested you try gollvm; have you done that? If so, what problems did you run into?

@eliasnaur eliasnaur changed the title x/mobile: 'gomobile bind' generated framework file doesn't have bitcode support x/mobile: add bitcode support Dec 18, 2018

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

ianlancetaylor commented Dec 18, 2018

@matti777

This comment has been minimized.

Copy link

matti777 commented Dec 18, 2018

Ahem, what do you mean tvOS and watchOS? What about iOS? :) Not sure if that is a 'niche' environment for gomobile bind..

@eliasnaur

This comment has been minimized.

Copy link
Contributor

eliasnaur commented Dec 18, 2018

Only watchOS and tvOS require bitcode; iOS does not.

@drewpitchford

This comment has been minimized.

Copy link

drewpitchford commented Dec 18, 2018

@eliasnaur

This comment has been minimized.

Copy link
Contributor

eliasnaur commented Dec 18, 2018

Whatever the likelyhood, let's worry about it when it happens and not waste precious development efforts on hypotheticals. Who knows, perhaps the very existence of non-bitcode gomobile apps will delay Apple's decision.

@matti777

This comment has been minimized.

Copy link

matti777 commented Dec 19, 2018

iOS "requires" bitcode in the way that if you are making a library with golang / gomobile bind, the library you produce will force the iOS app developer to disable bitcode in their app; and we iOS devs really do not like that. :) Also of course there is a point to having bitcode support in the first place and that is now lost. third, who knows when iSteve will decide bitcode support will become mandatory ..

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

ianlancetaylor commented Dec 19, 2018

Everyone agrees that it would be good if we could compile Go into bitcode. As mentioned above, the only way that is ever going to happen is if you use the GoLLVM compiler. So if you want Go compiled into bitcode, please help with GoLLVM.

@tmm1

This comment has been minimized.

Copy link
Contributor

tmm1 commented Mar 13, 2019

The Kotlin/Native team has found a work-around to full bitcode support: they add a __LLVM,__asm marker (via JetBrains/kotlin-native#2186) which tells Xcode the object was created with a bitcode-aware compiler, and that the object was generated from assembly and thus has no bitcode equivalent. (More details about the bitcode format on https://jonasdevlieghere.com/embedded-bitcode/).

See discussion on the kotlin-native repo: JetBrains/kotlin-native#1202 (comment)

The technique was discovered on an earlier PR, which attempted to emit bitcode but was unable to do so reliably without using Apple's version of LLVM: JetBrains/kotlin-native#1564 (comment)

Would it be possible to add a similar empty __LLVM,__asm section to the generated gomobile .framework?

@randall77

This comment has been minimized.

Copy link
Contributor

randall77 commented Mar 14, 2019

It would not be hard to add an empty section to the binary.

I worry that this is essentially a way to hack around Apple's policy. As such, they could revoke this exception at any time and then we're back to square one.

@tmm1

This comment has been minimized.

Copy link
Contributor

tmm1 commented Mar 15, 2019

Here's an overview of how the Apple toolchain deals with assembled objects.

Given this simple program named exit.s:

.data
    output: .ascii "foo"
.bss
    bar: .quad 0
.text
_run:
  movl $0x2000001, %eax # system call $1 with $0x2000000 offset
  movl $0, %ebx         # set the exit code to be $0
  syscall

We can compile it (with bitcode enabled) into either 1) an object, 2) a shared archive, or 3) a static archive

When compiling into an object, the assembler adds a __LLVM, __asm section which is one byte long, containing 0:

$ clang -g -fembed-bitcode -masm=att exit.s -c -o exit.o

$ size -x -l -m exit.o
Segment : 0x139 (vmaddr 0x0 fileoff 872)
	Section (__TEXT, __text): 0xc (addr 0x0 offset 872)
	Section (__LLVM, __asm): 0x1 (addr 0xc offset 884)
	Section (__DATA, __data): 0x3 (addr 0xd offset 885)
	Section (__DATA, __bss): 0x8 (addr 0x131 offset 0)
	Section (__DWARF, __debug_info): 0x8f (addr 0x10 offset 888)
	Section (__DWARF, __debug_abbrev): 0x28 (addr 0x9f offset 1031)
	Section (__DWARF, __debug_aranges): 0x30 (addr 0xc7 offset 1071)
	Section (__DWARF, __debug_line): 0x3a (addr 0xf7 offset 1119)
	total 0x139
total 0x139

$ otool -v -s __LLVM __asm exit.o
exit.o:
Contents of (__LLVM,__asm) section
000000000000000c	00

When compiling to a shared archive, the linker takes the __LLVM, __asm and summarizes it into __LLVM, __bundle as a "Object / Mach-O Object" (instead of "Bitcode"):

$ clang -g -fembed-bitcode -masm=att exit.s -shared -o exit.dylib

$ size -x -l -m exit.dylib 
Segment __TEXT: 0x1000 (vmaddr 0x0 fileoff 0)
	Section __text: 0xc (addr 0xfac offset 4012)
	Section __unwind_info: 0x48 (addr 0xfb8 offset 4024)
	total 0x54
Segment __DATA: 0x1000 (vmaddr 0x1000 fileoff 4096)
	Section __data: 0x3 (addr 0x1000 offset 4096)
	Section __bss: 0x8 (addr 0x1003 offset 0)
	total 0xb
Segment __LLVM: 0x2000 (vmaddr 0x2000 fileoff 8192)
	Section __bundle: 0x1256 (addr 0x2000 offset 8192)
	total 0x1256
Segment __LINKEDIT: 0x1000 (vmaddr 0x4000 fileoff 16384)
total 0x5000

$ otool -v -s __LLVM __bundle exit.dylib
exit.dylib:
Contents of (__LLVM,__bundle) section
For (__LLVM,__bundle) section: xar table of contents:
<?xml version="1.0" encoding="UTF-8"?>
...
   <contents>
    <type>Mach-O Object</type>
    <unknown>
     <type>Mach-O Object</type>
    </unknown>
   </contents>
   <file-type>Object</file-type>
...

You can also invoke the linker manually yourself with the .o file, using ld -bitcode_bundle to ensure the __LLVM, __bundle section is added:

$ ld -bitcode_bundle -arch x86_64 -macosx_version_min 10.14 exit.o -dylib -o exit.dylib
$ size -x -l -m exit.dylib
Segment __TEXT: 0x1000 (vmaddr 0x0 fileoff 0)
	Section __text: 0xc (addr 0xfac offset 4012)
	Section __unwind_info: 0x48 (addr 0xfb8 offset 4024)
	total 0x54
Segment __DATA: 0x1000 (vmaddr 0x1000 fileoff 4096)
	Section __data: 0x3 (addr 0x1000 offset 4096)
	Section __bss: 0x8 (addr 0x1003 offset 0)
	total 0xb
Segment __LLVM: 0x2000 (vmaddr 0x2000 fileoff 8192)
	Section __bundle: 0x122c (addr 0x2000 offset 8192)
	total 0x122c
Segment __LINKEDIT: 0x1000 (vmaddr 0x4000 fileoff 16384)
total 0x5000

If you try to create a static archive with bitcode, the linker step will fail:

$ clang -g -fembed-bitcode -masm=att exit.s -static -o exit.a
ld: -static and -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES) cannot be used together
clang: error: linker command failed with exit code 1 (use -v to see invocation)

The linker will also warn you if any of your objects are missing bitcode (or the __asm section in this case), and advise that you disable bitcode bundling altogether in order to submit the binary:

$ clang -g -masm=att exit.s -c -o exit.o
$ ld -bitcode_bundle -arch x86_64 -macosx_version_min 10.14 exit.o -dylib -o exit.dylib
ld: warning: all bitcode will be dropped because 'exit.o' was built without bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.

On the golang side, ideally the linker could be used with -buildmode=c-shared to generate a dylib containing a XAR table of contents in a __LLVM, __bundle section. But since generating and writing the bundle is non-trivial, we can reduce scope instead by using -linkmode=external (to let Apple's linker take care of the the bitcode bundle generation). In this scenario, golang's compiler only needs to add the 1-byte __LLVM, __asm section to each generated object.

Assuming __asm sections existed, the external linker could ideally be used as follows. Unfortunately, it doesn't work because golang is using the incompatible linker option -headerpad (in order to retrofit DWARF details later).

$ go build -ldflags="-linkmode=external -extldflags=-fembed-bitcode -v" -buildmode=c-shared -o test.dylib test.go
host link: "clang" "-m64" "-Wl,-headerpad,1144" "-dynamiclib" "-o" "test.dylib" "-Qunused-arguments" "/var/folders/_2/hljyy_zj3912lv9qqpy70t5w0000gn/T/go-link-181289496/go.o" "-fembed-bitcode"
/usr/local/Cellar/go/1.12/libexec/pkg/tool/darwin_amd64/link: running clang failed: exit status 1
ld: -headerpad and -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES) cannot be used together
clang: error: linker command failed with exit code 1 (use -v to see invocation)

As a work-around, we can use -buildmode=c-archive instead, and then invoke the linker ourselves to assemble the contained objects as a shared library using ld -force_load:

$ go build -buildmode=c-archive -o test.a test.go

$ ld -bitcode_bundle -force_load test.a -dylib -o test.dylib
ld: in test.a(go.o), building for macOS, but linking in object file built for , file 'test.a' for inferred architecture x86_64

This still fails because in c-archive mode the generate mach-o objects are missing LC_VERSION_MIN_MACOSX/LC_VERSION_MIN_IPHONEOS (or its newer combined variant LC_BUILD_VERSION). We can work around this for now with a hacky patch to force LC_VERSION_MIN_MACOSX to be added to each object:

diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index 8315de5152..45f8acfd5b 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -653,7 +680,7 @@ func Asmbmacho(ctxt *Link) {
                }
        }

-       if ctxt.LinkMode == LinkInternal {
+       if ctxt.LinkMode == LinkInternal || ctxt.BuildMode == BuildModeCArchive {
                // For lldb, must say LC_VERSION_MIN_MACOSX or else
                // it won't know that this Mach-O binary is from OS X
                // (could be iOS or WatchOS instead).

Now we can re-generate the c-archive and convert it into a dylib successfully with the external linker. However, since we are missing __LLVM, __asm sections, bitcode generation is disabled with a warning and the resulting dylib is missing the __LLVM, __bundle section:

$ ~/code/go/bin/go build -buildmode=c-archive -o test.a test.go

$ ld -bitcode_bundle -force_load test.a -dylib -o test.dylib -lpthread
ld: warning: all bitcode will be dropped because 'test.a(go.o)' was built without bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.

$ size -x -l -m test.dylib | grep -A2 __LLVM

At this point, all that's left is patching the golang compiler to add the __LLVM, __asm sections. Once that is in place, ld -bitcode_bundle -force_load should stop warning and generate a xar bundle containing <file-type>Object</file-type> like it did for exit.o above. To do that, we can use this patch:

diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index 8315de5152..923995a02e 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -395,6 +395,13 @@ func (ctxt *Link) domacho() {
                s.Type = sym.SMACHOINDIRECTGOT
                s.Attr |= sym.AttrReachable
        }
+
+       if ctxt.LinkMode == LinkExternal {
+               s := ctxt.Syms.Lookup(".llvmasm", 0) // will be __asm
+               s.Type = sym.SMACHO
+               s.Attr |= sym.AttrReachable
+               s.AddUint8('\x00')
+       }
 }

 func machoadddynlib(lib string, linkmode LinkMode) {
@@ -481,6 +488,11 @@ func machoshbits(ctxt *Link, mseg *MachoSeg, sect *sym.Section, segname string)
                msect.flag = S_MOD_INIT_FUNC_POINTERS
        }

+       if sect.Name == ".llvmasm" {
+               msect.name = "__asm"
+               msect.segname = "__LLVM"
+       }
+
        if segname == "__DWARF" {
                msect.flag |= S_ATTR_DEBUG
        }

Now we can re-generate the c-archive and see the 1-byte __LLVM, __asm marker:

$ ~/code/go/bin/go build -buildmode=c-archive -o test.a test.go

$ size -x -l -m test.a
test.a(go.o):
Segment : 0x0 (vmaddr 0x0 fileoff 4096)
	Section (__TEXT, __text): 0x9380c (addr 0x0 offset 4096)
	Section (__DATA, __rodata): 0x477e9 (addr 0x93820 offset 608288)
    ...
	Section (__LLVM, __asm): 0x1 (addr 0x154000 offset 1396736)
    ...

And we can turn it into a dylib with ld and see that a full bitcode bundle TOC is generated!

$ ld -bitcode_bundle -force_load test.a -dylib -o test.dylib -lpthread

$ size -x -l -m test.dylib | grep -A2 __LLVM
Segment __LLVM: 0x327000 (vmaddr 0x187000 fileoff 1478656)
	Section __bundle: 0x326260 (addr 0x187000 offset 1478656)
	total 0x326260

$ otool -v -s __LLVM __bundle test.dylib
test.dylib:
Contents of (__LLVM,__bundle) section
For (__LLVM,__bundle) section: xar table of contents:
<?xml version="1.0" encoding="UTF-8"?>
<xar>
...
   <file-type>Object</file-type>
...
</xar>

By adding the __asm marker, the toolchain now knows the go.o object was built with a bitcode aware compiler, and happily indexes that object into the bitcode bundle! Hooray!


Getting the same thing working on iOS is even easier, since the first hacky patch above for macOS testing is not required.

In addition, the linker step to convert the .a into a .dylib is also not required. As long as the individual objects in the .a contain either __bitcode or __asm sections, Xcode will link them correctly just by dragging the .a into your xcode project. I compiled my library as follows:

$ CGO_CFLAGS="-isysroot ... -arch arm64 -fembed-bitcode"
$ CGO_LDFLAGS="-isysroot ... -arch arm64 -fembed-bitcode"
$ CC=clang
$ CGO_ENABLED=1
$ GOOS=darwin
$ GOARCH=arm64
$ go build -buildmode=c-archive -tags=ios -o test.a test.go

Then I confirmed that the test.a generated for iOS could be copied into an xcodeproject and deployed to my iOS device, with bitcode enabled on the entire project. Without the patch, Xcode refuses to build and install the ipa on a real device. I assume the ipa could also be submitted to the App Store given user reports on the kotlin-native thread.

Given that this works on iOS, I assume it will also work on tvOS and allow golang to be used in Apple TV apps. I have an Apple TV device that I will run some tests on...

@drewpitchford

This comment has been minimized.

Copy link

drewpitchford commented Mar 15, 2019

@tmm1 make a PR with that. It’d help a lot of people!

@tmm1

This comment has been minimized.

Copy link
Contributor

tmm1 commented Mar 15, 2019

Given that this works on iOS, I assume it will also work on tvOS and allow golang to be used in Apple TV apps. I have an Apple TV device that I will run some tests on...

I was able to get my golang c-archive working on an Apple TV 4 as well!!

It was slightly more tricky there because the lack of a LC_VERSION_MIN_* in the go.o makes the Apple toolchain assume you built for iOS (which is why copying over the .a directly worked for the iOS case earlier). Without LC_VERSION_MIN_TVOS set in go.o I couldn't get Xcode to compile or run on device.

So I used this quick hack to see if it would work, and sure enough I am able to deploy and execute golang code on my bitcode-required tvOS device.

diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index 923995a02e..fbc1c8cf16 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -678,6 +705,10 @@ func Asmbmacho(ctxt *Link) {
                ml.data[0] = 10<<16 | 7<<8 | 0<<0 // OS X version 10.7.0
                ml.data[1] = 10<<16 | 7<<8 | 0<<0 // SDK 10.7.0
        }
+       if ctxt.BuildMode == BuildModeCArchive && true /* tvos */ {
+               ml := newMachoLoad(ctxt.Arch, LC_VERSION_MIN_TVOS, 2)
+               ml.data[0] = 11<<16 | 0<<8 | 0<<0 // tvOS version 11.0
+       }

        a := machowrite(ctxt.Arch, ctxt.Out, ctxt.LinkMode)
        if int32(a) > HEADR {
@eliasnaur

This comment has been minimized.

Copy link
Contributor

eliasnaur commented Mar 15, 2019

This is excellent news!
I'm confused by the plethora of LC_VERSIONs. Are they mutually exclusive? If so, can we rely on the user (such as gomobile) setting the versions with -fmin-version-*? We don't use internal linking for c-archive anyway.
Finally, you did this work for macOS as well. Is bitcode needed for macOS?

@tmm1

This comment has been minimized.

Copy link
Contributor

tmm1 commented Mar 15, 2019

I'm confused by the plethora of LC_VERSIONs. Are they mutually exclusive?

Yes they are mutually exclusive. Each object must be tagged as for iOS, tvOS, macOS, etc. To do this a LC_VERSION_MIN for that OS must be added.

If so, can we rely on the user (such as gomobile) setting the versions with -fmin-version-*?

We can set this via CGO_CFLAGS for all the externally compiled objects, but the main go.o is generated by the go compiler and will not add any of the required LC_VERSION commands. It seems to me we need a way to teach the go compiler about sub-platforms within GOOS=darwin. Perhaps something like GODARWIN=ios ?

Finally, you did this work for macOS as well. Is bitcode needed for macOS?

Not that I know of. It was just easier for me to test everything on macOS first, to understand how the pieces fit together.

@eliasnaur

This comment has been minimized.

Copy link
Contributor

eliasnaur commented Mar 15, 2019

Thank you.

@randall77 in light of the above, how do you think we should proceed? I hope adding the __asm section is ok, but what about correctly setting LC_VERSION_MIN?

@eliasnaur

This comment has been minimized.

Copy link
Contributor

eliasnaur commented Mar 15, 2019

@tmm1 can you think of ways to auto-detect the platform? For example, UIKit usage indicates iOS, AppKit macOS. Is there a certain way to detect watchOS and tvOS?

@tmm1

This comment has been minimized.

Copy link
Contributor

tmm1 commented Mar 15, 2019

@tmm1 can you think of ways to auto-detect the platform? For example, UIKit usage indicates iOS, AppKit macOS. Is there a certain way to detect watchOS and tvOS?

Interesting thought. If we're talking about CGO_ENABLED=1, then (since the user is already required to setup the compiler correctly, i.e. set -isysroot with the particular SDK they want to use) we could look for the symbols TARGET_OS_IPHONE, TARGET_OS_TV, TARGET_OS_WATCH etc from TargetConditionals.h

However I'm not sure if this information is readily available from the go compiler, nor what alternative solution might be available if cgo was disabled.

@randall77

This comment has been minimized.

Copy link
Contributor

randall77 commented Mar 15, 2019

A GODARWIN setting is a possibility. I'd like to see if we can figure out how to avoid it, though.
Can we force external linking for everything but macOS? And can we set this using a linker flag somehow?

@tmm1

This comment has been minimized.

Copy link
Contributor

tmm1 commented Mar 17, 2019

Can we force external linking for everything but macOS? And can we set this using a linker flag somehow?

I tried using -ldflags="-linkmode=external -extldflags=-mtvos-version-min=11.0" but the generated go.o is still missing the load command. I'm not clear on where the boundary between the go compiler and linker is. I assume the compiler generates the go.o object, but it seems to use link/internal/ld/macho.go to do it.

@eliasnaur

This comment has been minimized.

Copy link
Contributor

eliasnaur commented Mar 18, 2019

@randall77 Xcode checks all object files in a c-archive, so the go.o object file produced by the Go linker also needs the LC_VERSION_MIN_* load commands. I would be surprised if any linker flag could override this setting on an existing object file.
So I think that leaves setting the value from the Go linker. Since iOS et al are always externally linked, our linker could read load commands from the .o files produced by cgo and copy any LC_VERSION_MIN_* commands to go.o. Does that sound plausible? If so, any pointers to where in the linker to add this logic would be greatly appreciated.

@randall77

This comment has been minimized.

Copy link
Contributor

randall77 commented Mar 18, 2019

our linker could read load commands from the .o files produced by cgo

Does our linker see those files? In external linking mode I would think the Go linker only sees the output of the Go compiler - everything from C compiler gets passed directly to the C linker. At least, that's my (probably poor) understanding.

Also, just because we're externally linking doesn't mean we're using cgo.

But I do like the idea of tricking the C compiler into telling us what LC_VERSION_MIN_* string to use. Maybe we compile a very simple .c file and grovel through the output? This would mean that building for these targets would require a proper CC setup, including cross-compiling if that's what you're doing.

Maybe a GODARWIN would be simpler.

@gopherbot

This comment has been minimized.

Copy link

gopherbot commented Mar 19, 2019

Change https://golang.org/cl/168318 mentions this issue: cmd/go/internal/work: whitelist tvOS and watchOS compiler flags

@gopherbot

This comment has been minimized.

Copy link

gopherbot commented Mar 19, 2019

Change https://golang.org/cl/168062 mentions this issue: cmd/gomobile: enable bitcode

gopherbot pushed a commit that referenced this issue Mar 19, 2019

cmd/go/internal/work: whitelist tvOS and watchOS compiler flags
Updates #22395

Change-Id: I6c207934b32d38374875f756c4f8c6dfe38d8cb0
Reviewed-on: https://go-review.googlesource.com/c/go/+/168318
Run-TryBot: Elias Naur <mail@eliasnaur.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>

gopherbot pushed a commit to golang/mobile that referenced this issue Mar 19, 2019

cmd/gomobile: enable bitcode
Add -fembed-bitcode to the host compiler and linker to instruct it
to add bitcode to object files.

Updates golang/go#22395

Change-Id: Ie0297079f27c9b5b8ea55e0a36067dc768342986
Reviewed-on: https://go-review.googlesource.com/c/mobile/+/168062
Run-TryBot: Elias Naur <mail@eliasnaur.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
@tmm1

This comment has been minimized.

Copy link
Contributor

tmm1 commented Mar 19, 2019

@eliasnaur I'm curious what impact your whitelist change in b4f3b8a has?

@eliasnaur

This comment has been minimized.

Copy link
Contributor

eliasnaur commented Mar 19, 2019

Very little; it allows the tvOS and watchOS flags in #cgo directives. I noticed the missing flags while working on this.

@tmm1

This comment has been minimized.

Copy link
Contributor

tmm1 commented Mar 19, 2019

Very little; it allows the tvOS and watchOS flags in #cgo directives. I noticed the missing flags while working on this.

Aha, okay. It was working for me already via the CGO_CFLAGS env vars so I was curious what the change enabled.

Maybe a GODARWIN would be simpler.

Another idea would be to add some new flags to the go linker, so the user could do something like -ldflags="-machoplatform=ios -machominver=11.0.0"

If we can settle on a good approach here, I'm happy to start putting together some CLs.

Another thing to keep in mind is that the LC_VERSION_MIN_* have been replaced with LC_BUILD_VERSION for the next macOS release, and that this new load command will likely be required to build marzipan apps which work on both iOS and macOS. However since all existing macOS/iOS/tvOS/watchOS releases still use the older variant, we will need to keep supporting that for the foreseeable future.

For reference, here are the two load command definitions:

    struct version_min_command {
      uint32_t cmd;       // LC_VERSION_MIN_MACOSX or
                          // LC_VERSION_MIN_IPHONEOS
      uint32_t cmdsize;   // sizeof(struct version_min_command)
      uint32_t version;   // X.Y.Z is encoded in nibbles xxxx.yy.zz
      uint32_t sdk;       // X.Y.Z is encoded in nibbles xxxx.yy.zz
    };

    struct build_tool_version {
      uint32_t tool;      // enum for the tool
      uint32_t version;   // version of the tool
    };

    struct build_version_command {
      uint32_t cmd;       // LC_BUILD_VERSION
      uint32_t cmdsize;   // sizeof(struct build_version_command) +
                          // ntools * sizeof(struct build_tool_version)
      uint32_t platform;  // platform
      uint32_t minos;     // X.Y.Z is encoded in nibbles xxxx.yy.zz
      uint32_t sdk;       // X.Y.Z is encoded in nibbles xxxx.yy.zz
      uint32_t ntools;    // number of tool entries following this
    };

One advantage to the linker flag approach is that we could add more flags like -machosdkver etc as the need arises. Whereas GODARWIN could only support at-most platform and minver (via GODARWIN=ios or GODARWIN=ios11.0.0).

@gopherbot

This comment has been minimized.

Copy link

gopherbot commented Mar 19, 2019

Change https://golang.org/cl/168321 mentions this issue: cmd/link/internal/ld: copy LC_VERSION_MIN_* commands to Go object file

@gopherbot

This comment has been minimized.

Copy link

gopherbot commented Mar 19, 2019

Change https://golang.org/cl/168320 mentions this issue: cmd/link: enable bitcode build for iOS/tvOS/watchOS

@eliasnaur

This comment has been minimized.

Copy link
Contributor

eliasnaur commented Mar 19, 2019

You can check out my current progress at the two CLs posted above. I got LC_VERSION_MIN_* and LC_BUILD_VERSION copying to work, and only need tests.

tmm1 added a commit to fancybits/go that referenced this issue Mar 20, 2019

cmd/go/internal/work: whitelist tvOS and watchOS compiler flags
Updates golang#22395

Change-Id: I6c207934b32d38374875f756c4f8c6dfe38d8cb0
Reviewed-on: https://go-review.googlesource.com/c/go/+/168318
Run-TryBot: Elias Naur <mail@eliasnaur.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
@gopherbot

This comment has been minimized.

Copy link

gopherbot commented Mar 20, 2019

Change https://golang.org/cl/168459 mentions this issue: cmd/link/internal/ld: extract Mach-O load command parsing

@gopherbot gopherbot closed this in ba96564 Mar 21, 2019

gopherbot pushed a commit that referenced this issue Mar 21, 2019

cmd/link/internal/ld: extract Mach-O load command parsing
We're going to need the ability to extract the LC_VERSION_MIN_* and
LC_BUILD_VERSION load commands. This CL adds peekMachoPlatform to do
that and in the process simplifies machoCombineDwarf.

While here, disable DWARF combining for Apple platforms other than
macOS (watchOS, tvOS, bridgeOS), not just iOS.

Updates #22395

Change-Id: I4862b0f15ccc87b7be1a6532b4d37b47c8f7f243
Reviewed-on: https://go-review.googlesource.com/c/go/+/168459
Reviewed-by: Ian Lance Taylor <iant@golang.org>

tmm1 added a commit to fancybits/go that referenced this issue Mar 21, 2019

cmd/link/internal/ld: enable bitcode builds for iOS, tvOS, watchOS
The Go toolchain cannot output bitcode, but there is a trick where
object code can be marked with an __asm section, persuading the
Apple toolchain to include our object code in bitcode builds.

This enables Go builds with bitcode enabled; the next CL adds
the necessary plumbing for building on tvOS and watchOS.

Thanks to Aman Gupta for the trick.

Test is added two CLs from here.

Fixes golang#22395 (at least until Apple tightens bitcode requirements.)

Change-Id: Ic1c1448c4d46222bb3dd097b1f4df80848051e5f
Reviewed-on: https://go-review.googlesource.com/c/go/+/168320
Reviewed-by: Cherry Zhang <cherryyz@google.com>
(cherry picked from commit ba96564)

tmm1 added a commit to fancybits/go that referenced this issue Mar 21, 2019

cmd/link/internal/ld: extract Mach-O load command parsing
We're going to need the ability to extract the LC_VERSION_MIN_* and
LC_BUILD_VERSION load commands. This CL adds peekMachoPlatform to do
that and in the process simplifies machoCombineDwarf.

While here, disable DWARF combining for Apple platforms other than
macOS (watchOS, tvOS, bridgeOS), not just iOS.

Updates golang#22395

Change-Id: I4862b0f15ccc87b7be1a6532b4d37b47c8f7f243
Reviewed-on: https://go-review.googlesource.com/c/go/+/168459
Reviewed-by: Ian Lance Taylor <iant@golang.org>
(cherry picked from commit 0fe1986)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.