Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gcc -mbaserel option broken in newer gcc versions > 4.0.4 #14

Open
migthymax opened this issue Oct 14, 2024 · 4 comments
Open

gcc -mbaserel option broken in newer gcc versions > 4.0.4 #14

migthymax opened this issue Oct 14, 2024 · 4 comments

Comments

@migthymax
Copy link
Member

Olivier Roberts provided the following test case for the -mbasereladdress mode, which is broken in newer gcc version.
That issue is one topic which avoids compiling for example AmiSSL with a modern gcc.

/*
** -mbaserel test code
** by Oliver Roberts <[email protected]>
**
** Compile using:
** ppc-amigaos-gcc-4.0.4 -mbaserel -mno-sdata -O2 breltest.c -o breltest
*/

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

/* Causes functions to load/restore r2 */
#define BASEREL_FUNC __attribute__((baserel_restore)) __attribute__((noinline))

/* Forces an absolute reference, not r2 relative */
#define COMMON_VAR __attribute__((force_no_baserel))

/* These should be in .rodata */
const char versta[] = "\0$VER: breltest 1.1 (13.10.2024)\r\n";
const char rodata_string[] = "rodata_string";
const int rodata_array[] = { 0x01020304, 0x05060708 };

/* These are accessed relative to r2 */
int baserel_var_bss;
int baserel_var_bss_zero = 0;
int baserel_var_data = 0x12345678;
int baserel_arr1[2] = { 1, 2 };
int baserel_arr2[2] = { 3, 4 };
int *baserel_arr = baserel_arr1; // .data reloc

struct SignalSemaphore TLock;
struct Library *TUtilityBase, *TIntuitionBase;
struct UtilityIFace *TIUtility;
struct IntuitionIFace *TIIntuition;

/* These are accessed via absolute addressing - never r2 relative */
COMMON_VAR int common_var_bss;
COMMON_VAR int common_var_bss_zero = 0;
COMMON_VAR int common_var_data = 0x87654321;

extern COMMON_VAR uint32 _DATA_BASE_;
extern COMMON_VAR struct ExecIFace *IExec;
extern COMMON_VAR struct DOSIFace *IDOS;
COMMON_VAR struct ElfIFace *IElf;

/* Hacks to ensure suitable relocs and data are created */
int hack_array[16] = { 1 };
char *hack_xref = (char *)rodata_string;

/* This is typically what a shared library would use, with a pointer
** to the copy of the data segment stored in EnvironmentVector,
** so that each library opener gets its own library base, with its
** own data segment (and therefore, its own .data, .bss, etc)
*/
asm("	.text\n"
    "	.globl __baserel_get_addr\n"
    "__baserel_get_addr:\n"
    "	lwz	2,48(3)\n" // Fetch EnvironmentVector from struct Interface
    "	blr\n");

/* This is a standard function which should behave as normal with r2
** still pointing to the program's data
*/
void normal_function(void)
{
	/* Change the original data, so we can detect issues accessing the copy */
	baserel_var_bss = -1;
	baserel_var_bss_zero = -2;
	baserel_var_data = -3;
	baserel_arr1[0] = -4;
	baserel_arr1[1] = -4;
	baserel_arr2[0] = -4;
	baserel_arr2[1] = -4;

	common_var_bss = -5;
	common_var_bss_zero = -6;
	common_var_data = -7;
}

