From 4e1896a1b54ec4315970e6770f394ecbb299b0a4 Mon Sep 17 00:00:00 2001
From: Kai Backman <kaib@golang.org>
Date: Fri, 10 Apr 2009 16:35:36 -0700
Subject: [PATCH] Adding ARM elf support to the 5l linker.

R=rsc
APPROVED=rsc
DELTA=312  (312 added, 0 deleted, 0 changed)
OCL=27133
CL=27326
---
 src/cmd/5l/asm.c | 292 +++++++++++++++++++++++++++++++++++++++++++++++
 src/cmd/5l/l.h   |  10 ++
 src/cmd/5l/obj.c |  10 ++
 3 files changed, 312 insertions(+)

diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c
index 214c9adfd4..f0f3185a47 100644
--- a/src/cmd/5l/asm.c
+++ b/src/cmd/5l/asm.c
@@ -64,8 +64,15 @@ asmb(void)
 {
 	Prog *p;
 	int32 t, etext;
+	int np;
+	vlong va, fo, w, symo;
+	int strtabsize;
+	vlong symdatva = 0x99LL<<24;
 	Optab *o;
 
+	strtabsize = 0;
+	symo = 0;
+
 	if(debug['v'])
 		Bprint(&bso, "%5.2f asm\n", cputime());
 	Bflush(&bso);
@@ -112,6 +119,7 @@ asmb(void)
 			datblk(t, etext-t, 1);
 	}
 
+	/* output section header strings */
 	curtext = P;
 	switch(HEADTYPE) {
 	case 0:
@@ -125,6 +133,13 @@ asmb(void)
 		OFFSET = rnd(HEADR+textsize, 4096);
 		seek(cout, OFFSET, 0);
 		break;
+	case 6:
+		seek(cout, rnd(HEADR+textsize, INITRND)+datsize, 0);
+		strtabsize = linuxstrtable();
+		cflush();
+		t = rnd(HEADR+textsize, INITRND);
+		seek(cout, t, 0);
+		break;
 	}
 	if(dlm){
 		char buf[8];
@@ -140,6 +155,7 @@ asmb(void)
 	}
 	cflush();
 
+	/* output symbol table */
 	symsize = 0;
 	lcsize = 0;
 	if(!debug['s']) {
@@ -161,6 +177,11 @@ asmb(void)
 			OFFSET += rnd(datsize, 4096);
 			seek(cout, OFFSET, 0);
 			break;
+		case 6:
+			symo = rnd(HEADR+textsize, INITRND)+datsize+strtabsize;
+			symo = rnd(symo, INITRND);
+			seek(cout, symo + 8, 0);
+			break;
 		}
 		if(!debug['s'])
 			asmsym();
@@ -251,6 +272,181 @@ asmb(void)
 		lputl(0xe3300000);		/* nop */
 		lputl(0xe3300000);		/* nop */
 		break;
+	case 6:
+		/* elf arm */
+		strnput("\177ELF", 4);		/* e_ident */
+		cput(1);			/* class = 32 bit */
+		cput(1);			/* data = LSB */
+		cput(1);			/* version = CURRENT */
+		strnput("", 9);
+
+		wputl(2);			/* type = EXEC */
+		wputl(40);			/* machine = ARM */
+		lputl(1L);			/* version = CURRENT */
+		lputl(entryvalue());		/* entry vaddr */
+		lputl(52L);			/* offset to first phdr */
+		np = 3;
+		if(!debug['s'])
+			np++;
+		lputl(52L+32*np);		/* offset to first shdr */
+		lputl(0L);			/* processor specific flags */
+		wputl(52);			/* Ehdr size */
+		wputl(32);			/* Phdr size */
+		wputl(np);			/* # of Phdrs */
+		wputl(40);			/* Shdr size */
+		if (!debug['s'])
+			wputl(7);			/* # of Shdrs */
+		else
+			wputl(5);			/* # of Shdrs */
+		wputl(4);			/* Shdr with strings */
+
+		fo = 0;
+		va = INITTEXT & ~((vlong)INITRND - 1);
+		w = HEADR+textsize;
+
+		linuxphdr(1,			/* text - type = PT_LOAD */
+			1L+4L,			/* text - flags = PF_X+PF_R */
+			0,			/* file offset */
+			va,			/* vaddr */
+			va,			/* paddr */
+			w,			/* file size */
+			w,			/* memory size */
+			INITRND);		/* alignment */
+
+		fo = rnd(fo+w, INITRND);
+		va = rnd(va+w, INITRND);
+		w = datsize;
+
+		linuxphdr(1,			/* data - type = PT_LOAD */
+			2L+4L,			/* data - flags = PF_W+PF_R */
+			fo,			/* file offset */
+			va,			/* vaddr */
+			va,			/* paddr */
+			w,			/* file size */
+			w+bsssize,		/* memory size */
+			INITRND);		/* alignment */
+
+		if(!debug['s']) {
+			linuxphdr(1,			/* data - type = PT_LOAD */
+				2L+4L,			/* data - flags = PF_W+PF_R */
+				symo,		/* file offset */
+				symdatva,			/* vaddr */
+				symdatva,			/* paddr */
+				8+symsize+lcsize,			/* file size */
+				8+symsize+lcsize,		/* memory size */
+				INITRND);		/* alignment */
+		}
+
+		linuxphdr(0x6474e551,		/* gok - type = gok */
+			1L+2L+4L,		/* gok - flags = PF_X+PF_W+PF_R */
+			0,			/* file offset */
+			0,			/* vaddr */
+			0,			/* paddr */
+			0,			/* file size */
+			0,			/* memory size */
+			8);			/* alignment */
+
+		linuxshdr(nil,			/* name */
+			0,			/* type */
+			0,			/* flags */
+			0,			/* addr */
+			0,			/* off */
+			0,			/* size */
+			0,			/* link */
+			0,			/* info */
+			0,			/* align */
+			0);			/* entsize */
+
+		stroffset = 1;  /* 0 means no name, so start at 1 */
+		fo = HEADR;
+		va = (INITTEXT & ~((vlong)INITRND - 1)) + HEADR;
+		w = textsize;
+
+		linuxshdr(".text",		/* name */
+			1,			/* type */
+			6,			/* flags */
+			va,			/* addr */
+			fo,			/* off */
+			w,			/* size */
+			0,			/* link */
+			0,			/* info */
+			8,			/* align */
+			0);			/* entsize */
+
+		fo = rnd(fo+w, INITRND);
+		va = rnd(va+w, INITRND);
+		w = datsize;
+
+		linuxshdr(".data",		/* name */
+			1,			/* type */
+			3,			/* flags */
+			va,			/* addr */
+			fo,			/* off */
+			w,			/* size */
+			0,			/* link */
+			0,			/* info */
+			8,			/* align */
+			0);			/* entsize */
+
+		fo += w;
+		va += w;
+		w = bsssize;
+
+		linuxshdr(".bss",		/* name */
+			8,			/* type */
+			3,			/* flags */
+			va,			/* addr */
+			fo,			/* off */
+			w,			/* size */
+			0,			/* link */
+			0,			/* info */
+			8,			/* align */
+			0);			/* entsize */
+
+		w = strtabsize;
+
+		linuxshdr(".shstrtab",		/* name */
+			3,			/* type */
+			0,			/* flags */
+			0,			/* addr */
+			fo,			/* off */
+			w,			/* size */
+			0,			/* link */
+			0,			/* info */
+			1,			/* align */
+			0);			/* entsize */
+
+		if (debug['s'])
+			break;
+
+		fo = symo+8;
+		w = symsize;
+
+		linuxshdr(".gosymtab",		/* name */
+			1,			/* type 1 = SHT_PROGBITS */
+			0,			/* flags */
+			0,			/* addr */
+			fo,			/* off */
+			w,			/* size */
+			0,			/* link */
+			0,			/* info */
+			1,			/* align */
+			24);			/* entsize */
+
+		fo += w;
+		w = lcsize;
+
+		linuxshdr(".gopclntab",		/* name */
+			1,			/* type 1 = SHT_PROGBITS*/
+			0,			/* flags */
+			0,			/* addr */
+			fo,			/* off */
+			w,			/* size */
+			0,			/* link */
+			0,			/* info */
+			1,			/* align */
+			24);			/* entsize */
+		break;
 	}
 	cflush();
 	if(debug['c']){
@@ -306,6 +502,14 @@ wput(int32 l)
 		cflush();
 }
 
+void
+wputl(ushort w)
+{
+	cput(w);
+	cput(w>>8);
+}
+
+
 void
 hput(int32 l)
 {
@@ -1829,3 +2033,91 @@ chipfloat(Ieee *e)
 	}
 	return -1;
 }
+
+uint32
+linuxheadr(void)
+{
+	uint32 a;
+
+	a = 64;		/* a.out header */
+
+	a += 56;	/* page zero seg */
+	a += 56;	/* text seg */
+	a += 56;	/* stack seg */
+
+	a += 64;	/* nil sect */
+	a += 64;	/* .text sect */
+	a += 64;	/* .data seg */
+	a += 64;	/* .bss sect */
+	a += 64;	/* .shstrtab sect - strings for headers */
+	if (!debug['s']) {
+		a += 56;	/* symdat seg */
+		a += 64;	/* .gosymtab sect */
+		a += 64;	/* .gopclntab sect */
+	}
+
+	return a;
+}
+
+void
+linuxphdr(int type, int flags, vlong foff,
+	vlong vaddr, vlong paddr,
+	vlong filesize, vlong memsize, vlong align)
+{
+
+	lputl(type);			/* text - type = PT_LOAD */
+	lputl(foff);			/* file offset */
+	lputl(vaddr);			/* vaddr */
+	lputl(paddr);			/* paddr */
+	lputl(filesize);		/* file size */
+	lputl(memsize);		/* memory size */
+	lputl(flags);			/* text - flags = PF_X+PF_R */
+	lputl(align);			/* alignment */
+}
+
+void
+linuxshdr(char *name, uint32 type, vlong flags, vlong addr, vlong off,
+	vlong size, uint32 link, uint32 info, vlong align, vlong entsize)
+{
+	lputl(stroffset);
+	lputl(type);
+	lputl(flags);
+	lputl(addr);
+	lputl(off);
+	lputl(size);
+	lputl(link);
+	lputl(info);
+	lputl(align);
+	lputl(entsize);
+
+	if(name != nil)
+		stroffset += strlen(name)+1;
+}
+
+int
+putstrtab(char* name)
+{
+	int w;
+
+	w = strlen(name)+1;
+	strnput(name, w);
+	return w;
+}
+
+int
+linuxstrtable(void)
+{
+	int size;
+
+	size = 0;
+	size += putstrtab("");
+	size += putstrtab(".text");
+	size += putstrtab(".data");
+	size += putstrtab(".bss");
+	size += putstrtab(".shstrtab");
+	if (!debug['s']) {
+		size += putstrtab(".gosymtab");
+		size += putstrtab(".gopclntab");
+	}
+	return size;
+}
diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h
index dc578f5f9c..8cd9d2010b 100644
--- a/src/cmd/5l/l.h
+++ b/src/cmd/5l/l.h
@@ -323,6 +323,7 @@ EXTERN	Oprang	thumboprange[ALAST];
 EXTERN	char*	outfile;
 EXTERN	int32	pc;
 EXTERN	uchar	repop[ALAST];
+EXTERN	uint32	stroffset;
 EXTERN	int32	symsize;
 EXTERN	Prog*	textp;
 EXTERN	int32	textsize;
@@ -456,6 +457,7 @@ void	strnput(char*, int);
 void	undef(void);
 void	undefsym(Sym*);
 void	wput(int32);
+void    wputl(ushort w);
 void	xdefine(char*, int, int32);
 void	xfol(Prog*);
 void	zerosig(char*);
@@ -471,4 +473,12 @@ void	thumbcount(void);
 void reachable(void);
 void fnptrs(void);
 
+uint32	linuxheadr(void);
+void	linuxphdr(int type, int flags, vlong foff,
+	vlong vaddr, vlong paddr,
+	vlong filesize, vlong memsize, vlong align);
+void	linuxshdr(char *name, uint32 type, vlong flags, vlong addr, vlong off,
+	vlong size, uint32 link, uint32 info, vlong align, vlong entsize);
+int	linuxstrtable(void);
+
 #endif
diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c
index 275c3fe6e2..990c3597e1 100644
--- a/src/cmd/5l/obj.c
+++ b/src/cmd/5l/obj.c
@@ -153,6 +153,7 @@ main(int argc, char *argv[])
 			HEADTYPE = 1;
 		if(debug['9'])
 			HEADTYPE = 2;
+		HEADTYPE = 6;
 	}
 	switch(HEADTYPE) {
 	default:
@@ -212,6 +213,15 @@ main(int argc, char *argv[])
 		if(INITRND == -1)
 			INITRND = 1024;
 		break;
+	case 6:	/* arm elf */
+		HEADR = linuxheadr();
+		if(INITTEXT == -1)
+			INITTEXT = 0x8000+HEADR;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4096;
+		break;
 	}
 	if(INITDAT != 0 && INITRND != 0)
 		print("warning: -D0x%lux is ignored because of -R0x%lux\n",
-- 
2.30.9