This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM2432: Rebuilding in CCS produces different binaries each time

Part Number: AM2432

Issue description

When building a project with Code Composer Studio (CCS), the generated binary differs every time I build, even when using the same source code and the same commit.

From a configuration management point of view, I would like the generated binary to be identical as long as the source code and build environment are the same.
Is this behavior expected by design in CCS / ARM-CGT-CLANG, or is it caused by some build settings?

Environment

  • Code Composer Studio: 12.8.1.00005

  • ARM-CGT-CLANG: 4.0.1.LTS

  • INDUSTRIAL-COMMUNICATIONS-SDK-AM243X: 11.00.00.08

Build method

  • “Rebuild Project” in CCS

What I have tried

I compared the following outputs between two rebuilds, but none of them matched:

  • .hs_fs files

  • .out files

  • .bin files converted from .out using tiarmobjcopy

  • Disassembly output generated from .out using tiarmobjdump -d

Additional observations

Initially, I suspected that this might be an issue specific to our in-house project configuration.
However, I was able to reproduce the same behavior using an SDK example project.

The specific example I tested is:

ethernetip_adapter_discrete_io_device_mii_demo_am243x-evm_r5fss0-0_freertos_ti-arm-clang  

Even for this example, rebuilding the project multiple times results in different binaries.

