Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 29 Jan 2007 13:59:42 +0200
From:      Kostik Belousov <kostikbel@gmail.com>
To:        Jung-uk Kim <jkim@freebsd.org>
Cc:        freebsd-emulation@freebsd.org, freebsd-amd64@freebsd.org
Subject:   Re: load_fs() and load_gs()
Message-ID:  <20070129115942.GA56152@deviant.kiev.zoral.com.ua>
In-Reply-To: <200701261821.12274.jkim@FreeBSD.org>
References:  <200701261821.12274.jkim@FreeBSD.org>

next in thread | previous in thread | raw e-mail | index | archive | help

--gBBFr7Ir9EOA20Yy
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

On Fri, Jan 26, 2007 at 06:21:09PM -0500, Jung-uk Kim wrote:
> I have been chasing TLS problem for Linuxulator/amd64.  The whole=20
> thing actually boils down to the following simulation:
>=20
> ----------------
> #include <stdio.h>
> #include <sys/types.h>
> #include <machine/cpufunc.h>
> #include <machine/sysarch.h>
>=20
> static __thread u_int tls =3D 0xdeadbeef;
>=20
> int
> main(void)
> {
> #if defined(__amd64__)
> 	u_int		fs;
> 	uint64_t	fsbase;
>=20
> 	fs =3D rfs();
> 	if (sysarch(AMD64_GET_FSBASE, &fsbase))
> 		return (-1);
> 	printf("fsbase =3D 0x%lx, %%fs: 0x%08x, tls =3D 0x%x\n",
> 	    fsbase, fs, tls);
>=20
> 	/*
> 	 * glibc does the following two calls.
> 	 * Note: Actually we don't do anything here
> 	 *       but writing them back.
> 	 */
> 	if (sysarch(AMD64_SET_FSBASE, &fsbase))
> 		return (-1);
> 	load_fs(fs);
According to Intel docs,

In 64-bit mode, memory accesses using FS-segment and GS-segment
overrides are not checked for a runtime limit nor subjected to
attribute-checking. Normal segment loads (MOV to Sreg and POP Sreg) into
FS and GS load a standard 32-bit base value in the hidden portion of the
segment descriptor register. The base address bits above the standard 32
bits are cleared to 0 to allow consistency for implementations that use
less than 64 bits.

So, by executing load_fs(fs), you effectively load some low (<=3D 2^32) val=
ue
into fs base (I suspect that it is just 0, since GUDATA_SEL has 0 as segment
base, see gdt_segs in amd64/machdep.c). And then,
	mov    %fs:0x0,%rax
instruction just dereferences 0 instead of TLS.

I suspect that Linux does not use that code sequence too, since behaviour
on the segment register load in 64-bit mode is defined by CPU.

>=20
> 	if (sysarch(AMD64_GET_FSBASE, &fsbase))
> 		return (-1);
> 	printf("fsbase =3D 0x%lx, %%fs: 0x%08x, tls =3D 0x%x\n",
> 	    fsbase, rfs(), tls);
> #elif defined(__i386__)
> 	u_int		gs;
> 	uint32_t	gsbase;
>=20
> 	gs =3D rgs();
> 	if (sysarch(I386_GET_GSBASE, &gsbase))
> 		return (-1);
> 	printf("gsbase =3D 0x%lx, %%gs: 0x%08x, tls =3D 0x%x\n",
> 	    gsbase, gs, tls);
>=20
> 	/*
> 	 * glibc does the following two calls.
> 	 * Note: Actually we don't do anything here
> 	 *       but writing them back.
> 	 */
> 	if (sysarch(I386_SET_GSBASE, &gsbase))
> 		return (-1);
> 	load_gs(gs);
Again, this load segment base hidden register from the segment descriptor
in memory, that is 0. Access to tls would dereference NULL pointer.

In 32-bit mode, the problem is that FreeBSD does not support segmentation
(yet ?).
>=20
> 	if (sysarch(I386_GET_GSBASE, &gsbase))
> 		return (-1);
> 	printf("gsbase =3D 0x%lx, %%gs: 0x%08x, tls =3D 0x%x\n",
> 	    gsbase, rgs(), tls);
> #endif
>=20
> 	return (0);
> }
> ----------------
>=20
> If you run it on amd64 (both amd64 and i386 binaries), it segfaults=20
> at:
>=20
> 	mov	%fs:0x0,%rax	(amd64)
>=20
> or
>=20
> 	mov	%gs:0x0,%eax	(i386)
>=20
> which is basically reading tls.  Why does it segfaults when we just=20
> read and write them back?  Can anyone enlighten me?

In normal situation, when segment registers are not reloaded,
the IA32_FS_BASE and IA32_GS_BASE MSRs define the actual base
used when fs: or gs: segment override is supplied (that is set by
sysarch(XXX_SET_XSBASE) syscalls).

In fact, it seems that kernel uses only gs: for per-cpu data, and completely
ignores fs base. Due to this, IA32_FS_BASE is changed only on thread context
switch.

--gBBFr7Ir9EOA20Yy
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (FreeBSD)

iD8DBQFFveGtC3+MBN1Mb4gRApCgAKDXNPUkS5liGv8wCFiNCALKKbvvmACdEEDF
Iei4lesQuXChJalTg+4M2RM=
=2hyj
-----END PGP SIGNATURE-----

--gBBFr7Ir9EOA20Yy--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20070129115942.GA56152>