Can't run the C runtime constructors and destructors manually

This forum is for general developer support questions.
Post Reply
softwarefailure
Posts: 112
Joined: Fri Feb 14, 2014 10:29 pm

Can't run the C runtime constructors and destructors manually

Post by softwarefailure »

I'm having major problems running the C runtime constructors/destructors manually in a program compiled using -nostartfiles and loaded via LoadSeg(). For purposes of illustration I have created two little sample programs that show the issue:

This is loader.c which loads the child program and calls a function:

Code: Select all

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <exec/exec.h>
#include <exec/types.h>

#include <proto/elf.h>
#include <proto/exec.h>

struct magicwordinfo
{
	ULONG magicword;
	void (*func)(void);
};

struct Library *ElfBase = NULL;
struct ElfIFace *IElf = NULL;

static int findmagicword(UBYTE *data, int size)
{
	int i;

	for(i = 0; i <= size - sizeof(struct magicwordinfo); i += 2) {

		struct magicwordinfo *tmp = (struct magicwordinfo *) (data + i);

		if(tmp->magicword == 0xC0DEFACE) {
			tmp->func();
			return 1;
		}
	}

	return 0;
}

int main(int argc, char *argv[])
{
	APTR obj;
	int k, c = 0;

	ElfBase = OpenLibrary("elf.library", 0);
	IElf = (struct ElfIFace *) GetInterface(ElfBase, "main", 1, NULL);

	obj = OpenElfTags(OET_Filename, "child", TAG_DONE);
	ElfLoadSegTags(obj, ELS_FreeUnneeded, TRUE, TAG_DONE);
	GetElfAttrsTags(obj, EAT_NumSections, &c, TAG_DONE);

	for(k = 0; k < c; k++) {

		APTR s = GetSectionTags(obj, GST_SectionIndex, k, TAG_DONE);
		Elf32_Shdr *h = GetSectionHeaderTags(obj, GST_SectionIndex, k, TAG_DONE);

		if(s && h && h->sh_size > sizeof(struct magicwordinfo)) {
			if(findmagicword(s, h->sh_size)) break;
		}
	}

	CloseElfTags(obj, CET_UnloadSeg, TRUE, TAG_DONE);

	DropInterface((struct Interface *) IElf);
	CloseLibrary((struct Library *) ElfBase);

	return 0;
}
And this is child.c:

Code: Select all

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <dos/dos.h>
#include <exec/exec.h>
#include <exec/types.h>

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/utility.h>

struct magicwordinfo
{
	ULONG magicword;
	void (*func)(void);
};

struct Library *DOSBase = NULL;
struct DOSIFace *IDOS = NULL;

// use __UtilityBase because this is required for clib2 initialization --> see clib2/stdlib_lib_main.c
struct Library *__UtilityBase = NULL;
struct UtilityIFace *__IUtility = NULL;

void _start(void)
{
}

static BOOL InitClib2(void);
static void UnInitClib2(void);

int initamigastuff(void)
{
	SysBase = *((struct ExecBase **) 4);
	IExec = (struct ExecIFace *) ((struct ExecBase *) SysBase)->MainInterface;

 	DOSBase = OpenLibrary("dos.library", 0);
	IDOS = (struct DOSIFace *) GetInterface(DOSBase, "main", 1, NULL);

	if(!InitClib2()) return 0;

	return 1;
}

void freeamigastuff(void)
{
	UnInitClib2();

	if(IDOS) DropInterface((struct Interface *) IDOS);
	if(DOSBase) CloseLibrary((struct Library *) DOSBase);
}

void func(void)
{
	BPTR fh;

	initamigastuff();

	fh = Open("CON:100/100/320/240/Output/AUTO/CLOSE/WAIT", MODE_NEWFILE);
	FPrintf(fh, "Hello from child!\n");
	FFlush(fh);

	freeamigastuff();
}

const struct magicwordinfo magicword = {
	0xC0DEFACE,
	func
};

// NB: this has been taken from clib2/stdlib_lib_main.c --> normally, it wouldn't be
// necessary to copy this code here but we could just call __lib_init() and __lib_exit()
// exported by clib2 but we can't do this here because __lib_init() and __lib_exit()
// are only available in clib2-ts and this isn't included in the latest SDK any more
// so we have to work around the problem by copying the code from clib2 directly

#include <setjmp.h>

extern jmp_buf __exit_jmp_buf;
extern BOOL __exit_blocked;

/****************************************************************************/

