GCC ARM32 32F417 how to call or jump to a specific address? - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: June 13, 2022, 04:22:12 pm
content: Of course, the "answers" are all over the internet, but none of them compile, or (if they compile) none work.I have what is basically an overlay at FLASH base+32k ie.. 0x08008000 and need to jump to that address.The address is actually a symbol from the linkfile called FLASH_APP.I see examples likeldr r3, =JMPADDRbx r3and have been trying various forms of stuff like asm volatile("bx %0" : : "r" (0x08008000)); but nothing works. I spent decades coding in asm so this should be simpleI am using GCC v10. Many thanks for any tips.

author: emece67
author_rank: Frequent Contributor
timestamp: June 13, 2022, 04:33:32 pm
content: .

author: peter-h
author_rank: Super Contributor
timestamp: June 13, 2022, 05:08:21 pm
content: That doesn't appear to go to a fixed absolute address:

author: emece67
author_rank: Frequent Contributor
timestamp: June 13, 2022, 05:20:29 pm
content: .

author: SiliconWizard
author_rank: Super Contributor
timestamp: June 13, 2022, 05:50:15 pm
content: If you're using GCC, you can also use computed gotos:http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.htmlOf course, this is not standard C, and if the pointer points to an absolute address rather than "the address of a label", the behavior *may* be undefined. Not sure.But it's just a "fun" feature to know, and it seems to "work":https://godbolt.org/z/bfdMMaczv

author: pcprogrammer
author_rank: Super Contributor
timestamp: June 13, 2022, 05:51:33 pm
content: Try thisCode://Start the main programunsigned int address = 0x80000000;__asm__ __volatile__ ("mov pc, %0\n" :"=r"(address):"0"(address));I have used this in the FNIRSI-1013D bootloader.

author: ataradov
author_rank: Super Contributor
timestamp: June 13, 2022, 06:03:18 pm
content: Pretty typical stuff from bootloaders:Code:asm("bx %0"::"r" (0x08008000));No need for extra stuff, let the compiler figure it out. GCC inline assembly is incredibly flexible and powerful.

author: peter-h
author_rank: Super Contributor
timestamp: June 13, 2022, 06:35:48 pm
content: Unfortunately none of this works.This C code works, and has been for ages. The module main.o is located (in the linkfile) to FLASH_APP which is 0x8008000, and this does itand main() is this stubAnd I could just do the above, because if FLASH_APP changes then it will all still work.But if I use asmR3 has the right valuebut then it bombs, and same if I use 0x8008001.I saw C generates bl whereas the asm used bx, but bl produces a compiler error

author: ataradov
author_rank: Super Contributor
timestamp: June 13, 2022, 06:40:50 pm
content: You have to use the value with 1 in LSB, of course for the "bx" instruction. It absolutely works, so you are doing something wrong. So, what do you mean by "bombs". What actually happens to the PC?There is no "bl" instruction that takes a register as an argument, only immediate value. But either one should work if your offset is constant.Do you have raw code or a standard vector table at 0x08008000? If later, then the approach is entirely different.If you have a typical application linked at that address, then the code to run it would be something like this:Code:static void run_application(void){uint32_t msp = *(uint32_t *)(0x08008000);uint32_t reset_vector = *(uint32_t *)(0x08008004);__set_MSP(msp);asm("bx %0"::"r" (reset_vector));}

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: June 13, 2022, 06:42:56 pm
content: I'm no C expert but i think you can do this by casting and function pointers.This is what I use in PIC32 (MIPS) bootloaders, copied from Microchip's sample code - I would imagine ARM may be similar.void JumpToApp(void) {void (*fptr)(void);fptr = (void (*)(void))USER_APP_RESET_ADDRESS;fptr();}

author: peter-h
author_rank: Super Contributor
timestamp: June 13, 2022, 06:47:38 pm
content: What seems to be happening is that both 8008000 and 8008001 does the same job, but the code I am jumping to is getting removed by the compiler because it is no longer referencedNot sure how to stop that, other than put in some spurious code.

