Page 1 of 1

C++ Constructors missing from .ctors section

Posted: Sun Dec 06, 2015 7:21 am
by zhiayang
I'm back after a long hiatus, and after going through the rusty dusty gross codebase that I call a kernel, I realised that constructors... were never being called properly. The linker was somehow throwing away the .ctors and .dtors sections. To fix this, I put them in their own section in the output script, as such:

Code: Select all

ENTRY(KernelBootstrap)
OUTPUT_FORMAT(elf64-x86-64)

SECTIONS
{
	Kernel_VMA = 0xFFFFFFFF80000000;
	. = Kernel_VMA;

	.text ALIGN(0x1000) : AT(ADDR(.text) - Kernel_VMA)
	{
		*(.text*)
		*(.gnu.linkonce.t*)
	}

	.rodata ALIGN(0x1000) : AT(ADDR(.rodata) - Kernel_VMA)
	{
		*(.rodata*)
		*(.gnu.linkonce.r*)
	}

	.stuff ALIGN(0x1000) : AT(ADDR(.stuff) - Kernel_VMA)
	{
		start_ctors = .;
		*(SORT(.ctors))
		*(SORT(.ctors*))
		end_ctors = .;

		start_dtors = .;
		*(SORT(.dtors))
		end_dtors = .;
	}

	.data ALIGN(0x1000) : AT(ADDR(.data) - Kernel_VMA)
	{
		*(.data*)
		*(.gnu.linkonce.d*)
	}

	.bss ALIGN(0x1000) : AT(ADDR(.bss) - Kernel_VMA)
	{
		StartBSS = .;
		*(COMMON)
		*(.bss*)
		*(.gnu.linkonce.b*)
		EndBSS = .;
	}

	KernelEnd = .;
}

I can thus confirm that

Code: Select all

.stuff
is a section that appears in the final ELF file:

Code: Select all

$ ~/D/S/O/mx> tools/od -s -j .stuff build/kernel64.elf

build/kernel64.elf:     file format elf64-x86-64

Contents of section .stuff:
 ffffffff80042000 20170080 ffffffff 802d0080 ffffffff   ........-......
 ffffffff80042010 a0da0080 ffffffff 00140180 ffffffff  ................
 ffffffff80042020 408a0180 ffffffff b0aa0180 ffffffff  @...............
 ffffffff80042030 40c40180 ffffffff 20e20180 ffffffff  @....... .......
 ffffffff80042040 40fa0180 ffffffff f0230280 ffffffff  @........#......
$ ~/D/S/O/mx> 
It contains a nice list of 10 pointers to functions. Unfortunately, after using addr2line, I realised all 10 functions were coming from this, somehow:

Code: Select all


// Holy ...
template<typename TKey, typename TValue,
class THashFunc,
int TLoadFactor4,
class TKeyEqualFunc,
class TAllocator>

typename hash_map<TKey, TValue, THashFunc, TLoadFactor4, TKeyEqualFunc, TAllocator>::node hash_map<TKey, TValue, THashFunc, TLoadFactor4, TKeyEqualFunc, TAllocator>::ms_emptyNode;
I use rdestl for some of its datastructures. This is all fine, except of course other constructors never appear in the list. A quick example I put in a cpp file somewhere like this:

Code: Select all


struct Foo
{
	Foo()
	{
		HALT("");
	}

	int bar = 40;
};

struct Bar
{
	Foo f;
};

static Bar g;
never creates any constructors in the list. Here's how I'm calling the constructors (it shouldn't matter since the binary file shows only 10 entries, but just in case):

Code: Select all


uint64_t* ctorListBegin = &start_ctors;
uint64_t* ctorListEnd = &end_ctors;

Log("Calling Constructors:");

size_t i = 0;
while(ctorListBegin != ctorListEnd)
{
	Log("%d: %p", i, *ctorListBegin);
	void (*fn)() = (void(*)()) (*ctorListBegin);
	fn();

	ctorListBegin++, i++;
}

Thanks.

Re: C++ Constructors missing from .ctors section

Posted: Sun Dec 06, 2015 11:58 pm
by sebihepp
structs don't have constructors. That's why you can't put classes without a standard constructor inside a struct. You just define a struct Foo with a method Foo inside.

Re: C++ Constructors missing from .ctors section

Posted: Mon Dec 07, 2015 12:31 am
by zhiayang
sebihepp wrote:structs don't have constructors. That's why you can't put classes without a standard constructor inside a struct. You just define a struct Foo with a method Foo inside.
Correct me if I'm wrong, but it was my understanding that struct and class are simply aliases of each other in C++, with the only difference being default visibility. I think what you're describing (not being able to put a class without a "standard constructor" in a struct) is due to the default constructor being private or protected, not because structs cannot have constructors.

Either way, I changed the test code to something like this, replacing struct with class:

Code: Select all



class Foo
{
	public:

	Foo()
	{
		HALT("");
	}
	int bar = 40;
};

class Bar
{
	public:
	Foo f;
};

static Bar g;
The issue persists, so that isn't the problem. Thanks for responding anyway!




EDIT: I did a little rephrasing of my google-search terms, and I found this:
http://stackoverflow.com/questions/6343 ... ay-section

Turns out I was passing --gc-sections to the linker. Naturally I never thought it would remove actual code, and it never occurred to me to check it. Still, it's fixed now. Maybe some other person will find this topic in the future.

Re: C++ Constructors missing from .ctors section

Posted: Mon Dec 07, 2015 3:10 am
by Candy
Correct me if I'm wrong, but it was my understanding that struct and class are simply aliases of each other in C++, with the only difference being default visibility.
100% correct. Note that default visibility is both members and inheritance.

Your issue is that nothing else refers to that object file, so it is not being pulled in at all. You get that behaviour either by having the object in a static library (and it being omitted) or by having --gc-sections, and having all other sections of that file being GCed.

Use that knowledge to your advantage - don't have globals that are otherwise unreferenced (as they will be optimized out), but do have functions holding globals that are used elsewhere, as those will cause the globals to be there. Also, function-static objects get better initialization priority, as they are initialized on first call & scheduled for destruction, with appropriate multithread locking.