xref: /netbsd-src/external/gpl3/gdb/dist/gdbserver/linux-i386-ipa.cc (revision 2be465b09aca4bf6e67814eb0e0f409087138d90)
1 /* GNU/Linux/x86 specific low level interface, for the in-process
2    agent library for GDB.
3 
4    Copyright (C) 2010-2024 Free Software Foundation, Inc.
5 
6    This file is part of GDB.
7 
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
20 
21 #include <sys/mman.h>
22 #include "tracepoint.h"
23 #include "linux-x86-tdesc.h"
24 #include "gdbsupport/x86-xstate.h"
25 
26 /* GDB register numbers.  */
27 
28 enum i386_gdb_regnum
29 {
30   I386_EAX_REGNUM,		/* %eax */
31   I386_ECX_REGNUM,		/* %ecx */
32   I386_EDX_REGNUM,		/* %edx */
33   I386_EBX_REGNUM,		/* %ebx */
34   I386_ESP_REGNUM,		/* %esp */
35   I386_EBP_REGNUM,		/* %ebp */
36   I386_ESI_REGNUM,		/* %esi */
37   I386_EDI_REGNUM,		/* %edi */
38   I386_EIP_REGNUM,		/* %eip */
39   I386_EFLAGS_REGNUM,		/* %eflags */
40   I386_CS_REGNUM,		/* %cs */
41   I386_SS_REGNUM,		/* %ss */
42   I386_DS_REGNUM,		/* %ds */
43   I386_ES_REGNUM,		/* %es */
44   I386_FS_REGNUM,		/* %fs */
45   I386_GS_REGNUM,		/* %gs */
46   I386_ST0_REGNUM		/* %st(0) */
47 };
48 
49 #define i386_num_regs 16
50 
51 #define FT_CR_EAX 15
52 #define FT_CR_ECX 14
53 #define FT_CR_EDX 13
54 #define FT_CR_EBX 12
55 #define FT_CR_UESP 11
56 #define FT_CR_EBP 10
57 #define FT_CR_ESI 9
58 #define FT_CR_EDI 8
59 #define FT_CR_EIP 7
60 #define FT_CR_EFL 6
61 #define FT_CR_DS 5
62 #define FT_CR_ES 4
63 #define FT_CR_FS 3
64 #define FT_CR_GS 2
65 #define FT_CR_SS 1
66 #define FT_CR_CS 0
67 
68 /* Mapping between the general-purpose registers in jump tracepoint
69    format and GDB's register array layout.  */
70 
71 static const int i386_ft_collect_regmap[] =
72 {
73   FT_CR_EAX * 4, FT_CR_ECX * 4, FT_CR_EDX * 4, FT_CR_EBX * 4,
74   FT_CR_UESP * 4, FT_CR_EBP * 4, FT_CR_ESI * 4, FT_CR_EDI * 4,
75   FT_CR_EIP * 4, FT_CR_EFL * 4, FT_CR_CS * 4, FT_CR_SS * 4,
76   FT_CR_DS * 4, FT_CR_ES * 4, FT_CR_FS * 4, FT_CR_GS * 4
77 };
78 
79 void
80 supply_fast_tracepoint_registers (struct regcache *regcache,
81 				  const unsigned char *buf)
82 {
83   int i;
84 
85   for (i = 0; i < i386_num_regs; i++)
86     {
87       int regval;
88 
89       if (i >= I386_CS_REGNUM && i <= I386_GS_REGNUM)
90 	regval = *(short *) (((char *) buf) + i386_ft_collect_regmap[i]);
91       else
92 	regval = *(int *) (((char *) buf) + i386_ft_collect_regmap[i]);
93 
94       supply_register (regcache, i, &regval);
95     }
96 }
97 
98 ULONGEST
99 get_raw_reg (const unsigned char *raw_regs, int regnum)
100 {
101   /* This should maybe be allowed to return an error code, or perhaps
102      better, have the emit_reg detect this, and emit a constant zero,
103      or something.  */
104 
105   if (regnum > i386_num_regs)
106     return 0;
107   else if (regnum >= I386_CS_REGNUM && regnum <= I386_GS_REGNUM)
108     return *(short *) (raw_regs + i386_ft_collect_regmap[regnum]);
109   else
110     return *(int *) (raw_regs + i386_ft_collect_regmap[regnum]);
111 }
112 
113 #ifdef HAVE_UST
114 
115 #include <ust/processor.h>
116 
117 /* "struct registers" is the UST object type holding the registers at
118    the time of the static tracepoint marker call.  This doesn't
119    contain EIP, but we know what it must have been (the marker
120    address).  */
121 
122 #define ST_REGENTRY(REG)			\
123   {						\
124     offsetof (struct registers, REG),		\
125     sizeof (((struct registers *) NULL)->REG)	\
126   }
127 
128 static struct
129 {
130   int offset;
131   int size;
132 } i386_st_collect_regmap[] =
133   {
134     ST_REGENTRY(eax),
135     ST_REGENTRY(ecx),
136     ST_REGENTRY(edx),
137     ST_REGENTRY(ebx),
138     ST_REGENTRY(esp),
139     ST_REGENTRY(ebp),
140     ST_REGENTRY(esi),
141     ST_REGENTRY(edi),
142     { -1, 0 }, /* eip */
143     ST_REGENTRY(eflags),
144     ST_REGENTRY(cs),
145     ST_REGENTRY(ss),
146   };
147 
148 #define i386_NUM_ST_COLLECT_GREGS \
149   (sizeof (i386_st_collect_regmap) / sizeof (i386_st_collect_regmap[0]))
150 
151 void
152 supply_static_tracepoint_registers (struct regcache *regcache,
153 				    const unsigned char *buf,
154 				    CORE_ADDR pc)
155 {
156   int i;
157   unsigned int newpc = pc;
158 
159   supply_register (regcache, I386_EIP_REGNUM, &newpc);
160 
161   for (i = 0; i < i386_NUM_ST_COLLECT_GREGS; i++)
162     if (i386_st_collect_regmap[i].offset != -1)
163       {
164 	switch (i386_st_collect_regmap[i].size)
165 	  {
166 	  case 4:
167 	    supply_register (regcache, i,
168 			     ((char *) buf)
169 			     + i386_st_collect_regmap[i].offset);
170 	    break;
171 	  case 2:
172 	    {
173 	      unsigned long reg
174 		= * (short *) (((char *) buf)
175 			       + i386_st_collect_regmap[i].offset);
176 	      reg &= 0xffff;
177 	      supply_register (regcache, i, &reg);
178 	    }
179 	    break;
180 	  default:
181 	    internal_error ("unhandled register size: %d",
182 			    i386_st_collect_regmap[i].size);
183 	  }
184       }
185 }
186 
187 #endif /* HAVE_UST */
188 
189 
190 /* This is only needed because reg-i386-linux-lib.o references it.  We
191    may use it proper at some point.  */
192 const char *gdbserver_xmltarget;
193 
194 /* Attempt to allocate memory for trampolines in the first 64 KiB of
195    memory to enable smaller jump patches.  */
196 
197 static void
198 initialize_fast_tracepoint_trampoline_buffer (void)
199 {
200   const CORE_ADDR buffer_end = 64 * 1024;
201   /* Ensure that the buffer will be at least 1 KiB in size, which is
202      enough space for over 200 fast tracepoints.  */
203   const int min_buffer_size = 1024;
204   char buf[IPA_BUFSIZ];
205   CORE_ADDR mmap_min_addr = buffer_end + 1;
206   ULONGEST buffer_size;
207   FILE *f = fopen ("/proc/sys/vm/mmap_min_addr", "r");
208 
209   if (!f)
210     {
211       snprintf (buf, sizeof (buf), "mmap_min_addr open failed: %s",
212 		safe_strerror (errno));
213       set_trampoline_buffer_space (0, 0, buf);
214       return;
215     }
216 
217   if (fgets (buf, IPA_BUFSIZ, f))
218     sscanf (buf, "%llu", &mmap_min_addr);
219 
220   fclose (f);
221 
222   buffer_size = buffer_end - mmap_min_addr;
223 
224   if (buffer_size >= min_buffer_size)
225     {
226       if (mmap ((void *) (uintptr_t) mmap_min_addr, buffer_size,
227 		PROT_READ | PROT_EXEC | PROT_WRITE,
228 		MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
229 		-1, 0)
230 	  != MAP_FAILED)
231 	set_trampoline_buffer_space (mmap_min_addr, buffer_end, NULL);
232       else
233 	{
234 	  snprintf (buf, IPA_BUFSIZ, "low-64K-buffer mmap() failed: %s",
235 		    safe_strerror (errno));
236 	  set_trampoline_buffer_space (0, 0, buf);
237 	}
238     }
239   else
240     {
241       snprintf (buf, IPA_BUFSIZ, "mmap_min_addr is %d, must be %d or less",
242 		(int) mmap_min_addr, (int) buffer_end - min_buffer_size);
243       set_trampoline_buffer_space (0, 0, buf);
244     }
245 }
246 
247 /* Map the tdesc index to xcr0 mask.  */
248 static uint64_t idx2mask[X86_TDESC_LAST] = {
249   X86_XSTATE_X87_MASK,
250   X86_XSTATE_SSE_MASK,
251   X86_XSTATE_AVX_MASK,
252   X86_XSTATE_MPX_MASK,
253   X86_XSTATE_AVX_MPX_MASK,
254   X86_XSTATE_AVX_AVX512_MASK,
255   X86_XSTATE_AVX_MPX_AVX512_PKU_MASK,
256 };
257 
258 /* Return target_desc to use for IPA, given the tdesc index passed by
259    gdbserver.  */
260 
261 const struct target_desc *
262 get_ipa_tdesc (int idx)
263 {
264   if (idx >= X86_TDESC_LAST)
265     {
266       internal_error ("unknown ipa tdesc index: %d", idx);
267     }
268   return i386_linux_read_description (idx2mask[idx]);
269 }
270 
271 /* Allocate buffer for the jump pads.  On i386, we can reach an arbitrary
272    address with a jump instruction, so just allocate normally.  */
273 
274 void *
275 alloc_jump_pad_buffer (size_t size)
276 {
277   void *res = mmap (NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC,
278 		    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
279 
280   if (res == MAP_FAILED)
281     return NULL;
282 
283   return res;
284 }
285 
286 void
287 initialize_low_tracepoint (void)
288 {
289   initialize_fast_tracepoint_trampoline_buffer ();
290   for (auto i = 0; i < X86_TDESC_LAST; i++)
291     i386_linux_read_description (idx2mask[i]);
292 }
293