aboutsummaryrefslogtreecommitdiff
path: root/libjava/java/lang/StringBuffer.java
blob: eac1d5ae3ed4412a769e6a1a5a7c1906e673f127 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
/* StringBuffer.java -- Growable strings
   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
   Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath 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 2, or (at your option)
any later version.

GNU Classpath 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 GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */

package java.lang;

import java.io.Serializable;

/**
 * <code>StringBuffer</code> represents a changeable <code>String</code>.
 * It provides the operations required to modify the
 * <code>StringBuffer</code>, including insert, replace, delete, append,
 * and reverse. It is thread-safe; meaning that all modifications to a buffer
 * are in synchronized methods.
 *
 * <p><code>StringBuffer</code>s are variable-length in nature, so even if
 * you initialize them to a certain size, they can still grow larger than
 * that. <em>Capacity</em> indicates the number of characters the
 * <code>StringBuffer</code> can have in it before it has to grow (growing
 * the char array is an expensive operation involving <code>new</code>).
 *
 * <p>Incidentally, compilers often implement the String operator "+"
 * by using a <code>StringBuffer</code> operation:<br>
 * <code>a + b</code><br>
 * is the same as<br>
 * <code>new StringBuffer().append(a).append(b).toString()</code>.
 *
 * <p>Classpath's StringBuffer is capable of sharing memory with Strings for
 * efficiency.  This will help when a StringBuffer is converted to a String
 * and the StringBuffer is not changed after that (quite common when performing
 * string concatenation).
 *
 * @author Paul Fisher
 * @author John Keiser
 * @author Tom Tromey
 * @author Eric Blake (ebb9@email.byu.edu)
 * @see String
 * @since 1.0
 * @status updated to 1.4
 */
public final class StringBuffer implements Serializable, CharSequence
{
  /**
   * Compatible with JDK 1.0+.
   */
  private static final long serialVersionUID = 3388685877147921107L;

  /**
   * Index of next available character (and thus the size of the current
   * string contents).  Note that this has permissions set this way so that
   * String can get the value.
   *
   * @serial the number of characters in the buffer
   */
  int count;

  /**
   * The buffer.  Note that this has permissions set this way so that String
   * can get the value.
   *
   * @serial the buffer
   */
  char[] value;

  /**
   * True if the buffer is shared with another object (StringBuffer or
   * String); this means the buffer must be copied before writing to it again.
   * Note that this has permissions set this way so that String can get the
   * value.
   *
   * @serial whether the buffer is shared
   */
  boolean shared;

  /**
   * The default capacity of a buffer.
   */
  private static final int DEFAULT_CAPACITY = 16;

  /**
   * Create a new StringBuffer with default capacity 16.
   */
  public StringBuffer()
  {
    this(DEFAULT_CAPACITY);
  }

  /**
   * Create an empty <code>StringBuffer</code> with the specified initial
   * capacity.
   *
   * @param capacity the initial capacity
   * @throws NegativeArraySizeException if capacity is negative
   */
  public StringBuffer(int capacity)
  {
    value = new char[capacity];
  }

  /**
   * Create a new <code>StringBuffer</code> with the characters in the
   * specified <code>String</code>. Initial capacity will be the size of the
   * String plus 16.
   *
   * @param str the <code>String</code> to convert
   * @throws NullPointerException if str is null
   */
  public StringBuffer(String str)
  {
    // Unfortunately, because the size is 16 larger, we cannot share.
    count = str.count;
    value = new char[count + DEFAULT_CAPACITY];
    str.getChars(0, count, value, 0);
  }

  /**
   * Get the length of the <code>String</code> this <code>StringBuffer</code>
   * would create. Not to be confused with the <em>capacity</em> of the
   * <code>StringBuffer</code>.
   *
   * @return the length of this <code>StringBuffer</code>
   * @see #capacity()
   * @see #setLength(int)
   */
  public synchronized int length()
  {
    return count;
  }

  /**
   * Get the total number of characters this <code>StringBuffer</code> can
   * support before it must be grown.  Not to be confused with <em>length</em>.
   *
   * @return the capacity of this <code>StringBuffer</code>
   * @see #length()
   * @see #ensureCapacity(int)
   */
  public synchronized int capacity()
  {
    return value.length;
  }

  /**
   * Increase the capacity of this <code>StringBuffer</code>. This will
   * ensure that an expensive growing operation will not occur until
   * <code>minimumCapacity</code> is reached. The buffer is grown to the
   * larger of <code>minimumCapacity</code> and
   * <code>capacity() * 2 + 2</code>, if it is not already large enough.
   *
   * @param minimumCapacity the new capacity
   * @see #capacity()
   */
  public synchronized void ensureCapacity(int minimumCapacity)
  {
    ensureCapacity_unsynchronized(minimumCapacity);
  }

  /**
   * Set the length of this StringBuffer. If the new length is greater than
   * the current length, all the new characters are set to '\0'. If the new
   * length is less than the current length, the first <code>newLength</code>
   * characters of the old array will be preserved, and the remaining
   * characters are truncated.
   *
   * @param newLength the new length
   * @throws IndexOutOfBoundsException if the new length is negative
   *         (while unspecified, this is a StringIndexOutOfBoundsException)
   * @see #length()
   */
  public synchronized void setLength(int newLength)
  {
    if (newLength < 0)
      throw new StringIndexOutOfBoundsException(newLength);

    int valueLength = value.length;

    /* Always call ensureCapacity_unsynchronized in order to preserve
       copy-on-write semantics.  */
    ensureCapacity_unsynchronized(newLength);

    if (newLength < valueLength)
      {
        /* If the StringBuffer's value just grew, then we know that
           value is newly allocated and the region between count and
           newLength is filled with '\0'.  */
	count = newLength;
      }
    else
      {
	/* The StringBuffer's value doesn't need to grow.  However,
	   we should clear out any cruft that may exist.  */
	while (count < newLength)
          value[count++] = '\0';
      }
  }

  /**
   * Get the character at the specified index.
   *
   * @param index the index of the character to get, starting at 0
   * @return the character at the specified index
   * @throws IndexOutOfBoundsException if index is negative or &gt;= length()
   *         (while unspecified, this is a StringIndexOutOfBoundsException)
   */
  public synchronized char charAt(int index)
  {
    if (index < 0 || index >= count)
      throw new StringIndexOutOfBoundsException(index);
    return value[index];
  }

  /**
   * Get the specified array of characters. <code>srcOffset - srcEnd</code>
   * characters will be copied into the array you pass in.
   *
   * @param srcOffset the index to start copying from (inclusive)
   * @param srcEnd the index to stop copying from (exclusive)
   * @param dst the array to copy into
   * @param dstOffset the index to start copying into
   * @throws NullPointerException if dst is null
   * @throws IndexOutOfBoundsException if any source or target indices are
   *         out of range (while unspecified, source problems cause a
   *         StringIndexOutOfBoundsException, and dest problems cause an
   *         ArrayIndexOutOfBoundsException)
   * @see System#arraycopy(Object, int, Object, int, int)
   */
  public synchronized void getChars(int srcOffset, int srcEnd,
                                    char[] dst, int dstOffset)
  {
    if (srcOffset < 0 || srcEnd > count || srcEnd < srcOffset)
      throw new StringIndexOutOfBoundsException();
    System.arraycopy(value, srcOffset, dst, dstOffset, srcEnd - srcOffset);
  }

  /**
   * Set the character at the specified index.
   *
   * @param index the index of the character to set starting at 0
   * @param ch the value to set that character to
   * @throws IndexOutOfBoundsException if index is negative or &gt;= length()
   *         (while unspecified, this is a StringIndexOutOfBoundsException)
   */
  public synchronized void setCharAt(int index, char ch)
  {
    if (index < 0 || index >= count)
      throw new StringIndexOutOfBoundsException(index);
    // Call ensureCapacity to enforce copy-on-write.
    ensureCapacity_unsynchronized(count);
    value[index] = ch;
  }

  /**
   * Append the <code>String</code> value of the argument to this
   * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert
   * to <code>String</code>.
   *
   * @param obj the <code>Object</code> to convert and append
   * @return this <code>StringBuffer</code>
   * @see String#valueOf(Object)
   * @see #append(String)
   */
  public StringBuffer append(Object obj)
  {
    return append(obj == null ? "null" : obj.toString());
  }

