Internal overflow error on Mega2560

Go To Last Post
18 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm writing code for the 2560, lots of code, and have run into the usual error from the linker about an internal overflow error.

Archive searches give mostly Stu-san's post about fixing the FreeRTOS, which I don't use. However, I do have the same kind of issues.

Given that I have a different structure for a called function, I've changed the definition of the called function to be:


	#define FUNCTION_HANDLER __attribute__ ((section (".fptr_target")))


	typedef  int (function) (void*, int ival, int jval, void*);			// define *FUNCTION as pointer to int function(hardware block*, int task#, int argument, *more parameters)

Which is an attempt to maintain the same calling routine structure for a MEGA as an XMEGA. Code somewhat abstracted from the Atmel notes.

I have a function called as:

		int	 FUNCTION_HANDLER twi_reply_smart_data (struct TWI_Master_type* twi, int count, int block_count, void* data)

Note that all called functions (used in a table, etc of pointers to a function follow this mode. Declaring the function prototype to be of type FUNCTION_HANDLER does not work. No big deal there.

Where I had a problem was in looking for the means to define the start of the lower block of function pointers (which must reside in the lower 64K of memory). The linker additions as addressed from the configuration settings make the data exist at an address, but that address must be a particular memory address, numeric only, please. Obviously, I want it roughly before the main program code, and after the .init 9 section. I can't use any of the .init sections because they are executed code. This remains statically allocated data.

For a long time, I looked for the location of linker_script.x, with no luck. Apparently, it's a created file.

I did find the location of the linker scripts for a particular processor. Looks like looks like av6.x is the one for the MEGA 256x series. Making a modification to it, the way that Stu-san did, I added a section to the linker script for this processor model (and I could do so for the other models eventually, but I only use a few processor types, MEGA 16, 32, XX4, and XX8, plus xmega, where I won't need them. Or, I'll change the function definitions with a #ifdef statement and drop out the section relocation.) Hopefully, the xmega doesn't have the problem (although it might).

That modification is of the form

    *(.init9)  /* Call main().  */
    KEEP (*(.init9))
	/* Any code that is the target of a function pointer 	must also 
	reside in lower memory */ 
	*(.fptr_target) 
	KEEP(*(.fptr_target)) 

    *(.text)
    . = ALIGN(2);

Where the section added starts with the comment, and ends with the KEEP instruction.

This produces the following map file (I've tried it on only one routine to see if it relocates, it wasn't one of the "problem" routines so the errors persist)

*(.init8)
 *(.init9)
 .init9         0x00002816        0x8 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib/avr6/crtm2560.o
 *(.init9)
 *(.fptr_target)
 .fptr_target   0x0000281e       0x6a TWI_X_driver.o
                0x0000281e                twi_reply_smart_data
 *(.fptr_target)
 *(.text)
 .text          0x00002888        0x4 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib/avr6/crtm2560.o
                0x00002888                __vector_38
                0x00002888                __vector_22
                0x00002888                __vector_28
 

Which, if I read this properly, should solve the problem.
Main difficulty I have is that this is a workaround, I'd like to see something of this type of problem addressed a bit more gracefully. Any chance of that?
Did I manage to do this right? I will know in advance which functions are called by the task scheduler, the GUI_icon driver, and the TWI_slave execute routine, all of which are the routines potentially giving me the problem. I have yet to see if the code works, but so far, it at least looks as if it should.

Any comments?
Harvey

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't know what do you mean by "usual error from the linker about an internal overflow error", but if you for some reason desire to place some code below the 128kB limit, why don't you place it into some of the existing sections intended to be below that limit, e.g. .trampolines?

Otherwise, you might perhaps show us the offending function so that we could discusse the "usual error from the linker about an internal overflow error".

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is a known error. The pointer that C uses to jump to a function is 16 bits, not 24. The trampolines are used as a 16 bit locatable, but 24 bit value to functions further up in memory. For a statically allocated function, this works well, and code uses the trampoline to access functions in the top of memory.

I'm using the code as part of a table entry, so I have to treat it as a (far) pointer.

Without any sort of intervention, the compiler generates an "internal overflow error" when trying to build functions and put them in tables (see function definition above, which is a part of a table structure).

I could use the trampoline section, that's a good idea. Not sure how the linker would do it, but still, a very good idea. By forcing the code (just a routine) to be within the lower bounds of memory, the standard calls work well.
The .init sections can't be used, as they are code executed on startup. This is not code that is intended to be executed on startup at all. I could put it in the .fini sections, which, if I don't ever exit the main routine (and I don't), would work. It's not an elegant solution, though.

I could post part of the structures, if you wish. However, I think that it's a matter of where the linker puts things, not the things themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, I'd like to see a minimal compilable code which produces the error you mention - together with the command(s) you use to compile it. Thanks.

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Without playing games with the code, I'll need more than 64K of code to be able to reproduce the error, I can't show you the specific code, because it's not code related. It's size related. Having said that, I'll post a bit of code that shows how things are used. (the composition window cursor position bounces around, so I will do a longer message off line and cut & paste).

Harvey

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

NightSky wrote:
I'll need more than 64K of code to be able to reproduce the error,

Here you are, in a few lines:

void bigfoo(void) {
  __asm(
".skip 70000,0 \n\t"
  );
}

:-P

Jan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The basic function call is defined as:

typedef  int (function) (void*, int ival, int jval, void*);			// define *FUNCTION as pointer to int function(int task#, int argument)
	

This is common to all table accessed functions, although not all functions may use all fields.
Specifically, TWI communications use the following functions, of which this is a decent example. The TWI slave processor decodes a command field, does a table lookup, and based on the value in the table (the location of the routine), executes this routine.

	// ********************************************************************************************
		// *********************************** TWI SLAVE SEND TEST STRING *****************************
		// ********************************************************************************************
		//0x0F	SEND TEST STRING 

			int FUNCTION_HANDLER twi_slave_send_test(struct TWI_Master_type* twi, int address,int dummy,void* data)
			{
			int returned;
				#ifdef XMS
					xms_push(KERNEL_PAGE_TAG);
				#endif
				kernel->TWI_m.rec.msr.data[0] = 'A';						// set data buffer to user address area
				kernel->TWI_m.rec.msr.data[0] = 'B';						// set data buffer to user address area
				kernel->TWI_m.rec.msr.data[0] = 'C';						// set data buffer to user address area
				kernel->TWI_m.rec.msr.data[0] = 'D';						// set data buffer to user address area
				kernel->TWI_m.rec.msr.data[0] = 'E';						// set data buffer to user address area
				kernel->TWI_m.rec.msr.data[0] = 0;							// set data buffer to user address area
				returned = true;
				returned = twi_reply_smart_data (twi,17,0,NIL);				// set reply buffer
				twi_running = false;
				#ifdef XMS
					xms_pop();
				#endif
				return(returned);
			}
		

The section of code that fills the table, and the table definitions

		    #ifdef SLAVE_CAN_SRQ
			   kernel->twi_task[0xC].location = (function*) &twi_slave_enable_srq;
			#endif

			   kernel->twi_task[0xF].location = (function*) &twi_slave_send_test;

		    #ifdef SLAVE_CAN_A_D
			   kernel->twi_task[0x10].location = (function*) &twi_slave_read_a_d;
			   kernel->twi_task[0x11].location = (function*) &twi_slave_write_a_d_cal;
			   kernel->twi_task[0x12].location = (function*) &twi_slave_read_a_d_raw;		
			   kernel->twi_task[0x13].location = (function*) &twi_slave_read_a_d_name;
			   kernel->twi_task[0x14].location = (function*) &twi_slave_read_battery;
			#endif	

And the definition of the table: Kernel is simply an encompassing structure that can live in paged extended memory or in internal memory for smaller chips. I'll have to put the pointer to the parameter block in this, as well as the pointer to an optional data field (both probably void*), that code is still incomplete.

	struct		twi_task_type		
	{
		function*				location;					// location of task (address pointer to function)	
		int						argument;					// argument to function
		int						argument1;					// second argument to function
	};
	

All the functions resolve, there's no compile errors, no warnings about types not matching. However, the linker gives internal overflow errors. Those are caused because the pointers to the functions are 16 bits, not 24 or 32. When the program gets big enough that the called routines go over the 16 bit pointer limit, the linker has problems. Forcing the offending routines into lower memory solves the problem.
For a timer task, here's the start of the code that builds the table. The definitions of the task structure are below that.

	int task_insert_new (int flag, int increment, enum task_name_type task_name, int f(void*,int,int, void*), int argument)
	{
		int 			returned;
		signed char  	i,k;

		#ifdef XMS
			xms_push(KERNEL_PAGE_TAG);
		#endif
		i = task_find_slot();
		if (i == -1)
		{
			returned = i;
		}
		else
		{
			returned = i;
			kernel->task[i].flag	=	flag;				// task flag 
			kernel->task[i].task_name = task_name;			// named task
			kernel->task[i].status = task_idle;			// task is not busy and ready to run
			kernel->task[i].increment	=	increment;			// increment to next task time
			kernel->task[i].location	=	f;			// location of task (address pointer)
			kernel->task[i].argument = argument;
			kernel->task[i].argument1	=	0;	// argument #2
			kernel->task[i].initialized =	false;		// initialized	

Note that the location parameter is defined as a pointer to a function.

	struct		task_type		
	{
		int						flag;						// task flag, 
		enum task_name_type		task_name;					// added to ID task in table
		enum task_status_type	status;						// status of this task
		unsigned	int			now;						// execute task when this is equal to time_now
		unsigned	int			increment;					// increment to next task time
		function*				location;					// location of task (address pointer to function)	
		void*					pblock;						// pointer to parameter block (generally in kernel) XMEGA style
		void*					list;						// pointer to list of parameters for function to use
		int						argument;					// argument to function
		unsigned	int*		parameters;					// location of task parameters (address pointer)
		signed char 			subtask[MAXSUBTASKS];		// index in spawn table -1 if none
		signed char 			pair;						// task pair task number
		function*				taskpair;					// pointer to second task	
		int						argument1;					// second argument to function
		signed char 			offset;						// task offset to paired task
		signed char				initialized;				// task has been initialized
	};
	

Again, this is something that Stu-san ran into when working on the FreeRTOS version. Apparently, his program transgressed that 16 bit pointer value limit, he had to find a solution. This is adapted from his solution. [/code]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm stepping into this a little late. Let me see if I can lend a hand.

@Jan: There is some debate about the trampolines working for a function pointer call anywhere in memory. If the function pointer is called from lower memory, the trampoline works fine. If the function pointer is called from upper memory... well, let's just say I've had difficulties in previous versions of WinAVR and am unwilling to retest for the problem with every revision of GCC. I probably should spend some time to generate a test case, but that would have to be in my copious spare time. :wink:

@NightSky: Isn't a function pointer definitions supposed to be:

typedef  int (*funcptr) (void*, int ival, int jval, void*);

(note the * in front of funcptr)

As you noted, linker_script.x is a file you must create. I think you followed my instructions correctly. If you want, I can post my modified linker_script.x file.

I have found that the internal overflow linker errors tend to be related to large switch statements overlapping the 128KByte Flash boundary. Re-arranging the order of your source files in the makefile can usually get rid of those problems (as you found).

Finally, as you noted, if you are creating tasks and handing a task scheduler a pointer to the task routine, the task stack creation routine must generate a valid pointer to the routine. While it is not impossible to generate a 32-bit pointer to a routine (check out the GET_FAR_ADDRESS() macro in Carlos Lamas' morepgmspace.h), it can be easier to just pass a 16-bit function pointer and force the function to always reside in the bottom half of flash. It certainly made the stack initializer simpler!

BTW, also in my posting you will note a section .isr, similar to the .funcptr section. I use it to force all of my ISRs into bottom flash as well, for basically the same reason. I don't like having functions of any kind in an ISR, but I have a particularly gnarly ISR for SPI communication where I had to use multiple functions, so I used a function pointer pretty much as a pointer in a Finite State Machine. The interrupt is not the SPI interrupt, but instead is an external interrupt from the "slave" processor saying it wants to tell me something.

I've added a lot of smoke to this discussion, but I'm not sure that I've added any light.

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't use function pointers so I am still confused. I though this issue has been definitively solved using the trampolines and the ramblings were mostly historical, but now I see there are still issues associated with this.

stu_san wrote:
@Jan: There is some debate about the trampolines working for a function pointer call anywhere in memory. If the function pointer is called from lower memory, the trampoline works fine. If the function pointer is called from upper memory... well, let's just say I've had difficulties in previous versions of WinAVR [...]

I've noticed that the '07 vintage uses EIJMP to reach the trampolines, which is not necessary and I can imagine if there is some occurence of an instruction which changes the paging register (which I forgot how it's called and am lazy to look up now :-) ) from the default zero, the whole shebang jumps straight to void. This appears to be fixed in the '10 issue which uses IJMPs.

stu_san wrote:
I have found that the internal overflow linker errors tend to be related to large switch statements overlapping the 128KByte Flash boundary.

This is interestig, and related to http://sourceware.org/bugzilla/s... .
The warning is indeed misleading. I've never had ANY code crossing the 128k boundary (I'm using the big FLASH as a huge configuration memory) yet I've seen the warning so many times I've learned to ignore it myself (and "taught" my tools to do the same). Obviously, if the problem is 128kB-boundary related, the tool to detect it is NOT gas NOR gcc (as Joerg said in the above linked tracker item), but ld...

stu-san wrote:
Finally, as you noted, if you are creating tasks and handing a task scheduler a pointer to the task routine, the task stack creation routine must generate a valid pointer to the routine. While it is not impossible to generate a 32-bit pointer to a routine (check out the GET_FAR_ADDRESS() macro in Carlos Lamas' morepgmspace.h), it can be easier to just pass a 16-bit function pointer and force the function to always reside in the bottom half of flash.

Oh, and I thought that existence trampolines means you NEVER need to take the 32-bit address of an indirectly (i.e. through function pointer) called function! What is wrong in my thinking?

Jan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This archived thread may jog a few memories.

http://lists.nongnu.org/archive/html/avr-gcc-list/2010-01/msg00007.html

The 'bug' is not that trampolines are not used/created when a function in the upper 128k is called. Its about storing the address of a function in a table/variable. At this point the compiler won't know the function is going to end up high, etc, etc.. and since the call is effectively indirect, doesn't get fixed up.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
Oh, and I thought that existence trampolines means you NEVER need to take the 32-bit address of an indirectly (i.e. through function pointer) called function! What is wrong in my thinking?
Perhaps it is my thinking that is wrong. As I said, I did this work several years ago and never really revisited it. Please review my work below.

(In the discussion below, I've "processed" some of the actual FreeRTOS definitions from their macro-based source for clarity.)

In FreeRTOS, we create a task by handing the address of the task routine to the scheduler, for example:

xTaskCreate( vGeneralTask, (signed portCHAR*) taskName, GEN_STACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );

where vGeneralTask is:

void vGeneralTask( void* pvParameters );

So far, so good. We are passing the address of a function to the task creator function.

At this point, I need to ask: Is the address that is passed a pointer to the trampoline or a pointer to the actual function? The linker can only pass a 16-bit value, so I guess that must be the trampoline. In the old days, it was a pointer to the actual function.

Inside xTaskCreate we build an initial stack for the task, with the address of the function as the return address. Since we have an ATmega2560, we need to provide 3 bytes for the return address.

There are three ways out of this:

1) Assume the linker has done its job and the 16-bit pointer is to the bottom half trampoline. Supply the top PC byte as 0 when you're constructing the stack and you're golden.

2) Assume the linker has given us the bottom 16 bits of the function address. This was true when I did this originally, but may no longer be true. In this case, the answer is to force the function to be in the bottom half of flash, supply the top byte as 0 when constructing the stack and you're golden.

3) Assume the linker would give us 16-bits (which is what GCC would do), but instead of that change the definition of xTaskCreate from:

typedef void (*pdTASK_CODE)( void * );
signed portBASE_TYPE xTaskGenericCreate( pdTASK_CODE pvTaskCode, ...

to something like

signed portBASE_TYPE xTaskGenericCreate( uint32_t pvTaskCode, ...

Which would be called by:

xTaskCreate( GET_FAR_ADDRESS( vGeneralTask ), ...

Now we know we're getting the fully-qualified address (although GET_FAR_ADDRESS may hand us a byte address -- easily tweaked) so we can build the stack with the full address, where ever the routine may reside.

Of course, once the task is running, all of the above concern goes away.

One final (extremely minor) note: Even given the trampoline working, one will still incur the 3 cycles or so to execute the jump. Function pointers using the trampoline will always incur this penalty. I freely admit this is negligible in all but the most trying circumstances, but it is there. If I relocate the target routine to the bottom of flash, I can avoid that (extremely minor) overhead.

So, am I confused? Is the above analysis garbage? I dunno.

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've made a couple of experiments, and my conclusion is, that the whole trampolines mechanism is definitively broken, in the linker. That of course impacts all other things.

Stand by, still investigating (while trying to cope with the everyday tasks too :-( ).

I am reluctant to report this in the avr-gcc forum, as from the experience so far I expect the answer is "why do you report this if you don't have a patch to fix it".

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
I've made a couple of experiments, and my conclusion is, that the whole trampolines mechanism is definitively broken, in the linker. That of course impacts all other things.
JW

Jan, I don't believe the linker is broken. Here's my input from similar experience to Stu.

The OP has a table of function pointers. This is what breaks and Stu's analysis and workaround is good.

The issue is that for example:

typedef void (*pdTASK_CODE)( void * );

when used to define a table, or as a parameter, sets aside storage for a 16-bit value. Function addresses above 128k won't fit and this is where the linker fixups will fail. Not, I believe, through any fault of the linker. The compiler didn't foresee the possibility that the address might require 24-bits. Compiler issue? If it always set aside space for a 24-bit address, would the linker know to shrink it when appropriate?

Stu's workaround is to always define uint32_t to take the address. In this case to pass as a parameter in a function call. This is where GET_FAR_ADDRESS comes in handy. Then its a case of ensuring that the address passed, or stored in the table, is correct. Defining the table of function pointers to be of type uint32_t, would ensure there is always space to store any address. With overhead. Its a trade-off.

In my post to avr-gcc list, the hack below creates an explicit trampoline. There may be ways to make it more transparent. It has overhead, of course, there's the call to the trampoline and then a resulting jmp to the function.

#define JMP_TABLE_ENTRY(x)      __asm__ __volatile__ ("jmp "#x)

typedef void (*fptr) (void);

void foo(void);
void bar(void);
void mumble(void);

void jumptable_fn(void) __attribute__ ((naked));
void jumptable_fn(void) {
        JMP_TABLE_ENTRY(foo);
        JMP_TABLE_ENTRY(bar);
        JMP_TABLE_ENTRY(mumble);
}

fptr jumptable[] __attribute__((progmem)) = {
        jumptable_fn+0, // foo
        jumptable_fn+4, // bar,
        jumptable_fn+8, // mumble,
};

One further comment. To avoid the above and where executable code is likely to be smaller than 128k, it can be simpler to force all strings to reside above 128k and manage them specifically with routines (morepgmspace) that know how to fetch them.

This takes some discipline and some linker script mods.

#define FAR_PROGMEM_SECTION	__attribute__ ((section (".farprogmem")))

Then modify avr6.x to add:

   }  > data
+  .farprogmem  (LOADADDR (.text) + SIZEOF (.text) + SIZEOF (.data)) :
+  {
+     PROVIDE (__farprogmem_start = .) ;
+    *(.farprogmem*)
+     PROVIDE (__farprogmem_end = .) ;
+  } > text
   .eeprom  :
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@dalew1: I had forgotten about the explicit jump table solution. It is commonly used when the bootloader and the app want to share functions, but I had spaced out its application in other ways. Thanks for your comment!

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

dalew1 wrote:
Jan, I don't believe the linker is broken.

Thou shalt have confidence... ;-)

The compiler, when encounters an indirect jump (as a consequence of switch implemented through jumptable) or taking address of function (as in assigning/initialising function pointers), it uses a special modifier with the address, gs(address), in the assembler, to tell the linker, that that address should potentially go through the trampolines (see the link in http://lists.nongnu.org/archive/... and Joerg's explanation in the reply).

Btw, gs stands for generate stubs; how nice that there are two expressions used for the same thing, stubs and trampolines, adding to the confusion a bit. I'd use trampolines here; stubs is less specific.

If there are more gs()-d labels entering the linker, there are three possible options

  • after relocation, all gs()-d labels lie below 0x20000, then there are no trampolines and everything is OK
  • all gs()-d labels lie above 0x20000, then the trampolines are generated for all of them and the gs() reference the address of trampoline and everything is OK
  • some are above and some below 0x20000. This case fails. Apparently, the linker calculates the space needed for trampolines twice: first, to determine the target address of trampolines, i.e. where should they jump; second, to determine the actual size of the trampolines to make the real code shift (don't ask my why this complicated). The first one goes wrong, though, apparently calculating space for trampolines as if all gs() would end up there, including those, whose target lies below 0x20000. Through a slightly perverted test it detects its mistake, that's why it throws the "overflow - internal error" "warning", but it continues to emit garbage.
I'll post a minimal example as soon as possible (tomorrow is a busy day but I'll try).

Relaxation might somehow fix this problem, I did not investigate (IMHO what's broken is broken, and that there is an "unbroke" option is not a real solution).

Now your solutions with explicit trampolines and/or pushing all functions below the "magic" 0x20000 and/or by taking the "real" "far" address through Carlos Lamas' macro; and suppressing jumptable-implemented-switches either through code reorder or better IIRC there's a command-line option for that; so all these tricks avoid the problem described above and are thus OK. And, as it's unlikely that we'll see a real fix in a production avr-gcc-and-suite anywhen soon, I'd suggest to try to live with these fixes. I believe the knowledge of the nature of the error should help to avoid it more safely.

JW

PS. dalew1, I came exactly to the same conclusion as per moving extensive data to "far progmem", see https://www.avrfreaks.net/index.p... ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wrote:
I'll post a minimal example as soon as possible (tomorrow is a busy day but I'll try).
Here it is, as promised (file name y3a.S):

.text
.global	main
main:
  ret

.global K1
.org 0x100
.L1:
K1:
  nop
.global K2
.org 0x20002
.L2:
K2:
  nop

.section .progmem
.word gs(.L2)
.word gs(.L1)

Compiling (assembling & linking, to be more precise) using

"c:\Program Files\Atmel\AVRTools\WinAVR20100110\bin\avr-gcc" y3a.S -Os -DF_CPU=14745600UL -mmcu=atmega2561 -Wa,-adhlns=y3a.lst -Wl,-Map=y3a.map,--cref,--debug-stubs -o y3a.elf

results in

Adding stub with destination 0x20108 to the hash table.
(Pre-Alloc run: 1)
Adding stub with destination 0x206 to the hash table.
(Pre-Alloc run: 1)
Allocating 1 entries in the AMT
Building one Stub. Address: 0x20110, Offset: 0x0
Final Stub section Size: 4
LD: Using jump stub (at 0x20000) with destination 0x2010c for reloc at address 0xcc.
C:\Users\OM7ZZ\AppData\Local\Temp/cc4xOnGI.o:(.progmem+0x0): warning: internal error: out of range error

The following are relevant snippets of y3a.lss:

[... vectors...]
      cc:	00 00       	nop
      ce:	05 01       	movw	r0, r10

000000d0 <__trampolines_start>:
      d0:	0d 94 88 00 	jmp	0x20110	; 0x20110 <__stop_program>
[...]
0000020a :
	...

0002010c :
	...

0002010e <_exit>:
   2010e:	f8 94       	cli

00020110 <__stop_program>:
   20110:	ff cf       	rjmp	.-2      	; 0x20110 <__stop_program>

(that the jump in trampoline points (incorrectly) to a valid label (__stop_program) is a coincidence).

Note, that not only the trampoline jumps to an incorrect address, but that also the generated pointer to the trampoline (at address cc in .progmem section, in source gs(.L2) ) is incorrect (should be 0xd0 / 2 = 0x68).

Enjoy! ;-)

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm (just) recovering from a major OS crash (let's hear it for windows anyway upgrade to professional), not everything is back to where it should be at all.

That' occupied the last few days.

Some comments:

Stu-san, the function definition is fine, I'm not defining it as a pointer to a function, but a function. In all the tables, the entries are pointers to that defined function type. Seems to work ok.

Problem is, as stated, that the value put in the (pointer to function) variable is 16 bits, not 24 or 32. Hence the linker overflow.

Given that I'm not going to change the mechanism (works well and is sufficiently documented for what I'm doing, haven't seen a better one); it seems that the original solution to force the offending functions below the magic limit is the optimal solution.

I'm defining the word optimal to mean:

1) I do as little as possible to fix the problem
2) it works for small as well as large code (conditional compile is ok)
3) it works dependably
4) it disturbs the program structure as little as possible.

Note that the trampoline mechanism is correct, and does work with the larger code because the compiler can write a trampoline for the "far" function.

What I don't have is a mechanism (other than forcing the address low) that allows me to specify to the compiler that the pointer to a defined function class is going to be "far" and that it should allow 24 bits or 32 bits worth of address.

Trampolines are a wonderful mechanism for a workaround, but not an elegant solution for handling large code models. Other compilers/assemblers work with a different series of memory models, allowing "small", "large", and "buy lotsa intel memory" settings. GCC does not in this case.

Hence, the need for a workaround.

Main problem I had was not understanding the principle, which was decently explained, but was the realization that "linker_script.x" was a shorthand for "avr6.x" as done as a linker script.

I think that between this thread, and Stu-san's original post, it might be covered well enough that it can be implemented with a little less digging. Since I don't normally go in for explicit makefiles and the like, figuring that the tool *ought* to be capable of handling boundary situations gracefully, I do as little custom as needed.

Thanks for the comments, I'll keep working on the code once I get AVR studio back and running.

I'll also (happily) let others fix the trampoline mechanism, if broken.

Harvey

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

NightSky wrote:
This is a known error. The pointer that C uses to jump to a function is 16 bits, not 24.
You means an error in the application, yes?

To be sure you use the feature correctly you may want to turn to the GCC online manual and read section

"EIND and Devices with more than 128 Ki Bytes of Flash"

If your code and/or data is big, you may need a custom linker script that arranges to your needs.

A default script cannot fit *all* imagineable needs at the same time, in particular if the segmented layout of the underlying harware can no more be neglected.

For your error message, see binutils PR13812.

The wrong code is either abusing linker stubs in conflict with the aforementioned EIND policies, or — if you use bleeding edge binutils build — it might also be binutls PR13899.

avrfreaks does not support Opera. Profile inactive.