From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id QUE7EXkzFmmxpD4AWB0awg (envelope-from ) for ; Thu, 13 Nov 2025 14:37:29 -0500 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=FieAYH8k; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 422DE1E0B8; Thu, 13 Nov 2025 14:37:29 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 35B9F1E04C for ; Thu, 13 Nov 2025 14:37:26 -0500 (EST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 905DF3858405 for ; Thu, 13 Nov 2025 19:37:25 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 905DF3858405 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=FieAYH8k Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 60A2E3858C24 for ; Thu, 13 Nov 2025 19:35:15 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 60A2E3858C24 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 60A2E3858C24 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1763062515; cv=none; b=UyLtRMPjmHvZc82grTZiaNnYwgMuM1I2XpgOsHyz0NrkeOPq4zR7F3Lu3loCSAouhoPGaDfLvytV0GxspghQ2+5nGcz2xErm703L/t4O1UulToTznA+X1uYXa9fp7GkCGIaNQI6Tv5EYud+sw+W6/895LxFLQJWGRn+dOi12N94= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1763062515; c=relaxed/simple; bh=PFsexK+Q+zZOljFCZGfXZCB7T5kvjK1ZquntQNTy6hc=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=DiyrFpwTJZulqaVE4AXAIWwmV/ROdD5bLTDwc8k0vZTJQhQekodqjVQ+NkY28Hcg+sG8Ly2iptAzG5Ps59xcXqfKjrhZqFGJ8Z+vqebDGdEmpIN1It91vyAo9cMIPB/SMHWi7OsGs1dphWvUZyc2898T4+AT5RAkT9lFa+i6ntM= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 60A2E3858C24 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1763062515; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=38uNe4hhnQaHb1E4CpSIzp1gNMtg+hOHc4IH9mu+n4s=; b=FieAYH8kdo/h/Wv7dLqB/wvvYUOyJN2+7Cbt9eot8Kly550IK3fmfyr4lxsGn980QD/+r7 pggPlerILP6+A7ZRfNJQESM9sAmnW2MKLiIbxJmwHe8k4QOgkJNnC3WEw/Dl6xRoBgtk5/ Jjcxcp9sSg7PX4LiqV8ydCfMnh+A9uk= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-224-FvmDsgw8M6GK69aKtmIoSw-1; Thu, 13 Nov 2025 14:35:13 -0500 X-MC-Unique: FvmDsgw8M6GK69aKtmIoSw-1 X-Mimecast-MFC-AGG-ID: FvmDsgw8M6GK69aKtmIoSw_1763062512 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-4776079ada3so13862085e9.1 for ; Thu, 13 Nov 2025 11:35:13 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763062512; x=1763667312; h=content-transfer-encoding:mime-version:message-id:date:references :in-reply-to:subject:cc:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=r2YrhSrgtGsF1wKYPiUiRPn5bctY8OMrnKszV1T0Kn0=; b=NA55sQ2w+BEDHI/ZQufx9kLLAPlVTqEHSiltMgSNN+5UWQlmHAZgktP96YdjZCdXGW BPyBesDQ2gakC8Rursv7cy/RbikqwDiR5o7VMA+TGtQa7jm91bgETQhD4OkfxcwFWb9u LokShlbxcCCImg7szR1Cy2LrEWYB02Nl/cXZvs2xOq3RkRp+XzcPOdKRrQZOcYYABsWb tjS0B+g0RZVmjgdKBew7TM3S09VS68pwR0R2MJrb98H+ag1xnkmGNoxMYiUPlR35LGM9 phuoB/KMqUeXbsyCUn7C2c1MJHJZ5IohCQt9A6+goXGxjs49cyMtTTrjmol2Kw8IA1tO xTqg== X-Forwarded-Encrypted: i=1; AJvYcCXs+PcfPCxVgvlViNRBop6mDZuEnmD3nw3rbnFT8y3PvYnLq0ryTG3gJB+aFG3dU7Dr8gMF5+pNN+HTaA==@sourceware.org X-Gm-Message-State: AOJu0YzOLIJ5f//NszbmslHzG3eDr+xBbZbNq7qVkSKYtB/Z1gQ7jmgF NP0cTL2CODq899Ih9G9iOmlMVELDnIUDeIVY2h2X4ggX3TVTadZt/iWP/Gw+jV02zT8kYSUvSS8 hPlDQ32lEcN9yxxuTEEEI/7oASEEl8cZktynhGePnYEjVYTgm1VFZnecX7807D14= X-Gm-Gg: ASbGncsupuDQs9VJnkLBmZjt7mfsREP8wWfMRyW14Iafi+yuHHH9EHzqLis2tZeJNqG 7FBXjsYCnon59uHqUfg625kDbcZRPsak5WvqVSq19MnHDhGevkyJvBYTSVxNNYP+2GL28hbNvrJ 5sSaXNzezSm7N9dAVxlMe4SbuTo/yNN+oU2OPRCPnpjGyCNPlqoCpMM1vYQvekao3IrZ6XxTLNx ovxkuj72iQsCGzBHlkfR9OkDVdsjSK/rX5GpAIpThz0Topmu1wV8/9aSWNyCZil2ea/t10AEj+0 2vA/J6aKeL27DLXQgyr4odnXlapVgh9cdilrB8NdqfiEop1RYftrI4+Z5RmmNTsg4zZ1Ww== X-Received: by 2002:a05:600c:c4a1:b0:477:3f35:66d5 with SMTP id 5b1f17b1804b1-4778fe95fedmr5852175e9.26.1763062511530; Thu, 13 Nov 2025 11:35:11 -0800 (PST) X-Google-Smtp-Source: AGHT+IGSw0kIpWpMPSBg6eznjx+6HI/3BYEtLw9D7pS2e2rW/b1I3W54uTu39dACu0Wm+hmXV2XY2Q== X-Received: by 2002:a05:600c:c4a1:b0:477:3f35:66d5 with SMTP id 5b1f17b1804b1-4778fe95fedmr5851935e9.26.1763062510895; Thu, 13 Nov 2025 11:35:10 -0800 (PST) Received: from localhost ([31.111.84.207]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4778c850ca6sm51172865e9.6.2025.11.13.11.35.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 13 Nov 2025 11:35:10 -0800 (PST) From: Andrew Burgess To: "Schimpe, Christina" , "gdb-patches@sourceware.org" Cc: Eli Zaretskii , Keith Seitz Subject: RE: [PATCHv4] gdb: include NT_I386_TLS note in generated core files In-Reply-To: References: <730728a6858a3fcf4477ebe3895088c98f0fead2.1760450001.git.aburgess@redhat.com> Date: Thu, 13 Nov 2025 19:35:08 +0000 Message-ID: <87ldk9zpxv.fsf@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: NyyIas0NjdoDBJ8ISutady4M4539FSp6Q3WzeCTI3oA_1763062512 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org "Schimpe, Christina" writes: > Hi Andrew,=20 > > I only have some optional comments and found a few nits, please see below= . > >> -----Original Message----- >> From: Andrew Burgess >> Sent: Mittwoch, 5. November 2025 14:57 >> To: gdb-patches@sourceware.org >> Cc: Andrew Burgess ; Schimpe, Christina >> ; Eli Zaretskii >> Subject: [PATCHv4] gdb: include NT_I386_TLS note in generated core files >>=20 >> In v4: >>=20 >> - Updated based on Christina's feedback. >>=20 >> - Removed excessive use of 'struct' throughout the patch. >>=20 >> - Fixed whitespace issue in comment. >>=20 >> - Renamed test to remove 'linux-', this is inline with all the other >> Linux specific tests, which don't have 'linux' in their name. >>=20 >> - Changed 'return -1' to 'return' in the test script. >>=20 >> - Updated test to handle case where kernel produced core file >> doesn't work, e.g. system is configured to not allow the kernel to >> dump core. Test will now report 'unsupported'. >>=20 >> - Updated gdbserver changes so fetch_tls_area_register and >> store_tls_area_register now return 'void'. These functions have >> been updated to give a warning if something goes wrong. >>=20 >> - Rebased onto something more recent, reran the patch specific test >> only as the changes above are all minor and shouldn't impact >> anything outside of this change. >>=20 >> In v3: >>=20 >> - Fixed doc issue that Eli pointed out. >>=20 >> - Rebased to current HEAD, no merged conflicts. >>=20 >> In v2: >>=20 >> - Rebased to current HEAD, resolved merge conflicts related to >> recent i386 register changes. >>=20 >> - Updated test to take account of recent clean_restart changes. >>=20 >> - Retested. >>=20 >> --- >>=20 >> This commit extends GDB for x86/Linux to include the NT_I386_TLS note in >> generated core files (i.e. created with `generate-core-file` or `gcore` = command). >> This note contains the 3 per-thread TLS related GDT (global descriptor t= able) >> entries, and is present for i386 binaries, or those compiled on x86-64 w= ith - >> m32. >>=20 >> The approach I have taken to achieve this, is to make the 3 GDT entries >> available within 3 new registers. I added these registers to the >> org.gnu.gdb.i386.linux target description feature, as this feature seeme= d >> perfectly named. As the new registers are optional I don't see any harm= in >> extending this existing feature. I did consider adding a new feature wi= th `tls` in >> the name, but this seemed excessive given the existing feature. >>=20 >> Which GDT entries are used for TLS varies between i386 and x86-64 runnin= g in >> 32-bit mode. As such the registers are named with suffixes 0, 1, and 2,= and it is >> left to GDB or gdbserver, to find the correct GDT entries (based on the = precise >> target) and place the contents into these registers. >>=20 >> With this done, adding the relevant regset is sufficient to get the tls = contents >> emitted as a core file note. Support for emitting the note into the gen= erated >> core file relies on some BFD changes which were made in an earlier commi= t: >>=20 >> commit ea6ec00ff4520895735e4913cb90c933c7296f04 >> Date: Fri Jul 25 19:51:58 2025 +0100 >>=20 >> bfd: support for NT_386_TLS notes >>=20 >> The three new registers are readable and writable. Writing to one of th= e new >> registers will update the relevant kernel GDT entry. >>=20 >> Each TLS GDT is represented by a 'struct user_desc' (see 'man 2 >> get_thread_area' for details), the first 4 bytes of each 'user_desc' >> is the 'entry_number' field, this is the index of the GDT within the ker= nel, and >> cannot be modified. Attempts to write to this region of the register wi= ll be >> ignored, but will not give an error. >>=20 >> I did consider not including this part of the user_desc within the regis= ter value, >> but this becomes difficult when we consider remote targets, GDB would th= en >> need to figure out what these indexes were so that the core file note co= uld be >> generated. Sure, we probably could figure the correct index values out,= but I >> figure, why bother, we can just pass them through in the register and kn= ow for >> certain that we have the correct values. >>=20 >> For testing, there's a new test that covers the basic functionality, inc= luding >> read/write access to the new registers, and checking that the NT_386_TLS= note >> is added to the core file, and that the note contents can be read by GDB= . >>=20 >> I also manually tested opening a core file generated from an old GDB (so= no >> NT_386_TLS notes) using a GDB with this patch. This works fine, the new= tls >> registers are not created as the NT_GDB_TDESC note (the target descripti= on) >> doesn't include the new registers. >>=20 >> Out of interest I also patched an old version of GDB to avoid creating t= he >> NT_GDB_TDESC, and created a core file. This core file contained neither= the >> NT_386_TLS nor NT_GDB_TDESC. When opening this core file with a patched >> GDB, the new registers do show up, but their contents are given as >> , which is exactly what we'd expect, GDB builds a target >> description based on the architecture, the architecture says these regis= ters >> should exist, but they are missing from the core file, hence, . >>=20 >> I also tested using a patched GDB with an old version of gdbserver, the = new >> registers don't show up as the old gdbserver doesn't send them in its ta= rget >> description. And a core file created using the gcore command in such a = setup >> leaves no NT_386_TLS notes added, which is what we'd expect. >>=20 >> And I also tested a new gdbserver running with an old version of GDB. >> As the new tls registers are now mentioned in the target description, th= en >> obviously, the old GDB does see the registers, and present them to the u= ser, >> however GDB doesn't know how to use these registers to create a NT_386_T= LS, >> so that note isn't added to any core files. >> Also, while a new GDB places the tls registers into the 'system' >> group, an old GDB doesn't do this, so the registers end up in the 'gener= al' >> group by default. This means they show up within 'info registers' outpu= t. This >> isn't ideal, but there's not much that can be done about this. >>=20 >> Overall, I feel the combinations of old and new tools has been tested, a= nd the >> behaviours are what we'd want or expect. >>=20 >> I'm tagging this commit with PR gdb/15591, even though this patch isn't >> directly related. That bug is for improving GDB's testing of TLS suppor= t in core >> files. The test in this commit does do some very simple reading of a TL= S >> variable, but there's only two threads, and one TLS variable, so it's no= t >> extensive. Additionally, the test in this commit is x86 only, so this s= hould not be >> considered a full resolution to that bug. But still, it's something. >>=20 >> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=3D15591 >>=20 >> Reviewed-By: Eli Zaretskii >> --- >> gdb/NEWS | 4 + >> gdb/amd64-linux-nat.c | 26 +- >> gdb/doc/gdb.texinfo | 11 +- >> gdb/features/i386/32bit-linux.c | 7 + >> gdb/features/i386/32bit-linux.xml | 5 + >> gdb/i386-linux-nat.c | 21 ++ >> gdb/i386-linux-tdep.c | 174 +++++++++++- >> gdb/i386-linux-tdep.h | 15 + >> gdb/i386-tdep.h | 5 + >> gdb/nat/amd64-linux.h | 29 ++ >> gdb/nat/i386-linux.h | 10 + >> gdb/nat/x86-linux.c | 44 +++ >> gdb/nat/x86-linux.h | 15 + >> gdb/testsuite/gdb.arch/i386-tls-regs.c | 74 +++++ >> gdb/testsuite/gdb.arch/i386-tls-regs.exp | 335 +++++++++++++++++++++++ >> gdb/x86-linux-nat.c | 67 +++++ >> gdb/x86-linux-nat.h | 15 + >> gdbserver/linux-x86-low.cc | 105 +++++++ >> 18 files changed, 955 insertions(+), 7 deletions(-) create mode 100644 >> gdb/nat/amd64-linux.h create mode 100644 gdb/testsuite/gdb.arch/i386-tl= s- >> regs.c >> create mode 100644 gdb/testsuite/gdb.arch/i386-tls-regs.exp >>=20 >> diff --git a/gdb/NEWS b/gdb/NEWS >> index 097d8da350b..9c0ebd98b18 100644 >> --- a/gdb/NEWS >> +++ b/gdb/NEWS >> @@ -162,6 +162,10 @@ qExecAndArgs >> In systems that don't support linker namespaces, or if the inferior h= asn't >> started yet, these always return the integer 0. >>=20 >> +* The 'org.gnu.gdb.i386.linux' target description feature can now >> + contain three additional registers which provide access to the TLS >> + related GDT entries on i386 (and x86-64 when compiling with -m32). >> + >> * Add record full support for rv64gc architectures >>=20 >> * Debugging Linux programs that use AArch64 Guarded Control Stacks is n= ow >> diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index >> 96acfda7d00..53a92f89957 100644 >> --- a/gdb/amd64-linux-nat.c >> +++ b/gdb/amd64-linux-nat.c >> @@ -38,6 +38,9 @@ >> #include "x86-linux-nat.h" >> #include "nat/linux-ptrace.h" >> #include "nat/amd64-linux-siginfo.h" >> +#include "nat/i386-linux.h" >> + >> +#include >>=20 >> /* This definition comes from prctl.h. Kernels older than 2.5.64 >> do not have it. */ >> @@ -89,7 +92,8 @@ static int amd64_linux_gregset32_reg_offset[] =3D >> -1,=09=09=09=09 /* PKEYS register PKRU */ >> -1,=09=09=09=09 /* SSP register. */ >> -1, -1,=09=09=09 /* fs/gs base registers. */ >> - ORIG_RAX * 8=09=09=09 /* "orig_eax" */ >> + ORIG_RAX * 8,=09=09=09 /* "orig_eax" */ >> + -1, -1, -1,=09=09=09 /* TLS GDT regs: i386_tls_gdt_0...2. */ >> }; >>=20 >>=20 >>=20 >>=20 >> @@ -236,7 +240,13 @@ amd64_linux_nat_target::fetch_registers (struct >> regcache *regcache, int regnum) >>=20 >> if (regnum =3D=3D -1 || !amd64_native_gregset_supplies_p (gdbarch, re= gnum)) >> { >> - elf_fpregset_t fpregs; >> + if (regnum =3D=3D -1 || i386_is_tls_regnum_p (regnum)) >> +=09{ >> +=09 if (tdep->i386_linux_tls) >> +=09 i386_fetch_tls_regs (regcache, tid, regnum); >> +=09 if (regnum !=3D -1) >> +=09 return; >> +=09} >>=20 >> if (have_ptrace_getregset =3D=3D TRIBOOL_TRUE) >> =09{ >> @@ -266,6 +276,8 @@ amd64_linux_nat_target::fetch_registers (struct >> regcache *regcache, int regnum) >> =09} >> else >> =09{ >> +=09 elf_fpregset_t fpregs; >> + >> =09 if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0) >> =09 perror_with_name (_("Couldn't get floating point status")); >>=20 >> @@ -308,7 +320,13 @@ amd64_linux_nat_target::store_registers (struct >> regcache *regcache, int regnum) >>=20 >> if (regnum =3D=3D -1 || !amd64_native_gregset_supplies_p (gdbarch, re= gnum)) >> { >> - elf_fpregset_t fpregs; >> + if (regnum =3D=3D -1 || i386_is_tls_regnum_p (regnum)) >> +=09{ >> +=09 if (tdep->i386_linux_tls) >> +=09 i386_store_tls_regs (regcache, tid, regnum); >> +=09 if (regnum !=3D -1) >> +=09 return; >> +=09} >>=20 >> if (have_ptrace_getregset =3D=3D TRIBOOL_TRUE) >> =09{ >> @@ -337,6 +355,8 @@ amd64_linux_nat_target::store_registers (struct >> regcache *regcache, int regnum) >> =09} >> else >> =09{ >> +=09 elf_fpregset_t fpregs; >> + >> =09 if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0) >> =09 perror_with_name (_("Couldn't get floating point status")); >>=20 >> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index >> 821f3ed4b05..b8b70af931a 100644 >> --- a/gdb/doc/gdb.texinfo >> +++ b/gdb/doc/gdb.texinfo >> @@ -50311,8 +50311,15 @@ i386 Features >> @samp{ymm0h} through @samp{ymm15h} for amd64 @end itemize >>=20 >> -The @samp{org.gnu.gdb.i386.linux} feature is optional. It should -desc= ribe a >> single register, @samp{orig_eax}. >> +The @samp{org.gnu.gdb.i386.linux} feature is optional. If the feature >> +is present, then it should describe the 32 bit register, @samp{orig_eax= }. >> + >> +Additionally, the @samp{org.gnu.gdb.i386.linux} feature can optionally >> +contain three 128 bit registers called @samp{i386_tls_gdt_0}, >> +@samp{i386_tls_gdt_1}, and @samp{i386_tls_gdt_2}. Each of these >> +registers contains one 16 byte @samp{struct user_desc} (see @kbd{man >> +2 get_thread_area}) object which describes one of the three TLS related >> +GDT entries. >>=20 >> The @samp{org.gnu.gdb.i386.segments} feature is optional. It should >> describe two system registers: @samp{fs_base} and @samp{gs_base}. >> diff --git a/gdb/features/i386/32bit-linux.c b/gdb/features/i386/32bit-l= inux.c >> index 3289f07d332..6fba8a6880b 100644 >> --- a/gdb/features/i386/32bit-linux.c >> +++ b/gdb/features/i386/32bit-linux.c >> @@ -9,7 +9,14 @@ create_feature_i386_32bit_linux (struct target_desc >> *result, long regnum) >> struct tdesc_feature *feature; >>=20 >> feature =3D tdesc_create_feature (result, "org.gnu.gdb.i386.linux"); >> + tdesc_type *element_type; >> + element_type =3D tdesc_named_type (feature, "uint32"); >> + tdesc_create_vector (feature, "i386_tls_gdt_reg", element_type, 4); >> + >> regnum =3D 41; >> tdesc_create_reg (feature, "orig_eax", regnum++, 1, NULL, 32, "int"); >> + tdesc_create_reg (feature, "i386_tls_gdt_0", regnum++, 1, "system", >> + 128, "i386_tls_gdt_reg"); tdesc_create_reg (feature, >> + "i386_tls_gdt_1", regnum++, 1, "system", 128, "i386_tls_gdt_reg"); >> + tdesc_create_reg (feature, "i386_tls_gdt_2", regnum++, 1, "system", >> + 128, "i386_tls_gdt_reg"); >> return regnum; >> } >> diff --git a/gdb/features/i386/32bit-linux.xml b/gdb/features/i386/32bit= - >> linux.xml >> index 0bfc0bc5563..7de3198b7da 100644 >> --- a/gdb/features/i386/32bit-linux.xml >> +++ b/gdb/features/i386/32bit-linux.xml >> @@ -8,4 +8,9 @@ >> > name=3D"org.gnu.gdb.i386.linux"> >> >> + >> + > + name=3D"i386_tls_gdt_0" bitsize=3D"128" type=3D"i386_tls_gdt_reg" >> + group=3D"system" /> > + type=3D"i386_tls_gdt_reg" group=3D"system" /> > + bitsize=3D"128" type=3D"i386_tls_gdt_reg" group=3D"system" /> >> >> diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c index >> 4af06c448a8..f86005e30e8 100644 >> --- a/gdb/i386-linux-nat.c >> +++ b/gdb/i386-linux-nat.c >> @@ -447,6 +447,8 @@ store_fpxregs (const struct regcache *regcache, int >> tid, int regno) void i386_linux_nat_target::fetch_registers (struct re= gcache >> *regcache, int regno) { >> + gdbarch *gdbarch =3D regcache->arch (); const i386_gdbarch_tdep *tde= p >> + =3D gdbarch_tdep (gdbarch); >> pid_t tid; >>=20 >> /* Use the old method of peeking around in `struct user' if the @@ -4= 70,6 >> +472,9 @@ i386_linux_nat_target::fetch_registers (struct regcache *regca= che, >> int regno) >> zero. */ >> if (regno =3D=3D -1) >> { >> + if (tdep->i386_linux_tls) >> +=09i386_fetch_tls_regs (regcache, tid, regno); >> + >> fetch_regs (regcache, tid); >>=20 >> /* The call above might reset `have_ptrace_getregs'. */ @@ -514,= 6 >> +519,12 @@ i386_linux_nat_target::fetch_registers (struct regcache >> *regcache, int regno) >> return; >> } >>=20 >> + if (tdep->i386_linux_tls && i386_is_tls_regnum_p (regno)) >> + { >> + i386_fetch_tls_regs (regcache, tid, regno); >> + return; >> + } >> + >> internal_error (_("Got request for bad register number %d."), regno);= } >>=20 >> @@ -523,6 +534,8 @@ i386_linux_nat_target::fetch_registers (struct >> regcache *regcache, int regno) void i386_linux_nat_target::store_regis= ters >> (struct regcache *regcache, int regno) { >> + gdbarch *gdbarch =3D regcache->arch (); const i386_gdbarch_tdep *tde= p >> + =3D gdbarch_tdep (gdbarch); >> pid_t tid; >>=20 >> /* Use the old method of poking around in `struct user' if the @@ -54= 5,6 >> +558,8 @@ i386_linux_nat_target::store_registers (struct regcache *regca= che, >> int regno) >> store_fpxregs can fail, and return zero. */ >> if (regno =3D=3D -1) >> { >> + if (tdep->i386_linux_tls) >> +=09i386_store_tls_regs (regcache, tid, regno); >> store_regs (regcache, tid, regno); >> if (store_xstateregs (regcache, tid, regno)) >> =09return; >> @@ -578,6 +593,12 @@ i386_linux_nat_target::store_registers (struct >> regcache *regcache, int regno) >> return; >> } >>=20 >> + if (tdep->i386_linux_tls && i386_is_tls_regnum_p (regno)) >> + { >> + i386_store_tls_regs (regcache, tid, regno); >> + return; >> + } >> + >> internal_error (_("Got request to store bad register number %d."), re= gno); } >>=20 >>=20 >>=20 >> diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c index >> 20adf169c81..1d6abe2f546 100644 >> --- a/gdb/i386-linux-tdep.c >> +++ b/gdb/i386-linux-tdep.c >> @@ -59,7 +59,7 @@ static int >> i386_linux_register_reggroup_p (struct gdbarch *gdbarch, int regnum, >> =09=09=09=09const struct reggroup *group) >> { >> - if (regnum =3D=3D I386_LINUX_ORIG_EAX_REGNUM) >> + if (regnum =3D=3D I386_LINUX_ORIG_EAX_REGNUM || i386_is_tls_regnum_p >> + (regnum)) >> return (group =3D=3D system_reggroup >> =09 || group =3D=3D save_reggroup >> =09 || group =3D=3D restore_reggroup); >> @@ -1047,6 +1047,7 @@ int i386_linux_gregset_reg_offset[] =3D >> -1,=09=09=09=09 /* SSP register. */ >> -1, -1,=09=09=09 /* fs/gs base registers. */ >> 11 * 4,=09=09=09 /* "orig_eax" */ >> + -1, -1, -1,=09=09=09 /* TLS GDT regs: i386_tls_gdt_0...2. */ >> }; >>=20 >> /* Mapping between the general-purpose registers in `struct @@ -1163,15 >> +1164,150 @@ i386_linux_collect_xstateregset (const struct regset *regse= t, >> i387_collect_xsave (regcache, regnum, xstateregs, 1); } >>=20 >> +/* Within a tdep file we don't have access to system headers. This >> + structure is a clone of 'struct user_desc' from 'asm/ldt.h' on x86 >> + GNU/Linux systems. See 'see man 2 get_thread_area' on a suitable x8= 6 >> + machine for more details. */ >> + >> +struct x86_user_desc >> +{ >> + uint32_t entry_number; >> + uint32_t base_addr; >> + uint32_t limit; >> + >> + /* In the actual struct, these flags are a series of 1-bit separate >> + flags. But we don't need that level of insight for the >> + processing we do in GDB, so just make it a single field. */ >> + uint32_t flags; >> +}; >> + >> +/* Supply the 3 tls related registers from BUFFER (length LEN) into >> + REGCACHE. The REGSET and REGNUM are ignored, all three registers ar= e >> + always supplied from BUFFER. */ >> + >> +static void >> +i386_linux_supply_tls_regset (const regset *regset, >> +=09=09=09 regcache *regcache, int regnum, >> +=09=09=09 const void *buffer, size_t len) { >> + gdbarch *gdbarch =3D regcache->arch (); >> + i386_gdbarch_tdep *tdep =3D gdbarch_tdep (gdbarch)= ; >> + >> + if (!tdep->i386_linux_tls) >> + return; >> + >> + gdb_assert (len =3D=3D sizeof (x86_user_desc) * 3); >> + >> + for (int i =3D 0; i < 3; ++i) >> + { >> + int tls_regno =3D I386_LINUX_TLS_GDT_0 + i; >> + >> + gdb_assert (regcache->register_size (tls_regno) >> +=09=09 =3D=3D sizeof (x86_user_desc)); > > You're calling sizeof 4 times here for x86_user_desc. Would it make sense= to store it in a variable? I'll get this fixed. > >> + >> + regcache->raw_supply (tls_regno, buffer); >> + buffer =3D static_cast (buffer) + 1; >> + } >> +} >> + >> +/* Collect the 3 tls related registers from REGCACHE, placing the resul= ts >> + in to BUFFER (length LEN). The REGSET and REGNUM are ignored, all t= hree >> + registers are always collected from REGCACHE. */ >> + >> +static void >> +i386_linux_collect_tls_regset (const regset *regset, >> +=09=09=09 const regcache *regcache, >> +=09=09=09 int regnum, void *buffer, size_t len) { >> + gdbarch *gdbarch =3D regcache->arch (); >> + i386_gdbarch_tdep *tdep =3D gdbarch_tdep (gdbarch)= ; >> + >> + if (!tdep->i386_linux_tls) >> + return; >> + >> + gdb_assert (len =3D=3D sizeof (x86_user_desc) * 3); >> + >> + for (int i =3D 0; i < 3; ++i) >> + { >> + x86_user_desc desc; >> + int tls_regno =3D I386_LINUX_TLS_GDT_0 + i; >> + >> + gdb_assert (regcache->register_size (tls_regno) =3D=3D sizeof >> + (desc)); >> + >> + regcache->raw_collect (tls_regno, &desc); >> + memcpy (buffer, &desc, sizeof (desc)); >> + buffer =3D static_cast (buffer) + 1; >> + } >> +} >> + >> /* Register set definitions. */ >>=20 >> -static const struct regset i386_linux_xstateregset =3D >> +static const regset i386_linux_xstateregset =3D >> { >> NULL, >> i386_linux_supply_xstateregset, >> i386_linux_collect_xstateregset >> }; >>=20 >> +static const regset i386_linux_tls_regset =3D >> + { >> + NULL, >> + i386_linux_supply_tls_regset, >> + i386_linux_collect_tls_regset >> + }; >> + >> +/* Helper for i386_linux_iterate_over_regset_sections. Should we >> + visit the NT_386_TLS note? If REGCACHE is NULL then we are reading >> + the notes from the corefile, so we always visit the note. If >> + REGCACHE is not NULL, in this case we are creating a corefile. In >> + this case, we only visit the note if all the TLS registers are >> + valid, and their base address and limit are not zero, this mirrors >> + the kernel behaviour where the TLS note is elided when the TLS GDT >> + entries have not been set. >> + >> + Only call for architectures where i386_gdbarch_tdep::i386_linux_tls >> + is true. */ >> + >> +static bool >> +should_visit_i386_tls_note (const regcache *regcache) { >> + if (regcache =3D=3D nullptr) >> + return true; >> + >> + /* Check the pre-condition. */ >> + gdbarch *gdbarch =3D regcache->arch (); i386_gdbarch_tdep *tdep =3D >> + gdbarch_tdep (gdbarch); gdb_assert >> + (tdep->i386_linux_tls); >> + >> + for (int i =3D 0; i < 3; ++i) >> + { >> + int tls_regno =3D I386_LINUX_TLS_GDT_0 + i; >> + >> + /* If we failed to read any of the registers then we'll not be >> +=09 able to emit valid note. */ >> + if (regcache->get_register_status (tls_regno) !=3D REG_VALID) >> +=09return false; >> + >> + /* As i386_gdbarch_tdep::i386_linux_tls is true, the registers >> +=09 must be the right size. The flag is only set true when this >> +=09 condition holds. */ >> + gdb_assert (regcache->register_size (tls_regno) >> +=09=09 =3D=3D sizeof (x86_user_desc)); >> + >> + /* Read the TLS GDT entry. If it is in use then we want to >> +=09 write the NT_386_TLS note. */ >> + x86_user_desc ud; >> + regcache->raw_collect (tls_regno, &ud); >> + if (ud.base_addr !=3D 0 && ud.limit !=3D 0) >> +=09return true; >> + } >> + >> + /* Made it through the loop without finding any in-use TLS related >> + GDT entries. No point creating the NT_386_TLS note, the kernel >> + doesn't. */ >> + return false; >> +} >> + >> /* Iterate over core file register note sections. */ >>=20 >> static void >> @@ -1193,6 +1329,9 @@ i386_linux_iterate_over_regset_sections (struct >> gdbarch *gdbarch, >> =09cb_data); >> else >> cb (".reg2", 108, 108, &i386_fpregset, NULL, cb_data); >> + >> + if (tdep->i386_linux_tls && should_visit_i386_tls_note (regcache)) >> + cb (".reg-i386-tls", 48, 48, &i386_linux_tls_regset, nullptr, >> + cb_data); >> } >>=20 >> /* Linux kernel shows PC value after the 'int $0x80' instruction even i= f @@ - >> 1272,6 +1411,37 @@ i386_linux_init_abi (struct gdbarch_info info, struct >> gdbarch *gdbarch) >> if (!valid_p) >> return; >>=20 >> + /* Helper function. Look for TLS_REG_NAME in I386_FEATURE (with the >> + associated LOCAL_TDESC_DATA), and if the register is found assign = it >> + TLS_REGNO. Return true if the register is found, and it is the si= ze >> + of 'struct user_desc' (see man 2 get_thread_area), otherwise, retu= rn >> + false. */ >> + static const auto valid_tls_reg >> + =3D [] (const tdesc_feature *i386_feature, >> +=09 tdesc_arch_data *local_tdesc_data, >> +=09 const char *tls_reg_name, int tls_regno) -> bool >> + { >> + static constexpr int required_reg_size >> + =3D sizeof (x86_user_desc) * HOST_CHAR_BIT; >> + return (tdesc_numbered_register (i386_feature, local_tdesc_data, >> +=09=09=09=09 tls_regno, tls_reg_name) >> +=09 && (tdesc_register_bitsize (i386_feature, tls_reg_name) >> +=09=09=3D=3D required_reg_size)); >> + }; >> + >> + /* Check all the expected tls related registers are found, and are th= e >> + correct size. If they are then mark the tls feature as being acti= ve >> + in TDEP. Otherwise, leave the feature as deactivated. */ >> + valid_p =3D (valid_tls_reg (feature, tdesc_data, "i386_tls_gdt_0", >> +=09=09=09 I386_LINUX_TLS_GDT_0) >> +=09 && valid_tls_reg (feature, tdesc_data, "i386_tls_gdt_1", >> +=09=09=09 I386_LINUX_TLS_GDT_1) >> +=09 && valid_tls_reg (feature, tdesc_data, "i386_tls_gdt_2", >> +=09=09=09 I386_LINUX_TLS_GDT_2)); >> + >> + if (valid_p) >> + tdep->i386_linux_tls =3D true; >> + >> /* Add the %orig_eax register used for syscall restarting. */ >> set_gdbarch_write_pc (gdbarch, i386_linux_write_pc); >>=20 >> diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h index >> 8a1a2447776..e75e53aba95 100644 >> --- a/gdb/i386-linux-tdep.h >> +++ b/gdb/i386-linux-tdep.h >> @@ -39,6 +39,13 @@ enum i386_linux_regnum >> is supposed to restart. */ >> I386_LINUX_ORIG_EAX_REGNUM =3D I386_NUM_REGS, >>=20 >> + /* Register numbers for the three TLS GDT registers. These contain t= he >> + 'struct user_desc' (see 'man 2 get_thread_area') values for the th= ree >> + TLS related Global Descriptor Table entries. */ >> + I386_LINUX_TLS_GDT_0, I386_LINUX_TLS_GDT_1, >> I386_LINUX_TLS_GDT_2, >> + >> /* Total number of registers for GNU/Linux. */ >> I386_LINUX_NUM_REGS >>=20 >> @@ -61,4 +68,12 @@ extern bool i386_linux_core_read_x86_xsave_layout >> (struct gdbarch *gdbarch, >>=20 >> extern int i386_linux_gregset_reg_offset[]; >>=20 >> +/* Return true if REGNUM is one of the 3 tls gdt registers. */ >> + >> +static inline bool >> +i386_is_tls_regnum_p (int regnum) >> +{ >> + return regnum >=3D I386_LINUX_TLS_GDT_0 && regnum <=3D >> +I386_LINUX_TLS_GDT_2; } >> + >> #endif /* GDB_I386_LINUX_TDEP_H */ >> diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h index >> cfe0d7383d6..ad355eb597a 100644 >> --- a/gdb/i386-tdep.h >> +++ b/gdb/i386-tdep.h >> @@ -240,6 +240,11 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base >> struct type *i386_zmm_type =3D nullptr; >> struct type *i387_ext_type =3D nullptr; >>=20 >> + /* If the registers containing the i386 Linux TLS related global >> + descriptor table information are available. This is used to decid= e >> + whether to add the NT_386_TLS note to the core file or not. */ >> + bool i386_linux_tls =3D false; >> + >> /* Process record/replay target. */ >> /* The map for registers because the AMD64's registers order >> in GDB is not same as I386 instructions. */ diff --git a/gdb/nat/= amd64- >> linux.h b/gdb/nat/amd64-linux.h new file mode 100644 index >> 00000000000..1d5bf573829 >> --- /dev/null >> +++ b/gdb/nat/amd64-linux.h >> @@ -0,0 +1,29 @@ >> +/* Native-dependent code for GNU/Linux amd64. >> + >> + Copyright (C) 2025 Free Software Foundation, Inc. >> + >> + This file is part of GDB. >> + >> + This program is free software; you can redistribute it and/or modify >> + it under the terms of the GNU General Public License as published by >> + the Free Software Foundation; either version 3 of the License, or >> + (at your option) any later version. >> + >> + This program is distributed in the hope that it will be useful, >> + but WITHOUT ANY WARRANTY; without even the implied warranty of >> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + GNU General Public License for more details. >> + >> + You should have received a copy of the GNU General Public License >> + along with this program. If not, see >> + . */ >> + >> +#ifndef GDB_NAT_AMD64_LINUX_H >> +#define GDB_NAT_AMD64_LINUX_H >> + >> +/* See nat/i386-linux.h for a full description of this constant. This = is >> + the version used when GDB is compiled for amd64, and is running an >> + executable compiled with -m32. */ >> + >> +static inline constexpr int i386_initial_tls_gdt =3D 12; >> + >> +#endif /* GDB_NAT_AMD64_LINUX_H */ >> diff --git a/gdb/nat/i386-linux.h b/gdb/nat/i386-linux.h index >> d80e0f999bf..b99f0a02418 100644 >> --- a/gdb/nat/i386-linux.h >> +++ b/gdb/nat/i386-linux.h >> @@ -34,4 +34,14 @@ >> variable. */ >> extern tribool have_ptrace_getfpxregs; >>=20 >> +/* This constant defines the first GDT (Global Descriptor Table) entry >> + that the kernel allocates for holding TLS descriptors. There are th= ree >> + entries, starting at this index which can be accessed using the >> + PTRACE_GET_THREAD_AREA and PTRACE_SET_THREAD_AREA ptrace calls. >> This >> + constant is only valid for true i386 kernels. For amd64 kernels >> + running in 32-bit mode (i.e. executables compiled -m32) there is a >> + different constant, see nat/amd64-linux.h. */ >> + >> +static inline constexpr int i386_initial_tls_gdt =3D 6; >> + >> #endif /* GDB_NAT_I386_LINUX_H */ >> diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c index >> 55158268970..8906b7e6104 100644 >> --- a/gdb/nat/x86-linux.c >> +++ b/gdb/nat/x86-linux.c >> @@ -28,6 +28,12 @@ >> #include "nat/gdb_ptrace.h" >> #include >>=20 >> +#ifndef __x86_64__ >> +#include "nat/i386-linux.h" >> +#else >> +#include "nat/amd64-linux.h" >> +#endif >> + >> /* Per-thread arch-specific data we want to keep. */ >>=20 >> struct arch_lwp_info >> @@ -185,3 +191,41 @@ x86_check_ssp_support (const int tid) >>=20 >> return true; >> } >> + >> +/* See nat/x86-linux.h. */ >> + >> +bool >> +i386_ptrace_get_tls_data (int pid, gdb::array_view buffer) { >> + gdb_assert (buffer.size () =3D=3D 3); >> + >> + for (int i =3D 0; i < 3; ++i) >> + { >> + void *addr =3D (void *) (uintptr_t) (i386_initial_tls_gdt + i); >> + void *data =3D buffer.slice (i, 1).data (); >> + >> + if (ptrace (PTRACE_GET_THREAD_AREA, pid, addr, data) < 0) >> +=09return false; >> + } >> + >> + return true; >> +} >> + >> +/* See nat/x86-linux.h. */ >> + >> +bool >> +i386_ptrace_set_tls_data (int pid, gdb::array_view buffer) { >> + gdb_assert (buffer.size () =3D=3D 3); >> + >> + for (int i =3D 0; i < 3; ++i) >> + { >> + void *addr =3D (void *) (uintptr_t) (i386_initial_tls_gdt + i); >> + void *data =3D buffer.slice (i, 1).data (); >> + >> + if (ptrace (PTRACE_SET_THREAD_AREA, pid, addr, data) < 0) >> + return false; >> + } >> + >> + return true; >> +} >> diff --git a/gdb/nat/x86-linux.h b/gdb/nat/x86-linux.h index >> 1783aae05d0..c982a46bf2e 100644 >> --- a/gdb/nat/x86-linux.h >> +++ b/gdb/nat/x86-linux.h >> @@ -21,6 +21,7 @@ >> #define GDB_NAT_X86_LINUX_H >>=20 >> #include "nat/linux-nat.h" >> +#include >>=20 >> /* Set whether our local mirror of LWP's debug registers has been >> changed since the values were last written to the thread. Nonzero @= @ -79,4 >> +80,18 @@ extern x86_linux_arch_size x86_linux_ptrace_get_arch_size (int >> tid); >>=20 >> extern bool x86_check_ssp_support (const int tid); >>=20 >> +/* Get the three TLS related GDT (Global Descriptor Table) entries from >> + the kernel for thread PID, placing the results into BUFFER, an array= of >> + length 3. */ >> + >> +extern bool i386_ptrace_get_tls_data >> + (int pid, gdb::array_view buffer); >> + >> +/* Store the three TLS related GDT (Global Descriptor Table) entries he= ld >> + in BUFFER back into the kernel for thread PID. BUFFER must have a >> + length of three. */ >> + >> +extern bool i386_ptrace_set_tls_data >> + (int pid, gdb::array_view buffer); >> + >> #endif /* GDB_NAT_X86_LINUX_H */ >> diff --git a/gdb/testsuite/gdb.arch/i386-tls-regs.c >> b/gdb/testsuite/gdb.arch/i386-tls-regs.c >> new file mode 100644 >> index 00000000000..420d1566f20 >> --- /dev/null >> +++ b/gdb/testsuite/gdb.arch/i386-tls-regs.c >> @@ -0,0 +1,74 @@ >> +/* This testcase is part of GDB, the GNU debugger. >> + >> + Copyright 2025 Free Software Foundation, Inc. >> + >> + This program is free software; you can redistribute it and/or modify >> + it under the terms of the GNU General Public License as published by >> + the Free Software Foundation; either version 3 of the License, or >> + (at your option) any later version. >> + >> + This program is distributed in the hope that it will be useful, >> + but WITHOUT ANY WARRANTY; without even the implied warranty of >> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + GNU General Public License for more details. >> + >> + You should have received a copy of the GNU General Public License >> + along with this program. If not, see >> + . */ >> + >> +#include >> +#include >> +#include >> + >> +static pthread_barrier_t barrier; >> + >> +static __thread int local_var =3D 0; >> + >> +volatile int wait_for_gdb_p =3D 1; >> + >> +volatile int should_dump_core_p =3D 1; >> + >> +void >> +crash_func (void) >> +{ >> + if (should_dump_core_p) >> + abort (); >> +} >> + >> +void >> +spin_forever (void) >> +{ >> + while (wait_for_gdb_p) >> + sleep (1); >> +} >> + >> +void * >> +thread_func (void *arg) >> +{ >> + local_var =3D *((int*) arg); >> + >> + pthread_barrier_wait (&barrier); >> + >> + spin_forever (); >> +} >> + >> +int >> +main (void) >> +{ >> + pthread_t thr; >> + >> + if (pthread_barrier_init (&barrier, NULL, 2) !=3D 0) >> + abort (); >> + >> + local_var =3D 1; >> + int i =3D 2; >> + if (pthread_create (&thr, NULL, thread_func, &i) !=3D 0) >> + abort (); >> + >> + pthread_barrier_wait (&barrier); >> + >> + crash_func (); >> + >> + spin_forever (); >> + >> + return 0; >> +} >> diff --git a/gdb/testsuite/gdb.arch/i386-tls-regs.exp >> b/gdb/testsuite/gdb.arch/i386-tls-regs.exp >> new file mode 100644 >> index 00000000000..1753f8762c6 >> --- /dev/null >> +++ b/gdb/testsuite/gdb.arch/i386-tls-regs.exp >> @@ -0,0 +1,335 @@ >> +# Copyright 2025 Free Software Foundation, Inc. >> +# >> +# This program is free software; you can redistribute it and/or modify >> +# it under the terms of the GNU General Public License as published by >> +# the Free Software Foundation; either version 3 of the License, or # >> +(at your option) any later version. >> +# >> +# This program is distributed in the hope that it will be useful, # but >> +WITHOUT ANY WARRANTY; without even the implied warranty of # >> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU >> +General Public License for more details. >> +# >> +# You should have received a copy of the GNU General Public License # >> +along with this program. If not, see . >> + >> +# Check that the TLS GDT registers are available for i386 executable. >> + >> +require {is_any_target "i?86-*-linux*" "x86_64-*-linux*"} >> + >> +standard_testfile >> + >> +set options {debug pthreads} >> +if {![istarget "i386-*-*"]} { >> + lappend options "additional_flags=3D-m32" >> +} >> + >> +# Check that we can actually compile a 32-bit executable. >> +# >> +# It is possible that even with the above setting of OPTIONS, we might >> +# still get a 64-bit executable, for example, running on x86-64 with # >> +'--target_board=3Dunix/-m64' will add the -m64 after the above -m32, # >> +meaning we always end up with a 64-bit executable. >> +# >> +# If the following compiles then we are getting a 32-bit executable. >> +if {![gdb_can_simple_compile is_lp64_target { >> +=09int dummy[sizeof (int) =3D=3D 4 >> +=09=09 && sizeof (void *) =3D=3D 4 >> +=09=09 && sizeof (long) =3D=3D 4 ? 1 : -1];} \ >> +=09 object $options]} { >> + unsupported "cannot compile 32-bit executable" >> + return >> +} >> + >> +if { [build_executable "failed to prepare" $testfile $srcfile $options]= } { >> + return >> +} >> + >> +# Start an inferior and check we can read and modify the TLS registers. >> +proc_with_prefix test_tls_reg_availability {} { >> + clean_restart $::testfile >> + >> + if {![runto_main]} { >> +=09return >> + } >> + >> + set tls_reg_vals { "" "" "" } >> + gdb_test_multiple "info registers system" "check for TLS regs" { >> +=09-re "^info registers system\r\n" { >> +=09 exp_continue >> +=09} >> +=09-re "^i386_tls_gdt_(\\d) \\{($::hex), ($::hex), ($::hex), ($::hex)\\= }\r\n" { >> +=09 set idx $expect_out(1,string) >> +=09 set val0 $expect_out(2,string) >> +=09 set val1 $expect_out(3,string) >> +=09 set val2 $expect_out(4,string) >> +=09 set val3 $expect_out(5,string) >> +=09 set val [list $val0 $val1 $val2 $val3] >> +=09 set tls_reg_vals [lreplace $tls_reg_vals $idx $idx $val] >> +=09 exp_continue >> +=09} >> +=09-re "^$::gdb_prompt $" { >> +=09 gdb_assert {[lsearch -exact $tls_reg_vals ""] =3D=3D -1} $gdb_te= st_name >> +=09} >> +=09-re "^\[^\r\n\]+\r\n" { >> +=09 exp_continue >> +=09} >> + } >> + >> + if { [lindex $tls_reg_vals 0] !=3D [lindex $tls_reg_vals 1] } { >> +=09set new_vals [lindex $tls_reg_vals 0] >> +=09set old_vals [lindex $tls_reg_vals 1] >> + >> +=09set val0 [lindex $old_vals 0] >> +=09set val1 [lindex $new_vals 1] >> +=09set val2 [lindex $new_vals 2] >> +=09set val3 [lindex $new_vals 3] >> + >> +=09set new_val_str "{$val0, $val1, $val2, $val3}" >> + >> +=09gdb_test_no_output "set \$i386_tls_gdt_1 =3D $new_val_str" >> +=09set re [string_to_regexp $new_val_str] >> +=09gdb_test "print /x \$i386_tls_gdt_1" " =3D $re" >> + } >> + >> + gdb_test_no_output "set should_dump_core_p=3D0" >> + gdb_test_no_output "set wait_for_gdb_p=3D0" >> + >> + gdb_continue_to_end "" continue true } >> + >> +# Start GDB using global BINFILE, load COREFILE (which must match # >> +BINFILE), and check that the core has two threads, that the TLS # >> +registers are visible in both threads, and that the TLS register # >> +values are different in each thread. >> +proc load_core_and_test_tls_regs { corefile } { >> + clean_restart $::testfile >> + >> + gdb_core_cmd $corefile "load corefile" >> + >> + gdb_test "info threads" \ >> +=09[multi_line \ >> +=09 "\\*\\s+1\\s+Thread \[^\r\n\]+" \ >> +=09 "\\s+2\\s+Thread \[^\r\n\]+"] \ >> +=09"check for two threads" >> + >> + # Record the TLS values in thread 1. >> + set tls_reg_vals_thr_1 { "" "" "" } >> + gdb_test_multiple "info registers system" "check for TLS regs threa= d 1" { >> +=09-re "^info registers system\r\n" { >> +=09 exp_continue >> +=09} >> +=09-re "^i386_tls_gdt_(\\d) (\\{$::hex, $::hex, $::hex, $::hex\\})\r\n"= { >> +=09 set idx $expect_out(1,string) >> +=09 set val $expect_out(2,string) >> +=09 set tls_reg_vals_thr_1 [lreplace $tls_reg_vals_thr_1 $idx $idx $= val] >> +=09 exp_continue >> +=09} >> +=09-re "^$::gdb_prompt $" { >> +=09 gdb_assert {[lsearch -exact $tls_reg_vals_thr_1 ""] =3D=3D -1} >> $gdb_test_name >> +=09} >> +=09-re "^\[^\r\n\]+\r\n" { >> +=09 exp_continue >> +=09} >> + } >> + >> + # Check a TLS variable in thread 1. >> + gdb_test "print local_var" " =3D 1" \ >> +=09"check TLS variable in thread 1" >> + >> + # Switch to thread 2 and confirm the values are different. >> + gdb_test "thread 2" >> + >> + set tls_reg_vals_thr_2 { "" "" "" } >> + gdb_test_multiple "info registers system" "check for TLS regs threa= d 2" { >> +=09-re "^info registers system\r\n" { >> +=09 exp_continue >> +=09} >> +=09-re "^i386_tls_gdt_(\\d) (\\{$::hex, $::hex, $::hex, $::hex\\})\r\n"= { >> +=09 set idx $expect_out(1,string) >> +=09 set val $expect_out(2,string) >> +=09 set tls_reg_vals_thr_2 \ >> +=09=09[lreplace $tls_reg_vals_thr_2 $idx $idx $val] >> +=09 exp_continue >> +=09} >> +=09-re "^$::gdb_prompt $" { >> +=09 gdb_assert {[lsearch -exact $tls_reg_vals_thr_2 ""] =3D=3D -1} \ >> +=09=09$gdb_test_name >> +=09} >> +=09-re "^\[^\r\n\]+\r\n" { >> +=09 exp_continue >> +=09} >> + } >> + >> + # Check a TLS variable in thread 2. >> + gdb_test "print local_var" " =3D 2" \ >> +=09"check TLS variable in thread 2" >> + >> + set all_same [expr [lindex $tls_reg_vals_thr_1 0] =3D=3D [lindex >> $tls_reg_vals_thr_2 0] \ >> +=09=09 && [lindex $tls_reg_vals_thr_1 1] =3D=3D [lindex >> $tls_reg_vals_thr_2 1] \ >> +=09=09 && [lindex $tls_reg_vals_thr_1 2] =3D=3D [lindex >> $tls_reg_vals_thr_2 2]] >> + gdb_assert {!$all_same} \ >> +=09"tls regs are different between threads" >> +} >> + >> +# Generate a core file using the gcore command. Load it into GDB and # >> +check we can still read the TLS registers. >> +proc_with_prefix test_gcore_tls {} { >> + >> + if {![gcore_cmd_available]} { >> +=09unsupported "gcore command not available" >> +=09return >> + } >> + >> + clean_restart $::testfile >> + >> + if {![runto_main]} { >> +=09return >> + } >> + >> + gdb_test_no_output "set should_dump_core_p=3D0" >> + >> + gdb_breakpoint crash_func >> + gdb_continue_to_breakpoint "stop at crash_func" >> + >> + set corefile [standard_output_file ${::testfile}.gcore] >> + if {![gdb_gcore_cmd $corefile "dump core"]} { >> +=09return >> + } >> + >> + set readelf_program [gdb_find_readelf] >> + set res [catch {exec $readelf_program -n $corefile} output] >> + if { $res !=3D 0 } { >> +=09unresolved "unable to run readelf to check for TLS notes" >> +=09return >> + } >> + set lines [split $output \n] >> + set tls_count 0 >> + foreach line $lines { >> +=09if {[regexp "^\\s+LINUX\\s+$::hex\\s+NT_386_TLS" $line]} { >> +=09 incr tls_count >> +=09} >> + } >> + gdb_assert {$tls_count =3D=3D 2} \ >> +=09"expected number of TLS notes" >> + >> + load_core_and_test_tls_regs $corefile } >> + >> +# Generate a core file using the gcore command, but before doing so, # >> +clear all the TLS related GDT entries. When the TLS GDT entries # have >> +a base address and limit of zero the kernel doesn't emit the # >> +NT_386_TLS note, GDB copies this behaviour. >> +proc_with_prefix test_gcore_no_tls {} { >> + >> + if {![gcore_cmd_available]} { >> +=09unsupported "gcore command not available" >> +=09return >> + } >> + >> + clean_restart $::testfile >> + >> + if {![runto_main]} { >> +=09return >> + } >> + >> + gdb_test_no_output "set should_dump_core_p=3D0" >> + >> + gdb_breakpoint crash_func >> + gdb_continue_to_breakpoint "stop at crash_func" >> + >> + # Clear the TLS registers in each thread. >> + foreach_with_prefix thr { 1 2 } { >> +=09gdb_test "thread $thr" ".*" \ >> +=09 "switch thread to clear tls regs" >> +=09gdb_test_no_output "set \$i386_tls_gdt_0 =3D { 0x0, 0x0, 0x0, 0x28 }= " >> +=09gdb_test_no_output "set \$i386_tls_gdt_1 =3D { 0x0, 0x0, 0x0, 0x28 }= " >> +=09gdb_test_no_output "set \$i386_tls_gdt_2 =3D { 0x0, 0x0, 0x0, 0x28 }= " >> + } >> + >> + set corefile [standard_output_file ${::testfile}.gcore] >> + if {![gdb_gcore_cmd $corefile "dump core"]} { >> +=09return >> + } >> + >> + # Look in the core file for NT_386_TLS entries. There should be >> + # none. >> + set readelf_program [gdb_find_readelf] >> + set res [catch {exec $readelf_program -n $corefile} output] >> + if { $res !=3D 0 } { >> +=09unresolved "unable to run readelf to check for TLS notes" >> +=09return >> + } >> + set lines [split $output \n] >> + set tls_count 0 >> + foreach line $lines { >> +=09if {[regexp "^\\s+LINUX\\s+$::hex\\s+NT_386_TLS" $line]} { >> +=09 incr tls_count >> +=09} >> + } >> + gdb_assert {$tls_count =3D=3D 0} \ >> +=09"expected number of TLS notes" >> + >> + # Restart GDB and load the core file. As there are no NT_386_TLS >> + # entries the TLS registers should show as unavailable. >> + clean_restart $::testfile >> + >> + gdb_core_cmd $corefile "load corefile" >> + >> + gdb_test "info threads" \ >> +=09[multi_line \ >> +=09 "\\*\\s+1\\s+Thread \[^\r\n\]+" \ >> +=09 "\\s+2\\s+Thread \[^\r\n\]+"] \ >> +=09"check for two threads" >> + >> + foreach_with_prefix thr { 1 2 } { >> +=09set unavailable_tls_count 0 >> +=09set valid_tls_count 0 >> +=09gdb_test "thread $thr" ".*" \ >> +=09 "switch thread to check TLS register status" >> +=09gdb_test_multiple "info registers system" "check TLS reg values" { >> +=09 -re "^info registers system\r\n" { >> +=09=09exp_continue >> +=09 } >> +=09 -re "^i386_tls_gdt_\\d \\{, , >> , \\}\r\n" { >> +=09=09incr unavailable_tls_count >> +=09=09exp_continue >> +=09 } >> +=09 -re "^i386_tls_gdt_\\d \[^\r\n\]+\r\n" { >> +=09=09incr valid_tls_count >> +=09=09exp_continue >> +=09 } >> + >> +=09 -re "^$::gdb_prompt $" { >> +=09=09gdb_assert {$valid_tls_count =3D=3D 0 && \ >> +=09=09=09=09$unavailable_tls_count =3D=3D 3} \ >> +=09=09 $gdb_test_name >> +=09 } >> +=09 -re "^\[^\r\n\]+\r\n" { >> +=09=09exp_continue >> +=09 } >> +=09} >> + >> +=09# Check a TLS variable in this thread. >> +=09gdb_test "print local_var" " =3D $thr" \ >> +=09 "check TLS variable in thread $thr" >> + } >> +} >> + >> +# Generate a native core file. Load it into GDB and check the TLS # >> +registers can be read. >> +proc_with_prefix test_native_core {} { >> + set corefile [core_find $::binfile] >> + if { $corefile eq "" } { >> +=09unsupported "unable to generate core file" > > Given that this is usually a setup issue, I think UNTESTED would fit bett= er here. > I tried to make that consistent in our testsuite with this patch: > https://sourceware.org/git/?p=3Dbinutils-gdb.git;a=3Dcommit;h=3D857ef95cd= 9eb87a1548f1d4999ce230a454f16ac > Sorry for not being too specific in the first place. I think UNTESTED is the wrong choice. Here's what the DejaGNU manual has to say: UNTESTED A test was not run. This is a placeholder used when there is no real test case yet. So to me, this is for cases where we (GDB developers) either haven't written a test yet, or don't know how to write a test. The bit about "no real test case yet" certainly seems to exclude its use in this case. For UNSUPPORTED the manual says: UNSUPPORTED There is no support for the tested case. This may mean that a conditional feature of an operating system, or of a compiler, is not implemented. DejaGnu also uses this message when a testing environment (often a "bare board" target) lacks basic support for compiling or running the test case. For example, a test for the system subroutine _gethostname_ would never work on a target board running only a boot monitor. Which seems to be a slightly better match for this case, though I'd agree it's not a perfect match. The conditional feature (kernel core file generation) is implemented (we can assume), but is disabled on this system. And I think this makes sense; on a system where kernel core file creation has been disabled, running a test that relies on this feature is not supported. > >> +=09return >> + } >> + >> + load_core_and_test_tls_regs $corefile } >> + >> +# Run the tests. >> +test_tls_reg_availability >> +test_gcore_tls >> +test_gcore_no_tls >> +test_native_core >> diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c index >> 660a906cdb6..58b7b6379ff 100644 >> --- a/gdb/x86-linux-nat.c >> +++ b/gdb/x86-linux-nat.c >> @@ -254,6 +254,73 @@ x86_linux_store_ssp (const regcache *regcache, >> const int tid) >> perror_with_name (_("Failed to write pl3_ssp register")); } >>=20 >> +/* Copy from BUFFER into REGCACHE, supplying the tls gdt entry register= s. >> + Use REGNUM to decide exactly which registers are copied. */ >> + >> +static void >> +i386_supply_tls_regs (regcache *regcache, int regnum, >> +=09=09 gdb::array_view buffer) { > > Nit: I think there is an indent issue here. There is one whitespace too > much in the line above (also for i386_collect_tls_regs below). Thanks for spotting this. I'll get it fixed. > >> + gdb_assert (buffer.size () =3D=3D 3); >> + >> + for (int i =3D 0; i < 3; ++i) >> + { >> + if (regnum =3D=3D -1 || regnum =3D=3D I386_LINUX_TLS_GDT_0 + i) >> +=09regcache->raw_supply (I386_LINUX_TLS_GDT_0 + i, >> +=09=09=09 buffer.slice (i, 1).data ()); >> + } >> +} >> + >> +/* Copy from REGCACHE into BUFFER, collecting the tls gdt entry >> + registers. USE REGNUM to decide which registers are copied. */ > > Nit: USE-> Use Will fix. > >> + >> +static void >> +i386_collect_tls_regs (regcache *regcache, int regnum, >> +=09=09=09gdb::array_view buffer) >> +{ >> + gdb_assert (buffer.size () =3D=3D 3); >> + >> + for (int i =3D 0; i < 3; ++i) >> + { >> + if (regnum =3D=3D -1 || regnum =3D=3D I386_LINUX_TLS_GDT_0 + i) >> +=09regcache->raw_collect (I386_LINUX_TLS_GDT_0 + i, >> +=09=09=09 buffer.slice (i, 1).data ()); >> + } >> +} >> + >> +/* See x86-linux-nat.h. */ > > Nit: one whitespace too much before "*/" Will fix. > >> + >> +void >> +i386_fetch_tls_regs (regcache *regcache, int tid, int regnum) { >> + user_desc tls_ud[3]; >> + if (!i386_ptrace_get_tls_data (tid, tls_ud)) >> + perror_with_name (_("Couldn't get TLS area data")); >> + >> + i386_supply_tls_regs (regcache, regnum, tls_ud); } >> + >> +/* See x86-linux-nat.h. */ >> + >> +void >> +i386_store_tls_regs (regcache *regcache, int tid, int regnum) { >> + /* Read current values in to TLS_UD. */ >> + user_desc tls_ud[3]; >> + if (!i386_ptrace_get_tls_data (tid, tls_ud)) >> + perror_with_name (_("Couldn't get TLS area data")); >> + >> + /* Write new values from regcache into TLS_UD. Overwriting the >> + current values. */ >> + i386_collect_tls_regs (regcache, regnum, tls_ud); >> + >> + /* Write the new values back to the kernel. */ >> + if (!i386_ptrace_set_tls_data (tid, tls_ud)) >> + perror_with_name (_("Couldn't write TLS area data")); } >> + >> + >>=20 >>=20 >> + >> INIT_GDB_FILE (x86_linux_nat) >> { >> /* Initialize the debug register function vectors. */ diff --git a/g= db/x86-linux- >> nat.h b/gdb/x86-linux-nat.h index c4556532226..16a3b35b547 100644 >> --- a/gdb/x86-linux-nat.h >> +++ b/gdb/x86-linux-nat.h >> @@ -25,6 +25,7 @@ >> #include "gdbsupport/x86-xstate.h" >> #include "x86-nat.h" >> #include "nat/x86-linux.h" >> +#include "i386-linux-tdep.h" >>=20 >> struct x86_linux_nat_target : public x86_nat_target = { @@ - >> 103,4 +104,18 @@ extern void x86_linux_fetch_ssp (regcache *regcache, >> const int tid); >>=20 >> extern void x86_linux_store_ssp (const regcache *regcache, const int ti= d); >>=20 >> +/* Fetch the tls related registers for thread TID from the kernel and p= lace >> + them into REGCACHE. If REGNUM is -1 then all 3 tls registers are >> + fetched, otherwise only the register matching REGNUM is fetched. A = tls >> + register number is one for which i386_is_tls_regnum_p returns true. >> +*/ >> + >> +extern void i386_fetch_tls_regs (regcache *regcache, int tid, int >> +regnum); >> + >> +/* Store the tls related registers for thread TID from REGCACHE back in= to >> + the kernel. If REGNUM is -1 then all 3 tls registers are stored, >> + otherwise only the register matching REGNUM is stored. A tls regist= er >> + number is one for which i386_is_tls_regnum_p returns true. */ >> + >> +extern void i386_store_tls_regs (regcache *regcache, int tid, int >> +regnum); >> + >> #endif /* GDB_X86_LINUX_NAT_H */ >> diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc ind= ex >> 2aa85ec3d9c..0fd32a22b42 100644 >> --- a/gdbserver/linux-x86-low.cc >> +++ b/gdbserver/linux-x86-low.cc >> @@ -53,6 +53,8 @@ >> #include "nat/x86-linux-dregs.h" >> #include "nat/x86-linux-tdesc.h" >>=20 >> +#include >> + >> #ifdef __x86_64__ >> static target_desc_up tdesc_amd64_linux_no_xml; #endif @@ -132,6 >> +134,10 @@ public: >>=20 >> int get_ipa_tdesc_idx () override; >>=20 >> + /* Override these to provide access to i386 TLS state. */ void >> + fetch_registers (regcache *regcache, int regno) override; void >> + store_registers (regcache *regcache, int regno) override; >> + >> protected: >>=20 >> void low_arch_setup () override; >> @@ -573,6 +579,105 @@ static struct regset_info x86_regsets[] =3D >> NULL_REGSET >> }; >>=20 >> +/* Fetch TLS area data from the kernel and copy it into REGCACHE. REGN= O >> + indicates which TLS area register is wanted, or -1 for all of them. >> + >> + If anything goes wrong then this function will return without updati= ng >> + REGCACHE. */ >> + >> +static void >> +fetch_tls_area_register (regcache *regcache, int regno) { >> + int tid =3D current_thread->id.lwp (); >> + >> + /* Fetch all the TLS area data from the kernel. */ user_desc >> + tls_ud[3]; if (!i386_ptrace_get_tls_data (tid, tls_ud)) >> + { >> + warning (_("failed to read TLS GDT entries for register read")); >> + return; >> + } >> + >> + /* Now copy the values from TLS_UD back into the register cache. */ >> + int tls_regno[3] =3D { >> + find_regno (regcache->tdesc, "i386_tls_gdt_0"), >> + find_regno (regcache->tdesc, "i386_tls_gdt_1"), >> + find_regno (regcache->tdesc, "i386_tls_gdt_2") }; >> + >> + for (int i =3D 0; i < std::size (tls_regno); ++i) >> + supply_register (regcache, tls_regno[i], &tls_ud[i]); } >> + >> +/* See class declaration above. */ >> + >> +void >> +x86_target::fetch_registers (regcache *regcache, int regno) { >> + linux_process_target::fetch_registers (regcache, regno); >> + >> +#ifdef __x86_64__ >> + if (!is_64bit_tdesc (current_thread)) #endif >> + { >> + fetch_tls_area_register (regcache, regno); >> + } >> +} >> + >> +/* Copy TLS area data from REGCACHE back to the kernel. REGNO indicate= s >> + which TLS area register should be copied, or -1 for all of them. If >> + anything goes wrong then return immediately; some of the register ma= y >> + have been written back to the kernel in this case. */ >> + >> +static void >> +store_tls_area_registers (regcache *regcache, int regno) { >> + int tid =3D current_thread->id.lwp (); >> + >> + /* Read current TLS area data from the kernel into TLS_UD. We then >> + overwrite this with values from REGCACHE, and finally, copy the >> + updated values back to the kernel. */ user_desc tls_ud[3]; if >> + (!i386_ptrace_get_tls_data (tid, tls_ud)) >> + { >> + warning (_("failed to read TLS GDT entries for register store")); >> + return; >> + } >> + >> + int tls_regno[] =3D { >> + find_regno (regcache->tdesc, "i386_tls_gdt_0"), >> + find_regno (regcache->tdesc, "i386_tls_gdt_1"), >> + find_regno (regcache->tdesc, "i386_tls_gdt_2") }; >> + >> + /* Now copy data from REGCACHE over the top of the values written >> + into TLS_UD. */ >> + for (int i =3D 0; i < std::size (tls_regno); ++i) >> + collect_register (regcache, tls_regno[i], &tls_ud[i]); >> + >> + /* And write the contents of TLS_UD back to the kernel. We ignore th= e >> + return value from this call; if anything went wrong then there's >> + nothing we can do about it. */ >> + if (!i386_ptrace_set_tls_data (tid, tls_ud)) >> + warning (_("failed to write updated TLS GDT entries for register >> + store")); >> + >> +} >> + >> +/* See class declaration above. */ >> + >> +void >> +x86_target::store_registers (regcache *regcache, int regno) { >> + linux_process_target::store_registers (regcache, regno); >> + >> +#ifdef __x86_64__ >> + if (!is_64bit_tdesc (current_thread)) #endif >> + { >> + store_tls_area_registers (regcache, regno); >> + } >> +} >> + >> bool >> x86_target::low_supports_breakpoints () { >>=20 >> base-commit: 1ddfd4f3eac858294793801fa52d63d36042b4b3 >> -- >> 2.47.1 >>=20 > > With the comment from Keith and the nits fixed this lgtm. > > Reviewed-By: Christina Schimpe I'll get these fixed and push the patch sometime next week. Thanks, Andrew > > Christina > > > Intel Deutschland GmbH > Registered Address: Dornacher Stra=C3=9Fe 1, 85622 Feldkirchen, Germany > Tel: +49 89 991 430, www.intel.de > Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell > Chairperson of the Supervisory Board: Nicole Lau > Registered Seat: Munich > Commercial Register: Amtsgericht M=C3=BCnchen HRB 186928