changeset 13872:20563857eb2d

3265 link-editor builds bogus .eh_frame_hdr on ia32 Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com> Reviewed by: Gordon Ross <gwr@nexenta.com> Approved by: Garrett D'Amore <garrett@damore.org>
author Richard Lowe <richlowe@richlowe.net>
date Mon, 08 Oct 2012 03:37:11 +0100
parents a9c12c2c1647
children 89c188fb29f6
files usr/src/cmd/sgs/elfdump/common/_elfdump.h usr/src/cmd/sgs/elfdump/common/dwarf.c usr/src/cmd/sgs/elfdump/common/elfdump.c usr/src/cmd/sgs/include/dwarf.h usr/src/cmd/sgs/libld/common/unwind.c usr/src/cmd/sgs/packages/common/SUNWonld-README usr/src/cmd/sgs/tools/common/leb128.c
diffstat 7 files changed, 71 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/sgs/elfdump/common/_elfdump.h	Fri Sep 07 17:29:12 2012 -0700
+++ b/usr/src/cmd/sgs/elfdump/common/_elfdump.h	Mon Oct 08 03:37:11 2012 +0100
@@ -224,7 +224,7 @@
 
 extern	corenote_ret_t	corenote(Half, int, Word, const char *, Word);
 extern	void	dump_eh_frame(uchar_t *, size_t, uint64_t, Half e_machine,
-		    uchar_t *e_ident);
+		    uchar_t *e_ident, uint64_t gotaddr);
 extern	void	dump_hex_bytes(const void *, size_t, int, int, int);
 
 extern	int	fake_shdr_cache32(const char *, int, Elf *, Elf32_Ehdr *,
--- a/usr/src/cmd/sgs/elfdump/common/dwarf.c	Fri Sep 07 17:29:12 2012 -0700
+++ b/usr/src/cmd/sgs/elfdump/common/dwarf.c	Mon Oct 08 03:37:11 2012 +0100
@@ -49,6 +49,7 @@
 	uint64_t	ciecalign;	/* CIE code align factor */
 	int64_t		ciedalign;	/* CIE data align factor */
 	uint64_t	fdeinitloc;	/* FDE initial location */
+	uint64_t	gotaddr;	/* Address of the GOT */
 } dump_cfi_state_t;
 
 
@@ -301,8 +302,8 @@
 
 		case 0x01:		/* v2: DW_CFA_set_loc, address */
 			cur_pc = dwarf_ehe_extract(&data[off], ndx,
-			    state->cieRflag, state->e_ident,
-			    state->sh_addr, off + *ndx);
+			    state->cieRflag, state->e_ident, B_FALSE,
+			    state->sh_addr, off + *ndx, state->gotaddr);
 			dbg_print(0, MSG_ORIG(MSG_CFA_CFASET), PREFIX,
 			    EC_XWORD(cur_pc));
 			break;
