FAQ:Link

From GNU 68HC11/HC12
Jump to: navigation, search

This FAQ section deals with the GNU linker. The linker is the tool that you will use to do the final link and create the final binary based on one or several object files and libraries.

Contents

Mapping Programs to Memory

What is the memory.x?

The memory.x file is used when you use the m68hc11elfb linker script. It defines memory banks that tell the linker where to put the text, data and other program sections. The name of memory banks are specified within the linker script. A typical memory.x file defines the MEMORY banks as follows:

MEMORY
{
  page0 (rwx) : ORIGIN = 0x0, LENGTH = 256
  text  (rx)  : ORIGIN = 0xE000, LENGTH = 2048
  data        : ORIGIN = 0x0, LENGTH = 0
}

This definition creates 3 memory banks:

  • page0 represents a the HC11/HC12 region for direct addressing mode. This memory bank must always be completely included in the [0..0x0ff] interval.
  • text represents the memory bank where the linker will put the .text sections which contains the code. In most cases it represents the ROM of the board (but it can also represent RAM).
  • data represents the memory bank used to put the initialized and non-initialized data. This bank usually represents RAM.

See Also How does the linker know where memory.x is?

How does the linker know where memory.x is?

The memory.x file is searched in the current directory and in all directories specified with the -L switch. There is also a default directory which is /usr/m6811-elf/lib or xxx\m6811-elf\lib.

For example, in GEL, the memory.x file is read from the board specific config directory. This is caused by the option:

-L$(GEL_BASEDIR)/config/$(TARGET_BOARD)

For example:

-L/opt/gel-hc1x-1.4.2/config/m68hc11-32k

and this configuration directory contains the memory.x file that corresponds to the m68hc11-32k generic board.

See Also What is the memory.x?

What does "region text is full" mean?

This error occurs when the program is too big to fit in the memory you specified in memory.x. I don't have your memory.x so I can't tell what is your limit but in the following example:

MEMORY
{
  page0 (rwx) : ORIGIN = 0x0, LENGTH = 256
  /* Board with 32K ROM.  */
  text  (rx)  : ORIGIN = 0x8000, LENGTH = 0x8000

  /* And 32K RAM. */
  data : ORIGIN = 0x1040, LENGTH = 0x8000-0x1040
}

the program memory is limited by the text region which is 32Kb.

This can happen if you try to link with too many files (whose total exceeds your text region size). It is often the case if you use the Newlib or use floating point operations. The -mshort and -fshort-double may help in reducing that but in many cases this is not enough.

The printf() which comes with Newlib is pretty big and as far as I'm concerned I prefer not to use it. However, it is useful for gcc validation.

Some tips:

  • Try compiling with -mshort -fshort-double -Os
  • Consider using linker relaxation with -mrelax
  • Try using garbage collection of sections
  • Avoid using Newlib and printf

See Also What is the memory.x? How can I remove unused functions? How do I tell the compiler to use direct addressing mode to access data

How can I put the soft registers in a specific section?

The soft registers are defined in the section .softregs. During the final link, this section is merged with either the .page0 section (68HC11) or the .bss section (68HC12).

You can change this by using your own linker script and putting the following definition:

 *(.softregs)

In the section you want.

For example, copy /usr/m6811-elf/lib/ldscripts/m68hc11elfb.x to your project.

Change:

.page0 :
 {
   *(.page0)
   *(.softregs)
 }  > page0

Into:

.page0 :
 {
   *(.page0)
 }  > page0

And add the *(.softregs) somewhere in .bss for example, like:

 .bss   :
 {
   __bss_start = .;
   *(.softregs)
   *(.sbss)
   *(.scommon)
   *(.dynbss)
   *(.bss)
   *(.bss.*)
   *(.gnu.linkonce.b.*)
   *(COMMON)
   PROVIDE (_end = .);
 }  > data

And link with your own linker script with: -Wl,--script,myscript.ld

Be sure to compile with -mrelax otherwise you might have errors during the link (addresses truncated because too big for direct addressing modes).

What is the page0 concept?

Some of this may just be my opinion, but...

The register space of the 68HC12 processor family (0x0 to 0x1ff) is not actually tied to the page0 concept. "page0" refers to the address space (an address which only uses the low 8 bits of the address. The address range in page0 is therefore 0x00 to 0xff. This only has meaning because certain optimizations are only possible if it is assumed that the address is only 8 bits, so it fits in a smaller instruction. Since HC12 has a complete 16-bit bus, this optimization is less significant. I used to say it might make sense if page0 was only defined to use part of page0 space (such as LENGTH=0x80) since the space included is page0, but I think in this linker FAQ seems to emphasize that it should be the full range [0x00..0xff].

One problem with having a page0 defined above 0xff is if any space outside (over 0xff) was assumed to be page 0 space just because it was placed in section .page0. For example the address is used and interpreted as only one byte by functions.

With HC12, you can still optimize by using page0. One difference is it has by default, hardware registers instead of scratch RAM in page0. In HC11, page0 ram was critical because it had to access fast variables often to work with 16-bit values. Now that HC12 can work with that more in mcu registers, it can further optimize speed by accessing most hardware registers in page0. You noticed the register size exceeds page0, but hopefully the more critical registers made it into the first 256 bytes. Also, since the MCU is meant for working i/o more than doing software calculations, this sounds more optimal anyway.

The purpose of the page0 region in "memory.x" is primarily for a space to place the critical variables in HC11, into the page0 ram. In HC12, it is possible the compiler/linker does not even use it. Since the default ldscript does not place .page0 variables into the page0 region, the region might be unused. I have deleted it and my project still worked, but I decided I should leave it there in case some new or unseen feature does use it later, or until the code manager (Stephane, or whoever works on gnu-m68hc1x) states that it can be removed. Besides, I think it's better to do less work to keep something working than to do more work to risk breaking it.

An important question is how to access those registers, then. If you have been placing their variable names and types into page0, then perhaps it is good enough to simply use a different region name, such as "hwreg", instead of "page0". If you still have the page0 region defined in the same place, it might overlap. You would have to delete the page0 line in "memory.x". The GEL example seems to recommend a more versatile method. Note that it does not even use a memory region for this. It defines a symbol to the linker, with the start addr of registers, called "_io_ports" (see src/libbsp/ports.s). In a C header, this is defined to a type (volatile unsigned char), which can then be accessed as an array. It can also be type cast to (volatile unsigned short) for 16-bit registers. This is the way I am acessing it.

Thanks for the input on this subject. --jeffs

Jefferson Smith imajeffs -- at -- hotmail.com

See Also What is the memory.x?

Memory Banks

What is the address problem with 68HC12 memory banks?

It is very easy to make gcc generate a CALL and use RTC. This can be driven by some option (in 68HC12 mode).

Now, the big problem is the linker which must handle the 24-bit address space. It must do so with a paging notion and an overlapping notion (of the 16K window). Today, the linker is only able to manage contiguous address space (I mean that it puts *.text code together and builds a VMA address incrementally without holes). In other words, it is able to build something that maps (loggically) to:

          0x8000   0xC000   0x10000    0x14000    0x18000
----------+--------+--------+----------+---------+------+
          |        |        |          |         |      |
----------+--------+--------+----------+---------+------+
          <------------------ text section ------------->

Some kind of address translation is necessary.

Let's say that [0x08000..0x0bfff] is page 0
	       [0x0C000..0x0ffff] is page 1
	       [0x10000..0x14000] is page 2
	       [0x14000..0x18000] is page 3   (from a GNU linker VMA point of view)

It is clear that from 68HC12 VMA point of view, when page 1 is installed, it is not at [0x0C000..0x0ffff] but really at [0x08000..0x0bfff]. But the linker does not know that and there is no easy way to tell him.

To give another example, a function can be defined at 0x10000 from GNU ld point of view. When a call to that function will be made, if there is not address translation, the call will use page 2 and 0x0000 as address. And this is not in the program bank window.

How can I link in several memory banks?

Releases especially between 2.2 and 3.x support memory bank switching for 68HC12. To load your program correctly, your upload tool must know the special mapping that the linker and gdb use, or the output can be converted to a standard S-record format.

Basically, everything between 0..0x00ffff (64K) is the normal 16bit address space which can/should be loaded as is (not paged).

Everything >= 0x10000 (the offset) represents banked mem and is multiplexed in a 16K "window" visible within the normal 16bit address space.

Note: There was previously a documented offset of 0x1000000, which first looks like a typo. There was actually an older release (2.1.1) which does use the higher offset.

The page number is given by the formula:

(addr - 0x10000) / 0x4000

(The 0x4000 is 16K length. The 'addr' 0x10000 corresponds to page0, 0x14000 to page1, 0x18000 to page2, ...)

Regarding older GCC release (2.1.1): The - 0x1000000 is annoying but it is necesary for the current implementation of the linker so that it links correctly.

To link in several areas, you must define several regions, like:

MEMORY
{
   page0 (rwx)    : ORIGIN = 0x0, LENGTH = 256
   text  (rx)     : ORIGIN = 0x0C000, LENGTH = 0x10000 - 0x0C000
   bank0 (rx)     : ORIGIN = 0x10000, LENGTH = 16K
   bank1 (rx)     : ORIGIN = 0x14000, LENGTH = 16K
   ...
   data           : ORIGIN = 0x001040, LENGTH = 0x08000 - 0x01040
}

And you must manage to put what you want in either text or bankN. For example:

   .text_low  :
   {
     /* Put startup code at beginning so that _start keeps same address. */
     /* Startup code.  */
     KEEP (*(.install0))	/* Section should setup the stack pointer.  */
     KEEP (*(.install1))	/* Place holder for applications.  */
     KEEP (*(.install2))	/* Optional installation of data sections in RAM.  */
     KEEP (*(.install3))	/* Place holder for applications.  */
     KEEP (*(.install4))	/* Section that calls the main.  */
     KEEP (*(.init))
     ebcs.o(.text)
     date-panel.o(.text)
     main-panel.o(.text)
     info-panel.o(.text)
     debug.o(.text)
   }  > text

And put the banked code using:

    .bank0 {
      my_file.o(.text)
      my_file.o(.text.*)
      my_file.o(.rodata)
      my_file.o(.rodata.*)
    } > bank0

Now, be careful that the current linker does not yet check for function that cross a page boundary. So, I would recommend to define as many banks as you need in MEMORY and give them the fixed size 16K. You can put several files in the same bank as long as it fits.

MC9S12DP256 Addressing Explained

Kim Lux

Diesel Research Inc.

November 24, 2004 Document Version 0.01: ROUGH DRAFT


The MC9S12DP256 is a paged memory device.

One can think of it as having 2 separate memory systems:

  • a non paged memory bank with addresses from 0x0000 to 0xffff
  • a paged memory bank with addresses from 0x00000 to 0xfffff

When operated in non paged mode, the non paged memory bank is accessible just like any other 64K linear addressed memory map. In non paged mode you cannot access any of the paged memory and PPAGE must be kept stationary at 0x30. (I need to verify this with non far addressing.)

This will yield the following memory map:

0x1000 to 0x3FFF: RAM
0x4000 to 0xFFFF: Flash ROM

Simple, right ?

Lets say that we need more memory than that, so we expand our system with some external RAM/ROM and start to utilize the rest of the 256K of flash as well as the RAM/ROM we add. Now things get a bit complicated.

The rest of the flash and the ROM/RAM we added are now accessed through a 16K section right in the middle of our 64K memory map. This section is called the page window, because it is the "hole" or "window" through which we peak to access the paged memory.

The paged memory map looks like this:

0x1000 to 0x3FFF: RAM
0x4000 to 0x7FFF: Flash ROM
0x8000 to 0xBFFF: paged memory window, where we access the paged memory.
0xC000 to 0xFFFF: Flash ROM

PLUS all the paged memory.

Which part of the paged memory shows in the paged memory window ?

The memory that appears in the paged memory window depends on the value of PPAGE:

0x00 to 0x2F: 1 page of 48 16K pages of externally addressed memory appears in the window. (768K in total) This could be ROM or RAM or any external hardware addressable devices that we tie to the 9S12 external address bus.

0x30 to 0x3F: 1 page of 16 16K pages of on chip flash memory appears in the window. (256K in total.)

There is one other little tidbit of information you need to know: 2 of the paged flash pages are shared with the non paged memory.

The flash memory on page 0x3E is the same memory that appears in the non paged memory map from 0x4000 to ox7FFF.

The flash memory on page 0x3F is the same memory that appears in the non paged memory map from 0xC000 to 0xFFFF.

The memory in these two overlapping pages can be accessed by either the non paged access method or the paged access method.


Loading Programs and Data on the 'DP256 via D-BUG12

NOTE: The following discussion assumes a linear load addressing model, not a banked load addressing model. For further details, consult Appendix A of the D-Bug12 v4.x.x Reference Guide.

DBUG12 provides 2 commands for loading data onto the DP256: load and fload.

Between the two commands, all the paged and non paged memory on the DP256 is accessible for loading.

The "load" command is used to program non flash areas of the DP256. It can load data into non flash memory located in the non paged or paged areas.

The "fload" command is used to program the flash areas of the DP256 memory map. It can load data into flash located in the non paged or paged areas.

Loading Non Flash Areas

To load data into the non paged memory area (non flash) of the DP256, one issues the command "load" and sends an S1 or S2 file with appropriately addressed data. Since the only non paged and non flash address area in the DP256 is 0x1000 to 0x3FFF, this is the only suitable range of memory that can be loaded this way. Note that is the default memory map. The non paged flash from 0x4000 to 0x7FFF can be disabled, and external RAM could be mapped there. Combining the internal RAM with the external, one can use "load" for the contiguous non paged block from 0x1000 to 0x7FFF.

To load data into the paged memory area of the DP256, one issues the command "load ;f" and sends a file of S2 records with the appropriate addressing. To simplify the code generation, in linear load addressing mode, the load command handles the control of the PPAGE register, making it transparent to the user. To do this, a mapping is used between the load address used in the SRecords and the ultimate machine address the data will sit at:

Load Address        PPAGE  (Decimal)
0x00000  to 0x3FFF   0x00   ( 0)
0x04000  to 0x7FFF   0x01   ( 1)
..
0xB8FFF  to 0xBFFFF  0x2E   (46)

So.. you generate a linear load map and load will take care of putting your data on the correct page.

To recap loading non flash areas:

"load" loads non flash non paged areas. "load ;f" loads non flash paged areas.

"load" with records at 0x1000 will load on chip RAM. 'load ;f" with records at 0x1000 will load page 0 of the paged ram.

Loading Flash Memory Areas

To load data into the non paged flash area of the DP256, one issues the command "fload ;b" and sends an S1 or S2 file with appropriately addressed data. Since the only non paged flash address area in the DP256 is 0x4000 to 0x7FFF and 0xC000 to 0xFFFF, these are the only suitable range of memory that can be loaded this way.

To load data into the paged flash memory area of the DP256, one issues the command "fload" and sends a file of S2 records with the appropriate addressing. To simplify the code generation, in linear load addressing mode, the fload command handles the control of the PPAGE register, making it transparent to the user. To do this, a mapping is used between the fload address used in the SRecords and the ultimate machine address the data will sit at:

fload Address		PPAGE	Flash Page    	
0xC0000 to 0xC3FFF	0x30	1
0xC4000 to 0xC7FFF	0x31	2
..
0xFC000	to 0xFFFFF	0x3F	16

So.. you generate a linear load map and fload will take care of putting your data on the correct page.

To recap loading flash areas:

"load ;b" loads non paged flash areas. "load" loads paged flash areas.


Remember how the two non paged flash areas overlapped with two of the paged flash areas ? Ie page 0x3E overlapped with 0x4000 to 0x7FFF and page 3F overlapped with 0xFC000 to 0xFFFFF ? Well, you can load those pages by either method.

Accessing The Loaded Data - Manually

The non paged data outside of the page window (0x8000 to 0xBFFF), including the non paged flash pages, ie 0x4000 to 0x7FFF and 0xC000 to 0xFFFF, is accessed just like any other data with no special care needed. For example, if you loaded data in flash at location 0x4004, you could access it with the address 0x4004.

It takes a bit more to access the paged data. The paged data is always read in the window at 0x8000 to 0xBFFF. What appears in that window depends on the PPAGE value. For example, to access the second byte of the first page of paged ram, one would set PPAGE to 0x00 and the data would then appear at the window base address plus the offset address, ie 0x8000 + 0x01 = 0x8001.

Likewise, to access the second byte of the first page of flash memory, one would set PPAGE to 0x30 and the data would then appear at the window base address plus the offset address ie 0x8000 + 0x01 = 0x8001.

Note that in both of these cases the 64K address we are using to access the data is the same, however the PPAGE controls what we actually get by controlling the page that appears in the window. (Ie RAM page 0 or flash page 0 in this case.)

It can get very tiresome thinking of paged memory as being on certain pages, so code generators generally generate (say that 3x fast...) linearly addressed data which then allows the user to use a formula to set PPAGE and the window address:

PPAGE = PagedLoadAddress / PPAGEWindowSize;

and

PageWindowAddress = (PageLoadAddress % PPAGEWindowSize) +
PPAGEWindowStart;

Where:

"/" is integer division "%" is modulus PPAGEWindows size = 16K = 0x4000 for the 'DP256 PPAFEWindowStart = 0x8000 for the 'DP256

The above equations work directly as "C" code.

Thus as long as you know where you loaded the data via the linear load address, you can retreive it quite easily.

Note that this address decoding works with the two pages of paged flash shared with the non paged memory map. If you stored data at linear load address 0xF8002, you will be able to retreive it with the above equations.

Please be aware that the PAGED load addresses are not the same as the NON PAGED load addresses. PAGED load address 0x1000 will be on the first page of external memory. NONPAGED load address 0x1000 is the start of on chip RAM.

As explained previously, the PAGED load address is loaded with "load ;f" and the NON PAGED load address is loaded with "load".

Using gcc with 'DP256 Paged Addressing

When correctly configured, gcc will generate a linear memory map that has paged and non paged components. Furthermore, gcc lets the user, via the "far" keyword, designate where various code and data resides, either in the non paged "near" areas or in the paged "far" areas.

Fortunately for developers, gcc handles the calculation and setting of the PPAGE register automatically when accessing "far" data and code.

<To be continued>

Using gdb with 'DP256 Paged Addressing

gdb has several tasks with paged and non paged memory implications when debugging a 'DP256 device:

  • to program data on the DP256 on behalf of the gdb client
  • to read data on the DP256 device on behalf of the gdb client


<to be continued>


-- Kim Lux (Mr.) Diesel Research Inc

Optimizations

How do I tell the compiler to use direct addressing mode to access data

The compiler does not generate direct addressing modes to access global variables. However, the linker is able to transform the extended addressing in a direct page addressing when the address is in page0.

This transformation is called linker relaxation. It is enabled by the -mrelax option.

You should use the -mrelax option when compiling and linking.

How can I remove unused functions?

The GNU linker has a garbage collector of ELF sections. This garbage collector can remove all ELF sections which are not used by the program.

To remove unused functions, it is necessary to tell the compiler gcc to put each function in a separate .text section. To do this, you must use the following option:

m6811-elf-gcc -Os -ffunction-sections ....

Then, at link time, you can pass the following option:

-Wl,--gc-sections

The linker will garbage collect all unused functions and remove them.

So this way, I can use just a few functions each from various libraries
(gcc included, third party, and my own), but have my compiled program
not be HUGE.

Yes but take care that if there is a reference to a function somewhere, it can drag (import) many others and the GC will do nothing (due to the initial function being referenced).

Misc

Where is _io_ports defined?

This symbol is defined at link time. The examples pass the option -Wl,-defsym,_io_ports=0x1000 which tells the linker to define the symbol and give it the address 0x1000.

It is also possible to define that symbol in linker scripts and use:

PROVIDE(_io_ports=0x1000)

You can do this in memory.x part of the linker script.

What does "linking files compiled for 16-bit integers and others for 32-bit integers" mean?

When you obtain the following error during a link:

file.o: linking files compiled for 16-bit integers (-mshort) \
          and others for 32-bit integers

it means that you compiled or assembled one part of your project with 32-bit integers and another part with 16-bit integers. Since the ABI is different the linker complains. Such ABI incompatibility was not detected by release 1.x. This error detection will prevent you from strange execution errors like passing a 32-bit integer on the stack while expecting a 16-bit integer. The function will only see the high part only thus resulting in strange behavior. This error is now detected for your safety.

In general this is caused by the fact that you assembled some file with m6811-elf-as and you didn't pass the important -mshort flag to it. It is recommended to assemble with m6811-elf-gcc and pass the same flags as for a C or C++ compilation.

also useful is -mshort-double for as, -fshort-double for gcc. This flag prevents "file.o: linking files compiled for 32-bit doubles and others for 64-bit doubles"

What is the crt0 and how can I use or remove it?

The startup file crt0 is used to initialize the stack, install the data section (initialized variables), clear the bss section (uninitialized global and static variables), prepare and jump to the main. The startup file is automatically linked at beginning of each program. The normal way to use the crt0.s is to use the symbol _start as the reset vector, a 16-bit word usually stored at address 0xfffe/0xffff.

It is possible to avoid to use the default crt0 by doing the following:

  • Write your own startup code. This can be written in C or in assembly. The entry point should be named _start.
  • Pass the -nostartfiles option to m6811-elf-gcc during the final link. This option suppresses the link with the crt0 file.
Personal tools