aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--elf/elf.h6
-rw-r--r--sysdeps/s390/s390-32/dl-machine.h7
-rw-r--r--sysdeps/s390/s390-32/elf/start.S82
3 files changed, 95 insertions, 0 deletions
diff --git a/elf/elf.h b/elf/elf.h
index 1bc8ef3489..8af7c177ce 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -2493,6 +2493,12 @@ typedef Elf32_Addr Elf32_Conflict;
/* 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. */
diff --git a/sysdeps/s390/s390-32/dl-machine.h b/sysdeps/s390/s390-32/dl-machine.h
index 251a5f6922..415b388012 100644
--- a/sysdeps/s390/s390-32/dl-machine.h
+++ b/sysdeps/s390/s390-32/dl-machine.h
@@ -27,6 +27,7 @@
#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,6 +36,12 @@
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;
}
diff --git a/sysdeps/s390/s390-32/elf/start.S b/sysdeps/s390/s390-32/elf/start.S
index f7290106ce..066f7f0aa1 100644
--- a/sysdeps/s390/s390-32/elf/start.S
+++ b/sysdeps/s390/s390-32/elf/start.S
@@ -59,6 +59,88 @@
.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