mailing list of musl libc
 help / color / mirror / code / Atom feed
* 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).