Building/running a ThreadX Module under GCC where there are pointers to global variables
We are using ThreadX and ThreadX Modules in our product, have been doing so for many years. So far we have always built under ARMCC. We now need to build the ThreadX Modules under GCC (12.2 release 1).
We have followed the form of your example (i.e. compilation switches -fpie -fno-plt -mno-pic-data-is-text-relative -msingle-pic-base, no particular linker switches) and in general the code runs, however there is a specific case where these compilation flags do NOT work: when taking the address of a global variable. For instance:
char buffer[100];
char* pBuffer = buffer;
int appModMain()
{
printf("buffer address: %p, buffer address via pBuffer: %p \n", buffer, pBuffer);
}
...results in a printed output of the form:
buffer address: 60C8C25C, buffer address via pBuffer: 1000025C
In other words pBuffer is not being offset correctly. This is clear when you look at the disassembled code:
00000188 <appModMain>:
188: b580 push {r7, lr}
18a: af00 add r7, sp, #0
18c: 4b1a ldr r3, [pc, #104] ; (1f8 <appModMain+0x70>)
18e: f859 3003 ldr.w r3, [r9, r3]
192: 681b ldr r3, [r3, #0]
194: 461a mov r2, r3
196: 4b19 ldr r3, [pc, #100] ; (1fc <appModMain+0x74>)
198: f859 3003 ldr.w r3, [r9, r3]
19c: 4619 mov r1, r3
19e: 4b18 ldr r3, [pc, #96] ; (200 <appModMain+0x78>)
1a0: f859 3003 ldr.w r3, [r9, r3]
1a4: 4618 mov r0, r3
1a6: f000 f8bb bl 320 <printf>
1aa: f640 30b8 movw r0, #3000 ; 0xbb8
When loading-up the variable to put into r2, which is going to be passed to printf() as pBuffer, r3 needs to be adjusted, yet it is not (detail below).
We've tried various permutations of -mpic-data-is-text-relative/-mnopic-data-is-text-relative and -msingle-pic-base/-mnosingle-pic-base but none of them make a difference: we just cannot make a pointer reference to a global variable work. Unfortunately we can't change the source code we are compiling, there's quite a large amount of it and some if it is not ours; and of course this is perfectly valid C code.
Can you suggest how to compile a ThreadX Module under GCC so that it runs correctly?
Detail:
In the disassembled output, the contents of 1f8 and 1fc for the example above are:
1f8: 000001bc
1fc: 000001b0
...in the .got section of the disassembled output, these offset are towards the end, here:
Disassembly of section .got:
00006b88 <__ctors_end__>:
...
6d34: 1002c5a8
6d38: 1000025c ; 6b88 + 1b0
6d3c: 00001e15
6d40: 1002c5ac
6d44: 100001d0 ; 6b88 + 1bc
...and 100001d0 in the disassembled output turns out to be:
Disassembly of section .data:
...
100001d0 <pBuffer>:
100001d0: 1000025c
...while 1000025c in the disassembled output turns out to be:
Disassembly of section .bss:
...
1000025c <buffer>:
...
So pBuffer is initialised by the compiler to the un-offsetted address of buffer, and the compiler knows that pBuffer it is a pointer to something in the GOT, yet it did not do anything to add the offset when it generated the code.
Note: corrected the GCC version above, we are using the latest version, 12.2 release 1.
In case it is useful, there is a hint in this Stack Overflow question, where the "failing" code has a similar construct to ours, that there is an initialisation function that should be run as part of glibc which performs offset correction.
Is it possible that this is somehow missing? Note that we do NOT specify -nostdlib.
Also in case it is useful, we raised this with GCC in case they could be of help. They have so far suggested that a .reloc section should be present in the image to support this but there is no such section in our full disassembled output attached here.
Any views on this? We have concluded, so far, that it does not seem to be possible to compile C code under GCC that will work with ThreadX Modules. It would be really nice if you could tell us we're wrong :-).
<prod \>
Hi @RobMeades, I'm no longer on the Azure RTOS team, but I know Modules better than anyone. I don't recall ever testing something like this out (a global pointer to a global). I think either the linker settings need to be changed such that it will output a reloc section and/or the GOT needs to be fixed up. Perhaps the compiler libraries that do this aren't linked in because when a module starts, instead of calling cstartup, we call our own function: https://github.com/azure-rtos/threadx/blob/master/ports_module/cortex_m4/gnu/example_build/gcc_setup.s This may need to be modified to fix your issue.
@goldscott: thanks for responding, apologies for my delay in replying, I was away. Three (:-)) questions really:
a) am I correct that ThreadX Modules is intended to support compiling modules under GCC? What we're writing here is perfectly valid C code, it just doesn't work in a module. b) if (a) is true, I would hope that ThreadX/Azure/whoever would like to make this work; we have tried, see the analysis and links above, and concluded that it is actually impossible, at least without some script or other to parse output files and then modify the binary somehow or other. We'd like to be proved wrong :-). c) final thing - you are the person this issue is allocated to but, if you're not on the Azure RTOS team, I guess it should be assigned to someone who is. How do we do that?
Just as an FYI, we (u-blox) do pay ThreadZ/Azure/whoever for support.
@RobMeades , since you have paid for Azure support, could you create a support ticket here? Thanks! https://azure.microsoft.com/en-us/support/create-ticket/
@TiejunMS: hi, and thanks for the swift response. I am able to log-in to that link but I end up in a very basic Microsoft Azure portal that I expect we all get as employees of a company that use Azure for day-to-day things: I can only ask questions about billing and subscriptions, there is no linkage whatsoever to ThreadX and that support contract.
How do I go about asking [an extremely detailed] technical question about the ThreadX [embedded firmware] through that mechanism?
Here is the step:
-
Select issue type to "Technical" as shown below.

-
Choose "All services" and select "Service type" to "Azure RTOS"

Hmm, I don't get that option I'm afraid:

Just FYI, we've paid what was ThreadX for support for many many years (10's of them). In the last five years we have also adopted a lot of other Azure stuff for day to day business, so my account will take me to that latter. How do I get to the former?
@TiejunMS: just @,ing you in case you didn't receive the above.
Let me check internally. Our team will be on holiday for the next few days. Please expect a delay on response. Thanks for your patience!
@TiejunMS: no worries, we will be the same, have a good few days off :-).
@TiejunMS: purely FYI, I've asked the guy who is technically responsible for ThreadX in our company to do the same thing and he gets the same options as me.
<bump >
@TiejunMS
@RobMeades , we will look into this issue and let you know when there is update.
@TiejunMS: thanks very much, I will enquire again as to your thoughts in a few weeks.
Hi, @RobMeades, We create a module sample project with stmcube( 1.10.1) (https://www.st.com/en/development-tools/stm32cubeide.html) which is free to use.
Here is the project which uses the cortex-m4 gnu module port. the module can be loaded and run successfully. we also add similar test code with you mentioned above to see if the load address is correct. https://github.com/wangwen-4220/stm32f4-disco-module
Could you check if it is helpful to resolve your problem?
@wangwen-4220: thanks for doing this. I've taken a look at https://github.com/wangwen-4220/stm32f4-disco-module/blob/main/stm32cubeide/sample_threadx_module/sample_threadx_module.c but I can't see where it is doing the test we set out, i.e., within a ThreadX module, creating a global pointer to something and checking that the global pointer has been correctly initialised. So, for instance:
char buffer[100];
char* pBuffer = buffer;
int appModMain()
{
printf("buffer address: %p, buffer address via pBuffer: %p \n", buffer, pBuffer);
assert(buffer == pBuffer];
}
Can you show me where that test is now passing? Assuming you can, what did you do to fix it?
To be clear, I can see where you have set up the two global values, but I don't see a test.
We can monitor the global value from debugger windows:
you can build the project and see the gcc compiler and linker options from the console.
That's encouraging: of course we aren't using ST, but we can try those compiler options in our situation.
Just in case the debugger is doing something "clever", could you add the assert check and make sure that the executed code agrees?
Which version of GCC is this?
The git hub project is using the default version integrated in the IDE. I did not change it to use gcc 12.2 release 1, because there will be errors when others try to open the project but not installed the gcc 12.2 tool chain. Actually, I run the projects compiled by different verisons , it seems that there are no difference. I updated the tool chain to use gcc 12.2 at local where i installed gcc 12.2.
Good: just to humour me, could you add the assert check and make sure that the executed code agrees with what the debugger displays?
Actually, could you attach here a .txt file with the build options etc. in it, just as you get printed into the STM32Cube console window? They kind of trail off the end of the screen in your screenshot so we can't see all of them.
Good: just to humour me, could you add the assert check and make sure that the executed code agrees with what the debugger displays? Sure,
Actually, could you attach here a
.txtfile with the build options etc. in it, just as you get printed into the STM32Cube console window? They kind of trail off the end of the screen in your screenshot so we can't see all of them. cortex-m4-module.txt