/* This function should set r2 using __baserel_get_addr and restore in
** epilog, so that r2 points to our copy of the data segment
*/
BASEREL_FUNC void baserel_function(struct Interface *iface)
{
	APTR r2;

	asm volatile ("mr %0,2" : "=r" (r2));

	if(iface->Data.EnvironmentVector != r2)
	{
		IDOS->Printf("FATAL ERROR: bad r2 %p (should be %p)!\n",r2,iface->Data.EnvironmentVector);
		/* Bail out here as r2 may change midway through the below = crash */
		return;
	}

	/*
	** A bunch of seemingly random stuff to try to force the bug where
	** __baserel_get_addr() is called too late, after the r2 references below
	*/
	IExec->InitSemaphore(&TLock);

	if((TUtilityBase = IExec->OpenLibrary("utility.library", 50)) != NULL)
	{
		if((TIntuitionBase = IExec->OpenLibrary("intuition.library", 50)) != NULL)
		{
			if((TIUtility = (struct UtilityIFace *)IExec->GetInterface(TUtilityBase,"main",1,NULL)) != NULL)
			{
				if((TIIntuition = (struct IntuitionIFace *)IExec->GetInterface((struct Library *)TIntuitionBase, "main", 1, NULL)) != NULL)
				{
					IExec->DropInterface((struct Interface *)TIIntuition);
				}
				IExec->DropInterface((struct Interface *)TIUtility);
			}
			IExec->CloseLibrary(TIntuitionBase);
		}
		IExec->CloseLibrary(TUtilityBase);
	}

	/*
	** START TESTS
	*/
	if(baserel_var_bss != 0)
		IDOS->Printf("ERROR: baserel_var_bss = %ld (should be 0)\n",baserel_var_bss);

	if(baserel_var_bss_zero != 0)
		IDOS->Printf("ERROR: baserel_var_bss_zero = %ld (should be 0)\n",baserel_var_bss_zero);

	if(baserel_var_data != 0x12345678)
		IDOS->Printf("ERROR: baserel_var_data = %ld (should be 0x12345678)\n",baserel_var_data);

	if(baserel_arr[0] != 1)
		IDOS->Printf("ERROR: baserel_arr[0] = %ld (should be 1)\n",baserel_arr[0]);

	if(common_var_bss != -5)
		IDOS->Printf("ERROR: common_var_bss = %ld (should be -5)\n",common_var_bss);

	if(common_var_bss_zero != -6)
		IDOS->Printf("ERROR: common_var_bss_zero = %ld (should be -6)\n",common_var_bss_zero);

	if(common_var_data != -7)
		IDOS->Printf("ERROR: common_var_data = %ld (should be -7)\n",common_var_data);

	/* GCC 4.0.4 will incorrectly place this array in .rodata, causing the
	** references to be absolute and not r2 relative and thus pointing to the
	** wrong data. This mimics the OpenSSL code triggering this bug. A workaround
	** is to make this array static so that it is not placed in .rodata.
	*/
	{
		/* static */ int *tables[] = { baserel_arr1, baserel_arr2 };
		int i, j;
		for (j = 0; j < 2; j++)
		{
			for (i = 0; i < 2; i++)
			{
				if(tables[j][i] == -4)
				{
					IDOS->Printf("ERROR: Local array incorrectly placed in .rodata\n");
					return;
				}
			}
		}
	}
}

/* Run our tests */
void DoTests(struct Interface *iface)
{
	normal_function();
	baserel_function(iface);
	IDOS->Printf("Tests completed\n");
}