/*
 * Dummy constructor and destructor array. The linker script will put these at the
 * very beginning of section ".ctors" and ".dtors". crtend.o contains a similar entry
 * with a NULL pointer entry and is put at the end of the sections. This way, the init
 * code can find the global constructor/destructor pointers.
 *
 * WARNING:
 * This hack does not work correctly with GCC 5 and higher. The optimizer
 * will see a one element array and act appropriately. The current workaround
 * is to use -fno-aggressive-loop-optimizations when compiling this file.
 */
static void (*__CTOR_LIST__[1]) (void) __attribute__(( used, section(".ctors"), aligned(sizeof(void (*)(void))) ));
static void (*__DTOR_LIST__[1]) (void) __attribute__(( used, section(".dtors"), aligned(sizeof(void (*)(void))) ));

/****************************************************************************/

/****************************************************************************/

static void
_init(void)
{
	int num_ctors,i;
	int j;

	for(i = 1, num_ctors = 0 ; __CTOR_LIST__[i] != NULL ; i++)
		num_ctors++;

	for(j = 0 ; j < num_ctors ; j++)
		__CTOR_LIST__[num_ctors - j]();
}

/****************************************************************************/

static void
_fini(void)
{
	int num_dtors,i;
	static int j;

	for(i = 1, num_dtors = 0 ; __DTOR_LIST__[i] != NULL ; i++)
		num_dtors++;

	while(j++ < num_dtors)
		__DTOR_LIST__[j]();
}

STATIC BOOL lib_init_successful;

/****************************************************************************/

STATIC BOOL
open_libraries(void)
{
	BOOL success = FALSE;
	int os_version;

	/* Check which minimum operating system version we actually require. */
	os_version = 37;

	__UtilityBase = OpenLibrary("utility.library",os_version);
	if(__UtilityBase == NULL)
		goto out;

	#if defined(__amigaos4__)
	{
		__IUtility = (struct UtilityIFace *)GetInterface(__UtilityBase, "main", 1, 0);
		if(__IUtility == NULL)
			goto out;
	}
	#endif /* __amigaos4__ */

	success = TRUE;

 out:

	return(success);
}

/****************************************************************************/

STATIC VOID
close_libraries(VOID)
{
	#if defined(__amigaos4__)
	{
		if(__IUtility != NULL)
		{
			DropInterface((struct Interface *)__IUtility);
			__IUtility = NULL;
		}
 	}
	#endif /* __amigaos4__ */

	if(__UtilityBase != NULL)
	{
		CloseLibrary(__UtilityBase);
		__UtilityBase = NULL;
	}
}

/****************************************************************************/

static void UnInitClib2(void)
{
	if(lib_init_successful)
	{
		/* Enable exit() again. */
		__exit_blocked = FALSE;

		/* If one of the destructors drops into exit(), either directly
		   or through a failed assert() call, processing will resume with
		   the next following destructor. */
		(void)setjmp(__exit_jmp_buf);

		/* Go through the destructor list */
		_fini();

		close_libraries();

		lib_init_successful = FALSE;
	}
}

/****************************************************************************/

static BOOL InitClib2(void)
{
	int result = FALSE;

	/* Open dos.library and utility.library. */
	if(!open_libraries())
		goto out;

	/* This plants the return buffer for _exit(). */
	if(setjmp(__exit_jmp_buf) != 0)
	{
		/* If one of the destructors drops into exit(), either directly
		   or through a failed assert() call, processing will resume with
		   the next following destructor. */
		(void)setjmp(__exit_jmp_buf);

		/* Go through the destructor list */
		_fini();

		goto out;
	}

	/* Go through the constructor list */
	_init();

	/* Disable exit() and its kin. */
	__exit_blocked = TRUE;

	/* Remember this so that __lib_exit() will know what to do. */
	lib_init_successful = TRUE;

	result = TRUE;

 out:

	if(!lib_init_successful)
		close_libraries();

	return(result);
}
The code that manually runs the constructors and destructors is in the InitClib2() and UnInitClib2() functions and has been taken from clib2/stdlib_lib_main.c so it should be correct.

This is how I compile both programs:

Code: Select all

gcc -D__USE_INLINE__ -mcrt=clib2 -o loader loader.c
gcc -D__USE_INLINE__ -mcrt=clib2 -nostartfiles -o child child.c
When starting "loader", the function from the child program is called correctly because the console window opened in func() appears but then the program crashes while running destructors. Precisely, it's this code in the _fini() function that crashes:

Code: Select all

	while(j++ < num_dtors)
		__DTOR_LIST__[j]();
