/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * C runtime startup for m68k, i386 and ppc interface to the dynamic linker.
 * This is the same as the entry point in crt0.o with the addition of the
 * address of the mach header passed as the an extra first argument.
 *
 * Kernel sets up stack frame to look like:
 *
 *	| STRING AREA |
 *	+-------------+
#if defined(__MACH30__) && (defined(__ppc__) || defined(__i386__))
 *	+-------------+
 *	|      0      |	In MacOS X PR2 Beaker2E the path passed to exec() is
 *	+-------------+	passed on the stack just after the trailing 0 of the
 *	|  exec_path  | the envp[] array as a pointer to a string.  See the
 *	+-------------+ code at the end of pickup_environment_variables().
#endif
 *	+-------------+
 *	|      0      |
 *	+-------------+
 *	|    env[n]   |
 *	+-------------+
 *	       :
 *	       :
 *	+-------------+
 *	|    env[0]   |
 *	+-------------+
 *	|      0      |
 *	+-------------+
 *	| arg[argc-1] |
 *	+-------------+
 *	       :
 *	       :
 *	+-------------+
 *	|    arg[0]   |
 *	+-------------+
 *	|     argc    |
 *	+-------------+
 * sp->	|      mh     | address of where the a.out's file offset 0 is in memory
 *	+-------------+
 *
 *	Where arg[i] and env[i] point into the STRING AREA
 */
#ifdef m68k
	.text
	.even
L_start:
	.stabs "dyld_start.s",100,0,0,L_start
	.stabs "int:t1=r1;-2147483648;2147483647;",128,0,0,0
	.stabs "char:t2=r2;0;127;",128,0,0,0

	.globl __dyld_start
__dyld_start:
	lea	pc@(-2),a1	| Set d1 to the difference between where we
	movel	a1,d1		|  started executing at and __dyld_start to know
	subl	#__dyld_start,d1|  if the kernel slid this code.

	movl	sp,a0		| pointer to base of kernel frame
	subw	#16,sp		| room for new mh, argc, argv, & envp
	movl	a0@+,sp@	| mh to reserved stack word and a0 to pt to argc
	movl	a0@+,d0		| pickup argc and bump a0 to pt to arg[0]
	movl	d0,sp@(4)	| argc to reserved stack word
	movl	a0,sp@(8)	| argv to reserved stack word
	addql	#1,d0		| argc + 1 for zero word
	asll	#2,d0		| * sizeof(char *)
	addl	d0,a0		| addr of env[0]
	movl	a0,sp@(12)	| envp to reserved stack word

	cmpl	#0,d1		| If this code was slid we have to relocate
	beq	1f		|  ourself before we get going.
	movel	d1,sp@-
	bsr	__dyld_reloc	| _dyld_reloc(slide_amount)
	addw	#4,sp
1:
	bsr	_mach_init	| call mach_init() so we can make mach calls

	bsr	__dyld_init	| _dyld_init(mh, argc, argv, envp)
	movl	d0,a0		| put entry point in a0
	addw	#20,sp		| deallocate room for new mh, argc, argv, & envp
				|  and remove the mh argument
	jmp	a0@		| jump to the entry point

	.globl dyld_stub_binding_helper
dyld_stub_binding_helper:
	trapt.l	#0xfeadface

	.stabs "",100,0,0,L_end
L_end:
#endif /* m68k */

#ifdef __i386__
	.text
	.align	4, 0x90
L_start:
	.stabs "dyld_start.s",100,0,0,L_start
	.stabs "int:t1=r1;-2147483648;2147483647;",128,0,0,0
	.stabs "char:t2=r2;0;127;",128,0,0,0

	.globl __dyld_start
__dyld_start:
	call	L1			# Set %eax to the difference between
L1:	popl	%eax			#  where we started executing at and
	subl	$L1-__dyld_start,%eax	#  __dyld_start to know if the kernel
	subl	$__dyld_start,%eax	#  slid this code.

	pushl	$0		# push a zero for debugger end of frames marker
	movl	%esp,%ebp	# pointer to base of kernel frame
	andl	$-16,%esp	# force SSE alignment
	subl	$16,%esp	# room for new mh, argc, argv, & envp
	movl	4(%ebp),%ebx	# pickup mh in %ebx
	movl	%ebx,0(%esp)	# mh to reserved stack word
	movl	8(%ebp),%ebx	# pickup argc in %ebx
	movl	%ebx,4(%esp)	# argc to reserved stack word
	lea	12(%ebp),%ecx	# addr of arg[0], argv, into %ecx
	movl	%ecx,8(%esp)	# argv to reserved stack word
	addl	$1,%ebx		# argc + 1 for zero word
	sall	$2,%ebx		# * sizeof(char *)
	addl	%ecx,%ebx	# addr of env[0], envp, into %ebx
	movl	%ebx,12(%esp)	# envp to reserved stack word

	cmpl	$0,%eax		# If this code was slid we have to relocate
	je	1f		#  ourself before we get going.
	pushl	%eax
	call	__dyld_reloc	# _dyld_reloc(slide_amount)
	addl	$4,%esp
