Linker Script Madness

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
hubris
Member
Member
Posts: 28
Joined: Sun May 24, 2015 12:38 am
Location: Brisbane, Australia

Linker Script Madness

Post by hubris »

I am working on the transition to paging and virtual memory. After reading so many articles it seems this should be relatively straight forward. The current issue I have is with the linker script

Code: Select all

SECTIONS
{
	/* The kernel will live at 3GB + 4MB in the virtual address space, which will be mapped 
	 * to 4MB in the physical address space. 
	 */
	. = 0xC0400000;

	_kernel_head_address	=	.;
		
    .text ALIGN ( 0x1000 )	: AT ( ADDR ( .text ) - 0xC0000000 )
    {
        *(.text) *(.rdata*) *(.rodata)
    }

    .data ALIGN ( 0x1000 )	: AT ( ADDR ( .data ) - 0xC0000000 )
    {
        *(.data)
    }

    .bss ALIGN ( 0x1000 )	: AT ( ADDR ( .bss ) - 0xC0000000 )
    {
        *(.bss)
    }

	_kernel_tail_address	=	.;
	    
	_kernel_size	=	_kernel_tail_address - _kernel_head_address;

}
Two things the symbols _kernel_head_address, _lernel_tail_address and _kernel_size are all allocated an address inside the .bss section and this does not reflect what I was expecting which was the ability to calculate the kernel's usage of memory. Because the symbols are all addressed within the .bss the only calculation I have (which is the same as the _kernel_size) results in 4 bytes which is the difference between the addresses.

What is going on here?

The second issue is that I expected the linker to create a series of sections with VMA differing from the LMA by 0xC0000000 however when I use objdump I get this result.

Code: Select all

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         000012d4  c0400000  c0400000  00000400  2**9
                  CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA
  1 .text.st      00000014  c0402000  c0402000  00001800  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .init         00000024  c0403000  c0403000  00001a00  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .fini         00000024  c0404000  c0404000  00001c00  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .ctors        00000004  c0405000  c0405000  00001e00  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  5 .data         00000004  c0406000  c0406000  00002000  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  6 .bss          0000001c  c0407000  c0407000  00000000  2**2
                  ALLOC
Which again is completely different to what I expected. I can only assume I am missing some switch on the linker or doing something fundamentally wrong, but after staring at this for the last 3 days I just cannot see where or what I am doing incorrectly.

Anyone able to point out my misunderstanding?
Hellbender
Member
Member
Posts: 63
Joined: Fri May 01, 2015 2:23 am
Libera.chat IRC: Hellbender

Re: Linker Script Madness

Post by Hellbender »

hubris wrote: What is going on here?
Add "-Wl,-Map,output.map" to you GCC line and inspect the generated output.map.
It shows what logic was used to assign values to symbols, and where each piece of code/data ended up. At least I was able to debug my linker script problems with that.

Edit: If I had to guess, you have _kernel_head_address etc. in a .c file as "uintptr_t _kernel_head_address;" etc. without "extern", so those actually end up as memory locations in the .bss section.
Hellbender OS at github.
hubris
Member
Member
Posts: 28
Joined: Sun May 24, 2015 12:38 am
Location: Brisbane, Australia

Re: Linker Script Madness

Post by hubris »

I am an idiot.

This is what I had.

Code: Select all

extern "C"
{
ui32_v		kernel_head_address;
ui32_v		kernel_tail_address;
ui32_v		kernel_size;
}
And this is what I should have had. So obvious really and yet with a little brain fade it was hiding in front of my eyes.

Code: Select all

extern "C"
{
extern	ui32_v		kernel_head_address;
extern	ui32_v		kernel_tail_address;
extern	ui32_v		kernel_size;
}
Thank you for the pointer.
hubris
Member
Member
Posts: 28
Joined: Sun May 24, 2015 12:38 am
Location: Brisbane, Australia

Re: Linker Script Madness

Post by hubris »

And so the saga continues. After the guide towards the map file, this was interesting because it shows that somewhere the correct load address is being calculated because it shows up in the map file, see below. It seems that perhaps the objdump facility is not
correctly displaying the LMA as it is always the same as the VMA (I find this hard to credit but have no alternate theory at this point).

So a linker fragment

Code: Select all

SECTIONS
{
	/* The kernel will live at 3GB + 4MB in the virtual address space, which will be mapped 
	 * to 4MB in the physical address space. 
	 */
	. = 0xC0400000;
	
    .text ALIGN ( 0x1000 ) : AT ( ADDR ( .text ) - 0xC0000000 )
    {
         *(.text) *(.rdata*) *(.rodata)
    }

When I run objdump I get the following

Code: Select all

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         000012f4  c0400000  c0400000  00000400  2**9
                  CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA
  1 .text.st      00000014  c0402000  c0402000  00001800  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .init         00000024  c0403000  c0403000  00001a00  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
Which shows that the .text section VMA and LMA are the same, and the LMA is not what was expected. Then looking indise the map file I find the following lines

Code: Select all

.text           0xc0400000     0x1400 load address 0x00400000
 *(.text)
 .text          0xc0400000      0x305 runner.obj
Which indicates the the LMA is correctly calculated within the linker. Finally leading on to the next question about the PE format.

Each Section has a virtual address and a raw address (file) but does not have a LMA anywhere I can see. There is an image base which is 0x00400000 which is as I expected as this would be the LMA base address. So how is the LMA found? Do I just use the VMA offset as the offset from the LMA base address? Now I am confusing myself because if the VMA is an offset where is the virtual base address stored.

Any pointers to a method to calculate the VMA and LMA for a given section with a PE format file?
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Linker Script Madness

Post by Combuster »

Essentially, in PE there's no dedicated LMA and instead you get LMA=VMA as the LMA information is lost and objdump can't provide you with anything more accurate than that.

The confusion is what you get for using a gnu-unfriendly executable file format. PE is not incapable of housing binary code with LMA!=VMA, but it will be invisible on the outset and it might be impossible to convince ld to even make such a contraption. Have fun dealing with it ;)
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
hubris
Member
Member
Posts: 28
Joined: Sun May 24, 2015 12:38 am
Location: Brisbane, Australia

Re: Linker Script Madness

Post by hubris »

Yeah most of this is because I could not get a cross compiler build to work for either gcc or clang and I know I will probably have to move to linux to continue this. The more I read your signature line the more convinced that I have chosen the correct username. A little more thrashing about on my part to see if there is any escape.
Post Reply