* reloc.c (bfd_install_relocation): Cast data_start to bfd_byte *

before trying to add values to it.
This commit is contained in:
Ian Lance Taylor
1994-09-28 17:36:42 +00:00
parent d4cedfe4ae
commit 094e8be303
2 changed files with 493 additions and 0 deletions

View File

@@ -1,3 +1,26 @@
Wed Sep 28 13:35:05 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
* reloc.c (bfd_install_relocation): Cast data_start to bfd_byte *
before trying to add values to it.
Tue Sep 27 16:47:58 1994 Ken Raeburn <raeburn@cujo.cygnus.com>
* elf32-hppa.c (elf32_hppa_backend_final_write_processing): Cast
return value from elf_sym_extra.
(elf32_hppa_build_stubs): Cast return value from bfd_zalloc.
(elf32_hppa_size_stubs): Csat return value from malloc.
* gen-aout.c (main): Declare and initialize "arch".
* cofflink.c (coff_link_add_symbols): Cast return value of
bfd_hash_allocate.
* riscix.c (riscix_callback): Use PARAMS macro in prototype.
* reloc.c (bfd_install_relocation): New function, mostly copied
from bfd_perform_relocation, adjusted for the assembler's needs.
* bfd-in2.h: Regenerated.
Mon Sep 26 11:00:14 1994 Ian Lance Taylor (ian@sanguine.cygnus.com)
* elfcode.h (assign_file_positions_except_relocs): Align the

View File