1:
	call	_mach_init	# call mach_init() so we can make mach calls

	call	__dyld_init	# _dyld_init(mh, argc, argv, envp)
	movl	%ebp,%esp	# restore the unaligned stack pointer
	addl	$8,%esp		# remove the mh argument, and debugger end
				#  frame marker
	movl	$0,%ebp		# restore ebp back to zero
	jmp	%eax		# jump to the entry point

	.globl dyld_stub_binding_helper
dyld_stub_binding_helper:
	hlt

	.stabs "",100,0,0,L_end
L_end:
#endif /* __i386__ */

#ifdef __ppc__
	.text
	.align 2
L_start:
	.stabs "dyld_start.s",100,0,0,L_start
	.stabs "int:t1=r1;-2147483648;2147483647;",128,0,0,0
	.stabs "char:t2=r2;0;127;",128,0,0,0

	.globl __dyld_start
__dyld_start:
	bcl	20,31,L1	; put address of L1 in r11
L1:	mflr	r11		; Calculate the difference
	lis	r12,hi16(L1)	;  between where this code was
	ori	r12,r12,lo16(L1);  loaded and where it was
	sub	r11,r11,r12	;  staticly linked as the slide into r11

	mr	r26,r1		; save original stack pointer into r26
	mr	r3,r1		; get pointer to base of kernel frame into r3
	subi	r1,r1,64	; minimum space on stack for arguments
	clrrwi	r1,r1,5		; align to 32 bytes
	lwz	r30,0(r3)	; get mach_header into r30
	lwz	r29,4(r3)	; get argc into r29
	addi	r28,r3,8	; get argv into r28
	addi	r4,r29,1	; calculate argc + 1 into r4
	slwi	r4,r4,2		; calculate (argc + 1) * sizeof(char *) into r4
	add	r27,r28,r4	; get address of env[0] into r27

	cmpwi	cr1,r11,0	; If this code was slid we have to relocate
	beq+    cr1,L2		;  ourself before we get going.
	mr	r3,r11		; slide_amount was calculate into r11 above
	bl	__dyld_reloc    ; _dyld_reloc(slide_amount)
L2:
	bl	_mach_init	; call mach_init() so we can make mach calls

	mr	r3,r30		; move mach_header into r3
	mr	r4,r29		; move argc into r4
	mr	r5,r28		; move argv into r5
	mr	r6,r27		; move envp into r6
	bl	__dyld_init	; _dyld_init(mh, argc, argv, envp)
	mtctr	r3		; Put entry point in count register
	mr	r12,r3		;  also put in r12 for ABI convention.
	addi	r1,r26,4	; Restore the stack pointer and remove the
				;  mach_header argument.
	bctr			; jump to the entry point

	.globl dyld_stub_binding_helper
dyld_stub_binding_helper:
	trap

	.stabs "",100,0,0,L_end
L_end:
#endif /* __ppc__ */

#ifdef hppa
/*
 * C runtime startup for hppa interface to the dynamic linker.  This is the same
 * as the entry point in crt0.o with the addition of the address of the mach
 * header (mh) passed as the an extra first argument.
 *
 * Kernel sets up stack frame to look like:
 *
 *	+-------------+
 * sp->	|     ap      |        HIGH ADDRESS
 *	+-------------+
 *	|      0      | <-xxx (I don't know what this value is called
 *	+-------------+	       but it is the first thing on the stack see
 *	| STRING AREA |	       below at the lowest address).
 *	+-------------+
 *	|      0      |
 *	+-------------+
 *	|    env[n]   |
 *	+-------------+
 *	       :
 *	       :
 *	+-------------+
 *	|    env[0]   |
 *	+-------------+
 *	|      0      |
 *	+-------------+
 *	| arg[argc-1] |
 *	+-------------+
 *	       :
 *	       :
 *	+-------------+
 *	|    arg[0]   |
 *	+-------------+
 * ap->	|     argc    |
 *	+-------------+
 *	|      mh     |
 *	+-------------+
 *	|     xxx     |        LOW ADDRESS (first word on the stack)
 *	+-------------+
 *
 *	Where arg[i] and env[i] point into the STRING AREA
 *
 *	Initial sp points to a pointer to argc.  Stack then
 *	grows upward from sp.  [Special calling convention for
 *	PA-RISC and other architectures which grow upward].
 */
	.text
	.align	2
L_start:
	.stabs "dyld_start.s",100,0,0,L_start
	.stabs "int:t1=r1;-2147483648;2147483647;",128,0,0,0
	.stabs "char:t2=r2;0;127;",128,0,0,0

	.globl __dyld_start
__dyld_start:
	bl	L1,%r26			; put address of L1 in r26
	nop
L1:	depi	0,31,2,%r26		; clear protection bits
	ldil	L`L1-__dyld_start,%r1	; Calculate the difference between where
	ldo	R`L1-__dyld_start(%r1),%r1 ;  this code was loaded and where it
	sub	%r26,%r1,%r26		;  was staticly linked as the slide
	ldil	L`__dyld_start,%r2	;  amount and call _dyld_reloc() if the
	ldo	R`__dyld_start(%r2),%r2	;  slide amount is not zero.
	sub	%r26,%r2,%r26

	; adjust and align the stack and create the first frame area
	copy	%r30,%r3	  	; save stack pointer value in r3
	ldo	127(%r30),%r30		; setup stack pointer on the next 64
	depi	0,31,6,%r30		;  byte boundary for calls

	combt,=,n %r26,%r0,L2		; if slide amount is not zero call
	bl	__dyld_reloc,%r2	;  _dyld_reloc(slide_amount)
	nop
L2:
	bl	_mach_init,%r2	; call mach_init() so mach calls work
	nop

	; load the arguments to call _dyld_init(mh, argc, argv, envp)
	ldw	0(%r3),%r19	; load the value of ap in r19
	ldw	-4(%r19),%r26	; load the value of mh in arg0
	ldw	0(%r19),%r25	; load the value of argc in arg1
	ldo	4(%r19),%r24	; load the address of arg[0] in arg2
	; calculate the value for envp by walking the arg[] pointers
	copy	%r24,%r23	; start with the address of arg[0] in arg3
	ldw	0(%r23),%r20	; load the value of the arg[0] pointer
	comib,=	0,%r20,L4	; if 0 go on to call _dyld_init()

	ldws,mb 4(0,%r23),%r20	; load next pointer value and advance r23
L3:
	comib,<>,n 0,%r20,L3	; if next pointer is not 0 loop
	ldws,mb 4(0,%r23),%r20	; load next pointer value and advance r23
	
L4:
	bl	__dyld_init,%r2	; _dyld_init(mh, argc, argv, envp)
	ldo	4(%r23),%r23	; load address of the next pointer after the 0

	; adjust the stack back the way we came in
	bv	0(%r28)		; jump to the entry point
	copy	%r3,%r30  	; put back the value of the stack pointer

	.globl dyld_stub_binding_helper
dyld_stub_binding_helper:
	break	0,0

	.stabs "",100,0,0,L_end
L_end:
#endif /* hppa */

#ifdef sparc
/*
 *
 * Kernel sets up stack frame to look like:
 *
 * On entry the stack frame looks like:
 *
 *       _______________________  <- USRSTACK
 *      |           :           |
 *      |  arg and env strings  |
 *      |           :           |
 *      |-----------------------|
 *      |           0           |
 *      |-----------------------|
 *      |           :           |
 *      |  ptrs to env strings  |
 *      |-----------------------|
 *      |           0           |
 *      |-----------------------|
 *      |           :           |
 *      |  ptrs to arg strings  |
 *      |   (argc = # of wds)   |
 *      |-----------------------|
 *      |          argc         |
 *      |-----------------------|
 *	|           mh  	| <- new mach header arg for dyld
 *	|-----------------------|
 *      |    window save area   |
 *      |        (16 wds)       |
 *      |_______________________| <- %sp
 */


	.text
	.align	2
L_start:
	.stabs "dyld_start.s",100,0,0,L_start
	.stabs "int:t1=r1;-2147483648;2147483647;",128,0,0,0
	.stabs "char:t2=r2;0;127;",128,0,0,0

	.globl __dyld_start
__dyld_start:
	! determine our location and slide code if necessary
	save	%sp, -96, %sp		! get ready to make calls
	call	L1
	dec	4,%o7
L1:
	sethi	%hi(__dyld_start),%l0	! get our static address
	or	%l0,%lo(__dyld_start),%l0
	sub	%o7,%l0,%l0		! and compare with entry pc

	tst	%l0
	bz	1f			! not slid
	nop
	call	__dyld_reloc		! relocate ourself
	mov	%l0,%o0

1:
	call	_mach_init	! allows us to make mach calls
	nop
	! build arguments to dyld_init (mh, argc, argv, envp)
	mov	%fp,%l0
	add	%l0,16*4,%l0	! skip over window save area
	ld	[%l0],%o0	! 1st arg: mh ptr
	ld	[%l0+4],%o1	! 2nd arg: argc
	mov	%l0,%o2
	inc	8,%o2		! 3rd arg: argv ptr
	! now compute the envp ptr
	add	%o1,1,%o3	! account for null ptr
	sll	%o3,2,%o3	! adjust for ptrs
	
	call	__dyld_init	! dyld_init returns our entry point
	add	%o2,%o3,%o3	! 4th arg: envp ptr	

	jmp	%o0		! call the entry point
	restore
	
	.globl dyld_stub_binding_helper
dyld_stub_binding_helper:

	ta	9

	.stabs "",100,0,0,L_end
L_end:
#endif /* sparc */
