Subversion Repositories HelenOS

Rev

Rev 2726 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/* reloc_ia64.S - position independent IA-64 ELF shared object relocator
   Copyright (C) 1999 Hewlett-Packard Co.
    Contributed by David Mosberger <davidm@hpl.hp.com>.

   This file is part of GNU-EFI, the GNU EFI development environment.

   GNU EFI 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 2, or (at your option)
   any later version.

   GNU EFI 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 GNU EFI; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA. */

/*
 * This is written in assembly because the entire code needs to be position
 * independent.  Note that the compiler does not generate code that's position
 * independent by itself because it relies on the global offset table being
 * relocated.
 */
    .text
    .psr abi64
    .psr lsb
    .lsb

/*
 * This constant determines how many R_IA64_FPTR64LSB relocations we
 * can deal with.  If you get EFI_BUFFER_TOO_SMALL errors, you may
 * need to increase this number.
 */
#define MAX_FUNCTION_DESCRIPTORS    750

#define ST_VALUE_OFF    8       /* offset of st_value in elf sym */

#define EFI_SUCCESS     0
#define EFI_LOAD_ERROR      1
#define EFI_BUFFER_TOO_SMALL    5

#define DT_NULL     0        /* Marks end of dynamic section */
#define DT_RELA     7       /* Address of Rela relocs */
#define DT_RELASZ   8       /* Total size of Rela relocs */
#define DT_RELAENT  9       /* Size of one Rela reloc */
#define DT_SYMTAB   6       /* Address of symbol table */
#define DT_SYMENT   11      /* Size of one symbol table entry */

#define R_IA64_NONE     0
#define R_IA64_REL64MSB     0x6e
#define R_IA64_REL64LSB     0x6f
#define R_IA64_DIR64MSB     0x26
#define R_IA64_DIR64LSB     0x27
#define R_IA64_FPTR64MSB    0x46
#define R_IA64_FPTR64LSB    0x47

#define ldbase  in0 /* load address (address of .text) */
#define dyn in1 /* address of _DYNAMIC */

#define d_tag   r16
#define d_val   r17
#define rela    r18
#define relasz  r19
#define relaent r20
#define addr    r21
#define r_info  r22
#define r_offset r23
#define r_addend r24
#define r_type  r25
#define r_sym   r25 /* alias of r_type ! */
#define fptr    r26
#define fptr_limit r27
#define symtab  f8
#define syment  f9
#define ftmp    f10

#define target  r16
#define val r17

#define NLOC    0

#define Pnull       p6
#define Prela       p7
#define Prelasz     p8
#define Prelaent    p9
#define Psymtab     p10
#define Psyment     p11

#define Pnone       p6
#define Prel        p7
#define Pfptr       p8

#define Pmore       p6

#define Poom        p6  /* out-of-memory */

    .global _relocate
    .proc _relocate
_relocate:
    alloc r2=ar.pfs,2,0,0,0
    movl    fptr = @gprel(fptr_mem_base)
    ;;
    add fptr = fptr, gp
    movl    fptr_limit = @gprel(fptr_mem_limit)
    ;;
    add fptr_limit = fptr_limit, gp

search_dynamic:
    ld8 d_tag = [dyn],8
    ;;
    ld8 d_val = [dyn],8
    cmp.eq  Pnull,p0 = DT_NULL,d_tag
(Pnull) br.cond.sptk.few apply_relocs
    cmp.eq  Prela,p0 = DT_RELA,d_tag
    cmp.eq  Prelasz,p0 = DT_RELASZ,d_tag
    cmp.eq  Psymtab,p0 = DT_SYMTAB,d_tag
    cmp.eq  Psyment,p0 = DT_SYMENT,d_tag
    cmp.eq  Prelaent,p0 = DT_RELAENT,d_tag
    ;;
(Prela) add rela = d_val, ldbase
(Prelasz) mov relasz = d_val
(Prelaent) mov relaent = d_val
(Psymtab) add val = d_val, ldbase
(Psyment) setf.sig syment = d_val
    ;;
(Psymtab) setf.sig symtab = val
    br.sptk.few search_dynamic

apply_loop:
    ld8 r_offset = [rela]
    add addr = 8,rela
    sub relasz = relasz,relaent
    ;;

    ld8 r_info = [addr],8
    ;;
    ld8 r_addend = [addr]
    add target = ldbase, r_offset

    add rela = rela,relaent
    extr.u  r_type = r_info, 0, 32
    ;;
    cmp.eq  Pnone,p0 = R_IA64_NONE,r_type
    cmp.eq  Prel,p0 = R_IA64_REL64LSB,r_type
    cmp.eq  Pfptr,p0 = R_IA64_FPTR64LSB,r_type
(Prel)  br.cond.sptk.few apply_REL64
    ;;
    cmp.eq  Prel,p0 = R_IA64_DIR64LSB,r_type // treat DIR64 just like REL64

(Pnone) br.cond.sptk.few apply_relocs
(Prel)  br.cond.sptk.few apply_REL64
(Pfptr) br.cond.sptk.few apply_FPTR64

    mov r8 = EFI_LOAD_ERROR
    br.ret.sptk.few rp

apply_relocs:
    cmp.ltu Pmore,p0=0,relasz
(Pmore) br.cond.sptk.few apply_loop

    mov r8 = EFI_SUCCESS
    br.ret.sptk.few rp

apply_REL64:
    ld8 val = [target]
    ;;
    add val = val,ldbase
    ;;
    st8 [target] = val
    br.cond.sptk.few apply_relocs

    // FPTR relocs are a bit more interesting: we need to lookup
    // the symbol's value in symtab, allocate 16 bytes of memory,
    // store the value in [target] in the first and the gp in the
    // second dword.
apply_FPTR64:
    st8 [target] = fptr
    extr.u  r_sym = r_info,32,32
    add target = 8,fptr
    ;;

    setf.sig ftmp = r_sym
    mov r8=EFI_BUFFER_TOO_SMALL
    ;;
    cmp.geu Poom,p0 = fptr,fptr_limit

    xma.lu  ftmp = ftmp,syment,symtab
(Poom)  br.ret.sptk.few rp
    ;;
    getf.sig addr = ftmp
    st8 [target] = gp
    ;;
    add addr = ST_VALUE_OFF, addr
    ;;
    ld8 val = [addr]
    ;;
    add val = val,ldbase
    ;;
    st8 [fptr] = val,16
    br.cond.sptk.few apply_relocs

    .endp _relocate

    .data
    .align 16
fptr_mem_base:
    .space  MAX_FUNCTION_DESCRIPTORS*16
fptr_mem_limit: