/* @(#)crt0.s 1.1 92/07/30 SMI */

/*
 * "C" run-time program bootstrap.
 */

/*
 * Copyright (c) 1987 Sun Microsystems, Inc.
 */

#include <sys/syscall.h>
#include <sys/mman.h>

/*
 * Start a program by calling its "main" with argument list and environment
 * strings.  In addition, if program requires dynamic link editing, obtain
 * and hand control off to the link editor to do so.
 */

	.text
	.globl	start
start:					| Execution begins here
	lea	0,a6			| Initial stack frame link is 0
	movl	sp@,d2			| argc
	lea	sp@(4),a3		| argv
	movl	d2,d1			| Find end of argv[] as
	asll	#2,d1			|   sizeof(argv[0]) * argc
	lea	a3@(4,d1:l),a4		| Yields environment
	movl	a4,_environ		| Save for program's use
	tstl	__dp:l			| Does this program require editing?
	beq	1f			|   No
	bsr	__getrtld		| getrtld()
1:
	pea	a4@			| main(argc, argv, envp)
	pea	a3@
	movl	d2,sp@-
	jsr	start_float		| Initialize floating point
__main:
	jsr	_main			| Do the program
	addw	#12,sp			| Clean up
#ifdef OLD
	movl	#0,sp-			| Ignore main's return value
#else
	movl	d0,sp@-			| Use main's return value
#endif OLD
	jsr	_exit			| exit(status)
	addql	#4,sp			| In the event exit doesn't
	movl	d0,sp@-			|   then _exit(exit(status))
	jsr	__exit
	/*NOTREACHED*/

/*
 * __getrtld: load and call the link editor.  When a program is link edited
 * by "ld", a structure is created if the program is "incomplete".  Such
 * programs must "complete themselves" by getting the link editor involved
 * again.
 */

/* XXX Derive these constants */
#define	_RTLD_INTERFACE_	1	/* Interface version */
#define	FRAME	0x38			/* sizeof (struct exec) + locals */
#define	A_MACH	(-FRAME+0x0)		/* Offsets into struct exec */
#define	A_MAGIC	(-FRAME+0x2)
#define	A_TEXT	(-FRAME+0x4)
#define	A_DATA	(-FRAME+0x8)
#define	A_BSS	(-FRAME+0xc)
#define	A_ENTRY	(-FRAME+0x14)
#define	exec	a6@(-FRAME)		/* Symbols for stack offsets */
#define	a_mach	a6@(A_MACH)
#define	a_magic	a6@(A_MAGIC)
#define	a_text	a6@(A_TEXT)
#define	a_data	a6@(A_DATA)
#define	a_bss	a6@(A_BSS)
#define	a_entry	a6@(A_ENTRY)
#define	ldba	a6@(-0x18)		/* ld.so base address */
#define	fd_dz	a6@(-0x14)		/* fd on /dev/zero */
#define	fd_ld	a6@(-0x10)		/* fd on link editor */
#define	udp	a6@(-0xc)		/* &__DYNAMIC */
#define	ep	a6@(-0x8)		/* environ */
#define	break	a6@(-0x4)		/* &main */
#define	SEGSIZ	0x20000
#define	PAGSIZ	0x2000
#define	ZMAGIC	0413

__getrtld:
	link	a6,#-FRAME		| Allocate frame
	moveml	d2-d3,sp@-		| Save some registers

| Find and map in the link editor

	pea	__e_ld			| Find the link editor
	pea	0			| No flags
	pea	__link_editor		| File name
	jsr	__open			| fd_ld = open(link_editor, 0)
	addl	#0x8,sp
	movl	d0,fd_ld

| Read in its a.out header

	pea	0x20			| Size of buffer
	pea	exec			| Address of buffer
	movl	d0,sp@-			| fd_ld
	jsr	__read			| count = read(fd_ld, &exec, sizeof)
	addl	#0x10,sp		| Clean
	cmpl	#0x20,d0		| Get full count?
	jeq	1f			|   Yes
2:	lea	__e_ld,a0		| Otherwise
	jra	__errxit		|   dispatch error

| Determine if we've got a reasonable executable

1:	cmpw	#ZMAGIC,a_magic		| Demand-paged executable?
	jne	2b			|   Nope, this is fatal

| Determine how much of the address space the link editor will occupy

	movl	#(PAGSIZ-1),d2		| Starts one page up
	addl	a_text,d2		| Now figure out how long the text is
	andl	#~(SEGSIZ-1),d2		| Figure out last text segment and
	addl	#SEGSIZ,d2		|   determine data segment start
	movl	d2,d0			| d0 == amount for text + gap
	addl	a_data,d0		| d0 == text + gap + data
	addl	a_bss,d0		| d0 == text + gap + data + bss

| Map in the link editor

	pea	__e_mmap		| Set up for mapping
	pea	0			| No offset
	movl	fd_ld,sp@-		| fd on link editor
	movl	__mmap_assign,sp@- 	| Specify mapping options
	movl	__mmap_ro,sp@-		| Access protection
	movl	d0,sp@-			| How much we're mapping
	pea	0			| We don't care where it goes
	jsr	__mmap			| mmap(all of link editor)
	addl	#0x18,sp		| Clean up (but not error string)
	movl	d0,ldba			| Save the address