  /**
   * Append the <code>String</code> to this <code>StringBuffer</code>. If
   * str is null, the String "null" is appended.
   *
   * @param str the <code>String</code> to append
   * @return this <code>StringBuffer</code>
   */
  public synchronized StringBuffer append(String str)
  {
    if (str == null)
      str = "null";
    int len = str.count;
    ensureCapacity_unsynchronized(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
  }

  /**
   * Append the <code>StringBuffer</code> value of the argument to this
   * <code>StringBuffer</code>. This behaves the same as
   * <code>append((Object) stringBuffer)</code>, except it is more efficient.
   *
   * @param stringBuffer the <code>StringBuffer</code> to convert and append
   * @return this <code>StringBuffer</code>
   * @see #append(Object)
   * @since 1.4
   */
  public synchronized StringBuffer append(StringBuffer stringBuffer)
  {
    if (stringBuffer == null)
      return append("null");
    synchronized (stringBuffer)
      {
        int len = stringBuffer.count;
        ensureCapacity_unsynchronized(count + len);
        System.arraycopy(stringBuffer.value, 0, value, count, len);
        count += len;
      }
    return this;
  }

  /**
   * Append the <code>char</code> array to this <code>StringBuffer</code>.
   * This is similar (but more efficient) than
   * <code>append(new String(data))</code>, except in the case of null.
   *
   * @param data the <code>char[]</code> to append
   * @return this <code>StringBuffer</code>
   * @throws NullPointerException if <code>str</code> is <code>null</code>
   * @see #append(char[], int, int)
   */
  public StringBuffer append(char[] data)
  {
    return append(data, 0, data.length);
  }

  /**
   * Append part of the <code>char</code> array to this
   * <code>StringBuffer</code>. This is similar (but more efficient) than
   * <code>append(new String(data, offset, count))</code>, except in the case
   * of null.
   *
   * @param data the <code>char[]</code> to append
   * @param offset the start location in <code>str</code>
   * @param count the number of characters to get from <code>str</code>
   * @return this <code>StringBuffer</code>
   * @throws NullPointerException if <code>str</code> is <code>null</code>
   * @throws IndexOutOfBoundsException if offset or count is out of range
   *         (while unspecified, this is a StringIndexOutOfBoundsException)
   */
  public synchronized StringBuffer append(char[] data, int offset, int count)
  {
    if (offset < 0 || count < 0 || offset > data.length - count)
      throw new StringIndexOutOfBoundsException();
    ensureCapacity_unsynchronized(this.count + count);
    System.arraycopy(data, offset, value, this.count, count);
    this.count += count;
    return this;
  }

  /**
   * Append the <code>String</code> value of the argument to this
   * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert
   * to <code>String</code>.
   *
   * @param bool the <code>boolean</code> to convert and append
   * @return this <code>StringBuffer</code>
   * @see String#valueOf(boolean)
   */
  public StringBuffer append(boolean bool)
  {
    return append(bool ? "true" : "false");
  }

  /**
   * Append the <code>char</code> to this <code>StringBuffer</code>.
   *
   * @param ch the <code>char</code> to append
   * @return this <code>StringBuffer</code>
   */
  public synchronized StringBuffer append(char ch)
  {
    ensureCapacity_unsynchronized(count + 1);
    value[count++] = ch;
    return this;
  }

  /**
   * Append the <code>String</code> value of the argument to this
   * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert
   * to <code>String</code>.
   *
   * @param inum the <code>int</code> to convert and append
   * @return this <code>StringBuffer</code>
   * @see String#valueOf(int)
   */
  // GCJ LOCAL: this is native for efficiency.
  public native StringBuffer append (int inum);

  /**
   * Append the <code>String</code> value of the argument to this
   * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert
   * to <code>String</code>.
   *
   * @param lnum the <code>long</code> to convert and append
   * @return this <code>StringBuffer</code>
   * @see String#valueOf(long)
   */
  public StringBuffer append(long lnum)
  {
    return append(Long.toString(lnum, 10));
  }

  /**
   * Append the <code>String</code> value of the argument to this
   * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert
   * to <code>String</code>.
   *
   * @param fnum the <code>float</code> to convert and append
   * @return this <code>StringBuffer</code>
   * @see String#valueOf(float)
   */
  public StringBuffer append(float fnum)
  {
    return append(Float.toString(fnum));
  }

  /**
   * Append the <code>String</code> value of the argument to this
   * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert
   * to <code>String</code>.
   *
   * @param dnum the <code>double</code> to convert and append
   * @return this <code>StringBuffer</code>
   * @see String#valueOf(double)
   */
  public StringBuffer append(double dnum)
  {
    return append(Double.toString(dnum));
  }

  /**
   * Delete characters from this <code>StringBuffer</code>.
   * <code>delete(10, 12)</code> will delete 10 and 11, but not 12. It is
   * harmless for end to be larger than length().
   *
   * @param start the first character to delete
   * @param end the index after the last character to delete
   * @return this <code>StringBuffer</code>
   * @throws StringIndexOutOfBoundsException if start or end are out of bounds
   * @since 1.2
   */
  public synchronized StringBuffer delete(int start, int end)
  {
    if (start < 0 || start > count || start > end)
      throw new StringIndexOutOfBoundsException(start);
    if (end > count)
      end = count;
    // This will unshare if required.
    ensureCapacity_unsynchronized(count);
    if (count - end != 0)
      System.arraycopy(value, end, value, start, count - end);
    count -= end - start;
    return this;
  }

  /**
   * Delete a character from this <code>StringBuffer</code>.
   *
   * @param index the index of the character to delete
   * @return this <code>StringBuffer</code>
   * @throws StringIndexOutOfBoundsException if index is out of bounds
   * @since 1.2
   */
  public StringBuffer deleteCharAt(int index)
  {
    return delete(index, index + 1);
  }

  /**
   * Replace characters between index <code>start</code> (inclusive) and
   * <code>end</code> (exclusive) with <code>str</code>. If <code>end</code>
   * is larger than the size of this StringBuffer, all characters after
   * <code>start</code> are replaced.
   *
   * @param start the beginning index of characters to delete (inclusive)
   * @param end the ending index of characters to delete (exclusive)
   * @param str the new <code>String</code> to insert
   * @return this <code>StringBuffer</code>
   * @throws StringIndexOutOfBoundsException if start or end are out of bounds
   * @throws NullPointerException if str is null
   * @since 1.2
   */
  public synchronized StringBuffer replace(int start, int end, String str)
  {
    if (start < 0 || start > count || start > end)
      throw new StringIndexOutOfBoundsException(start);

    int len = str.count;
    // Calculate the difference in 'count' after the replace.
    int delta = len - (end > count ? count : end) + start;
    ensureCapacity_unsynchronized(count + delta);

    if (delta != 0 && end < count)
      System.arraycopy(value, end, value, end + delta, count - end);

    str.getChars(0, len, value, start);
    count += delta;
    return this;
  }

  /**
   * Creates a substring of this StringBuffer, starting at a specified index
   * and ending at the end of this StringBuffer.
   *
   * @param beginIndex index to start substring (base 0)
   * @return new String which is a substring of this StringBuffer
   * @throws StringIndexOutOfBoundsException if beginIndex is out of bounds
   * @see #substring(int, int)
   * @since 1.2
   */
  public String substring(int beginIndex)
  {
    return substring(beginIndex, count);
  }

  /**
   * Creates a substring of this StringBuffer, starting at a specified index
   * and ending at one character before a specified index. This is implemented
   * the same as <code>substring(beginIndex, endIndex)</code>, to satisfy
   * the CharSequence interface.
   *
   * @param beginIndex index to start at (inclusive, base 0)
   * @param endIndex index to end at (exclusive)
   * @return new String which is a substring of this StringBuffer
   * @throws IndexOutOfBoundsException if beginIndex or endIndex is out of
   *         bounds
   * @see #substring(int, int)
   * @since 1.4
   */
  public CharSequence subSequence(int beginIndex, int endIndex)
  {
    return substring(beginIndex, endIndex);
  }

  /**
   * Creates a substring of this StringBuffer, starting at a specified index
   * and ending at one character before a specified index.
   *
   * @param beginIndex index to start at (inclusive, base 0)
   * @param endIndex index to end at (exclusive)
   * @return new String which is a substring of this StringBuffer
   * @throws StringIndexOutOfBoundsException if beginIndex or endIndex is out
   *         of bounds
   * @since 1.2
   */
  public synchronized String substring(int beginIndex, int endIndex)
  {
    int len = endIndex - beginIndex;
    if (beginIndex < 0 || endIndex > count || endIndex < beginIndex)
      throw new StringIndexOutOfBoundsException();
    if (len == 0)
      return "";
    // Don't copy unless substring is smaller than 1/4 of the buffer.
    boolean share_buffer = ((len << 2) >= value.length);
    if (share_buffer)
      this.shared = true;
    // Package constructor avoids an array copy.
    return new String(value, beginIndex, len, share_buffer);
  }

  /**
   * Insert a subarray of the <code>char[]</code> argument into this
   * <code>StringBuffer</code>.
   *
   * @param offset the place to insert in this buffer
   * @param str the <code>char[]</code> to insert
   * @param str_offset the index in <code>str</code> to start inserting from
   * @param len the number of characters to insert
   * @return this <code>StringBuffer</code>
   * @throws NullPointerException if <code>str</code> is <code>null</code>
   * @throws StringIndexOutOfBoundsException if any index is out of bounds
   * @since 1.2
   */
  public synchronized StringBuffer insert(int offset,
                                          char[] str, int str_offset, int len)
  {
    if (offset < 0 || offset > count || len < 0
        || str_offset < 0 || str_offset > str.length - len)
      throw new StringIndexOutOfBoundsException();
    ensureCapacity_unsynchronized(count + len);
    System.arraycopy(value, offset, value, offset + len, count - offset);
    System.arraycopy(str, str_offset, value, offset, len);
    count += len;
    return this;
  }

  /**
   * Insert the <code>String</code> value of the argument into this
   * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert
   * to <code>String</code>.
   *
   * @param offset the place to insert in this buffer
   * @param obj the <code>Object</code> to convert and insert
   * @return this <code>StringBuffer</code>
   * @exception StringIndexOutOfBoundsException if offset is out of bounds
   * @see String#valueOf(Object)
   */
  public StringBuffer insert(int offset, Object obj)
  {
    return insert(offset, obj == null ? "null" : obj.toString());
  }

  /**
   * Insert the <code>String</code> argument into this
   * <code>StringBuffer</code>. If str is null, the String "null" is used
   * instead.
   *
   * @param offset the place to insert in this buffer
   * @param str the <code>String</code> to insert
   * @return this <code>StringBuffer</code>
   * @throws StringIndexOutOfBoundsException if offset is out of bounds
   */
  public synchronized StringBuffer insert(int offset, String str)
  {
    if (offset < 0 || offset > count)
      throw new StringIndexOutOfBoundsException(offset);
    if (str == null)
      str = "null";
    int len = str.count;
    ensureCapacity_unsynchronized(count + len);
    System.arraycopy(value, offset, value, offset + len, count - offset);
    str.getChars(0, len, value, offset);
    count += len;
    return this;
  }

  /**
   * Insert the <code>char[]</code> argument into this
   * <code>StringBuffer</code>.
   *
   * @param offset the place to insert in this buffer
   * @param data the <code>char[]</code> to insert
   * @return this <code>StringBuffer</code>
   * @throws NullPointerException if <code>data</code> is <code>null</code>
   * @throws StringIndexOutOfBoundsException if offset is out of bounds
   * @see #insert(int, char[], int, int)
   */
  public StringBuffer insert(int offset, char[] data)
  {
    return insert(offset, data, 0, data.length);
  }

  /**
   * Insert the <code>String</code> value of the argument into this
   * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert
   * to <code>String</code>.
   *
   * @param offset the place to insert in this buffer
   * @param bool the <code>boolean</code> to convert and insert
   * @return this <code>StringBuffer</code>
   * @throws StringIndexOutOfBoundsException if offset is out of bounds
   * @see String#valueOf(boolean)
   */
  public StringBuffer insert(int offset, boolean bool)
  {
    return insert(offset, bool ? "true" : "false");
  }

  /**
   * Insert the <code>char</code> argument into this <code>StringBuffer</code>.
   *
   * @param offset the place to insert in this buffer
   * @param ch the <code>char</code> to insert
   * @return this <code>StringBuffer</code>
   * @throws StringIndexOutOfBoundsException if offset is out of bounds
   */
  public synchronized StringBuffer insert(int offset, char ch)
  {
    if (offset < 0 || offset > count)
      throw new StringIndexOutOfBoundsException(offset);
    ensureCapacity_unsynchronized(count + 1);
    System.arraycopy(value, offset, value, offset + 1, count - offset);
    value[offset] = ch;
    count++;
    return this;
  }

  /**
   * Insert the <code>String</code> value of the argument into this
   * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert
   * to <code>String</code>.
   *
   * @param offset the place to insert in this buffer
   * @param inum the <code>int</code> to convert and insert
   * @return this <code>StringBuffer</code>
   * @throws StringIndexOutOfBoundsException if offset is out of bounds
   * @see String#valueOf(int)
   */
  public StringBuffer insert(int offset, int inum)
  {
    return insert(offset, String.valueOf(inum));
  }

  /**
   * Insert the <code>String</code> value of the argument into this
   * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert
   * to <code>String</code>.
   *
   * @param offset the place to insert in this buffer
   * @param lnum the <code>long</code> to convert and insert
   * @return this <code>StringBuffer</code>
   * @throws StringIndexOutOfBoundsException if offset is out of bounds
   * @see String#valueOf(long)
   */
  public StringBuffer insert(int offset, long lnum)
  {
    return insert(offset, Long.toString(lnum, 10));
  }

  /**
   * Insert the <code>String</code> value of the argument into this
   * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert
   * to <code>String</code>.
   *
   * @param offset the place to insert in this buffer
   * @param fnum the <code>float</code> to convert and insert
   * @return this <code>StringBuffer</code>
   * @throws StringIndexOutOfBoundsException if offset is out of bounds
   * @see String#valueOf(float)
   */
  public StringBuffer insert(int offset, float fnum)
  {
    return insert(offset, Float.toString(fnum));
  }

  /**
   * Insert the <code>String</code> value of the argument into this
   * <code>StringBuffer</code>. Uses <code>String.valueOf()</code> to convert
   * to <code>String</code>.
   *
   * @param offset the place to insert in this buffer
   * @param dnum the <code>double</code> to convert and insert
   * @return this <code>StringBuffer</code>
   * @throws StringIndexOutOfBoundsException if offset is out of bounds
   * @see String#valueOf(double)
   */
  public StringBuffer insert(int offset, double dnum)
  {
    return insert(offset, Double.toString(dnum));
  }

  /**
   * Finds the first instance of a substring in this StringBuffer.
   *
   * @param str String to find
   * @return location (base 0) of the String, or -1 if not found
   * @throws NullPointerException if str is null
   * @see #indexOf(String, int)
   * @since 1.4
   */
  public int indexOf(String str)
  {
    return indexOf(str, 0);
  }

  /**
   * Finds the first instance of a String in this StringBuffer, starting at
   * a given index.  If starting index is less than 0, the search starts at
   * the beginning of this String.  If the starting index is greater than the
   * length of this String, or the substring is not found, -1 is returned.
   *
   * @param str String to find
   * @param fromIndex index to start the search
   * @return location (base 0) of the String, or -1 if not found
   * @throws NullPointerException if str is null
   * @since 1.4
   */
  public synchronized int indexOf(String str, int fromIndex)
  {
    if (fromIndex < 0)
      fromIndex = 0;
    int limit = count - str.count;
    for ( ; fromIndex <= limit; fromIndex++)
      if (regionMatches(fromIndex, str))
        return fromIndex;
    return -1;
  }

  /**
   * Finds the last instance of a substring in this StringBuffer.
   *
   * @param str String to find
   * @return location (base 0) of the String, or -1 if not found
   * @throws NullPointerException if str is null
   * @see #lastIndexOf(String, int)
   * @since 1.4
   */
  public int lastIndexOf(String str)
  {
    return lastIndexOf(str, count - str.count);
  }

  /**
   * Finds the last instance of a String in this StringBuffer, starting at a
   * given index.  If starting index is greater than the maximum valid index,
   * then the search begins at the end of this String.  If the starting index
   * is less than zero, or the substring is not found, -1 is returned.
   *
   * @param str String to find
   * @param fromIndex index to start the search
   * @return location (base 0) of the String, or -1 if not found
   * @throws NullPointerException if str is null
   * @since 1.4
   */
  public synchronized int lastIndexOf(String str, int fromIndex)
  {
    fromIndex = Math.min(fromIndex, count - str.count);
    for ( ; fromIndex >= 0; fromIndex--)
      if (regionMatches(fromIndex, str))
        return fromIndex;
    return -1;
  }

  /**
   * Reverse the characters in this StringBuffer. The same sequence of
   * characters exists, but in the reverse index ordering.
   *
   * @return this <code>StringBuffer</code>
   */
  public synchronized StringBuffer reverse()
  {
    // Call ensureCapacity to enforce copy-on-write.
    ensureCapacity_unsynchronized(count);
    for (int i = count >> 1, j = count - i; --i >= 0; ++j)
      {
        char c = value[i];
        value[i] = value[j];
        value[j] = c;
      }
    return this;
  }

  /**
   * Convert this <code>StringBuffer</code> to a <code>String</code>. The
   * String is composed of the characters currently in this StringBuffer. Note
   * that the result is a copy, and that future modifications to this buffer
   * do not affect the String.
   *
   * @return the characters in this StringBuffer
   */
  public String toString()
  {
    // The string will set this.shared = true.
    return new String(this);
  }

  /**
   * An unsynchronized version of ensureCapacity, used internally to avoid
   * the cost of a second lock on the same object. This also has the side
   * effect of duplicating the array, if it was shared (to form copy-on-write
   * semantics).
   *
   * @param minimumCapacity the minimum capacity
   * @see #ensureCapacity(int)
   */
  private void ensureCapacity_unsynchronized(int minimumCapacity)
  {
    if (shared || minimumCapacity > value.length)
      {
        // We don't want to make a larger vector when `shared' is
        // set.  If we do, then setLength becomes very inefficient
        // when repeatedly reusing a StringBuffer in a loop.
        int max = (minimumCapacity > value.length
                   ? value.length * 2 + 2
                   : value.length);
        minimumCapacity = (minimumCapacity < max ? max : minimumCapacity);
        char[] nb = new char[minimumCapacity];
        System.arraycopy(value, 0, nb, 0, count);
        value = nb;
        shared = false;
      }
  }

  /**
   * Predicate which determines if a substring of this matches another String
   * starting at a specified offset for each String and continuing for a
   * specified length. This is more efficient than creating a String to call
   * indexOf on.
   *
   * @param toffset index to start comparison at for this String
   * @param other non-null String to compare to region of this
   * @return true if regions match, false otherwise
   * @see #indexOf(String, int)
   * @see #lastIndexOf(String, int)
   * @see String#regionMatches(boolean, int, String, int, int)
   */
  // GCJ LOCAL: native for gcj.
  private native boolean regionMatches(int toffset, String other);
}
'#n2509'>2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114
/* Target-dependent code for GNU/Linux, architecture independent.

   Copyright (C) 2009-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 <http://www.gnu.org/licenses/>.  */

#include "exceptions.h"
#include "gdbtypes.h"
#include "linux-tdep.h"
#include "auxv.h"
#include "target.h"
#include "gdbthread.h"
#include "gdbcore.h"
#include "regcache.h"
#include "regset.h"
#include "elf/common.h"
#include "elf-bfd.h"
#include "inferior.h"
#include "cli/cli-utils.h"
#include "arch-utils.h"
#include "gdbsupport/gdb_obstack.h"
#include "observable.h"
#include "objfiles.h"
#include "infcall.h"
#include "cli/cli-cmds.h"
#include "gdbsupport/gdb_regex.h"
#include "gdbsupport/enum-flags.h"
#include <optional>
#include "gcore.h"
#include "gcore-elf.h"
#include "solib-svr4.h"
#include "memtag.h"
#include "cli/cli-style.h"
#include "gdbsupport/unordered_map.h"

#include <ctype.h>

/* This enum represents the values that the user can choose when
   informing the Linux kernel about which memory mappings will be
   dumped in a corefile.  They are described in the file
   Documentation/filesystems/proc.txt, inside the Linux kernel
   tree.  */

enum filter_flag
  {
    COREFILTER_ANON_PRIVATE = 1 << 0,
    COREFILTER_ANON_SHARED = 1 << 1,
    COREFILTER_MAPPED_PRIVATE = 1 << 2,
    COREFILTER_MAPPED_SHARED = 1 << 3,
    COREFILTER_ELF_HEADERS = 1 << 4,
    COREFILTER_HUGETLB_PRIVATE = 1 << 5,
    COREFILTER_HUGETLB_SHARED = 1 << 6,
  };
DEF_ENUM_FLAGS_TYPE (enum filter_flag, filter_flags);

/* This struct is used to map flags found in the "VmFlags:" field (in
   the /proc/<PID>/smaps file).  */

struct smaps_vmflags
  {
    /* Zero if this structure has not been initialized yet.  It
       probably means that the Linux kernel being used does not emit
       the "VmFlags:" field on "/proc/PID/smaps".  */

    unsigned int initialized_p : 1;

    /* Memory mapped I/O area (VM_IO, "io").  */

    unsigned int io_page : 1;

    /* Area uses huge TLB pages (VM_HUGETLB, "ht").  */

    unsigned int uses_huge_tlb : 1;

    /* Do not include this memory region on the coredump (VM_DONTDUMP, "dd").  */

    unsigned int exclude_coredump : 1;

    /* Is this a MAP_SHARED mapping (VM_SHARED, "sh").  */

    unsigned int shared_mapping : 1;

    /* Memory map has memory tagging enabled.  */

    unsigned int memory_tagging : 1;
  };

/* Data structure that holds the information contained in the
   /proc/<pid>/smaps file.  */

struct smaps_data
{
  ULONGEST start_address;
  ULONGEST end_address;
  std::string filename;
  struct smaps_vmflags vmflags;
  bool read;
  bool write;
  bool exec;
  bool priv;
  bool has_anonymous;
  bool mapping_anon_p;
  bool mapping_file_p;

  ULONGEST inode;
  ULONGEST offset;
};

/* Whether to take the /proc/PID/coredump_filter into account when
   generating a corefile.  */

static bool use_coredump_filter = true;

/* Whether the value of smaps_vmflags->exclude_coredump should be
   ignored, including mappings marked with the VM_DONTDUMP flag in
   the dump.  */
static bool dump_excluded_mappings = false;

/* This enum represents the signals' numbers on a generic architecture
   running the Linux kernel.  The definition of "generic" comes from
   the file <include/uapi/asm-generic/signal.h>, from the Linux kernel
   tree, which is the "de facto" implementation of signal numbers to
   be used by new architecture ports.

   For those architectures which have differences between the generic
   standard (e.g., Alpha), we define the different signals (and *only*
   those) in the specific target-dependent file (e.g.,
   alpha-linux-tdep.c, for Alpha).  Please refer to the architecture's
   tdep file for more information.

   ARM deserves a special mention here.  On the file
   <arch/arm/include/uapi/asm/signal.h>, it defines only one different
   (and ARM-only) signal, which is SIGSWI, with the same number as
   SIGRTMIN.  This signal is used only for a very specific target,
   called ArthurOS (from RISCOS).  Therefore, we do not handle it on
   the ARM-tdep file, and we can safely use the generic signal handler
   here for ARM targets.

   As stated above, this enum is derived from
   <include/uapi/asm-generic/signal.h>, from the Linux kernel
   tree.  */

enum
  {
    LINUX_SIGHUP = 1,
    LINUX_SIGINT = 2,
    LINUX_SIGQUIT = 3,
    LINUX_SIGILL = 4,
    LINUX_SIGTRAP = 5,
    LINUX_SIGABRT = 6,
    LINUX_SIGIOT = 6,
    LINUX_SIGBUS = 7,
    LINUX_SIGFPE = 8,
    LINUX_SIGKILL = 9,
    LINUX_SIGUSR1 = 10,
    LINUX_SIGSEGV = 11,
    LINUX_SIGUSR2 = 12,
    LINUX_SIGPIPE = 13,
    LINUX_SIGALRM = 14,
    LINUX_SIGTERM = 15,
    LINUX_SIGSTKFLT = 16,
    LINUX_SIGCHLD = 17,
    LINUX_SIGCONT = 18,
    LINUX_SIGSTOP = 19,
    LINUX_SIGTSTP = 20,
    LINUX_SIGTTIN = 21,
    LINUX_SIGTTOU = 22,
    LINUX_SIGURG = 23,
    LINUX_SIGXCPU = 24,
    LINUX_SIGXFSZ = 25,
    LINUX_SIGVTALRM = 26,
    LINUX_SIGPROF = 27,
    LINUX_SIGWINCH = 28,
    LINUX_SIGIO = 29,
    LINUX_SIGPOLL = LINUX_SIGIO,
    LINUX_SIGPWR = 30,
    LINUX_SIGSYS = 31,
    LINUX_SIGUNUSED = 31,

    LINUX_SIGRTMIN = 32,
    LINUX_SIGRTMAX = 64,
  };

struct linux_gdbarch_data
{
  struct type *siginfo_type = nullptr;
  int num_disp_step_buffers = 0;
};

static const registry<gdbarch>::key<linux_gdbarch_data>
     linux_gdbarch_data_handle;

static struct linux_gdbarch_data *
get_linux_gdbarch_data (struct gdbarch *gdbarch)
{
  struct linux_gdbarch_data *result = linux_gdbarch_data_handle.get (gdbarch);
  if (result == nullptr)
    result = linux_gdbarch_data_handle.emplace (gdbarch);
  return result;
}

/* Linux-specific cached data.  This is used by GDB for caching
   purposes for each inferior.  This helps reduce the overhead of
   transferring data from a remote target to the local host.  */
struct linux_info
{
  /* Cache of the inferior's vsyscall/vDSO mapping range.  Only valid
     if VSYSCALL_RANGE_P is positive.  This is cached because getting
     at this info requires an auxv lookup (which is itself cached),
     and looking through the inferior's mappings (which change
     throughout execution and therefore cannot be cached).  */
  struct mem_range vsyscall_range {};

  /* Zero if we haven't tried looking up the vsyscall's range before
     yet.  Positive if we tried looking it up, and found it.  Negative
     if we tried looking it up but failed.  */
  int vsyscall_range_p = 0;

  /* Inferior's displaced step buffers.  */
  std::optional<displaced_step_buffers> disp_step_bufs;
};

/* Per-inferior data key.  */
static const registry<inferior>::key<linux_info> linux_inferior_data;

/* Frees whatever allocated space there is to be freed and sets INF's
   linux cache data pointer to NULL.  */

static void
invalidate_linux_cache_inf (struct inferior *inf)
{
  linux_inferior_data.clear (inf);
}

/* inferior_execd observer.  */

static void
linux_inferior_execd (inferior *exec_inf, inferior *follow_inf)
{
  invalidate_linux_cache_inf (follow_inf);
}

/* Fetch the linux cache info for INF.  This function always returns a
   valid INFO pointer.  */

static struct linux_info *
get_linux_inferior_data (inferior *inf)
{
  linux_info *info = linux_inferior_data.get (inf);

  if (info == nullptr)
    info = linux_inferior_data.emplace (inf);

  return info;
}

/* See linux-tdep.h.  */

struct type *
linux_get_siginfo_type_with_fields (struct gdbarch *gdbarch,
				    linux_siginfo_extra_fields extra_fields)
{
  struct linux_gdbarch_data *linux_gdbarch_data;
  struct type *int_type, *uint_type, *long_type, *void_ptr_type, *short_type;
  struct type *uid_type, *pid_type;
  struct type *sigval_type, *clock_type;
  struct type *siginfo_type, *sifields_type;
  struct type *type;

  linux_gdbarch_data = get_linux_gdbarch_data (gdbarch);
  if (linux_gdbarch_data->siginfo_type != NULL)
    return linux_gdbarch_data->siginfo_type;

  type_allocator alloc (gdbarch);

  int_type = init_integer_type (alloc, gdbarch_int_bit (gdbarch),
			 	0, "int");
  uint_type = init_integer_type (alloc, gdbarch_int_bit (gdbarch),
				 1, "unsigned int");
  long_type = init_integer_type (alloc, gdbarch_long_bit (gdbarch),
				 0, "long");
  short_type = init_integer_type (alloc, gdbarch_long_bit (gdbarch),
				 0, "short");
  void_ptr_type = lookup_pointer_type (builtin_type (gdbarch)->builtin_void);

  /* sival_t */
  sigval_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_UNION);
  sigval_type->set_name (xstrdup ("sigval_t"));
  append_composite_type_field (sigval_type, "sival_int", int_type);
  append_composite_type_field (sigval_type, "sival_ptr", void_ptr_type);

  /* __pid_t */
  pid_type = alloc.new_type (TYPE_CODE_TYPEDEF,
			     int_type->length () * TARGET_CHAR_BIT,
			     "__pid_t");
  pid_type->set_target_type (int_type);
  pid_type->set_target_is_stub (true);

  /* __uid_t */
  uid_type = alloc.new_type (TYPE_CODE_TYPEDEF,
			     uint_type->length () * TARGET_CHAR_BIT,
			     "__uid_t");
  uid_type->set_target_type (uint_type);
  uid_type->set_target_is_stub (true);

  /* __clock_t */
  clock_type = alloc.new_type (TYPE_CODE_TYPEDEF,
			       long_type->length () * TARGET_CHAR_BIT,
			       "__clock_t");
  clock_type->set_target_type (long_type);
  clock_type->set_target_is_stub (true);

  /* _sifields */
  sifields_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_UNION);

  {
    const int si_max_size = 128;
    int si_pad_size;
    int size_of_int = gdbarch_int_bit (gdbarch) / HOST_CHAR_BIT;

    /* _pad */
    if (gdbarch_ptr_bit (gdbarch) == 64)
      si_pad_size = (si_max_size / size_of_int) - 4;
    else
      si_pad_size = (si_max_size / size_of_int) - 3;
    append_composite_type_field (sifields_type, "_pad",
				 init_vector_type (int_type, si_pad_size));
  }

  /* _kill */
  type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
  append_composite_type_field (type, "si_pid", pid_type);
  append_composite_type_field (type, "si_uid", uid_type);
  append_composite_type_field (sifields_type, "_kill", type);

  /* _timer */
  type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
  append_composite_type_field (type, "si_tid", int_type);
  append_composite_type_field (type, "si_overrun", int_type);
  append_composite_type_field (type, "si_sigval", sigval_type);
  append_composite_type_field (sifields_type, "_timer", type);

  /* _rt */
  type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
  append_composite_type_field (type, "si_pid", pid_type);
  append_composite_type_field (type, "si_uid", uid_type);
  append_composite_type_field (type, "si_sigval", sigval_type);
  append_composite_type_field (sifields_type, "_rt", type);

  /* _sigchld */
  type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
  append_composite_type_field (type, "si_pid", pid_type);
  append_composite_type_field (type, "si_uid", uid_type);
  append_composite_type_field (type, "si_status", int_type);
  append_composite_type_field (type, "si_utime", clock_type);
  append_composite_type_field (type, "si_stime", clock_type);
  append_composite_type_field (sifields_type, "_sigchld", type);

  /* _sigfault */
  type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
  append_composite_type_field (type, "si_addr", void_ptr_type);

  /* Additional bound fields for _sigfault in case they were requested.  */
  if ((extra_fields & LINUX_SIGINFO_FIELD_ADDR_BND) != 0)
    {
      struct type *sigfault_bnd_fields;

      append_composite_type_field (type, "_addr_lsb", short_type);
      sigfault_bnd_fields = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
      append_composite_type_field (sigfault_bnd_fields, "_lower", void_ptr_type);
      append_composite_type_field (sigfault_bnd_fields, "_upper", void_ptr_type);
      append_composite_type_field (type, "_addr_bnd", sigfault_bnd_fields);
    }
  append_composite_type_field (sifields_type, "_sigfault", type);

  /* _sigpoll */
  type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
  append_composite_type_field (type, "si_band", long_type);
  append_composite_type_field (type, "si_fd", int_type);
  append_composite_type_field (sifields_type, "_sigpoll", type);

  /* _sigsys */
  type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
  append_composite_type_field (type, "_call_addr", void_ptr_type);
  append_composite_type_field (type, "_syscall", int_type);
  append_composite_type_field (type, "_arch", uint_type);
  append_composite_type_field (sifields_type, "_sigsys", type);

  /* struct siginfo */
  siginfo_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
  siginfo_type->set_name (xstrdup ("siginfo"));
  append_composite_type_field (siginfo_type, "si_signo", int_type);
  append_composite_type_field (siginfo_type, "si_errno", int_type);
  append_composite_type_field (siginfo_type, "si_code", int_type);
  append_composite_type_field_aligned (siginfo_type,
				       "_sifields", sifields_type,
				       long_type->length ());

  linux_gdbarch_data->siginfo_type = siginfo_type;

  return siginfo_type;
}

/* This function is suitable for architectures that don't
   extend/override the standard siginfo structure.  */

static struct type *
linux_get_siginfo_type (struct gdbarch *gdbarch)
{
  return linux_get_siginfo_type_with_fields (gdbarch, 0);
}

/* Return true if the target is running on uClinux instead of normal
   Linux kernel.  */

int
linux_is_uclinux (void)
{
  CORE_ADDR dummy;

  return (target_auxv_search (AT_NULL, &dummy) > 0
	  && target_auxv_search (AT_PAGESZ, &dummy) == 0);
}

static int
linux_has_shared_address_space (struct gdbarch *gdbarch)
{
  return linux_is_uclinux ();
}

/* This is how we want PTIDs from core files to be printed.  */

static std::string
linux_core_pid_to_str (struct gdbarch *gdbarch, ptid_t ptid)
{
  if (ptid.lwp () != 0)
    return string_printf ("LWP %ld", ptid.lwp ());

  return normal_pid_to_str (ptid);
}

/* Data from one mapping from /proc/PID/maps.  */

struct mapping
{
  ULONGEST addr;
  ULONGEST endaddr;
  std::string permissions;
  ULONGEST offset;
  std::string_view device;
  ULONGEST inode;

  /* This field is guaranteed to be NULL-terminated, hence it is not a
     std::string_view.  */
  const char *filename;
};

/* Service function for corefiles and info proc.  */

static mapping
read_mapping (const char *line)
{
  struct mapping mapping;
  const char *p = line;

  mapping.addr = strtoulst (p, &p, 16);
  if (*p == '-')
    p++;
  mapping.endaddr = strtoulst (p, &p, 16);

  p = skip_spaces (p);
  const char *permissions_start = p;
  while (*p && !isspace (*p))
    p++;
  mapping.permissions = std::string (permissions_start,
				     (size_t) (p - permissions_start));

  mapping.offset = strtoulst (p, &p, 16);

  p = skip_spaces (p);
  const char *device_start = p;
  while (*p && !isspace (*p))
    p++;
  mapping.device = {device_start, (size_t) (p - device_start)};

  mapping.inode = strtoulst (p, &p, 10);

  p = skip_spaces (p);
  mapping.filename = p;

  return mapping;
}

/* Helper function to decode the "VmFlags" field in /proc/PID/smaps.

   This function was based on the documentation found on
   <Documentation/filesystems/proc.txt>, on the Linux kernel.

   Linux kernels before commit
   834f82e2aa9a8ede94b17b656329f850c1471514 (3.10) do not have this
   field on smaps.  */

static void
decode_vmflags (char *p, struct smaps_vmflags *v)
{
  char *saveptr = NULL;
  const char *s;

  v->initialized_p = 1;
  p = skip_to_space (p);
  p = skip_spaces (p);

  for (s = strtok_r (p, " ", &saveptr);
       s != NULL;
       s = strtok_r (NULL, " ", &saveptr))
    {
      if (strcmp (s, "io") == 0)
	v->io_page = 1;
      else if (strcmp (s, "ht") == 0)
	v->uses_huge_tlb = 1;
      else if (strcmp (s, "dd") == 0)
	v->exclude_coredump = 1;
      else if (strcmp (s, "sh") == 0)
	v->shared_mapping = 1;
      else if (strcmp (s, "mt") == 0)
	v->memory_tagging = 1;
    }
}

/* Regexes used by mapping_is_anonymous_p.  Put in a structure because
   they're initialized lazily.  */

struct mapping_regexes
{
  /* Matches "/dev/zero" filenames (with or without the "(deleted)"
     string in the end).  We know for sure, based on the Linux kernel
     code, that memory mappings whose associated filename is
     "/dev/zero" are guaranteed to be MAP_ANONYMOUS.  */
  compiled_regex dev_zero
    {"^/dev/zero\\( (deleted)\\)\\?$", REG_NOSUB,
     _("Could not compile regex to match /dev/zero filename")};

  /* Matches "/SYSV%08x" filenames (with or without the "(deleted)"
     string in the end).  These filenames refer to shared memory
     (shmem), and memory mappings associated with them are
     MAP_ANONYMOUS as well.  */
  compiled_regex shmem_file
    {"^/\\?SYSV[0-9a-fA-F]\\{8\\}\\( (deleted)\\)\\?$", REG_NOSUB,
     _("Could not compile regex to match shmem filenames")};

  /* A heuristic we use to try to mimic the Linux kernel's 'n_link ==
     0' code, which is responsible to decide if it is dealing with a
     'MAP_SHARED | MAP_ANONYMOUS' mapping.  In other words, if
     FILE_DELETED matches, it does not necessarily mean that we are
     dealing with an anonymous shared mapping.  However, there is no
     easy way to detect this currently, so this is the best
     approximation we have.

     As a result, GDB will dump readonly pages of deleted executables
     when using the default value of coredump_filter (0x33), while the
     Linux kernel will not dump those pages.  But we can live with
     that.  */
  compiled_regex file_deleted
    {" (deleted)$", REG_NOSUB,
     _("Could not compile regex to match '<file> (deleted)'")};
};

/* Return 1 if the memory mapping is anonymous, 0 otherwise.

   FILENAME is the name of the file present in the first line of the
   memory mapping, in the "/proc/PID/smaps" output.  For example, if
   the first line is:

   7fd0ca877000-7fd0d0da0000 r--p 00000000 fd:02 2100770   /path/to/file

   Then FILENAME will be "/path/to/file".  */

static int
mapping_is_anonymous_p (const char *filename)
{
  static std::optional<mapping_regexes> regexes;
  static int init_regex_p = 0;

  if (!init_regex_p)
    {
      /* Let's be pessimistic and assume there will be an error while
	 compiling the regex'es.  */
      init_regex_p = -1;

      regexes.emplace ();

      /* If we reached this point, then everything succeeded.  */
      init_regex_p = 1;
    }

  if (init_regex_p == -1)
    {
      const char deleted[] = " (deleted)";
      size_t del_len = sizeof (deleted) - 1;
      size_t filename_len = strlen (filename);

      /* There was an error while compiling the regex'es above.  In
	 order to try to give some reliable information to the caller,
	 we just try to find the string " (deleted)" in the filename.
	 If we managed to find it, then we assume the mapping is
	 anonymous.  */
      return (filename_len >= del_len
	      && strcmp (filename + filename_len - del_len, deleted) == 0);
    }

  if (*filename == '\0'
      || regexes->dev_zero.exec (filename, 0, NULL, 0) == 0
      || regexes->shmem_file.exec (filename, 0, NULL, 0) == 0
      || regexes->file_deleted.exec (filename, 0, NULL, 0) == 0)
    return 1;

  return 0;
}

/* Return false if the memory mapping represented by MAP should not be
   dumped, or true if it should.  FILTERFLAGS guides which mappings
   should be dumped.

   In a nutshell, this is the logic that we follow in order to decide
   if a mapping should be dumped or not.

   - If the mapping is associated to a file whose name ends with
     " (deleted)", or if the file is "/dev/zero", or if it is
     "/SYSV%08x" (shared memory), or if there is no file associated
     with it, or if the AnonHugePages: or the Anonymous: fields in the
     /proc/PID/smaps have contents, then GDB considers this mapping to
     be anonymous.  Otherwise, GDB considers this mapping to be a
     file-backed mapping (because there will be a file associated with
     it).
 
     It is worth mentioning that, from all those checks described
     above, the most fragile is the one to see if the file name ends
     with " (deleted)".  This does not necessarily mean that the
     mapping is anonymous, because the deleted file associated with
     the mapping may have been a hard link to another file, for
     example.  The Linux kernel checks to see if "i_nlink == 0", but
     GDB cannot easily (and normally) do this check (iff running as
     root, it could find the mapping in /proc/PID/map_files/ and
     determine whether there still are other hard links to the
     inode/file).  Therefore, we made a compromise here, and we assume
     that if the file name ends with " (deleted)", then the mapping is
     indeed anonymous.  FWIW, this is something the Linux kernel could
     do better: expose this information in a more direct way.
 
   - If we see the flag "sh" in the "VmFlags:" field (in
     /proc/PID/smaps), then certainly the memory mapping is shared
     (VM_SHARED).  If we have access to the VmFlags, and we don't see
     the "sh" there, then certainly the mapping is private.  However,
     Linux kernels before commit
     834f82e2aa9a8ede94b17b656329f850c1471514 (3.10) do not have the
     "VmFlags:" field; in that case, we use another heuristic: if we
     see 'p' in the permission flags, then we assume that the mapping
     is private, even though the presence of the 's' flag there would
     mean VM_MAYSHARE, which means the mapping could still be private.
     This should work OK enough, however.

   - Even if, at the end, we decided that we should not dump the
     mapping, we still have to check if it is something like an ELF
     header (of a DSO or an executable, for example).  If it is, and
     if the user is interested in dump it, then we should dump it.  */

static bool
dump_mapping_p (filter_flags filterflags, const smaps_data &map)
{
  /* Older Linux kernels did not support the "Anonymous:" counter.
     If it is missing, we can't be sure what to dump, so dump everything.  */
  if (!map.has_anonymous)
    return true;

  /* Initially, we trust in what we received from our caller.  This
     value may not be very precise (i.e., it was probably gathered
     from the permission line in the /proc/PID/smaps list, which
     actually refers to VM_MAYSHARE, and not VM_SHARED), but it is
     what we have until we take a look at the "VmFlags:" field
     (assuming that the version of the Linux kernel being used
     supports it, of course).  */
  int private_p = map.priv;

  /* We always dump vDSO and vsyscall mappings, because it's likely that
     there'll be no file to read the contents from at core load time.
     The kernel does the same.  */
  if (map.filename == "[vdso]" || map.filename == "[vsyscall]")
    return true;

  if (map.vmflags.initialized_p)
    {
      /* We never dump I/O mappings.  */
      if (map.vmflags.io_page)
	return false;

      /* Check if we should exclude this mapping.  */
      if (!dump_excluded_mappings && map.vmflags.exclude_coredump)
	return false;

      /* Update our notion of whether this mapping is shared or
	 private based on a trustworthy value.  */
      private_p = !map.vmflags.shared_mapping;

      /* HugeTLB checking.  */
      if (map.vmflags.uses_huge_tlb)
	{
	  if ((private_p && (filterflags & COREFILTER_HUGETLB_PRIVATE))
	      || (!private_p && (filterflags & COREFILTER_HUGETLB_SHARED)))
	    return true;

	  return false;
	}
    }

  int mapping_anon_p = map.mapping_anon_p;
  int mapping_file_p = map.mapping_file_p;
  bool dump_p;
  if (private_p)
    {
      if (mapping_anon_p && mapping_file_p)
	{
	  /* This is a special situation.  It can happen when we see a
	     mapping that is file-backed, but that contains anonymous
	     pages.  */
	  dump_p = ((filterflags & COREFILTER_ANON_PRIVATE) != 0
		    || (filterflags & COREFILTER_MAPPED_PRIVATE) != 0);
	}
      else if (mapping_anon_p)
	dump_p = (filterflags & COREFILTER_ANON_PRIVATE) != 0;
      else
	dump_p = (filterflags & COREFILTER_MAPPED_PRIVATE) != 0;
    }
  else
    {
      if (mapping_anon_p && mapping_file_p)
	{
	  /* This is a special situation.  It can happen when we see a
	     mapping that is file-backed, but that contains anonymous
	     pages.  */
	  dump_p = ((filterflags & COREFILTER_ANON_SHARED) != 0
		    || (filterflags & COREFILTER_MAPPED_SHARED) != 0);
	}
      else if (mapping_anon_p)
	dump_p = (filterflags & COREFILTER_ANON_SHARED) != 0;
      else
	dump_p = (filterflags & COREFILTER_MAPPED_SHARED) != 0;
    }

  /* Even if we decided that we shouldn't dump this mapping, we still
     have to check whether (a) the user wants us to dump mappings
     containing an ELF header, and (b) the mapping in question
     contains an ELF header.  If (a) and (b) are true, then we should
     dump this mapping.

     A mapping contains an ELF header if it is a private mapping, its
     offset is zero, and its first word is ELFMAG.  */
  if (!dump_p && private_p && map.offset == 0
      && (filterflags & COREFILTER_ELF_HEADERS) != 0)
    {
      /* Useful define specifying the size of the ELF magical
	 header.  */
#ifndef SELFMAG
#define SELFMAG 4
#endif

      /* Let's check if we have an ELF header.  */
      gdb_byte h[SELFMAG];
      if (target_read_memory (map.start_address, h, SELFMAG) == 0)
	{
	  /* The EI_MAG* and ELFMAG* constants come from
	     <elf/common.h>.  */
	  if (h[EI_MAG0] == ELFMAG0 && h[EI_MAG1] == ELFMAG1
	      && h[EI_MAG2] == ELFMAG2 && h[EI_MAG3] == ELFMAG3)
	    {
	      /* This mapping contains an ELF header, so we
		 should dump it.  */
	      dump_p = true;
	    }
	}
    }

  return dump_p;
}

/* As above, but return true only when we should dump the NT_FILE
   entry.  */

static bool
dump_note_entry_p (filter_flags filterflags, const smaps_data &map)
{
  /* No NT_FILE entry for mappings with no filename.  */
  if (map.filename.length () == 0)
    return false;

  /* Special kernel mappings, those with names like '[vdso]' and
     '[vsyscall]' will be placed in the core file, but shouldn't get an
     NT_FILE entry.  These special mappings all have a zero inode.  */
  if (map.inode == 0
      && map.filename.front () == '['
      && map.filename.back () == ']')
    return false;

  /* Otherwise, any other file-based mapping should be placed in the
     note.  */
  return true;
}

/* Implement the "info proc" command.  */

static void
linux_info_proc (struct gdbarch *gdbarch, const char *args,
		 enum info_proc_what what)
{
  /* A long is used for pid instead of an int to avoid a loss of precision
     compiler warning from the output of strtoul.  */
  long pid;
  int cmdline_f = (what == IP_MINIMAL || what == IP_CMDLINE || what == IP_ALL);
  int cwd_f = (what == IP_MINIMAL || what == IP_CWD || what == IP_ALL);
  int exe_f = (what == IP_MINIMAL || what == IP_EXE || what == IP_ALL);
  int mappings_f = (what == IP_MAPPINGS || what == IP_ALL);
  int status_f = (what == IP_STATUS || what == IP_ALL);
  int stat_f = (what == IP_STAT || what == IP_ALL);
  char filename[100];
  fileio_error target_errno;

  if (args && isdigit (args[0]))
    {
      char *tem;

      pid = strtoul (args, &tem, 10);
      args = tem;
    }
  else
    {
      if (!target_has_execution ())
	error (_("No current process: you must name one."));
      if (current_inferior ()->fake_pid_p)
	error (_("Can't determine the current process's PID: you must name one."));

      pid = current_inferior ()->pid;
    }

  args = skip_spaces (args);
  if (args && args[0])
    error (_("Too many parameters: %s"), args);

