[haiku-gsoc] Golang port: amd64 support

  • From: "Zhuowei zhang" <dmarc-noreply@xxxxxxxxxxxxx> (Redacted sender "zhuoweizhang@xxxxxxxxx" for DMARC)
  • To: haiku-gsoc <haiku-gsoc@xxxxxxxxxxxxx>
  • Date: Fri, 8 Aug 2014 22:04:19 -0700

Hello,

During the last three days, I worked on porting the Golang port to the 64-bit 
version of Haiku.

The amd64 port is at the same level of functionality as the 386 port: signals, 
semaphores/timers, I/O, etc, are all working.

The amd64 port was a lot more straightforward compared to the 386 port, since 
the Solaris port, which the Haiku port was based on, only supported amd64. 
Unlike the 32-bit port, I was able to basically use Solaris' assembly code 
unmodified.

On the day before yesterday, I added the toolchain changes to allow generation 
of executables for 64-bit Haiku, and copied definition files for errnos, etc 
from the 386 port. Yesterday, I worked on adapting the runtime for amd64. 
Today, after fixing an incorrectly sized Timespec struct, I was able to build 
the toolchain and use it to regenerate the definition files. After regenerating 
the definitions, I was also able to enable the signal handler.

Most of the Haiku port just worked when compiled for amd64; however, because of 
the different architecture, some changes needed to be made.

First of all, the method to access TLS is a bit different than on 386. On 386, 
the Golang runtime uses the last of the 64 TLS slots (which are indexed 
directly off the FS register) to avoid conflicting with the C library (which 
allocates the smallest slots first). To save one slot, the slot holds a pointer 
to another memory area, which holds the two per-thread structures that Golang 
needs, the G and the M. On amd64, the slots are contained in another memory 
area that one accesses by dereferencing the FS register. Thus, I found it 
easier to take up the last *two* slots and use one for G and one for M.

Secondly, Haiku enables ASLR for all processes, and Golang by default doesn't 
support ASLR, as it uses 32-bit relocations that cannot fit all addresses. 
Elias Naur contributed a patchset for Go 1.2 that added the -largemodel flag 
for ASLR compatibility. This patch is still present but disabled in 1.3; 
however, the NaCL port needed this flag, so I simply looked for references to 
NaCL and added a check for Haiku also to enable large model. In addition, Elias 
Naur also contributed a patch for the assembly files that replaced instructions 
with 64-bit relocation compatiable ones; I applied that patch, and now 
executables were able to start on Haiku.

Finally, the amd64 C compiler doesn't have the dynimport function pointer bug. 
As you may recall, the Golang 386 C compiler and linker had a bug in which, 
when one tries to get the value of a function pointer imported from a shared 
library, one gets the first bytes of the method instead. i.e. foo(libc·write) 
gets compiled to the equivalent of foo(*libc·write). The workaround was to do 
foo(&libc·write) instead. This bug is not present on amd64, so I had to use a 
preprocessor macro to only enable the workaround for 386.

During this port, I understood *how* the Solaris porters worked around a 
problem, but not *why* the workaround was needed in the first place. In the 
Solaris runtime, the pipe() system call is implemented in hand-written 
assembly, and passes the results in registers instead of storing them in a 
caller-provided array. I found that this handwritten function wasn't needed on 
386, but is required on amd64, otherwise one gets an invalid value error from 
the kernel. I'm confused about why this is needed - what's the difference 
between writing to an array on 386 and on amd64?

Also, stylistically, my amd64 port doesn't really follow what I guessed is a Go 
toolchain coding rule, which is to avoid ifdefs when possible. (The stock 
toolchain uses C if statements with a constant condition instead, however, this 
is difficult to use when, for example, a datatype changes between platforms, as 
seen in the Timespec struct) This might be an issue if we decide to contribute 
the port upstream.

On a related note, during this port, I noticed that Haiku x86_64, unlike 
Solaris and possibly other 64-bit Unix-like OSes, has a 32-bit time_t i.e. the 
number of seconds in struct timespec is a 32-bit value. Does this mean that 
64-bit Haiku is also subject to the Year 2038 problem?

I'll post a precompiled package soon: for now, the source is at 
https://bitbucket.org/zhuowei/go-1-3-haiku/commits/branch/porthaiku2 .

Zhuowei Zhang

Other related posts: