6l/8l: emit DWARF frame info.

R=rsc, ken2, r
CC=golang-dev
https://golang.org/cl/2151044
diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h
index 22f266f..b8f47fc 100644
--- a/src/cmd/6l/l.h
+++ b/src/cmd/6l/l.h
@@ -95,6 +95,7 @@
 	Prog*	dlink;
 	Prog*	pcond;	/* work on this */
 	vlong	pc;
+	int32	spadj;
 	int32	line;
 	short	as;
 	char	ft;	/* oclass cache */
@@ -102,7 +103,7 @@
 	uchar	mark;	/* work on these */
 	uchar	back;
 
-	char	width;		/* fake for DATA */
+	char	width;	/* fake for DATA */
 	char	mode;	/* 16, 32, or 64 */
 };
 struct	Auto
@@ -397,7 +398,7 @@
 void	cflush(void);
 void	ckoff(Sym*, int32);
 Prog*	copyp(Prog*);
-vlong   cpos(void);
+vlong	cpos(void);
 double	cputime(void);
 void	datblk(int32, int32);
 void	deadcode(void);
@@ -463,3 +464,9 @@
 #pragma	varargck	type	"R"	int
 #pragma	varargck	type	"A"	int
 #pragma	varargck	argpos	diag 1
+
+/* Used by ../ld/dwarf.c */
+enum
+{
+	DWARFREGSP = 7
+};
diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c
index ea33512..d207e74 100644
--- a/src/cmd/6l/pass.c
+++ b/src/cmd/6l/pass.c
@@ -692,7 +692,7 @@
 					p->from.type = D_INDIR+D_GS;
 				p->from.offset = tlsoffset+0;
 				p->to.type = D_CX;
-				
+
 				if(debug['K']) {
 					// 6l -K means check not only for stack
 					// overflow but stack underflow.
@@ -843,6 +843,7 @@
 				p->as = AADJSP;
 				p->from.type = D_CONST;
 				p->from.offset = autoffset;
+				p->spadj = autoffset;
 				if(q != P)
 					q->pcond = p;
 			}
@@ -903,26 +904,32 @@
 		case APUSHL:
 		case APUSHFL:
 			deltasp += 4;
+			p->spadj = 4;
 			continue;
 		case APUSHQ:
 		case APUSHFQ:
 			deltasp += 8;
+			p->spadj = 8;
 			continue;
 		case APUSHW:
 		case APUSHFW:
 			deltasp += 2;
+			p->spadj = 2;
 			continue;
 		case APOPL:
 		case APOPFL:
 			deltasp -= 4;
+			p->spadj = -4;
 			continue;
 		case APOPQ:
 		case APOPFQ:
 			deltasp -= 8;
+			p->spadj = -8;
 			continue;
 		case APOPW:
 		case APOPFW:
 			deltasp -= 2;
+			p->spadj = -2;
 			continue;
 		case ARET:
 			break;
@@ -937,7 +944,7 @@
 			p->as = AADJSP;
 			p->from.type = D_CONST;
 			p->from.offset = -autoffset;
-
+			p->spadj = -autoffset;
 			p = appendp(p);
 			p->as = ARET;
 		}
@@ -954,6 +961,7 @@
 		q->from = zprg.from;
 		q->from.type = D_CONST;
 		q->from.offset = -autoffset;
+		q->spadj = -autoffset;
 		q->to = zprg.to;
 		continue;
 	}
diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h
index e3b53f2..489f1ae 100644
--- a/src/cmd/8l/l.h
+++ b/src/cmd/8l/l.h
@@ -95,6 +95,7 @@
 	Prog*	dlink;
 	Prog*	pcond;	/* work on this */
 	int32	pc;
+	int32	spadj;
 	int32	line;
 	short	as;
 	char	width;		/* fake for DATA */
@@ -346,7 +347,7 @@
 void	cflush(void);
 void	ckoff(Sym*, int32);
 Prog*	copyp(Prog*);
-vlong   cpos(void);
+vlong	cpos(void);
 double	cputime(void);
 void	datblk(int32, int32, int32);
 void	diag(char*, ...);
@@ -412,3 +413,9 @@
 #pragma	varargck	type	"P"	Prog*
 #pragma	varargck	type	"R"	int
 #pragma	varargck	type	"A"	int
+
+/* Used by ../ld/dwarf.c */
+enum
+{
+	DWARFREGSP = 4
+};
diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c
index bf09fd9..f59ccdb 100644
--- a/src/cmd/8l/pass.c
+++ b/src/cmd/8l/pass.c
@@ -392,10 +392,10 @@
 	for(p = firstp; p != P; p = p->link) {
 		if(HEADTYPE == 10) {	// Windows
 			// Convert
-			//   op   n(GS), reg
+			//   op	  n(GS), reg
 			// to
 			//   MOVL 0x2C(FS), reg
-			//   op   n(reg), reg
+			//   op	  n(reg), reg
 			// The purpose of this patch is to fix some accesses
 			// to extern register variables (TLS) on Windows, as
 			// a different method is used to access them.
@@ -770,6 +770,7 @@
 				p->as = AADJSP;
 				p->from.type = D_CONST;
 				p->from.offset = autoffset;
+				p->spadj = autoffset;
 				if(q != P)
 					q->pcond = p;
 			}
@@ -792,18 +793,22 @@
 		case APUSHL:
 		case APUSHFL:
 			deltasp += 4;
+			p->spadj = 4;
 			continue;
 		case APUSHW:
 		case APUSHFW:
 			deltasp += 2;
+			p->spadj = 2;
 			continue;
 		case APOPL:
 		case APOPFL:
 			deltasp -= 4;
+			p->spadj = -4;
 			continue;
 		case APOPW:
 		case APOPFW:
 			deltasp -= 2;
+			p->spadj = -2;
 			continue;
 		case ARET:
 			break;
@@ -822,6 +827,7 @@
 			q->as = AADJSP;
 			q->from.type = D_CONST;
 			q->from.offset = -autoffset;
+			p->spadj = -autoffset;
 		}
 		continue;
 
@@ -836,6 +842,7 @@
 		q->from = zprg.from;
 		q->from.type = D_CONST;
 		q->from.offset = -autoffset;
+		p->spadj = -autoffset;
 		q->to = zprg.to;
 		continue;
 	}
diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c
index add9277..6e440d0 100644
--- a/src/cmd/ld/dwarf.c
+++ b/src/cmd/ld/dwarf.c
@@ -10,7 +10,7 @@
 #include	"../ld/macho.h"
 
 /*
- * Offsets and sizes of the .debug_* sections in the cout file.
+ * Offsets and sizes of the debug_* sections in the cout file.
  */
 
 static vlong abbrevo;
@@ -19,7 +19,78 @@
 static vlong linesize;
 static vlong infoo;
 static vlong infosize;
+static vlong frameo;
+static vlong framesize;
 
+/*
+ *  Basic I/O
+ */
+
+static void
+addrput(vlong addr)
+{
+	switch(PtrSize) {
+	case 4:
+		LPUT(addr);
+		break;
+	case 8:
+		VPUT(addr);
+		break;
+	}
+}
+
+
+static int
+uleb128enc(uvlong v, char* dst)
+{
+	uint8 c, len;
+
+	len = 0;
+	do {
+		c = v & 0x7f;
+		v >>= 7;
+		if (v)
+			c |= 0x80;
+		if (dst)
+			*dst++ = c;
+		len++;
+	} while (c & 0x80);
+	return len;
+};
+
+
+static int
+sleb128enc(vlong v, char *dst)
+{
+	uint8 c, s, len;
+
+	len = 0;
+	do {
+		c = v & 0x7f;
+		s = v & 0x40;
+		v >>= 7;
+		if ((v != -1 || !s) && (v != 0 || s))
+			c |= 0x80;
+		if (dst)
+			*dst++ = c;
+		len++;
+	} while(c & 0x80);
+	return len;
+}
+
+static void
+uleb128put(vlong v)
+{
+	char buf[10];
+	strnput(buf, uleb128enc(v, buf));
+}
+
+static void
+sleb128put(vlong v)
+{
+	char buf[10];
+	strnput(buf, sleb128enc(v, buf));
+}
 
 /*
  * Defining Abbrevs.  This is hardcoded, and there will be
@@ -72,13 +143,34 @@
 	},
 };
 
+static void
+writeabbrev(void)
+{
+	int i, n;
+
+	abbrevo = cpos();
+	for (i = 1; i < DW_NABRV; i++) {
+		// See section 7.5.3
+		uleb128put(i);
+		uleb128put(abbrevs[i].tag);
+		cput(abbrevs[i].children);
+		// 0 is not a valid attr or form, so we can treat this as
+		// a string
+		n = strlen((char*)abbrevs[i].attr) / 2;
+		strnput((char*)abbrevs[i].attr,
+			(n+1) * sizeof(DWAttrForm));
+	}
+	cput(0);
+	abbrevsize = cpos() - abbrevo;
+}
+
 /*
  * Debugging Information Entries and their attributes
  */
 
-
-// for string and block, value contains the length, and data the data,
-// for all others, value is the whole thing and data is null.
+// For DW_CLS_string and _block, value should contain the length, and
+// data the data, for all others, value is the whole thing and data is
+// null.
 
 typedef struct DWAttr DWAttr;
 struct DWAttr {
@@ -126,46 +218,6 @@
 	return a;
 }
 
-static void addrput(vlong addr)
-{
-	switch(PtrSize) {
-	case 4:
-		LPUT(addr);
-		break;
-	case 8:
-		VPUT(addr);
-		break;
-	}
-}
-
-static void
-uleb128put(uvlong v)
-{
-	uint8 c;
-
-	do {
-		c = v & 0x7f;
-		v >>= 7;
-		if (v) c |= 0x80;
-		cput(c);
-	} while (c & 0x80);
-};
-
-static void
-sleb128put(vlong v)
-{
-	uint8 c, s;
-
-	do {
-		c = v & 0x7f;
-		s = c & 0x40;
-		v >>= 7;
-		if ((v != -1 || !s) && (v != 0 || s))
-			c |= 0x80;
-		cput(c);
-	} while(c & 0x80);
-};
-
 static void
 putattr(int form, int cls, vlong value, char *data)
 {
@@ -209,11 +261,11 @@
 		WPUT(value);
 		break;
 
-	case DW_FORM_data4:	// constant, lineptr, loclistptr, macptr, rangelistptr
+	case DW_FORM_data4:	// constant, {line,loclist,mac,rangelist}ptr
 		LPUT(value);
 		break;
 
-	case DW_FORM_data8:	// constant, lineptr, loclistptr, macptr, rangelistptr
+	case DW_FORM_data8:	// constant, {line,loclist,mac,rangelist}ptr
 		VPUT(value);
 		break;
 
@@ -542,28 +594,6 @@
 	return lh;
 }
 
-static void
-writeabbrev(void)
-{
-	int i, n;
-
-	abbrevo = cpos();
-	for (i = 1; i < DW_NABRV; i++) {
-		// See section 7.5.3
-		uleb128put(i);
-		uleb128put(abbrevs[i].tag);
-		cput(abbrevs[i].children);
-		// 0 is not a valid attr or form, so we can treat this as
-		// a string
-		n = strlen((char *) abbrevs[i].attr) / 2;
-		strnput((char *) abbrevs[i].attr,
-			(n + 1) * sizeof(DWAttrForm));
-	}
-	cput(0);
-	abbrevsize = cpos() - abbrevo;
-}
-
-
 static int
 guesslang(char *s)
 {
@@ -596,8 +626,8 @@
 	}
 
 	if (delta_pc) {
-	  cput(DW_LNS_advance_pc);
-	  sleb128put(delta_pc);
+		cput(DW_LNS_advance_pc);
+		sleb128put(delta_pc);
 	}
 
 	cput(DW_LNS_advance_line);
@@ -616,8 +646,8 @@
 {
 	vlong here;
 
-	if (dwinfo != 0 && pc != 0) {
-		newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc, 0);
+	if (dwinfo != nil && pc != 0) {
+		newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, 0);
 	}
 
 	if (unitstart >= 0) {
@@ -641,19 +671,22 @@
 	Sym *s;
 	char *unitname;
 	vlong unitstart;
-	vlong pc, lc, llc, lline;
+	vlong pc, epc, lc, llc, lline;
 	int currfile;
 	int i;
 	Linehist *lh;
 
+	q = nil;
 	unitstart = -1;
-	pc = 0;
+	epc = pc = 0;
 	lc = 1;
 	llc = 1;
 	currfile = -1;
 	lineo = cpos();
 
 	for (p = textp; p != P; p = p->pcond) {
+		curtext = p; // for diag
+
 		s = p->from.sym;
 		if (s == nil || s->type != STEXT) {
 			diag("->pcond was supposed to loop over STEXT: %P", p);
@@ -663,7 +696,7 @@
 		// Look for history stack.  If we find one,
 		// we're entering a new compilation unit
 		if ((unitname = inithist(p->to.autom)) != 0) {
-			flushunit(pc, unitstart);
+			flushunit(epc, unitstart);
 			unitstart = cpos();
 			if(debug['v'] > 1) {
 				print("dwarf writelines found %s\n", unitname);
@@ -675,8 +708,8 @@
 			dwinfo = newdie(dwinfo, DW_ABRV_COMPUNIT);
 			newattr(dwinfo, DW_AT_name, DW_CLS_STRING, strlen(unitname), unitname);
 			newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT, guesslang(unitname), 0);
-			newattr(dwinfo, DW_AT_stmt_list,  DW_CLS_PTR, unitstart - lineo, 0);
-			newattr(dwinfo, DW_AT_low_pc,  DW_CLS_ADDRESS, p->pc, 0);
+			newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart - lineo, 0);
+			newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, p->pc, 0);
 			// Write .debug_line Line Number Program Header (sec 6.2.4)
 			// Fields marked with (*) must be changed for 64-bit dwarf
 			LPUT(0);   // unit_length (*), will be filled in later.
@@ -701,7 +734,7 @@
 				// 4 zeros: the string termination + 3 fields.
 			}
 
-			pc = p->pc;
+			epc = pc = p->pc;
 			currfile = 1;
 			lc = 1;
 			llc = 1;
@@ -711,7 +744,7 @@
 			cput(DW_LNE_set_address);
 			addrput(pc);
 		}
-		if (!p->from.sym->reachable)
+		if (!s->reachable)
 			continue;
 		if (unitstart < 0) {
 			diag("reachable code before seeing any history: %P", p);
@@ -721,11 +754,9 @@
 		dwinfo->child = newdie(dwinfo->child, DW_ABRV_FUNCTION);
 		newattr(dwinfo->child, DW_AT_name, DW_CLS_STRING, strlen(s->name), s->name);
 		newattr(dwinfo->child, DW_AT_low_pc, DW_CLS_ADDRESS, p->pc, 0);
-		if (debug['v'] > 1)
-		  print("frame offset: %d\n", p->to.offset);
-//		newattr(dwinfo->child, DW_AT_return_addr,  DW_CLS_BLOCK, p->to.offset, 0);
 
 		for(q = p; q != P && (q == p || q->as != ATEXT); q = q->link) {
+                        epc = q->pc;
 			lh = searchhist(q->line);
 			if (lh == nil) {
 				diag("corrupt history or bad absolute line: %P", q);
@@ -734,7 +765,7 @@
 			lline = lh->line + q->line - lh->absline;
 			if (debug['v'] > 1)
 				print("%6llux %s[%lld] %P\n", q->pc, histfile[lh->file], lline, q);
-			// Only emit a line program statement if line has changed.
+
 			if (q->line == lc)
 				continue;
 			if (currfile != lh->file) {
@@ -747,13 +778,131 @@
 			lc  = q->line;
 			llc = lline;
 		}
-		newattr(dwinfo->child, DW_AT_high_pc, DW_CLS_ADDRESS, pc, 0);
+
+		newattr(dwinfo->child, DW_AT_high_pc, DW_CLS_ADDRESS, epc+1, 0);
+
 	}
-	flushunit(pc, unitstart);
+
+	flushunit(epc, unitstart);
 	linesize = cpos() - lineo;
 }
 
 /*
+ *  Emit .debug_frame
+ */
+enum
+{
+	CIERESERVE = 16,
+	DATAALIGNMENTFACTOR = -4,
+	FAKERETURNCOLUMN = 16
+};
+
+static void
+putpccfadelta(vlong deltapc, vlong cfa)
+{
+	if (deltapc < 0x40) {
+		cput(DW_CFA_advance_loc + deltapc);
+	} else if (deltapc < 0x100) {
+		cput(DW_CFA_advance_loc1);
+		cput(deltapc);
+	} else if (deltapc < 0x10000) {
+		cput(DW_CFA_advance_loc2);
+		WPUT(deltapc);
+	} else {
+		cput(DW_CFA_advance_loc4);
+		LPUT(deltapc);
+	}
+
+	cput(DW_CFA_def_cfa_offset_sf);
+	sleb128put(cfa / DATAALIGNMENTFACTOR);
+}
+
+static void
+writeframes(void)
+{
+	Prog *p, *q;
+	Sym *s;
+	vlong fdeo, fdesize, pad, cfa, pc, epc;
+
+	frameo = cpos();
+
+	// Emit the CIE, Section 6.4.1
+	LPUT(CIERESERVE);  // initial length, must be multiple of PtrSize
+	LPUT(0xffffffff);  // cid.
+	cput(3);	// dwarf version
+	cput(0);	// augmentation ""
+	uleb128put(1);	// code_alignment_factor
+	sleb128put(DATAALIGNMENTFACTOR); // guess
+	uleb128put(FAKERETURNCOLUMN); // return_address_register
+
+	cput(DW_CFA_def_cfa);
+	uleb128put(DWARFREGSP);	// register SP (**ABI-dependent, defined in l.h)
+	uleb128put(PtrSize);	// offset
+
+	cput(DW_CFA_offset + FAKERETURNCOLUMN);	 // return address
+	uleb128put(-PtrSize / DATAALIGNMENTFACTOR);	// at cfa - x*4
+
+	// 4 is to exclude the length field.
+	pad = CIERESERVE + frameo + 4 - cpos();
+	if (pad < 0) {
+		diag("CIERESERVE too small by %lld bytes.", -pad);
+		errorexit();
+	}
+	strnput("", pad);
+
+	for (p = textp; p != P; p = p->pcond) {
+		curtext = p; // for diag
+		s = p->from.sym;
+		if (s == nil || s->type != STEXT) {
+			diag("->pcond was supposed to loop over STEXT: %P", p);
+			continue;
+		}
+		if (!s->reachable)
+			continue;
+
+		fdeo = cpos();
+		// Emit a FDE, Section 6.4.1, starting wit a placeholder.
+		LPUT(0);	// length, must be multiple of PtrSize
+		LPUT(0);	// Pointer to the CIE above, at offset 0
+		addrput(0);	// initial location
+		addrput(0);	// address range
+
+		cfa = PtrSize;	// CFA starts at sp+PtrSize
+		pc = p->pc;
+		epc = p->pc;
+
+		for(q = p; q != P && (q == p || q->as != ATEXT); q = q->link) {
+			epc = q->pc;
+			if (q->spadj == 0)
+				continue;
+
+			cfa += q->spadj;
+			putpccfadelta(q->pc - pc, cfa);
+			pc = q->pc;
+		}
+
+		fdesize = cpos() - fdeo - 4;	// exclude the length field.
+		pad = rnd(fdesize, PtrSize) - fdesize;
+		strnput("", pad);
+		fdesize += pad;
+		cflush();
+
+		// Emit the FDE header for real, Section 6.4.1.
+		seek(cout, fdeo, 0);
+		LPUT(fdesize);
+		LPUT(0);
+		addrput(p->pc);
+		addrput(epc - p->pc);
+
+		cflush();
+		seek(cout, fdeo + 4 + fdesize, 0);
+	}
+
+	cflush();
+	framesize = cpos() - frameo;
+}
+
+/*
  *  Walk DWarfDebugInfoEntries, and emit .debug_info
  */
 static void
@@ -790,8 +939,17 @@
 	infosize = cpos() - infoo;
 }
 
+void
+dwarfemitdebugsections(void)
+{
+	writeabbrev();
+	writelines();
+	writeframes();
+	writeinfo();
+}
+
 /*
- *  Elf sections.
+ *  Elf.
  */
 enum
 {
@@ -828,14 +986,6 @@
 }
 
 void
-dwarfemitdebugsections(void)
-{
-	writeabbrev();
-	writelines();
-	writeinfo();
-}
-
-void
 dwarfaddelfheaders(void)
 {
 	ElfShdr *sh;
@@ -852,6 +1002,12 @@
 	sh->size = linesize;
 	sh->addralign = 1;
 
+	sh = newElfShdr(elfstrdbg[ElfStrDebugFrame]);
+	sh->type = SHT_PROGBITS;
+	sh->off = frameo;
+	sh->size = framesize;
+	sh->addralign = 1;
+
 	sh = newElfShdr(elfstrdbg[ElfStrDebugInfo]);
 	sh->type = SHT_PROGBITS;
 	sh->off = infoo;
@@ -859,6 +1015,9 @@
 	sh->addralign = 1;
 }
 
+/*
+ * Macho
+ */
 void
 dwarfaddmachoheaders(void)
 {
@@ -873,7 +1032,7 @@
 
 	ms = newMachoSeg("__DWARF", 3);
 	ms->fileoffset = fakestart;
-	ms->filesize = abbrevo-fakestart + abbrevsize+linesize+infosize;
+	ms->filesize = abbrevo-fakestart + abbrevsize+linesize+framesize+infosize;
 
 	msect = newMachoSect(ms, "__debug_abbrev");
 	msect->off = abbrevo;
@@ -883,6 +1042,10 @@
 	msect->off = lineo;
 	msect->size = linesize;
 
+	msect = newMachoSect(ms, "__debug_frame");
+	msect->off = frameo;
+	msect->size = framesize;
+
 	msect = newMachoSect(ms, "__debug_info");
 	msect->off = infoo;
 	msect->size = infosize;
diff --git a/src/cmd/ld/dwarf_defs.h b/src/cmd/ld/dwarf_defs.h
index 7bff4fb..3b54f77 100644
--- a/src/cmd/ld/dwarf_defs.h
+++ b/src/cmd/ld/dwarf_defs.h
@@ -452,3 +452,39 @@
 	DW_MACINFO_end_file = 0x04,
 	DW_MACINFO_vendor_ext = 0xff,
 };
+
+// Table 40.
+enum
+{					// operand,...
+	DW_CFA_nop = 0x00,
+	DW_CFA_set_loc = 0x01,		// address
+	DW_CFA_advance_loc1 = 0x02,	// 1-byte delta
+	DW_CFA_advance_loc2 = 0x03,	// 2-byte delta
+	DW_CFA_advance_loc4 = 0x04,	// 4-byte delta
+	DW_CFA_offset_extended = 0x05,	// ULEB128 register, ULEB128 offset
+	DW_CFA_restore_extended = 0x06, // ULEB128 register
+	DW_CFA_undefined = 0x07,	// ULEB128 register
+	DW_CFA_same_value = 0x08,	// ULEB128 register
+	DW_CFA_register = 0x09,		// ULEB128 register, ULEB128 register
+	DW_CFA_remember_state = 0x0a,
+	DW_CFA_restore_state = 0x0b,
+	DW_CFA_def_cfa = 0x0c,		// ULEB128 register, ULEB128 offset
+	DW_CFA_def_cfa_register = 0x0d,	// ULEB128 register
+	DW_CFA_def_cfa_offset = 0x0e,	// ULEB128 offset
+	DW_CFA_def_cfa_expression = 0x0f, // BLOCK
+	DW_CFA_expression = 0x10,	// ULEB128 register, BLOCK
+	DW_CFA_offset_extended_sf = 0x11, // ULEB128 register, SLEB128 offset
+	DW_CFA_def_cfa_sf = 0x12,	// ULEB128 register, SLEB128 offset
+	DW_CFA_def_cfa_offset_sf = 0x13, // SLEB128 offset
+	DW_CFA_val_offset = 0x14,	// ULEB128, ULEB128
+	DW_CFA_val_offset_sf = 0x15,	// ULEB128, SLEB128
+	DW_CFA_val_expression = 0x16,	// ULEB128, BLOCK
+
+	DW_CFA_lo_user = 0x1c,
+	DW_CFA_hi_user = 0x3f,
+
+	// Opcodes that take an addend operand.
+	DW_CFA_advance_loc = 0x1<<6, // +delta
+	DW_CFA_offset	   = 0x2<<6, // +register (ULEB128 offset)
+	DW_CFA_restore	   = 0x3<<6, // +register
+};