OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 4:43 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 4 posts ] 
Author Message
 Post subject: Linker script and PT_PHDR not working as expected (x86_64)
PostPosted: Sat Feb 06, 2021 1:20 am 
Offline
Member
Member

Joined: Mon Feb 02, 2015 7:11 pm
Posts: 898
Consider this snippet from my linker script for ia32:
Code:
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
...

This produces the following program headers in my ELF file:
Code:
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

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:
Code:
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

This produces the following in my ELF file:
Code:
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

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:
Code:
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

With PT_PHDR:
Code:
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


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?

_________________
https://github.com/kiznit/rainbow-os


Top
 Profile  
 
 Post subject: Re: Linker script and PT_PHDR not working as expected (x86_6
PostPosted: Sat Feb 06, 2021 3:34 am 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1593
I had the same problem with the bloody linker, until I added "-z max-page-size=0x1000" to the linker command line. (That is, "-Wl,-z,max-page-size=0x1000" for GCC). This is because the linker somehow starts using 2MB pages, but fails to recognize that 4kB pages would fit the linker script better. No clue how it does realize this in 32-bit mode. Anyway, fixing the page size really helped.

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: Linker script and PT_PHDR not working as expected (x86_6
PostPosted: Sat Feb 06, 2021 10:00 am 
Offline
Member
Member

Joined: Tue Aug 11, 2020 12:14 pm
Posts: 151
It's a reasonable assumption on the linker's part that if you're generating a 32-bit binary, there's no guarantee your target environment will support anything more than 4K pages. The 32-bit linear space isn't huge, so you don't want to waste it putting a 10-20-30k section in a multi-MB block.

However, 64-bit always supports 2MB pages, so it's safe in assuming that.


Top
 Profile  
 
 Post subject: Re: Linker script and PT_PHDR not working as expected (x86_6
PostPosted: Sat Feb 06, 2021 11:58 am 
Offline
Member
Member

Joined: Mon Feb 02, 2015 7:11 pm
Posts: 898
nullplan wrote:
I had the same problem with the bloody linker, until I added "-z max-page-size=0x1000" to the linker command line. (That is, "-Wl,-z,max-page-size=0x1000" for GCC). This is because the linker somehow starts using 2MB pages, but fails to recognize that 4kB pages would fit the linker script better. No clue how it does realize this in 32-bit mode. Anyway, fixing the page size really helped.

OMG. It did cross my mind at some point last night that I might need this flag, but I was too tired and went to bed. I completely forgot about it until I saw your comment today.

It did indeed fix my problem. Thanks so much nullplan.

_________________
https://github.com/kiznit/rainbow-os


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 4 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 27 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group