@@ -465,7 +466,7 @@
 
 void
 dump_eh_frame(uchar_t *data, size_t datasize, uint64_t sh_addr,
-    Half e_machine, uchar_t *e_ident)
+    Half e_machine, uchar_t *e_ident, uint64_t gotaddr)
 {
 	Conv_dwarf_ehe_buf_t	dwarf_ehe_buf;
 	dump_cfi_state_t	cfi_state;
@@ -479,6 +480,7 @@
 	cfi_state.e_ident = e_ident;
 	cfi_state.sh_addr = sh_addr;
 	cfi_state.do_swap = _elf_sys_encoding() != e_ident[EI_DATA];
+	cfi_state.gotaddr = gotaddr;
 
 	off = 0;
 	while (off < datasize) {
@@ -568,8 +570,8 @@
 					ndx += 1;
 
 					persVal = dwarf_ehe_extract(&data[off],
-					    &ndx, ciePflag, e_ident,
-					    sh_addr, off + ndx);
+					    &ndx, ciePflag, e_ident, B_FALSE,
+					    sh_addr, off + ndx, gotaddr);
 					dbg_print(0,
 					    MSG_ORIG(MSG_UNW_CIEAXPERS));
 					dbg_print(0,
@@ -633,11 +635,11 @@
 			    fdelength, fdecieptr);
 
 			cfi_state.fdeinitloc = dwarf_ehe_extract(&data[off],
-			    &ndx, cfi_state.cieRflag, e_ident,
-			    sh_addr, off + ndx);
+			    &ndx, cfi_state.cieRflag, e_ident, B_FALSE,
+			    sh_addr, off + ndx, gotaddr);
 			fdeaddrrange = dwarf_ehe_extract(&data[off], &ndx,
 			    (cfi_state.cieRflag & ~DW_EH_PE_pcrel),
-			    e_ident, sh_addr, off + ndx);
+			    e_ident, B_FALSE, sh_addr, off + ndx, gotaddr);
 
 			dbg_print(0, MSG_ORIG(MSG_UNW_FDEINITLOC),
 			    EC_XWORD(cfi_state.fdeinitloc),
@@ -660,7 +662,8 @@
 
 					lsda = dwarf_ehe_extract(&data[off],
 					    &lndx, cieLflag, e_ident,
-					    sh_addr, off + lndx);
+					    B_FALSE, sh_addr, off + lndx,
+					    gotaddr);
 					dbg_print(0,
 					    MSG_ORIG(MSG_UNW_FDEAXLSDA),
 					    EC_XWORD(lsda));
--- a/usr/src/cmd/sgs/elfdump/common/elfdump.c	Fri Sep 07 17:29:12 2012 -0700
+++ b/usr/src/cmd/sgs/elfdump/common/elfdump.c	Mon Oct 08 03:37:11 2012 +0100
@@ -521,6 +521,7 @@
  * entry:
  *	cache - Cache of all section headers
  *	shndx - Index of .eh_frame or .eh_frame_hdr section to be displayed
+ *	shnum - Total number of sections which exist
  *	uphdr - NULL, or unwind program header associated with
  *		the .eh_frame_hdr section.
  *	ehdr - ELF header for file
@@ -532,7 +533,7 @@
  *	flags - Command line option flags
  */
 static void