| Now, map in the data segment in its proper position

	movl	a_text,sp@-		| Offset in file to data segment
	movl	fd_ld,sp@-		| fd on link editor
	movl	__mmap_fixed,sp@-	| Mapping options
	movl	__mmap_prot,sp@-	| Protection
	movl	a_data,sp@-		| Map in the whole data segment
	movl	d2,d0			| Get relative start of data segment
	addl	ldba,d0			| Calculate absolute address
	movl	d0,sp@-
	jsr	__mmap			| mmap(abs, data segment)
	addl	#0x18,sp		| Clean up all but error string
	movl	d0,d3			| Save address where data segment went

| Unmap the gap between the text and data segments

	subl	a_text,d2		| Calculate size of gap
	movl	d2,sp@-			| Use that as length of unmapping
	movl	ldba,d0			| Set starting address as base +
	addl	a_text,d0		|   size of the text segment
	movl	d0,sp@-
	jsr	__munmap		| Unmap the gap
	addl	#0xc,sp			| Clear arguments + error string

| Obtain /dev/zero to build the bss from

	pea	__e_zero		| Set up for failure
	pea	0			| No flags
	pea	__dev_zero		| File name
	jsr	__open			| fd_dz = open(/dev/zero, 0)
	addql	#0x8,sp			| Pop non-error arguments
	movl	d0,fd_dz		|   and save file descriptor

| Map in enough to build bss from

	tstl	a_bss			| Any bss?
	jne	1f			|   No, skip all this
	addql	#4,sp			| Pop off error string
	jra	2f			|   and join common code
1:	pea	0			| Otherwise, map from offset 0
	movl	d0,sp@-			| Using /dev/zero fd
	movl	__mmap_fixed,sp@-	| PRIVATE, FIXED
	movl	__mmap_prot,sp@-	| All access
	movl	a_bss,sp@-		| Use a bss's worth
	addl	a_data,d3		| Starting at the end of the
	movl	d3,sp@-			|   data segment
	jsr	__mmap			| mmap(/dev/zero, a_bss)
	addl	#0x1c,sp		| Clear all arguments

| Fill in rest of argument list and call link editor

2:	lea	__main,a0		| Pass
	movl	a0,break		|   breakpoint address
	movl	_environ,ep		| Pass environment
	movl	__dp,udp		|   and this program's __DYNAMIC
	pea	ldba			| Prepare to call link editor
	pea	_RTLD_INTERFACE_	| Tell interface number
	movl	ldba,a0			| Get link editor base address
	addl	a_entry,a0		| Offset by entry point
	jsr	a0@			| Call it
	addql	#8,sp			| Clean
	moveml	sp@+,d2-d3		| Restore registers
	unlk	a6			|   and clean frame
	rts				|     and return

| System call interfaces

#define	STDERR	2			/* File descriptor for errors */
#define	STATUS	0x7f			/* Exit status */
#define	eo_open	sp@(0xc)		/* open() error pointer offset */
#define	eo_mmap sp@(0x1c)		/* mmap() */
#define	eo_munmap sp@(0xc)		/* munmap() */
#define	eo_read sp@(0x10)		/* read */

__open:	movl	eo_open,a0		| Get pointer to error structure
	pea	SYS_open		| Push trap code
	jra	__syscom		| Execute common code

__mmap:	movl	eo_mmap,a0		| See above
	pea	SYS_mmap
	jra	__syscom

__munmap:
	movl	eo_munmap,a0
	pea	SYS_munmap
	jra	__syscom

__read:	movl	eo_read,a0
	pea	SYS_read

__syscom:
	trap	#0			| Do the system call
	bcss	__errxit		| Error?
	rts				|   No, return success

__errxit:
	movl	a0@+,sp@-		| Push character count
	pea	a0@			|   and buffer address
	pea	STDERR
	pea	0			| Dummy
	pea	SYS_write
	trap	#0			| write(stderr, &msg, count)
	movl	#STATUS,sp@(4)
	pea	SYS_exit
	trap	#0			| _exit(status)
	/* NOTREACHED */

| Initialized, read-only data

	.even
__dp:	.long	__DYNAMIC		| ld makes this 0 or a pointer
__link_editor:				| Name of file containing link editor
	.asciz	"/usr/lib/ld.so"
__dev_zero:				| Name of source of zero'ed memory
	.asciz	"/dev/zero"
	.even
__e_ld:	.long	0x18			| Missing/trashed link editor
	.ascii	"crt0: no /usr/lib/ld.so\012"
	.even
__e_mmap:				| mmap/munmap don't work
	.long	0x25
	.ascii	"crt0: /usr/lib/ld.so mapping failure\012"
	.even
__e_zero:				| /dev/zero missing, unmappable
	.long	0x13
	.ascii	"crt0: no /dev/zero\012"
	.even
__mmap_assign:				| mmap flags for system assignment
	.long	_MAP_NEW+MAP_PRIVATE
__mmap_fixed:				| mmap flags for program assignment
	.long	_MAP_NEW+MAP_FIXED+MAP_PRIVATE
__mmap_prot:				| protection flags
	.long	PROT_READ+PROT_WRITE+PROT_EXEC
__mmap_ro:				| read only
	.long	PROT_READ+PROT_EXEC

| Writeable data

	.data
	.globl	_environ
_environ:
	.long	0
