0

I'm trying to compile two different C programs to flash on my embedded device. First program has to check for a condition, if it is valid, it has to jump to the second program, if the condition is not valid, it has to continue execution. I would like to have both programs compiled together to one binary, second program should be in the binary at a specific location. I know the first program size compiled is 3251B, therefore I'd like to have the second program at the address 4KB from the start of the binary.

Do you know how I can achieve that? I'm working on Linux with makefiles.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • Sounds like the two programs should just be two sets of function in one main program. If condition 1, call process 1 start function. If condition 2, call process 2 start function. – dbush Jun 01 '21 at 13:21
  • You really should combine these into one program. Otherwise you will have to look into a boot-loader design. How is it related to Linux and compiler construction? – Eugene Sh. Jun 01 '21 at 13:22
  • @dbush they are not two simple programs that can be combined to 1. The second one is something complicated which was not coded by me, I would rather not touch it. The first one is a small boot-loader that has to check a specific condition before starting. – Zonta01 Jun 01 '21 at 13:25
  • @EugeneSh. it's too complicated to be in one program as two separate functions. My bad if the tags are not correct. – Zonta01 Jun 01 '21 at 13:27
  • 1
    Believe me, it will be more complicated when separate. Well, at least technically. – Eugene Sh. Jun 01 '21 at 13:27
  • What you're trying to do is far harder than modifying the code so that the two programs can be combined into one. You can rename the `main()` in ProgramA as `main_A()`; you can rename the `main()` in ProgramB as `main_B()`; you can write a new `main()` that does `if (condition) main_A(); else main_B();` — which is not rocket science! The only residual problem is if there are (global) functions (or global variables) with the same name in both programs — rename any such name collisions in the code you control — add a `progB_` prefix if need be. – Jonathan Leffler Jun 01 '21 at 13:32
  • I'd still like to try it. Anyone maybe has a small idea even on how to start? – Zonta01 Jun 01 '21 at 13:35
  • Have at it! There isn't a simple way to do it. I'm not sure what the complicated ways are, and have zero interest in finding out. – Jonathan Leffler Jun 01 '21 at 13:35
  • 1
    The approach would be largely dependent on the system you are working on. But that would be too broad. As I have mentioned previously, look up for bootloaders for your system - they do more or less the thing you are looking for. – Eugene Sh. Jun 01 '21 at 13:39
  • Coding for an embedded target using Linux as a development host does not make this a an "embedded -linux" tag question. That is for when the embedded system is running Linux. Your question makes no sense in that context. – Clifford Jun 01 '21 at 13:51
  • Just changed the tag. – Zonta01 Jun 01 '21 at 13:56
  • Sounds like an X-Y problem though. Why do you thing you need to do that? What are you actually trying to achieve? There may be a better solution. – Clifford Jun 01 '21 at 14:21
  • _"it's too complicated to be in one program as two separate functions."_ That seems unlikely. Two separate programs is more complex in any case and unnecessarily duplicates resources such as start-up code and standard library code. You would do this only if you were a) writing a bootloader b) one of the programs is not yours and you do not have the source code to integrate it normally. – Clifford Jun 01 '21 at 15:12

2 Answers2

3

If these are truly separate programs - i.e. both have a main() entry point then they clearly cannot be in a single binary generated by the linker. However you can combine two separately linked programs in different address spaces into a single image file using a tool such as srec_cat. This is best done with a format that includes address information such as Intel Hex (otherwise for raw binary you would need padding between the two images, and tell the programmer the start address):

srec_cat -Output combined.hex -Intel program1.hex -Intel program2.hex -Intel

srec_cat is capable of combining multiple formats and can specify address offsets for input that does not include location information.

If your toolchain is not generating relocatable executables, you cannot arbitrarily locate the program at any particular offset. Generally you would specify memory map to the linker, and build the two separate programs in different memory spaces. That way you can simply combine them with srec_cat as described.

You should be aware that separately linked executables each have their own runtime start-up code, vector table and hardware initialisation. Switching programs arbitrarily is not always straightforward. The hardware initialisation may for example assume that the hardware is in the reset state but the preceding program has made that untrue. You need to at least disable interrupts before starting the second program, otherwise if an interrupt occurs before the second application has set up the vector table, the interrupt handler of the first program will run erroneously.

A simpler solution is to not have two separate programs, but to have two functions in a single program. But if the first program is a bootloader and then second may be distributed separately then you may indeed want to do this simply to have a single load file for production (though that is by no means necessary - you can program two hex files separately for example).

You are still left with the problem of program1 knowing the start address of program2. That is seldom the same as the start address of the image. That is on may architectures where the vector table resides. For ARM-Cortex-M the start of the vector table included the initial stack-pointer address and that start address so it is relatively easy for example: How to jump between programs in Stellaris

Clifford
  • 88,407
  • 13
  • 85
  • 165
0

The linker flag --section-start=.text=$(BOOT_START_ADD) relocates the beginning of .text (program memory) section to BOOT_START_ADD and the relative and absolute jump addresses in the instructions are translated properly. Example snippet,

BOOT_START_ADD  = 0x3E000
LSRC            = linker/bootloader.x
LDFLAGS = -Wl,-Map=$(OUTPUT_DIR)/$(TARGET).map,--cref,--section-start=.text=$(BOOT_START_ADD) -T $(LSRC)
LDFLAGS += -nostartfiles -nostdlib -nodefaultlibs