OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 8:06 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 6 posts ] 
Author Message
 Post subject: [Solved] GNU LD section attributes for 64-bit kernel ELF
PostPosted: Tue Feb 21, 2023 3:21 am 
Offline

Joined: Tue Jan 05, 2016 9:10 am
Posts: 12
I am trying to link a 64-bit kernel ELF using GNU LD. I have a executable section named `lowerhalf` and then the other usual sections. The linker script I use is this
Code:
ENTRY(kernelCompatibilityModeStart);

SECTIONS {
   . = 0x80000000; /* lowerhalf origin */
   .lowerhalf : ALIGN(0x1000) {
      *(.lowerhalf);
   }

   . = 0xffffffff80000000; /* higherhalf origin */
   .GDT64 : ALIGN(0x1000) {
      *(.GDT64);
   }
   .IDT64 : ALIGN(0x1000) {
      *(.IDT64);
   }
   .TSS64 : ALIGN(0x1000) {
      *(.TSS64);
   }
   .KERNELSTACK : ALIGN(0x1000) {
      *(.KERNELSTACK);
   }
   .ISTs : ALIGN(0x1000) {
      *(.ISTs);
   }
   .text : ALIGN(0x1000) {
      *(.text);
   }
   .data : ALIGN(0x1000) {
      *(.data);
   }
   .rodata : ALIGN(0x1000) {
      *(.rodata*);
   }
   .bss : ALIGN(0x1000) {
      *(COMMON);
      *(.bss);
   }
}


When I look at the generated segments using `readelf` I see something like
Code:
Elf file type is EXEC (Executable file)
Entry point 0x80000000
There are 3 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000001000 0x0000000080000000 0x0000000080000000
                 0x0000000000000030 0x0000000000000030  R      0x1000
  LOAD           0x0000000000002000 0xffffffff80000000 0xffffffff80000000
                 0x0000000000026718 0x0000000000026718  R E    0x1000
  LOAD           0x0000000000029000 0xffffffff80027000 0xffffffff80027000
                 0x0000000000004954 0x00000000000053d8  RW     0x1000

Section to Segment mapping:
  Segment Sections...
   00     .lowerhalf
   01     .GDT64 .IDT64 .TSS64 .KERNELSTACK .ISTs .text
   02     .data .ctors .rodata .eh_frame .bss

Some problems with the output ELF:
1. the `.text` section gets bundled up with other sections like `.GDT64` and `.KERNELSTACK`
2. `.lowerhalf` should have `RE` attributes instead of just `R`
3. all `.data`, `.ctors`, and `.rodata` end up with the `RW` flags.

I tried using the `MEMORY` directive but that results in a load of linker errors or warnings and doesn't give the desired output.

I'd like for the sections to have appropriate `RWE` attributes so I can create my paging entries accordingly.
How do I add the correct attributes?


Last edited by startrail on Tue Feb 21, 2023 5:37 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: GNU LD section attributes and flags for 64-bit kernel EL
PostPosted: Tue Feb 21, 2023 4:35 am 
Offline
Member
Member

Joined: Fri Aug 26, 2016 1:41 pm
Posts: 671
Was there a Windows tools chain involved in this at some point?

When you defined .GDT64 .IDT64 .TSS64 .KERNELSTACK .ISTs sections did you specify what kind of attributes you wanted on the sections in the assembly code? Sounds like you didn't specify any and it defaulted to attributes associated with the .text section.


Top
 Profile  
 
 Post subject: Re: GNU LD section attributes and flags for 64-bit kernel EL
PostPosted: Tue Feb 21, 2023 8:35 am 
Offline

Joined: Tue Jan 05, 2016 9:10 am
Posts: 12
No, there's no Windows or MinGW toolchain involved. Cross-compiling on Ubuntu 22.04 with GCC 12.1.
I didn't know you could specify section attributes in the ASM file itself. I'm using NASM for my assembly files.
Is there any doc or example you could point me to?

