This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] S/390: Add runtime check for the highgprs kernel feature


Hi,

the attached patch adds a runtime check for the highgprs kernel
facility to the crt1 startup code.

The whole thing is about supporting 64 bit registers in 32 bit
applications on S/390.  In order to make this work the kernel has to
save and restore the upper halfs of 64 bit registers even when running
just 32 bit code.  Kernels starting with version 2.6.32 already
support this and indicate the fact in /proc/cpuinfo as "highgprs" and
in the AT_HWCAPS field of the auxiliary vector with the
HWCAP_S390_HIGH_GPRS flag.

Now the target is to mark executables and DSOs somehow if they are
compiled in a way which needs the upper halfs of the registers to be
saved (-m31 -mzarch) and prevent the execution if the kernel does not
provide that feature.  This is important since the lacking kernel
support would lead to problems which would be extremely difficult to
debug and to reproduce.

For the binutils package a patch has been proposed which adds a flag
to the e_flags field in the elf header if a binary is compiled with
-m31 -mzarch:
http://sourceware.org/ml/binutils/2010-01/msg00373.html

It turned out to be rather tricky to locate the elf header at runtime.
After a discussion with Roland and Jakub (btw. many thanks for your
help).  I've implemented the following strategy:

The program headers reside in memory and can be found using the
AT_PHDR field in the auxiliary vector.  Doing this I ran into problems
if the executable is executed as a ld.so parameter.  Fixed with that
patch:
http://sources.redhat.com/ml/libc-alpha/2010-01/msg00022.html

For non-relocatable binaries the elf header can be found at the
p_vaddr field of the first PT_LOAD program header with a p_offset of
0.

For PIE code the p_vaddr is 0 since the address is determined at load
time.  In order to detect this situation the code compares the AT_PHDR
field with the p_vaddr field of the PT_PHDR program header.  If they
don't match the binary has been relocated and the p_vaddr value is
subtracted from AT_PHDR to find the load address of the binary which
is the location where the ELF header can be found.

Unfortunately the PT_PHDR header is not necessarily available.  So the
check does not support PIE code without a PT_PHDR program header.  In
that case the checking is simply skipped and such a binary might be
executed on a kernel without the highgprs feature regardless of what
e_flags says.

I've tested the checking functionality with different binaries, shared
libs and of course kernels providing/not providing the highgprs
facility.  Binaries get a sigill in the startup code and shared libs
requiring the facility are ignored if the kernel does not provide it.

Tested on s390. No regressions.
Ok?

Bye,

-Andreas-


2010-01-15  Andreas Krebbel  <Andreas.Krebbel@de.ibm.com>

	* sysdeps/s390/s390-32/elf/start.S (_start): Added check for the
	high gprs kernel facility.
	* sysdeps/s390/s390-32/dl-machine.h (elf_machine_matches_host):
	Added high gprs check for DSOs.
	* elf/elf.h (EF_S390_HIGH_GPRS): Added macro definition for the
	new elf header flag.


Index: glibc/sysdeps/s390/s390-32/elf/start.S
===================================================================
*** glibc.orig/sysdeps/s390/s390-32/elf/start.S	2010-01-13 14:26:28.000000000 +0100
--- glibc/sysdeps/s390/s390-32/elf/start.S	2010-01-15 12:27:18.000000000 +0100
***************
*** 59,64 ****
--- 59,146 ----
  	.globl _start
  	.type _start,@function
  _start:
+ 	/* Check if the kernel provides highgprs facility if needed by
+ 	   the binary.  */
+ 
+ 	lr	%r6,%r15
+ 	la	%r6,4(%r6)     /* Skip the argument counter.  */
+ 
+ .L11:	l	%r5,0(%r6)     /* Skip the argument vector.  */
+ 	la	%r6,4(%r6)
+ 	ltr	%r5,%r5
+ 	jne	.L11
+ 
+ .L12:	l	%r5,0(%r6)     /* Skip the environment vector.  */
+ 	la	%r6,4(%r6)
+ 	ltr	%r5,%r5
+ 	jne	.L12
+ 
+ 	/* Obtain the needed values from the auxiliary vector.  */
+ 
+ 	lhi	%r7,16	       /* AT_HWCAP */
+ 	lhi	%r8,3	       /* AT_PHDR */
+ 	lhi	%r9,5          /* AT_PHNUM */
+ 	lhi	%r2,4          /* AT_PHENT */
+ .L13:	l	%r5,0(%r6)
+ 	clr	%r5,%r7
+ 	jne	.L15
+ 	l	%r10,4(%r6)    /* r10 = AT_HWCAP value.  */
+ .L15:	clr	%r5,%r8
+ 	jne	.L16
+ 	l	%r11,4(%r6)    /* r11 = AT_PHDR value.  */
+ .L16:	clr	%r5,%r9
+ 	jne	.L17
+ 	l	%r12,4(%r6)    /* r12 = AT_PHNUM value.  */
+ .L17:	clr	%r5,%r2
+ 	jne	.L18
+ 	l	%r0,4(%r6)     /* r0 = AT_PHENT value.  */
+ .L18:	ltr	%r5,%r5
+ 	la	%r6,8(%r6)
+ 	jnz	.L13
+ 
+ 	/* Locate the ELF header by looking for the first PT_LOAD
+ 	   segment with a p_offset of zero.  */
+ 
+ 	lr	%r4,%r11       /* Backup AT_PHDR.  */
+ 	lhi	%r7,1          /* PT_LOAD id */
+ 	lhi	%r8,0
+ .L19:	cl	%r7,0(%r4)     /* p_type == PT_LOAD? */
+ 	jne	.L20
+ 	cl	%r8,4(%r4)     /* p_offset == 0? */
+ 	jne	.L20
+ 	l	%r9,8(%r4)     /* r9 = p_vaddr <- ELF header address  */
+ 	j	.L24
+ .L20:	alr	%r4,%r0        /* r4 += AT_PHENT value */
+ 	brct	%r12,.L19
+ 
+ 	j	.+2            /* Trap, there must be such a phdr.  */
+ 
+ .L24:	lr	%r4,%r11       /* Backup AT_PHDR.  */
+ 	lhi	%r2,6          /* PT_PHDR id */
+ .L23:	cl	%r2,0(%r4)
+ 	jne	.L22
+ 	l	%r3,8(%r4)     /* r3 = PT_PHDR p_vaddr */
+ 	j	.L25
+ .L22:	alr	%r4,%r0        /* r4 += AT_PHENT value */
+ 	brct	%r12,.L23
+ 
+ 	ltr	%r9,%r9        /* Load address == 0? */
+ 	jz	.L14           /* No checking for PIE without PT_PHDR.  */
+ 	j	.L21
+ 
+ .L25:	clr	%r3,%r11       /* PT_PHDR p_vaddr == AT_PHDR? */
+ 	je	.L21
+ 	lr	%r9,%r11
+ 	slr	%r9,%r3        /* elf_header_addr = AT_PHDR - PT_PHDR.p_vaddr */
+ 
+ .L21:	l	%r5,36(%r9)    /* Load the e_flags field.  */
+ 	tml	%r5,1
+ 	jz	.L14	       /* Binary does not require highgprs facility.  */
+ 
+ 	tml	%r10,512       /* Check the AT_HWCAP value.  */
+ 	jz	2              /* Trap if no highgprs facility available.  */
+ .L14:
+ 
  	/* Setup pointer to literal pool of _start */
  	basr    %r13,0
  .L0:    ahi     %r13,.Llit-.L0
Index: glibc/elf/elf.h
===================================================================
*** glibc.orig/elf/elf.h	2010-01-13 14:26:28.000000000 +0100
--- glibc/elf/elf.h	2010-01-15 11:13:47.000000000 +0100
*************** typedef Elf32_Addr Elf32_Conflict;
*** 2493,2498 ****
--- 2493,2504 ----
  /* Keep this the last entry.  */
  #define	R_SH_NUM		256
  
+ /* S/390 specific definitions.  */
+ 
+ /* Valid values for the e_flags field.  */
+ 
+ #define EF_S390_HIGH_GPRS    0x00000001  /* High GPRs kernel facility needed.  */
+ 
  /* Additional s390 relocs */
  
  #define R_390_NONE		0	/* No reloc.  */
Index: glibc/sysdeps/s390/s390-32/dl-machine.h
===================================================================
*** glibc.orig/sysdeps/s390/s390-32/dl-machine.h	2010-01-13 14:26:28.000000000 +0100
--- glibc/sysdeps/s390/s390-32/dl-machine.h	2010-01-15 11:15:02.000000000 +0100
***************
*** 27,32 ****
--- 27,33 ----
  #include <sys/param.h>
  #include <string.h>
  #include <link.h>
+ #include <sysdeps/s390/dl-procinfo.h>
  
  /* This is an older, now obsolete value.  */
  #define EM_S390_OLD	0xA390
***************
*** 35,40 ****
--- 36,47 ----
  static inline int
  elf_machine_matches_host (const Elf32_Ehdr *ehdr)
  {
+   /* Check if the kernel provides the high gpr facility if needed by
+      the binary.  */
+   if ((ehdr->e_flags & EF_S390_HIGH_GPRS)
+       && !(GLRO (dl_hwcap) & HWCAP_S390_HIGH_GPRS))
+     return 0;
+ 
    return (ehdr->e_machine == EM_S390 || ehdr->e_machine == EM_S390_OLD)
           && ehdr->e_ident[EI_CLASS] == ELFCLASS32;
  }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]