/* Initialize */
int main(int argc, char **argv)
{
	struct Interface iface;
	APTR baserelData, r2;
	uint32 offset;
	struct Library *lib;
	BPTR seglist;
	Elf32_Handle elf;

	/* Read r2 */
	asm volatile ("mr %0,2" : "=r" (r2));

	/* Check r2 was set by startup code */
	if(r2 == &_DATA_BASE_)
	{
		IDOS->Printf("INFO: Startup correctly set r2 to %p\n",r2);
	}
	else
	{
		IDOS->Printf("ERROR: Startup incorrectly set r2 to %p (should be %p)\n",r2,&_DATA_BASE_);
		/* Set it ourselves */
		asm volatile ("mr 2,%0" : : "r" (&_DATA_BASE_));
	}

	/* Copy the data segment and run the tests */
	if((lib = IExec->OpenLibrary("elf.library",53)))
	{
		if((IElf = (struct ElfIFace *)IExec->GetInterface(lib,"main",1,NULL)))
		{
			if((seglist = IDOS->GetProcSegList(NULL,GPSLF_CLI | GPSLF_SEG)))
			{
				if(1 == IDOS->GetSegListInfoTags(seglist, GSLI_ElfHandle, &elf, TAG_DONE) && elf)
				{
					if((baserelData = IElf->CopyDataSegment(elf, &offset)))
					{
						IDOS->Printf("INFO: Data segment %p copied to %p\n",&_DATA_BASE_,baserelData + offset);

						iface.Data.EnvironmentVector = baserelData + offset;

						DoTests(&iface);

						IElf->FreeDataSegmentCopy(elf, baserelData);
					}
					else
					{
						IDOS->Printf("ERROR: CopyDataSegment() failed\n");
					}
				}
				else
				{
					IDOS->Printf("ERROR: Failed to get ElfHandle\n");
				}
			}
			else
			{
				IDOS->Printf("ERROR: Failed to get seglist\n");
			}
			IExec->DropInterface((struct Interface *)IElf);
		}
		IExec->CloseLibrary(lib);
	}

	/* Restore r2 */
	if(r2 != &_DATA_BASE_)
	{
		asm volatile ("mr 2,%0" : : "r" (r2));
	}

	return 0;
}
@3246251196
Copy link
Member

Thanks. What is the actual issue? Is it:

ppc-amigaos-gcc -mbaserel -mno-sdata -O2    breltest.c   -o breltest
/home/rjd/projects/amiga/adtools_testing/build/adt_build/lib/gcc/ppc-amigaos/11.3.0/../../../../ppc-amigaos/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
make: *** [<builtin>: breltest] Error 1

With the -mbaserel option I get the error above and without out, it builds.

@migthymax
Copy link
Member Author

Which gcc , binutils version are yo using, stock SDK?

@3246251196
Copy link
Member

3246251196 commented Dec 29, 2024

Hi @migthymax, this was just using all stock options. It looks like the source of information wrt. baserel is: https://www.amigans.net/modules/newbb/viewtopic.php?topic_id=9293.

https://franke.ms/amiga/hacking_gcc_and_binutils.wiki
https://eab.abime.net/showthread.php?t=85474&page=71

I read roughly what it is about, but if we had a single source document that would be useful.

It sounds like this is to do with making programs be able to be reentrant by copying ELF sections for each instance of a program so that they all have their own data sections?

It also looks like baserel has been left behind for sometime and this would require some work - perhaps a lot, perhaps not - to put back in to our later versions of GCC. I still think a document would be useful, but then - you could say that about anything.

@migthymax
Copy link
Member Author

It sounds like this is to do with making programs be able to be reentrant by copying ELF sections for each instance of a program so that they all have their own data sections?

Yeah that's the right direction. The "main" usage for baserel (with my current understanding) is that it is used in library development so that each opener has its own data sections. This is can be handy if you want to port a *unix so library to a amiga library (no sobj). The so library has per default a data section for each opener, so the data section per opener can be modeled by baserel.

So in the word of shared code in AmigaOS 4, we have the following concepts:

a) A "basic" Amiga os library, which is really shared between all openers, so all openers have access to the same data section, code section of the library.

b) A step further we have a Amiga os library using -baserel, thus each all openers share the same code section, but each has its own data section.

c) A Amiga sobj shared object. The data section and code section is dynamically loaded during startup for each program (opener). With the current implementation nothing is shared with other openers. So each opener has its own data section, like solution b), but here even symbols can be exported to the opener, and even be imported from the opener.

All three concepts have are advantages/disadvantages. c) makes it easy to port so *unix library, but optimized memory usage is no a priority. a) is the best in terms of memory usages, b) is a compromise between a) and c) with the lost of symbol import/export. And for example AmiSSL uses b) to port ssl.

Here even another source of information:
https://www.amigans.net/modules/newbb/viewtopic.php?post_id=11800

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants