Files
binutils-gdb/bfd/tekhex.c
Kevin Buettner bf0f85df12 Fix tekhex format related gdb.base/dump.exp failures
On s390x, a big-endian machine, I'm seeing these test failures:

FAIL: gdb.base/dump.exp: array as memory, tekhex; file restored ok
FAIL: gdb.base/dump.exp: array as memory, tekhex; value restored ok
FAIL: gdb.base/dump.exp: array as value, tekhex; file restored ok
FAIL: gdb.base/dump.exp: array as value, tekhex; value restored ok
FAIL: gdb.base/dump.exp: array copy, tekhex; file restored ok
FAIL: gdb.base/dump.exp: array copy, tekhex; value restored ok
FAIL: gdb.base/dump.exp: array partial, tekhex; file restored ok
FAIL: gdb.base/dump.exp: array partial, tekhex; value restored ok
FAIL: gdb.base/dump.exp: dump array as memory, tekhex
FAIL: gdb.base/dump.exp: dump array as value, tekhex
FAIL: gdb.base/dump.exp: dump struct as memory, tekhex
FAIL: gdb.base/dump.exp: dump struct as value, tekhex
FAIL: gdb.base/dump.exp: reload array as memory, tekhex; value restored ok
FAIL: gdb.base/dump.exp: reload array as value, tekhex; value restored ok
FAIL: gdb.base/dump.exp: reload struct as memory, tekhex; value restored ok
FAIL: gdb.base/dump.exp: reload struct as value, tekhex; value restored ok
FAIL: gdb.base/dump.exp: struct as memory, tekhex; file restored ok
FAIL: gdb.base/dump.exp: struct as memory, tekhex; value restored ok
FAIL: gdb.base/dump.exp: struct as value, tekhex; file restored ok
FAIL: gdb.base/dump.exp: struct as value, tekhex; value restored ok
FAIL: gdb.base/dump.exp: struct copy, tekhex; file restored ok
FAIL: gdb.base/dump.exp: struct copy, tekhex; value restored ok

It turns out that there's a subtle bug in move_section_contents in
bfd/tekhex.c.  The bug is that when attempting to write a buffer that
starts with a zero byte, the function will return false, an error
condition, without writing anything.  But it also doesn't set
bfd_error, so GDB ends up displaying whatever the last unrelated error
was, e.g.:

warning: writing dump file '.../intstr1.tekhex' (No such file or directory)

When I investigated this, the bfd error was set during failure to
open a separate debug file for the test case, which is totally
unrelated to this problem.

The reason this fails on big endian machines is that the test case
writes out structs and arrays of int initialized to small values.  On
little endian machines, the small integer is the first byte, so the
error doesn't occur.  On big endian machines, a zero byte occurs
first, triggering the error.

On the GDB side of things, I've made a one line change to the test
case to cause the error to also happen on little endian machines.  I
simply shift value of the first field in the struct left by 16 bits.
That leaves at least one zero byte on both sides of the non-zero part
of the int.  I shifted it by 16 because, for a moment, there was a
question in my mind about what would happen with a second zero byte,
but it turns out that it's not a problem.

On the bfd side of things, take a look at move_section_contents() and
find_chunk() in tekhex.c.  The scenario is this: we enter
move_section_contents with locationp pointing at a character buffer
whose first byte is zero.  The 'get' parameter is false, i.e. we're
writing, not reading.  The other critical fact is that the
abfd->tdata.tekhex_data->data is NULL (0).

I'm going to go through the execution path pretty much line by line
with commentary below the line(s) just executed.

  char *location = (char *) locationp;
  bfd_vma prev_number = 1;      /* Nothing can have this as a high bit.  */

I can't say that the comment provides the best explanation about
what's happening, but the gist is this: later on, chunk_number will
have it's low bits masked away, therefore no matter what it is, it
can't possibly be equal to prev_number when it's set to 1.

  struct data_struct *d = NULL;

  BFD_ASSERT (offset == 0);
  for (addr = section->vma; count != 0; count--, addr++)
    {

Set d to NULL and enter the loop.

      /* Get high bits of address.  */
      bfd_vma chunk_number = addr & ~(bfd_vma) CHUNK_MASK;
      bfd_vma low_bits = addr & CHUNK_MASK;

Use CHUNK_MASK, which is 0x1fff, to obtain the chunk number, i.e.
whatever's left after masking off the low 13 bits of addr, and
low_bits, which are the low 13 bits of addr.  chunk_number matters for
understanding this bug, low_bits does not.  Remember that no matter
what addr is, once you mask off the low 13 bits, it can't be equal to 1.

      bool must_write = !get && *location != 0;

!get is true, *location != 0 is false, therefore the conjunction is
false, and furthermore must_write is false.  I.e.  even though we are
writing, we don't transfer zero bytes to the chunk - this is why
must_write is false.  (The reason this works is that a chunk, once
allocated, is zero'd as part of the allocation using bfd_zalloc.
Therefore we can skip transferring zero bytes and, if enough of them
are skipped one after another, chunk allocation simply doesn't happen.
That's a good thing.)

      if (chunk_number != prev_number || (!d && must_write))

For the reason provided above, chunk_number != prev_number is true.
The other part of the disjunction doesn't matter since the first part
is true.  This means that the if-block is entered.

          /* Different chunk, so move pointer. */
          d = find_chunk (abfd, chunk_number, must_write);

find_chunk is entered with must_write set to false.  Now, remember
where we left off here, because we're going to switch to find_chunk.

  static struct data_struct *
  find_chunk (bfd *abfd, bfd_vma vma, bool create)
  {

(Above 3 lines indented to distinguish code from commentary.)

When we enter find_chunk, create is false because must_write was false.

  struct data_struct *d = abfd->tdata.tekhex_data->data;

d is set to NULL since abfd->tdata.texhex_data->data is NULL (one of
the conditions for the scenario).

  vma &= ~CHUNK_MASK;
  while (d && (d->vma) != vma)
    d = d->next;

d is NULL, so the while loop doesn't execute.

  if (!d && create)
    ...

d is NULL so !d is true, but create is false, so the condition
evaluates to false, meaning that the if-block is skipped.

  return d;

find_chunk returns NULL, since d is NULL.

Back in move_section_contents:

          if (!d)
            return false;

d is NULL (because that's what find_chunk returned), so
move_section_contents returns false at this point.

Note that find_section_contents has allocated no memory, nor even
tried to transfer any bytes beyond the first (zero) byte.  This
is a bug.

The key to understanding this bug is to observe that find_chunk can
return NULL to indicate that no chunk was found.  This is especially
important for the read (get=true) case.  But it can also be NULL
to indicate a memory allocation error.  I toyed around with the
idea of using a different value to distinguish these cases, i.e.
something like (struct data_struct *) -1, but although bfd contains
plenty of code where -1 is used to indicate various interesting
conditions for scalars, there's no prior art where this is done
for a pointer.  Therefore the idea was discarded in favor of
modifying this statement:

          if (!d)
            return false;

to:
          if (!d && must_write)
            return false;

This works because, in find_chunk, the only way to return a NULL
memory allocation error is for must_write / create to be true.  When
it is true, if bfd_zalloc successfully allocates a chunk, then that
(non-NULL) chunk will be returned at the end of the function.  When it
fails, it'll return NULL early.  The point is that when bfd_zalloc()
fails and returns NULL, must_write (in move_section_contents) / create
(in find_chunk) HAD to be true.  That provides us with an easy test
back in move_section_contents to distinguish a memory-allocation-NULL
from a block-not-found-NULL.

The other NULL return case happens when the end of the function is
reached when either searching for a chunk to read or attempting to
find a chunk to write when abfd->tdata.tekhex_data->data is NULL.  But
for the latter case, must_write was false, which does not (now, with
the above fix) trigger the early return of false.

(Alan Modra approved the bfd/tekhex.c change.)
Approved-By: Simon Marchi <simon.marchi@efficios.com> (GDB)
2025-08-25 15:39:18 -07:00

1036 lines
26 KiB
C

/* BFD backend for Extended Tektronix Hex Format objects.
Copyright (C) 1992-2025 Free Software Foundation, Inc.
Written by Steve Chamberlain of Cygnus Support <sac@cygnus.com>.
This file is part of BFD, the Binary File Descriptor library.
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, write to the Free Software
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
MA 02110-1301, USA. */
/* SUBSECTION
Tektronix Hex Format handling
DESCRIPTION
Tek Hex records can hold symbols and data, but not
relocations. Their main application is communication with
devices like PROM programmers and ICE equipment.
It seems that the sections are described as being really big,
the example I have says that the text section is 0..ffffffff.
BFD would barf with this, many apps would try to alloc 4GB to
read in the file.
Tex Hex may contain many sections, but the data which comes in
has no tag saying which section it belongs to, so we create
one section for each block of data, called "blknnnn" which we
stick all the data into.
TekHex may come out of order and there is no header, so an
initial scan is required to discover the minimum and maximum
addresses used to create the vma and size of the sections we
create.
We read in the data into pages of CHUNK_MASK+1 size and read
them out from that whenever we need to.
Any number of sections may be created for output, we save them
up and output them when it's time to close the bfd.
A TekHex record looks like:
EXAMPLE
%<block length><type><checksum><stuff><cr>
DESCRIPTION
Where
o length
is the number of bytes in the record not including the % sign.
o type
is one of:
3) symbol record
6) data record
8) termination record
The data can come out of order, and may be discontigous. This is a
serial protocol, so big files are unlikely, so we keep a list of 8k chunks. */
#include "sysdep.h"
#include "bfd.h"
#include "libbfd.h"
#include "libiberty.h"
typedef struct
{
bfd_vma low;
bfd_vma high;
} addr_range_type;
typedef struct tekhex_symbol_struct
{
asymbol symbol;
struct tekhex_symbol_struct *prev;
} tekhex_symbol_type;
static const char digs[] = "0123456789ABCDEF";
static char sum_block[256];
#define NOT_HEX 20
#define NIBBLE(x) hex_value(x)
#define HEX(buffer) ((NIBBLE ((buffer)[0]) << 4) + NIBBLE ((buffer)[1]))
#define ISHEX(x) hex_p(x)
#define TOHEX(d, x) \
(d)[1] = digs[(x) & 0xf]; \
(d)[0] = digs[((x)>>4)&0xf];
/* Here's an example
%3A6C6480004E56FFFC4E717063B0AEFFFC6D0652AEFFFC60F24E5E4E75
%1B3709T_SEGMENT1108FFFFFFFF
%2B3AB9T_SEGMENT7Dgcc_compiled$1087hello$c10
%373829T_SEGMENT80int$t1$r1$$214741080char$t2$r2$0$12710
%373769T_SEGMENT80long$int$t3$r1$$1080unsigned$int$t4$10
%373CA9T_SEGMENT80long$unsigned$in1080short$int$t6$r1$10
%373049T_SEGMENT80long$long$int$t71080short$unsigned$i10
%373A29T_SEGMENT80long$long$unsign1080signed$char$t10$10
%373D69T_SEGMENT80unsigned$char$t11080float$t12$r1$4$010
%373D19T_SEGMENT80double$t13$r1$8$1080long$double$t14$10
%2734D9T_SEGMENT8Bvoid$t15$151035_main10
%2F3CA9T_SEGMENT81$1081$1681$1E81$21487main$F110
%2832F9T_SEGMENT83i$18FFFFFFFC81$1481$214
%07 8 10 10
explanation:
%3A6C6480004E56FFFC4E717063B0AEFFFC6D0652AEFFFC60F24E5E4E75
^ ^^ ^ ^-data
| || +------ 4 char integer 0x8000
| |+-------- checksum
| +--------- type 6 (data record)
+----------- length 3a chars
<---------------------- 3a (58 chars) ------------------->
%1B3709T_SEGMENT1108FFFFFFFF
^ ^^ ^- 8 character integer 0xffffffff
| |+- 1 character integer 0
| +-- type 1 symbol (section definition)
+------------ 9 char symbol T_SEGMENT
%2B3AB9T_SEGMENT7Dgcc_compiled$1087hello$c10
%373829T_SEGMENT80int$t1$r1$$214741080char$t2$r2$0$12710
%373769T_SEGMENT80long$int$t3$r1$$1080unsigned$int$t4$10
%373CA9T_SEGMENT80long$unsigned$in1080short$int$t6$r1$10
%373049T_SEGMENT80long$long$int$t71080short$unsigned$i10
%373A29T_SEGMENT80long$long$unsign1080signed$char$t10$10
%373D69T_SEGMENT80unsigned$char$t11080float$t12$r1$4$010
%373D19T_SEGMENT80double$t13$r1$8$1080long$double$t14$10
%2734D9T_SEGMENT8Bvoid$t15$151035_main10
%2F3CA9T_SEGMENT81$1081$1681$1E81$21487main$F110
%2832F9T_SEGMENT83i$18FFFFFFFC81$1481$214
%0781010
Turns into
sac@thepub$ ./objdump -dx -m m68k f
f: file format tekhex
-----x--- 9/55728 -134219416 Sep 29 15:13 1995 f
architecture: UNKNOWN!, flags 0x00000010:
HAS_SYMS
start address 0x00000000
SECTION 0 [D00000000] : size 00020000 vma 00000000 align 2**0
ALLOC, LOAD
SECTION 1 [D00008000] : size 00002001 vma 00008000 align 2**0
SECTION 2 [T_SEGMENT] : size ffffffff vma 00000000 align 2**0
SYMBOL TABLE:
00000000 g T_SEGMENT gcc_compiled$
00000000 g T_SEGMENT hello$c
00000000 g T_SEGMENT int$t1$r1$$21474
00000000 g T_SEGMENT char$t2$r2$0$127
00000000 g T_SEGMENT long$int$t3$r1$$
00000000 g T_SEGMENT unsigned$int$t4$
00000000 g T_SEGMENT long$unsigned$in
00000000 g T_SEGMENT short$int$t6$r1$
00000000 g T_SEGMENT long$long$int$t7
00000000 g T_SEGMENT short$unsigned$i
00000000 g T_SEGMENT long$long$unsign
00000000 g T_SEGMENT signed$char$t10$
00000000 g T_SEGMENT unsigned$char$t1
00000000 g T_SEGMENT float$t12$r1$4$0
00000000 g T_SEGMENT double$t13$r1$8$
00000000 g T_SEGMENT long$double$t14$
00000000 g T_SEGMENT void$t15$15
00000000 g T_SEGMENT _main
00000000 g T_SEGMENT $
00000000 g T_SEGMENT $
00000000 g T_SEGMENT $
00000010 g T_SEGMENT $
00000000 g T_SEGMENT main$F1
fcffffff g T_SEGMENT i$1
00000000 g T_SEGMENT $
00000010 g T_SEGMENT $
RELOCATION RECORDS FOR [D00000000]: (none)
RELOCATION RECORDS FOR [D00008000]: (none)
RELOCATION RECORDS FOR [T_SEGMENT]: (none)
Disassembly of section D00000000:
...
00008000 ($+)7ff0 linkw fp,#-4
00008004 ($+)7ff4 nop
00008006 ($+)7ff6 movel #99,d0
00008008 ($+)7ff8 cmpl fp@(-4),d0
0000800c ($+)7ffc blts 00008014 ($+)8004
0000800e ($+)7ffe addql #1,fp@(-4)
00008012 ($+)8002 bras 00008006 ($+)7ff6
00008014 ($+)8004 unlk fp
00008016 ($+)8006 rts
... */
static void
tekhex_init (void)
{
unsigned int i;
static bool inited = false;
int val;
if (! inited)
{
inited = true;
hex_init ();
val = 0;
for (i = 0; i < 10; i++)
sum_block[i + '0'] = val++;
for (i = 'A'; i <= 'Z'; i++)
sum_block[i] = val++;
sum_block['$'] = val++;
sum_block['%'] = val++;
sum_block['.'] = val++;
sum_block['_'] = val++;
for (i = 'a'; i <= 'z'; i++)
sum_block[i] = val++;
}
}
/* The maximum number of bytes on a line is FF. */
#define MAXCHUNK 0xff
/* The number of bytes we fit onto a line on output. */
#define CHUNK 21
/* We cannot output our tekhexords as we see them, we have to glue them
together, this is done in this structure : */
struct tekhex_data_list_struct
{
unsigned char *data;
bfd_vma where;
bfd_size_type size;
struct tekhex_data_list_struct *next;
};
typedef struct tekhex_data_list_struct tekhex_data_list_type;
#define CHUNK_MASK 0x1fff
#define CHUNK_SPAN 32
struct data_struct
{
unsigned char chunk_data[CHUNK_MASK + 1];
unsigned char chunk_init[(CHUNK_MASK + 1 + CHUNK_SPAN - 1) / CHUNK_SPAN];
bfd_vma vma;
struct data_struct *next;
};
typedef struct tekhex_data_struct
{
tekhex_data_list_type *head;
unsigned int type;
struct tekhex_symbol_struct *symbols;
struct data_struct *data;
} tdata_type;
#define enda(x) (x->vma + x->size)
static bool
getvalue (char **srcp, bfd_vma *valuep, char * endp)
{
char *src = *srcp;
bfd_vma value = 0;
unsigned int len;
if (src >= endp)
return false;
if (!ISHEX (*src))
return false;
len = hex_value (*src++);
if (len == 0)
len = 16;
while (len-- && src < endp)
{
if (!ISHEX (*src))
return false;
value = value << 4 | hex_value (*src++);
}
*srcp = src;
*valuep = value;
return len == -1U;
}
static bool
getsym (char *dstp, char **srcp, unsigned int *lenp, char * endp)
{
char *src = *srcp;
unsigned int i;
unsigned int len;
if (!ISHEX (*src))
return false;
len = hex_value (*src++);
if (len == 0)
len = 16;
for (i = 0; i < len && (src + i) < endp; i++)
dstp[i] = src[i];
dstp[i] = 0;
*srcp = src + i;
*lenp = len;
return i == len;
}
static struct data_struct *
find_chunk (bfd *abfd, bfd_vma vma, bool create)
{
struct data_struct *d = abfd->tdata.tekhex_data->data;
vma &= ~CHUNK_MASK;
while (d && (d->vma) != vma)
d = d->next;
if (!d && create)
{
/* No chunk for this address, so make one up. */
d = bfd_zalloc (abfd, sizeof (struct data_struct));
if (!d)
return NULL;
d->next = abfd->tdata.tekhex_data->data;
d->vma = vma;
abfd->tdata.tekhex_data->data = d;
}
return d;
}
static bool
insert_byte (bfd *abfd, int value, bfd_vma addr)
{
if (value != 0)
{
/* Find the chunk that this byte needs and put it in. */
struct data_struct *d = find_chunk (abfd, addr, true);
if (!d)
return false;
d->chunk_data[addr & CHUNK_MASK] = value;
d->chunk_init[(addr & CHUNK_MASK) / CHUNK_SPAN] = 1;
}
return true;
}
/* The first pass is to find the names of all the sections, and see
how big the data is. */
static bool
first_phase (bfd *abfd, int type, char *src, char * src_end)
{
asection *section, *alt_section;
unsigned int len;
bfd_vma addr;
bfd_vma val;
char sym[17]; /* A symbol can only be 16chars long. */
switch (type)
{
case '6':
/* Data record - read it and store it. */
if (!getvalue (&src, &addr, src_end))
return false;
while (*src && src < src_end - 1)
{
if (!insert_byte (abfd, HEX (src), addr))
return false;
src += 2;
addr++;
}
return true;
case '3':
/* Symbol record, read the segment. */
if (!getsym (sym, &src, &len, src_end))
return false;
section = bfd_get_section_by_name (abfd, sym);
if (section == NULL)
{
char *n = (char *) bfd_alloc (abfd, (bfd_size_type) len + 1);
if (!n)
return false;
memcpy (n, sym, len + 1);
section = bfd_make_section_old_way (abfd, n);
if (section == NULL)
return false;
}
alt_section = NULL;
while (src < src_end && *src)
{
switch (*src)
{
case '1': /* Section range. */
src++;
if (!getvalue (&src, &addr, src_end))
return false;
if (!getvalue (&src, &val, src_end))
return false;
if (bfd_is_const_section (section))
break;
section->vma = addr;
if (val < addr)
val = addr;
section->size = val - addr;
/* PR 17512: file: objdump-s-endless-loop.tekhex.
Check for overlarge section sizes. */
if (section->size & 0x80000000)
return false;
section->flags = SEC_HAS_CONTENTS | SEC_LOAD | SEC_ALLOC;
break;
case '0':
case '2':
case '3':
case '4':
case '6':
case '7':
case '8':
/* Symbols, add to section. */
{
size_t amt = sizeof (tekhex_symbol_type);
tekhex_symbol_type *new_symbol = (tekhex_symbol_type *)
bfd_alloc (abfd, amt);
char stype = (*src);
if (!new_symbol)
return false;
new_symbol->symbol.the_bfd = abfd;
src++;
abfd->symcount++;
abfd->flags |= HAS_SYMS;
new_symbol->prev = abfd->tdata.tekhex_data->symbols;
abfd->tdata.tekhex_data->symbols = new_symbol;
if (!getsym (sym, &src, &len, src_end))
return false;
new_symbol->symbol.name = (const char *)
bfd_alloc (abfd, (bfd_size_type) len + 1);
if (!new_symbol->symbol.name)
return false;
memcpy ((char *) (new_symbol->symbol.name), sym, len + 1);
new_symbol->symbol.section = section;
if (stype <= '4')
new_symbol->symbol.flags = (BSF_GLOBAL | BSF_EXPORT);
else
new_symbol->symbol.flags = BSF_LOCAL;
if (stype == '2' || stype == '6')
new_symbol->symbol.section = bfd_abs_section_ptr;
else if (bfd_is_const_section (section))
;
else if (stype == '3' || stype == '7')
{
if ((section->flags & SEC_DATA) == 0)
section->flags |= SEC_CODE;
else
{
if (alt_section == NULL)
alt_section
= bfd_get_next_section_by_name (NULL, section);
if (alt_section == NULL)
alt_section = bfd_make_section_anyway_with_flags
(abfd, section->name,
(section->flags & ~SEC_DATA) | SEC_CODE);
if (alt_section == NULL)
return false;
new_symbol->symbol.section = alt_section;
}
}
else if (stype == '4' || stype == '8')
{
if ((section->flags & SEC_CODE) == 0)
section->flags |= SEC_DATA;
else
{
if (alt_section == NULL)
alt_section
= bfd_get_next_section_by_name (NULL, section);
if (alt_section == NULL)
alt_section = bfd_make_section_anyway_with_flags
(abfd, section->name,
(section->flags & ~SEC_CODE) | SEC_DATA);
if (alt_section == NULL)
return false;
new_symbol->symbol.section = alt_section;
}
}
if (!getvalue (&src, &val, src_end))
return false;
new_symbol->symbol.value = val - section->vma;
break;
}
default:
return false;
}
}
}
return true;
}
/* Pass over a tekhex, calling one of the above functions on each
record. */
static bool
pass_over (bfd *abfd, bool (*func) (bfd *, int, char *, char *))
{
unsigned int chars_on_line;
bool is_eof = false;
/* To the front of the file. */
if (bfd_seek (abfd, 0, SEEK_SET) != 0)
return false;
while (! is_eof)
{
char src[MAXCHUNK];
char type;
/* Find first '%'. */
is_eof = bfd_read (src, 1, abfd) != 1;
while (!is_eof && *src != '%')
is_eof = bfd_read (src, 1, abfd) != 1;
if (is_eof)
break;
/* Fetch the type and the length and the checksum. */
if (bfd_read (src, 5, abfd) != 5)
return false;
type = src[2];
if (!ISHEX (src[0]) || !ISHEX (src[1]))
break;
/* Already read five chars. */
chars_on_line = HEX (src) - 5;
if (chars_on_line >= MAXCHUNK)
return false;
if (bfd_read (src, chars_on_line, abfd) != chars_on_line)
return false;
/* Put a null at the end. */
src[chars_on_line] = 0;
if (!func (abfd, type, src, src + chars_on_line))
return false;
}
return true;
}
static long
tekhex_canonicalize_symtab (bfd *abfd, asymbol **table)
{
tekhex_symbol_type *p = abfd->tdata.tekhex_data->symbols;
unsigned int c = bfd_get_symcount (abfd);
table[c] = 0;
while (p)
{
table[--c] = &(p->symbol);
p = p->prev;
}
return bfd_get_symcount (abfd);
}
static long
tekhex_get_symtab_upper_bound (bfd *abfd)
{
return (abfd->symcount + 1) * (sizeof (struct tekhex_asymbol_struct *));
}
static bool
tekhex_mkobject (bfd *abfd)
{
tdata_type *tdata;
tdata = (tdata_type *) bfd_alloc (abfd, (bfd_size_type) sizeof (tdata_type));
if (!tdata)
return false;
abfd->tdata.tekhex_data = tdata;
tdata->type = 1;
tdata->head = NULL;
tdata->symbols = NULL;
tdata->data = NULL;
return true;
}
/* Return TRUE if the file looks like it's in TekHex format. Just look
for a percent sign and some hex digits. */
static bfd_cleanup
tekhex_object_p (bfd *abfd)
{
char b[4];
tekhex_init ();
if (bfd_seek (abfd, 0, SEEK_SET) != 0
|| bfd_read (b, 4, abfd) != 4)
return NULL;
if (b[0] != '%' || !ISHEX (b[1]) || !ISHEX (b[2]) || !ISHEX (b[3]))
return NULL;
if (!tekhex_mkobject (abfd))
return NULL;
if (!pass_over (abfd, first_phase))
{
bfd_release (abfd, abfd->tdata.tekhex_data);
return NULL;
}
return _bfd_no_cleanup;
}
static bool
move_section_contents (bfd *abfd,
asection *section,
const void * locationp,
file_ptr offset,
bfd_size_type count,
bool get)
{
bfd_vma addr;
char *location = (char *) locationp;
bfd_vma prev_number = 1; /* Nothing can have this as a high bit. */
struct data_struct *d = NULL;
BFD_ASSERT (offset == 0);
for (addr = section->vma; count != 0; count--, addr++)
{
/* Get high bits of address. */
bfd_vma chunk_number = addr & ~(bfd_vma) CHUNK_MASK;
bfd_vma low_bits = addr & CHUNK_MASK;
bool must_write = !get && *location != 0;
if (chunk_number != prev_number || (!d && must_write))
{
/* Different chunk, so move pointer. */
d = find_chunk (abfd, chunk_number, must_write);
if (!d && must_write)
return false;
prev_number = chunk_number;
}
if (get)
{
if (d)
*location = d->chunk_data[low_bits];
else
*location = 0;
}
else if (must_write)
{
d->chunk_data[low_bits] = *location;
d->chunk_init[low_bits / CHUNK_SPAN] = 1;
}
location++;
}
return true;
}
static bool
tekhex_get_section_contents (bfd *abfd,
asection *section,
void *location,
file_ptr offset,
bfd_size_type count)
{
if ((section->flags & (SEC_LOAD | SEC_ALLOC)) == 0)
return false;
return move_section_contents (abfd, section, location, offset, count, true);
}
static bool
tekhex_set_arch_mach (bfd *abfd,
enum bfd_architecture arch,
unsigned long machine)
{
/* Ignore errors about unknown architecture. */
return (bfd_default_set_arch_mach (abfd, arch, machine)
|| arch == bfd_arch_unknown);
}
/* We have to save up all the Tekhexords for a splurge before output. */
static bool
tekhex_set_section_contents (bfd *abfd,
sec_ptr section,
const void *location,
file_ptr offset,
bfd_size_type count)
{
if ((section->flags & (SEC_LOAD | SEC_ALLOC)) == 0)
return false;
return move_section_contents (abfd, section, location, offset, count, false);
}
static void
writevalue (char **dst, bfd_vma value)
{
char *p = *dst;
int len;
int shift;
for (len = BFD_ARCH_SIZE / 4, shift = len * 4 - 4; len > 1; shift -= 4, len--)
if ((value >> shift) & 0xf)
break;
*p++ = digs[len & 0xf];
for (; len; shift -= 4, len--)
*p++ = digs[(value >> shift) & 0xf];
*dst = p;
}
static void
writesym (char **dst, const char *sym)
{
char *p = *dst;
int len = (sym ? strlen (sym) : 0);
if (len >= 16)
len = 16;
else if (len == 0)
{
len = 1;
sym = "$";
}
*p++ = digs[len & 0xf];
while (len--)
*p++ = *sym++;
*dst = p;
}
static void
out (bfd *abfd, int type, char *start, char *end)
{
int sum = 0;
char *s;
char front[6];
bfd_size_type wrlen;
front[0] = '%';
TOHEX (front + 1, end - start + 5);
front[3] = type;
for (s = start; s < end; s++)
sum += sum_block[(unsigned char) *s];
sum += sum_block[(unsigned char) front[1]]; /* Length. */
sum += sum_block[(unsigned char) front[2]];
sum += sum_block[(unsigned char) front[3]]; /* Type. */
TOHEX (front + 4, sum);
if (bfd_write (front, 6, abfd) != 6)
abort ();
end[0] = '\n';
wrlen = end - start + 1;
if (bfd_write (start, wrlen, abfd) != wrlen)
abort ();
}
static bool
tekhex_write_object_contents (bfd *abfd)
{
char buffer[100];
asymbol **p;
asection *s;
struct data_struct *d;
tekhex_init ();
/* And the raw data. */
for (d = abfd->tdata.tekhex_data->data;
d != NULL;
d = d->next)
{
int low;
int addr;
/* Write it in blocks of 32 bytes. */
for (addr = 0; addr < CHUNK_MASK + 1; addr += CHUNK_SPAN)
{
if (d->chunk_init[addr / CHUNK_SPAN])
{
char *dst = buffer;
writevalue (&dst, addr + d->vma);
for (low = 0; low < CHUNK_SPAN; low++)
{
TOHEX (dst, d->chunk_data[addr + low]);
dst += 2;
}
out (abfd, '6', buffer, dst);
}
}
}
/* Write all the section headers for the sections. */
for (s = abfd->sections; s != NULL; s = s->next)
{
char *dst = buffer;
writesym (&dst, s->name);
*dst++ = '1';
writevalue (&dst, s->vma);
writevalue (&dst, s->vma + s->size);
out (abfd, '3', buffer, dst);
}
/* And the symbols. */
if (abfd->outsymbols)
{
for (p = abfd->outsymbols; *p; p++)
{
int section_code = bfd_decode_symclass (*p);
if (section_code != '?')
{
/* Do not include debug symbols. */
asymbol *sym = *p;
char *dst = buffer;
writesym (&dst, sym->section->name);
switch (section_code)
{
case 'A':
*dst++ = '2';
break;
case 'a':
*dst++ = '6';
break;
case 'D':
case 'B':
case 'O':
*dst++ = '4';
break;
case 'd':
case 'b':
case 'o':
*dst++ = '8';
break;
case 'T':
*dst++ = '3';
break;
case 't':
*dst++ = '7';
break;
case 'C':
case 'U':
bfd_set_error (bfd_error_wrong_format);
goto fail;
}
writesym (&dst, sym->name);
writevalue (&dst, sym->value + sym->section->vma);
out (abfd, '3', buffer, dst);
}
}
}
/* And the terminator. */
if (bfd_write ("%0781010\n", 9, abfd) != 9)
goto fail;
return true;
fail:
return false;
}
static int
tekhex_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED,
struct bfd_link_info *info ATTRIBUTE_UNUSED)
{
return 0;
}
static asymbol *
tekhex_make_empty_symbol (bfd *abfd)
{
size_t amt = sizeof (struct tekhex_symbol_struct);
tekhex_symbol_type *new_symbol = (tekhex_symbol_type *) bfd_zalloc (abfd,
amt);
if (!new_symbol)
return NULL;
new_symbol->symbol.the_bfd = abfd;
new_symbol->prev = NULL;
return &(new_symbol->symbol);
}
static void
tekhex_get_symbol_info (bfd *abfd ATTRIBUTE_UNUSED,
asymbol *symbol,
symbol_info *ret)
{
bfd_symbol_info (symbol, ret);
}
static void
tekhex_print_symbol (bfd *abfd,
void * filep,
asymbol *symbol,
bfd_print_symbol_type how)
{
FILE *file = (FILE *) filep;
switch (how)
{
case bfd_print_symbol_name:
fprintf (file, "%s", symbol->name);
break;
case bfd_print_symbol_more:
break;
case bfd_print_symbol_all:
{
const char *section_name = symbol->section->name;
bfd_print_symbol_vandf (abfd, (void *) file, symbol);
fprintf (file, " %-5s %s",
section_name, symbol->name);
}
}
}
#define tekhex_close_and_cleanup _bfd_generic_close_and_cleanup
#define tekhex_bfd_free_cached_info _bfd_generic_bfd_free_cached_info
#define tekhex_new_section_hook _bfd_generic_new_section_hook
#define tekhex_bfd_is_target_special_symbol _bfd_bool_bfd_asymbol_false
#define tekhex_bfd_is_local_label_name bfd_generic_is_local_label_name
#define tekhex_get_lineno _bfd_nosymbols_get_lineno
#define tekhex_find_nearest_line _bfd_nosymbols_find_nearest_line
#define tekhex_find_nearest_line_with_alt _bfd_nosymbols_find_nearest_line_with_alt
#define tekhex_find_line _bfd_nosymbols_find_line
#define tekhex_find_inliner_info _bfd_nosymbols_find_inliner_info
#define tekhex_get_symbol_version_string _bfd_nosymbols_get_symbol_version_string
#define tekhex_bfd_make_debug_symbol _bfd_nosymbols_bfd_make_debug_symbol
#define tekhex_read_minisymbols _bfd_generic_read_minisymbols
#define tekhex_minisymbol_to_symbol _bfd_generic_minisymbol_to_symbol
#define tekhex_bfd_get_relocated_section_contents bfd_generic_get_relocated_section_contents
#define tekhex_bfd_relax_section bfd_generic_relax_section
#define tekhex_bfd_gc_sections bfd_generic_gc_sections
#define tekhex_bfd_lookup_section_flags bfd_generic_lookup_section_flags
#define tekhex_bfd_merge_sections bfd_generic_merge_sections
#define tekhex_bfd_is_group_section bfd_generic_is_group_section
#define tekhex_bfd_group_name bfd_generic_group_name
#define tekhex_bfd_discard_group bfd_generic_discard_group
#define tekhex_section_already_linked _bfd_generic_section_already_linked
#define tekhex_bfd_define_common_symbol bfd_generic_define_common_symbol
#define tekhex_bfd_link_hide_symbol _bfd_generic_link_hide_symbol
#define tekhex_bfd_define_start_stop bfd_generic_define_start_stop
#define tekhex_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
#define tekhex_bfd_link_add_symbols _bfd_generic_link_add_symbols
#define tekhex_bfd_link_just_syms _bfd_generic_link_just_syms
#define tekhex_bfd_copy_link_hash_symbol_type _bfd_generic_copy_link_hash_symbol_type
#define tekhex_bfd_final_link _bfd_generic_final_link
#define tekhex_bfd_link_split_section _bfd_generic_link_split_section
#define tekhex_bfd_link_check_relocs _bfd_generic_link_check_relocs
const bfd_target tekhex_vec =
{
"tekhex", /* Name. */
bfd_target_tekhex_flavour,
BFD_ENDIAN_UNKNOWN, /* Target byte order. */
BFD_ENDIAN_UNKNOWN, /* Target headers byte order. */
EXEC_P | HAS_SYMS, /* Object flags. */
(SEC_CODE | SEC_DATA | SEC_ROM | SEC_HAS_CONTENTS
| SEC_ALLOC | SEC_LOAD), /* Section flags. */
0, /* Leading underscore. */
' ', /* AR_pad_char. */
16, /* AR_max_namelen. */
0, /* match priority. */
TARGET_KEEP_UNUSED_SECTION_SYMBOLS, /* keep unused section symbols. */
bfd_getb64, bfd_getb_signed_64, bfd_putb64,
bfd_getb32, bfd_getb_signed_32, bfd_putb32,
bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* Data. */
bfd_getb64, bfd_getb_signed_64, bfd_putb64,
bfd_getb32, bfd_getb_signed_32, bfd_putb32,
bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* Headers. */
{
_bfd_dummy_target,
tekhex_object_p, /* bfd_check_format. */
_bfd_dummy_target,
_bfd_dummy_target,
},
{
_bfd_bool_bfd_false_error,
tekhex_mkobject,
_bfd_bool_bfd_false_error,
_bfd_bool_bfd_false_error,
},
{ /* bfd_write_contents. */
_bfd_bool_bfd_false_error,
tekhex_write_object_contents,
_bfd_bool_bfd_false_error,
_bfd_bool_bfd_false_error,
},
BFD_JUMP_TABLE_GENERIC (tekhex),
BFD_JUMP_TABLE_COPY (_bfd_generic),
BFD_JUMP_TABLE_CORE (_bfd_nocore),
BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive),
BFD_JUMP_TABLE_SYMBOLS (tekhex),
BFD_JUMP_TABLE_RELOCS (_bfd_norelocs),
BFD_JUMP_TABLE_WRITE (tekhex),
BFD_JUMP_TABLE_LINK (tekhex),
BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
NULL,
NULL
};