author: ataradov
author_rank: Super Contributor
timestamp: June 13, 2022, 06:50:33 pm
content: How exactly are you placing it there? You really need to be more specific, we can't guess what you are doing.And if it comes from the same project, then why not just call it directly? It will be used in that case, and you don't need to do anything else.Can you explain what you are actually trying to achieve? You seem to be over complicating things, as usual.

author: peter-h
author_rank: Super Contributor
timestamp: June 13, 2022, 07:29:52 pm
content: I am trying to keep explanations simple because I learnt that any post longer than x lines never gets a replyThe internet is full of desperate people posting huge amount of code and 99% never get a reply and disappear. The result is a lot of google hits and a load of clickbait.The code I am trying to jump to is like this in the linkfile:FLASH_APP is 0x8008000.I noted that if I use the asm jump form, the stub disappears and the jump goes to the vector table instead which follows it. This is despite the KEEP directives you can see there.Yes it is the same project but the code after base+32k is customer-replaceable. So it is a kind of an overlay.I can go back to my old method but the same disappearing-code issue will surface when compiling the overlay... nothing will be referencing its base so it will get optimised away too.

author: ataradov
author_rank: Super Contributor
timestamp: June 13, 2022, 08:04:26 pm
content: If it is user-replaceable, why not keep it in the separate project? Once you want to replace it, you will have to separate the code anyway. And then the whole thing just becomes a standard bootloader.How are you checking that it is eliminated? How are you preparing the loadable image? Are you sure your programming tools can handle multiple sections like this?I have no idea about linker syntax for individual files, to me this always seemed like an avoidable and unnecessary hack. You may need to add KEEPs around other statements there. But again, double check that your tools can deal with the resulting multi-section files.

author: Nominal Animal
author_rank: Super Contributor
timestamp: June 13, 2022, 08:23:39 pm
content: Quote from: peter-h on June 13, 2022, 06:47:38 pmSince you are using GCC, mark the target function(s)__attribute__((used)). In other words:__attribute__((used))return_typefunction_name(argument_list...) { ... }Edited to add: Since it is intended to be user-replaceable and never returns and doesn't take any arguments, you can do e.g.__attribute__((used, weak, noreturn)) voidfunction_name(void) { for ( ; ; ) /* nothing */; }Any other definition offunction_namewill override that "default" implementation.

author: peter-h
author_rank: Super Contributor
timestamp: June 13, 2022, 08:24:49 pm
content: It is eliminated, probably by the linker but I am not sure. I can see it is eliminated because at 0x8008000 I can see the next module (a vector table, which is 0x200-aligned as you know).I am successfully doing the jump now, using some code based on code used on the RAM-resident boot loader which I developed about a year ago, with much help here:and with this I do indeed get a jump to 0x8008000Now I need to work out how to prevent that stub from getting eliminated. Tried all kinds of stuff but none works. The KEEP attribute in the linkfile does nothing. Problem is that there are so many different versions of these tools

author: ataradov
author_rank: Super Contributor
timestamp: June 13, 2022, 08:30:00 pm
content: I don't get it. Your base project has main() too? Why not name this entry_main() and just call entry_main() from the regular code? Then in the linker place entry_main at whatever offset you want.You are really making it more complicated than it has to be.But again, what are you going to do then it is time to extract the user section? You'd need to have a way to compiler and link it separate from the main project, or some way to extract it. But if you are building all the code at the same time anyway, why does it matter where it is located?Also, why even have this dummy main()? What you are doing is literally what bootloaders do. Place the user vector table at the offset 0x08008000 and use the code I previously provided.

author: SiliconWizard
author_rank: Super Contributor
timestamp: June 13, 2022, 08:30:17 pm
content: GCC might prune unused static functions, but not non-static ones.LD will not prune the code for unused functions either on its own.Unless you instruct them to do so. Typically using the '-ffunction-sections' option for GCC and '--gc-sections' for the linker.If those options are set, then I'm not sure there is any other way of bypassing this pruning than what Nominal just suggested above.

