Page 1 of 1

Strange virtual function call behaviour

Posted: Fri Dec 30, 2016 10:40 pm
by NGiddings
I really don't want to ask for help before I've even solved my previous issue, but I'm at a loss. If I'm missing something stupid, I apologize in advance. Anyway, here goes:

I was reworking the way text is streamed to the screen to move towards a simple console that can process events from the keyboard, and ended up with the following system:

- An EventListener class with a virtual function to process some arbitrary event
- An EventListenerKey, inheriting from EventListener, class to specifically handle key press/release events

- A Stream class (just virtual << operator overloads)
- A Window class inheriting from Stream to write text to a portion of the screen. A Display class contains all windows on screen.

- A Console class that inherits from both Window and EventListenerKey

EventListenerKeys are passed to the keyboard driver. It queues up events during interrupt processing, and passes them out to each event listener in the meantime. The Console instance is one of those listeners.

I'm not quite happy with this design, but that isn't the issue right now. The problem is that I can't call any functions of my Console object. QEMU just crashes with "Trying to execute code outside RAM or ROM". I have an instance of Display that contains a Window object, error, and a Console object, console. While the constructor of Display is running, the console object behaves normally. Afterwards, however, the emulator crashes when I try to call member functions. Even with no steps between the constructor returning and calling a member function of the console, I get the same results. I've been staring at my code for hours and I just can't figure out what I'm doing wrong.

What I know so far is that duing the Display constructor call, the display object (and therefore the windows) are allocated on the stack. Afterwards they are somewhere below the stack. That makes sense; the new object is copied off the stack and into System::display. However, is there something about that causing my problem? Since that is pretty much the only thing happening when the console stops working, that's my only guess.

Here's the relevant code:

System::initialize is what creates the Display object

Code: Select all

Display System::display;

void System::initialize()
{	
	display = Display();
	
	display.printError((uint32_t) &display);
	display.printError("\n");
	
	display.initialize();
	
	display.print("hi\n");
	panic();
	
	KeyboardTranslator::initialize();
	Keyboard::initialize();
	InterruptController::init(0x100000);
	
	display.printError("Finished init.\n");
	panic();
}
Relevant parts of Display

Code: Select all

Display::Display()
{
	clear();
	console = Console(0, 1, 40, 24);
	error = Window(40, 1, 40, 24);
	
	printError((uint32_t) this);
	printError("\n");
	
	console << (uint32_t) &console;
}

void Display::initialize()
{
	error << (uint32_t) &console;
	//Keyboard::addListener(&console);
}
Thanks in advance to anyone willing to help me out.

Re: Strange virtual function call behaviour

Posted: Fri Dec 30, 2016 10:57 pm
by alexfru
Before you start digging the ++ part of C++, can you make sure that you can call C functions indirectly, by pointer?

Re: Strange virtual function call behaviour

Posted: Fri Dec 30, 2016 11:07 pm
by NGiddings
alexfru wrote:Before you start digging the ++ part of C++, can you make sure that you can call C functions indirectly, by pointer?
I tried that; it works fine. In fact, everything BUT any member function of Console works as one would expect.

Re: Strange virtual function call behaviour

Posted: Fri Dec 30, 2016 11:48 pm
by dchapiesky
[quote="NGiddings"]

Code: Select all


display = Display();
display.printError("\n");
display.initialize();

Display::Display()
{
	printError("\n");
}
Presuming that printError() is a virtual function of the Display class (and noting that display.initialize() is called after the printError() of the constructor) then most likely the problem is calling a virtual method from a constructor...

http://www.artima.com/cppsource/nevercall.html

http://www.stroustrup.com/bs_faq2.html#vcall

It would help to see the class definitions....

Re: Strange virtual function call behaviour

Posted: Sat Dec 31, 2016 12:11 am
by NGiddings
I can't find any code where a constructor calls a virtual function from its own class. I tried removing the virtual function calls from Display() anyway, but it didn't help. I'm really confused as to how EIP is ending up outside of memory. I can't find any place the stack would have gotten corrupted...

Here are the class definitions:

Code: Select all

class EventListener
{
public:

	EventListener();
	
	virtual void process();
			
private:
	
};

Code: Select all

class EventListenerKey: public EventListener
{
public:

