diff mbox

PING: [PATCH] Add --enable-static-pie to build static PIE

Message ID CAMe9rOpSHD14xsK51DG7c+UzGptk4LFFCDM2_dbJ7cA1Quyfjg@mail.gmail.com
State New, archived
Headers show

Commit Message

H.J. Lu Sept. 27, 2017, 8:30 a.m. UTC
On 9/26/17, Joseph Myers <joseph@codesourcery.com> wrote:
> On Tue, 26 Sep 2017, H.J. Lu wrote:
>
>> Any objections?
>
> This is a complicated feature; it clearly needs someone to do a detailed
> technical review before it can go in, however long that takes.  It's only
> borderline-obvious patches one might consider committing after a while in
> the absence of objections but without review or relevant maintainership.
>
> Perhaps you could expand more on the design and the rationale for the

Dynamic linker, ld.so, is a standalone program which can be loaded at
any address.  This patch adds a configure option, --enable-static-pie,
to embed the part of ld.so in static executable to create static position
independent executable (static PIE).

> choices made?  Why are x86_64 configure changes needed, for example?

This checks for linker fix:

https://sourceware.org/bugzilla/show_bug.cgi?id=21782

> What changes might other architectures need to support this feature (when

Static PIE should work on all architectures which support PIE.

> would an architecture need or not need architecture-specific configure
> changes, etc.), and have you tried a build-many-glibcs.py run with this
> option passed to all the builds to see how other architectures do?  Why

build-many-glibcs.py has no regressions.  But I didn't try --enable-static-pie
with build-many-glibcs.py.  I encourage target maintainers to give
hjl/pie/static
branch a try.

> build libc.a with -fPIE rather than building a separate PIE copy of libc.a
> (i.e., making this a bit more like --enable-profile as an option to build
> an additional library variant) and making GCC use that separate copy in
> the static-PIE case?
>

No separate PIE copy of libc.a, is used for static PIE to minimize
GCC driver change.  To improve system security, glibc can be built with
--enable-static-pie, and the same libc.a can be used to build both static
executable and static PIE.

> The patch is missing documentation in install.texi of the new configure
> option and associated regeneration of INSTALL.

Done.  I also updated NEWS.

Here is the updated patch.

Thanks.

Comments

Szabolcs Nagy Sept. 27, 2017, 9:43 a.m. UTC | #1
On 27/09/17 09:30, H.J. Lu wrote:
> On 9/26/17, Joseph Myers <joseph@codesourcery.com> wrote:
>> On Tue, 26 Sep 2017, H.J. Lu wrote:
>>
>>> Any objections?
>>
>> This is a complicated feature; it clearly needs someone to do a detailed
>> technical review before it can go in, however long that takes.  It's only
>> borderline-obvious patches one might consider committing after a while in
>> the absence of objections but without review or relevant maintainership.
>>
>> Perhaps you could expand more on the design and the rationale for the
> 
> Dynamic linker, ld.so, is a standalone program which can be loaded at
> any address.  This patch adds a configure option, --enable-static-pie,
> to embed the part of ld.so in static executable to create static position
> independent executable (static PIE).
> 

can it be too late to do the relocation in __libc_start_main?

that comes after the crt1 startup code and an arch specific wrapper
which may end up having R_*_RELATIVE relocs for various function
pointers (including __libc_start_main itself and its arguments)

what guarantees that __libc_start_main can always be called
without relocations (pc relative calls have limits on some
targets)?

i'd expect the relocation to happen in the crt1.o startup code.
Florian Weimer Sept. 27, 2017, 10:55 a.m. UTC | #2
On 09/27/2017 10:30 AM, H.J. Lu wrote:
>> would an architecture need or not need architecture-specific configure
>> changes, etc.), and have you tried a build-many-glibcs.py run with this
>> option passed to all the builds to see how other architectures do?  Why

> build-many-glibcs.py has no regressions.

On which tree?  I think commit 6cd380dd366d728da9f579eeb9f7f4c47f48e474 
added absolute relocations to the startup code, which I expect will not 
work for static PIE.

Thanks,
Florian
Szabolcs Nagy Sept. 27, 2017, 11:08 a.m. UTC | #3
On 27/09/17 11:55, Florian Weimer wrote:
> On 09/27/2017 10:30 AM, H.J. Lu wrote:
>>> would an architecture need or not need architecture-specific configure
>>> changes, etc.), and have you tried a build-many-glibcs.py run with this
>>> option passed to all the builds to see how other architectures do?  Why
> 
>> build-many-glibcs.py has no regressions.
> 
> On which tree?  I think commit 6cd380dd366d728da9f579eeb9f7f4c47f48e474 added absolute relocations to the
> startup code, which I expect will not work for static PIE.
> 

i'd expect the Scrt1.o to be used for PIE which have no ABS relocs
however it can have RELATIVE relocs if GOT entries are used i think
(even on x86_64).
Joseph Myers Sept. 27, 2017, 12:41 p.m. UTC | #4
On Wed, 27 Sep 2017, H.J. Lu wrote:

> > would an architecture need or not need architecture-specific configure
> > changes, etc.), and have you tried a build-many-glibcs.py run with this
> > option passed to all the builds to see how other architectures do?  Why
> 
> build-many-glibcs.py has no regressions.  But I didn't try --enable-static-pie
> with build-many-glibcs.py.  I encourage target maintainers to give
> hjl/pie/static
> branch a try.

I think the most useful test for this purpose (of assessing architecture 
support) is adding --enable-static-pie alongside --enable-profile in 
build-many-glibcs.py and seeing how the build goes.

> No separate PIE copy of libc.a, is used for static PIE to minimize
> GCC driver change.  To improve system security, glibc can be built with

The question should be what's best for the GNU system and the GNU 
toolchain as a whole, rather than minimizing the GCC driver changes.

> > The patch is missing documentation in install.texi of the new configure
> > option and associated regeneration of INSTALL.
> 
> Done.  I also updated NEWS.

To me, both the install.texi and NEWS changes read as saying that programs 
such as ldconfig are built as PIEs.  That's a separate matter from 
supporting user programs built as static PIEs.  I'd have thought the main 
point of this feature, which is the one that NEWS and install.texi should 
emphasize, is the support for user programs built as static PIEs.
H.J. Lu Sept. 27, 2017, 9:12 p.m. UTC | #5
On 9/27/17, Szabolcs Nagy <szabolcs.nagy@arm.com> wrote:
> On 27/09/17 09:30, H.J. Lu wrote:
>> On 9/26/17, Joseph Myers <joseph@codesourcery.com> wrote:
>>> On Tue, 26 Sep 2017, H.J. Lu wrote:
>>>
>>>> Any objections?
>>>
>>> This is a complicated feature; it clearly needs someone to do a detailed
>>> technical review before it can go in, however long that takes.  It's
>>> only
>>> borderline-obvious patches one might consider committing after a while
>>> in
>>> the absence of objections but without review or relevant maintainership.
>>>
>>> Perhaps you could expand more on the design and the rationale for the
>>
>> Dynamic linker, ld.so, is a standalone program which can be loaded at
>> any address.  This patch adds a configure option, --enable-static-pie,
>> to embed the part of ld.so in static executable to create static position
>> independent executable (static PIE).
>>
>
> can it be too late to do the relocation in __libc_start_main?
>
> that comes after the crt1 startup code and an arch specific wrapper
> which may end up having R_*_RELATIVE relocs for various function
> pointers (including __libc_start_main itself and its arguments)
>
> what guarantees that __libc_start_main can always be called
> without relocations (pc relative calls have limits on some
> targets)?
>
> i'd expect the relocation to happen in the crt1.o startup code.
>

I expect that all relocations before __libc_start_main should be
resolved by linker.

For x86, I did

commit 5b736bc9b55115e67129e77db4de6cf193054cd2
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Wed Aug 2 10:27:14 2017 -0700

    x86-64: Check PIC instead of SHARED in start.S

    Since start.o may be compiled as PIC, we should check PIC instead of
    SHARED.

            * sysdeps/x86_64/start.S (_start): Check PIC instead of SHARED.

 commit 1e8e527dd9718eaebe8417b73befb0c821b7b327
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Fri Aug 4 12:59:35 2017 -0700

    i386: Support static PIE in start.S

    Since start.o may be compiled as PIC, we should check PIC instead of
    SHARED.  Also avoid dynamic relocation against main in static PIE since
    _start is the entry point before the executable is relocated.

            * sysdeps/i386/start.S (_start): Check Check PIC instead of
            SHARED.  Avoid dynamic relocation against main in static PIE.

I expected other targets can be fixed similarly.

H.J.
H.J. Lu Sept. 27, 2017, 9:14 p.m. UTC | #6
On 9/27/17, Florian Weimer <fweimer@redhat.com> wrote:
> On 09/27/2017 10:30 AM, H.J. Lu wrote:
>>> would an architecture need or not need architecture-specific configure
>>> changes, etc.), and have you tried a build-many-glibcs.py run with this
>>> option passed to all the builds to see how other architectures do?  Why
>
>> build-many-glibcs.py has no regressions.
>
> On which tree?  I think commit 6cd380dd366d728da9f579eeb9f7f4c47f48e474

I used hjl/pie/static branch.

> added absolute relocations to the startup code, which I expect will not
> work for static PIE.
>

It may need something like

commit 1e8e527dd9718eaebe8417b73befb0c821b7b327
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Fri Aug 4 12:59:35 2017 -0700

    i386: Support static PIE in start.S

    Since start.o may be compiled as PIC, we should check PIC instead of
    SHARED.  Also avoid dynamic relocation against main in static PIE since
    _start is the entry point before the executable is relocated.

            * sysdeps/i386/start.S (_start): Check Check PIC instead of
H.J. Lu Sept. 27, 2017, 9:16 p.m. UTC | #7
On 9/27/17, Szabolcs Nagy <szabolcs.nagy@arm.com> wrote:
> On 27/09/17 11:55, Florian Weimer wrote:
>> On 09/27/2017 10:30 AM, H.J. Lu wrote:
>>>> would an architecture need or not need architecture-specific configure
>>>> changes, etc.), and have you tried a build-many-glibcs.py run with this
>>>> option passed to all the builds to see how other architectures do?  Why
>>
>>> build-many-glibcs.py has no regressions.
>>
>> On which tree?  I think commit 6cd380dd366d728da9f579eeb9f7f4c47f48e474
>> added absolute relocations to the
>> startup code, which I expect will not work for static PIE.
>>
>
> i'd expect the Scrt1.o to be used for PIE which have no ABS relocs
> however it can have RELATIVE relocs if GOT entries are used i think
> (even on x86_64).

x86--64 and i686 are OK.  You may need to update your start.S to avoid
dynamic relocations in static PIE.
H.J. Lu Sept. 27, 2017, 9:28 p.m. UTC | #8
On 9/27/17, Joseph Myers <joseph@codesourcery.com> wrote:
> On Wed, 27 Sep 2017, H.J. Lu wrote:
>
>> > would an architecture need or not need architecture-specific configure
>> > changes, etc.), and have you tried a build-many-glibcs.py run with this
>> > option passed to all the builds to see how other architectures do?  Why
>>
>> build-many-glibcs.py has no regressions.  But I didn't try
>> --enable-static-pie
>> with build-many-glibcs.py.  I encourage target maintainers to give
>> hjl/pie/static
>> branch a try.
>
> I think the most useful test for this purpose (of assessing architecture
> support) is adding --enable-static-pie alongside --enable-profile in
> build-many-glibcs.py and seeing how the build goes.

I will do that.

>> No separate PIE copy of libc.a, is used for static PIE to minimize
>> GCC driver change.  To improve system security, glibc can be built with
>
> The question should be what's best for the GNU system and the GNU
> toolchain as a whole, rather than minimizing the GCC driver changes.

PIE libc.a can be used to create both static executable and static PIE.
Also CCC from some OSVs are now default to PIE.  There libc.a is built
with PIE today.  We are discourage static executables.  I don't see the
need for a separate PIE copy of libc.a.  That is OSV has a choice to
either have PIE libc.a or non-PIE libc.a.  I am expecting PIE libc.a
is provided by more OSVs.

>> > The patch is missing documentation in install.texi of the new configure
>> > option and associated regeneration of INSTALL.
>>
>> Done.  I also updated NEWS.
>
> To me, both the install.texi and NEWS changes read as saying that programs
> such as ldconfig are built as PIEs.  That's a separate matter from

All static executables, including tests,  within glibc are built as
static PIE when
 --enable-static-pie is used.  That is how I noticed that
--eh-frame-hdr is needed
for static PIE.

> supporting user programs built as static PIEs.  I'd have thought the main
> point of this feature, which is the one that NEWS and install.texi should
> emphasize, is the support for user programs built as static PIEs.
>
Joseph Myers Sept. 28, 2017, 12:48 a.m. UTC | #9
On Wed, 27 Sep 2017, H.J. Lu wrote:

> > I think the most useful test for this purpose (of assessing architecture
> > support) is adding --enable-static-pie alongside --enable-profile in
> > build-many-glibcs.py and seeing how the build goes.
> 
> I will do that.

Thanks.  To be clear, I don't think such a build-many-glibcs.py change 
would be appropriate for mainline (given a version of --enable-static-pie 
that changes how libc.a is built rather than enabling building additional 
static-PIE libraries) - although adding one or two additional static-PIE 
configurations to build-many-glibcs.py might be - but for testing purposes 
it's very useful to understand how widely this option works (or at least 
builds) for other architectures.

> > To me, both the install.texi and NEWS changes read as saying that programs
> > such as ldconfig are built as PIEs.  That's a separate matter from
> 
> All static executables, including tests,  within glibc are built as
> static PIE when
>  --enable-static-pie is used.  That is how I noticed that
> --eh-frame-hdr is needed
> for static PIE.

My point is that NEWS and the install.texi text are for users of glibc, 
and I think it's more significant for users that they can build *their 
own* programs as static PIEs, than that ldconfig and sln are static PIEs.  
So the NEWS and install.texi text should talk about the effects of the 
option on what users can do with the resulting glibc.
Markus Trippelsdorf Sept. 28, 2017, 9:37 a.m. UTC | #10
On 2017.09.27 at 01:30 -0700, H.J. Lu wrote:
> No separate PIE copy of libc.a, is used for static PIE to minimize
> GCC driver change.  To improve system security, glibc can be built with
> --enable-static-pie, and the same libc.a can be used to build both static
> executable and static PIE.

It doesn't work:

 ~ % echo "int main(){}" | gcc -x c - -static
