/* PLT trampolines.  ARM version.
   Copyright (C) 2005 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#include <sysdep.h>
#include <libc-symbols.h>

#if defined(__USE_BX__)
#define BX(x) bx	x
#else
#define BX(x) mov	pc, x
#endif

	.text
	.globl _dl_runtime_resolve
	.type _dl_runtime_resolve, #function
	cfi_startproc
	.align 2
_dl_runtime_resolve:
	cfi_adjust_cfa_offset (4)
	cfi_rel_offset (lr, 0)

	@ we get called with
	@ 	stack[0] contains the return address from this call
	@	ip contains &GOT[n+3] (pointer to function)
	@	lr points to &GOT[2]

	@ Save arguments.  We save r4 to realign the stack.
	stmdb	sp!,{r0-r4}
	cfi_adjust_cfa_offset (20)
	cfi_rel_offset (r0, 0)
	cfi_rel_offset (r1, 4)
	cfi_rel_offset (r2, 8)
	cfi_rel_offset (r3, 12)

	@ get pointer to linker struct
	ldr	r0, [lr, #-4]

	@ prepare to call _dl_fixup()
	@ change &GOT[n+3] into 8*n        NOTE: reloc are 8 bytes each
	sub	r1, ip, lr
	sub	r1, r1, #4
	add	r1, r1, r1

	@ call fixup routine
	bl	_dl_fixup

	@ save the return
	mov	ip, r0

	@ get arguments and return address back.  We restore r4
	@ only to realign the stack.
	ldmia	sp!, {r0-r4,lr}
	cfi_adjust_cfa_offset (-24)

	@ jump to the newly found address
	BX(ip)

	cfi_endproc
	.size _dl_runtime_resolve, .-_dl_runtime_resolve

#ifndef PROF
	.globl _dl_runtime_profile
	.type _dl_runtime_profile, #function
	cfi_startproc
	.align 2
_dl_runtime_profile:
	cfi_adjust_cfa_offset (4)
	cfi_rel_offset (lr, 0)

	@ we get called with
	@ 	stack[0] contains the return address from this call
	@	ip contains &GOT[n+3] (pointer to function)
	@	lr points to &GOT[2]

	@ Stack layout:
	@ 212 - saved lr
	@ 208 - framesize returned from pltenter
	@ 16 - La_arm_regs
	@ 8 - Saved two arguments to _dl_profile_fixup
	@ 4 - Saved result of _dl_profile_fixup
	@ 0 - outgoing argument to _dl_profile_fixup
	@ For now, we only save the general purpose registers.

	sub	sp, sp, #196
	cfi_adjust_cfa_offset (196)
	stmia	sp, {r0-r3}
	cfi_rel_offset (r0, 0)
	cfi_rel_offset (r1, 4)
	cfi_rel_offset (r2, 8)
	cfi_rel_offset (r3, 12)

	sub	sp, sp, #16
	cfi_adjust_cfa_offset (16)

	@ Save sp and lr.
	add	r0, sp, #216
	str	r0, [sp, #32]
	ldr	r2, [sp, #212]
	str	r2, [sp, #36]

	@ get pointer to linker struct
	ldr	r0, [lr, #-4]

	@ prepare to call _dl_profile_fixup()
	@ change &GOT[n+3] into 8*n        NOTE: reloc are 8 bytes each
	sub	r1, ip, lr
	sub	r1, r1, #4
	add	r1, r1, r1

	@ Save these two arguments for pltexit.
	add	r3, sp, #8
	stmia	r3!, {r0,r1}

	@ Set up extra args for _dl_profile_fixup.
	@ r2 and r3 are already loaded.
	add	ip, sp, #208
	str	ip, [sp, #0]

	@ call profiling fixup routine
	bl	_dl_profile_fixup

	@ The address to call is now in r0.

	@ Check whether we're wrapping this function.
	ldr	ip, [sp, #208]
	cmp	ip, #0
	bge	1f
	cfi_remember_state

	@ save the return
	mov	ip, r0

	@ get arguments and return address back
	add	sp, sp, #16
	cfi_adjust_cfa_offset (-16)
	ldmia	sp, {r0-r3,sp,lr}
	cfi_adjust_cfa_offset (-200)

	@ jump to the newly found address
	BX(ip)

	cfi_restore_state
1:
	@ The new frame size is in ip.

	@ New stack layout:
	@ 268 - saved r7
	@ 264 - saved result of _dl_profile_fixup
	@ 72 - La_arm_regs
	@ 64 - Saved two arguments to _dl_profile_fixup
	@ 0 - La_arm_retval
	@ For now, we only save the general purpose registers.

	@ Build the new frame.
	str	r7, [sp, #212]
	cfi_rel_offset (r7, 212)
	sub	r7, sp, #56
	cfi_def_cfa_register (r7)
	cfi_adjust_cfa_offset (56)
	sub	sp, sp, ip
	bic	sp, sp, #7

	@ Save the _dl_profile_fixup result around the call to memcpy.
	str	r0, [r7, #264]

	@ Copy the stack arguments.
	mov	r0, sp
	add	r1, r7, #272
	mov	r2, ip
	bl	memcpy

	@ Call the function.
	add	ip, r7, #72
	ldmia	ip, {r0-r3}
	ldr	ip, [r7, #264]
	mov	lr, pc
	BX(ip)
	stmia	r7, {r0-r3}

	@ Call pltexit.
	add	ip, r7, #64
	ldmia	ip, {r0,r1}
	add	r2, r7, #72
	add	r3, r7, #0
	bl	_dl_call_pltexit

	@ Return to caller.
	ldmia	r7, {r0-r3}
	mov	sp, r7
	cfi_def_cfa_register (sp)
	ldr	r7, [sp, #268]
	ldr	lr, [sp, #92]
	add	sp, sp, #272
	cfi_adjust_cfa_offset (-272)
	BX(lr)

	cfi_endproc
	.size _dl_runtime_profile, .-_dl_runtime_profile
#endif
	.previous