	EventListenerKey();
	
	virtual void process(KeyboardEvent event);
			
private:
	
};

Code: Select all

class Stream
{
public:
	virtual void operator<<(char* data) = 0;
	virtual void operator<<(uint8_t data) = 0;
	virtual void operator<<(uint16_t data) = 0;
	virtual void operator<<(uint32_t data) = 0;
	virtual void operator<<(uint64_t data) = 0;
};

Code: Select all

class Window : public Stream
{
public:

	Window();
	
	Window(int x, int y, int width, int height);
	
	void operator<<(char* data);
	
	void operator<<(uint8_t data);
	
	void operator<<(uint16_t data);
	
	void operator<<(uint32_t data);
	
	void operator<<(uint64_t data);
	
	void putChar(char c);
	
	void clear();
	
protected:

	char* vga;

	int x, y;
	
	int width, height;

	int cursorX, cursorY;
	
	int coordToOffset(int x, int y);
	
	void printString(char* data);
	
	void newline();

};

Code: Select all

class Console: public Window, public EventListenerKey
{
public:

		Console();
		
		Console(int x, int y, int width, int height);
		
		virtual void process(KeyboardEvent event);
			
private:
	
	int bufferLength;
	
	char commandBuffer[256];
	
};

Re: Strange virtual function call behaviour

Posted: Sat Dec 31, 2016 12:46 am
by alexfru
NGiddings wrote:I'm really confused as to how EIP is ending up outside of memory. I can't find any place the stack would have gotten corrupted...
Onto the next step. What happens if you remove hardware-specific stuff from said classes, put some "std::cout <<"'s instead and compile and run your code as a regular C++ app?

As for how things can get wrong, yes, you may have some form of memory corruption. You may also have issues with your linker script and startup code. For example, there are additional code/data sections that contain some necessary stuff to support C++ (e.g. constructors and destructors for static objects). Are those missing from the binary? Are you processing those constructor sections? Those constructors won't be magically called on their own.

Re: Strange virtual function call behaviour

Posted: Sat Dec 31, 2016 1:48 am
by NGiddings
Good lord, I'm dumb. Yes, I forgot to call _init and _fini in my startup code. Everything works perfectly now.

However, why does this behavior occur? I get that GCC expects global constructors to be called before the program runs, but does anyone know what is going on internally that GCC is producing such unstable code?

Anyway, thanks for the help. I'm not sure how long I would have spent before I realized that.

Re: Strange virtual function call behaviour

Posted: Sat Dec 31, 2016 2:21 am
by alexfru
NGiddings wrote:Good lord, I'm dumb. Yes, I forgot to call _init and _fini in my startup code. Everything works perfectly now.

Anyway, thanks for the help. I'm not sure how long I would have spent before I realized that.
Cool.
NGiddings wrote:However, why does this behavior occur? I get that GCC expects global constructors to be called before the program runs, but does anyone know what is going on internally that GCC is producing such unstable code?
You need to see the compiler documentation, if there's any that describes this (I haven't seen it, but I haven't looked for it either as I never tried to write a kernel in C++, used plain C with portions in assembly). At any rate, I don't think you can call the generated code unstable without understanding the implementation or the reasons behind it. There may be more stuff to initialize than just calling constructors. It may be that some supporting data structures are best initialized at runtime (e.g. a small piece of code to initialize a potentially large portion of data memory).

Re: Strange virtual function call behaviour

Posted: Sat Dec 31, 2016 9:42 am
by dozniak
NGiddings wrote:I get that GCC expects global constructors to be called before the program runs, but does anyone know what is going on internally that GCC is producing such unstable code
It is specified in C++ standard 3.6.3(3).
C++ std wrote:It is implementation-defined whether the dynamic initialization of a non-local non-inline variable with static storage duration happens before the first statement of main.
It's up to the compiler how to perform such initialization and most, if not all, compilers use initializer sections, that are executed by the CRT.

Re: Strange virtual function call behaviour

Posted: Mon Jan 02, 2017 1:08 am
by Boris
It's not unstable.
Virtual functions depend on virtual tables which are done at run time ( because you can dynamically load stuff )

Those tables are written by the constructor .
If you don't want to iterate over GCC init sections, you can always use placement news to call your constructors manually .