-unwind_eh_frame(Cache *cache, Word shndx, Phdr *uphdr, Ehdr *ehdr,
+unwind_eh_frame(Cache *cache, Word shndx, Word shnum, Phdr *uphdr, Ehdr *ehdr,
     gnu_eh_state_t *eh_state, uchar_t osabi, const char *file, uint_t flags)
 {
 #if	defined(_ELF64)
@@ -551,7 +552,16 @@
 	uint64_t		ndx, frame_ptr, fde_cnt, tabndx;
 	uint_t			vers, frame_ptr_enc, fde_cnt_enc, table_enc;
 	uint64_t		initloc, initloc0;
-
+	uint64_t		gotaddr = 0;
+	int			cnt;
+
+	for (cnt = 1; cnt < shnum; cnt++) {
+		if (strncmp(cache[cnt].c_name, MSG_ORIG(MSG_ELF_GOT),
+		    MSG_ELF_GOT_SIZE) == 0) {
+			gotaddr = cache[cnt].c_shdr->sh_addr;
+			break;
+		}
+	}
 
 	/*
 	 * Is this a .eh_frame_hdr?
@@ -578,7 +588,7 @@
 		dbg_print(0, MSG_ORIG(MSG_UNW_FRMVERS), vers);
 
 		frame_ptr = dwarf_ehe_extract(data, &ndx, frame_ptr_enc,
-		    ehdr->e_ident, shdr->sh_addr, ndx);
+		    ehdr->e_ident, B_TRUE, shdr->sh_addr, ndx, gotaddr);
 		if (eh_state->hdr_cnt == 1) {
 			eh_state->hdr_ndx = shndx;
 			eh_state->frame_ptr = frame_ptr;
@@ -589,7 +599,7 @@
 		    EC_XWORD(frame_ptr));
 
 		fde_cnt = dwarf_ehe_extract(data, &ndx, fde_cnt_enc,
-		    ehdr->e_ident, shdr->sh_addr, ndx);
+		    ehdr->e_ident, B_TRUE, shdr->sh_addr, ndx, gotaddr);
 
 		dbg_print(0, MSG_ORIG(MSG_UNW_FDCNENC),
 		    conv_dwarf_ehe(fde_cnt_enc, &dwarf_ehe_buf),
@@ -601,7 +611,7 @@
 
 		for (tabndx = 0; tabndx < fde_cnt; tabndx++) {
 			initloc = dwarf_ehe_extract(data, &ndx, table_enc,
-			    ehdr->e_ident, shdr->sh_addr, ndx);
+			    ehdr->e_ident, B_TRUE, shdr->sh_addr, ndx, gotaddr);
 			/*LINTED:E_VAR_USED_BEFORE_SET*/
 			if ((tabndx != 0) && (initloc0 > initloc))
 				(void) fprintf(stderr,
@@ -610,8 +620,8 @@
 			dbg_print(0, MSG_ORIG(MSG_UNW_BINSRTABENT),
 			    EC_XWORD(initloc),
 			    EC_XWORD(dwarf_ehe_extract(data, &ndx,
-			    table_enc, ehdr->e_ident, shdr->sh_addr,
-			    ndx)));
+			    table_enc, ehdr->e_ident, B_TRUE, shdr->sh_addr,
+			    ndx, gotaddr)));
 			initloc0 = initloc;
 		}
 	} else {		/* Display the .eh_frame section */
@@ -628,7 +638,7 @@
 			    conv_ehdr_type(osabi, ehdr->e_type, 0, &inv_buf));
 		}
 		dump_eh_frame(data, datasize, shdr->sh_addr,
-		    ehdr->e_machine, ehdr->e_ident);
+		    ehdr->e_machine, ehdr->e_ident, gotaddr);
 	}
 
 	/*
@@ -875,8 +885,8 @@
 			unwind_exception_ranges(_cache, file,
 			    _elf_sys_encoding() != ehdr->e_ident[EI_DATA]);
 		else
-			unwind_eh_frame(cache, cnt, uphdr, ehdr, &eh_state,
-			    osabi, file, flags);
+			unwind_eh_frame(cache, cnt, shnum, uphdr, ehdr,
+			    &eh_state, osabi, file, flags);
 	}
 }
 
--- a/usr/src/cmd/sgs/include/dwarf.h	Fri Sep 07 17:29:12 2012 -0700
+++ b/usr/src/cmd/sgs/include/dwarf.h	Mon Oct 08 03:37:11 2012 +0100
@@ -232,6 +232,8 @@
 #define	DW_EH_PE_datarel	0x30	/* Value is reletive to the beginning */
 					/*  of the eh_frame_hdr segment */
 					/*  ( segment type PT_AMD64_UNWIND ) */
+					/*  when within that segment, or to */
+					/*  the GOT when without. */
 #define	DW_EH_PE_funcrel	0x40
 #define	DW_EH_PE_aligned	0x50	/* value is an aligned void* */
 #define	DW_EH_PE_indirect	0x80	/* bit to signal indirection after */
@@ -251,7 +253,8 @@
 extern	uint64_t	uleb_extract(unsigned char *, uint64_t *);
 extern	int64_t		sleb_extract(unsigned char *, uint64_t *);
 extern	uint64_t	dwarf_ehe_extract(unsigned char *, uint64_t *,
-			    uint_t, unsigned char *, uint64_t, uint64_t);
+			    uint_t, unsigned char *, boolean_t, uint64_t,
+			    uint64_t, uint64_t);
 
 #ifdef	__cplusplus
 }
