diff options
author | Steve Chamberlain <steve@cygnus> | 1991-07-04 16:52:19 +0000 |
---|---|---|
committer | Steve Chamberlain <steve@cygnus> | 1991-07-04 16:52:19 +0000 |
commit | 985fca129365a7bfa8e8e91b4d8b57d65f308583 (patch) | |
tree | 1c1837786a6af5798ec8ae9d2a2ecfc744536bf0 /bfd/reloc.c | |
parent | bdf3621b9e6618e13efd3bedd3be56d32c296075 (diff) | |
download | gdb-985fca129365a7bfa8e8e91b4d8b57d65f308583.zip gdb-985fca129365a7bfa8e8e91b4d8b57d65f308583.tar.gz gdb-985fca129365a7bfa8e8e91b4d8b57d65f308583.tar.bz2 |
Initial revision
Diffstat (limited to 'bfd/reloc.c')
-rw-r--r-- | bfd/reloc.c | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/bfd/reloc.c b/bfd/reloc.c new file mode 100644 index 0000000..2d1f6a5 --- /dev/null +++ b/bfd/reloc.c @@ -0,0 +1,612 @@ +/*doc* +@section Relocations + +Bfd maintains relocations in much the same was as it maintains +symbols; they are left alone until required, then read in en-mass and +traslated into an internal form. There is a common routine +@code{bfd_perform_relocation} which acts upon the canonical form to to +the actual fixup. + +Note that relocations are maintained on a per section basis, whilst +symbols are maintained on a per bfd basis. + +All a back end has to do to fit the bfd interface is to create as many +@code{struct reloc_cache_entry} as there are relocations in a +particuar section, and fill in the right bits: + +@menu +* typedef arelent:: +* reloc handling functions:: +@end menu + +*/ +#include "sysdep.h" +#include "bfd.h" +#include "libbfd.h" +/*doc +*node typedef arelent, Relocations, reloc handling functions, Relocations +@section typedef arelent + + +*/ + +/*proto* bfd_perform_relocation +The relocation routine returns as a status an enumerated type: + +*+++ + +$typedef enum bfd_reloc_status { +No errors detected + +$ bfd_reloc_ok, + +The relocation was performed, but there was an overflow. + +$ bfd_reloc_overflow, + +The address to relocate was not within the section supplied + +$ bfd_reloc_outofrange, + +Used by special functions + +$ bfd_reloc_continue, + +Unused + +$ bfd_reloc_notsupported, + +Unsupported relocation size requested. + +$ bfd_reloc_other, + +The symbol to relocate against was undefined. + +$ bfd_reloc_undefined, + +The relocaction was performed, but may not be ok - presently generated +only when linking i960 coff files with i960 b.out symbols. + +$ bfd_reloc_dangerous +$ } +$ bfd_reloc_status_enum_type; + +*--- + +*/ + +/*proto* + +*+++ + +$typedef struct reloc_cache_entry +${ + +A pointer into the canonical table of pointers + +$ struct symbol_cache_entry **sym_ptr_ptr; + +offset in section + +$ rawdata_offset address; + +addend for relocation value + +$ bfd_vma addend; + +if sym is null this is the section + +$ struct sec *section; + +Pointer to how to perform the required relocation + +$ struct reloc_howto_struct *howto; +$} arelent; + +*--- + +*/ + +/*doc* +@table @code +@item sym_ptr_ptr +The symbol table pointer points to a pointer to the symbol ascociated with the +relocation request. This would naturaly be the pointer into the table +returned by the back end's get_symtab action. @xref{Symbols}. The +symbol is referenced through a pointer to a pointer so that tools like +the linker can fixup all the symbols of the same name by modifying +only one pointer. The relocation routine looks in the symbol and uses +the base of the section the symbol is attatched to and the value of +the symbol as the initial relocation offset. If the symbol pointer is +zero, then the section provided is looked up. +@item address +The address field gives the offset in bytes from the base of the +section data which owns the relocation record to the first byte of +relocatable information. The actual data relocated will be relative to +this point - for example, a relocation type which modifies the bottom +two bytes of a four byte word would not touch the first byte pointed +to in a big endian world. +@item addend +The addend is a value provided by the back end to be added (!) to the +relocation offset. It's interpretation is dependent upon the howto. +For example, on the 68k the code: + +*+ + char foo[]; + main() + { + return foo[0x12345678]; + } +*- +Could be compiled into: + +*+ + linkw fp,#-4 + moveb @@#12345678,d0 + extbl d0 + unlk fp + rts +*- + +This could create a reloc pointing to foo, but leave the offset in the data +(something like) + +*+ +RELOCATION RECORDS FOR [.text]: +OFFSET TYPE VALUE +00000006 32 _foo + +00000000 4e56 fffc ; linkw fp,#-4 +00000004 1039 1234 5678 ; moveb @@#12345678,d0 +0000000a 49c0 ; extbl d0 +0000000c 4e5e ; unlk fp +0000000e 4e75 ; rts +*- +Using coff and an 88k, some instructions don't have enough space in them to +represent the full address range, and pointers have to be loaded in +two parts. So you'd get something like: + +*+ + or.u r13,r0,hi16(_foo+0x12345678) + ld.b r2,r13,lo16(_foo+0x12345678) + jmp r1 +*- +This whould create two relocs, both pointing to _foo, and with 0x12340000 +in their addend field. The data would consist of: + +*+ + +RELOCATION RECORDS FOR [.text]: +OFFSET TYPE VALUE +00000002 HVRT16 _foo+0x12340000 +00000006 LVRT16 _foo+0x12340000 + +00000000 5da05678 ; or.u r13,r0,0x5678 +00000004 1c4d5678 ; ld.b r2,r13,0x5678 +00000008 f400c001 ; jmp r1 +*- +The relocation routine digs out the value from the data, adds it to +the addend to get the original offset and then adds the value of _foo. +Note that all 32 bits have to be kept around somewhere, to cope with +carry from bit 15 to bit 16. + +On further example is the sparc and the a.out format. The sparc has a +similar problem to the 88k, in that some instructions don't have +room for an entire offset, but on the sparc the parts are created odd +sized lumps. The designers of the a.out format chose not to use the +data within the section for storing part of the offset; all the offset +is kept within the reloc. Any thing in the data should be ignored. + +*+ + save %sp,-112,%sp + sethi %hi(_foo+0x12345678),%g2 + ldsb [%g2+%lo(_foo+0x12345678)],%i0 + ret + restore +*- +Both relocs contains a pointer to foo, and the offsets would contain junk. + +*+ +RELOCATION RECORDS FOR [.text]: +OFFSET TYPE VALUE +00000004 HI22 _foo+0x12345678 +00000008 LO10 _foo+0x12345678 + +00000000 9de3bf90 ; save %sp,-112,%sp +00000004 05000000 ; sethi %hi(_foo+0),%g2 +00000008 f048a000 ; ldsb [%g2+%lo(_foo+0)],%i0 +0000000c 81c7e008 ; ret +00000010 81e80000 ; restore +*- +@item section +The section field is only used when the symbol pointer field is null. +It supplies the section into which the data should be relocated. The +field's main use comes from assemblers which do most of the symbol fixups +themselves; an assembler may take an internal reference to a label, +but since it knows where the label is, it can turn the relocation +request from a symbol lookup into a section relative relocation - the +relocation emitted has no symbol, just a section to relocate against. + +I'm not sure what it means when both a symbol pointer an a section +pointer are present. Some formats use this sort of mechanism to +describe PIC relocations, but bfd can't to that sort of thing yet. +@item howto +The howto field can be imagined as a relocation instruction. It is a +pointer to a struct which contains information on what to do with all +the other information in the reloc record and data section. A back end +would normally have a relocation instruction set and turn relocations +into pointers to the correct structure on input - but it would be +possible to create each howto field on demand. +@end table +*/ + + +/*proto* reloc_howto_type +The @code{reloc_howto_type} is a structure which contains all the +information that bfd needs to know to tie up a back end's data. + +*+++ + +$typedef CONST struct reloc_howto_struct +${ +The type field has mainly a documetary use - the back end can to what +it wants with it, though the normally the back end's external idea of +what a reloc number would be would be stored in this field. For +example, the a PC relative word relocation in a coff environment would +have the type 023 - because that's what the outside world calls a +R_PCRWORD reloc. + +$ unsigned int type; + +The value the final relocation is shifted right by. This drops +unwanted data from the relocation. + +$ unsigned int rightshift; + +The size of the item to be relocated - 0, is one byte, 1 is 2 bytes, 3 +is four bytes. + +$ unsigned int size; + +Now obsolete + +$ unsigned int bitsize; + +Notes that the relocation is relative to the location in the data +section of the addend. The relocation function will subtract from the +relocation value the address of the location being relocated. + +$ boolean pc_relative; + +Now obsolete + +$ unsigned int bitpos; + +Now obsolete + +$ boolean absolute; + +Causes the relocation routine to return an error if overflow is +detected when relocating. + +$ boolean complain_on_overflow; + +If this field is non null, then the supplied function is called rather +than the normal function. This allows really strange relocation +methods to be accomodated (eg, i960 callj instructions). + +$ bfd_reloc_status_enum_type (*special_function)(); + +The textual name of the relocation type. + +$ char *name; + +When performing a partial link, some formats must modify the +relocations rather than the data - this flag signals this. + +$ boolean partial_inplace; + +The src_mask is used to select what parts of the read in data are to +be used in the relocation sum. Eg, if this was an 8 bit bit of data +which we read and relocated, this would be 0x000000ff. When we have +relocs which have an addend, such as sun4 extended relocs, the value +in the offset part of a relocating field is garbage so we never use +it. In this case the mask would be 0x00000000. + +$ bfd_word src_mask; +The dst_mask is what parts of the instruction are replaced into the +instruction. In most cases src_mask == dst_mask, except in the above +special case, where dst_mask would be 0x000000ff, and src_mask would +be 0x00000000. + +$ bfd_word dst_mask; + +When some formats create PC relative instructions, they leave the +value of the pc of the place being relocated in the offset slot of the +instruction, so that a PC relative relocation can be made just by +adding in an ordinary offset (eg sun3 a.out). Some formats leave the +displacement part of an instruction empty (eg m88k bcs), this flag +signals the fact. + +$ boolean pcrel_offset; +$} reloc_howto_type; +*--- + +*/ + +/*proto* HOWTO +The HOWTO define is horrible and will go away. +*+ +#define HOWTO(C, R,S,B, P, BI, ABS, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC) \ + {(unsigned)C,R,S,B, P, BI, ABS,O,SF,NAME,INPLACE,MASKSRC,MASKDST,PC} +*- + +*/ + +/*proto* reloc_chain +*+ +typedef unsigned char bfd_byte; + +typedef struct relent_chain { + arelent relent; + struct relent_chain *next; +} arelent_chain; + +*- + +*/ + + + +/*proto* +If an output_bfd is supplied to this function the generated image +will be relocatable, the relocations are copied to the output file +after they have been changed to reflect the new state of the world. +There are two ways of reflecting the results of partial linkage in an +output file; by modifying the output data in place, and by modifying +the relocation record. Some native formats (eg basic a.out and basic +coff) have no way of specifying an addend in the relocation type, so +the addend has to go in the output data. This is no big deal since in +these formats the output data slot will always be big enough for the +addend. Complex reloc types with addends were invented to solve just +this problem. +*; PROTO(bfd_reloc_status_enum_type, + bfd_perform_relocation, + (bfd * abfd, + arelent *reloc_entry, + PTR data, + asection *input_section, + bfd *output_bfd)); +*/ + + +bfd_reloc_status_enum_type +DEFUN(bfd_perform_relocation,(abfd, + reloc_entry, + data, + input_section, + output_bfd), + bfd *abfd AND + arelent *reloc_entry AND + PTR data AND + asection *input_section AND + bfd *output_bfd) +{ + bfd_vma relocation; + bfd_reloc_status_enum_type flag = bfd_reloc_ok; + bfd_vma addr = reloc_entry->address ; + bfd_vma output_base = 0; + reloc_howto_type *howto = reloc_entry->howto; + asection *reloc_target_output_section; + asection *reloc_target_input_section; + asymbol *symbol; + + if (reloc_entry->sym_ptr_ptr) { + symbol = *( reloc_entry->sym_ptr_ptr); + if ((symbol->flags & BSF_UNDEFINED) && output_bfd == (bfd *)NULL) { + flag = bfd_reloc_undefined; + } + } + else { + symbol = (asymbol*)NULL; + } + + if (howto->special_function){ + bfd_reloc_status_enum_type cont; + cont = howto->special_function(abfd, + reloc_entry, + symbol, + data, + input_section); + if (cont != bfd_reloc_continue) return cont; + } + + /* + Work out which section the relocation is targetted at and the + initial relocation command value. + */ + + + if (symbol != (asymbol *)NULL){ + if (symbol->flags & BSF_FORT_COMM) { + relocation = 0; + } + else { + relocation = symbol->value; + } + if (symbol->section != (asection *)NULL) + { + reloc_target_input_section = symbol->section; + } + else { + reloc_target_input_section = (asection *)NULL; + } + } + else if (reloc_entry->section != (asection *)NULL) + { + relocation = 0; + reloc_target_input_section = reloc_entry->section; + } + else { + relocation = 0; + reloc_target_input_section = (asection *)NULL; + } + + + if (reloc_target_input_section != (asection *)NULL) { + + reloc_target_output_section = + reloc_target_input_section->output_section; + + if (output_bfd && howto->partial_inplace==false) { + output_base = 0; + } + else { + output_base = reloc_target_output_section->vma; + + } + + relocation += output_base + reloc_target_input_section->output_offset; + } + + relocation += reloc_entry->addend ; + + + if(reloc_entry->address > (bfd_vma)(input_section->size)) + { + return bfd_reloc_outofrange; + } + + + if (howto->pc_relative == true) + { + /* + Anything which started out as pc relative should end up that + way too. + + There are two ways we can see a pcrel instruction. Sometimes + the pcrel displacement has been partially calculated, it + includes the distance from the start of the section to the + instruction in it (eg sun3), and sometimes the field is + totally blank - eg m88kbcs. + */ + + + relocation -= + output_base + input_section->output_offset; + + if (howto->pcrel_offset == true) { + relocation -= reloc_entry->address; + } + + } + + if (output_bfd!= (bfd *)NULL) { + 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->section = reloc_target_input_section; + if (reloc_target_input_section != (asection *)NULL) { + /* If we know the output section we can forget the symbol */ + reloc_entry->sym_ptr_ptr = (asymbol**)NULL; + } + reloc_entry->address += + input_section->output_offset; + return flag; + } + else + { + /* This is a partial relocation, but inplace, so modify the + reloc record a bit + */ + + } + } + + reloc_entry->addend = 0; + + + /* + 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) + */ + relocation >>= howto->rightshift; + + /* Shift everything up to where it's going to be used */ + + relocation <<= 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)) + + switch (howto->size) + { + case 0: + { + char x = bfd_get_8(abfd, (char *)data + addr); + DOIT(x); + bfd_put_8(abfd,x, (unsigned char *) data + addr); + } + break; + + case 1: + { + short x = bfd_get_16(abfd, (bfd_byte *)data + addr); + DOIT(x); + bfd_put_16(abfd, x, (unsigned char *)data + addr); + } + break; + case 2: + { + long x = bfd_get_32(abfd, (bfd_byte *) data + addr); + DOIT(x); + bfd_put_32(abfd,x, (bfd_byte *)data + addr); + } + break; + case 3: + /* Do nothing */ + break; + default: + return bfd_reloc_other; + } + + return flag; +} |