I don't see any problem with the code. I've tried it with different gcc versions (8, 10, 11) from the latest SDK all to no avail. It always crashes when running the destructors so I'm out of ideas here. Can anybody help?

This is what the .ctors and .dtors sections look like in the map file for the child program:

Code: Select all

.ctors          0x01013064        0x8
 *crtbegin.o(.ctors)
 *crtbegin?.o(.ctors)
 *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
 .ctors         0x01013064        0x4 /tmp/cc7dGGOl.o
 *(SORT(.ctors.*))
 .ctors._9      0x01013068        0x4 /SDK/clib2/lib/libc.a(stdlib_malloc.o)
 *(.ctors)

.dtors          0x0101306c        0x8
 *crtbegin.o(.dtors)
 *crtbegin?.o(.dtors)
 *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
 .dtors         0x0101306c        0x4 /tmp/cc7dGGOl.o
 *(SORT(.dtors.*))
 .dtors._9      0x01013070        0x4 /SDK/clib2/lib/libc.a(stdlib_malloc.o)
 *(.dtors) 
afxgroup
Beta Tester
Beta Tester
Posts: 20
Joined: Sun Jun 19, 2011 1:15 pm
Contact:

Re: Can't run the C runtime constructors and destructors manually

Post by afxgroup »

You have to use - O0 fno-aggressive-loop-optimizations or gcc will remove constructors and destructors
rjd324
Posts: 14
Joined: Sun Nov 21, 2021 3:52 am

Re: Can't run the C runtime constructors and destructors manually

Post by rjd324 »

Hi,

I have added this test case to the repository: https://github.com/3246251196/adtools_testing. Specifically: https://github.com/3246251196/adtools_t ... destructor.
softwarefailure
Posts: 112
Joined: Fri Feb 14, 2014 10:29 pm

Re: Can't run the C runtime constructors and destructors manually

Post by softwarefailure »

afxgroup wrote: Sat Aug 12, 2023 9:25 pm You have to use - O0 fno-aggressive-loop-optimizations or gcc will remove constructors and destructors
Check my original posting again. I've posted the build lines I'm using. As you can see from the build lines I'm not using any optimization options at all so passing -O0 fno-aggressive-loop-optimizations seems superfluous. FWIW I've tried to explicitly pass -O0 fno-aggressive-loop-optimizations to gcc now but unsurprisingly, it didn't make a difference. It still crashes.
rjd324 wrote: Sat Aug 12, 2023 11:57 pm I have added this test case to the repository: https://github.com/3246251196/adtools_testing. Specifically: https://github.com/3246251196/adtools_t ... destructor.
Maybe someone should fix it first before adding it as a test case ;)

Something is wrong either in my code or in the OS4 SDK's build toolchain. As I said, it doesn't work here at all and I'm out of ideas because I don't see any problems in my code. So I'm still looking for a solution to this problem. Otherwise I can't release my latest Hollywood plugin for OS4.
afxgroup
Beta Tester
Beta Tester
Posts: 20
Joined: Sun Jun 19, 2011 1:15 pm
Contact:

Re: Can't run the C runtime constructors and destructors manually

Post by afxgroup »

It is still crashing in the .dtors because .dtors section is not null terminated. And it is a problem on our binutils we are working at.

Use

Code: Select all

ppc-amigaos-objdump -Dr -j .dtors <filename>
and you will see that null terminator on both .ctors and .dtors section is missing. And I suspect there is nothing you can do about it at moment.
softwarefailure
Posts: 112
Joined: Fri Feb 14, 2014 10:29 pm

Re: Can't run the C runtime constructors and destructors manually

Post by softwarefailure »

afxgroup wrote: Sun Aug 20, 2023 7:39 pm It is still crashing in the .dtors because .dtors section is not null terminated.
Right, this makes sense. Thanks for checking! When can I expect a fix for this?
afxgroup
Beta Tester
Beta Tester
Posts: 20
Joined: Sun Jun 19, 2011 1:15 pm
Contact:

Re: Can't run the C runtime constructors and destructors manually

Post by afxgroup »

We are working on new binutils. Don't know if it will fix the problem. Maybe we need also a new elf.library
softwarefailure
Posts: 112
Joined: Fri Feb 14, 2014 10:29 pm

Re: Can't run the C runtime constructors and destructors manually

Post by softwarefailure »

Who is responsible for the binutils on OS4 nowadays? I think it might be the best idea to contact him personally so that this can get fixed rather soonish. It's currently blocking me from release so I'd really need a solution for this.
Post Reply