Linker script and PT_PHDR not working as expected (x86_64)
Posted: Sat Feb 06, 2021 1:20 am
Consider this snippet from my linker script for ia32:
This produces the following program headers in my ELF file:
Now I want to include the program headers in my image so that user space can walk them (needed by TLS code). I change the PHDRS section as follow:
This produces the following in my ELF file:
This works as expected and I am happy with it.
My problem is on x86_64 where I basically use identical linker scripts (except for OUTPUT_FORMAT and OUTPUT_ARCH). The result of applying the changes doesn't result in the same behaviour.
Without PT_PHDR:
With PT_PHDR:
Notice how the VMA of the PHDR header is not at 0x10040 as I would expect it to be. What's more, the size of the first LOAD segment increased by roughly 0x10000. It looks like the linker wasn't smart enough to put the PHDR at VMA 0x10000 and instead added 0x10000 bytes of padding between the PHDR info and the start of my code (.text).
If I don't set the program counter at all and base my image at 0, it works the same in both cases (ia32 and x86_64). This results in an image based at 0. But I'd like my image to start at 0x10000 as I don't want to be relocating executables.
Anyone has ideas here and/or a different way to approach this?
Code: Select all
PHDRS
{
phdr_text PT_LOAD FLAGS(5); /* read + execute */
phdr_rodata PT_LOAD FLAGS(4); /* read */
phdr_data PT_LOAD FLAGS(6); /* read + write */
phdr_tls PT_TLS FLAGS(4);
}
SECTIONS
{
. = 0x00010000;
.text :
{
*(.text*)
} :phdr_text
...
Code: Select all
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x00010000 0x00010000 0x3ee02 0x3ee02 R E 0x1000
LOAD 0x040000 0x0004f000 0x0004f000 0x12ebf 0x12ebf R 0x1000
LOAD 0x053000 0x00062000 0x00062000 0x00258 0x00898 RW 0x1000
TLS 0x053258 0x00063000 0x00063000 0x00000 0x00060 R 0x4
Code: Select all
PHDRS
{
phdr_phdrs PT_PHDR PHDRS;
phdr_text PT_LOAD PHDRS FLAGS(5); /* read + execute */
phdr_rodata PT_LOAD FLAGS(4); /* read */
phdr_data PT_LOAD FLAGS(6); /* read + write */
phdr_tls PT_TLS FLAGS(4);
}
SECTIONS
{
. = 0x00010000 + SIZEOF_HEADERS;
.text :
{
*(.text*)
} :phdr_text
Code: Select all
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00010034 0x00010034 0x000a0 0x000a0 R 0x4
LOAD 0x000034 0x00010034 0x00010034 0x3eeae 0x3eeae R E 0x1000
LOAD 0x03f000 0x0004f000 0x0004f000 0x12ebf 0x12ebf R 0x1000
LOAD 0x052000 0x00062000 0x00062000 0x00258 0x00898 RW 0x1000
TLS 0x052258 0x00063000 0x00063000 0x00000 0x00060 R 0x4
My problem is on x86_64 where I basically use identical linker scripts (except for OUTPUT_FORMAT and OUTPUT_ARCH). The result of applying the changes doesn't result in the same behaviour.
Without PT_PHDR:
Code: Select all
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align
LOAD 0x0000000000010000 0x0000000000010000 0x0000000000010000 0x0000000000046782 0x0000000000046782 R E 0x200000
LOAD 0x0000000000057000 0x0000000000057000 0x0000000000057000 0x000000000001c69b 0x000000000001c69b R 0x200000
LOAD 0x0000000000074000 0x0000000000074000 0x0000000000074000 0x0000000000000308 0x0000000000000ee0 RW 0x200000
TLS 0x0000000000074308 0x0000000000075000 0x0000000000075000 0x0000000000000000 0x00000000000000b0 R 0x8
Code: Select all
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040 0x0000000000000118 0x0000000000000118 R 0x8
LOAD 0x0000000000000040 0x0000000000000040 0x0000000000000040 0x00000000000568a2 0x00000000000568a2 R E 0x200000
LOAD 0x0000000000057000 0x0000000000057000 0x0000000000057000 0x000000000001c69b 0x000000000001c69b R 0x200000
LOAD 0x0000000000074000 0x0000000000074000 0x0000000000074000 0x0000000000000308 0x0000000000000ee0 RW 0x200000
TLS 0x0000000000074308 0x0000000000075000 0x0000000000075000 0x0000000000000000 0x00000000000000b0 R 0x8
If I don't set the program counter at all and base my image at 0, it works the same in both cases (ia32 and x86_64). This results in an image based at 0. But I'd like my image to start at 0x10000 as I don't want to be relocating executables.
Anyone has ideas here and/or a different way to approach this?