Questions

  • Is non-deterministic binary output a known or expected behavior when building with CCS and ARM-CGT-CLANG?

  • If not, are there recommended compiler, linker, or CCS settings to ensure reproducible (deterministic) builds?

  • Hi,

    This is a bit urgent. Now it's final stage. Could you raise the priority?

    Thank you.

    Best Regards,

    Kasai

  • Hi,

    The thread owner is out of office till until week of Feb 17. Please ping the thread if you do not get an update during that week.

    Thank you for your patience.

    Regards,
    Harshith

  • Hello Kasai,

    You should not use the option "Rebuild Project" as this clean the project first and build it from scratch which results in different binary generation each time.

    Instead you can use the option "Build Project" for CCS so it won't generate any binary if the project didn't change. When you do change in libraries or build SDK, you should go with Rebuild Project as the library changes done in SDK will not be reflected if you just "Build Project". 

    Please refer below image.

    In short, use Build Project option, if changing the application in the CCS. (this does not build new binary if no changes are made).

    use Rebuild Project option, if you want to build project from scratch, or have made any changes in the linked library.

    Regards,

    Tushar

  • Adding more clarification here.

    When you rebuild the project, it does two steps:

    1. Clean the project (delete the older binaries)

    2. Build the project (create new binaries) due to which the metadata inside the binary may change and have difference between the binaries even if the source code is not change.

  • I understand the explanation about the difference between Build Project and Rebuild Project.
    However, I am observing non-deterministic binary output even when using Build Project, which seems to be the core issue.

    Reproduction steps

    1. Import an SDK example project into CCS

    2. Run Build Project

    3. Save the generated binary

    4. Delete the project from the workspace

    5. Import the same example project again

    6. Run Build Project

    7. Save the generated binary

    8. Compare two file

    Result: the binary are different, although

    • Source code is identical

    • Build options are identical

    • Toolchain and SDK versions are identical

    • Rebuild Project is not used

    Question

    From a configuration management point of view, the binary should be identical as long as the source code and build environment are the same.

    • Are there any compiler, linker, or CCS settings to make the build deterministic?

    Attachments

    I have attached a ZIP file containing the two outputs generated by importing the same project twice and building with Build Project, for reference and comparison.

    SDK_example.zip

  • When comparing the two generated binaries, I can see there are few differences in some code blocks. Looks like the block alignment of the generated binaries has changed. 

    This can happen because of compiler optimization process. During the optimization, the compiler might use multi-threading to speed up the process.

    Depending on which thread finishes first, the order in which code blocks are laid out in the final binary can change.

    Please refer below images.

    Users do not need to worry about these changes. The binary will functionally still behave the same.

    Hope the above information helps.

  • Hi Tushar,

    Thank you for your quick analysis. 

    Which optimization level does not cause this? If we should ask compiler expert, could you let me know?

    Best Regards,

    Kasai

  • Hello Kasai,

    Which optimization level does not cause this?

    I am not specifically talking about the binary file optimization but about the process to build the binary. I am not sure if there are any flags to stop the build process optimization.

    I am routing this thread to compiler team for further comments.

    Regards,

    Tushar

  • Hi Tushar,

    Thank you for routing this to compiler team.

    Best Regards,

    Kasai

  • When I compare files from ...

    I have attached a ZIP file containing the two outputs generated by importing the same project twice and building with Build Project, for reference and comparison.

    I do not see any differences in binary or disassembly files.  I only see differences in the .debug_line section.  This is an information only section only used by CCS.  It does not affect execution.

    This does not match what is reported in the first post ...

    I compared the following outputs between two rebuilds, but none of them matched:

    • .hs_fs files

    • .out files

    • .bin files converted from .out using tiarmobjcopy

    • Disassembly output generated from .out using tiarmobjdump -d

    Please focus on the disassembly differences.  This implies a difference should appear in the linker map file.  Here is a example line from a map file ...

      7010fd58    00000150     discrete_io_device_dog.o (.text.DIO_DEVICE_DOG_addObject)
      

    This says that at address 0x7010fd58, for a length of 0x150 bytes, is the contents of the section .text.DIO_DEVICE_DOG_addObject.  It is from the object file discrete_io_device_dog.o.  Put another way, the code for function DIO_DEVICE_DOG_addObject from the file discrete_io_device_dog.c is at that address.  Focus on the length (2nd) column.  For the two builds, compare the map files.  Ignore differences in the address.  Focus on differences in the length.  For one source file with a difference in length, please follow the directions in the article How to Submit a Compiler Test Case.  But do it two times.  Once for each build.  Check and be sure those two builds create different object files.

    Thanks and regards,

    -George

  • At the same time you work on the test case requested in my first post ... Consider the possibility you are affected by the known issue EXT_EP-11620.  To avoid the problem, do one of the following:

    • upgrade to version 4.0.x.LTS, where x >= 2
    • use the option -fno-unique-string-section-names

    Thanks and regards,

    -George

  • Based on your previous reply, I performed additional testing and observed the following:

    1. When generating a binary using

      tiarmobjcopy --strip-debug a.out a.bin
      

      the resulting .bin files are identical even after using Rebuild Project, as long as the project absolute path is the same.

    2. If the project absolute path is different, the generated .bin files differ.

    3. However, when adding the option

      -fno-unique-string-section-names
      

      and then generating the .bin file with

      tiarmobjcopy --strip-debug
      

      the resulting .bin files are identical even when the project absolute path differs.

    Question

    Is it correct to conclude that:

    • If the .bin files generated with --strip-debug are identical,

    • Then the executable behavior (runtime behavior on the target) is guaranteed to be identical?

    In other words, can we consider comparison of the stripped .bin file as a valid method to verify that there is no difference in the execution binary behavior?

    Our main concern is to ensure that there is no difference in runtime behavior, even if debug-related sections or metadata differ.

  • The command ...

    tiarmobjcopy --strip-debug a.out a.bin

    ... does not create a binary file.  It creates a copy of the ELF binary executable file a.out, but without the debug sections like .debug_info, .debug_frame, etc.

    To create a binary file you have to use the option --output-target=binary (or an equivalent like -O binary).   For general background on binary files, please see the article An Introduction to Binary Files.  A binary file never includes debug sections, symbols tables, or other similar sections.  Thus, when using tiarmobjcopy to create a binary file, the option --strip-debug has no effect.

    If two binary files are the same, does that mean ...

    there is no difference in the execution binary behavior?

    As a practical matter, yes.  Many people think of it that way.

    But for the sake of completeness, I have to describe a caveat.  A binary file reflects the contents of the initialized sections like .text, .rodata, etc.  Because it does not directly reflect the contents of uninitialized sections like .bss and .stack, it remains theoretically possible that two binary files will be the same yet have some difference regarding an uninitialized section.  But this is just a theory.  I've never seen it happen in practice.  Suppose, for instance, the only difference is the length of the .stack section.  That has to cause some difference in the boot code.  Which causes a difference in the .text section.  Which is part of the binary file.

    Thanks and regards,

    -George

  • Hi George,

    Thank you for your reply.

    Can we say that when source is the same, the binary is always the same? This is what they want to know.

    Best Regards,

    Kasai

  • There is more to it than matching source code.  Please see this forum post.  Ignore the detail about --keep_asm.  That does not apply to clang-based compilers like tiarmclang or c29clang.

    Thanks and regards,

    -George

  • As a summary, I have confirmed the following:

    • By adding the -fno-unique-string-section-names option, we are able to reproduce identical binary files (either .bin generated with tiarmobjcopy -O binary or .appimage) when using the same source code, build environment, and build options.

    I also understand your explanation that if two binary files are identical, their execution behavior can be considered practically the same.

    Thank you for your detailed explanations and support.