Edit: Looks like NASM 8.9.2 ELF extensions to the SECTION Directive (https://www.nasm.us/doc/nasmdoc8.html#section-8.9.2) may be relevant here. I'll look into it.


Top
 Profile  
 
 Post subject: Re: GNU LD section attributes and flags for 64-bit kernel EL
PostPosted: Tue Feb 21, 2023 10:47 am 
Offline
Member
Member

Joined: Tue Feb 18, 2020 3:29 pm
Posts: 1071
When you declare .lowerhalf in your NASM file, do
Code:
section .lowerhalf progbits alloc exec nowrite align=4096

And do the same for other sections, just change the attributes as needed.

You said that several sections got "bundled". Technically, they are in different sections (you would see that if read the section headers with readelf), but they all got placed in one segment as they have the same attributes and are contiguous. TBH, I wouldn't try to change that, unless you have a specific reason why you want them in separate segments.

_________________
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg


Top
 Profile  
 
 Post subject: Re: GNU LD section attributes and flags for 64-bit kernel EL
PostPosted: Tue Feb 21, 2023 12:01 pm 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1593
startrail wrote:
3. all `.data`, `.ctors`, and `.rodata` end up with the `RW` flags.

Well, duh. You are telling the linker to place .rodata after .data, and .ctors is not mentioned at all, meaning the linker just has to link it anywhere. My guess is you never thought about what sections needed to be writable. That would also explain the excessive 4k alignment all over the place.

For reference, here's my linker script again:
Code:
ENTRY(_kstart)
OUTPUT_FORMAT("elf64-x86-64")

PHDRS {
    headers PT_PHDR PHDRS;
    text PT_LOAD FILEHDR PHDRS;
    data PT_LOAD;
}

SECTIONS {
    . = 0xffffffff80000000 + SIZEOF_HEADERS;
    .text : {
        *(.text)
        *(.text.*)
    } :text
    .rodata : {
        *(.rodata)
        *(.rodata.*)
    }

    .eh_frame_hdr : {
        __eh_frame_hdr = .;
        *(.eh_frame_hdr)
    }
    .eh_frame : {
        *(.eh_frame)
        *(.eh_frame.*)
    }

    /* Normally, the overlap between text and data section is handled by having
     * two different pages for the last bits of text and the first bits of data.
     * That way, if the last bits of text are overwritten, it won't affect the
     * text that is actually used. Unfortunately, for the kernel this is not
     * possible. The whole file is loaded into memory en bloc, so the same page
     * would be mapped twice. Therefore, a write access to the writable page
     * would end up being visible in the non-writable side of things. Therefore,
     * we must actually page-align here.
     */
    . = ALIGN(2M);
    .data : {
        *(.data)
        *(.data.*)
    } :data
    .bss : {
        *(.bss)
        *(COMMON)
        *(.bss.*)
    }
}
Note that I don't have a lower half. I get the paging set up by the bootloader. Where the bootloader does not provide for that, I add a shim that does it for me. This allows me to completely uncouple the unpaged and paged code.

Alignment directives should never be necessary in the linker script because you already have section alignment in all the input sections. And even there, page alignment is rarely necessary. I only use one single alignment directive to cause a page break between the writable and non.writable sections. And I could normally do that well enough with just ". += 2M" were it not for the reasoning outlined in the linker script.

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: GNU LD section attributes and flags for 64-bit kernel EL
PostPosted: Tue Feb 21, 2023 5:36 pm 
Offline

Joined: Tue Jan 05, 2016 9:10 am
Posts: 12
nullplan's linker script definitely helped a lot
Quote:
My guess is you never thought about what sections needed to be writable.

Well yes, that linker script was written 8 years ago when I thought it was cool to have as many segments as possible, see their names pop up in readelf output, and wanted to get to long mode as quickly as possible. I'm moving them to appropriate .data and .bss sections and adding NX bit to my paging entries. Hence the question.

Quote:
When you declare .lowerhalf in your NASM file, do
Code:
section .lowerhalf progbits alloc exec nowrite align=4096


Did that and got RE attributes as expected. Thanks MichaelPetch and nexos.

I use the lowerhalf so I can load a 64-bit GDT to get out of the compatibility mode setup by 32-bit stage2 loader. Now that I think about it, my stage2 loader already does all the paging and 'dummy' 64-bit GDT setup before jumping to kernelCompatibilityModeStart, which looks like may actually be in true long mode. I may not even need the lowerhalf stuff.

The linker script that works for now is
Code:
ENTRY(kernelCompatibilityModeStart)

PHDRS {
   lowerhalf PT_LOAD;
   text PT_LOAD;
   rodata PT_LOAD;
   data PT_LOAD;
}

SECTIONS {
   . = 0x80000000;
   .lowerhalf : {
      *(.lowerhalf)
   } : lowerhalf

   . = 0xffffffff80000000;
   .text : {
      *(.text)
      *(.text.*)
   } : text

   . = ALIGN(0x1000);
   .rodata : {
      *(.rodata)
      *(.rodata.*)
   } : rodata

   .eh_frame_hdr : {
      *(.eh_frame_hdr)
   }
   .eh_frame : {
      *(.eh_frame)
      *(.eh_frame.*)
   }

   . = ALIGN(0x1000);
   .data : {
      *(.data)
      *(.data.*)
   } : data
   .bss : {
      *(.bss)
      *(.bss.*)
      *(COMMON)
   }
}


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

All times are UTC - 6 hours


Who is online

Users browsing this forum: SemrushBot [Bot] and 62 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