Page 1 of 2
Linking messes up C++ constructors
Posted: Wed Jun 30, 2010 10:14 am
by Neolander
I just don't understand this one.
My C++ kernel, like almost any C++ program, includes some constructors, which are put int the .ctors section and must be run before operation. When compiling the source file where the constructors are stored, the .ctors section is present in the object file and includes a valid address : everything is fine.
Code: Select all
Contents of section .ctors:
0000 00000000 00000000 ........
Now I link the kernel binary, using this linker script :
Code: Select all
ENTRY(kinit)
PHDRS {
headers PT_PHDR PHDRS;
rx PT_LOAD FLAGS(5);
r PT_LOAD FLAGS(4);
rw PT_LOAD FLAGS(6);
}
SECTIONS {
. = 0x1001000; /* Loaded at 16MB+4KB in order to prevent erasing GRUB-provided modules */
.text ALIGN(4096) :
{
*(.text*)
*(.gnu.linkonce.t*)
} : rx
.rodata ALIGN(4096) :
{
start_ctors = .;
*(.ctor*)
end_ctors = .;
start_dtors = .;
*(.dtor*)
end_dtors = .;
*(.rodata*)
*(.gnu.linkonce.r*)
} : r
.data ALIGN(4096) :
{
*(.data*)
*(.gnu.linkonce.d*)
} : rw
.bss ALIGN(4096) :
{
*(.COMMON*)
*(.bss*)
*(.gnu.linkonce.b*)
} : rw
/DISCARD/ :
{
*(.comment)
*(.eh_frame) /* We're not implementing runtime support for C++ exceptions. */
}
}
You'll probably notice the start_ctors and end_ctors symbols. Those are used by the assembly snippet which runs before the kernel. They should contain the beginning and the end of the area where pointers to the constructors are stored.
When I look at said symbols, their value sounds fine :
Code: Select all
0000000001002000 g .rodata 0000000000000000 start_ctors
0000000001002008 g .rodata 0000000000000000 end_ctors
Indeed, there's one pointer at the beginning of the .rodata section, if linking went well. Now, let's see what's actually happening in the .rodata section of the final binary...
Code: Select all
Contents of section .rodata:
1002000 2c130001 00000000 00800b00 00000000 ,...............
1002010 0e .
What ??? Where did the pointer go ? What's with this unrealistically high 0x2c13000100000000 address ?
Can someone help me understand what went wrong there ?
Re: Linking messes up C++ constructors
Posted: Wed Jun 30, 2010 12:12 pm
by Firestryke31
Are you sure it's 0x0x2c13000100000000 (big endian) and not 0x000000000100132c (little endian)?
Re: Linking messes up C++ constructors
Posted: Wed Jun 30, 2010 1:30 pm
by Neolander
Firestryke31 wrote:Are you sure it's 0x0x2c13000100000000 (big endian) and not 0x000000000100132c (little endian)?
That would mean that x86_64-elf-objdump displays data in big endian and addresses on the left in little endian. Wouldn't that be a somewhat inconsistent way of operating ?
Re: Linking messes up C++ constructors
Posted: Wed Jun 30, 2010 2:22 pm
by Owen
Neolander wrote:Firestryke31 wrote:Are you sure it's 0x0x2c13000100000000 (big endian) and not 0x000000000100132c (little endian)?
That would mean that x86_64-elf-objdump displays data in big endian and addresses on the left in little endian. Wouldn't that be a somewhat inconsistent way of operating ?
The data is displayed bytewise. It wouldn't.
Re: Linking messes up C++ constructors
Posted: Wed Jun 30, 2010 3:02 pm
by Creature
As a side note, I see this piece of text in your linker script:
Code: Select all
. = 0x1001000; /* Loaded at 16MB+4KB in order to prevent erasing GRUB-provided modules */
Are you sure that GRUB places them at 1 MB? I thought that GRUB always placed its modules after your kernel (and you should read the values from the multiboot structure in order to retrieve how many there are and thus, how many memory to skip before trampling them).
Re: Linking messes up C++ constructors
Posted: Wed Jun 30, 2010 3:30 pm
by Neolander
You're right guys, it works in fact at the linker level.
Creature wrote:As a side note, I see this piece of text in your linker script:
Code: Select all
. = 0x1001000; /* Loaded at 16MB+4KB in order to prevent erasing GRUB-provided modules */
Are you sure that GRUB places them at 1 MB? I thought that GRUB always placed its modules after your kernel (and you should read the values from the multiboot structure in order to retrieve how many there are and thus, how many memory to skip before trampling them).
Yes, but that would require a relocatable kernel. At the moment, my loader is fine with using a statically-defined absolute kernel address like this one
Re: Linking messes up C++ constructors
Posted: Wed Jun 30, 2010 4:16 pm
by Combuster
Neolander wrote:Yes, but that would require a relocatable kernel. At the moment, my loader is fine with using a statically-defined absolute kernel address like this one
That doesn't matter at all. GRUB does not overwrite your kernel binary. You, on the other hand, might be overwriting memory grub used to put modules in.
Re: Linking messes up C++ constructors
Posted: Wed Jun 30, 2010 4:42 pm
by Neolander
Combuster wrote:That doesn't matter at all. GRUB does not overwrite your kernel binary. You, on the other hand, might be overwriting memory grub used to put modules in.
That's what the 16MB margin is here for : grub loads the bootstrap 32-bit part, which it considers as the kernel, at 1MB, and then it loads modules above it. Somewhere up above, at 16MB, the bootstrap kernel loads the real kernel. Theoretically, this shouldn't fail, as long as page-aligned bs kernel size + page-aligned modules size < 15 MB
Re: Linking messes up C++ constructors
Posted: Wed Jun 30, 2010 5:03 pm
by Combuster
Multiboot doesn't specify where modules are loaded, only that you can dictate a single range (kernel image + x bytes) that should be left free for use. If you let your loader relocate, you might end up in module space.
What's wrong with just loading the kernel proper to 1MB directly? It works for a reason.
Re: Linking messes up C++ constructors
Posted: Wed Jun 30, 2010 6:14 pm
by gerryg400
Yes, but that would require a relocatable kernel. At the moment, my loader is fine with using a statically-defined absolute kernel address like this one
Actually you don't need a relocatable kernel. I'm not sure if you're aiming for a -2GB kernel or not, but what you should do is, as Creature said, examine the Grub tables and find some empty memory to load you kernel into. Then use paging to move that memory to the address the kernel expects to execute at. In fact (I think??) this is the only way to load a -2GB kernel (or higher half 64bit kernel for that matter) using a 32bit loader.
Re: Linking messes up C++ constructors
Posted: Wed Jun 30, 2010 11:53 pm
by Neolander
Combuster wrote:Multiboot doesn't specify where modules are loaded, only that you can dictate a single range (kernel image + x bytes) that should be left free for use. If you let your loader relocate, you might end up in module space.
Yet another magnificently vague part in the multiboot standard...
Then I will indeed have to find another solution
What's wrong with just loading the kernel proper to 1MB directly? It works for a reason.
I need a bootstrap part because
1/This is where I put all the bootloader-dependent code (I don't want to give up on easy portability that early).
2/Among other things, it switches to long mode and handles ELF64 loading, which is needed before the real kernel can operate
I could avoid 2/, but that would require using GRUB 2 (eeek !) because GRUB legacy cannot load ELF64 binaries. I prefer to stick with GRUB legacy until GRUB 2 (and documentation about it !) has matured. 1/ is more of a design decision.
Re: Linking messes up C++ constructors
Posted: Thu Jul 01, 2010 12:03 am
by Neolander
gerryg400 wrote:Actually you don't need a relocatable kernel. I'm not sure if you're aiming for a -2GB kernel or not, but what you should do is, as Creature said, examine the Grub tables and find some empty memory to load you kernel into. Then use paging to move that memory to the address the kernel expects to execute at. In fact (I think??) this is the only way to load a -2GB kernel (or higher half 64bit kernel for that matter) using a 32bit loader.
Yes, *that* could be done. It would remove one of those nasty hard-coded addresses in my code, which is a Good Thing, and make higher half kernel more doable, at the expense of making the loading job a bit more complicated and introducing a new feature to test in the bootstrap part (non-identity mapped paging).
Going to try that. It looks fun
Re: Linking messes up C++ constructors
Posted: Thu Jul 01, 2010 12:05 am
by gerryg400
Multiboot doesn't specify where modules are loaded,
Combuster meant that you can't tell Grub where to put the modules. BUT, Grub will tell you where it put them.
Re: Linking messes up C++ constructors
Posted: Thu Jul 01, 2010 12:08 am
by Neolander
gerryg400 wrote:Multiboot doesn't specify where modules are loaded,
Combuster meant that you can't tell Grub where to put the modules. BUT, Grub will tell you where it put them.
Yup, I know, I already have a big memory map where this information is stored somewhere...
Re: Linking messes up C++ constructors
Posted: Thu Jul 01, 2010 12:32 am
by Combuster
Neolander wrote:Combuster wrote:Multiboot doesn't specify where modules are loaded, only that you can dictate a single range (kernel image + x bytes) that should be left free for use. If you let your loader relocate, you might end up in module space.
Yet another magnificently vague part in the multiboot standard...
It's called the BSS section. It's not vague, you only need to know how to use it to your advantage.