author: peter-h
author_rank: Super Contributor
timestamp: June 13, 2022, 08:40:36 pm
content: The boot block, 0x8008000-0x8007FFF, gets entered via the standard vector table. It does various stuff and jumps (or calls; it doesn't matter) the replaceable module at 0x8008000.Thus far, for the year+ I've been working on this, I have been compiling the two parts together. But in the future somebody will have a Cube installation which generates only the replaceable module, origin 0x8008000. And he will have a main() because he, like all C coders, expect to have a main()This is not rocket science...And it is possible that the main() in this second development environment will not get optimised away. Partly because a compiler which removes main() is going to cause a load of problemsand partly because, on an ARM32, main() is pointed to by a jump in the vector table, in the startup.s file.The problem is that in my current project this whole stub is getting removed.Why is it a stub? So I can use the linkfile to place it at 0x8008000, while making sure there is no .h file #included at the start which just happens to contain somecode. Only a simple clean stub achieves this.

author: ataradov
author_rank: Super Contributor
timestamp: June 13, 2022, 08:46:15 pm
content: Yes, this is not a rocket science. As I said, what you are doing is the standard way all bootloaders work. Just link the user vector table at the beginning of the 0x8000 block and jump there by reading the SP value and reset vector. Reset vector would be your entry point. If you don't need initialization, you can skip it in the reset handler and just jump to the application main().You are not doing anything special, just a really bad way to implement a bootloader.But you also have to think though how all this works with the memory allocation. If you are building this as one project, then SRAM occupied by the boot code would not be available to the user code. If you build them separately, then you need to do exactly what I described.

author: Nominal Animal
author_rank: Super Contributor
timestamp: June 13, 2022, 09:02:08 pm
content: Quote from: peter-h on June 13, 2022, 08:24:49 pmBe sure, by checking the object (.o) file, via e.g.objdump -tname.oorreadelf -sname.o. They list even local objects (those without external linkage).If they are missing from the intermediate object file, then it is the compiler that never emitted the code for the function, and__attribute__((used))should fix it. If they are only missing from the final object file (before being converted to hex format), then it is the linker that threw them away.Quote from: peter-h on June 13, 2022, 08:40:36 pmNo, but understanding and correctly using theweakandusedfunctionandvariableattributes is key here.A real stub function, when using GCC, is therefore usuallyrettype__name(arglist) { /* default implementation */ }rettypename(arglist) __attribute__((used, weak, alias ("__name")));which lets anyone reimplement thename()function, but still have access to the default__name()version also. Many projects use a macro (taking the return type, name, argument list with surrounding parentheses, and the default implementation body as parameters) if there are a few of these.peter-h, note that I edited my previous post after you posted your previous post; there might be something useful there for you.

author: peter-h
author_rank: Super Contributor
timestamp: June 13, 2022, 09:30:59 pm
content: QuoteIndeed; the 32k boot block is standalone. It cannot call functions outside of itself, and outside code cannot call anything in it (except the RAM-based boot loader, about 3k, which lives within this boot block and gets copied from there to RAM, just below the top of RAM, does its job, and the whole thing reboots. And all that works).QuoteThe main_real stuff is in the .map file in the right placeso yeah it looks like the linker has removed it because this is what is in thereThe (used) attrib doesn't work, unless I am not using it right (see posts above).

author: ataradov
author_rank: Super Contributor
timestamp: June 13, 2022, 09:35:43 pm
content: Are you sure that your .text sections from the KDE_main_stub.o did not get collected by previous more generic statements? Linker scripts are processed linearly. If there is a more generic statement first, it will collect the values. The following statement would be useless.But again, what you are doing is very bad and will cause users to curse you. There is no need for this. What you are doing is a very well solved problem with known solutions.

author: peter-h
author_rank: Super Contributor
timestamp: June 13, 2022, 09:47:24 pm
content: See my post above. KDE_main_stub is shown (in the .map file) at 0x8008000, which is right. It's just that there isn't any code at that address, so that function has been stripped out, presumably because nothing is calling it.You are right; this is a funny way to do it. I think the problem appears because I am trying to build the whole project (boot block and the "overlay") in one go, which absolutely does work, not least because the boot blockcallsthe first function in the overlay (main()), whereas if I create a separate Cube "devkit" for somebody, which just builds the "overlay", located at 0x8008000, they won't have the problem because, one hopes, nothing is going to strip outtheirmain(). But why will their main() not get stripped out? My guess is that in the arm32/gcc setup, a vector is always pointing at main(), so main() can be anywhere within the FLASH.So what can I do to prevent main() getting stripped out here? I've tried everything I can think of. Maybe I should point some unused vector (table at 0x8008000+0x200) at itNot kidding - that does actually work! Asm code is not optimised out. But causes some other issue. Another way which works is to reference main() with this, where the code after the asm(... is never executedUnfortunately both above methods cause a hard fault handler IRQ0, after this pointwhere the code looks plausible, SP is 0x2001fd98 which is ok... This is the hard fault dataI wonder if this hex dump of memory at 8008000 gives a clue. Bit 1 is not 1, is it?The trap is generated by the push r7, lr instruction. r7=0, lr=0x800163d. Is that a write into FLASH? Maybe in GCC C you cannot just jump to a C function. Maybe some setup is needed first. But I struggle to see a significant difference between jumping to main() and calling main() from C, in terms of register values.It is really stupid for a compiler to be so determined to remove code. I am running the project with -Og, but this module has the -O0 attribute. This is GCC 10. Maybe they were running out of ideas after GCC9?This stuff used to be so easy.ORG 0x100some codeORG 0x8000some codeORG 0xF000some codeetcand it all worked.

author: ataradov
author_rank: Super Contributor
timestamp: June 13, 2022, 10:34:58 pm
content: No, your map shows the file, but it does not show that any functions from this file were included in the current section, possibly because they all were already collected in previous sections. Or your matching filters for ".text" and other stuff did not match anything.This stuff is still easy, just done a bit differently. And you completely refuse to acknowledge that difference.Let them build overlay with the vector table at the beginning, so they won't have to do the same stupidity as you are doing here. And treat that application as any normal bootloader would. And all they would need to do is specify a new offset for the code without changing anything else in their application.And you can do the same - just place the new vector table at that address and everything else would used automatically because it is refereed from the vector table.

author: peter-h
author_rank: Super Contributor
timestamp: June 14, 2022, 06:42:24 am
content: QuoteThis is not happening. The .map file shows KDE_main_stub at 0x8008000, or not, according to whether the entry point main() is referenced somewhere, or not.Is it actually possible to do a simple asm jump to an address of a GCC function, without setting up some registers etc?

author: ataradov
author_rank: Super Contributor
timestamp: June 14, 2022, 06:51:46 am
content: Quote from: peter-h on June 14, 2022, 06:42:24 amWhere? In the screenshots you showed it shows that KDE_main_stub.o is at that address with no code used from this file. The same file can provide code for multiple locations.Quote from: peter-h on June 14, 2022, 06:42:24 amThis does jump to a (void) functionCode:asm("bx %0"::"r" (addr));addr must have LSB set.Or, if address is fixed then "bl" instruction would do the same.

author: Fungus
author_rank: Super Contributor
timestamp: June 14, 2022, 06:54:04 am
content: Quote from: peter-h on June 13, 2022, 04:22:12 pmPush it on the stack and do a RET.

author: peter-h
author_rank: Super Contributor
timestamp: June 14, 2022, 07:04:06 am
content: QuoteMain() is an "int" not a "void". I can't change that; the compilerrequiresa function called "main" to be "int"QuoteThismatters. Now it works. Previously I tested with and without, and was misguided by this example aboveThe compiler doesnot"figure it out".But the bit in yellow (which never runs) is still needed to prevent the target code disappearing.

author: ataradov
author_rank: Super Contributor
timestamp: June 14, 2022, 07:08:20 am
content: Quote from: peter-h on June 14, 2022, 07:04:06 amI meant the arguments. Return type does not matter, but you have to fill out the arguments if the function takes them.Quote from: peter-h on June 14, 2022, 07:04:06 amWhy would it? This is a behaviour specific to the implementation. On device with full ARM/Thumb-2 support either one will work and will switch the mode. On Thump-2 only devices you must remain in the Thumb-2 state, so LSB=1 is mandatory on all calls. Otherwise you will get INSTATE exception.But again, let me advocate for your customers. What you are doing is just stupid. Don't do this. Place the vector table directly at the 0x08008000 address and jump there as a normal bootloader would. This way customers won't have to see with linker scripts.

author: peter-h
author_rank: Super Contributor
timestamp: June 14, 2022, 07:12:49 am
content: What arguments does aint main(void)have, and how do I set them up in assembler?QuoteYes; that would address the code removal because main() will have something pointing to it, which normally happens in a ST arm32 environment. Thanks.

author: ataradov
author_rank: Super Contributor
timestamp: June 14, 2022, 07:14:31 am
content: Quote from: peter-h on June 14, 2022, 07:12:49 amNone. That's why I said it must be a (void) function. You don't need to do anything extra.

author: peter-h
author_rank: Super Contributor
timestamp: June 14, 2022, 07:58:41 am
content: OK, thanks. This all works now.I actually did it a bit differently. This is the vector table in the "overlay". It is a copy of the standard table.The _estack word should be redundant since SP is already set to top of RAM from the boot block, but putting 0 in there bombs it all, and I found it is required because the RTOS directly fetches that value for its internal useThe reset_handler is indeed redundant and I can't see it being used because a reset also clears VTOR, so the table in the boot block gets used instead, which is as intended.If one did this scheme where the vector table, instead of main(), is at thebaseof the overlay, one would have to enter the overlay by faking a hardware reset (but without changing VTOR - how?) to produce a jump to the 2nd vector and that would say.word mainOr one would read out the 2nd vector value with code, and jmp to that, which doesn't actually get you anything.I eliminated the code disappearance withso there is a permanent (if unused) reference to main() which is non-removable because it is in an asm file.The reason what I am doing seems weird is partly because I am not fully familiar with the arm32 environment (who is, at ~10k pages?) and partly because I am juggling multiple objectives which I have not posted here, partly for brevity and partly because I don't want to tell you all what I am developingThe reason why the various "keep" directives (in the C source and in the linkfile) seem to do nothing, remains unsolved. But I am not surprised; most websites that list compiler and linker directives are duff because there are so many versions. One can spend literally days on that.

author: ataradov
author_rank: Super Contributor
timestamp: June 14, 2022, 04:17:25 pm
content: SP is not redundant, If the code is compiled in the outside project, the project may want to place SP in a different place.Same with the reset handler. The secondary project will need to initialize its own memory. You are thinking about your embedded project. But this will not work with the standalone project at all.Right now it all works because reset handler of your main project loads the data for the initialized variables from the second part. But as soon as you replace the second part, none of this data would be valid.Do not deviate from standard stuff. You are just going to miss something like this. You need to have both values.And it is much better to develop systems like this with the outside project first and then implement embedded projects. You will immediately see all the issues I'm describing.

author: peter-h
author_rank: Super Contributor
timestamp: June 14, 2022, 05:27:07 pm
content: Not quite, but I can see I didn't describe it fully.The boot blockalwaysgets entered after a reset. There is no way for a 32F4 to do otherwise, because VTOR is set to zero.The boot block isstandalone; it doesn't call anything outside and nothing outside of it calls it. There is no variable sharing either. No data is passed between the two (except via data in a serial FLASH).When control is passed to the "overlay" the SP is set to top of the RAM. C doesn't move the SP. That stack is also used for interrupts. FreeRTOS sets up its own task stacks in a chunk of RAM you give it and it does its own SP switching there.The "overlay" never runs standalone. The boot block always runs first, and initialises most of the hardware.This separation was done to enable later replacement of the "overlay".I've designed other products like this. In one of them, the "overlay" can call functions in the boot block (due to memory limits, the boot block contained bulky functions like float maths) but to prevent obvious address problems across code versions, the boot block has a function address table in a fixed location and calls go via that. On this product there is no such function sharing.I had some fun and games e.g. there was a data copy loop in the boot block which the compiler detected could be replaced with a memcpy() but memcpy is in a huge code library which wasn't in the boot block. There is probably a way to check for this by doing sed/grep on the symbol table. So that function is compiled with a -O0 attribute.In a scenario where I know the overlay willneverbe replaced, I would build the project as a single project, but the above mentioned separation can be maintained even though it is not needed.One reason for the boot block is to prevent a corrupted (or buggy) overlay from bricking the device. A "standard factory overlay" will always be available in a serial FLASH from where it can be restored.

author: ataradov
author_rank: Super Contributor
timestamp: June 14, 2022, 05:36:02 pm
content: I understand you perfectly and saying that you are wrong. If you put an arbitrary standalone project binary into the second part, but skip the reset handler, what code would be initializing the global variables for that code? You main boot program can't do that because it does not know anything about that binary. So, if you jump into the main() directly, all your BSS memory would not be zeroed out and initialized memory would not be initialized.SP is less critical, but it is still a good idea to give control over it to the application. It might not want to have it at the end of SRAM. The binary may want to reserve some SRAM at the end.

author: peter-h
author_rank: Super Contributor
timestamp: June 14, 2022, 06:29:33 pm
content: Indeed, any development environment which generatesjustthe "overlay" would have code in main() which sets up the BSS and the initialised statics.I had the same issue with the boot loader. There, it was done in C:Code:extern char _loader_start;extern char _loader_end;extern char _loader_loadaddr;extern char _loader_bss_start;extern char _loader_bss_end;// Copy loader code to RAM. This also sets up its initialised data.B_memcpy(&_loader_start, &_loader_loadaddr, &_loader_end - &_loader_start);// Clear loader's BSS and COMMONB_memset(&_loader_bss_start, 0, &_loader_bss_end - &_loader_bss_start);extern void loader_entry() __attribute__((long_call));loader_entry();// never get here (loader always reboots)for (;;);

author: ataradov
author_rank: Super Contributor
timestamp: June 14, 2022, 06:38:05 pm
content: Quote from: peter-h on June 14, 2022, 06:29:33 pmBut why? All the reset handler is doing is this initialization and a call to main, except if you instruct people to place main() a a reset vector, you will also have to explain to them rules for BSS and data initialization and have them copy the code from the reset handler. And all you had to do to avoid all this is literally do nothing. Just keep the reset handler in the vector table and everything will work with standard environments.All you have here is a standard bootloader, just behave like normal bootloaders behave and people will have no issues using your stuff. But you are inventing something that would be confusing to anyone familiar with how any of this works.

author: peter-h
author_rank: Super Contributor
timestamp: June 14, 2022, 07:38:03 pm
content: A development environment which builds just the overlay would be the same as my current one, minus the boot block.

author: ataradov
author_rank: Super Contributor
timestamp: June 14, 2022, 07:53:04 pm
content: Why are you mandating the development environment? Why not let user pick what they want to do?Anyway, do whatever you want, I'm not going to be the user, and that makes me happy.

author: peter-h
author_rank: Super Contributor
timestamp: June 14, 2022, 08:26:41 pm
content: This is a product which my little company will have to support (possibly for a long time; it's been running since 1991) and not everybody is as clever as youI certainly am not. But I much prefer working with people who are much more clever than I am, so if I am looking for somebody one day I will give you a shoutThanks everyone for all your help. It's running fine now.