@@ -922,6 +922,455 @@ space consuming. For each target:
return flag;
}
/*
FUNCTION
bfd_install_relocation
SYNOPSIS
bfd_reloc_status_type
bfd_install_relocation
(bfd *abfd,
arelent *reloc_entry,
PTR data, bfd_vma data_start,
asection *input_section,
char **error_message);
DESCRIPTION
This looks remarkably like <<bfd_perform_relocation>>, except it
does not expect that the section contents have been filled in.
I.e., it's suitable for use when creating, rather than applying
a relocation.
For now, this function should be considered reserved for the
assembler.
*/
bfd_reloc_status_type
bfd_install_relocation (abfd, reloc_entry, data_start, data_start_offset,
input_section, error_message)
bfd *abfd;
arelent *reloc_entry;
PTR data_start;
bfd_vma data_start_offset;
asection *input_section;
char **error_message;
{
bfd_vma relocation;
bfd_reloc_status_type flag = bfd_reloc_ok;
bfd_size_type addr = reloc_entry->address;
bfd_vma output_base = 0;
const reloc_howto_type *howto = reloc_entry->howto;
asection *reloc_target_output_section;
asymbol *symbol;
PTR data;
symbol = *(reloc_entry->sym_ptr_ptr);
if (bfd_is_abs_section (symbol->section))
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
/* If there is a function supplied to handle this relocation type,
call it. It'll return `bfd_reloc_continue' if further processing
can be done. */
if (howto->special_function)
{
bfd_reloc_status_type cont;
/* XXX - The special_function calls haven't been fixed up to deal
with creating new relocations and section contents. */
cont = howto->special_function (abfd, reloc_entry, symbol,
/* XXX - Non-portable! */
((bfd_byte *) data_start
- data_start_offset),
input_section, abfd, error_message);
if (cont != bfd_reloc_continue)
return cont;
}
/* Is the address of the relocation really within the section? */
if (reloc_entry->address > input_section->_cooked_size)
return bfd_reloc_outofrange;
/* Work out which section the relocation is targetted at and the
initial relocation command value. */
/* Get symbol value. (Common symbols are special.) */
if (bfd_is_com_section (symbol->section))
relocation = 0;
else
relocation = symbol->value;
reloc_target_output_section = symbol->section->output_section;
/* Convert input-section-relative symbol value to absolute. */
if (howto->partial_inplace == false)
output_base = 0;
else
output_base = reloc_target_output_section->vma;
relocation += output_base + symbol->section->output_offset;
/* Add in supplied addend. */
relocation += reloc_entry->addend;
/* Here the variable relocation holds the final address of the
symbol we are relocating against, plus any addend. */
if (howto->pc_relative == true)
{
/* This is a PC relative relocation. We want to set RELOCATION
to the distance between the address of the symbol and the
location. RELOCATION is already the address of the symbol.
We start by subtracting the address of the section containing
the location.
If pcrel_offset is set, we must further subtract the position
of the location within the section. Some targets arrange for
the addend to be the negative of the position of the location
within the section; for example, i386-aout does this. For
i386-aout, pcrel_offset is false. Some other targets do not
include the position of the location; for example, m88kbcs,
or ELF. For those targets, pcrel_offset is true.
If we are producing relocateable output, then we must ensure
that this reloc will be correctly computed when the final
relocation is done. If pcrel_offset is false we want to wind
up with the negative of the location within the section,
which means we must adjust the existing addend by the change
in the location within the section. If pcrel_offset is true
we do not want to adjust the existing addend at all.
FIXME: This seems logical to me, but for the case of
producing relocateable output it is not what the code
actually does. I don't want to change it, because it seems
far too likely that something will break. */
relocation -=
input_section->output_section->vma + input_section->output_offset;
if (howto->pcrel_offset == true && howto->partial_inplace == true)
relocation -= reloc_entry->address;
}
if (howto->partial_inplace == false)
{
/* This is a partial relocation, and we want to apply the relocation
to the reloc entry rather than the raw data. Modify the reloc
inplace to reflect what we now know. */
reloc_entry->addend = relocation;
reloc_entry->address += input_section->output_offset;
return flag;
}
else
{
/* This is a partial relocation, but inplace, so modify the
reloc record a bit.
If we've relocated with a symbol with a section, change
into a ref to the section belonging to the symbol. */
reloc_entry->address += input_section->output_offset;
/* WTF?? */
if (abfd->xvec->flavour == bfd_target_coff_flavour
&& strcmp (abfd->xvec->name, "aixcoff-rs6000") != 0
&& strcmp (abfd->xvec->name, "coff-Intel-little") != 0
&& strcmp (abfd->xvec->name, "coff-Intel-big") != 0)
{
#if 1
/* For m68k-coff, the addend was being subtracted twice during
relocation with -r. Removing the line below this comment
fixes that problem; see PR 2953.
However, Ian wrote the following, regarding removing the line below,
which explains why it is still enabled: --djm
If you put a patch like that into BFD you need to check all the COFF
linkers. I am fairly certain that patch will break coff-i386 (e.g.,
SCO); see coff_i386_reloc in coff-i386.c where I worked around the
problem in a different way. There may very well be a reason that the
code works as it does.
Hmmm. The first obvious point is that bfd_install_relocation should
not have any tests that depend upon the flavour. It's seem like
entirely the wrong place for such a thing. The second obvious point
is that the current code ignores the reloc addend when producing
relocateable output for COFF. That's peculiar. In fact, I really
have no idea what the point of the line you want to remove is.
A typical COFF reloc subtracts the old value of the symbol and adds in
the new value to the location in the object file (if it's a pc
relative reloc it adds the difference between the symbol value and the
location). When relocating we need to preserve that property.
BFD handles this by setting the addend to the negative of the old
value of the symbol. Unfortunately it handles common symbols in a
non-standard way (it doesn't subtract the old value) but that's a
different story (we can't change it without losing backward
compatibility with old object files) (coff-i386 does subtract the old
value, to be compatible with existing coff-i386 targets, like SCO).
So everything works fine when not producing relocateable output. When
we are producing relocateable output, logically we should do exactly
what we do when not producing relocateable output. Therefore, your
patch is correct. In fact, it should probably always just set
reloc_entry->addend to 0 for all cases, since it is, in fact, going to
add the value into the object file. This won't hurt the COFF code,
which doesn't use the addend; I'm not sure what it will do to other
formats (the thing to check for would be whether any formats both use
the addend and set partial_inplace).
When I wanted to make coff-i386 produce relocateable output, I ran
into the problem that you are running into: I wanted to remove that
line. Rather than risk it, I made the coff-i386 relocs use a special
function; it's coff_i386_reloc in coff-i386.c. The function
specifically adds the addend field into the object file, knowing that
bfd_install_relocation is not going to. If you remove that line, then
coff-i386.c will wind up adding the addend field in twice. It's
trivial to fix; it just needs to be done.
The problem with removing the line is just that it may break some
working code. With BFD it's hard to be sure of anything. The right
way to deal with this is simply to build and test at least all the
supported COFF targets. It should be straightforward if time and disk
space consuming. For each target:
1) build the linker
2) generate some executable, and link it using -r (I would
probably use paranoia.o and link against newlib/libc.a, which
for all the supported targets would be available in
/usr/cygnus/progressive/H-host/target/lib/libc.a).
3) make the change to reloc.c
4) rebuild the linker
5) repeat step 2
6) if the resulting object files are the same, you have at least
made it no worse
7) if they are different you have to figure out which version is
right
*/
relocation -= reloc_entry->addend;
#endif
reloc_entry->addend = 0;
}
else
{
reloc_entry->addend = relocation;
}
}
/* FIXME: This overflow checking is incomplete, because the value
might have overflowed before we get here. For a correct check we
need to compute the value in a size larger than bitsize, but we
can't reasonably do that for a reloc the same size as a host
machine word.
FIXME: We should also do overflow checking on the result after
adding in the value contained in the object file. */
if (howto->complain_on_overflow != complain_overflow_dont)
{
bfd_vma check;
/* Get the value that will be used for the relocation, but
starting at bit position zero. */
if (howto->rightshift > howto->bitpos)
check = relocation >> (howto->rightshift - howto->bitpos);
else
check = relocation << (howto->bitpos - howto->rightshift);
switch (howto->complain_on_overflow)
{
case complain_overflow_signed:
{
/* Assumes two's complement. */
bfd_signed_vma reloc_signed_max = (1 << (howto->bitsize - 1)) - 1;
bfd_signed_vma reloc_signed_min = ~reloc_signed_max;
/* The above right shift is incorrect for a signed value.
Fix it up by forcing on the upper bits. */
if (howto->rightshift > howto->bitpos
&& (bfd_signed_vma) relocation < 0)
check |= ((bfd_vma) - 1
& ~((bfd_vma) - 1
>> (howto->rightshift - howto->bitpos)));
if ((bfd_signed_vma) check > reloc_signed_max
|| (bfd_signed_vma) check < reloc_signed_min)
flag = bfd_reloc_overflow;
}
break;
case complain_overflow_unsigned:
{
/* Assumes two's complement. This expression avoids
overflow if howto->bitsize is the number of bits in
bfd_vma. */
bfd_vma reloc_unsigned_max =
(((1 << (howto->bitsize - 1)) - 1) << 1) | 1;
if ((bfd_vma) check > reloc_unsigned_max)
flag = bfd_reloc_overflow;
}
break;
case complain_overflow_bitfield:
{
/* Assumes two's complement. This expression avoids
overflow if howto->bitsize is the number of bits in
bfd_vma. */
bfd_vma reloc_bits = (((1 << (howto->bitsize - 1)) - 1) << 1) | 1;
if (((bfd_vma) check & ~reloc_bits) != 0
&& ((bfd_vma) check & ~reloc_bits) != (-1 & ~reloc_bits))
{
/* The above right shift is incorrect for a signed
value. See if turning on the upper bits fixes the
overflow. */
if (howto->rightshift > howto->bitpos
&& (bfd_signed_vma) relocation < 0)
{
check |= ((bfd_vma) - 1
& ~((bfd_vma) - 1
>> (howto->rightshift - howto->bitpos)));
if (((bfd_vma) check & ~reloc_bits) != (-1 & ~reloc_bits))
flag = bfd_reloc_overflow;
}
else
flag = bfd_reloc_overflow;
}
}
break;
default:
abort ();
}
}
/*
Either we are relocating all the way, or we don't want to apply
the relocation to the reloc entry (probably because there isn't
any room in the output format to describe addends to relocs)
*/
/* The cast to bfd_vma avoids a bug in the Alpha OSF/1 C compiler
(OSF version 1.3, compiler version 3.11). It miscompiles the
following program:
struct str
{
unsigned int i0;
} s = { 0 };
int
main ()
{
unsigned long x;
x = 0x100000000;
x <<= (unsigned long) s.i0;
if (x == 0)
printf ("failed\n");
else
printf ("succeeded (%lx)\n", x);
}
*/
relocation >>= (bfd_vma) howto->rightshift;
/* Shift everything up to where it's going to be used */
relocation <<= (bfd_vma) howto->bitpos;
/* Wait for the day when all have the mask in them */
/* What we do:
i instruction to be left alone
o offset within instruction
r relocation offset to apply
S src mask
D dst mask
N ~dst mask
A part 1
B part 2
R result
Do this:
i i i i i o o o o o from bfd_get<size>
and S S S S S to get the size offset we want
+ r r r r r r r r r r to get the final value to place
and D D D D D to chop to right size
-----------------------
A A A A A
And this:
... i i i i i o o o o o from bfd_get<size>
and N N N N N get instruction
-----------------------
... B B B B B
And then:
B B B B B
or A A A A A
-----------------------
R R R R R R R R R R put into bfd_put<size>
*/
#define DOIT(x) \
x = ( (x & ~howto->dst_mask) | (((x & howto->src_mask) + relocation) & howto->dst_mask))
data = (bfd_byte *) data_start + (addr - data_start_offset);
switch (howto->size)
{
case 0:
{
char x = bfd_get_8 (abfd, (char *) data);
DOIT (x);
bfd_put_8 (abfd, x, (unsigned char *) data);
}
break;
case 1:
if (relocation)
{
short x = bfd_get_16 (abfd, (bfd_byte *) data);
DOIT (x);
bfd_put_16 (abfd, x, (unsigned char *) data);
}
break;
case 2:
if (relocation)
{
long x = bfd_get_32 (abfd, (bfd_byte *) data);
DOIT (x);
bfd_put_32 (abfd, x, (bfd_byte *) data);
}
break;
case -2:
{
long x = bfd_get_32 (abfd, (bfd_byte *) data);
relocation = -relocation;
DOIT (x);
bfd_put_32 (abfd, x, (bfd_byte *) data);
}
break;
case 3:
/* Do nothing */
break;
case 4:
if (relocation)
{
bfd_vma x = bfd_get_64 (abfd, (bfd_byte *) data);
DOIT (x);
bfd_put_64 (abfd, x, (bfd_byte *) data);
}
break;
default:
return bfd_reloc_other;
}
return flag;
}
/* This relocation routine is used by some of the backend linkers.
They do not construct asymbol or arelent structures, so there is no
reason for them to use bfd_perform_relocation. Also,
@@ -1533,6 +1982,27 @@ ENUMDOC
probably a 32 bit wide absolute relocation, but the target can choose.
It generally does map to one of the other relocation types.
ENUM
BFD_RELOC_ARM_PCREL_BRANCH
ENUMDOC
ARM 26 bit pc-relative branch. The lowest two bits must be zero and are
not stored in the instruction.
ENUM
BFD_RELOC_ARM_IMMEDIATE
ENUMX
BFD_RELOC_ARM_OFFSET_IMM
ENUMX
BFD_RELOC_ARM_SHIFT_IMM
ENUMX
BFD_RELOC_ARM_SWI
ENUMX
BFD_RELOC_ARM_MULTI
ENUMX
BFD_RELOC_ARM_CP_OFF_IMM
ENUMDOC
These relocs are only used within the ARM assembler. They are not
(at present) written to any object files.
ENDSENUM
BFD_RELOC_UNUSED