/usr/lib/gcc/x86_64-pc-linux-gnu/7.2.1/../../../../lib64/libc.a(dl-support.o): In function `elf_machine_load_address':
/home/markus/glibc/elf/../sysdeps/x86_64/dl-machine.h:59: undefined reference to `_DYNAMIC'
/usr/lib/gcc/x86_64-pc-linux-gnu/7.2.1/../../../../lib64/libc.a(dl-support.o): In function `elf_get_dynamic_info':
/home/markus/glibc/elf/get-dynamic-info.h:48: undefined reference to `_DYNAMIC'
/usr/lib/gcc/x86_64-pc-linux-gnu/7.2.1/../../../../x86_64-pc-linux-gnu/bin/ld: a.out: hidden symbol `_DYNAMIC' isn't defined
/usr/lib/gcc/x86_64-pc-linux-gnu/7.2.1/../../../../x86_64-pc-linux-gnu/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
diff mbox

Patch

From f39009437f70000675460313c20b597ac3675e37 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Mon, 17 Jul 2017 08:17:32 -0700
Subject: [PATCH] Add --enable-static-pie configure option to build static PIE

Dynamic linker, ld.so, is a standalone program which can be loaded at
any address.  This patch adds a configure option, --enable-static-pie,
to embed the part of ld.so in static executable to create static position
independent executable (static PIE).  A static PIE is similar to static
executable, but can be loaded at any address without help from a dynamic
linker.  When --enable-static-pie is used to configure glibc, libc.a is
built as PIE.  The resulting libc.a can be used together with GCC 8 or
above to build static PIE with the compiler option, -static-pie.  But
GCC 8 isn't required to build glibc with --enable-static-pie.  When an
older GCC is used to build glibc with --enable-static-pie, proper input
files are passed to linker to create static executables as static PIE,
together with "-z text" to prevent dynamic relocations in read-only
segments, which are allowed in static PIE.

Static PIE should work on all architectures which support PIE.

NB: No separate PIE copy of libc.a, is used for static PIE to minimize
GCC driver change.  To improve system security, glibc can be built with
--enable-static-pie, and the same libc.a can be used to build both static
executable and static PIE.

Linker requirements to build glibc with --enable-static-pie:

1. Linker supports --no-dynamic-linker to remove PT_INTERP segment from
static PIE.
2. Linker can create working static PIE.  The x86-64 linker needs the
fix for

https://sourceware.org/bugzilla/show_bug.cgi?id=21782

Binutils 2.29 or above are OK.

Tested on i686 and x86-64.

	* INSTALL: Regenerated.
	* Makeconfig (pic-default): Updated for --enable-static-pie.
	(pie-default): New for --enable-static-pie.
	(default-pie-ldflag): Likewise.
	(+link-static-before-libc): Add $(default-pie-ldflag).
	(+prectorT): Updated for --enable-static-pie.
	(+postctorT): Likewise.
	(CFLAGS-.o): Add $(pie-default).
	(CFLAGS-.op): Likewise.
	* NEWS: Mention --enable-static-pie.
	* config.h.in (ENABLE_STATIC_PIE): New.
	* configure.ac (--enable-static-pie): New configure option.
	(have-no-dynamic-linker): New LIBC_CONFIG_VAR.
	(have-static-pie): Likewise.
	Enable static PIE if linker supports --no-dynamic-linker.
	(ENABLE_STATIC_PIE): New AC_DEFINE.
	(enable-static-pie): New LIBC_CONFIG_VAR.
	* configure: Regenerated.
	* csu/libc-start.c (LIBC_START_MAIN): Call _dl_relocate_static_pie
	in libc.a.
	* csu/libc-tls.c (__libc_setup_tls): Add main_map->l_addr to
	initimage.
	* elf/dl-support.c: Include "dynamic-link.h" and don't include
	"get-dynamic-info.h" for --enable-static-pie.
	(_dl_relocate_static_pie): New function for --enable-static-pie.
	(STATIC_PIE_BOOTSTRAP): New for --enable-static-pie.
	(RESOLVE_MAP): Likewise.
	* elf/dynamic-link.h (ELF_DURING_STARTUP): Also check
	STATIC_PIE_BOOTSTRAP.
	* elf/get-dynamic-info.h (elf_get_dynamic_info): Likewise.
	* manual/install.texi: Document --enable-static-pie.
	* sysdeps/generic/ldsodefs.h (_dl_relocate_static_pie): New.
	* sysdeps/x86_64/configure.ac: Check if linker supports static PIE.
	* sysdeps/x86_64/configure: Regenerated.
---
 INSTALL                     |  5 +++
 Makeconfig                  | 20 ++++++++++--
 NEWS                        |  2 ++
 config.h.in                 |  3 ++
 configure                   | 78 +++++++++++++++++++++++++++++++++++++++++++++
 configure.ac                | 29 +++++++++++++++++
 csu/libc-start.c            |  2 ++
 csu/libc-tls.c              |  6 ++--
 elf/dl-support.c            | 39 +++++++++++++++++++++--
 elf/dynamic-link.h          |  2 +-
 elf/get-dynamic-info.h      |  6 ++--
 manual/install.texi         |  5 +++
 sysdeps/generic/ldsodefs.h  |  7 ++++
 sysdeps/x86_64/configure    | 33 +++++++++++++++++++
 sysdeps/x86_64/configure.ac | 25 +++++++++++++++
 15 files changed, 252 insertions(+), 10 deletions(-)

diff --git a/INSTALL b/INSTALL
index 4d9024c667..67a0083b88 100644
--- a/INSTALL
+++ b/INSTALL
@@ -106,6 +106,11 @@  will be used, and CFLAGS sets optimization options for the compiler.
      systems support shared libraries; you need ELF support and
      (currently) the GNU linker.
 
+'--enable-static-pie'
+     Build static executables as position independent executable (static
+     PIE) which is similar to static executable, but can be loaded at
+     any address without help from a dynamic linker.
+
 '--disable-profile'
      Don't build libraries with profiling information.  You may want to
      use this option if you don't plan to do profiling.
diff --git a/Makeconfig b/Makeconfig
index b51904b797..b45209fe5f 100644
--- a/Makeconfig
+++ b/Makeconfig
@@ -386,6 +386,16 @@  LDFLAGS.so += $(hashstyle-LDFLAGS)
 LDFLAGS-rtld += $(hashstyle-LDFLAGS)
 endif
 
+ifeq (yes,$(enable-static-pie))
+pic-default = -DPIC
+pie-default = $(pie-ccflag)
+ifeq (yes,$(have-static-pie))
+default-pie-ldflag = -static-pie
+else
+default-pie-ldflag = -Wl,-pie,--no-dynamic-linker,--eh-frame-hdr,-z,text
+endif
+endif
+
 # If lazy relocations are disabled, add the -z now flag.  Use
 # LDFLAGS-lib.so instead of LDFLAGS.so, to avoid adding the flag to
 # test modules.
@@ -435,6 +445,7 @@  endif
 # Command for statically linking programs with the C library.
 ifndef +link-static
 +link-static-before-libc = $(CC) -nostdlib -nostartfiles -static -o $@ \
+	      $(default-pie-ldflag) \
 	      $(sysdep-LDFLAGS) $(LDFLAGS) $(LDFLAGS-$(@F))  \
 	      $(addprefix $(csu-objpfx),$(static-start-installed-name)) \
 	      $(+preinit) $(+prectorT) \
@@ -651,8 +662,13 @@  endif
 +prectorS = `$(CC) $(sysdep-LDFLAGS) --print-file-name=crtbeginS.o`
 +postctorS = `$(CC) $(sysdep-LDFLAGS) --print-file-name=crtendS.o`
 # Variants of the two previous definitions for statically linking programs.
+ifeq (yes,$(enable-static-pie))
++prectorT = $(+prectorS)
++postctorT = $(+postctorS)
+else
 +prectorT = `$(CC) $(sysdep-LDFLAGS) --print-file-name=crtbeginT.o`
 +postctorT = `$(CC) $(sysdep-LDFLAGS) --print-file-name=crtend.o`
+endif
 csu-objpfx = $(common-objpfx)csu/
 elf-objpfx = $(common-objpfx)elf/
 
@@ -973,7 +989,7 @@  libtypes = $(foreach o,$(object-suffixes-for-libc),$(libtype$o))
 all-object-suffixes := .o .os .oS
 object-suffixes :=
 CPPFLAGS-.o = $(pic-default)
-CFLAGS-.o = $(filter %frame-pointer,$(+cflags))
+CFLAGS-.o = $(filter %frame-pointer,$(+cflags)) $(pie-default)
 libtype.o := lib%.a
 object-suffixes += .o
 ifeq (yes,$(build-shared))
@@ -998,7 +1014,7 @@  ifeq (yes,$(build-profile))
 all-object-suffixes += .op
 object-suffixes += .op
 CPPFLAGS-.op = -DPROF $(pic-default)
-CFLAGS-.op = -pg
+CFLAGS-.op = -pg $(pie-default)
 libtype.op = lib%_p.a
 endif
 
diff --git a/NEWS b/NEWS
index e346d2afe6..fcdef4385f 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,8 @@  Version 2.27
 
 Major new features:
 
+* Add --enable-static-pie configure option to build static PIE.
+
 * Optimized x86-64 asin, atan2, exp, expf, log, pow, atan, sin and tan
   with FMA, contributed by Arjan van de Ven and H.J. Lu from Intel.
 
diff --git a/config.h.in b/config.h.in
index 014fb4ea0e..26ed7865ef 100644
--- a/config.h.in
+++ b/config.h.in
@@ -236,6 +236,9 @@ 
 /* Build glibc with tunables support.  */
 #define HAVE_TUNABLES 0
 
+/* Define if static PIE is enabled.  */
+#define ENABLE_STATIC_PIE 0
+
 /* Some compiler options may now allow to use ebp in __asm__ (used mainly
    in i386 6 argument syscall issue).  */
 #define CAN_USE_REGISTER_ASM_EBP 0
diff --git a/configure b/configure
index 067d92d2ba..cd5cf5729f 100755
--- a/configure
+++ b/configure
@@ -767,6 +767,7 @@  with_default_link
 enable_sanity_checks
 enable_shared
 enable_profile
+enable_static_pie
 enable_timezone_tools
 enable_hardcoded_path_in_tests
 enable_stackguard_randomization
@@ -1424,6 +1425,7 @@  Optional Features:
                           in special situations) [default=yes]
   --enable-shared         build shared library [default=yes if GNU ld]
   --enable-profile        build profiled library [default=no]
+  --enable-static-pie     build static executables as PIE [default=no]
   --disable-timezone-tools
                           do not install timezone tools [default=install]
   --enable-hardcoded-path-in-tests
@@ -3372,6 +3374,13 @@  else
   profile=no
 fi
 
+# Check whether --enable-static-pie was given.
+if test "${enable_static_pie+set}" = set; then :
+  enableval=$enable_static_pie; static_pie=$enableval
+else
+  static_pie=no
+fi
+
 # Check whether --enable-timezone-tools was given.
 if test "${enable_timezone_tools+set}" = set; then :
   enableval=$enable_timezone_tools; enable_timezone_tools=$enableval
@@ -5994,6 +6003,62 @@  fi
 $as_echo "$libc_linker_feature" >&6; }
 
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker that supports --no-dynamic-linker" >&5
+$as_echo_n "checking for linker that supports --no-dynamic-linker... " >&6; }
+libc_linker_feature=no
+if test x"$gnu_ld" = x"yes"; then
+  libc_linker_check=`$LD -v --help 2>/dev/null | grep "\--no-dynamic-linker"`
+  if test -n "$libc_linker_check"; then
+    cat > conftest.c <<EOF
+int _start (void) { return 42; }
+EOF
+    if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp
+				-Wl,--no-dynamic-linker -nostdlib -nostartfiles
+				-fPIC -shared -o conftest.so conftest.c
+				1>&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+    then
+      libc_linker_feature=yes
+    fi
+    rm -f conftest*
+  fi
+fi
+if test $libc_linker_feature = yes; then
+  libc_cv_no_dynamic_linker=yes
+else
+  libc_cv_no_dynamic_linker=no
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_linker_feature" >&5
+$as_echo "$libc_linker_feature" >&6; }
+config_vars="$config_vars
+have-no-dynamic-linker = $libc_cv_no_dynamic_linker"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -static-pie" >&5
+$as_echo_n "checking for -static-pie... " >&6; }
+if ${libc_cv_static_pie+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if { ac_try='${CC-cc} -static-pie -xc /dev/null -S -o /dev/null'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then :
+  libc_cv_static_pie=yes
+else
+  libc_cv_static_pie=no
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_static_pie" >&5
+$as_echo "$libc_cv_static_pie" >&6; }
+config_vars="$config_vars
+have-static-pie = $libc_cv_static_pie"
+
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -fpie" >&5
 $as_echo_n "checking for -fpie... " >&6; }
 if ${libc_cv_fpie+:} false; then :
@@ -6888,6 +6953,19 @@  fi
 $as_echo "$libc_cv_pie_default" >&6; }
 
 
+if test "$static_pie" = yes; then
+  # The linker must support --no-dynamic-linker.
+  if test "$libc_cv_no_dynamic_linker" != yes; then
+    as_fn_error $? "linker support for --no-dynamic-linker needed" "$LINENO" 5
+  fi
+  # Default to PIE.
+  libc_cv_pie_default=yes
+  $as_echo "#define ENABLE_STATIC_PIE 1" >>confdefs.h
+
+fi
+config_vars="$config_vars
+enable-static-pie = $static_pie"
+
 
 
 
diff --git a/configure.ac b/configure.ac
index d412104d8c..09e1c7f23b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -176,6 +176,11 @@  AC_ARG_ENABLE([profile],
 			     [build profiled library @<:@default=no@:>@]),
 	      [profile=$enableval],
 	      [profile=no])
+AC_ARG_ENABLE([static-pie],
+	      AC_HELP_STRING([--enable-static-pie],
+			     [build static executables as PIE @<:@default=no@:>@]),
+	      [static_pie=$enableval],
+	      [static_pie=no])
 AC_ARG_ENABLE([timezone-tools],
 	      AC_HELP_STRING([--disable-timezone-tools],
 			     [do not install timezone tools @<:@default=install@:>@]),
@@ -1460,6 +1465,19 @@  LIBC_LINKER_FEATURE([-z execstack], [-Wl,-z,execstack],
 		    [libc_cv_z_execstack=yes], [libc_cv_z_execstack=no])
 AC_SUBST(libc_cv_z_execstack)
 
+LIBC_LINKER_FEATURE([--no-dynamic-linker],
+		    [-Wl,--no-dynamic-linker],
+		    [libc_cv_no_dynamic_linker=yes],
+		    [libc_cv_no_dynamic_linker=no])
+LIBC_CONFIG_VAR([have-no-dynamic-linker], [$libc_cv_no_dynamic_linker])
+
+AC_CACHE_CHECK(for -static-pie, libc_cv_static_pie, [dnl
+LIBC_TRY_CC_OPTION([-static-pie],
+		   [libc_cv_static_pie=yes],
+		   [libc_cv_static_pie=no])
+])
+LIBC_CONFIG_VAR([have-static-pie], [$libc_cv_static_pie])
+
 AC_CACHE_CHECK(for -fpie, libc_cv_fpie, [dnl
 LIBC_TRY_CC_OPTION([-fpie], [libc_cv_fpie=yes], [libc_cv_fpie=no])
 ])
@@ -1962,6 +1980,17 @@  fi
 rm -f conftest.*])
 AC_SUBST(libc_cv_pie_default)
 
+if test "$static_pie" = yes; then
+  # The linker must support --no-dynamic-linker.
+  if test "$libc_cv_no_dynamic_linker" != yes; then
+    AC_MSG_ERROR([linker support for --no-dynamic-linker needed])
+  fi
+  # Default to PIE.
+  libc_cv_pie_default=yes
+  AC_DEFINE(ENABLE_STATIC_PIE)
+fi
+LIBC_CONFIG_VAR([enable-static-pie], [$static_pie])
+
 AC_SUBST(profile)
 AC_SUBST(static_nss)
 
diff --git a/csu/libc-start.c b/csu/libc-start.c
index 24c63be02f..34dd125260 100644
--- a/csu/libc-start.c
+++ b/csu/libc-start.c
@@ -141,6 +141,8 @@  LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
   __libc_multiple_libcs = &_dl_starting_up && !_dl_starting_up;
 
 #ifndef SHARED
+  _dl_relocate_static_pie ();
+
   char **ev = &argv[argc + 1];
 
   __environ = ev;
diff --git a/csu/libc-tls.c b/csu/libc-tls.c
index 00138eb43a..1f8ddaf543 100644
--- a/csu/libc-tls.c
+++ b/csu/libc-tls.c
@@ -114,6 +114,8 @@  __libc_setup_tls (void)
   size_t tcb_offset;
   const ElfW(Phdr) *phdr;
 
+  struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+
   /* Look through the TLS segment if there is any.  */
   if (_dl_phdr != NULL)
     for (phdr = _dl_phdr; phdr < &_dl_phdr[_dl_phnum]; ++phdr)
@@ -122,7 +124,7 @@  __libc_setup_tls (void)
 	  /* Remember the values we need.  */
 	  memsz = phdr->p_memsz;
 	  filesz = phdr->p_filesz;
-	  initimage = (void *) phdr->p_vaddr;
+	  initimage = (void *) phdr->p_vaddr + main_map->l_addr;
 	  align = phdr->p_align;
 	  if (phdr->p_align > max_align)
 	    max_align = phdr->p_align;
@@ -163,8 +165,6 @@  __libc_setup_tls (void)
   _dl_static_dtv[0].counter = (sizeof (_dl_static_dtv) / sizeof (_dl_static_dtv[0])) - 2;
   // _dl_static_dtv[1].counter = 0;		would be needed if not already done
 
-  struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
-
   /* Initialize the TLS block.  */
 #if TLS_TCB_AT_TP
   _dl_static_dtv[2].pointer.val = ((char *) tlsblock + tcb_offset
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 5e3de90598..4e1e37072b 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -26,7 +26,11 @@ 
 #include <sys/param.h>
 #include <stdint.h>
 #include <ldsodefs.h>
-#include <dl-machine.h>
+#if ENABLE_STATIC_PIE
+# include "dynamic-link.h"
+#else
+# include <dl-machine.h>
+#endif
 #include <libc-lock.h>
 #include <dl-cache.h>
 #include <dl-librecon.h>
@@ -199,7 +203,9 @@  const ElfW(Ehdr) *_dl_sysinfo_dso;
 
 struct link_map *_dl_sysinfo_map;
 
-# include "get-dynamic-info.h"
+# if !ENABLE_STATIC_PIE
+#  include "get-dynamic-info.h"
+# endif
 #endif
 #include "setup-vdso.h"
 
@@ -302,6 +308,35 @@  _dl_aux_init (ElfW(auxv_t) *av)
 }
 #endif
 
+#if ENABLE_STATIC_PIE
+/* Relocate static executable with PIE.  */
+
+void
+_dl_relocate_static_pie (void)
+{
+# define STATIC_PIE_BOOTSTRAP
+# define RESOLVE_MAP(sym, version, flags) (&_dl_main_map)
+# include "dynamic-link.h"
+
+  /* Figure out the run-time load addres of static PIE.  */
+  _dl_main_map.l_addr = elf_machine_load_address ();
+
+  /* Read our own dynamic section and fill in the info array.  */
+  _dl_main_map.l_ld = ((void *) _dl_main_map.l_addr
+		       + elf_machine_dynamic ());
+  elf_get_dynamic_info (&_dl_main_map, NULL);
+
+# ifdef ELF_MACHINE_BEFORE_RTLD_RELOC
+  ELF_MACHINE_BEFORE_RTLD_RELOC (_dl_main_map.l_info);
+# endif
+
+  /* Relocate ourselves so we can do normal function calls and
+     data access using the global offset table.  */
+  ELF_DYNAMIC_RELOCATE (&_dl_main_map, 0, 0, 0);
+  _dl_main_map.l_relocated = 1;
+}
+#endif
+
 
 void
 _dl_non_dynamic_init (void)
diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h
index ebea7567cd..6278649711 100644
--- a/elf/dynamic-link.h
+++ b/elf/dynamic-link.h
@@ -94,7 +94,7 @@  elf_machine_lazy_rel (struct link_map *map,
 
 #ifdef RESOLVE_MAP
 
-# ifdef RTLD_BOOTSTRAP
+# if defined RTLD_BOOTSTRAP || defined STATIC_PIE_BOOTSTRAP
 #  define ELF_DURING_STARTUP (1)
 # else
 #  define ELF_DURING_STARTUP (0)
diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h
index 7525c3a5b2..eb26d23649 100644
--- a/elf/get-dynamic-info.h
+++ b/elf/get-dynamic-info.h
@@ -38,7 +38,7 @@  elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
   typedef Elf64_Xword d_tag_utype;
 #endif
 
-#ifndef RTLD_BOOTSTRAP
+#if !defined RTLD_BOOTSTRAP && !defined STATIC_PIE_BOOTSTRAP
   if (dyn == NULL)
     return;
 #endif
@@ -139,9 +139,11 @@  elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
   /* Only the bind now flags are allowed.  */
   assert (info[VERSYMIDX (DT_FLAGS_1)] == NULL
 	  || (info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val & ~DF_1_NOW) == 0);
+  /* Flags must not be set for ld.so.  */
   assert (info[DT_FLAGS] == NULL
 	  || (info[DT_FLAGS]->d_un.d_val & ~DF_BIND_NOW) == 0);
-  /* Flags must not be set for ld.so.  */
+#endif
+#if defined RTLD_BOOTSTRAP || defined STATIC_PIE_BOOTSTRAP
   assert (info[DT_RUNPATH] == NULL);
   assert (info[DT_RPATH] == NULL);
 #else
diff --git a/manual/install.texi b/manual/install.texi
index 35948b1bbb..e09fb2a4db 100644
--- a/manual/install.texi
+++ b/manual/install.texi
@@ -136,6 +136,11 @@  Don't build shared libraries even if it is possible.  Not all systems
 support shared libraries; you need ELF support and (currently) the GNU
 linker.
 
+@item --enable-static-pie
+Build static executables as position independent executable (static
+PIE) which is similar to static executable, but can be loaded at any
+address without help from a dynamic linker.
+
 @item --disable-profile
 Don't build libraries with profiling information.  You may want to use
 this option if you don't plan to do profiling.
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 1a4449eeb3..b4c032a06c 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1048,6 +1048,13 @@  extern void _dl_determine_tlsoffset (void) attribute_hidden;
    stack protector, among other things).  */
 void __libc_setup_tls (void);
 
+# if ENABLE_STATIC_PIE
+/* Relocate static executable with PIE.  */
+void _dl_relocate_static_pie (void) attribute_hidden;
+# else
+#  define _dl_relocate_static_pie()
+# endif
+
 /* Initialization of libpthread for statically linked applications.
    If libpthread is not linked in, this is an empty function.  */
 void __pthread_initialize_minimal (void) weak_function;
diff --git a/sysdeps/x86_64/configure b/sysdeps/x86_64/configure
index efef46b1b7..8ee15b8a25 100644
--- a/sysdeps/x86_64/configure
+++ b/sysdeps/x86_64/configure
@@ -85,6 +85,39 @@  if test x"$build_mathvec" = xnotset; then
   build_mathvec=yes
 fi
 
+if test "$static_pie" = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker static PIE support" >&5
+$as_echo_n "checking for linker static PIE support... " >&6; }
+if ${libc_cv_ld_static_pie+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat > conftest.s <<\EOF
+	.text
+	.global _start
+	.weak foo
+_start:
+	leaq	foo(%rip), %rax
+EOF
+  libc_cv_pie_option="-Wl,-pie"
+  if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS -nostartfiles -nostdlib $no_ssp $libc_cv_pie_option conftest.s 1>&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    libc_cv_ld_static_pie=yes
+  else
+    libc_cv_ld_static_pie=no
+  fi
+rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_ld_static_pie" >&5
+$as_echo "$libc_cv_ld_static_pie" >&6; }
+  if test "$libc_cv_ld_static_pie" != yes; then
+    as_fn_error $? "linker support for static PIE needed" "$LINENO" 5
+  fi
+fi
+
 $as_echo "#define PI_STATIC_AND_HIDDEN 1" >>confdefs.h
 
 
diff --git a/sysdeps/x86_64/configure.ac b/sysdeps/x86_64/configure.ac
index fa86e953ee..53e4a8277c 100644
--- a/sysdeps/x86_64/configure.ac
+++ b/sysdeps/x86_64/configure.ac
@@ -44,6 +44,31 @@  if test x"$build_mathvec" = xnotset; then
   build_mathvec=yes
 fi
 
+dnl Check if linker supports static PIE with the fix for
+dnl
+dnl https://sourceware.org/bugzilla/show_bug.cgi?id=21782
+dnl
+if test "$static_pie" = yes; then
+  AC_CACHE_CHECK(for linker static PIE support, libc_cv_ld_static_pie, [dnl
+cat > conftest.s <<\EOF
+	.text
+	.global _start
+	.weak foo
+_start:
+	leaq	foo(%rip), %rax
+EOF
+  libc_cv_pie_option="-Wl,-pie"
+  if AC_TRY_COMMAND(${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS -nostartfiles -nostdlib $no_ssp $libc_cv_pie_option conftest.s 1>&AS_MESSAGE_LOG_FD); then
+    libc_cv_ld_static_pie=yes
+  else
+    libc_cv_ld_static_pie=no
+  fi
+rm -f conftest*])
+  if test "$libc_cv_ld_static_pie" != yes; then
+    AC_MSG_ERROR([linker support for static PIE needed])
+  fi
+fi
+
 dnl It is always possible to access static and hidden symbols in an
 dnl position independent way.
 AC_DEFINE(PI_STATIC_AND_HIDDEN)
-- 
2.13.5