-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
runtime: remove assumptions on Android Bionic's TLS layout #29674
Comments
Thank you for working on this. I'll page someone more knowledgable on the runtime than myself: @randall77 @ianlancetaylor. |
What are the chances that x86 Bionic could implement TLS relocations as they are implemented on x86 GNU/Linux systems? Because then I think everything would just work. Reserving a register on amd64 would break all existing assembly code, which means breaking a lot of crypto packages. I think that would be difficult. Though the ABI support in 1.12 might provide a stepping stone toward supporting that. I don't fully understand your TLS IE example for amd64. It seems to assume that the offset from In glibc, certain offsets from |
Implementing the relocations is straightforward. The difficulty is that Go always uses static TLS ("IE" accesses), even though an Android app's solib is loaded with dlopen. Using IE in an solib doesn't generally work, because libc needs to allocate a thread's static TLS memory before it starts running, and it can't move/extend that memory when dlopen is called later. It can work if the libc reserves enough surplus static TLS memory. glibc reserves space ( The surplus amount needs to be enough for all dlopen'ed solibs. Once it's exhausted, dlopen fails. There are other problems with surplus static TLS involving (a) initialization and (b) the
On GNU/Linux, Go uses a TLS LE access in an executable, and each access is a single instruction, e.g.:
The offset is known at build-time and encoded into the instruction. Typically the static linker would encode the offset, but I think Go's compiler knows that the runtime.tlsg symbol is at offset -8 and can encode it earlier. In a GNU/Linux shared object (
The dynamic loader computes the TPOFF value for the solib's TLS segment and writes it into the GOT. My proposal is to use something resembling TLS IE, but instead of storing the TPOFF in the GOT at load-time, it's computed at run-time and stored in an ordinary STB_LOCAL symbol.
We could allocate a TLS slot that Go could use, but I think we're hesitant to give one to a particular language runtime if we can avoid it. If we did allocate a slot today, Go would only be able to use it on new versions of Android. That's problematic for Android/x86 because it uses TLS LE in the app solib, e.g.:
The situation for Android/ARM is better. For ARM, Go is already computing a TP-relative offset at run-time. |
I think that I have now paged enough memory to say that this seems reasonable to me. Thanks. |
Ok, I think Bionic can add an API like the one I've proposed[1], and then I can upload a patch for Go to use the API on arm/arm64. Fixing x86 is more involved, though -- maybe someone on the Go team could look at that? I'll work on adding the Bionic API. [1] https://android-review.googlesource.com/c/platform/bionic/+/862083 |
Bionic could reserve a TLS slot for use by apps: https://android-review.googlesource.com/c/platform/bionic/+/883872. |
What's the status of this? The feature freeze is about a month away and it would be a shame if Android Q didn't work with Go 1.13. |
@rprichard I see that your pthread_alloc_static_tls_word_np CL is not merged yet (https://android-review.googlesource.com/c/platform/bionic/+/862083). The TLS_SLOT_APP CL is (https://android-review.googlesource.com/c/platform/bionic/+/883872), but since that slot is now "Available for use by apps" and not just for Go, I'm not sure it's a good idea to depend on that. |
I suspect it's too late to get the FWIW, so far, I don't know of any other third-party software that's trying to allocate memory at fixed offsets from the thread pointer. Using
The "underalignment" errors on Q are related to this issue, but I think they can be fixed independently of the "how does Go access g" issue. AFAIK, the errors could be fixed by omitting the runtime.tlsg ELF symbol and the PT_TLS segment on targets that don't use them. AFAIK, those errors only happen when a Go binary is exec'ed (which I thought would be rare?). |
Ok, thank you Ryan. @ianlancetaylor @cherrymui @aclements how do we go about changing 386 and amd64 to not use static offsets on Android? amd64 is using %fs:0x1d0, 386 is using %gs:0xf8, but as far as I understand this issue, we need to use a different offset (0x02?) for Android Q. |
The offset is 2 words (0x8 on x86 and 0x10 on x86-64). |
Of course, silly me. Does the same apply for arm and arm64? |
Yes, |
So which byte has to be patched to get syncthing saved from dying out on the android q emulator with x86? |
Change https://golang.org/cl/169618 mentions this issue: |
@Catfriend1 please try CL 169618. |
We don't use the TLS section on android, and dropping it avoids complaints about underalignment from the Android Q linker. Updates #29674 Change-Id: I91dabf2a58e6eb1783872639a6a144858db09cef Reviewed-on: https://go-review.googlesource.com/c/go/+/169618 Reviewed-by: Ian Lance Taylor <[email protected]>
The reserved TLS_SLOT_APP slot will work only for Q, not for P or earlier. The idea suggested by @ianlancetaylor was
But I guess it will be a bigger change and no one has time to deal with it. |
Backport issue(s) opened: #31155 (for 1.12). Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://golang.org/wiki/MinorReleases. |
@eliasnaur Good work! I can confirm syncthing is running fine on Android Q, x86 emulator now. |
@eliasnaur can you create the backport CL per #29674 (comment) ? Thanks |
Are you sure? The linker changes are quite invasive and Android Q is in beta. Every Android version since 4.4 was released in August or later, so I hope that Go 1.13 will be out just in time for Q final. |
@eliasnaur thanks for the context. We'll wait until 1.13. |
Thanks for fixing this! I see that Go is using For Android versions prior to Q, the Android NDK provides a static inline Another option is to inline this function into the Go source. That'd be automatic with NDK r20, but with earlier NDKs, it could do something like: #include <stdlib.h>
#include <sys/system_properties.h>
int is_Q() {
char value[PROP_VALUE_MAX];
if (__system_property_get("ro.build.version.sdk", value) < 1) {
return 0;
}
return atoi(value) >= 29;
} Unfortunately, it looks like Q still reports SDK 28 and won't be updated for a while, which would make testing the fix difficult/impossible. |
Change https://golang.org/cl/170127 mentions this issue: |
Thanks, I created CL 170127 to look in libc.so directly. I'll leave the arguably more convenient NDK version to sometime after it returns the correct value. |
Yeah, that change looks good to me. |
The presence of the android_get_device_api_level symbol is used to detect Android Q or later. Use the suggestion by Ryan Prichard and look for it in libc.so and not in the entire program where someone else might have defined it. Manually tested on an Android Q amd64 emulator and arm64 Pixel. Updates #29674 Change-Id: Iaef35d8f8910037b3690aa21f319e216a05a9a73 Reviewed-on: https://go-review.googlesource.com/c/go/+/170127 Run-TryBot: Elias Naur <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
Change https://golang.org/cl/170955 mentions this issue: |
Android Q frees a static TLS slot for us to use. Use the offset of that slot as the default for our TLS offset. As a result, runtime/cgo is no more a requirement for Android Q and newer. Updates #31343 Updates #29674 Change-Id: I759049b2e2865bd3d4fdc05a8cfc6db8b0da1f5d Reviewed-on: https://go-review.googlesource.com/c/go/+/170955 TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Cherry Zhang <[email protected]>
Which release version has this commit already on-board? go1.12.7? |
This should be fixed in the upcoming 1.13 release. It is not fixed in any current release, and there are no plans to backport the various changes. |
Thanks for the information. Will wait for 1.13 :) |
Still have this issue after upgrade to go 1.13, could any one provide some kindly suggestion. Many thanks. |
This issue is closed, so please file a new one with the error message you see, the device or emulator version, and Android version (10 final or a beta?). FWIW, I upgraded my Pixel 1 to Android 10 yesterday and ran https://play.google.com/apps/testing/im.scatter.app with no problems. |
Change https://golang.org/cl/200337 mentions this issue: |
When compiling for GOARCH=386 GOOS=android, the compiler was attaching R_TLS_LE relocations inappropriately -- as of Go 1.13 the TLS access recipe for Android refers to a runtime symbol and no longer needs this type of relocation (which was causing a crash when the linker tried to process it). Updates #29674. Fixes #34788. Change-Id: Ida01875011b524586597b1f7e273aa14e11815d6 Reviewed-on: https://go-review.googlesource.com/c/go/+/200337 Run-TryBot: Than McIntosh <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Elias Naur <[email protected]> Reviewed-by: Cherry Zhang <[email protected]>
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
yes
What operating system and processor architecture are you using (
go env
)?Android
go env
OutputDetails
I posted about this issue earlier on golang-dev:
https://groups.google.com/forum/#!msg/golang-dev/yVrkFnYrYPE/2G3aFzYqBgAJ
I'm working on adding ELF TLS support to Android's libc (Bionic), and Go is making assumptions about Android's TLS memory layout that a future version of Android might break. Specifically:
On Android/{arm,arm64}, Go saves and restores its g register to a pthread key, which it accesses directly using the thread pointer. Go allocates a pthread key and assumes that it can find it at a positive offset, within 384 words after the thread pointer. ELF TLS on arm/arm64 allocates the space after the thread pointer to PT_TLS segments instead, which can be arbitrarily large. For maximum efficiency, Bionic might prefer to allocate the pthread keys at a negative offset from the TP, known at compile-time, but then Go can't find them.
On Android/{386,amd64}, Go uses %gs:0xf8/%fs:1d0 for its g register and assumes it can allocate the location by repeatedly calling
pthread_key_create
until it finds a key such thatpthread_setspecific
modifies the desired location. This assumption breaks if Bionic has an even number of TLS slots, because then Go's fixed location is allocated to apthread_key_data_t::seq
field rather than apthread_key_data_t::data
field.I suspect Go is assuming that its pthread-allocated word is initialized to zero, but that's not generally the case when pthread keys are recycled. Bionic's
pthread_getspecific
lazily initializes a key to zero. (It'd be great if I could get confirmation on this point.)Android may be able to keep Go working, but it would be better if Go had a reliable interface for accessing its TLS memory.
There's a simple way to fix arm/arm64 by adding an API to Bionic that allocates a single word of static TLS memory at a fixed offset. For example, I could add something like this to Bionic:
int pthread_alloc_static_tls_word_np(intptr_t* tpoff, void** tlsbase)
(https://android-review.googlesource.com/c/platform/bionic/+/862083)
gcc_android_{arm,arm64}.c
could then look for the new API withdlopen
and fall back to the current allocation behavior if the API is missing.386/amd64 is harder to fix. Two possibilities:
For the ARM design, I'm wondering to what extent Go could use
pthread_getspecific
or dynamic TLS (whether__tls_get_addr
or TLSDESC) to load the g register. Possible downsides:I could upload a Go patch demonstrating arm/arm64 use of the API I proposed.
The text was updated successfully, but these errors were encountered: