* Static PIE! (experimental)
@ 2012-05-24 4:57 Rich Felker
2012-05-24 6:06 ` Rich Felker
0 siblings, 1 reply; 2+ messages in thread
From: Rich Felker @ 2012-05-24 4:57 UTC (permalink / raw)
To: musl
[-- Attachment #1: Type: text/plain, Size: 2070 bytes --]
I've got very experimental static-linked PIE support working, using
the attached files. This isn't ready to go in (in fact, part of it
belongs in binutils' ldscripts, not musl), but it's a very cool proof
of concept. Some caveats:
- ALL object files, including those in static libs (that means libc
too!) must be built with -fPIE or -fPIC. So to use this, you really
need to commit to building your whole system this way.
- No error checking is done for invalid binaries; they'll likely crash
horribly. You can use readelf -a to check that the only relocations
are RELATIVE. If you see any other types of reloc, the binary is
broken (miscompiled or mislinked).
- Textrels are not supported. They would be very difficult to support
on hardened systems that don't allow RWX pages, since the binary
would be self-modifying while running (and thus switching
temporarily to RW- and later to R-X would not suffice).
- The provided linker script is for i386, but it's just a simple
change to the original file (sending .interp section to /DISCARD/)
and you can make the same change for any arch. Likewise, Zcrt1.s is
for i386, but it's trivially generated from Scrt1.s by adding a call
to __static_pie_reloc.
Command to use to link:
gcc -static -pie -fPIE -nostartfiles Zcrt1.s Zcrt2.c \
/usr/lib/crt[in].o -Wl,-T,elf_i386.xd "$@"
Of course you can pre-compile/assemble Zcrt1.o and Zcrt2.o if you
like. The reason they're split into 2 files is that the first is
startup asm (arch-specific) and the latter is written in C for
convenience (and arch-independence).
To get this to production stage...
1. The linker scripts need to be incorporated into binutils.
2. The gcc specfile needs to be edited to use alternate startfiles
when both -static and -pie are specified.
3. The new crt files need to be added to musl.
By the way, there's nothing musl-specific about any of this. If
anybody wants to build a static glibc with -fPIE and patch the call to
__static_pie_reloc into its startfiles, it should work too.. Same goes
for uClibc.
Rich
[-- Attachment #2: Zcrt1.s --]
[-- Type: text/plain, Size: 441 bytes --]
.weak _init
.weak _fini
.text
.global _start
_start:
mov %edx,%ebp
push %esp
addl $4,(%esp)
call __static_pie_reloc
pop %eax
mov %ebp,%edx
xor %ebp,%ebp
pop %ecx
mov %esp,%eax
and $-16,%esp
push %esp
push %esp
push %edx
call 1f
1: addl $_GLOBAL_OFFSET_TABLE_,(%esp)
pop %ebx
call 1f
1: addl $[_fini-.],(%esp)
call 1f
1: addl $[_init-.],(%esp)
push %eax
push %ecx
push main@GOT(%ebx)
call __libc_start_main@plt
1: jmp 1b
[-- Attachment #3: Zcrt2.c --]
[-- Type: text/plain, Size: 1056 bytes --]
#include <stddef.h>
#include <elf.h>
#include <limits.h>
#if SIZE_MAX == 0xffffffff
typedef Elf32_Phdr Phdr;
#else
typedef Elf64_Phdr Phdr;
#endif
void __static_pie_reloc(size_t *p)
{
size_t aux[16] = {0}, dyn[24] = {0}, *z;
for (; *p; p++);
for (p++; *p; p++);
for (p++; *p; p+=2) if (*p<16) aux[*p]=p[1];
if (!aux[AT_BASE]) aux[AT_BASE] = aux[AT_PHDR] & -4096;
for (p=(void *)aux[AT_PHDR]; *p; p=(void *)((char *)p+aux[AT_PHENT])) {
Phdr *ph = (void *)p;
if (ph->p_type == PT_DYNAMIC) {
p = (void *)(aux[AT_BASE] + ph->p_vaddr);
break;
}
}
for (; *p; p+=2) if (*p<24) dyn[*p]=p[1];
if (dyn[DT_REL]) {
p = (void *)(aux[AT_BASE] + dyn[DT_REL]);
z = (void *)((char *)p + dyn[DT_RELSZ]);
for (; p<z; p = (void *)((char *)p + dyn[DT_RELENT]))
*(size_t *)(aux[AT_BASE] + p[0]) += aux[AT_BASE];
}
if (dyn[DT_RELA]) {
p = (void *)(aux[AT_BASE] + dyn[DT_RELA]);
z = (void *)((char *)p + dyn[DT_RELASZ]);
for (; p<z; p = (void *)((char *)p + dyn[DT_RELAENT]))
*(size_t *)(aux[AT_BASE] + p[0]) = aux[AT_BASE] + p[2];
}
}
[-- Attachment #4: elf_i386.xd --]
[-- Type: text/plain, Size: 7094 bytes --]
/* Script for ld -pie: link position independent executable */
OUTPUT_FORMAT("elf32-i386", "elf32-i386",
"elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SEARCH_DIR("/usr/i486-pc-linux-gnu/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
SECTIONS
{
/* Read-only sections, merged into text segment: */
. = SEGMENT_START("text-segment", 0) + SIZEOF_HEADERS;
/DISCARD/ : { *(.interp) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
.hash : { *(.hash) }
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.init : { *(.rel.init) }
.rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
.rel.fini : { *(.rel.fini) }
.rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
.rel.data.rel.ro : { *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*) }
.rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
.rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
.rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
.rel.ctors : { *(.rel.ctors) }
.rel.dtors : { *(.rel.dtors) }
.rel.got : { *(.rel.got) }
.rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
.rel.ifunc : { *(.rel.ifunc) }
.rel.plt :
{
*(.rel.plt)
PROVIDE_HIDDEN (__rel_iplt_start = .);
*(.rel.iplt)
PROVIDE_HIDDEN (__rel_iplt_end = .);
}
.init :
{
KEEP (*(.init))
} =0x90909090
.plt : { *(.plt) *(.iplt) }
.text :
{
*(.text.unlikely .text.*_unlikely)
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
} =0x90909090
.fini :
{
KEEP (*(.fini))
} =0x90909090
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.eh_frame_hdr : { *(.eh_frame_hdr) }
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
. = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
/* Exception handling */
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
/* Thread Local Storage sections */
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
}
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
PROVIDE_HIDDEN (__fini_array_end = .);
}
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.jcr : { KEEP (*(.jcr)) }
.data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
.dynamic : { *(.dynamic) }
.got : { *(.got) *(.igot) }
. = DATA_SEGMENT_RELRO_END (12, .);
.got.plt : { *(.got.plt) *(.igot.plt) }
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .; PROVIDE (edata = .);
__bss_start = .;
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we don't
pad the .data section. */
. = ALIGN(. != 0 ? 32 / 8 : 1);
}
. = ALIGN(32 / 8);
. = ALIGN(32 / 8);
_end = .; PROVIDE (end = .);
. = DATA_SEGMENT_END (.);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* DWARF 3 */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: Static PIE! (experimental)
2012-05-24 4:57 Static PIE! (experimental) Rich Felker
@ 2012-05-24 6:06 ` Rich Felker
0 siblings, 0 replies; 2+ messages in thread
From: Rich Felker @ 2012-05-24 6:06 UTC (permalink / raw)
To: musl
On Thu, May 24, 2012 at 12:57:52AM -0400, Rich Felker wrote:
> Command to use to link:
>
> gcc -static -pie -fPIE -nostartfiles Zcrt1.s Zcrt2.c \
> /usr/lib/crt[in].o -Wl,-T,elf_i386.xd "$@"
One thing I forgot: it's possible that if -fPIC was used instead of
-fPIE for some files, -Wl,-Bsymbolic should be added to the command
line. Not sure if it's ever needed but it can't hurt.
Rich
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2012-05-24 6:06 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-05-24 4:57 Static PIE! (experimental) Rich Felker
2012-05-24 6:06 ` Rich Felker
Code repositories for project(s) associated with this public inbox
https://git.vuxu.org/mirror/musl/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).