From 094e8be3036a8ee7d8cdd1510698433cfe017360 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 28 Sep 1994 17:36:42 +0000 Subject: [PATCH] * reloc.c (bfd_install_relocation): Cast data_start to bfd_byte * before trying to add values to it. --- bfd/ChangeLog | 23 +++ bfd/reloc.c | 470 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 493 insertions(+) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 2fe36c1c46..86b2b111a4 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -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 + + * 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 diff --git a/bfd/reloc.c b/bfd/reloc.c index 1e5292c459..24a5236a0f 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -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 <>, 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 + 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 + 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 + */ + +#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 -- 2.34.1