2

I am trying to create a minimum executable using gcc/binutils. My ld scripts is as below:

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
              "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib64"); SEARCH_DIR("/usr/lib64/binutils/x86_64-pc-linux-gnu/2.35.164"); SEARCH_DIR("/usr/local/lib64"); SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib"); SEARCH_DIR("/usr/lib64/binutils/x86_64-pc-linux-gnu/2.35.1"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
SECTIONS
{
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
  .text           :
  {
    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
    *(.text.exit .text.exit.*)
    *(.text.startup .text.startup.*)
    *(.text.hot .text.hot.*)
    *(SORT(.text.sorted.*))
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf.em.  */
    *(.gnu.warning)
  }

  .data           :
  {
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  }
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
  }
}

Everything is OK for me, except that I see a extra segment header named "GNU_STACK" with size 0. The header increases 56 bytes of course. Does anyone know how to remove it? Creating elf by hand is crazy, I want to use gcc/binutils to do it.

Mr Pang
  • 883
  • 1
  • 6
  • 18
  • 1
    https://stackoverflow.com/q/58260465/634919 suggests that if you remove this section, all the pages of your program will be executable (including the stack), which is not a good thing in general. – Nate Eldredge Nov 23 '20 at 04:33

2 Answers2

1

Looks like no way to do it. Finally I modified binutils source to remove it.

Mr Pang
  • 883
  • 1
  • 6
  • 18
0

Note: the GNU_STACK ELF program header entry is required to mark the stack as not executable on legacy systems (32-bits x86, arm and powerpc, for example). If you remove it, the stack will be automatically marked as executable by the kernel:

linux-5.17/arch/arm/kernel.elf.c#n80

In modern architectures GNU_STACK is not needed, so your linker script should be modified with the following:

OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
              "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib64"); SEARCH_DIR("/usr/lib64/binutils/x86_64-pc-linux-gnu/2.35.164"); SEARCH_DIR("/usr/local/lib64"); SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib"); SEARCH_DIR("/usr/lib64/binutils/x86_64-pc-linux-gnu/2.35.1"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");

PHDRS
{
    READONLY PT_LOAD FILEHDR PHDRS;
    READWRITE PT_LOAD;
}

SECTIONS
{
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
  .text           :
  {
    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
    *(.text.exit .text.exit.*)
    *(.text.startup .text.startup.*)
    *(.text.hot .text.hot.*)
    *(SORT(.text.sorted.*))
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf.em.  */
    *(.gnu.warning)
  } : READONLY

  . = ALIGN(0x1000);

  .data           :
  {
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  } : READWRITE
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
  }
}

Change the PHDRS configuration as you prefer.

If you remove the GNU_STACK entry, you should probably use the -Wtrampoline or -fno-trampolines compiler flags:

3.8 Options to Request or Suppress Warnings

3.17 Options for Code Generation Conventions

hdante
  • 7,106
  • 2
  • 28
  • 34