  gdb_printf (_("process %ld\n"), pid);
  if (cmdline_f)
    {
      xsnprintf (filename, sizeof filename, "/proc/%ld/cmdline", pid);
      gdb_byte *buffer;
      LONGEST len = target_fileio_read_alloc (nullptr, filename, &buffer);

      if (len > 0)
	{
	  gdb::unique_xmalloc_ptr<char> cmdline ((char *) buffer);
	  ssize_t pos;

	  for (pos = 0; pos < len - 1; pos++)
	    {
	      if (buffer[pos] == '\0')
		buffer[pos] = ' ';
	    }
	  buffer[len - 1] = '\0';
	  gdb_printf ("cmdline = '%s'\n", buffer);
	}
      else
	warning (_("unable to open /proc file '%s'"), filename);
    }
  if (cwd_f)
    {
      xsnprintf (filename, sizeof filename, "/proc/%ld/cwd", pid);
      std::optional<std::string> contents
	= target_fileio_readlink (NULL, filename, &target_errno);
      if (contents.has_value ())
	gdb_printf ("cwd = '%s'\n", contents->c_str ());
      else
	warning (_("unable to read link '%s'"), filename);
    }
  if (exe_f)
    {
      xsnprintf (filename, sizeof filename, "/proc/%ld/exe", pid);
      std::optional<std::string> contents
	= target_fileio_readlink (NULL, filename, &target_errno);
      if (contents.has_value ())
	gdb_printf ("exe = '%s'\n", contents->c_str ());
      else
	warning (_("unable to read link '%s'"), filename);
    }
  if (mappings_f)
    {
      xsnprintf (filename, sizeof filename, "/proc/%ld/maps", pid);
      gdb::unique_xmalloc_ptr<char> map
	= target_fileio_read_stralloc (NULL, filename);
      if (map != NULL)
	{
	  gdb_printf (_("Mapped address spaces:\n\n"));
	  ui_out_emit_table emitter (current_uiout, 6, -1, "ProcMappings");

	  int width = gdbarch_addr_bit (gdbarch) == 32 ? 10 : 18;
	  current_uiout->table_header (width, ui_left, "start", "Start Addr");
	  current_uiout->table_header (width, ui_left, "end", "End Addr");
	  current_uiout->table_header (width, ui_left, "size", "Size");
	  current_uiout->table_header (width, ui_left, "offset", "Offset");
	  current_uiout->table_header (5, ui_left, "perms", "Perms");
	  current_uiout->table_header (0, ui_left, "objfile", "File");
	  current_uiout->table_body ();

	  char *saveptr;
	  for (const char *line = strtok_r (map.get (), "\n", &saveptr);
	       line != nullptr;
	       line = strtok_r (nullptr, "\n", &saveptr))
	    {
	      struct mapping m = read_mapping (line);

	      ui_out_emit_tuple tuple_emitter (current_uiout, nullptr);
	      current_uiout->field_core_addr ("start", gdbarch, m.addr);
	      current_uiout->field_core_addr ("end", gdbarch, m.endaddr);
	      /* These next two aren't really addresses and so
		 shouldn't be styled as such.  */
	      current_uiout->field_string ("size",
					   paddress (gdbarch,
						     m.endaddr - m.addr));
	      current_uiout->field_string ("offset",
					   paddress (gdbarch, m.offset));
	      current_uiout->field_string ("perms", m.permissions);
	      current_uiout->field_string ("objfile", m.filename,
					   file_name_style.style ());
	      current_uiout->text ("\n");
	    }
	}
      else
	warning (_("unable to open /proc file '%s'"), filename);
    }
  if (status_f)
    {
      xsnprintf (filename, sizeof filename, "/proc/%ld/status", pid);
      gdb::unique_xmalloc_ptr<char> status
	= target_fileio_read_stralloc (NULL, filename);
      if (status)
	gdb_puts (status.get ());
      else
	warning (_("unable to open /proc file '%s'"), filename);
    }
  if (stat_f)
    {
      xsnprintf (filename, sizeof filename, "/proc/%ld/stat", pid);
      gdb::unique_xmalloc_ptr<char> statstr
	= target_fileio_read_stralloc (NULL, filename);
      if (statstr)
	{
	  const char *p = statstr.get ();

	  gdb_printf (_("Process: %s\n"),
		      pulongest (strtoulst (p, &p, 10)));

	  p = skip_spaces (p);
	  if (*p == '(')
	    {
	      /* ps command also relies on no trailing fields
		 ever contain ')'.  */
	      const char *ep = strrchr (p, ')');
	      if (ep != NULL)
		{
		  gdb_printf ("Exec file: %.*s\n",
			      (int) (ep - p - 1), p + 1);
		  p = ep + 1;
		}
	    }

	  p = skip_spaces (p);
	  if (*p)
	    gdb_printf (_("State: %c\n"), *p++);

	  if (*p)
	    gdb_printf (_("Parent process: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Process group: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Session id: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("TTY: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("TTY owner process group: %s\n"),
			pulongest (strtoulst (p, &p, 10)));

	  if (*p)
	    gdb_printf (_("Flags: %s\n"),
			hex_string (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Minor faults (no memory page): %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Minor faults, children: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Major faults (memory page faults): %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Major faults, children: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("utime: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("stime: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("utime, children: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("stime, children: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("jiffies remaining in current "
			  "time slice: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("'nice' value: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("jiffies until next timeout: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("jiffies until next SIGALRM: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("start time (jiffies since "
			  "system boot): %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Virtual memory size: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Resident set size: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("rlim: %s\n"),
			pulongest (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Start of text: %s\n"),
			hex_string (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("End of text: %s\n"),
			hex_string (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Start of stack: %s\n"),
			hex_string (strtoulst (p, &p, 10)));
#if 0	/* Don't know how architecture-dependent the rest is...
	   Anyway the signal bitmap info is available from "status".  */
	  if (*p)
	    gdb_printf (_("Kernel stack pointer: %s\n"),
			hex_string (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Kernel instr pointer: %s\n"),
			hex_string (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Pending signals bitmap: %s\n"),
			hex_string (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Blocked signals bitmap: %s\n"),
			hex_string (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Ignored signals bitmap: %s\n"),
			hex_string (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("Caught signals bitmap: %s\n"),
			hex_string (strtoulst (p, &p, 10)));
	  if (*p)
	    gdb_printf (_("wchan (system call): %s\n"),
			hex_string (strtoulst (p, &p, 10)));
#endif
	}
      else
	warning (_("unable to open /proc file '%s'"), filename);
    }
}

/* Implementation of `gdbarch_read_core_file_mappings', as defined in
   gdbarch.h.
   
   This function reads the NT_FILE note (which BFD turns into the
   section ".note.linuxcore.file").  The format of this note / section
   is described as follows in the Linux kernel sources in
   fs/binfmt_elf.c:
   
      long count     -- how many files are mapped
      long page_size -- units for file_ofs
      array of [COUNT] elements of
	long start
	long end
	long file_ofs
      followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL...
      
   CBFD is the BFD of the core file.

   PRE_LOOP_CB is the callback function to invoke prior to starting
   the loop which processes individual entries.  This callback will
   only be executed after the note has been examined in enough
   detail to verify that it's not malformed in some way.
   
   LOOP_CB is the callback function that will be executed once
   for each mapping.  */

static void
linux_read_core_file_mappings
  (struct gdbarch *gdbarch,
   struct bfd *cbfd,
   read_core_file_mappings_pre_loop_ftype pre_loop_cb,
   read_core_file_mappings_loop_ftype  loop_cb)
{
  /* Ensure that ULONGEST is big enough for reading 64-bit core files.  */
  static_assert (sizeof (ULONGEST) >= 8);

  /* It's not required that the NT_FILE note exists, so return silently
     if it's not found.  Beyond this point though, we'll complain
     if problems are found.  */
  asection *section = bfd_get_section_by_name (cbfd, ".note.linuxcore.file");
  if (section == nullptr)
    return;

  unsigned int addr_size_bits = gdbarch_addr_bit (gdbarch);
  unsigned int addr_size = addr_size_bits / 8;
  size_t note_size = bfd_section_size (section);

  if (note_size < 2 * addr_size)
    {
      warning (_("malformed core note - too short for header"));
      return;
    }

  gdb::byte_vector contents (note_size);
  if (!bfd_get_section_contents (cbfd, section, contents.data (), 0,
				 note_size))
    {
      warning (_("could not get core note contents"));
      return;
    }

  gdb_byte *descdata = contents.data ();
  char *descend = (char *) descdata + note_size;

  if (descdata[note_size - 1] != '\0')
    {
      warning (_("malformed note - does not end with \\0"));
      return;
    }

  ULONGEST count = bfd_get (addr_size_bits, cbfd, descdata);
  descdata += addr_size;

  ULONGEST page_size = bfd_get (addr_size_bits, cbfd, descdata);
  descdata += addr_size;

  if (note_size < 2 * addr_size + count * 3 * addr_size)
    {
      warning (_("malformed note - too short for supplied file count"));
      return;
    }

  char *filenames = (char *) descdata + count * 3 * addr_size;

  /* Make sure that the correct number of filenames exist.  Complain
     if there aren't enough or are too many.  */
  char *f = filenames;
  for (int i = 0; i < count; i++)
    {
      if (f >= descend)
	{
	  warning (_("malformed note - filename area is too small"));
	  return;
	}
      f += strnlen (f, descend - f) + 1;
    }
  /* Complain, but don't return early if the filename area is too big.  */
  if (f != descend)
    warning (_("malformed note - filename area is too big"));

  const bfd_build_id *orig_build_id = cbfd->build_id;
  gdb::unordered_map<ULONGEST, const bfd_build_id *> vma_map;

  /* Search for solib build-ids in the core file.  Each time one is found,
     map the start vma of the corresponding elf header to the build-id.  */
  for (bfd_section *sec = cbfd->sections; sec != nullptr; sec = sec->next)
    {
      cbfd->build_id = nullptr;

      if (sec->flags & SEC_LOAD
	  && (get_elf_backend_data (cbfd)->elf_backend_core_find_build_id
	       (cbfd, (bfd_vma) sec->filepos)))
	vma_map[sec->vma] = cbfd->build_id;
    }

  cbfd->build_id = orig_build_id;
  pre_loop_cb (count);

  for (int i = 0; i < count; i++)
    {
      ULONGEST start = bfd_get (addr_size_bits, cbfd, descdata);
      descdata += addr_size;
      ULONGEST end = bfd_get (addr_size_bits, cbfd, descdata);
      descdata += addr_size;
      ULONGEST file_ofs = bfd_get (addr_size_bits, cbfd, descdata) * page_size;
      descdata += addr_size;
      char * filename = filenames;
      filenames += strlen ((char *) filenames) + 1;
      const bfd_build_id *build_id = nullptr;
      auto vma_map_it = vma_map.find (start);

      if (vma_map_it != vma_map.end ())
	build_id = vma_map_it->second;

      loop_cb (i, start, end, file_ofs, filename, build_id);
    }
}

/* Implement "info proc mappings" for corefile CBFD.  */

static void
linux_core_info_proc_mappings (struct gdbarch *gdbarch, struct bfd *cbfd,
			       const char *args)
{
  std::optional<ui_out_emit_table> emitter;

  linux_read_core_file_mappings (gdbarch, cbfd,
    [&] (ULONGEST count)
      {
	gdb_printf (_("Mapped address spaces:\n\n"));
	emitter.emplace (current_uiout, 5, -1, "ProcMappings");
	int width = gdbarch_addr_bit (gdbarch) == 32 ? 10 : 18;
	current_uiout->table_header (width, ui_left, "start", "Start Addr");
	current_uiout->table_header (width, ui_left, "end", "End Addr");
	current_uiout->table_header (width, ui_left, "size", "Size");
	current_uiout->table_header (width, ui_left, "offset", "Offset");
	current_uiout->table_header (0, ui_left, "objfile", "File");
	current_uiout->table_body ();
      },
    [=] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
	 const char *filename, const bfd_build_id *build_id)
      {
	ui_out_emit_tuple tuple_emitter (current_uiout, nullptr);
	current_uiout->field_core_addr ("start", gdbarch, start);
	current_uiout->field_core_addr ("end", gdbarch, end);
	/* These next two aren't really addresses and so shouldn't be
	   styled as such.  */
	current_uiout->field_string ("size", paddress (gdbarch, end - start));
	current_uiout->field_string ("offset", paddress (gdbarch, file_ofs));
	current_uiout->field_string ("objfile", filename,
				     file_name_style.style ());
	current_uiout->text ("\n");
      });
}

/* Implement "info proc" for corefile CBFD.  */

static void
linux_core_info_proc (struct gdbarch *gdbarch, struct bfd *cbfd,
		      const char *args, enum info_proc_what what)
{
  int exe_f = (what == IP_MINIMAL || what == IP_EXE || what == IP_ALL);
  int mappings_f = (what == IP_MAPPINGS || what == IP_ALL);

  if (exe_f)
    {
      const char *exe = bfd_core_file_failing_command (cbfd);

      if (exe != NULL)
	gdb_printf ("exe = '%s'\n", exe);
      else
	warning (_("unable to find command name in core file"));
    }

  if (mappings_f)
    linux_core_info_proc_mappings (gdbarch, cbfd, args);

  if (!exe_f && !mappings_f)
    error (_("unable to handle request"));
}

/* Read siginfo data from the core, if possible.  Returns -1 on
   failure.  Otherwise, returns the number of bytes read.  READBUF,
   OFFSET, and LEN are all as specified by the to_xfer_partial
   interface.  */

static LONGEST
linux_core_xfer_siginfo (struct gdbarch *gdbarch, gdb_byte *readbuf,
			 ULONGEST offset, ULONGEST len)
{
  thread_section_name section_name (".note.linuxcore.siginfo", inferior_ptid);
  asection *section
    = bfd_get_section_by_name (current_program_space->core_bfd (),
			       section_name.c_str ());
  if (section == NULL)
    return -1;

  if (!bfd_get_section_contents (current_program_space->core_bfd (), section,
				 readbuf, offset, len))
    return -1;

  return len;
}

typedef int linux_find_memory_region_ftype (ULONGEST vaddr, ULONGEST size,
					    ULONGEST offset,
					    int read, int write,
					    int exec, int modified,
					    bool memory_tagged,
					    const std::string &filename,
					    void *data);

typedef bool linux_dump_mapping_p_ftype (filter_flags filterflags,
					 const smaps_data &map);

/* Helper function to parse the contents of /proc/<pid>/smaps into a data
   structure, for easy access.

   DATA is the contents of the smaps file.  The parsed contents are stored
   into the SMAPS vector.  */

static std::vector<struct smaps_data>
parse_smaps_data (const char *data,
		  const std::string maps_filename)
{
  char *line, *t;

  gdb_assert (data != nullptr);

  line = strtok_r ((char *) data, "\n", &t);

  std::vector<struct smaps_data> smaps;

  while (line != NULL)
    {
      struct smaps_vmflags v;
      int read, write, exec, priv;
      int has_anonymous = 0;
      int mapping_anon_p;
      int mapping_file_p;

      memset (&v, 0, sizeof (v));
      struct mapping m = read_mapping (line);
      mapping_anon_p = mapping_is_anonymous_p (m.filename);
      /* If the mapping is not anonymous, then we can consider it
	 to be file-backed.  These two states (anonymous or
	 file-backed) seem to be exclusive, but they can actually
	 coexist.  For example, if a file-backed mapping has
	 "Anonymous:" pages (see more below), then the Linux
	 kernel will dump this mapping when the user specified
	 that she only wants anonymous mappings in the corefile
	 (*even* when she explicitly disabled the dumping of
	 file-backed mappings).  */
      mapping_file_p = !mapping_anon_p;

      /* Decode permissions.  */
      auto has_perm = [&m] (char c)
	{ return m.permissions.find (c) != std::string_view::npos; };
      read = has_perm ('r');
      write = has_perm ('w');
      exec = has_perm ('x');

      /* 'private' here actually means VM_MAYSHARE, and not
	 VM_SHARED.  In order to know if a mapping is really
	 private or not, we must check the flag "sh" in the
	 VmFlags field.  This is done by decode_vmflags.  However,
	 if we are using a Linux kernel released before the commit
	 834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will
	 not have the VmFlags there.  In this case, there is
	 really no way to know if we are dealing with VM_SHARED,
	 so we just assume that VM_MAYSHARE is enough.  */
      priv = has_perm ('p');

      /* Try to detect if region should be dumped by parsing smaps
	 counters.  */
      for (line = strtok_r (NULL, "\n", &t);
	   line != NULL && line[0] >= 'A' && line[0] <= 'Z';
	   line = strtok_r (NULL, "\n", &t))
	{
	  char keyword[64 + 1];

	  if (sscanf (line, "%64s", keyword) != 1)
	    {
	      warning (_("Error parsing {s,}maps file '%s'"),
		       maps_filename.c_str ());
	      break;
	    }

	  if (strcmp (keyword, "Anonymous:") == 0)
	    {
	      /* Older Linux kernels did not support the
		 "Anonymous:" counter.  Check it here.  */
	      has_anonymous = 1;
	    }
	  else if (strcmp (keyword, "VmFlags:") == 0)
	    decode_vmflags (line, &v);

	  if (strcmp (keyword, "AnonHugePages:") == 0
	      || strcmp (keyword, "Anonymous:") == 0)
	    {
	      unsigned long number;

	      if (sscanf (line, "%*s%lu", &number) != 1)
		{
		  warning (_("Error parsing {s,}maps file '%s' number"),
			   maps_filename.c_str ());
		  break;
		}
	      if (number > 0)
		{
		  /* Even if we are dealing with a file-backed
		     mapping, if it contains anonymous pages we
		     consider it to be *also* an anonymous
		     mapping, because this is what the Linux
		     kernel does:

		     // Dump segments that have been written to.
		     if (vma->anon_vma && FILTER(ANON_PRIVATE))
		       goto whole;

		    Note that if the mapping is already marked as
		    file-backed (i.e., mapping_file_p is
		    non-zero), then this is a special case, and
		    this mapping will be dumped either when the
		    user wants to dump file-backed *or* anonymous
		    mappings.  */
		  mapping_anon_p = 1;
		}
	    }
	}
      /* Save the smaps entry to the vector.  */
	struct smaps_data map;

	map.start_address = m.addr;
	map.end_address = m.endaddr;
	map.filename = m.filename;
	map.vmflags = v;
	map.read = read? true : false;
	map.write = write? true : false;
	map.exec = exec? true : false;
	map.priv = priv? true : false;
	map.has_anonymous = has_anonymous;
	map.mapping_anon_p = mapping_anon_p? true : false;
	map.mapping_file_p = mapping_file_p? true : false;
	map.offset = m.offset;
	map.inode = m.inode;

	smaps.emplace_back (map);
    }

  return smaps;
}

/* Helper that checks if an address is in a memory tag page for a live
   process.  */

static bool
linux_process_address_in_memtag_page (CORE_ADDR address)
{
  if (current_inferior ()->fake_pid_p)
    return false;

  pid_t pid = current_inferior ()->pid;

  std::string smaps_file = string_printf ("/proc/%d/smaps", pid);

  gdb::unique_xmalloc_ptr<char> data
    = target_fileio_read_stralloc (NULL, smaps_file.c_str ());

  if (data == nullptr)
    return false;

  /* Parse the contents of smaps into a vector.  */
  std::vector<struct smaps_data> smaps
    = parse_smaps_data (data.get (), smaps_file);

  for (const smaps_data &map : smaps)
    {
      /* Is the address within [start_address, end_address) in a page
	 mapped with memory tagging?  */
      if (address >= map.start_address
	  && address < map.end_address
	  && map.vmflags.memory_tagging)
	return true;
    }

  return false;
}

/* Helper that checks if an address is in a memory tag page for a core file
   process.  */

static bool
linux_core_file_address_in_memtag_page (CORE_ADDR address)
{
  if (current_program_space->core_bfd () == nullptr)
    return false;

  memtag_section_info info;
  return get_next_core_memtag_section (current_program_space->core_bfd (),
				       nullptr, address, info);
}

/* See linux-tdep.h.  */

bool
linux_address_in_memtag_page (CORE_ADDR address)
{
  if (!target_has_execution ())
    return linux_core_file_address_in_memtag_page (address);

  return linux_process_address_in_memtag_page (address);
}

/* List memory regions in the inferior for a corefile.  */

static int
linux_find_memory_regions_full (struct gdbarch *gdbarch,
				linux_dump_mapping_p_ftype *should_dump_mapping_p,
				linux_find_memory_region_ftype *func,
				void *obfd)
{
  pid_t pid;
  /* Default dump behavior of coredump_filter (0x33), according to
     Documentation/filesystems/proc.txt from the Linux kernel
     tree.  */
  filter_flags filterflags = (COREFILTER_ANON_PRIVATE
			      | COREFILTER_ANON_SHARED
			      | COREFILTER_ELF_HEADERS
			      | COREFILTER_HUGETLB_PRIVATE);

  /* We need to know the real target PID to access /proc.  */
  if (current_inferior ()->fake_pid_p)
    return 1;

  pid = current_inferior ()->pid;

  if (use_coredump_filter)
    {
      std::string core_dump_filter_name
	= string_printf ("/proc/%d/coredump_filter", pid);

      gdb::unique_xmalloc_ptr<char> coredumpfilterdata
	= target_fileio_read_stralloc (NULL, core_dump_filter_name.c_str ());

      if (coredumpfilterdata != NULL)
	{
	  unsigned int flags;

	  sscanf (coredumpfilterdata.get (), "%x", &flags);
	  filterflags = (enum filter_flag) flags;
	}
    }

  std::string maps_filename = string_printf ("/proc/%d/smaps", pid);

  gdb::unique_xmalloc_ptr<char> data
    = target_fileio_read_stralloc (NULL, maps_filename.c_str ());

  if (data == NULL)
    {
      /* Older Linux kernels did not support /proc/PID/smaps.  */
      maps_filename = string_printf ("/proc/%d/maps", pid);
      data = target_fileio_read_stralloc (NULL, maps_filename.c_str ());

      if (data == nullptr)
	return 1;
    }

  /* Parse the contents of smaps into a vector.  */
  std::vector<struct smaps_data> smaps
    = parse_smaps_data (data.get (), maps_filename.c_str ());

  for (const struct smaps_data &map : smaps)
    {
      /* Invoke the callback function to create the corefile segment.  */
      if (should_dump_mapping_p (filterflags, map))
	{
	  func (map.start_address, map.end_address - map.start_address,
		map.offset, map.read, map.write, map.exec,
		1, /* MODIFIED is true because we want to dump
		      the mapping.  */
		map.vmflags.memory_tagging != 0,
		map.filename, obfd);
	}
    }

  return 0;
}

/* A structure for passing information through
   linux_find_memory_regions_full.  */

struct linux_find_memory_regions_data
{
  /* The original callback.  */

  find_memory_region_ftype func;

  /* The original datum.  */

  void *obfd;
};

/* A callback for linux_find_memory_regions that converts between the
   "full"-style callback and find_memory_region_ftype.  */

static int
linux_find_memory_regions_thunk (ULONGEST vaddr, ULONGEST size,
				 ULONGEST offset,
				 int read, int write, int exec, int modified,
				 bool memory_tagged,
				 const std::string &filename, void *arg)
{
  struct linux_find_memory_regions_data *data
    = (struct linux_find_memory_regions_data *) arg;

  return data->func (vaddr, size, read, write, exec, modified, memory_tagged,
		     data->obfd);
}

/* A variant of linux_find_memory_regions_full that is suitable as the
   gdbarch find_memory_regions method.  */

static int
linux_find_memory_regions (struct gdbarch *gdbarch,
			   find_memory_region_ftype func, void *obfd)
{
  struct linux_find_memory_regions_data data;

  data.func = func;
  data.obfd = obfd;

  return linux_find_memory_regions_full (gdbarch,
					 dump_mapping_p,
					 linux_find_memory_regions_thunk,
					 &data);
}

/* This is used to pass information from
   linux_make_mappings_corefile_notes through
   linux_find_memory_regions_full.  */

struct linux_make_mappings_data
{
  /* Number of files mapped.  */
  ULONGEST file_count;

  /* The obstack for the main part of the data.  */
  struct obstack *data_obstack;

  /* The filename obstack.  */
  struct obstack *filename_obstack;

  /* The architecture's "long" type.  */
  struct type *long_type;
};

/* A callback for linux_find_memory_regions_full that updates the
   mappings data for linux_make_mappings_corefile_notes.

   MEMORY_TAGGED is true if the memory region contains memory tags, false
   otherwise.  */

static int
linux_make_mappings_callback (ULONGEST vaddr, ULONGEST size,
			      ULONGEST offset,
			      int read, int write, int exec, int modified,
			      bool memory_tagged,
			      const std::string &filename, void *data)
{
  struct linux_make_mappings_data *map_data
    = (struct linux_make_mappings_data *) data;
  gdb_byte buf[sizeof (ULONGEST)];

  gdb_assert (filename.length () > 0);

  ++map_data->file_count;

  pack_long (buf, map_data->long_type, vaddr);
  obstack_grow (map_data->data_obstack, buf, map_data->long_type->length ());
  pack_long (buf, map_data->long_type, vaddr + size);
  obstack_grow (map_data->data_obstack, buf, map_data->long_type->length ());
  pack_long (buf, map_data->long_type, offset);
  obstack_grow (map_data->data_obstack, buf, map_data->long_type->length ());

  obstack_grow_str0 (map_data->filename_obstack, filename.c_str ());

  return 0;
}

/* Write the file mapping data to the core file, if possible.  OBFD is
   the output BFD.  NOTE_DATA is the current note data, and NOTE_SIZE
   is a pointer to the note size.  Updates NOTE_DATA and NOTE_SIZE.  */

static void
linux_make_mappings_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
				    gdb::unique_xmalloc_ptr<char> &note_data,
				    int *note_size)
{
  struct linux_make_mappings_data mapping_data;
  type_allocator alloc (gdbarch);
  struct type *long_type
    = init_integer_type (alloc, gdbarch_long_bit (gdbarch), 0, "long");
  gdb_byte buf[sizeof (ULONGEST)];

  auto_obstack data_obstack, filename_obstack;

  mapping_data.file_count = 0;
  mapping_data.data_obstack = &data_obstack;
  mapping_data.filename_obstack = &filename_obstack;
  mapping_data.long_type = long_type;

  /* Reserve space for the count.  */
  obstack_blank (&data_obstack, long_type->length ());
  /* We always write the page size as 1 since we have no good way to
     determine the correct value.  */
  pack_long (buf, long_type, 1);
  obstack_grow (&data_obstack, buf, long_type->length ());

  linux_find_memory_regions_full (gdbarch, 
				  dump_note_entry_p,
				  linux_make_mappings_callback,
				  &mapping_data);

  if (mapping_data.file_count != 0)
    {
      /* Write the count to the obstack.  */
      pack_long ((gdb_byte *) obstack_base (&data_obstack),
		 long_type, mapping_data.file_count);

      /* Copy the filenames to the data obstack.  */
      int size = obstack_object_size (&filename_obstack);
      obstack_grow (&data_obstack, obstack_base (&filename_obstack),
		    size);

      note_data.reset (elfcore_write_file_note (obfd, note_data.release (), note_size,
						obstack_base (&data_obstack),
						obstack_object_size (&data_obstack)));
    }
}

/* Fetch the siginfo data for the specified thread, if it exists.  If
   there is no data, or we could not read it, return an empty
   buffer.  */

static gdb::byte_vector
linux_get_siginfo_data (thread_info *thread, struct gdbarch *gdbarch)
{
  struct type *siginfo_type;
  LONGEST bytes_read;

  if (!gdbarch_get_siginfo_type_p (gdbarch))
    return gdb::byte_vector ();

  scoped_restore_current_thread save_current_thread;
  switch_to_thread (thread);

  siginfo_type = gdbarch_get_siginfo_type (gdbarch);

  gdb::byte_vector buf (siginfo_type->length ());

  bytes_read = target_read (current_inferior ()->top_target (),
			    TARGET_OBJECT_SIGNAL_INFO, NULL,
			    buf.data (), 0, siginfo_type->length ());
  if (bytes_read != siginfo_type->length ())
    buf.clear ();

  return buf;
}

/* Records the thread's register state for the corefile note
   section.  */

static void
linux_corefile_thread (struct thread_info *info,
		       struct gdbarch *gdbarch, bfd *obfd,
		       gdb::unique_xmalloc_ptr<char> &note_data,
		       int *note_size, gdb_signal stop_signal)
{
  gcore_elf_build_thread_register_notes (gdbarch, info, stop_signal, obfd,
					 &note_data, note_size);

  /* Don't return anything if we got no register information above,
     such a core file is useless.  */
  if (note_data != nullptr)
    {
      gdb::byte_vector siginfo_data
	= linux_get_siginfo_data (info, gdbarch);
      if (!siginfo_data.empty ())
	note_data.reset (elfcore_write_note (obfd, note_data.release (),
					     note_size, "CORE", NT_SIGINFO,
					     siginfo_data.data (),
					     siginfo_data.size ()));
    }
}

/* Try to extract the inferior arguments, environment, and executable name
   from core file CBFD.  */

static core_file_exec_context
linux_corefile_parse_exec_context_1 (struct gdbarch *gdbarch, bfd *cbfd)
{
  gdb_assert (gdbarch != nullptr);

  /* If there's no core file loaded then we're done.  */
  if (cbfd == nullptr)
    return {};

  /* This function (currently) assumes the stack grows down.  If this is
     not the case then this function isn't going to help.  */
  if (!gdbarch_stack_grows_down (gdbarch))
    return {};

  int ptr_bytes = gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT;

  /* Find the .auxv section in the core file. The BFD library creates this
     for us from the AUXV note when the BFD is opened.  If the section
     can't be found then there's nothing more we can do.  */
  struct bfd_section * section = bfd_get_section_by_name (cbfd, ".auxv");
  if (section == nullptr)
    return {};

  /* Grab the contents of the .auxv section.  If we can't get the contents
     then there's nothing more we can do.  */
  bfd_size_type size = bfd_section_size (section);
  if (bfd_section_size_insane (cbfd, section))
    return {};
  gdb::byte_vector contents (size);
  if (!bfd_get_section_contents (cbfd, section, contents.data (), 0, size))
    return {};

  /* Parse the .auxv section looking for the AT_EXECFN attribute.  The
     value of this attribute is a pointer to a string, the string is the
     executable command.  Additionally, this string is placed at the top of
     the program stack, and so will be in the same PT_LOAD segment as the
     argv and envp arrays.  We can use this to try and locate these arrays.
     If we can't find the AT_EXECFN attribute then we're not going to be
     able to do anything else here.  */
  CORE_ADDR execfn_string_addr;
  if (target_auxv_search (contents, current_inferior ()->top_target (),
			  gdbarch, AT_EXECFN, &execfn_string_addr) != 1)
    return {};

  /* Read in the program headers from CBFD.  If we can't do this for any
     reason then just give up.  */
  long phdrs_size = bfd_get_elf_phdr_upper_bound (cbfd);
  if (phdrs_size == -1)
    return {};
  gdb::unique_xmalloc_ptr<Elf_Internal_Phdr>
    phdrs ((Elf_Internal_Phdr *) xmalloc (phdrs_size));
  int num_phdrs = bfd_get_elf_phdrs (cbfd, phdrs.get ());
  if (num_phdrs == -1)
    return {};

  /* Now scan through the headers looking for the one which contains the
     address held in EXECFN_STRING_ADDR, this is the address of the
     executable command pointed too by the AT_EXECFN auxv entry.  */
  Elf_Internal_Phdr *hdr = nullptr;
  for (int i = 0; i < num_phdrs; i++)
    {
      /* The program header that contains the address EXECFN_STRING_ADDR
	 should be one where all content is contained within CBFD, hence
	 the check that the file size matches the memory size.  */
      if (phdrs.get ()[i].p_type == PT_LOAD
	  && phdrs.get ()[i].p_vaddr <= execfn_string_addr
	  && (phdrs.get ()[i].p_vaddr
	      + phdrs.get ()[i].p_memsz) > execfn_string_addr
	  && phdrs.get ()[i].p_memsz == phdrs.get ()[i].p_filesz)
	{
	  hdr = &phdrs.get ()[i];
	  break;
	}
    }

  /* If we failed to find a suitable program header then give up.  */
  if (hdr == nullptr)
    return {};

  /* As we assume the stack grows down (see early check in this function)
     we know that the information we are looking for sits somewhere between
     EXECFN_STRING_ADDR and the segments virtual address.  These define
     the HIGH and LOW addresses between which we are going to search.  */
  CORE_ADDR low = hdr->p_vaddr;
  CORE_ADDR high = execfn_string_addr;

  /* This PTR is going to be the address we are currently accessing.  */
  CORE_ADDR ptr = align_down (high, ptr_bytes);

  /* Setup DEREF a helper function which loads a value from an address.
     The returned value is always placed into a uint64_t, even if we only
     load 4-bytes, this allows the code below to be pretty generic.  All
     the values we're dealing with are unsigned, so this should be OK.   */
  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
  const auto deref = [=] (CORE_ADDR p) -> uint64_t
    {
      ULONGEST value = read_memory_unsigned_integer (p, ptr_bytes, byte_order);
      return (uint64_t) value;
    };

  /* Now search down through memory looking for a PTR_BYTES sized object
     which contains the value EXECFN_STRING_ADDR.  The hope is that this
     will be the AT_EXECFN entry in the auxv table.  There is no guarantee
     that we'll find the auxv table this way, but we will do our best to
     validate that what we find is the auxv table, see below.  */
  while (ptr > low)
    {
      if (deref (ptr) == execfn_string_addr
	  && (ptr - ptr_bytes) > low
	  && deref (ptr - ptr_bytes) == AT_EXECFN)
	break;

      ptr -= ptr_bytes;
    }

  /* If we reached the lower bound then we failed -- bail out.  */
  if (ptr <= low)
    return {};

  /* Assuming that we are looking at a value field in the auxv table, move
     forward PTR_BYTES bytes so we are now looking at the next key field in
     the auxv table, then scan forward until we find the null entry which
     will be the last entry in the auxv table.  */
  ptr += ptr_bytes;
  while ((ptr + (2 * ptr_bytes)) < high
	 && (deref (ptr) != 0 || deref (ptr + ptr_bytes) != 0))
    ptr += (2 * ptr_bytes);

  /* PTR now points to the null entry in the auxv table, or we think it
     does.  Now we want to find the start of the auxv table.  There's no
     in-memory pattern we can search for at the start of the table, but
     we can find the start based on the size of the .auxv section within
     the core file CBFD object.  In the actual core file the auxv is held
     in a note, but the bfd library makes this into a section for us.

     The addition of (2 * PTR_BYTES) here is because PTR is pointing at the
     null entry, but the null entry is also included in CONTENTS.  */
  ptr = ptr + (2 * ptr_bytes) - contents.size ();

  /* If we reached the lower bound then we failed -- bail out.  */
  if (ptr <= low)
    return {};

  /* PTR should now be pointing to the start of the auxv table mapped into
     the inferior memory.  As we got here using a heuristic then lets
     compare an auxv table sized block of inferior memory, if this matches
     then it's not a guarantee that we are in the right place, but it does
     make it more likely.  */
  gdb::byte_vector target_contents (size);
  if (target_read_memory (ptr, target_contents.data (), size) != 0)
    memory_error (TARGET_XFER_E_IO, ptr);
  if (memcmp (contents.data (), target_contents.data (), size) != 0)
    return {};

  /* We have reasonable confidence that PTR points to the start of the auxv
     table.  Below this should be the null terminated list of pointers to
     environment strings, and below that the null terminated list of
     pointers to arguments strings.  After that we should find the
     argument count.  First, check for the null at the end of the
     environment list.  */
  if (deref (ptr - ptr_bytes) != 0)
    return {};

  ptr -= (2 * ptr_bytes);
  while (ptr > low && deref (ptr) != 0)
    ptr -= ptr_bytes;

  /* If we reached the lower bound then we failed -- bail out.  */
  if (ptr <= low)
    return {};

  /* PTR is now pointing to the null entry at the end of the argument
     string pointer list.  We now want to scan backward to find the entire
     argument list.  There's no handy null marker that we can look for
     here, instead, as we scan backward we look for the argument count
     (argc) value which appears immediately before the argument list.

     Technically, we could have zero arguments, so the argument count would
     be zero, however, we don't support this case.  If we find a null entry
     in the argument list before we find the argument count then we just
     bail out.

     Start by moving to the last argument string pointer, we expect this
     to be non-null.  */
  ptr -= ptr_bytes;
  uint64_t argc = 0;
  while (ptr > low)
    {
      uint64_t val = deref (ptr);
      if (val == 0)
	return {};

      if (val == argc)
	break;

      /* For GNU/Linux on ARM, glibc removes argc from the stack and
	 replaces it with the "stack-limit".  This actually means a pointer
	 to the first argument string.  This is unfortunate, but we can
	 still detect this case.  */
      if (val == (ptr + ptr_bytes))
	break;

      argc++;
      ptr -= ptr_bytes;
    }

  /* If we reached the lower bound then we failed -- bail out.  */
  if (ptr <= low)
    return {};

  /* PTR is now pointing at the argument count value (or where the argument
     count should be, see notes on ARM above).  Move it forward so we're
     pointing at the first actual argument string pointer.  */
  ptr += ptr_bytes;

  /* We can now parse all of the argument strings.  */
  std::vector<gdb::unique_xmalloc_ptr<char>> arguments;

  /* Skip the first argument.  This is the executable command, but we'll
     load that separately later.  */
  ptr += ptr_bytes;

  uint64_t v;
  while ((v = deref (ptr)) != 0)
    {
      gdb::unique_xmalloc_ptr<char> str = target_read_string (v, INT_MAX);
      if (str == nullptr)
	return {};
      arguments.emplace_back (std::move (str));
      ptr += ptr_bytes;
    }

  /* Skip the null-pointer at the end of the argument list.  We will now
     be pointing at the first environment string.  */
  ptr += ptr_bytes;

  /* Parse the environment strings.  */
  std::vector<gdb::unique_xmalloc_ptr<char>> environment;
  while ((v = deref (ptr)) != 0)
    {
      gdb::unique_xmalloc_ptr<char> str = target_read_string (v, INT_MAX);
      if (str == nullptr)
	return {};
      environment.emplace_back (std::move (str));
      ptr += ptr_bytes;
    }

  gdb::unique_xmalloc_ptr<char> execfn
    = target_read_string (execfn_string_addr, INT_MAX);
  if (execfn == nullptr)
    return {};

  /* When the core-file was loaded GDB processed the file backed mappings
     (from the NT_FILE note).  One of these should have been for the
     executable.  The AT_EXECFN string might not be an absolute path, but
     the path in NT_FILE will be absolute, though if AT_EXECFN is a
     symlink, then the NT_FILE entry will point to the actual file, not the
     symlink.

     Use the AT_ENTRY address to look for the NT_FILE entry which contains
     that address, this should be the executable.  */
  gdb::unique_xmalloc_ptr<char> exec_filename;
  CORE_ADDR exec_entry_addr;
  if (target_auxv_search (contents, current_inferior ()->top_target (),
			  gdbarch, AT_ENTRY, &exec_entry_addr) == 1)
    {
      std::optional<core_target_mapped_file_info> info
	= core_target_find_mapped_file (nullptr, exec_entry_addr);
      if (info.has_value () && !info->filename ().empty ()
	  && IS_ABSOLUTE_PATH (info->filename ().c_str ()))
	exec_filename = make_unique_xstrdup (info->filename ().c_str ());
    }

  return core_file_exec_context (std::move (execfn),
				 std::move (exec_filename),
				 std::move (arguments),
				 std::move (environment));
}

/* Parse and return execution context details from core file CBFD.  */

static core_file_exec_context
linux_corefile_parse_exec_context (struct gdbarch *gdbarch, bfd *cbfd)
{
  /* Catch and discard memory errors.

     If the core file format is not as we expect then we can easily trigger
     a memory error while parsing the core file.  We don't want this to
     prevent the user from opening the core file; the information provided
     by this function is helpful, but not critical, debugging can continue
     without it.  Instead just give a warning and return an empty context
     object.  */
  try
    {
      return linux_corefile_parse_exec_context_1 (gdbarch, cbfd);
    }
  catch (const gdb_exception_error &ex)
    {
      if (ex.error == MEMORY_ERROR)
	{
	  warning
	    (_("failed to parse execution context from corefile: %s"),
	     ex.message->c_str ());
	  return {};
	}
      else
	throw;
    }
}

/* Fill the PRPSINFO structure with information about the process being
   debugged.  Returns 1 in case of success, 0 for failures.  Please note that
   even if the structure cannot be entirely filled (e.g., GDB was unable to
   gather information about the process UID/GID), this function will still
   return 1 since some information was already recorded.  It will only return
   0 iff nothing can be gathered.  */

static int
linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
{
  /* The filename which we will use to obtain some info about the process.
     We will basically use this to store the `/proc/PID/FILENAME' file.  */
  char filename[100];
  /* The basename of the executable.  */
  const char *basename;
  /* Temporary buffer.  */
  char *tmpstr;
  /* The valid states of a process, according to the Linux kernel.  */
  const char valid_states[] = "RSDTZW";
  /* The program state.  */
  const char *prog_state;
  /* The state of the process.  */
  char pr_sname;
  /* The PID of the program which generated the corefile.  */
  pid_t pid;
  /* Process flags.  */
  unsigned int pr_flag;
  /* Process nice value.  */
  long pr_nice;
  /* The number of fields read by `sscanf'.  */
  int n_fields = 0;

  gdb_assert (p != nullptr);

  /* Obtaining PID and filename.  */
  pid = inferior_ptid.pid ();
  xsnprintf (filename, sizeof (filename), "/proc/%d/cmdline", (int) pid);
  /* The full name of the program which generated the corefile.  */
  gdb_byte *buf = nullptr;
  LONGEST buf_len = target_fileio_read_alloc (nullptr, filename, &buf);
  gdb::unique_xmalloc_ptr<char> fname ((char *)buf);

  if (buf_len < 1 || fname.get () == nullptr || fname.get ()[0] == '\0')
    {
      /* No program name was read, so we won't be able to retrieve more
	 information about the process.  */
      return 0;
    }
  if (fname.get ()[buf_len - 1] != '\0')
    {
      warning (_("target file %s "
		 "does not contain a trailing null character"),
	       filename);
      return 0;
    }

  memset (p, 0, sizeof (*p));

  /* Defining the PID.  */
  p->pr_pid = pid;

  /* Copying the program name.  Only the basename matters.  */
  basename = lbasename (fname.get ());
  strncpy (p->pr_fname, basename, sizeof (p->pr_fname) - 1);
  p->pr_fname[sizeof (p->pr_fname) - 1] = '\0';

  const std::string &infargs = current_inferior ()->args ();

  /* The arguments of the program.  */
  std::string psargs = fname.get ();
  if (!infargs.empty ())
    psargs += ' ' + infargs;

  strncpy (p->pr_psargs, psargs.c_str (), sizeof (p->pr_psargs) - 1);
  p->pr_psargs[sizeof (p->pr_psargs) - 1] = '\0';

  xsnprintf (filename, sizeof (filename), "/proc/%d/stat", (int) pid);
  /* The contents of `/proc/PID/stat'.  */
  gdb::unique_xmalloc_ptr<char> proc_stat_contents
    = target_fileio_read_stralloc (NULL, filename);
  char *proc_stat = proc_stat_contents.get ();

  if (proc_stat == NULL || *proc_stat == '\0')
    {
      /* Despite being unable to read more information about the
	 process, we return 1 here because at least we have its
	 command line, PID and arguments.  */
      return 1;
    }

  /* Ok, we have the stats.  It's time to do a little parsing of the
     contents of the buffer, so that we end up reading what we want.

     The following parsing mechanism is strongly based on the
     information generated by the `fs/proc/array.c' file, present in
     the Linux kernel tree.  More details about how the information is
     displayed can be obtained by seeing the manpage of proc(5),
     specifically under the entry of `/proc/[pid]/stat'.  */

  /* Getting rid of the PID, since we already have it.  */
  while (isdigit (*proc_stat))
    ++proc_stat;

  proc_stat = skip_spaces (proc_stat);

  /* ps command also relies on no trailing fields ever contain ')'.  */
  proc_stat = strrchr (proc_stat, ')');
  if (proc_stat == NULL)
    return 1;
  proc_stat++;

  proc_stat = skip_spaces (proc_stat);

  n_fields = sscanf (proc_stat,
		     "%c"		/* Process state.  */
		     "%d%d%d"		/* Parent PID, group ID, session ID.  */
		     "%*d%*d"		/* tty_nr, tpgid (not used).  */
		     "%u"		/* Flags.  */
		     "%*s%*s%*s%*s"	/* minflt, cminflt, majflt,
					   cmajflt (not used).  */
		     "%*s%*s%*s%*s"	/* utime, stime, cutime,
					   cstime (not used).  */
		     "%*s"		/* Priority (not used).  */
		     "%ld",		/* Nice.  */
		     &pr_sname,
		     &p->pr_ppid, &p->pr_pgrp, &p->pr_sid,
		     &pr_flag,
		     &pr_nice);

  if (n_fields != 6)
    {
      /* Again, we couldn't read the complementary information about
	 the process state.  However, we already have minimal
	 information, so we just return 1 here.  */
      return 1;
    }

  /* Filling the structure fields.  */
  prog_state = strchr (valid_states, pr_sname);
  if (prog_state != NULL)
    p->pr_state = prog_state - valid_states;
  else
    {
      /* Zero means "Running".  */
      p->pr_state = 0;
    }

  p->pr_sname = p->pr_state > 5 ? '.' : pr_sname;
  p->pr_zomb = p->pr_sname == 'Z';
  p->pr_nice = pr_nice;
  p->pr_flag = pr_flag;

  /* Finally, obtaining the UID and GID.  For that, we read and parse the
     contents of the `/proc/PID/status' file.  */
  xsnprintf (filename, sizeof (filename), "/proc/%d/status", (int) pid);
  /* The contents of `/proc/PID/status'.  */
  gdb::unique_xmalloc_ptr<char> proc_status_contents
    = target_fileio_read_stralloc (NULL, filename);
  char *proc_status = proc_status_contents.get ();

  if (proc_status == NULL || *proc_status == '\0')
    {
      /* Returning 1 since we already have a bunch of information.  */
      return 1;
    }

  /* Extracting the UID.  */
  tmpstr = strstr (proc_status, "Uid:");
  if (tmpstr != NULL)
    {
      /* Advancing the pointer to the beginning of the UID.  */
      tmpstr += sizeof ("Uid:");
      while (*tmpstr != '\0' && !isdigit (*tmpstr))
	++tmpstr;

      if (isdigit (*tmpstr))
	p->pr_uid = strtol (tmpstr, &tmpstr, 10);
    }

  /* Extracting the GID.  */
  tmpstr = strstr (proc_status, "Gid:");
  if (tmpstr != NULL)
    {
      /* Advancing the pointer to the beginning of the GID.  */
      tmpstr += sizeof ("Gid:");
      while (*tmpstr != '\0' && !isdigit (*tmpstr))
	++tmpstr;

      if (isdigit (*tmpstr))
	p->pr_gid = strtol (tmpstr, &tmpstr, 10);
    }

  return 1;
}

/* Build the note section for a corefile, and return it in a malloc
   buffer.  */

static gdb::unique_xmalloc_ptr<char>
linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
{
  struct elf_internal_linux_prpsinfo prpsinfo;
  gdb::unique_xmalloc_ptr<char> note_data;

  if (! gdbarch_iterate_over_regset_sections_p (gdbarch))
    return NULL;

  if (linux_fill_prpsinfo (&prpsinfo))
    {
      if (gdbarch_ptr_bit (gdbarch) == 64)
	note_data.reset (elfcore_write_linux_prpsinfo64 (obfd,
							 note_data.release (),
							 note_size, &prpsinfo));
      else
	note_data.reset (elfcore_write_linux_prpsinfo32 (obfd,
							 note_data.release (),
							 note_size, &prpsinfo));
    }

  /* Thread register information.  */
  try
    {
      update_thread_list ();
    }
  catch (const gdb_exception_error &e)
    {
      exception_print (gdb_stderr, e);
    }

  /* Like the kernel, prefer dumping the signalled thread first.
     "First thread" is what tools use to infer the signalled
     thread.  */
  thread_info *signalled_thr = gcore_find_signalled_thread ();
  gdb_signal stop_signal;
  if (signalled_thr != nullptr)
    stop_signal = signalled_thr->stop_signal ();
  else
    stop_signal = GDB_SIGNAL_0;

  if (signalled_thr != nullptr)
    {
      /* On some architectures, like AArch64, each thread can have a distinct
	 gdbarch (due to scalable extensions), and using the inferior gdbarch
	 is incorrect.

	 Fetch each thread's gdbarch and pass it down to the lower layers so
	 we can dump the right set of registers.  */
      linux_corefile_thread (signalled_thr,
			     target_thread_architecture (signalled_thr->ptid),
			     obfd, note_data, note_size, stop_signal);
    }
  for (thread_info *thr : current_inferior ()->non_exited_threads ())
    {
      if (thr == signalled_thr)
	continue;

      /* On some architectures, like AArch64, each thread can have a distinct
	 gdbarch (due to scalable extensions), and using the inferior gdbarch
	 is incorrect.

	 Fetch each thread's gdbarch and pass it down to the lower layers so
	 we can dump the right set of registers.  */
      linux_corefile_thread (thr, target_thread_architecture (thr->ptid),
			     obfd, note_data, note_size, stop_signal);
    }

  if (!note_data)
    return NULL;

  /* Auxiliary vector.  */
  std::optional<gdb::byte_vector> auxv =
    target_read_alloc (current_inferior ()->top_target (),
		       TARGET_OBJECT_AUXV, NULL);
  if (auxv && !auxv->empty ())
    {
      note_data.reset (elfcore_write_note (obfd, note_data.release (),
					   note_size, "CORE", NT_AUXV,
					   auxv->data (), auxv->size ()));

      if (!note_data)
	return NULL;
    }

  /* File mappings.  */
  linux_make_mappings_corefile_notes (gdbarch, obfd, note_data, note_size);

  /* Include the target description when possible.  Some architectures
     allow for per-thread gdbarch so we should really be emitting a tdesc
     per-thread, however, we don't currently support reading in a
     per-thread tdesc, so just emit the tdesc for the signalled thread.  */
  gdbarch = target_thread_architecture (signalled_thr->ptid);
  gcore_elf_make_tdesc_note (gdbarch, obfd, &note_data, note_size);

  return note_data;
}

/* Implementation of `gdbarch_gdb_signal_from_target', as defined in
   gdbarch.h.  This function is not static because it is exported to
   other -tdep files.  */

enum gdb_signal
linux_gdb_signal_from_target (struct gdbarch *gdbarch, int signal)
{
  switch (signal)
    {
    case 0:
      return GDB_SIGNAL_0;

    case LINUX_SIGHUP:
      return GDB_SIGNAL_HUP;

    case LINUX_SIGINT:
      return GDB_SIGNAL_INT;

    case LINUX_SIGQUIT:
      return GDB_SIGNAL_QUIT;

    case LINUX_SIGILL:
      return GDB_SIGNAL_ILL;

    case LINUX_SIGTRAP:
      return GDB_SIGNAL_TRAP;

    case LINUX_SIGABRT:
      return GDB_SIGNAL_ABRT;

    case LINUX_SIGBUS:
      return GDB_SIGNAL_BUS;

    case LINUX_SIGFPE:
      return GDB_SIGNAL_FPE;

    case LINUX_SIGKILL:
      return GDB_SIGNAL_KILL;

    case LINUX_SIGUSR1:
      return GDB_SIGNAL_USR1;

    case LINUX_SIGSEGV:
      return GDB_SIGNAL_SEGV;

    case LINUX_SIGUSR2:
      return GDB_SIGNAL_USR2;

    case LINUX_SIGPIPE:
      return GDB_SIGNAL_PIPE;

    case LINUX_SIGALRM:
      return GDB_SIGNAL_ALRM;

    case LINUX_SIGTERM:
      return GDB_SIGNAL_TERM;

    case LINUX_SIGCHLD:
      return GDB_SIGNAL_CHLD;

    case LINUX_SIGCONT:
      return GDB_SIGNAL_CONT;

    case LINUX_SIGSTOP:
      return GDB_SIGNAL_STOP;

    case LINUX_SIGTSTP:
      return GDB_SIGNAL_TSTP;

    case LINUX_SIGTTIN:
      return GDB_SIGNAL_TTIN;

    case LINUX_SIGTTOU:
      return GDB_SIGNAL_TTOU;

    case LINUX_SIGURG:
      return GDB_SIGNAL_URG;

    case LINUX_SIGXCPU:
      return GDB_SIGNAL_XCPU;

    case LINUX_SIGXFSZ:
      return GDB_SIGNAL_XFSZ;

    case LINUX_SIGVTALRM:
      return GDB_SIGNAL_VTALRM;

    case LINUX_SIGPROF:
      return GDB_SIGNAL_PROF;

    case LINUX_SIGWINCH:
      return GDB_SIGNAL_WINCH;

    /* No way to differentiate between SIGIO and SIGPOLL.
       Therefore, we just handle the first one.  */
    case LINUX_SIGIO:
      return GDB_SIGNAL_IO;

    case LINUX_SIGPWR:
      return GDB_SIGNAL_PWR;

    case LINUX_SIGSYS:
      return GDB_SIGNAL_SYS;

    /* SIGRTMIN and SIGRTMAX are not continuous in <gdb/signals.def>,
       therefore we have to handle them here.  */
    case LINUX_SIGRTMIN:
      return GDB_SIGNAL_REALTIME_32;

    case LINUX_SIGRTMAX:
      return GDB_SIGNAL_REALTIME_64;
    }

  if (signal >= LINUX_SIGRTMIN + 1 && signal <= LINUX_SIGRTMAX - 1)
    {
      int offset = signal - LINUX_SIGRTMIN + 1;

      return (enum gdb_signal) ((int) GDB_SIGNAL_REALTIME_33 + offset);
    }

  return GDB_SIGNAL_UNKNOWN;
}

/* Implementation of `gdbarch_gdb_signal_to_target', as defined in
   gdbarch.h.  This function is not static because it is exported to
   other -tdep files.  */

int
linux_gdb_signal_to_target (struct gdbarch *gdbarch,
			    enum gdb_signal signal)
{
  switch (signal)
    {
    case GDB_SIGNAL_0:
      return 0;

    case GDB_SIGNAL_HUP:
      return LINUX_SIGHUP;

    case GDB_SIGNAL_INT:
      return LINUX_SIGINT;

    case GDB_SIGNAL_QUIT:
      return LINUX_SIGQUIT;

    case GDB_SIGNAL_ILL:
      return LINUX_SIGILL;

    case GDB_SIGNAL_TRAP:
      return LINUX_SIGTRAP;

    case GDB_SIGNAL_ABRT:
      return LINUX_SIGABRT;

    case GDB_SIGNAL_FPE:
      return LINUX_SIGFPE;

    case GDB_SIGNAL_KILL:
      return LINUX_SIGKILL;

    case GDB_SIGNAL_BUS:
      return LINUX_SIGBUS;

    case GDB_SIGNAL_SEGV:
      return LINUX_SIGSEGV;

    case GDB_SIGNAL_SYS:
      return LINUX_SIGSYS;

    case GDB_SIGNAL_PIPE:
      return LINUX_SIGPIPE;

    case GDB_SIGNAL_ALRM:
      return LINUX_SIGALRM;

    case GDB_SIGNAL_TERM:
      return LINUX_SIGTERM;

    case GDB_SIGNAL_URG:
      return LINUX_SIGURG;

    case GDB_SIGNAL_STOP:
      return LINUX_SIGSTOP;

    case GDB_SIGNAL_TSTP:
      return LINUX_SIGTSTP;

    case GDB_SIGNAL_CONT:
      return LINUX_SIGCONT;

    case GDB_SIGNAL_CHLD:
      return LINUX_SIGCHLD;

    case GDB_SIGNAL_TTIN:
      return LINUX_SIGTTIN;

    case GDB_SIGNAL_TTOU:
      return LINUX_SIGTTOU;

    case GDB_SIGNAL_IO:
      return LINUX_SIGIO;

    case GDB_SIGNAL_XCPU:
      return LINUX_SIGXCPU;

    case GDB_SIGNAL_XFSZ:
      return LINUX_SIGXFSZ;

    case GDB_SIGNAL_VTALRM:
      return LINUX_SIGVTALRM;

    case GDB_SIGNAL_PROF:
      return LINUX_SIGPROF;

    case GDB_SIGNAL_WINCH:
      return LINUX_SIGWINCH;

    case GDB_SIGNAL_USR1:
      return LINUX_SIGUSR1;

    case GDB_SIGNAL_USR2:
      return LINUX_SIGUSR2;

    case GDB_SIGNAL_PWR:
      return LINUX_SIGPWR;

    case GDB_SIGNAL_POLL:
      return LINUX_SIGPOLL;

    /* GDB_SIGNAL_REALTIME_32 is not continuous in <gdb/signals.def>,
       therefore we have to handle it here.  */
    case GDB_SIGNAL_REALTIME_32:
      return LINUX_SIGRTMIN;

    /* Same comment applies to _64.  */
    case GDB_SIGNAL_REALTIME_64:
      return LINUX_SIGRTMAX;
    }

  /* GDB_SIGNAL_REALTIME_33 to _64 are continuous.  */
  if (signal >= GDB_SIGNAL_REALTIME_33
      && signal <= GDB_SIGNAL_REALTIME_63)
    {
      int offset = signal - GDB_SIGNAL_REALTIME_33;

      return LINUX_SIGRTMIN + 1 + offset;
    }

  return -1;
}

/* Helper for linux_vsyscall_range that does the real work of finding
   the vsyscall's address range.  */

static int
linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
{
  char filename[100];
  long pid;

  if (target_auxv_search (AT_SYSINFO_EHDR, &range->start) <= 0)
    return 0;

  /* It doesn't make sense to access the host's /proc when debugging a
     core file.  Instead, look for the PT_LOAD segment that matches
     the vDSO.  */
  if (!target_has_execution ())
    {
      long phdrs_size;
      int num_phdrs, i;

      phdrs_size
	= bfd_get_elf_phdr_upper_bound (current_program_space->core_bfd ());
      if (phdrs_size == -1)
	return 0;

      gdb::unique_xmalloc_ptr<Elf_Internal_Phdr>
	phdrs ((Elf_Internal_Phdr *) xmalloc (phdrs_size));
      num_phdrs = bfd_get_elf_phdrs (current_program_space->core_bfd (),
				     phdrs.get ());
      if (num_phdrs == -1)
	return 0;

      for (i = 0; i < num_phdrs; i++)
	if (phdrs.get ()[i].p_type == PT_LOAD
	    && phdrs.get ()[i].p_vaddr == range->start)
	  {
	    range->length = phdrs.get ()[i].p_memsz;
	    return 1;
	  }

      return 0;
    }

  /* We need to know the real target PID to access /proc.  */
  if (current_inferior ()->fake_pid_p)
    return 0;

  pid = current_inferior ()->pid;

  /* Note that reading /proc/PID/task/PID/maps (1) is much faster than
     reading /proc/PID/maps (2).  The later identifies thread stacks
     in the output, which requires scanning every thread in the thread
     group to check whether a VMA is actually a thread's stack.  With
     Linux 4.4 on an Intel i7-4810MQ @ 2.80GHz, with an inferior with
     a few thousand threads, (1) takes a few milliseconds, while (2)
     takes several seconds.  Also note that "smaps", what we read for
     determining core dump mappings, is even slower than "maps".  */
  xsnprintf (filename, sizeof filename, "/proc/%ld/task/%ld/maps", pid, pid);
  gdb::unique_xmalloc_ptr<char> data
    = target_fileio_read_stralloc (NULL, filename);
  if (data != NULL)
    {
      char *line;
      char *saveptr = NULL;

      for (line = strtok_r (data.get (), "\n", &saveptr);
	   line != NULL;
	   line = strtok_r (NULL, "\n", &saveptr))
	{
	  ULONGEST addr, endaddr;
	  const char *p = line;

	  addr = strtoulst (p, &p, 16);
	  if (addr == range->start)
	    {
	      if (*p == '-')
		p++;
	      endaddr = strtoulst (p, &p, 16);
	      range->length = endaddr - addr;
	      return 1;
	    }
	}
    }
  else
    warning (_("unable to open /proc file '%s'"), filename);

  return 0;
}

/* Implementation of the "vsyscall_range" gdbarch hook.  Handles
   caching, and defers the real work to linux_vsyscall_range_raw.  */

static int
linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
{
  struct linux_info *info = get_linux_inferior_data (current_inferior ());

  if (info->vsyscall_range_p == 0)
    {
      if (linux_vsyscall_range_raw (gdbarch, &info->vsyscall_range))
	info->vsyscall_range_p = 1;
      else
	info->vsyscall_range_p = -1;
    }

  if (info->vsyscall_range_p < 0)
    return 0;

  *range = info->vsyscall_range;
  return 1;
}

/* Symbols for linux_infcall_mmap's ARG_FLAGS; their Linux MAP_* system
   definitions would be dependent on compilation host.  */
#define GDB_MMAP_MAP_PRIVATE	0x02		/* Changes are private.  */
#define GDB_MMAP_MAP_ANONYMOUS	0x20		/* Don't use a file.  */

/* See gdbarch.sh 'infcall_mmap'.  */

static CORE_ADDR
linux_infcall_mmap (CORE_ADDR size, unsigned prot)
{
  struct objfile *objf;
  /* Do there still exist any Linux systems without "mmap64"?
     "mmap" uses 64-bit off_t on x86_64 and 32-bit off_t on i386 and x32.  */
  struct value *mmap_val = find_function_in_inferior ("mmap64", &objf);
  struct value *addr_val;
  struct gdbarch *gdbarch = objf->arch ();
  CORE_ADDR retval;
  enum
    {
      ARG_ADDR, ARG_LENGTH, ARG_PROT, ARG_FLAGS, ARG_FD, ARG_OFFSET, ARG_LAST
    };
  struct value *arg[ARG_LAST];

  arg[ARG_ADDR] = value_from_pointer (builtin_type (gdbarch)->builtin_data_ptr,
				      0);
  /* Assuming sizeof (unsigned long) == sizeof (size_t).  */
  arg[ARG_LENGTH] = value_from_ulongest
		    (builtin_type (gdbarch)->builtin_unsigned_long, size);
  gdb_assert ((prot & ~(GDB_MMAP_PROT_READ | GDB_MMAP_PROT_WRITE
			| GDB_MMAP_PROT_EXEC))
	      == 0);
  arg[ARG_PROT] = value_from_longest (builtin_type (gdbarch)->builtin_int, prot);
  arg[ARG_FLAGS] = value_from_longest (builtin_type (gdbarch)->builtin_int,
				       GDB_MMAP_MAP_PRIVATE
				       | GDB_MMAP_MAP_ANONYMOUS);
  arg[ARG_FD] = value_from_longest (builtin_type (gdbarch)->builtin_int, -1);
  arg[ARG_OFFSET] = value_from_longest (builtin_type (gdbarch)->builtin_int64,
					0);
  addr_val = call_function_by_hand (mmap_val, NULL, arg);
  retval = value_as_address (addr_val);
  if (retval == (CORE_ADDR) -1)
    error (_("Failed inferior mmap call for %s bytes, errno is changed."),
	   pulongest (size));
  return retval;
}

/* See gdbarch.sh 'infcall_munmap'.  */

static void
linux_infcall_munmap (CORE_ADDR addr, CORE_ADDR size)
{
  struct objfile *objf;
  struct value *munmap_val = find_function_in_inferior ("munmap", &objf);
  struct value *retval_val;
  struct gdbarch *gdbarch = objf->arch ();
  LONGEST retval;
  enum
    {
      ARG_ADDR, ARG_LENGTH, ARG_LAST
    };
  struct value *arg[ARG_LAST];

  arg[ARG_ADDR] = value_from_pointer (builtin_type (gdbarch)->builtin_data_ptr,
				      addr);
  /* Assuming sizeof (unsigned long) == sizeof (size_t).  */
  arg[ARG_LENGTH] = value_from_ulongest
		    (builtin_type (gdbarch)->builtin_unsigned_long, size);
  retval_val = call_function_by_hand (munmap_val, NULL, arg);
  retval = value_as_long (retval_val);
  if (retval != 0)
    warning (_("Failed inferior munmap call at %s for %s bytes, "
	       "errno is changed."),
	     hex_string (addr), pulongest (size));
}

/* See linux-tdep.h.  */

CORE_ADDR
linux_displaced_step_location (struct gdbarch *gdbarch)
{
  CORE_ADDR addr;
  int bp_len;

  /* Determine entry point from target auxiliary vector.  This avoids
     the need for symbols.  Also, when debugging a stand-alone SPU
     executable, entry_point_address () will point to an SPU
     local-store address and is thus not usable as displaced stepping
     location.  The auxiliary vector gets us the PowerPC-side entry
     point address instead.  */
  if (target_auxv_search (AT_ENTRY, &addr) <= 0)
    throw_error (NOT_SUPPORTED_ERROR,
		 _("Cannot find AT_ENTRY auxiliary vector entry."));

  /* Make certain that the address points at real code, and not a
     function descriptor.  */
  addr = gdbarch_convert_from_func_ptr_addr
    (gdbarch, addr, current_inferior ()->top_target ());

  /* Inferior calls also use the entry point as a breakpoint location.
     We don't want displaced stepping to interfere with those
     breakpoints, so leave space.  */
  gdbarch_breakpoint_from_pc (gdbarch, &addr, &bp_len);
  addr += bp_len * 2;

  return addr;
}

/* See linux-tdep.h.  */

displaced_step_prepare_status
linux_displaced_step_prepare (gdbarch *arch, thread_info *thread,
			      CORE_ADDR &displaced_pc)
{
  linux_info *per_inferior = get_linux_inferior_data (thread->inf);

  if (!per_inferior->disp_step_bufs.has_value ())
    {
      /* Figure out the location of the buffers.  They are contiguous, starting
	 at DISP_STEP_BUF_ADDR.  They are all of size BUF_LEN.  */
      CORE_ADDR disp_step_buf_addr
	= linux_displaced_step_location (thread->inf->arch ());
      int buf_len = gdbarch_displaced_step_buffer_length (arch);

      linux_gdbarch_data *gdbarch_data = get_linux_gdbarch_data (arch);
      gdb_assert (gdbarch_data->num_disp_step_buffers > 0);

      std::vector<CORE_ADDR> buffers;
      for (int i = 0; i < gdbarch_data->num_disp_step_buffers; i++)
	buffers.push_back (disp_step_buf_addr + i * buf_len);