1/* KVX-specific support for ELF.
2   Copyright (C) 2009-2024 Free Software Foundation, Inc.
3   Contributed by Kalray SA.
4
5   This file is part of BFD, the Binary File Descriptor library.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; see the file COPYING3. If not,
19   see <http://www.gnu.org/licenses/>.  */
20
21#include "sysdep.h"
22#include "elfxx-kvx.h"
23#include <stdarg.h>
24#include <string.h>
25
26/* Return non-zero if the indicated VALUE has overflowed the maximum
27   range expressible by a unsigned number with the indicated number of
28   BITS.  */
29
30static bfd_reloc_status_type
31kvx_unsigned_overflow (bfd_vma value, unsigned int bits)
32{
33  bfd_vma lim;
34  if (bits >= sizeof (bfd_vma) * 8)
35    return bfd_reloc_ok;
36  lim = (bfd_vma) 1 << bits;
37  if (value >= lim)
38    return bfd_reloc_overflow;
39  return bfd_reloc_ok;
40}
41
42/* Return non-zero if the indicated VALUE has overflowed the maximum
43   range expressible by an signed number with the indicated number of
44   BITS.  */
45
46static bfd_reloc_status_type
47kvx_signed_overflow (bfd_vma value, unsigned int bits)
48{
49  bfd_vma lim;
50
51  if (bits >= sizeof (bfd_vma) * 8)
52    return bfd_reloc_ok;
53  lim = (bfd_vma) 1 << (bits - 1);
54  if (value + lim >= lim * 2)
55    return bfd_reloc_overflow;
56  return bfd_reloc_ok;
57}
58
59/* Insert the addend/value into the instruction or data object being
60   relocated.  */
61bfd_reloc_status_type
62_bfd_kvx_elf_put_addend (bfd *abfd,
63			 bfd_byte *address,
64			 bfd_reloc_code_real_type r_type ATTRIBUTE_UNUSED,
65			 reloc_howto_type *howto,
66			 bfd_signed_vma addend)
67{
68  bfd_reloc_status_type status = bfd_reloc_ok;
69  bfd_vma contents;
70  int size;
71
72  size = bfd_get_reloc_size (howto);
73  switch (size)
74    {
75    case 2:
76      contents = bfd_get_16 (abfd, address);
77      break;
78    case 4:
79      if (howto->src_mask != 0xffffffff)
80	/* Must be 32-bit instruction, always little-endian.  */
81	contents = bfd_getl32 (address);
82      else
83	/* Must be 32-bit data (endianness dependent).  */
84	contents = bfd_get_32 (abfd, address);
85      break;
86    case 8:
87      contents = bfd_get_64 (abfd, address);
88      break;
89    default:
90      abort ();
91    }
92
93  switch (howto->complain_on_overflow)
94    {
95    case complain_overflow_dont:
96      break;
97    case complain_overflow_signed:
98      status = kvx_signed_overflow (addend,
99				    howto->bitsize + howto->rightshift);
100      break;
101    case complain_overflow_unsigned:
102      status = kvx_unsigned_overflow (addend,
103				      howto->bitsize + howto->rightshift);
104      break;
105    case complain_overflow_bitfield:
106    default:
107      abort ();
108    }
109
110  addend >>= howto->rightshift;
111
112  /* FIXME KVX : AARCH64 is "redoing" what the link_relocate bfd
113   * function does ie. extract bitfields and apply then to the
114   * existing content (insn) (howto's job) Not sure exactly
115   * why. Maybe because we need this even when not applying reloc
116   * against a input_bfd (eg. when doing PLT).  On KVX, we have not
117   * reached a point where we would need to write similar
118   * functions for each insn. So we'll simply enrich the default
119   * case for handling a bit more than "right aligned bitfields"
120   *
121   * Beware that this won't be able to apply generic howto !
122   */
123
124  /* if (howto->dst_mask & (howto->dst_mask + 1)) */
125  /* 	return bfd_reloc_notsupported; */
126  addend <<= howto->bitpos;
127  contents = ((contents & ~howto->dst_mask) | (addend & howto->dst_mask));
128
129  switch (size)
130    {
131    case 2:
132      bfd_put_16 (abfd, contents, address);
133      break;
134    case 4:
135      if (howto->dst_mask != 0xffffffff)
136	/* must be 32-bit instruction, always little-endian */
137	bfd_putl32 (contents, address);
138      else
139	/* must be 32-bit data (endianness dependent) */
140	bfd_put_32 (abfd, contents, address);
141      break;
142    case 8:
143      bfd_put_64 (abfd, contents, address);
144      break;
145    default:
146      abort ();
147    }
148
149  return status;
150}
151
152bool
153_bfd_kvx_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
154{
155  int offset;
156  size_t size;
157
158  switch (note->descsz)
159    {
160    case 680: /* sizeof(struct elf_prstatus) on Linux/kvx.  */
161      /* pr_cursig */
162      elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12);
163
164      /* pr_pid */
165      elf_tdata (abfd)->core->lwpid = bfd_get_32 (abfd, note->descdata + 32);
166
167      /* pr_reg */
168      offset = 112;
169      size = 560;
170      break;
171
172    default:
173      return false;
174    }
175
176  /* Make a ".reg/999" section.  */
177  return _bfd_elfcore_make_pseudosection (abfd, ".reg", size,
178					  note->descpos + offset);
179}
180
181bool
182_bfd_kvx_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
183{
184  switch (note->descsz)
185    {
186    case 136: /* This is sizeof(struct elf_prpsinfo) on Linux/kvx.  */
187      elf_tdata (abfd)->core->pid = bfd_get_32 (abfd, note->descdata + 24);
188      elf_tdata (abfd)->core->program
189	= _bfd_elfcore_strndup (abfd, note->descdata + 40, 16);
190      elf_tdata (abfd)->core->command
191	= _bfd_elfcore_strndup (abfd, note->descdata + 56, 80);
192      break;
193
194    default:
195      return false;
196    }
197
198  /* Note that for some reason, a spurious space is tacked
199     onto the end of the args in some (at least one anyway)
200     implementations, so strip it off if it exists.  */
201
202  {
203    char *command = elf_tdata (abfd)->core->command;
204    int n = strlen (command);
205
206    if (n > 0 && command[n - 1] == ' ')
207      command[n - 1] = 0;
208  }
209
210  return true;
211}
212