vDSO reference parser updates and Go workaround
Subsystems (2)
v3 cover vDSO reference parser updates and Go workaround
Patches 1 and 2 update the vDSO reference parser.
Despite the late submission, this might be 3.16 material: it's just documentation. If I'd been more on top of this, I would have done it for 3.15. It's also probably worthwhile given that there is at least one rather buggy vDSO parser in the wild.
Patches 3 and 4 make "Hello world" as built by Go stop segfaulting. It's a performance regression for Go programs: it causes them to stop finding any symbols in the vDSO, but that's a lot better than segfaulting.
Changes from v1:
- Improve portability of the vdso parser.
- Add an explicit ELFCLASS check in the vdso parser (why not?).
- Fix x32 build.
- Add a comment describing what the hack works around, why, and where the Go bug was introduced.
Changes from v2: Re-add patch 1. Whoops.
Andy Lutomirski (4):
doc,vdso: Rename vdso_test.c to vdso_standalone_test_x86.c doc,vdso: Make vDSO examples more portable x86,vdso: Add PUT_LE to store little-endian values x86,vdso: Hack to keep 64-bit Go programs working
Documentation/vDSO/parse_vdso.c | 67 ++++++++------ Documentation/vDSO/vdso_standalone_test_x86.c | 128 ++++++++++++++++++++++++++ Documentation/vDSO/vdso_test.c | 107 +++++---------------- arch/x86/vdso/Makefile | 18 ++-- arch/x86/vdso/vdso-fakesections.c | 32 +++++++ arch/x86/vdso/vdso2c.c | 19 +++- arch/x86/vdso/vdso2c.h | 23 ++++- 7 files changed, 268 insertions(+), 126 deletions(-) create mode 100644 Documentation/vDSO/vdso_standalone_test_x86.c create mode 100644 arch/x86/vdso/vdso-fakesections.c
Signature · 1 line
1.9.3
PATCH v3 1/4 doc,vdso: Rename vdso_test.c to vdso_standalone_test_x86.c
This thing is hopelessly x86_64-specific: it's an example of how to access the vDSO without any runtime support at all.
--- Documentation/vDSO/vdso_standalone_test_x86.c | 111 ++++++++++++++++++++++++++ Documentation/vDSO/vdso_test.c | 111 -------------------------- 2 files changed, 111 insertions(+), 111 deletions(-) create mode 100644 Documentation/vDSO/vdso_standalone_test_x86.c delete mode 100644 Documentation/vDSO/vdso_test.c
Documentation/vDSO/vdso_standalone_test_x86.c new file +111 -0
@@ -0,0 +1,111 @@ +/* +* vdso_test.c: Sample code to test parse_vdso.c on x86_64 +* Copyright (c) 2011 Andy Lutomirski +* Subject to the GNU General Public License, version 2 +* +* You can amuse yourself by compiling with: +* gcc -std=gnu99 -nostdlib +* -Os -fno-asynchronous-unwind-tables -flto +* vdso_standalone_test_x86.c parse_vdso.c +* to generate a small binary with no dependencies at all. +*/ + +#include <sys/syscall.h> +#include <sys/time.h> +#include <unistd.h> +#include <stdint.h> + +extern void *vdso_sym (const char *version,const char *name); +extern void vdso_init_from_sysinfo_ehdr (uintptr_t base); +extern void vdso_init_from_auxv (void *auxv); + +/* We need a libc functions... */ +int strcmp (const char *a,const char *b) +{ +/* This implementation is buggy: it never returns -1. */ +while (*a || *b) { +if (*a != *b) +return 1 ; +if (*a ==0 || *b ==0 ) +return 1 ; + a++; + b++; + } + +return 0 ; +} + +/* ...and two syscalls. This is x86_64-specific. */ +static inline long linux_write (int fd,const void *data,size_t len) +{ + +long ret; + asmvolatile ("syscall" :"=a" (ret) :"a" (__NR_write), +"D" (fd),"S" (data),"d" (len) : +"cc" ,"memory" ,"rcx" , +"r8" ,"r9" ,"r10" ,"r11" ); +return ret; +} + +static inline void linux_exit (int code) +{ + asmvolatile ("syscall" : :"a" (__NR_exit),"D" (code)); +} + +void to_base10 (char *lastdig,uint64_t n) +{ +while (n) { + *lastdig = (n %10 ) +'0' ; + n /=10 ; + lastdig--; + } +} + +__attribute__((externally_visible))void c_main (void **stack) +{ +/* Parse the stack */ +long argc = (long )*stack; + stack += argc +2 ; + +/* Now we're pointing at the environment. Skip it. */ +while (*stack) + stack++; + stack++; + +/* Now we're pointing at auxv. Initialize the vDSO parser. */ +vdso_init_from_auxv ((void *)stack); + +/* Find gettimeofday. */ +typedef long (*gtod_t )(struct timeval *tv,struct timezone *tz); +gtod_t gtod = (gtod_t )vdso_sym ("LINUX_2.6" ,"__vdso_gettimeofday" ); + +if (!gtod) +linux_exit (1 ); + +struct timeval tv; +long ret =gtod (&tv,0 ); + +if (ret ==0 ) { +char buf[] ="The time is .000000\n" ; +to_base10 (buf +31 , tv.tv_sec); +to_base10 (buf +38 , tv.tv_usec); +linux_write (1 , buf,sizeof (buf) -1 ); + }else { +linux_exit (ret); + } + +linux_exit (0 ); +} + +/* +* This is the real entry point. It passes the initial stack into +* the C entry point. +*/ +asm ( +".text\n" +".global _start\n" +".type _start,@function\n" +"_start:\n\t" +"mov %rsp,%rdi\n\t" +"jmp c_main" + );
Documentation/vDSO/vdso_test.c deleted +0 -111
111 lines not on this page · raw message
Signature · 1 line
1.9.3
tip-bot for Andy Lutomirski
merge bot[tip:x86/vdso] x86/vdso/doc: Rename vdso_test.c to vdso_standalone_test_x86.c
x86/vdso/doc: Rename vdso_test.c to vdso_standalone_test_x86.c
Commit-ID: 6e8f21584a30ba6ce73cfef34f316d5bf3fadaab Gitweb: http://git.kernel.org/tip/6e8f21584a30ba6ce73cfef34f316d5bf3fadaab Author: Andy Lutomirski <[email protected]> AuthorDate: Thu, 12 Jun 2014 17:53:09 -0700 Committer: H. Peter Anvin <[email protected]> CommitDate: Thu, 12 Jun 2014 19:01:16 -0700
x86/vdso/doc: Rename vdso_test.c to vdso_standalone_test_x86.c
This thing is hopelessly x86_64-specific: it's an example of how to access the vDSO without any runtime support at all.
---
Documentation/vDSO/{vdso_test.c => vdso_standalone_test_x86.c} | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)Documentation/vDSO/vdso_standalone_test_x86.c renamed from Documentation/vDSO/vdso_test.c +1 -1
@@ -6,7 +6,7 @@* You can amuse yourself by compiling with: * gcc -std=gnu99 -nostdlib * -Os -fno-asynchronous-unwind-tables -flto -* vdso_test.c parse_vdso.c -o vdso_test +* vdso_standalone_test_x86.c parse_vdso.c * to generate a small binary with no dependencies at all. */
PATCH v3 2/4 doc,vdso: Make vDSO examples more portable
In reply to Andy LutomirskiThis adds a new vdso_test.c that's written entirely in C. It also makes all of the vDSO examples work on 32-bit x86.
--- Documentation/vDSO/parse_vdso.c | 67 ++++++++++++++++----------- Documentation/vDSO/vdso_standalone_test_x86.c | 45 ++++++++++++------ Documentation/vDSO/vdso_test.c | 52 +++++++++++++++++++++ 3 files changed, 123 insertions(+), 41 deletions(-) create mode 100644 Documentation/vDSO/vdso_test.c
Documentation/vDSO/parse_vdso.c +40 -27
@@ -1,6 +1,6 @@/* * parse_vdso.c: Linux reference vDSO parser -* Written by Andrew Lutomirski, 2011. +* Written by Andrew Lutomirski, 2011-2014. * * This code is meant to be linked in to various programs that run on Linux. * As such, it is available with as few restrictions as possible. This file
@@ -11,13 +11,14 @@* it starts a program. It works equally well in statically and dynamically * linked binaries. * -* This code is tested on x86_64. In principle it should work on any 64-bit +* This code is tested on x86. In principle it should work on any * architecture that has a vDSO. */ #include <stdbool.h> #include <stdint.h> #include <string.h> +#include <limits.h> #include <elf.h> /*
@@ -45,11 +46,18 @@ extern void *vdso_sym(const char *version, const char *name);/* And here's the code. */ - -#ifndef __x86_64__ -# error Not yet ported to non-x86_64 architectures +#ifndef ELF_BITS +# if ULONG_MAX >0xffffffffUL +# define ELF_BITS 64 +# else +# define ELF_BITS 32 +# endif #endif +#define ELF_BITS_XFORM2 (bits, x) Elf##bits##_##x +#define ELF_BITS_XFORM (bits, x) ELF_BITS_XFORM2(bits, x) +#define ELF (x) ELF_BITS_XFORM(ELF_BITS, x) + static struct vdso_info { bool valid;
@@ -59,14 +67,14 @@ static struct vdso_infouintptr_t load_offset;/* load_addr - recorded vaddr */ /* Symbol table */ -Elf64_Sym *symtab; +ELF (Sym) *symtab;const char *symstrings; -Elf64_Word *bucket, *chain; -Elf64_Word nbucket, nchain; +ELF (Word) *bucket, *chain; +ELF (Word) nbucket, nchain;/* Version table */ -Elf64_Versym *versym; -Elf64_Verdef *verdef; +ELF (Versym) *versym; +ELF (Verdef) *verdef; } vdso_info;/* Straight from the ELF specification. */
@@ -92,9 +100,14 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base) vdso_info.load_addr = base; -Elf64_Ehdr *hdr = (Elf64_Ehdr *)base; -Elf64_Phdr *pt = (Elf64_Phdr *)(vdso_info.load_addr + hdr->e_phoff); -Elf64_Dyn *dyn =0 ; +ELF (Ehdr) *hdr = (ELF (Ehdr)*)base; +if (hdr->e_ident[EI_CLASS ] != + (ELF_BITS ==32 ?ELFCLASS32 :ELFCLASS64 )) { +return ;/* Wrong ELF class -- check ELF_BITS */ + } + +ELF (Phdr) *pt = (ELF (Phdr)*)(vdso_info.load_addr + hdr->e_phoff); +ELF (Dyn) *dyn =0 ;/* * We need two things from the segment table: the load offset
@@ -108,7 +121,7 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base) + (uintptr_t )pt[i].p_offset - (uintptr_t )pt[i].p_vaddr; }else if (pt[i].p_type ==PT_DYNAMIC ) { - dyn = (Elf64_Dyn *)(base + pt[i].p_offset); + dyn = (ELF (Dyn)*)(base + pt[i].p_offset); } }
@@ -118,7 +131,7 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)/* * Fish out the useful bits of the dynamic table. */ -Elf64_Word *hash =0 ; +ELF (Word) *hash =0 ; vdso_info.symstrings =0 ; vdso_info.symtab =0 ; vdso_info.versym =0 ;
@@ -131,22 +144,22 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base) + vdso_info.load_offset);break ;case DT_SYMTAB : - vdso_info.symtab = (Elf64_Sym *) + vdso_info.symtab = (ELF (Sym) *) ((uintptr_t )dyn[i].d_un.d_ptr + vdso_info.load_offset);break ;case DT_HASH : - hash = (Elf64_Word *) + hash = (ELF (Word) *) ((uintptr_t )dyn[i].d_un.d_ptr + vdso_info.load_offset);break ;case DT_VERSYM : - vdso_info.versym = (Elf64_Versym *) + vdso_info.versym = (ELF (Versym) *) ((uintptr_t )dyn[i].d_un.d_ptr + vdso_info.load_offset);break ;case DT_VERDEF : - vdso_info.verdef = (Elf64_Verdef *) + vdso_info.verdef = (ELF (Verdef) *) ((uintptr_t )dyn[i].d_un.d_ptr + vdso_info.load_offset);break ;
@@ -168,8 +181,8 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base) vdso_info.valid = true; } -static bool vdso_match_version (Elf64_Versym ver, -const char *name,Elf64_Word hash) +static bool vdso_match_version (ELF (Versym) ver, +const char *name,ELF (Word) hash) {/* * This is a helper function to check if the version indexed by
@@ -188,7 +201,7 @@ static bool vdso_match_version(Elf64_Versym ver,/* First step: find the version definition */ ver &=0x7fff ;/* Apparently bit 15 means "hidden" */ -Elf64_Verdef *def = vdso_info.verdef; +ELF (Verdef) *def = vdso_info.verdef;while (true) {if ((def->vd_flags &VER_FLG_BASE ) ==0 && (def->vd_ndx &0x7fff ) == ver)
@@ -197,11 +210,11 @@ static bool vdso_match_version(Elf64_Versym ver,if (def->vd_next ==0 )return false;/* No definition. */ - def = (Elf64_Verdef *)((char *)def + def->vd_next); + def = (ELF (Verdef) *)((char *)def + def->vd_next); }/* Now figure out whether it matches. */ -Elf64_Verdaux *aux = (Elf64_Verdaux *)((char *)def + def->vd_aux); +ELF (Verdaux) *aux = (ELF (Verdaux)*)((char *)def + def->vd_aux);return def->vd_hash == hash && !strcmp (name, vdso_info.symstrings + aux->vda_name); }
@@ -213,10 +226,10 @@ void *vdso_sym(const char *version, const char *name)return 0 ; ver_hash =elf_hash (version); -Elf64_Word chain = vdso_info.bucket[elf_hash (name) % vdso_info.nbucket]; +ELF (Word) chain = vdso_info.bucket[elf_hash (name) % vdso_info.nbucket];for (; chain !=STN_UNDEF ; chain = vdso_info.chain[chain]) { -Elf64_Sym *sym = &vdso_info.symtab[chain]; +ELF (Sym) *sym = &vdso_info.symtab[chain];/* Check for a defined global or weak function w/ right name. */ if (ELF64_ST_TYPE (sym->st_info) !=STT_FUNC )
@@ -243,7 +256,7 @@ void *vdso_sym(const char *version, const char *name)void vdso_init_from_auxv (void *auxv) { -Elf64_auxv_t *elf_auxv = auxv; +ELF (auxv_t) *elf_auxv = auxv;for (int i =0 ; elf_auxv[i].a_type !=AT_NULL ; i++) {if (elf_auxv[i].a_type ==AT_SYSINFO_EHDR ) {
Documentation/vDSO/vdso_standalone_test_x86.c +31 -14
73 lines not on this page · raw message
Documentation/vDSO/vdso_test.c new file +52 -0
52 lines not on this page · raw message
Signature · 1 line
1.9.3
tip-bot for Andy Lutomirski
merge bot[tip:x86/vdso] x86/vdso/doc: Make vDSO examples more portable
x86/vdso/doc: Make vDSO examples more portable
Commit-ID: 4ebbefd6b93c34d6da0d950b1d2e0dcca2f1e6ef Gitweb: http://git.kernel.org/tip/4ebbefd6b93c34d6da0d950b1d2e0dcca2f1e6ef Author: Andy Lutomirski <[email protected]> AuthorDate: Thu, 12 Jun 2014 17:53:10 -0700 Committer: H. Peter Anvin <[email protected]> CommitDate: Thu, 12 Jun 2014 19:01:24 -0700
x86/vdso/doc: Make vDSO examples more portable
This adds a new vdso_test.c that's written entirely in C. It also makes all of the vDSO examples work on 32-bit x86.
--- Documentation/vDSO/parse_vdso.c | 67 ++++++++++++++++----------- Documentation/vDSO/vdso_standalone_test_x86.c | 45 ++++++++++++------ Documentation/vDSO/vdso_test.c | 52 +++++++++++++++++++++ 3 files changed, 123 insertions(+), 41 deletions(-)
Documentation/vDSO/parse_vdso.c +40 -27
@@ -1,6 +1,6 @@/* * parse_vdso.c: Linux reference vDSO parser -* Written by Andrew Lutomirski, 2011. +* Written by Andrew Lutomirski, 2011-2014. * * This code is meant to be linked in to various programs that run on Linux. * As such, it is available with as few restrictions as possible. This file
@@ -11,13 +11,14 @@* it starts a program. It works equally well in statically and dynamically * linked binaries. * -* This code is tested on x86_64. In principle it should work on any 64-bit +* This code is tested on x86. In principle it should work on any * architecture that has a vDSO. */ #include <stdbool.h> #include <stdint.h> #include <string.h> +#include <limits.h> #include <elf.h> /*
@@ -45,11 +46,18 @@ extern void *vdso_sym(const char *version, const char *name);/* And here's the code. */ - -#ifndef __x86_64__ -# error Not yet ported to non-x86_64 architectures +#ifndef ELF_BITS +# if ULONG_MAX >0xffffffffUL +# define ELF_BITS 64 +# else +# define ELF_BITS 32 +# endif #endif +#define ELF_BITS_XFORM2 (bits, x) Elf##bits##_##x +#define ELF_BITS_XFORM (bits, x) ELF_BITS_XFORM2(bits, x) +#define ELF (x) ELF_BITS_XFORM(ELF_BITS, x) + static struct vdso_info { bool valid;
@@ -59,14 +67,14 @@ static struct vdso_infouintptr_t load_offset;/* load_addr - recorded vaddr */ /* Symbol table */ -Elf64_Sym *symtab; +ELF (Sym) *symtab;const char *symstrings; -Elf64_Word *bucket, *chain; -Elf64_Word nbucket, nchain; +ELF (Word) *bucket, *chain; +ELF (Word) nbucket, nchain;/* Version table */ -Elf64_Versym *versym; -Elf64_Verdef *verdef; +ELF (Versym) *versym; +ELF (Verdef) *verdef; } vdso_info;/* Straight from the ELF specification. */
@@ -92,9 +100,14 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base) vdso_info.load_addr = base; -Elf64_Ehdr *hdr = (Elf64_Ehdr *)base; -Elf64_Phdr *pt = (Elf64_Phdr *)(vdso_info.load_addr + hdr->e_phoff); -Elf64_Dyn *dyn =0 ; +ELF (Ehdr) *hdr = (ELF (Ehdr)*)base; +if (hdr->e_ident[EI_CLASS ] != + (ELF_BITS ==32 ?ELFCLASS32 :ELFCLASS64 )) { +return ;/* Wrong ELF class -- check ELF_BITS */ + } + +ELF (Phdr) *pt = (ELF (Phdr)*)(vdso_info.load_addr + hdr->e_phoff); +ELF (Dyn) *dyn =0 ;/* * We need two things from the segment table: the load offset
@@ -108,7 +121,7 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base) + (uintptr_t )pt[i].p_offset - (uintptr_t )pt[i].p_vaddr; }else if (pt[i].p_type ==PT_DYNAMIC ) { - dyn = (Elf64_Dyn *)(base + pt[i].p_offset); + dyn = (ELF (Dyn)*)(base + pt[i].p_offset); } }
@@ -118,7 +131,7 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)/* * Fish out the useful bits of the dynamic table. */ -Elf64_Word *hash =0 ; +ELF (Word) *hash =0 ; vdso_info.symstrings =0 ; vdso_info.symtab =0 ; vdso_info.versym =0 ;
@@ -131,22 +144,22 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base) + vdso_info.load_offset);break ;case DT_SYMTAB : - vdso_info.symtab = (Elf64_Sym *) + vdso_info.symtab = (ELF (Sym) *) ((uintptr_t )dyn[i].d_un.d_ptr + vdso_info.load_offset);break ;case DT_HASH : - hash = (Elf64_Word *) + hash = (ELF (Word) *) ((uintptr_t )dyn[i].d_un.d_ptr + vdso_info.load_offset);break ;case DT_VERSYM : - vdso_info.versym = (Elf64_Versym *) + vdso_info.versym = (ELF (Versym) *) ((uintptr_t )dyn[i].d_un.d_ptr + vdso_info.load_offset);break ;case DT_VERDEF : - vdso_info.verdef = (Elf64_Verdef *) + vdso_info.verdef = (ELF (Verdef) *) ((uintptr_t )dyn[i].d_un.d_ptr + vdso_info.load_offset);break ;
@@ -168,8 +181,8 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base) vdso_info.valid = true; } -static bool vdso_match_version (Elf64_Versym ver, -const char *name,Elf64_Word hash) +static bool vdso_match_version (ELF (Versym) ver, +const char *name,ELF (Word) hash) {/* * This is a helper function to check if the version indexed by
@@ -188,7 +201,7 @@ static bool vdso_match_version(Elf64_Versym ver,/* First step: find the version definition */ ver &=0x7fff ;/* Apparently bit 15 means "hidden" */ -Elf64_Verdef *def = vdso_info.verdef; +ELF (Verdef) *def = vdso_info.verdef;while (true) {if ((def->vd_flags &VER_FLG_BASE ) ==0 && (def->vd_ndx &0x7fff ) == ver)
@@ -197,11 +210,11 @@ static bool vdso_match_version(Elf64_Versym ver,if (def->vd_next ==0 )return false;/* No definition. */ - def = (Elf64_Verdef *)((char *)def + def->vd_next); + def = (ELF (Verdef) *)((char *)def + def->vd_next); }/* Now figure out whether it matches. */ -Elf64_Verdaux *aux = (Elf64_Verdaux *)((char *)def + def->vd_aux); +ELF (Verdaux) *aux = (ELF (Verdaux)*)((char *)def + def->vd_aux);return def->vd_hash == hash && !strcmp (name, vdso_info.symstrings + aux->vda_name); }
@@ -213,10 +226,10 @@ void *vdso_sym(const char *version, const char *name)return 0 ; ver_hash =elf_hash (version); -Elf64_Word chain = vdso_info.bucket[elf_hash (name) % vdso_info.nbucket]; +ELF (Word) chain = vdso_info.bucket[elf_hash (name) % vdso_info.nbucket];for (; chain !=STN_UNDEF ; chain = vdso_info.chain[chain]) { -Elf64_Sym *sym = &vdso_info.symtab[chain]; +ELF (Sym) *sym = &vdso_info.symtab[chain];/* Check for a defined global or weak function w/ right name. */ if (ELF64_ST_TYPE (sym->st_info) !=STT_FUNC )
@@ -243,7 +256,7 @@ void *vdso_sym(const char *version, const char *name)void vdso_init_from_auxv (void *auxv) { -Elf64_auxv_t *elf_auxv = auxv; +ELF (auxv_t) *elf_auxv = auxv;for (int i =0 ; elf_auxv[i].a_type !=AT_NULL ; i++) {if (elf_auxv[i].a_type ==AT_SYSINFO_EHDR ) {
Documentation/vDSO/vdso_standalone_test_x86.c +31 -14
73 lines not on this page · raw message
Documentation/vDSO/vdso_test.c new file +52 -0
52 lines not on this page · raw message
PATCH v3 3/4 x86,vdso: Add PUT_LE to store little-endian values
In reply to Andy Lutomirski--- arch/x86/vdso/vdso2c.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
arch/x86/vdso/vdso2c.c +16 -3
@@ -54,7 +54,7 @@ static void fail(const char *format, ...) }/* -* Evil macros to do a little-endian read. +* Evil macros for little-endian reads and writes */ #define GLE (x, bits, ifnot) \ __builtin_choose_expr( \
@@ -62,11 +62,24 @@ static void fail(const char *format, ...) (__typeof__(*(x)))get_unaligned_le##bits(x), ifnot)extern void bad_get_le (void ); -#define LAST_LE (x) \ +#define LAST_GLE (x) \ __builtin_choose_expr(sizeof(*(x)) == 1, *(x), bad_get_le())#define GET_LE (x) \ - GLE(x, 64, GLE(x, 32, GLE(x, 16, LAST_LE(x)))) + GLE(x, 64, GLE(x, 32, GLE(x, 16, LAST_GLE(x)))) + +#define PLE (x, val, bits, ifnot) \ + __builtin_choose_expr( \ + (sizeof(*(x)) == bits/8), \ + put_unaligned_le##bits((val), (x)), ifnot) + +extern void bad_put_le (void ); +#define LAST_PLE (x, val) \ + __builtin_choose_expr(sizeof(*(x)) == 1, *(x) = (val), bad_put_le()) + +#define PUT_LE (x, val) \ + PLE(x, val, 64, PLE(x, val, 32, PLE(x, val, 16, LAST_PLE(x, val)))) +#define NSYMS (sizeof(required_syms) / sizeof(required_syms[0]))
Signature · 1 line
1.9.3
tip-bot for Andy Lutomirski
merge bot[tip:x86/vdso] x86/vdso: Add PUT_LE to store little-endian values
x86/vdso: Add PUT_LE to store little-endian values
Commit-ID: b4b31f6101433e4b8ee73779b69b935af07682f8 Gitweb: http://git.kernel.org/tip/b4b31f6101433e4b8ee73779b69b935af07682f8 Author: Andy Lutomirski <[email protected]> AuthorDate: Thu, 12 Jun 2014 17:53:11 -0700 Committer: H. Peter Anvin <[email protected]> CommitDate: Thu, 12 Jun 2014 19:01:51 -0700
x86/vdso: Add PUT_LE to store little-endian values
Add PUT_LE() by analogy with GET_LE() to write littleendian values in addition to reading them.
--- arch/x86/vdso/vdso2c.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
arch/x86/vdso/vdso2c.c +16 -3
@@ -54,7 +54,7 @@ static void fail(const char *format, ...) }/* -* Evil macros to do a little-endian read. +* Evil macros for little-endian reads and writes */ #define GLE (x, bits, ifnot) \ __builtin_choose_expr( \
@@ -62,11 +62,24 @@ static void fail(const char *format, ...) (__typeof__(*(x)))get_unaligned_le##bits(x), ifnot)extern void bad_get_le (void ); -#define LAST_LE (x) \ +#define LAST_GLE (x) \ __builtin_choose_expr(sizeof(*(x)) == 1, *(x), bad_get_le())#define GET_LE (x) \ - GLE(x, 64, GLE(x, 32, GLE(x, 16, LAST_LE(x)))) + GLE(x, 64, GLE(x, 32, GLE(x, 16, LAST_GLE(x)))) + +#define PLE (x, val, bits, ifnot) \ + __builtin_choose_expr( \ + (sizeof(*(x)) == bits/8), \ + put_unaligned_le##bits((val), (x)), ifnot) + +extern void bad_put_le (void ); +#define LAST_PLE (x, val) \ + __builtin_choose_expr(sizeof(*(x)) == 1, *(x) = (val), bad_put_le()) + +#define PUT_LE (x, val) \ + PLE(x, val, 64, PLE(x, val, 32, PLE(x, val, 16, LAST_PLE(x, val)))) +#define NSYMS (sizeof(required_syms) / sizeof(required_syms[0]))
PATCH v3 4/4 x86,vdso: Hack to keep 64-bit Go programs working
In reply to Andy LutomirskiThe Go runtime has a buggy vDSO parser that currently segfaults. This writes an empty SHT_DYNSYM entry that causes Go's runtime to malfunction by thinking that the vDSO is empty rather than malfunctioning by running off the end and segfaulting.
This is currently broken for big-endian build hosts. The hack should also be disabled for x32, but I'm not sure what the right way to do that is.
--- arch/x86/vdso/Makefile | 18 ++++++++++-------- arch/x86/vdso/vdso-fakesections.c | 32 ++++++++++++++++++++++++++++++++ arch/x86/vdso/vdso2c.h | 23 ++++++++++++++++++----- 3 files changed, 60 insertions(+), 13 deletions(-) create mode 100644 arch/x86/vdso/vdso-fakesections.c
arch/x86/vdso/Makefile +10 -8
@@ -15,12 +15,8 @@ vdso-install-$(VDSO32-y) += $(vdso32-images) # files to link into the vdso -vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o - -vobjs-$(VDSOX32-y) += $(vobjx32s-compat) - -# Filter out x32 objects. -vobj64s := $(filter-out $(vobjx32s-compat),$(vobjs-y)) +vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o vdso-fakesections.o +vobjs-nox32 := vdso-fakesections.o # files to link into kernel obj-y += vma.o
@@ -34,7 +30,7 @@ vdso_img-$(VDSO32-y) += 32-sysenter obj-$(VDSO32-y) += vdso32-setup.o -vobjs := $(foreach F,$(vobj64s),$(obj)/$F) +vobjs := $(foreach F,$(vobjs-y),$(obj)/$F) $(obj)/vdso.o: $(obj)/vdso.so
@@ -104,7 +100,13 @@ VDSO_LDFLAGS_vdsox32.lds = -Wl,-m,elf32_x86_64 \ -Wl,-z,max-page-size=4096 \ -Wl,-z,common-page-size=4096 -vobjx32s-y := $(vobj64s:.o=-x32.o) +# 64-bit objects to re-brand as x32 +vobjs64-for-x32 := $(filter-out $(vobjs-nox32),$(vobjs-y)) + +# x32-rebranded versions +vobjx32s-y := $(vobjs64-for-x32:.o=-x32.o) + +# same thing, but in the output directory vobjx32s := $(foreach F,$(vobjx32s-y),$(obj)/$F) # Convert 64bit object file to x32 for x32 vDSO.
arch/x86/vdso/vdso-fakesections.c new file +32 -0
@@ -0,0 +1,32 @@ +/* +* Copyright 2014 Andy Lutomirski +* Subject to the GNU Public License, v.2 +* +* Hack to keep broken Go programs working. +* +* The Go runtime had a couple of bugs: it would read the section table to try +* to figure out how many dynamic symbols there were (it shouldn't have looked +* at the section table at all) and, if there were no SHT_SYNDYM section table +* entry, it would use an uninitialized value for the number of symbols. As a +* workaround, we supply a minimal section table. vdso2c will adjust the +* in-memory image so that "vdso_fake_sections" becomes the section table. +* +* The bug was introduced by: +* https://code.google.com/p/go/source/detail?r=56ea40aac72b (2012-08-31) +* and is being addressed in the Go runtime in this issue: +* https://code.google.com/p/go/issues/detail?id=8197 +*/ + +#ifndef __x86_64__ +#error This hack is specific to the 64-bit vDSO +#endif + +#include <linux/elf.h> + +extern const __visible struct elf64_shdr vdso_fake_sections[]; +const __visible struct elf64_shdr vdso_fake_sections[] = { + { + .sh_type =SHT_DYNSYM , + .sh_entsize =sizeof (Elf64_Sym), + } +};
arch/x86/vdso/vdso2c.h +18 -5
@@ -18,6 +18,8 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name)const char *secstrings;uint64_t syms[NSYMS ] = {}; +uint64_t fake_sections_value =0 , fake_sections_size =0 ; +Elf_Phdr *pt = (Elf_Phdr *)(addr +GET_LE (&hdr->e_phoff));/* Walk the segment table. */
@@ -84,6 +86,7 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name)GET_LE (&symtab_hdr->sh_entsize) * i;const char *name = addr +GET_LE (&strtab_hdr->sh_offset) +GET_LE (&sym->st_name); +for (k =0 ; k <NSYMS ; k++) {if (!strcmp (name, required_syms[k])) {if (syms[k]) {
@@ -93,6 +96,13 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name) syms[k] =GET_LE (&sym->st_value); } } + +if (!strcmp (name,"vdso_fake_sections" )) { +if (fake_sections_value) +fail ("duplicate vdso_fake_sections\n" ); + fake_sections_value =GET_LE (&sym->st_value); + fake_sections_size =GET_LE (&sym->st_size); + } }/* Validate mapping addresses. */
@@ -112,11 +122,14 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name)if (syms[sym_end_mapping] %4096 )fail ("end_mapping must be a multiple of 4096\n" ); -/* Remove sections. */ - hdr->e_shoff =0 ; - hdr->e_shentsize =0 ; - hdr->e_shnum =0 ; - hdr->e_shstrndx =SHN_UNDEF ;/* SHN_UNDEF == 0 */ +/* Remove sections or use fakes */ +if (fake_sections_size %sizeof (Elf_Shdr)) +fail ("vdso_fake_sections size is not a multiple of %ld\n" , + (long )sizeof (Elf_Shdr)); +PUT_LE (&hdr->e_shoff, fake_sections_value); +PUT_LE (&hdr->e_shentsize, fake_sections_value ?sizeof (Elf_Shdr) :0 ); +PUT_LE (&hdr->e_shnum, fake_sections_size /sizeof (Elf_Shdr)); +PUT_LE (&hdr->e_shstrndx,SHN_UNDEF );if (!name) {fwrite (addr, load_size,1 , outfile);
Signature · 1 line
1.9.3
H. Peter Anvin
reviewerRe: [PATCH v3 4/4] x86,vdso: Hack to keep 64-bit Go programs working
Quoted 5 lines · Andy Lutomirski
This is currently broken for big-endian build hosts. The hack should also be disabled for x32, but I'm not sure what the right way to do that is.
This comment is obsolete, no?
-hpa
Re: [PATCH v3 4/4] x86,vdso: Hack to keep 64-bit Go programs working
Quoted 8 lines · H. Peter Anvin
Quoted 5 lines · Andy Lutomirski
This is currently broken for big-endian build hosts. The hack should also be disabled for x32, but I'm not sure what the right way to do that is.
This comment is obsolete, no?
Yes, it's obsolete. Whoops.
Want a new patch?
--Andy
H. Peter Anvin
reviewerRe: [PATCH v3 4/4] x86,vdso: Hack to keep 64-bit Go programs working
Quoted 8 lines · Andy Lutomirski
Quoted 7 lines · H. Peter Anvin
Quoted 5 lines · Andy Lutomirski
This is currently broken for big-endian build hosts. The hack should also be disabled for x32, but I'm not sure what the right way to do that is.
Quoted 6 lines
Quoted 1 line
This comment is obsolete, no?
Yes, it's obsolete. Whoops.
Want a new patch?
Nah. I can edit.
-hpa
tip-bot for Andy Lutomirski
merge bot[tip:x86/vdso] x86/vdso: Hack to keep 64-bit Go programs working
In reply to Andy Lutomirskix86/vdso: Hack to keep 64-bit Go programs working
Commit-ID: e0bf7b86dace87eccdabdd66d2769ccad19cb81c Gitweb: http://git.kernel.org/tip/e0bf7b86dace87eccdabdd66d2769ccad19cb81c Author: Andy Lutomirski <[email protected]> AuthorDate: Thu, 12 Jun 2014 17:53:12 -0700 Committer: H. Peter Anvin <[email protected]> CommitDate: Thu, 12 Jun 2014 19:02:30 -0700
x86/vdso: Hack to keep 64-bit Go programs working
The Go runtime has a buggy vDSO parser that currently segfaults. This writes an empty SHT_DYNSYM entry that causes Go's runtime to malfunction by thinking that the vDSO is empty rather than malfunctioning by running off the end and segfaulting.
This affects x86-64 only as far as we know, so we do not need this for the i386 and x32 vdsos.
--- arch/x86/vdso/Makefile | 18 ++++++++++-------- arch/x86/vdso/vdso-fakesections.c | 32 ++++++++++++++++++++++++++++++++ arch/x86/vdso/vdso2c.h | 23 ++++++++++++++++++----- 3 files changed, 60 insertions(+), 13 deletions(-)
arch/x86/vdso/Makefile +10 -8
@@ -15,12 +15,8 @@ vdso-install-$(VDSO32-y) += $(vdso32-images) # files to link into the vdso -vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o - -vobjs-$(VDSOX32-y) += $(vobjx32s-compat) - -# Filter out x32 objects. -vobj64s := $(filter-out $(vobjx32s-compat),$(vobjs-y)) +vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o vdso-fakesections.o +vobjs-nox32 := vdso-fakesections.o # files to link into kernel obj-y += vma.o
@@ -34,7 +30,7 @@ vdso_img-$(VDSO32-y) += 32-sysenter obj-$(VDSO32-y) += vdso32-setup.o -vobjs := $(foreach F,$(vobj64s),$(obj)/$F) +vobjs := $(foreach F,$(vobjs-y),$(obj)/$F) $(obj)/vdso.o: $(obj)/vdso.so
@@ -104,7 +100,13 @@ VDSO_LDFLAGS_vdsox32.lds = -Wl,-m,elf32_x86_64 \ -Wl,-z,max-page-size=4096 \ -Wl,-z,common-page-size=4096 -vobjx32s-y := $(vobj64s:.o=-x32.o) +# 64-bit objects to re-brand as x32 +vobjs64-for-x32 := $(filter-out $(vobjs-nox32),$(vobjs-y)) + +# x32-rebranded versions +vobjx32s-y := $(vobjs64-for-x32:.o=-x32.o) + +# same thing, but in the output directory vobjx32s := $(foreach F,$(vobjx32s-y),$(obj)/$F) # Convert 64bit object file to x32 for x32 vDSO.
arch/x86/vdso/vdso-fakesections.c new file +32 -0
@@ -0,0 +1,32 @@ +/* +* Copyright 2014 Andy Lutomirski +* Subject to the GNU Public License, v.2 +* +* Hack to keep broken Go programs working. +* +* The Go runtime had a couple of bugs: it would read the section table to try +* to figure out how many dynamic symbols there were (it shouldn't have looked +* at the section table at all) and, if there were no SHT_SYNDYM section table +* entry, it would use an uninitialized value for the number of symbols. As a +* workaround, we supply a minimal section table. vdso2c will adjust the +* in-memory image so that "vdso_fake_sections" becomes the section table. +* +* The bug was introduced by: +* https://code.google.com/p/go/source/detail?r=56ea40aac72b (2012-08-31) +* and is being addressed in the Go runtime in this issue: +* https://code.google.com/p/go/issues/detail?id=8197 +*/ + +#ifndef __x86_64__ +#error This hack is specific to the 64-bit vDSO +#endif + +#include <linux/elf.h> + +extern const __visible struct elf64_shdr vdso_fake_sections[]; +const __visible struct elf64_shdr vdso_fake_sections[] = { + { + .sh_type =SHT_DYNSYM , + .sh_entsize =sizeof (Elf64_Sym), + } +};
arch/x86/vdso/vdso2c.h +18 -5
@@ -18,6 +18,8 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name)const char *secstrings;uint64_t syms[NSYMS ] = {}; +uint64_t fake_sections_value =0 , fake_sections_size =0 ; +Elf_Phdr *pt = (Elf_Phdr *)(addr +GET_LE (&hdr->e_phoff));/* Walk the segment table. */
@@ -84,6 +86,7 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name)GET_LE (&symtab_hdr->sh_entsize) * i;const char *name = addr +GET_LE (&strtab_hdr->sh_offset) +GET_LE (&sym->st_name); +for (k =0 ; k <NSYMS ; k++) {if (!strcmp (name, required_syms[k])) {if (syms[k]) {
@@ -93,6 +96,13 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name) syms[k] =GET_LE (&sym->st_value); } } + +if (!strcmp (name,"vdso_fake_sections" )) { +if (fake_sections_value) +fail ("duplicate vdso_fake_sections\n" ); + fake_sections_value =GET_LE (&sym->st_value); + fake_sections_size =GET_LE (&sym->st_size); + } }/* Validate mapping addresses. */
@@ -112,11 +122,14 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name)if (syms[sym_end_mapping] %4096 )fail ("end_mapping must be a multiple of 4096\n" ); -/* Remove sections. */ - hdr->e_shoff =0 ; - hdr->e_shentsize =0 ; - hdr->e_shnum =0 ; - hdr->e_shstrndx =SHN_UNDEF ;/* SHN_UNDEF == 0 */ +/* Remove sections or use fakes */ +if (fake_sections_size %sizeof (Elf_Shdr)) +fail ("vdso_fake_sections size is not a multiple of %ld\n" , + (long )sizeof (Elf_Shdr)); +PUT_LE (&hdr->e_shoff, fake_sections_value); +PUT_LE (&hdr->e_shentsize, fake_sections_value ?sizeof (Elf_Shdr) :0 ); +PUT_LE (&hdr->e_shnum, fake_sections_size /sizeof (Elf_Shdr)); +PUT_LE (&hdr->e_shstrndx,SHN_UNDEF );if (!name) {fwrite (addr, load_size,1 , outfile);