Compiling for stm8 with sdcc version 4.2.0 (latest release):
If I declare a variable as such:
const char __at(0x8080) Special[4] = {0x00,0x00,0x83,0x83};
The linker overwrites code, causing the program to crash.
Location of code in rst file:
00807F 91 00002$:
00807F AE 00 00 [ 2] 92 ldw x, #l_INITIALIZER
008082 27 09 [ 1] 93 jreq 00004$
008084 94 00003$:
Location of variable in rst file:
008080 204 .org 0x8080
008080 205 _Special:
008080 00 206 .db #0x00 ; 0
008081 00 207 .db #0x00 ; 0
008082 83 208 .db #0x83 ; 131
008083 83 209 .db #0x83 ; 131
The variable named "Special" starting at 0x8080 overwrites code created earlier at 0x8082. Why doesn't the linker work around the const variable?
How do I prevent this from happening? Am I doing something wrong, making an incorrect assumption?
According to the sdcc manual, paragraph 3.5.6 Absolute Addressing, on page 45, "If however you provide an initializer actual memory allocation will take place and overlaps will be detected by the linker". I read this to mean that the linker will locate code around this initialized absolute data.
The attached c code handles a trap if the code stumbles on the overlapped const data. Sure enough, I see the trap happen when monitoring the output pins. C code comments have compile commands.
I just noticed something else: the highest interrupt vector declared in my code appears at 0x806C. However, the hardware's interrupt vector table doesn't actually end until 0x807F and the first byte of code should appear at 0x8080. Why is the linker placing code at address 0x8073? This is 3 bytes into interrupt vector 26 (SPI TX empty)!
Incidentally, the device I'm programming is the STM8L101F3. On STM8S devices such as STM8S003 and STM8S103, the area between 0x806C and 0x807F is reserved and there shouldn't be any code there anyway!
I just tried adding a handler for interrupt vector 29 and now the startup segment starts at address0x8083, with 3 bytes of unused flash between the end of the interrupt vector table and the beginning of the startup code. Why this value? Can I bump it up to0x8084? How do I do this?
I also tried
-Wl-bGSINIT=0x8084 -Wl-bCODE=0x8084and everything moved out of the way except for__sdcc_program_startupwhich ends up at 0x8080.Unfortunately, if I move HOME, the interrupt vector table moves as well which is also unexpected.
This would be useful for me in the z80 front-end. Some systems cartridges need a header/checksum at the end of 32K, even though the cartridge may be up to 48K in size.
Last edit: Under4Mhz 2022-10-04
I'm afraid the linker is in lesser shape for stm8 than it is for mcs51. When I wrote that piece of the manual I had mcs51 in mind.
I don't think the problem is in the linker; rather it looks more like a code generation problem. The compiler needs to separate the startup area from the interrupt vector table area. The linker command can then be modified for edge cases such as this.
I came up with a bit of a hacky little workaround for my little project. I wrote a script that compiles to asm and then massages the asm file before assembling and linking. It seems to work OK for my monolithic 1 file project but something more intelligent would be needed as a workaround for a mutiple file project. I've included it for your reading enjoyment.
Of course, it would be much better if the compiler were fixed.
Last edit: Allan Macdonald 2022-10-13