Coverage Report

Created: 2025-06-24 06:45

/src/binutils-gdb/bfd/coff-z80.c
Line
Count
Source (jump to first uncovered line)
1
/* BFD back-end for Zilog Z80 COFF binaries.
2
   Copyright (C) 2005-2025 Free Software Foundation, Inc.
3
   Contributed by Arnold Metselaar <arnold_m@operamail.com>
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; if not, write to the Free Software
19
   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20
   MA 02110-1301, USA.  */
21
22
#include "sysdep.h"
23
#include "bfd.h"
24
#include "libbfd.h"
25
#include "bfdlink.h"
26
#include "coff/z80.h"
27
#include "coff/internal.h"
28
#include "libcoff.h"
29
#include "libiberty.h"
30
31
3.88M
#define COFF_DEFAULT_SECTION_ALIGNMENT_POWER 0
32
33
typedef const struct {
34
  bfd_reloc_code_real_type r_type;
35
  reloc_howto_type howto;
36
} bfd_howto_type;
37
38
#define BFD_EMPTY_HOWTO(rt,x) {rt, EMPTY_HOWTO(x)}
39
#define BFD_HOWTO(rt,a,b,c,d,e,f,g,h,i,j,k,l,m) {rt, HOWTO(a,b,c,d,e,f,g,h,i,j,k,l,m)}
40
41
static bfd_howto_type howto_table[] =
42
{
43
  BFD_EMPTY_HOWTO (BFD_RELOC_NONE, 0),
44
45
  BFD_HOWTO (BFD_RELOC_32,
46
     R_IMM32,   /* type */
47
     0,     /* rightshift */
48
     4,     /* size */
49
     32,    /* bitsize */
50
     false,   /* pc_relative */
51
     0,     /* bitpos */
52
     complain_overflow_bitfield, /* complain_on_overflow */
53
     0,     /* special_function */
54
     "r_imm32",   /* name */
55
     false,   /* partial_inplace */
56
     0xffffffff,  /* src_mask */
57
     0xffffffff,  /* dst_mask */
58
     false),    /* pcrel_offset */
59
60
  BFD_HOWTO (BFD_RELOC_24,
61
     R_IMM24,   /* type */
62
     0,     /* rightshift */
63
     3,     /* size */
64
     24,    /* bitsize */
65
     false,   /* pc_relative */
66
     0,     /* bitpos */
67
     complain_overflow_bitfield, /* complain_on_overflow */
68
     0,     /* special_function */
69
     "r_imm24",   /* name */
70
     false,   /* partial_inplace */
71
     0x00ffffff,  /* src_mask */
72
     0x00ffffff,  /* dst_mask */
73
     false),    /* pcrel_offset */
74
75
  BFD_HOWTO (BFD_RELOC_16,
76
     R_IMM16,   /* type */
77
     0,     /* rightshift */
78
     2,     /* size */
79
     16,    /* bitsize */
80
     false,   /* pc_relative */
81
     0,     /* bitpos */
82
     complain_overflow_bitfield, /* complain_on_overflow */
83
     0,     /* special_function */
84
     "r_imm16",   /* name */
85
     false,   /* partial_inplace */
86
     0x0000ffff,  /* src_mask */
87
     0x0000ffff,  /* dst_mask */
88
     false),    /* pcrel_offset */
89
90
  BFD_HOWTO (BFD_RELOC_8,
91
     R_IMM8,    /* type */
92
     0,     /* rightshift */
93
     1,     /* size */
94
     8,     /* bitsize */
95
     false,   /* pc_relative */
96
     0,     /* bitpos */
97
     complain_overflow_bitfield, /* complain_on_overflow */
98
     0,     /* special_function */
99
     "r_imm8",    /* name */
100
     false,   /* partial_inplace */
101
     0x000000ff,  /* src_mask */
102
     0x000000ff,  /* dst_mask */
103
     false),    /* pcrel_offset */
104
105
  BFD_HOWTO (BFD_RELOC_8_PCREL,
106
     R_JR,    /* type */
107
     0,     /* rightshift */
108
     1,     /* size */
109
     8,     /* bitsize */
110
     true,    /* pc_relative */
111
     0,     /* bitpos */
112
     complain_overflow_signed, /* complain_on_overflow */
113
     0,     /* special_function */
114
     "r_jr",    /* name */
115
     false,   /* partial_inplace */
116
     0,     /* src_mask */
117
     0xFF,    /* dst_mask */
118
     true),   /* pcrel_offset */
119
120
  BFD_HOWTO (BFD_RELOC_Z80_DISP8,
121
     R_OFF8,    /* type */
122
     0,     /* rightshift */
123
     1,     /* size */
124
     8,     /* bitsize */
125
     false,   /* pc_relative */
126
     0,     /* bitpos */
127
     complain_overflow_signed, /* complain_on_overflow */
128
     0,     /* special_function */
129
     "r_off8",    /* name */
130
     false,   /* partial_inplace */
131
     0,     /* src_mask */
132
     0xff,    /* dst_mask */
133
     false),    /* pcrel_offset */
134
135
  BFD_HOWTO (BFD_RELOC_Z80_BYTE0,
136
     R_BYTE0,   /* type */
137
     0,     /* rightshift */
138
     1,     /* size */
139
     8,     /* bitsize */
140
     false,   /* pc_relative */
141
     0,     /* bitpos */
142
     complain_overflow_dont, /* complain_on_overflow */
143
     0,     /* special_function */
144
     "r_byte0",   /* name */
145
     false,   /* partial_inplace */
146
     0,     /* src_mask */
147
     0xff,    /* dst_mask */
148
     false),    /* pcrel_offset */
149
150
  BFD_HOWTO (BFD_RELOC_Z80_BYTE1,
151
     R_BYTE1,   /* type */
152
     8,     /* rightshift */
153
     1,     /* size */
154
     8,     /* bitsize */
155
     false,   /* pc_relative */
156
     0,     /* bitpos */
157
     complain_overflow_dont, /* complain_on_overflow */
158
     0,     /* special_function */
159
     "r_byte1",   /* name */
160
     false,   /* partial_inplace */
161
     0,     /* src_mask */
162
     0xff,    /* dst_mask */
163
     false),    /* pcrel_offset */
164
165
  BFD_HOWTO (BFD_RELOC_Z80_BYTE2,
166
     R_BYTE2,   /* type */
167
     16,    /* rightshift */
168
     1,     /* size */
169
     8,     /* bitsize */
170
     false,   /* pc_relative */
171
     0,     /* bitpos */
172
     complain_overflow_dont, /* complain_on_overflow */
173
     0,     /* special_function */
174
     "r_byte2",   /* name */
175
     false,   /* partial_inplace */
176
     0,     /* src_mask */
177
     0xff,    /* dst_mask */
178
     false),    /* pcrel_offset */
179
180
  BFD_HOWTO (BFD_RELOC_Z80_BYTE3,
181
     R_BYTE3,   /* type */
182
     24,    /* rightshift */
183
     1,     /* size */
184
     8,     /* bitsize */
185
     false,   /* pc_relative */
186
     0,     /* bitpos */
187
     complain_overflow_dont, /* complain_on_overflow */
188
     0,     /* special_function */
189
     "r_byte3",   /* name */
190
     false,   /* partial_inplace */
191
     0,     /* src_mask */
192
     0xff,    /* dst_mask */
193
     false),    /* pcrel_offset */
194
195
  BFD_HOWTO (BFD_RELOC_Z80_WORD0,
196
     R_WORD0,   /* type */
197
     0,     /* rightshift */
198
     2,     /* size */
199
     16,    /* bitsize */
200
     false,   /* pc_relative */
201
     0,     /* bitpos */
202
     complain_overflow_dont, /* complain_on_overflow */
203
     0,     /* special_function */
204
     "r_word0",   /* name */
205
     false,   /* partial_inplace */
206
     0,     /* src_mask */
207
     0xffff,    /* dst_mask */
208
     false),    /* pcrel_offset */
209
210
  BFD_HOWTO (BFD_RELOC_Z80_WORD1,
211
     R_WORD1,   /* type */
212
     16,    /* rightshift */
213
     2,     /* size */
214
     16,    /* bitsize */
215
     false,   /* pc_relative */
216
     0,     /* bitpos */
217
     complain_overflow_dont, /* complain_on_overflow */
218
     0,     /* special_function */
219
     "r_word1",   /* name */
220
     false,   /* partial_inplace */
221
     0,     /* src_mask */
222
     0xffff,    /* dst_mask */
223
     false),    /* pcrel_offset */
224
225
  BFD_HOWTO (BFD_RELOC_Z80_16_BE,
226
     R_IMM16BE,   /* type */
227
     0,     /* rightshift */
228
     2,     /* size */
229
     16,    /* bitsize */
230
     false,   /* pc_relative */
231
     0,     /* bitpos */
232
     complain_overflow_bitfield, /* complain_on_overflow */
233
     0,     /* special_function */
234
     "r_imm16be", /* name */
235
     false,   /* partial_inplace */
236
     0x0000ffff,  /* src_mask */
237
     0x0000ffff,  /* dst_mask */
238
     false),    /* pcrel_offset */
239
};
240
241
51.0k
#define NUM_HOWTOS ARRAY_SIZE (howto_table)
242
243
3.35M
#define BADMAG(x) Z80BADMAG(x)
244
#define Z80 1     /* Customize coffcode.h.  */
245
#define __A_MAGIC_SET__
246
247
/* Code to swap in the reloc.  */
248
249
29.7k
#define SWAP_IN_RELOC_OFFSET  H_GET_32
250
0
#define SWAP_OUT_RELOC_OFFSET H_PUT_32
251
252
#define SWAP_OUT_RELOC_EXTRA(abfd, src, dst) \
253
0
  dst->r_stuff[0] = 'S'; \