--- a/usr/src/cmd/sgs/libld/common/unwind.c	Fri Sep 07 17:29:12 2012 -0700
+++ b/usr/src/cmd/sgs/libld/common/unwind.c	Mon Oct 08 03:37:11 2012 +0100
@@ -639,8 +639,8 @@
 					    (void) dwarf_ehe_extract(
 						&data[off + ndx],
 						&ndx, ciePflag,
-						ofl->ofl_dehdr->e_ident,
-						shdr->sh_addr, off + ndx);
+						ofl->ofl_dehdr->e_ident, B_FALSE,
+						shdr->sh_addr, off + ndx, 0);
 					    break;
 					case 'R':
 					    /* code encoding */
@@ -658,10 +658,17 @@
 				uint_t	    bintabndx;
 				uint64_t    initloc;
 				uint64_t    fdeaddr;
+				uint64_t    gotaddr = 0;
+
+				if (ofl->ofl_osgot != NULL)
+					gotaddr =
+					    ofl->ofl_osgot->os_shdr->sh_addr;
 
 				initloc = dwarf_ehe_extract(&data[off],
 				    &ndx, cieRflag, ofl->ofl_dehdr->e_ident,
-				    shdr->sh_addr, off + ndx);
+				    B_FALSE,
+				    shdr->sh_addr, off + ndx,
+				    gotaddr);
 
 				/*
 				 * Ignore FDEs with initloc set to 0.
--- a/usr/src/cmd/sgs/packages/common/SUNWonld-README	Fri Sep 07 17:29:12 2012 -0700
+++ b/usr/src/cmd/sgs/packages/common/SUNWonld-README	Mon Oct 08 03:37:11 2012 +0100
@@ -1639,3 +1639,4 @@
 3230	ld.so.1 should check default paths for DT_DEPAUDIT
 3260	linker is insufficiently careful with strtok
 3261	linker should ignore unknown hardware capabilities
+3265	link-editor builds bogus .eh_frame_hdr on ia32
--- a/usr/src/cmd/sgs/tools/common/leb128.c	Fri Sep 07 17:29:12 2012 -0700
+++ b/usr/src/cmd/sgs/tools/common/leb128.c	Mon Oct 08 03:37:11 2012 +0100
@@ -190,12 +190,16 @@
  *		at which the desired datum starts.
  *	ehe_flags - DWARF encoding
  *	eident - ELF header e_ident[] array for object being processed
+ *	frame_hdr - Boolean, true if we're extracting from .eh_frame_hdr
  *	sh_base - Base address of ELF section containing desired datum
  *	sh_offset - Offset relative to sh_base of desired datum.
+ *	dbase - The base address to which DW_EH_PE_datarel is relative
+ *		(if frame_hdr is false)
  */
 uint64_t
 dwarf_ehe_extract(unsigned char *data, uint64_t *dotp, uint_t ehe_flags,
-    unsigned char *eident, uint64_t sh_base, uint64_t sh_offset)
+    unsigned char *eident, boolean_t frame_hdr, uint64_t sh_base,
+    uint64_t sh_offset, uint64_t dbase)
 {
 	uint64_t    dot = *dotp;
 	uint_t	    lsb;
@@ -281,17 +285,27 @@
 	/*
 	 * If value is relative to a base address, adjust it
 	 */
-	if (result) {
-		switch (ehe_flags & 0xf0) {
-		case DW_EH_PE_pcrel:
-			result += sh_base + sh_offset;
-			break;
+	switch (ehe_flags & 0xf0) {
+	case DW_EH_PE_pcrel:
+		result += sh_base + sh_offset;
+		break;
 
-		case DW_EH_PE_datarel:
+	/*
+	 * datarel is relative to .eh_frame_hdr if within .eh_frame,
+	 * but GOT if not.
+	 */
+	case DW_EH_PE_datarel:
+		if (frame_hdr)
 			result += sh_base;
-			break;
-		}
+		else
+			result += dbase;
+		break;
 	}
+
+	/* Truncate the result to its specified size */
+	result = (result << ((sizeof (uint64_t) - fsize) * 8)) >>
+	    ((sizeof (uint64_t) - fsize) * 8);
+
 	*dotp = dot;
 	return (result);
 }