From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 9F810384AB5D for ; Wed, 8 May 2024 16:47:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 9F810384AB5D Authentication-Results: sourceware.org; dmarc=pass (p=none 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 9F810384AB5D Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1715186832; cv=none; b=Q1tn+g0UJxLNpguPop63epdtFN41uRCPjtWCXF1eyGWaklRZs1v1pxj7J7CSHFEW8aqLhZQGcOE6xu4yVQ+f/Y3CLI1VqzweAHMZd/nXaPxlS1afCuxpuf+HW27stYdn9ovJulMghmMCTgepD+GvXORNGLMlKdXxx9c2M9/HcRc= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1715186832; c=relaxed/simple; bh=LFPcJBob+a0kPfJchJx7jWScuVR0kE5gkqoq10JXi+c=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=EarFnkqnYuVoV2NV4w8wbi6aPeZA6NFP0FKT/VR5hgWa0tisyC8oDj8MlTKLdhmv/D+Nq82vu0qAHqhUhYJjlhH/qUBZtdXW6lL96LjtIi2nFQNaH2T32rBuJD81aWrCRnva0wsc/9vaAF7fGEedClY20g34k4cRf9hCQCWiHQE= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1715186825; 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=4QMTLf13dCguFA4ZsukvehhhfPVR4wj6J0t3euGgloE=; b=d/T/YF5OyCaKrXqbAA9rOzE4li9OZaBlJzQs5ho6M77l/cyMasot30HYhs9v1VsXtp5JIi VHQJ/4GW0EDdsxE6JM+i9V2lPq8Sm+rIRyquWS0vTJxlFcVylW2nskEvVrTLY8ssy9uB0x vD3yIlZa0Wg8olI9J0+yOhiG0pXXulo= Received: from mail-lj1-f200.google.com (mail-lj1-f200.google.com [209.85.208.200]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-287-gSS6dhqIMJWghBhpNqYNQw-1; Wed, 08 May 2024 12:47:03 -0400 X-MC-Unique: gSS6dhqIMJWghBhpNqYNQw-1 Received: by mail-lj1-f200.google.com with SMTP id 38308e7fff4ca-2e2da49e82aso45629921fa.3 for ; Wed, 08 May 2024 09:47:03 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715186819; x=1715791619; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=4QMTLf13dCguFA4ZsukvehhhfPVR4wj6J0t3euGgloE=; b=SUggSe/ZzR4zIqyW/zoKr31za/jufLrEU10QcKqDSn0KfkNhLZP57SGMYj6hBpBXcw Rn7sG3ORnwSAqo4/UGB82wBLTQbIeeC0L7J03ocIrzEIi0hspJBvkEAyLXmZbNr4ABo6 ZMWpYeUk5cNEsJ/xhbndR3+3J78HZNfhb8iopv+NF0O6umazqUQxt4HNcR/tIKh9V5am s/gjB60C9Zo0AxeLBnGnhre9PEf9S7/X2I457lmLnW7QCqw6fm8LRnoNxoWhVK7kq3Sq 3x3Cm5w1czJIRn8JvQgVYzZbxgEvh3aPzSVzTexLx8kt7dlvf/0SeMoTi2U7J/d9I+YT VkyQ== X-Gm-Message-State: AOJu0Yyeh7ZIhIrTBWSXTVLHIkkAeuu1d+cF9KjBx8PmRGmItT7iUvIN KiHLSMkPIVgymqCsovqulTCJZZ3LQGFaI0TANBC4ccSevGH9HVlYSwi4coIWCNawuFRuKcKTHv0 JjVlag2v1n15aFgTNXVMXmxLlX0Pn9o3pKfcnmaJiDR5SDFKZiZkw/8AUa2vVEOsDvjzCQtfzVg l4uU/ER7Dr+rszNhZIgGQzFxfsEi7eiauc/MN///L5PvQ= X-Received: by 2002:a05:651c:2115:b0:2e3:3b4e:43ee with SMTP id 38308e7fff4ca-2e44738d29fmr25953681fa.19.1715186818031; Wed, 08 May 2024 09:46:58 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFpJPpe4/gDAfPoeVwSoTQze500dChsHWf6G+1pX0JzEk6QcqNthqH43PPPLmJyOGBaEAc6Hw== X-Received: by 2002:a05:651c:2115:b0:2e3:3b4e:43ee with SMTP id 38308e7fff4ca-2e44738d29fmr25953331fa.19.1715186817121; Wed, 08 May 2024 09:46:57 -0700 (PDT) Received: from localhost ([31.111.84.240]) by smtp.gmail.com with ESMTPSA id a7-20020adfed07000000b0034d839bed92sm15724079wro.64.2024.05.08.09.46.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 May 2024 09:46:56 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , felix.willgerodt@intel.com, jhb@FreeBSD.org Subject: [PATCHv6 8/9] gdbserver: update target description creation for x86/linux Date: Wed, 8 May 2024 17:46:44 +0100 Message-Id: <7aca2b7b9ccd5ea782a4be22f8f965678ddad38a.1715185683.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-12.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: This commit is part of a series which aims to share more of the target description creation between GDB and gdbserver for x86/Linux. After some refactoring earlier in this series the shared x86_linux_tdesc_for_tid function was added into nat/x86-linux-tdesc.c. However, this function still relies on amd64_linux_read_description and i386_linux_read_description which are implemented separately for both gdbserver and GDB. Given that at their core, all these functions do is: 1. take an xcr0 value as input, 2. mask out some feature bits, 3. look for a cached pre-generated target description and return it if found, 4. if no cached target description is found then call either amd64_create_target_description or i386_create_target_description to create a new target description, which is then added to the cache. Return the newly created target description. The inner functions amd64_create_target_description and i386_create_target_description are already shared between GDB and gdbserver (in the gdb/arch/ directory), so the only thing that the *_read_description functions really do is add the caching layer, and it feels like this really could be shared. However, we have a small problem. Despite using the same {amd64,i386}_create_target_description functions in both GDB and gdbserver to create the target descriptions, on the gdbserver side we cache target descriptions based on a reduced set of xcr0 feature bits. What this means is that, in theory, different xcr0 values can map to the same cache entry, which could result in the wrong target description being used. However, I'm not sure if this can actually happen in reality. Within gdbserver we already split the target description cache based on i386, amd64, and x32. I suspect within a given gdbserver session we'll only see at most one target description for each of these. The cache conflicting problem is caused by xcr0_to_tdesc_idx, which maps an xcr0 value to a enum x86_linux_tdesc value, and there are only 7 usable values in enum x86_linux_tdesc. In contrast, on the GDB side there are 64, 32, and 16 cache slots for i386, amd64, and x32 respectively. On the GDB side it is much more important to cache things correctly as a single GDB session might connect to multiple different remote targets, each of which might have slightly different x86 architectures. And so, if we want to merge the target description caching between GDB and gdbserver, then we need to first update gdbserver so that it caches in the same way as GDB, that is, it needs to adopt a mechanism that allows for the same number of cache slots of each of i386, amd64, and x32. In this way, when the caching is shared, GDB's behaviour will not change. Unfortunately it's a little more complex than that due to the in process agent (IPA). When the IPA is in use, gdbserver sends a target description index to the IPA, and the IPA uses this to find the correct target description to use, the IPA having first generated every possible target description. Interestingly, there is certainly a bug here which results from only having 7 values in enum x86_linux_tdesc. As multiple possible target descriptions in gdbserver map to the same enum x86_linux_tdesc value, then, when the enum x86_linux_tdesc value is sent to the IPA there is no way for gdbserver to know that the IPA will select the correct target description. This bug will get fixed by this commit. ** START OF AN ASIDE ** Back in the day I suspect this approach of sending a target description index made perfect sense. However since this commit: commit a8806230241d201f808d856eaae4d44088117b0c Date: Thu Dec 7 17:07:01 2017 +0000 Initialize target description early in IPA I think that passing an index was probably a bad idea. We used to pass the index, and then use that index to lookup which target description to instantiate and use, the target description was not generated until the index arrived. However, the above commit fixed an issue where we can't call malloc() within (certain parts of) the IPA (apparently), so instead we now pre-compute _every_ possible target description within the IPA. The index is only used to lookup which of the (many) pre-computed target descriptions to use. It would (I think) have been easier all around if the IPA just self-inspected, figured out its own xcr0 value, and used that to create the one target description that is required. So long as the xcr0 to target description code is shared (at compile time) with gdbserver, then we can be sure that the IPA will derive the same target description as gdbserver, and we would avoid all this index passing business, which has made this commit so very, very painful. I did look at how a process might derive its own xcr0 value, but I don't believe this is actually that simple, so for now I've just doubled down on the index passing approach. While reviewing earlier iterations of this patch there has been discussion about the possibility of removing the IPA from GDB. That would certainly make all of the code touched in this patch much simpler, but I don't really want to do that as part of this series. ** END OF AN ASIDE ** Currently then for x86/linux, gdbserver sends a number between 0 and 7 to the IPA, and the IPA uses this to create a target description. However, I am proposing that gdbserver should now create one of (up to) 64 different target descriptions for i386, so this 0 to 7 index isn't going to be good enough any more (amd64 and x32 have slightly fewer possible target descriptions, but still more than 8, so the problem is the same). For a while I wondered if I was going to have to try and find some backward compatible solution for this mess. But after seeing how lightly the IPA is actually documented, I wonder if it is not the case that there is a tight coupling between a version of gdbserver and a version of the IPA? At least I'm hoping so, and that's what I've assumed in this commit. In this commit I have thrown out the old IPA target description index numbering scheme, and switched to a completely new numbering scheme. Instead of the index that is passed being arbitrary, the index is instead calculated from the set of xcr0 features that are present on the target. Within the IPA we can then reverse this logic to recreate the xcr0 value based on the index, and from the xcr0 value we can choose the correct target description. With the gdbserver to IPA numbering scheme issue resolved I have then update the gdbserver versions of amd64_linux_read_description and i386_linux_read_description so that they cache target descriptions using the same set of xcr0 features as GDB itself. After this gdbserver should now always come up with the same target description as GDB does on any x86/Linux target. This commit does not introduce any new code sharing between GDB and gdbserver as previous commits in this series have done. Instead this commit is all about bringing GDB and gdbserver into alignment functionally so that the next commit(s) can merge the GDB and gdbserver versions of these functions. Notes On The Implementation --------------------------- Previously, within gdbserver, target descriptions were cached within arrays. These arrays were sized based on enum x86_linux_tdesc and xcr0_to_tdesc_idx returned the array (cache) index. Now we need different array lengths for each of i386, amd64, and x32. And the index to use within each array is calculated based on which xcr0 bits are set and valid for a particular target type. I really wanted to avoid having fixed array sizes, or having the set of relevant xcr0 bits encoded in multiple places. The solution I came up with was to create a single data structure which would contain a list of xcr0 bits along with flags to indicate which of the i386, amd64, and x32 targets the bit is relevant for. By making the table constexpr, and adding some constexpr helper functions, it is possible to calculate the sizes for the cache arrays at compile time, as well as the bit masks needed to each target type. During review it was pointed out[1] that possibly the failure to check the SSE and X87 bits for amd64/x32 targets might be an error, however, if this is the case then this is an issue that existed long before this patch. I'd really like to keep this patch focused on reworking the existing code and try to avoid changing how target descriptions are actually created, mostly out of fear that I'll break something. [1] https://inbox.sourceware.org/gdb-patches/MN2PR11MB4566070607318EE7E669A5E28E1B2@MN2PR11MB4566.namprd11.prod.outlook.com Approved-By: John Baldwin --- gdbserver/linux-amd64-ipa.cc | 43 +---- gdbserver/linux-i386-ipa.cc | 23 +-- gdbserver/linux-x86-low.cc | 24 ++- gdbserver/linux-x86-tdesc.cc | 335 ++++++++++++++++++++++++++--------- gdbserver/linux-x86-tdesc.h | 48 ++--- 5 files changed, 306 insertions(+), 167 deletions(-) diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc index df5e6aca081..0c80812cc6f 100644 --- a/gdbserver/linux-amd64-ipa.cc +++ b/gdbserver/linux-amd64-ipa.cc @@ -164,47 +164,21 @@ supply_static_tracepoint_registers (struct regcache *regcache, #endif /* HAVE_UST */ -#if !defined __ILP32__ -/* Map the tdesc index to xcr0 mask. */ -static uint64_t idx2mask[X86_TDESC_LAST] = { - X86_XSTATE_X87_MASK, - X86_XSTATE_SSE_MASK, - X86_XSTATE_AVX_MASK, - X86_XSTATE_MPX_MASK, - X86_XSTATE_AVX_MPX_MASK, - X86_XSTATE_AVX_AVX512_MASK, - X86_XSTATE_AVX_MPX_AVX512_PKU_MASK, -}; -#endif - /* Return target_desc to use for IPA, given the tdesc index passed by gdbserver. */ const struct target_desc * get_ipa_tdesc (int idx) { - if (idx >= X86_TDESC_LAST) - { - internal_error ("unknown ipa tdesc index: %d", idx); - } + uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx); #if defined __ILP32__ - switch (idx) - { - case X86_TDESC_SSE: - return amd64_linux_read_description (X86_XSTATE_SSE_MASK, true); - case X86_TDESC_AVX: - return amd64_linux_read_description (X86_XSTATE_AVX_MASK, true); - case X86_TDESC_AVX_AVX512: - return amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true); - default: - break; - } + bool is_x32 = true; #else - return amd64_linux_read_description (idx2mask[idx], false); + bool is_x32 = false; #endif - internal_error ("unknown ipa tdesc index: %d", idx); + return amd64_linux_read_description (xcr0, is_x32); } /* Allocate buffer for the jump pads. The branch instruction has a @@ -272,11 +246,10 @@ void initialize_low_tracepoint (void) { #if defined __ILP32__ - amd64_linux_read_description (X86_XSTATE_SSE_MASK, true); - amd64_linux_read_description (X86_XSTATE_AVX_MASK, true); - amd64_linux_read_description (X86_XSTATE_AVX_AVX512_MASK, true); + for (auto i = 0; i < x86_linux_x32_tdesc_count (); i++) + amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), true); #else - for (auto i = 0; i < X86_TDESC_LAST; i++) - amd64_linux_read_description (idx2mask[i], false); + for (auto i = 0; i < x86_linux_amd64_tdesc_count (); i++) + amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), false); #endif } diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc index aa346fc9bc3..c1c3152fb04 100644 --- a/gdbserver/linux-i386-ipa.cc +++ b/gdbserver/linux-i386-ipa.cc @@ -245,28 +245,15 @@ initialize_fast_tracepoint_trampoline_buffer (void) } } -/* Map the tdesc index to xcr0 mask. */ -static uint64_t idx2mask[X86_TDESC_LAST] = { - X86_XSTATE_X87_MASK, - X86_XSTATE_SSE_MASK, - X86_XSTATE_AVX_MASK, - X86_XSTATE_MPX_MASK, - X86_XSTATE_AVX_MPX_MASK, - X86_XSTATE_AVX_AVX512_MASK, - X86_XSTATE_AVX_MPX_AVX512_PKU_MASK, -}; - /* Return target_desc to use for IPA, given the tdesc index passed by gdbserver. */ const struct target_desc * get_ipa_tdesc (int idx) { - if (idx >= X86_TDESC_LAST) - { - internal_error ("unknown ipa tdesc index: %d", idx); - } - return i386_linux_read_description (idx2mask[idx]); + uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx); + + return i386_linux_read_description (xcr0); } /* Allocate buffer for the jump pads. On i386, we can reach an arbitrary @@ -288,6 +275,6 @@ void initialize_low_tracepoint (void) { initialize_fast_tracepoint_trampoline_buffer (); - for (auto i = 0; i < X86_TDESC_LAST; i++) - i386_linux_read_description (idx2mask[i]); + for (auto i = 0; i < x86_linux_i386_tdesc_count (); i++) + i386_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i)); } diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc index 5d60f6b6fe1..86dbe165eca 100644 --- a/gdbserver/linux-x86-low.cc +++ b/gdbserver/linux-x86-low.cc @@ -2876,14 +2876,28 @@ x86_target::get_ipa_tdesc_idx () struct regcache *regcache = get_thread_regcache (current_thread, 0); const struct target_desc *tdesc = regcache->tdesc; + if (!use_xml) + { + /* If USE_XML is false then we should be using one of these target + descriptions, see x86_linux_read_description for where we choose + one of these. Both of these descriptions are created from this + fixed xcr0 value X86_XSTATE_SSE_MASK. */ + gdb_assert (tdesc == tdesc_i386_linux_no_xml.get () #ifdef __x86_64__ - return amd64_get_ipa_tdesc_idx (tdesc); -#endif + || tdesc == tdesc_amd64_linux_no_xml.get () +#endif /* __x86_64__ */ + ); + return x86_linux_xcr0_to_tdesc_idx (X86_XSTATE_SSE_MASK); + } - if (tdesc == tdesc_i386_linux_no_xml.get ()) - return X86_TDESC_SSE; + /* The xcr0 value and xsave layout value are cached when the target + description is read. Grab their cache location, and use the cached + value to calculate a tdesc index. */ + std::pair storage + = i387_get_xsave_storage (); + uint64_t xcr0 = *storage.first; - return i386_get_ipa_tdesc_idx (tdesc); + return x86_linux_xcr0_to_tdesc_idx (xcr0); } /* The linux target ops object. */ diff --git a/gdbserver/linux-x86-tdesc.cc b/gdbserver/linux-x86-tdesc.cc index af3735aa895..f5636898d71 100644 --- a/gdbserver/linux-x86-tdesc.cc +++ b/gdbserver/linux-x86-tdesc.cc @@ -28,96 +28,297 @@ #include "x86-tdesc.h" #include "arch/i386-linux-tdesc.h" -/* Return the right x86_linux_tdesc index for a given XCR0. Return - X86_TDESC_LAST if can't find a match. */ +/* A structure used to describe a single xstate feature bit that might, or + might not, be checked for when creating a target description for one of + i386, amd64, or x32. -static enum x86_linux_tdesc -xcr0_to_tdesc_idx (uint64_t xcr0, bool is_x32) + The different CPU/ABI types check for different xstate features when + creating a target description. + + We want to cache target descriptions, and this is currently done in + three separate caches, one each for i386, amd64, and x32. Additionally, + the caching we're discussing here is Linux only, and for Linux, the only + thing that has an impact on target description creation is the xcr0 + value. + + In order to ensure the cache functions correctly we need to filter out + only those xcr0 feature bits that are relevant, we can then cache target + descriptions based on the relevant feature bits. Two xcr0 values might + be different, but have the same relevant feature bits. In this case we + would expect the two xcr0 values to map to the same cache entry. */ + +struct x86_xstate_feature { + /* The xstate feature mask. This is a mask against an xcr0 value. */ + uint64_t feature; + + /* Is this feature checked when creating an i386 target description. */ + bool is_i386; + + /* Is this feature checked when creating an amd64 target description. */ + bool is_amd64; + + /* Is this feature checked when creating an x32 target description. */ + bool is_x32; +}; + +/* A constant table that describes all of the xstate features that are + checked when building a target description for i386, amd64, or x32. + + If in the future, due to simplifications or refactoring, this table ever + ends up with 'true' for every xcr0 feature on every target type, then this + is an indication that this table should probably be removed, and that the + rest of the code in this file can be simplified. */ + +static constexpr x86_xstate_feature x86_linux_all_xstate_features[] = { + /* Feature, i386, amd64, x32. */ + { X86_XSTATE_PKRU, true, true, true }, + { X86_XSTATE_AVX512, true, true, true }, + { X86_XSTATE_AVX, true, true, true }, + { X86_XSTATE_MPX, true, true, false }, + { X86_XSTATE_SSE, true, false, false }, + { X86_XSTATE_X87, true, false, false } +}; + +/* Return a compile time constant which is a mask of all the xstate features + that are checked for when building an i386 target description. */ + +static constexpr uint64_t +x86_linux_i386_xcr0_feature_mask () { - if (xcr0 & X86_XSTATE_PKRU) - { - if (is_x32) - { - /* No x32 MPX and PKU, fall back to avx_avx512. */ - return X86_TDESC_AVX_AVX512; - } - else - return X86_TDESC_AVX_MPX_AVX512_PKU; - } - else if (xcr0 & X86_XSTATE_AVX512) - return X86_TDESC_AVX_AVX512; - else if ((xcr0 & X86_XSTATE_AVX_MPX_MASK) == X86_XSTATE_AVX_MPX_MASK) + uint64_t mask = 0; + + for (const auto &entry : x86_linux_all_xstate_features) + if (entry.is_i386) + mask |= entry.feature; + + return mask; +} + +/* Return a compile time constant which is a mask of all the xstate features + that are checked for when building an amd64 target description. */ + +static constexpr uint64_t +x86_linux_amd64_xcr0_feature_mask () +{ + uint64_t mask = 0; + + for (const auto &entry : x86_linux_all_xstate_features) + if (entry.is_amd64) + mask |= entry.feature; + + return mask; +} + +/* Return a compile time constant which is a mask of all the xstate features + that are checked for when building an x32 target description. */ + +static constexpr uint64_t +x86_linux_x32_xcr0_feature_mask () +{ + uint64_t mask = 0; + + for (const auto &entry : x86_linux_all_xstate_features) + if (entry.is_x32) + mask |= entry.feature; + + return mask; +} + +/* Return a compile time constant which is a count of the number of xstate + features that are checked for when building an i386 target description. */ + +static constexpr int +x86_linux_i386_tdesc_count_1 () +{ + uint64_t count = 0; + + for (const auto &entry : x86_linux_all_xstate_features) + if (entry.is_i386) + ++count; + + gdb_assert (count > 0); + + return (1 << count); +} + +/* Return a compile time constant which is a count of the number of xstate + features that are checked for when building an amd64 target description. */ + +static constexpr int +x86_linux_amd64_tdesc_count_1 () +{ + uint64_t count = 0; + + for (const auto &entry : x86_linux_all_xstate_features) + if (entry.is_amd64) + ++count; + + gdb_assert (count > 0); + + return (1 << count); +} + +/* Return a compile time constant which is a count of the number of xstate + features that are checked for when building an x32 target description. */ + +static constexpr int +x86_linux_x32_tdesc_count_1 () +{ + uint64_t count = 0; + + for (const auto &entry : x86_linux_all_xstate_features) + if (entry.is_x32) + ++count; + + gdb_assert (count > 0); + + return (1 << count); +} + +#ifdef IN_PROCESS_AGENT + +/* See linux-x86-tdesc.h. */ + +int +x86_linux_amd64_tdesc_count () +{ + return x86_linux_amd64_tdesc_count_1 (); +} + +/* See linux-x86-tdesc.h. */ + +int +x86_linux_x32_tdesc_count () +{ + return x86_linux_x32_tdesc_count_1 (); +} + +/* See linux-x86-tdesc.h. */ + +int +x86_linux_i386_tdesc_count () +{ + return x86_linux_i386_tdesc_count_1 (); +} + +#endif /* IN_PROCESS_AGENT */ + +/* Convert an xcr0 value into an integer. The integer will be passed to + the in-process-agent where it will then be passed to + x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value. */ + +int +x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0) +{ + /* The following table shows which features are checked for when creating + the target descriptions (see nat/x86-linux-tdesc.c), the feature order + represents the bit order within the generated index number. + + i386 | x87 sse mpx avx avx512 pkru + amd64 | mpx avx avx512 pkru + i32 | avx avx512 pkru + + The features are ordered so that for each mode (i386, amd64, i32) the + generated index will form a continuous range. */ + + int idx = 0; + + for (int i = 0; i < ARRAY_SIZE (x86_linux_all_xstate_features); ++i) { - if (is_x32) /* No MPX on x32. */ - return X86_TDESC_AVX; - else - return X86_TDESC_AVX_MPX; + if ((xcr0 & x86_linux_all_xstate_features[i].feature) + == x86_linux_all_xstate_features[i].feature) + idx |= (1 << i); } - else if (xcr0 & X86_XSTATE_MPX) + + return idx; +} + +#ifdef IN_PROCESS_AGENT + +/* Convert an index number (as returned from x86_linux_xcr0_to_tdesc_idx) + into an xcr0 value which can then be used to create a target + description. */ + +uint64_t +x86_linux_tdesc_idx_to_xcr0 (int idx) +{ + uint64_t xcr0 = 0; + + for (int i = 0; i < ARRAY_SIZE (x86_linux_all_xstate_features); ++i) { - if (is_x32) /* No MPX on x32. */ - return X86_TDESC_AVX; - else - return X86_TDESC_MPX; + if ((idx & (1 << i)) != 0) + xcr0 |= x86_linux_all_xstate_features[i].feature; } - else if (xcr0 & X86_XSTATE_AVX) - return X86_TDESC_AVX; - else if (xcr0 & X86_XSTATE_SSE) - return X86_TDESC_SSE; - else if (xcr0 & X86_XSTATE_X87) - return X86_TDESC_MMX; - else - return X86_TDESC_LAST; + + return xcr0; } +#endif /* IN_PROCESS_AGENT */ + #if defined __i386__ || !defined IN_PROCESS_AGENT -static struct target_desc *i386_tdescs[X86_TDESC_LAST] = { }; +/* A cache of all possible i386 target descriptions. */ -/* Return the target description according to XCR0. */ +static struct target_desc *i386_tdescs[x86_linux_i386_tdesc_count_1 ()] = { }; + +/* See nat/x86-linux-tdesc.h. */ const struct target_desc * i386_linux_read_description (uint64_t xcr0) { - enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, false); + xcr0 &= x86_linux_i386_xcr0_feature_mask (); + int idx = x86_linux_xcr0_to_tdesc_idx (xcr0); - if (idx == X86_TDESC_LAST) - return NULL; + gdb_assert (idx >= 0 && idx < x86_linux_i386_tdesc_count_1 ()); - struct target_desc **tdesc = &i386_tdescs[idx]; + target_desc **tdesc = &i386_tdescs[idx]; - if (*tdesc == NULL) + if (*tdesc == nullptr) { *tdesc = i386_create_target_description (xcr0, true, false); init_target_desc (*tdesc, i386_expedite_regs); } - return *tdesc;; + return *tdesc; } #endif #ifdef __x86_64__ -static target_desc *amd64_tdescs[X86_TDESC_LAST] = { }; -static target_desc *x32_tdescs[X86_TDESC_LAST] = { }; +/* A cache of all possible amd64 target descriptions. */ + +static target_desc *amd64_tdescs[x86_linux_amd64_tdesc_count_1 ()] = { }; + +/* A cache of all possible x32 target descriptions. */ + +static target_desc *x32_tdescs[x86_linux_x32_tdesc_count_1 ()] = { }; + +/* See nat/x86-linux-tdesc.h. */ const struct target_desc * amd64_linux_read_description (uint64_t xcr0, bool is_x32) { - enum x86_linux_tdesc idx = xcr0_to_tdesc_idx (xcr0, is_x32); + if (is_x32) + xcr0 &= x86_linux_x32_xcr0_feature_mask (); + else + xcr0 &= x86_linux_amd64_xcr0_feature_mask (); + + int idx = x86_linux_xcr0_to_tdesc_idx (xcr0); - if (idx == X86_TDESC_LAST) - return NULL; + if (is_x32) + gdb_assert (idx >= 0 && idx < x86_linux_x32_tdesc_count_1 ()); + else + gdb_assert (idx >= 0 && idx < x86_linux_amd64_tdesc_count_1 ()); - struct target_desc **tdesc = NULL; + target_desc **tdesc = nullptr; if (is_x32) tdesc = &x32_tdescs[idx]; else tdesc = &amd64_tdescs[idx]; - if (*tdesc == NULL) + if (*tdesc == nullptr) { *tdesc = amd64_create_target_description (xcr0, is_x32, true, true); @@ -127,39 +328,3 @@ amd64_linux_read_description (uint64_t xcr0, bool is_x32) } #endif - -#ifndef IN_PROCESS_AGENT - -int -i386_get_ipa_tdesc_idx (const struct target_desc *tdesc) -{ - for (int i = 0; i < X86_TDESC_LAST; i++) - { - if (tdesc == i386_tdescs[i]) - return i; - } - - /* If none tdesc is found, return the one with minimum features. */ - return X86_TDESC_MMX; -} - -#if defined __x86_64__ -int -amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc) -{ - for (int i = 0; i < X86_TDESC_LAST; i++) - { - if (tdesc == amd64_tdescs[i]) - return i; - } - for (int i = 0; i < X86_TDESC_LAST; i++) - { - if (tdesc == x32_tdescs[i]) - return i; - } - - return X86_TDESC_SSE; -} - -#endif -#endif diff --git a/gdbserver/linux-x86-tdesc.h b/gdbserver/linux-x86-tdesc.h index 576aaf5e165..c06af21da48 100644 --- a/gdbserver/linux-x86-tdesc.h +++ b/gdbserver/linux-x86-tdesc.h @@ -21,29 +21,29 @@ #ifndef GDBSERVER_LINUX_X86_TDESC_H #define GDBSERVER_LINUX_X86_TDESC_H -/* Note: since IPA obviously knows what ABI it's running on (i386 vs x86_64 - vs x32), it's sufficient to pass only the register set here. This, - together with the ABI known at IPA compile time, maps to a tdesc. */ - -enum x86_linux_tdesc { - X86_TDESC_MMX = 0, - X86_TDESC_SSE = 1, - X86_TDESC_AVX = 2, - X86_TDESC_MPX = 3, - X86_TDESC_AVX_MPX = 4, - X86_TDESC_AVX_AVX512 = 5, - X86_TDESC_AVX_MPX_AVX512_PKU = 6, - X86_TDESC_LAST = 7, -}; - -#if defined __i386__ || !defined IN_PROCESS_AGENT -int i386_get_ipa_tdesc_idx (const struct target_desc *tdesc); -#endif - -#if defined __x86_64__ && !defined IN_PROCESS_AGENT -int amd64_get_ipa_tdesc_idx (const struct target_desc *tdesc); -#endif - -const struct target_desc *i386_get_ipa_tdesc (int idx); +/* Convert an xcr0 value into an integer. The integer will be passed to + the in-process-agent where it will then be passed to + x86_linux_tdesc_idx_to_xcr0 to get back the xcr0 value. */ + +extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0); + +#ifdef IN_PROCESS_AGENT + +/* Convert an index number (as returned from x86_linux_xcr0_to_tdesc_idx) + into an xcr0 value which can then be used to create a target + description. */ + +extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx); + +/* Within the in-process-agent we need to pre-initialise all of the target + descriptions, to do this we need to know how many target descriptions + there are for each different target type. These functions return the + target description count for the relevant target. */ + +extern int x86_linux_amd64_tdesc_count (); +extern int x86_linux_x32_tdesc_count (); +extern int x86_linux_i386_tdesc_count (); + +#endif /* IN_PROCESS_AGENT */ #endif /* GDBSERVER_LINUX_X86_TDESC_H */ -- 2.25.4