254
0
  dst->r_stuff[1] = 'C';
255
256
/* Code to turn a r_type into a howto ptr, uses the above howto table.  */
257
static void
258
rtype2howto (arelent *internal, struct internal_reloc *dst)
259
29.7k
{
260
29.7k
  unsigned i;
261
51.0k
  for (i = 0; i < NUM_HOWTOS; i++)
262
49.7k
    {
263
49.7k
      if (howto_table[i].howto.type == dst->r_type)
264
28.4k
        {
265
28.4k
          internal->howto = &howto_table[i].howto;
266
28.4k
          return;
267
28.4k
        }
268
49.7k
    }
269
1.29k
  internal->howto = NULL;
270
1.29k
}
271
272
0
#define RTYPE2HOWTO(internal, relocentry) rtype2howto (internal, relocentry)
273
274
static reloc_howto_type *
275
coff_z80_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
276
          bfd_reloc_code_real_type code)
277
0
{
278
0
  unsigned i;
279
0
  for (i = 0; i < NUM_HOWTOS; i++)
280
0
    if (howto_table[i].r_type == code)
281
0
      return &howto_table[i].howto;
282
283
0
  BFD_FAIL ();
284
0
  return NULL;
285
0
}
286
287
static reloc_howto_type *
288
coff_z80_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
289
          const char *r_name)
290
0
{
291
0
  unsigned i;
292
0
  for (i = 0; i < NUM_HOWTOS; i++)
293
0
    if (strcasecmp(howto_table[i].howto.name, r_name) == 0)
294
0
      return &howto_table[i].howto;
295
296
0
  return NULL;
297
0
}
298
299
/* Perform any necessary magic to the addend in a reloc entry.  */
300
301
#define CALC_ADDEND(abfd, symbol, ext_reloc, cache_ptr) \
302
 cache_ptr->addend =  ext_reloc.r_offset;
303
304
#define RELOC_PROCESSING(relent,reloc,symbols,abfd,section) \
305
29.7k
 reloc_processing(relent, reloc, symbols, abfd, section)
306
307
static void
308
reloc_processing (arelent *relent,
309
      struct internal_reloc *reloc,
310
      asymbol **symbols,
311
      bfd *abfd,
312
      asection *section)
313
29.7k
{
314
29.7k
  relent->address = reloc->r_vaddr;
315
29.7k
  rtype2howto (relent, reloc);
316
317
29.7k
  if (reloc->r_symndx == -1 || symbols == NULL)
318
10.7k
    relent->sym_ptr_ptr = &bfd_abs_section_ptr->symbol;
319
19.0k
  else if (reloc->r_symndx >= 0 && reloc->r_symndx < obj_conv_table_size (abfd))
320
17.8k
    relent->sym_ptr_ptr = symbols + obj_convert (abfd)[reloc->r_symndx];
321
1.18k
  else
322
1.18k
    {
323
1.18k
      _bfd_error_handler
324
  /* xgettext:c-format */
325
1.18k
  (_("%pB: warning: illegal symbol index %ld in relocs"),
326
1.18k
   abfd, reloc->r_symndx);
327
1.18k
      relent->sym_ptr_ptr = &bfd_abs_section_ptr->symbol;
328
1.18k
    }
329
29.7k
  relent->addend = reloc->r_offset;
330
29.7k
  relent->address -= section->vma;
331
29.7k
}
332
333
static bool
334
extra_case (bfd *in_abfd,
335
      struct bfd_link_info *link_info,
336
      struct bfd_link_order *link_order,
337
      arelent *reloc,
338
      bfd_byte *data,
339
      size_t *src_ptr,
340
      size_t *dst_ptr)
341
2
{
342
2
  asection * input_section = link_order->u.indirect.section;
343
2
  bfd_size_type end = bfd_get_section_limit_octets (in_abfd, input_section);
344
2
  bfd_size_type reloc_size = bfd_get_reloc_size (reloc->howto);
345
346
2
  if (*src_ptr > end
347
2
      || reloc_size > end - *src_ptr)
348
0
    {
349
0
      link_info->callbacks->einfo
350
  /* xgettext:c-format */
351
0
  (_("%X%P: %pB(%pA): relocation \"%pR\" goes out of range\n"),
352
0
   in_abfd, input_section, reloc);
353
0
      return false;
354
0
    }
355
356
2
  int val = bfd_coff_reloc16_get_value (reloc, link_info, input_section);
357
2
  switch (reloc->howto->type)
358
2
    {
359
0
    case R_OFF8:
360
0
      if (reloc->howto->partial_inplace)
361
0
  val += (signed char) (bfd_get_8 (in_abfd, data + *src_ptr)
362
0
            & reloc->howto->src_mask);
363
0
      if (val > 127 || val < -128)
364
0
  {
365
0
    link_info->callbacks->reloc_overflow
366
0
      (link_info, NULL, bfd_asymbol_name (*reloc->sym_ptr_ptr),
367
0
       reloc->howto->name, reloc->addend, input_section->owner,
368
0
       input_section, reloc->address);
369
0
    return false;
370
0
  }
371
372
0
      bfd_put_8 (in_abfd, val, data + *dst_ptr);
373
0
      *dst_ptr += 1;
374
0
      *src_ptr += 1;
375
0
      break;
376
377
0
    case R_BYTE3:
378
0
      bfd_put_8 (in_abfd, val >> 24, data + *dst_ptr);
379
0
      *dst_ptr += 1;
380
0
      *src_ptr += 1;
381
0
      break;
382
383
0
    case R_BYTE2:
384
0
      bfd_put_8 (in_abfd, val >> 16, data + *dst_ptr);
385
0
      *dst_ptr += 1;
386
0
      *src_ptr += 1;
387
0
      break;
388
389
0
    case R_BYTE1:
390
0
      bfd_put_8 (in_abfd, val >> 8, data + *dst_ptr);
391
0
      *dst_ptr += 1;
392
0
      *src_ptr += 1;
393
0
      break;
394
395
0
    case R_IMM8:
396
0
      if (reloc->howto->partial_inplace)
397
0
  val += bfd_get_8 (in_abfd, data + *src_ptr) & reloc->howto->src_mask;
398
      /* Fall through.  */
399
0
    case R_BYTE0:
400
0
      bfd_put_8 (in_abfd, val, data + *dst_ptr);
401
0
      *dst_ptr += 1;
402
0
      *src_ptr += 1;
403
0
      break;
404
405
0
    case R_WORD1:
406
0
      bfd_put_16 (in_abfd, val >> 16, data + *dst_ptr);
407
0
      *dst_ptr += 2;
408
0
      *src_ptr += 2;
409
0
      break;
410
411
0
    case R_IMM16:
412
0
      if (reloc->howto->partial_inplace)
413
0
  val += bfd_get_16 (in_abfd, data + *src_ptr) & reloc->howto->src_mask;
414
      /* Fall through.  */
415
0
    case R_WORD0:
416
0
      bfd_put_16 (in_abfd, val, data + *dst_ptr);
417
0
      *dst_ptr += 2;
418
0
      *src_ptr += 2;
419
0
      break;
420
421
0
    case R_IMM24:
422
0
      if (reloc->howto->partial_inplace)
423
0
  val += (bfd_get_24 (in_abfd, data + *src_ptr)
424
0
    & reloc->howto->src_mask);
425
0
      bfd_put_24 (in_abfd, val, data + *dst_ptr);
426
0
      *dst_ptr += 3;
427
0
      *src_ptr += 3;
428
0
      break;
429
430
0
    case R_IMM32:
431
0
      if (reloc->howto->partial_inplace)
432
0
  val += bfd_get_32 (in_abfd, data + *src_ptr) & reloc->howto->src_mask;
433
0
      bfd_put_32 (in_abfd, val, data + *dst_ptr);
434
0
      *dst_ptr += 4;
435
0
      *src_ptr += 4;
436
0
      break;
437
438
0
    case R_JR:
439
0
      {
440
0
  if (reloc->howto->partial_inplace)
441
0
    val += (signed char) (bfd_get_8 (in_abfd, data + *src_ptr)
442
0
        & reloc->howto->src_mask);
443
0
  bfd_vma dot = (*dst_ptr
444
0
           + input_section->output_offset
445
0
           + input_section->output_section->vma);
446
0
  bfd_signed_vma gap = val - dot;
447
0
  if (gap >= 128 || gap < -128)
448
0
    {
449
0
      link_info->callbacks->reloc_overflow
450
0
        (link_info, NULL, bfd_asymbol_name (*reloc->sym_ptr_ptr),
451
0
         reloc->howto->name, reloc->addend, input_section->owner,
452
0
         input_section, reloc->address);
453
0
      return false;
454
0
    }
455
456
0
  bfd_put_8 (in_abfd, gap, data + *dst_ptr);
457
0
  *dst_ptr += 1;
458
0
  *src_ptr += 1;
459
0
  break;
460
0
      }
461
462
0
    case R_IMM16BE:
463
0
      if (reloc->howto->partial_inplace)
464
0
  val += ((bfd_get_8 (in_abfd, data + *src_ptr + 0) * 0x100
465
0
     + bfd_get_8 (in_abfd, data + *src_ptr + 1))
466
0
    & reloc->howto->src_mask);
467
      
468
0
      bfd_put_8 (in_abfd, val >> 8, data + *dst_ptr + 0);
469
0
      bfd_put_8 (in_abfd, val, data + *dst_ptr + 1);
470
0
      *dst_ptr += 2;
471
0
      *src_ptr += 2;
472
0
      break;
473
474
2
    default:
475
2
      link_info->callbacks->einfo
476
  /* xgettext:c-format */
477
2
  (_("%X%P: %pB(%pA): relocation \"%pR\" is not supported\n"),
478
2
   in_abfd, input_section, reloc);
479
2
      return false;
480
2
    }
481
0
  return true;
482
2
}
483
484
static bool
485
z80_is_local_label_name (bfd *        abfd ATTRIBUTE_UNUSED,
486
                         const char * name)
487
0
{
488
0
  return (name[0] == '.' && name[1] == 'L') ||
489
0
         _bfd_coff_is_local_label_name (abfd, name);
490
0
}
491
492
#define coff_bfd_is_local_label_name z80_is_local_label_name
493
494
#define coff_reloc16_extra_cases    extra_case
495
#define coff_bfd_reloc_type_lookup  coff_z80_reloc_type_lookup
496
#define coff_bfd_reloc_name_lookup coff_z80_reloc_name_lookup
497
498
#ifndef bfd_pe_print_pdata
499
#define bfd_pe_print_pdata  NULL
500
#endif
501
502
#include "coffcode.h"
503
504
#undef  coff_bfd_get_relocated_section_contents
505
#define coff_bfd_get_relocated_section_contents \
506
  bfd_coff_reloc16_get_relocated_section_contents
507
508
#undef  coff_bfd_relax_section
509
#define coff_bfd_relax_section bfd_coff_reloc16_relax_section
510
511
CREATE_LITTLE_COFF_TARGET_VEC (z80_coff_vec, "coff-z80", 0,
512
             SEC_CODE | SEC_DATA, '\0', NULL,
513
             COFF_SWAP_TABLE)
514