Zig cc huge intermediate binary objects

Hi, I’m using zig to build a C project (see here Static linking newlibc as libc, freestanding on ARM, zig + c libraries - #2 by simoneruffini) and I’m having a lot of troubles.
I’m trying to debug the process step by step and first I want to understand how zig is building my source files. I’m able to compile the project but I’m getting a huge binary output file.

ll build/test.elf zig-out/bin/test.elf                                                                                                                                                                                           ~/Documents/test/stm32f446re
-rwxr-xr-x 1 simone simone 328K  8 feb 22.36 build/test.elf
-rwxr-xr-x 1 simone simone 1,1M  9 feb 11.50 zig-out/bin/test.elf

So I took a look at the intermediate compilation units object files and their sizes. With respect to the ones build by gcc they are sometimes smaller but other times a lot bigger:

:r !ls -al ./build/main.o zig-cache/o/9e04bf4154200a9ad229a98adf1ef133/main.o
-rw-r--r-- 1 simone simone 30288  8 feb 22.35 ./build/main.o
-rw-r--r-- 1 simone simone 35228  8 feb 22.59 zig-cache/o/9e04bf4154200a9ad229a98adf1ef133/main.o

:r !ls -al ./build/MedianFilter.o zig-cache/o/608588b52ad424e39fbe7b754bc2d13b/MedianFilter.o
-rw-r--r-- 1 simone simone 5372  8 feb 22.35 ./build/MedianFilter.o
-rw-r--r-- 1 simone simone 9328  8 feb 22.59 zig-cache/o/608588b52ad424e39fbe7b754bc2d13b/MedianFilter.o

:r !ls -al ./build/stm32f4xx_it.o zig-cache/o/8556142a4e8cb3a64acad0a21c600e20/stm32f4xx_it.o
-rw-r--r-- 1 simone simone 5268  8 feb 22.35 ./build/stm32f4xx_it.o
-rw-r--r-- 1 simone simone 4896  8 feb 22.59 zig-cache/o/8556142a4e8cb3a64acad0a21c600e20/stm32f4xx_it.o

:r !ls -al ./build/stm32f4xx_hal_msp.o zig-cache/o/88e82eefb964b9cc17f8339c485391dd/stm32f4xx_hal_msp.o
-rw-r--r-- 1 simone simone 13776  8 feb 22.35 ./build/stm32f4xx_hal_msp.o
-rw-r--r-- 1 simone simone 13568  8 feb 22.59 zig-cache/o/88e82eefb964b9cc17f8339c485391dd/stm32f4xx_hal_msp.o

:r !ls -al ./build/stm32f4xx_hal_tim.o zig-cache/o/99288ce3e59a0a256c3a994803be2af0/stm32f4xx_hal_tim.o
-rw-r--r-- 1 simone simone 153896  8 feb 22.35 ./build/stm32f4xx_hal_tim.o
-rw-r--r-- 1 simone simone 236464  8 feb 22.59 zig-cache/o/99288ce3e59a0a256c3a994803be2af0/stm32f4xx_hal_tim.o

:r !ls -al ./build/stm32f4xx_hal_tim_ex.o zig-cache/o/3392d5d156564ae16d92e6e1e61287dd/stm32f4xx_hal_tim_ex.o
-rw-r--r-- 1 simone simone 61536  8 feb 22.35 ./build/stm32f4xx_hal_tim_ex.o
-rw-r--r-- 1 simone simone 93924  8 feb 22.59 zig-cache/o/3392d5d156564ae16d92e6e1e61287dd/stm32f4xx_hal_tim_ex.o

:r !ls -al ./build/stm32f4xx_hal_uart.o zig-cache/o/f45f1e22a18dfeb915af7d502fe65480/stm32f4xx_hal_uart.o
-rw-r--r-- 1 simone simone 116424  8 feb 22.35 ./build/stm32f4xx_hal_uart.o
-rw-r--r-- 1 simone simone 110272  8 feb 22.59 zig-cache/o/f45f1e22a18dfeb915af7d502fe65480/stm32f4xx_hal_uart.o

:r !ls -al ./build/stm32f4xx_hal_rcc.o zig-cache/o/dfc9ef3cafd45a08e1748e5daf7d89c1/stm32f4xx_hal_rcc.o
-rw-r--r-- 1 simone simone 22404  8 feb 22.35 ./build/stm32f4xx_hal_rcc.o
-rw-r--r-- 1 simone simone 29416  8 feb 22.59 zig-cache/o/dfc9ef3cafd45a08e1748e5daf7d89c1/stm32f4xx_hal_rcc.o

:r !ls -al ./build/stm32f4xx_hal_rcc_ex.o zig-cache/o/dd46e027c458622b9f55f8559f13606c/stm32f4xx_hal_rcc_ex.o
-rw-r--r-- 1 simone simone 32904  8 feb 22.35 ./build/stm32f4xx_hal_rcc_ex.o
-rw-r--r-- 1 simone simone 49076  8 feb 22.59 zig-cache/o/dd46e027c458622b9f55f8559f13606c/stm32f4xx_hal_rcc_ex.o

:r !ls -al ./build/stm32f4xx_hal_flash.o zig-cache/o/7ec7a26337c091e4b5b68b21fca20066/stm32f4xx_hal_flash.o
-rw-r--r-- 1 simone simone 17144  8 feb 22.35 ./build/stm32f4xx_hal_flash.o
-rw-r--r-- 1 simone simone 17440  8 feb 22.59 zig-cache/o/7ec7a26337c091e4b5b68b21fca20066/stm32f4xx_hal_flash.o

:r !ls -al ./build/stm32f4xx_hal_flash_ex.o zig-cache/o/efd3655fa5f8371e61870ae4070e0039/stm32f4xx_hal_flash_ex.o
-rw-r--r-- 1 simone simone 22280  8 feb 22.35 ./build/stm32f4xx_hal_flash_ex.o
-rw-r--r-- 1 simone simone 22092  8 feb 22.59 zig-cache/o/efd3655fa5f8371e61870ae4070e0039/stm32f4xx_hal_flash_ex.o

:r !ls -al ./build/stm32f4xx_hal_flash_ramfunc.o zig-cache/o/e575702641443b72ac5f71bcd076bb7e/stm32f4xx_hal_flash_ramfunc.o
-rw-r--r-- 1 simone simone 6220  8 feb 22.35 ./build/stm32f4xx_hal_flash_ramfunc.o
-rw-r--r-- 1 simone simone 5608  8 feb 22.59 zig-cache/o/e575702641443b72ac5f71bcd076bb7e/stm32f4xx_hal_flash_ramfunc.o

:r !ls -al ./build/stm32f4xx_hal_gpio.o zig-cache/o/8bd73bf66e49c02b4de785fa40745d3d/stm32f4xx_hal_gpio.o
-rw-r--r-- 1 simone simone 14472  8 feb 22.35 ./build/stm32f4xx_hal_gpio.o
-rw-r--r-- 1 simone simone 16064  8 feb 22.59 zig-cache/o/8bd73bf66e49c02b4de785fa40745d3d/stm32f4xx_hal_gpio.o

:r !ls -al ./build/stm32f4xx_hal_dma_ex.o zig-cache/o/320697216849f57288f06ee30b883927/stm32f4xx_hal_dma_ex.o
-rw-r--r-- 1 simone simone 17432  8 feb 22.35 ./build/stm32f4xx_hal_dma_ex.o
-rw-r--r-- 1 simone simone 41084  8 feb 22.59 zig-cache/o/320697216849f57288f06ee30b883927/stm32f4xx_hal_dma_ex.o

:r !ls -al ./build/stm32f4xx_hal_dma.o zig-cache/o/d8005e7182f54a18ef4f67f4611b352e/stm32f4xx_hal_dma.o
-rw-r--r-- 1 simone simone 26436  8 feb 22.35 ./build/stm32f4xx_hal_dma.o
-rw-r--r-- 1 simone simone 36356  8 feb 22.59 zig-cache/o/d8005e7182f54a18ef4f67f4611b352e/stm32f4xx_hal_dma.o

:r !ls -al ./build/stm32f4xx_hal_pwr.o zig-cache/o/b47ab7db75a3fa4f15862b8bf641f383/stm32f4xx_hal_pwr.o
-rw-r--r-- 1 simone simone 12296  8 feb 22.35 ./build/stm32f4xx_hal_pwr.o
-rw-r--r-- 1 simone simone 14244  8 feb 22.59 zig-cache/o/b47ab7db75a3fa4f15862b8bf641f383/stm32f4xx_hal_pwr.o

:r !ls -al ./build/stm32f4xx_hal_pwr_ex.o zig-cache/o/01e7e5906a3cfce5eb6706ad53b571b8/stm32f4xx_hal_pwr_ex.o
-rw-r--r-- 1 simone simone 13128  8 feb 22.36 ./build/stm32f4xx_hal_pwr_ex.o
-rw-r--r-- 1 simone simone 12496  8 feb 22.59 zig-cache/o/01e7e5906a3cfce5eb6706ad53b571b8/stm32f4xx_hal_pwr_ex.o

:r !ls -al ./build/stm32f4xx_hal_cortex.o zig-cache/o/e5b65b2fa6b1256cab359bf320787ca0/stm32f4xx_hal_cortex.o
-rw-r--r-- 1 simone simone 26452  8 feb 22.36 ./build/stm32f4xx_hal_cortex.o
-rw-r--r-- 1 simone simone 29120  8 feb 22.59 zig-cache/o/e5b65b2fa6b1256cab359bf320787ca0/stm32f4xx_hal_cortex.o

:r !ls -al ./build/stm32f4xx_hal.o zig-cache/o/1696de14f92d2c979677719783df1848/stm32f4xx_hal.o
-rw-r--r-- 1 simone simone 20352  8 feb 22.36 ./build/stm32f4xx_hal.o
-rw-r--r-- 1 simone simone 21256  8 feb 22.59 zig-cache/o/1696de14f92d2c979677719783df1848/stm32f4xx_hal.o

:r !ls -al ./build/stm32f4xx_hal_exti.o zig-cache/o/47c1c46543fadda6325e01461f19c71c/stm32f4xx_hal_exti.o
-rw-r--r-- 1 simone simone 12620  8 feb 22.36 ./build/stm32f4xx_hal_exti.o
-rw-r--r-- 1 simone simone 14124  8 feb 22.59 zig-cache/o/47c1c46543fadda6325e01461f19c71c/stm32f4xx_hal_exti.o

:r !ls -al ./build/system_stm32f4xx.o zig-cache/o/458200b72dc662e8df51c6bf2e572a28/system_stm32f4xx.o
-rw-r--r-- 1 simone simone 7388  8 feb 22.36 ./build/system_stm32f4xx.o
-rw-r--r-- 1 simone simone 7292  8 feb 22.59 zig-cache/o/458200b72dc662e8df51c6bf2e572a28/system_stm32f4xx.o

:r !ls -al ./build/startup_stm32f446xx.o zig-cache/o/34deb1aa406cbf5da07069ba8d251c04/startup_stm32f446xx.o
-rw-r--r-- 1 simone simone 7284  8 feb 22.36 ./build/startup_stm32f446xx.o
-rw-r--r-- 1 simone simone 7108  8 feb 22.59 zig-cache/o/34deb1aa406cbf5da07069ba8d251c04/startup_stm32f446xx.o

Why is this happening? how can I understand what the llvm compiler is adding that the gcc one is not?
I tried comparing the object files with objdump but the symbols are all unsorted so I can’t compare one to one those files. Do you have any suggestions?

(on a side not what is the correct way to get a verbose output from zig build: I see there is zig build --verbose that is doing nothing in particular, then exe.setVerboseLink(true) and b.verbose = true , b.verbose_cc=true… where b is the parameter of pub fn build())

Maybe this is because of your build mode ? have you tried ReleaseFast or ReleaseSmall ? because most likely the huge difference comes from safety checks that are enabled in debug mode (which is the default build mode).

@pierrelgol is correct. Did you try with ReleaseSmall or whatever you mode currently is but stripping debug info? By default Zig will always emit debug info, while your system C compilers will not.

Will try but the problem is both binaries where compiled in debug mode with debug symbols… I’m okay using different build modes but I need a way to keep debug-symbols (dwarf) in my elf file.
Moreover I noticed that at the link step (even with the --gc-collect option flag) most of the libc symbols where included even if they where not used in the code. For example free, malloc, realloc etc… I’m sure nothing is using those functions.

ReleaseSmall returns

simone@simone-thinkpad> ll zig-out/bin/test.elf                                                                                                                                                                                                                ~/Documents/test/stm32f446re
-rwxr-xr-x 1 simone simone 132K  9 feb 16.50 zig-out/bin/test.elf
simone@simone-thinkpad> file zig-out/bin/test.elf                                                                                                                                                                                                              ~/Documents/test/stm32f446re
zig-out/bin/test.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, stripped

So not ok. ReleaseFast returns:

simone@simone-thinkpad> ll zig-out/bin/test.elf                                                                                                                                                                                                                ~/Documents/test/stm32f446re
-rwxr-xr-x 1 simone simone 890K  9 feb 16.51 zig-out/bin/test.elf
simone@simone-thinkpad> file zig-out/bin/test.elf                                                                                                                                                                                                              ~/Documents/test/stm32f446re
zig-out/bin/test.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped

Not stripped but still higher then gcc 328K and ReleaseSafe returns:

simone@simone-thinkpad> ll zig-out/bin/test.elf                                                                                                                                                                                                                ~/Documents/test/stm32f446re
-rwxr-xr-x 1 simone simone 908K  9 feb 16.53 zig-out/bin/test.elf
simone@simone-thinkpad> file zig-out/bin/test.elf                                                                                                                                                                                                              ~/Documents/test/stm32f446re
zig-out/bin/test.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped

Not stripped is ok but still higher then gcc. So what else can I do? I suspect is some problem during linking.

I’m sorry I can’t provide you with better help, I’d suggest you try the discord too, you might get more advice there, Otherwise, may want to look inside the build script, after looking at it I’ve found this not sure if it’s of any help

/// On ELF targets, this will emit a link section called ".note.gnu.build-id"
/// which can be used to coordinate a stripped binary with its debug symbols.
/// As an example, the bloaty project refuses to work unless its inputs have
/// build ids, in order to prevent accidental mismatches.
/// The default is to not include this section because it slows down linking.
build_id: ?std.zig.BuildId = null,

It was in the : lib/std/Build/step/Compile.zig

I’m sure it’s doable but you should definitely ask around more on the discord, there is a dedicated channel for help and for embedded.

Any chance I can have a look at your project? Or perhaps you could provide a repro I could work with? The biggest bummer is that I haven’t yet added support for 32-bit arm to zig’s elf linker but I don’t think it would be anything linker related.

Yes you can see everything in the first forum post that I did on the topic Static linking newlibc as libc, freestanding on ARM, zig + c libraries - #2 by simoneruffini
There you can find the link to the repo… unfortunately I link to custom-arm libc (newlib nano) that is available only by installing a package (on arch arm-none-eabi-newlib and arm-none-eabi-gcc, arm-none-eabi-binutils to build with make). I have to do this because zig doesn’t have the libc for this specific freestanding target (embedded platorm).

Maybe the linker step is not fully the problem because the makefile built elf has malloc, realloc and free symbols too. But I just did a test to see if all the symbols of the makefile built elf are in the zig elf and they are not (the zig one especially misses some important ones like ErrorHandler), here the output:

simone@simone-thinkpad> diff <(arm-none-eabi-nm build/test.elf | cut -d" " -f3|sort) <(arm-none-eabi-nm zig-out/bin/test.elf | cut -d" " -f3|sort )                                                                                                            ~/Documents/test/stm32f446re
0a1,4
>
>
>
> abs
2,3d5
< __aeabi_idiv0
< __aeabi_ldiv0
47d48
< Error_Handler
60,62d60
< filtered_sample15.7
< filtered_sample31.4
< filtered_sample7.10
75,76d72
< HAL_Delay
< HAL_GetTick
78,98d73
< HAL_GPIO_WritePin
< HAL_IncTick
< HAL_Init
< HAL_InitTick
< HAL_MspInit
< HAL_NVIC_SetPriority
< HAL_NVIC_SetPriorityGrouping
< HAL_RCC_ClockConfig
< HAL_RCC_GetHCLKFreq
< HAL_RCC_GetPCLK1Freq
< HAL_RCC_GetPCLK2Freq
< HAL_RCC_GetSysClockFreq
< HAL_RCC_OscConfig
< HAL_SYSTICK_Config
< HAL_TIM_Base_Init
< HAL_TIM_Base_MspInit
< HAL_TIM_Base_Start
< HAL_TIMEx_MasterConfigSynchronization
< HAL_UART_Init
< HAL_UART_MspInit
< HAL_UART_Transmit
101,103d75
< hmedfilt15.14
< hmedfilt31.13
< hmedfilt7.15
118a91
> .L_MergedGlobals
122a96,117
> main.filtered_sample15
> main.filtered_sample31
> main.filtered_sample7
> main.hmedfilt15.2
> main.hmedfilt15.3
> main.hmedfilt15.4
> main.hmedfilt31.2
> main.hmedfilt31.3
> main.hmedfilt31.4
> main.hmedfilt7.2
> main.hmedfilt7.3
> main.hmedfilt7.4
> main.medfilt_wndw15
> main.medfilt_wndw31
> main.medfilt_wndw7
> main.sample_cnt
> main.time_end15
> main.time_end31
> main.time_end7
> main.time_start15
> main.time_start31
> main.time_start7
129,133d123
< MEDFILT_Init
< MEDFILT_Insert
< medfilt_wndw15.1
< medfilt_wndw31.2
< medfilt_wndw7.0
138d127
< memset
141,143d129
< MX_GPIO_Init
< MX_TIM7_Init
< MX_USART2_UART_Init
145,146d130
< NVIC_EncodePriority
< __NVIC_SetPriority
170d153
< sample_cnt.12
192d174
< SystemClock_Config
211,217d192
< TIM_Base_SetConfig
< time_end15.6
< time_end31.3
< time_end7.9
< time_start15.8
< time_start31.5
< time_start7.11
220,221d194
< UART_SetConfig
< UART_WaitOnFlagUntilTimeout
229d201
< uwTickFreq

I don’t understand that comment, what its trying to achieve that section?

I tried the discord but with no success, I don’t know how to get help there… there is too much happening and my question on the help channel disappeared or became stale… I don’t know… so I tried here as a last resource.

(small rant ahead because it’s weeks I’m trying to build this project, sorry in advance) I’m sad to see that it is so difficult to build for embedded platforms especially because zig is always targeted for system-developers and embedded developers. I know I’m relying only on C and the task is difficult by itself but the state of embedded zig is nowhere near to be ready/“useful” therefore I’m trying to use what is available and hugely tested in the C market. I tried this approach because zig is advertised as a language that enables reuse of already available software so in theory this should be possible.

Have you tried the Embedded Zig Discord Community · ziglang/zig Wiki · GitHub ?

I haven’t looked into it, because I haven’t done any embedded programming, but it seems you may have an easier time finding people with similar problems there, due to its narrower focus.

yes, tried there too

Have you tried turning on lto? In my project, it removed a lot of stuff from the binaries.

I added exe.want_lto=true and with that i get:
Debug 931K, ReleaseSave 908K, ReleaseFast 890K, still nowhere near the 328K of gcc. I need to reach similar sizes because the flash on an embedded cpu is small.

I found an additional info.
This is the output of diff -u <(arm-none-eabi-readelf -e build/test.elf) <(arm-none-eabi-readelf -e zig-out/bin/test.elf) | delta -s (delta is just a tool to show this diff side by side)


Those are my considerations (left side gcc output, right side zig with LTO and relesaseFast)

  1. The entry point is different (I think this is bad, the place should be the same because the linker script decides where to put stuff)
  2. all locations are different, but
  3. .text size is actually smaller so maybe the problem is not in the code
  4. .rodata is the same size but in a different location
  5. .ARM is bigger (I don’t know what it is)
  6. .bss slightly bigger
  7. .heap changed type ( I don’t know what those types correspond to)
  8. .debug_info increase by 3x (maybe the culprit)
  9. Section to segment mappping is totally different and put in order instead of a single section

I’m not competent enough (right now) to understand this, maybe someone can better understand what is happening @kubkon

Here the diff of sizes

Update: .debug symbols are not actually flashed on the microcontroller, so it’s not important if they are bigger. The real size of the elf file that will be flashed on the microcontroller is the output of the arm-none-eabi-size command.

simone@simone-thinkpad> arm-none-eabi-size zig-out/bin/test.elf                                                                                                  ~/Documents/test/stm32f446re
   text	   data	    bss	    dec	    hex	filename
  13376	   1632	   1472	  16480	   4060	zig-out/bin/test.elf
simone@simone-thinkpad> arm-none-eabi-size build/test.elf                                                                                                        ~/Documents/test/stm32f446re
   text	   data	    bss	    dec	    hex	filename
  13508	    160	   2928	  16596	   40d4	build/test.elf

As you can see the sizes are comparable: the zig output is actually smaller if it is compiled in ReleaseFast (nice!), but I’m still not sure about the fact that some sections are in different memory addresses. Maybe I need to generate the map file and inspect more there.

LLD seems to make .user_heap_stack non-zerofill (meaning the loader/kernel will mmap the actual contents of the file for that section) while gold seems to make it zerofill (meaning the loader will zero-out the contents without mapping the area in the file). This will contribute to a larger file size if not zerofilled. It also seems like LLD is wasting file space on .bss in the file judging by the file offsets. If you look at the output generated by gold, the offset of .bss overlaps a debug section offset in file - this is fine since we don’t mmap zerofill when loading the binary. This doesn’t seem to be the case for LLD output according to your screenshots. All in all though every linker is in general free to organise sections in VM addresses and file offsets how it sees fit with one restriction, it has to conform to the target requirements.

1 Like

Thank you very much for the response, but I don’t think I understand everything… What I understood is that lld (zig) condenses the heap by non zero-fillining, but you say this makes the binary output bigger how is that so?

My main concern is with the .isr_vector section, that section is critical because contains addresses of interrupt service routines (like Reset_Handler that is the entry point of an embedded target even before _start). Previously I was wrong in saying that it is in a difference place (08000000 for both) but it is bigger in lld output. The linker script specifies where that section is and how big it is:

/* Entry Point */
ENTRY(Reset_Handler)

/* Specify the memory areas */
MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 512K
}

/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM);    /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200;      /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Define output sections */
SECTIONS
{
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH

  /* The program code and other data goes into FLASH */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH

  /* Constant data goes into FLASH */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >FLASH

  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
  .ARM : {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } >FLASH

  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >FLASH
  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >FLASH
  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >FLASH

  /* used by the startup to initialize data */
  _sidata = LOADADDR(.data);

  /* Initialized data sections goes into RAM, load LMA copy after code */
  .data :
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */

    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
  } >RAM AT> FLASH


  /* Uninitialized data section */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss secion */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM

  /* User_heap_stack section, used to check that there is enough RAM left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM



  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }
}

It is very strange that the gold output (gcc) has an offset of 00100 while lld produces a bigger offset (01000). Should I be concerned? Because immediately after that is the code section.

Moreover I see that the .preinit_array section is produeced by lld but my gcc result skips it even if the linker script references it… is this somthing lld does? What does it mean?

It also seems like LLD is wasting file space on .bss in the file judging by the file offsets. If you look at the output generated by gold, the offset of .bss overlaps a debug section offset in file - this is fine since we don’t mmap zerofill when loading the binary. This doesn’t seem to be the case for LLD output according to your screenshots.

What do you mean it overlaps a debug section offset? Do you mean the .bss section overlaps a debug section?

Anyway at the end of the day my main concern is that the binary outputs behave identically and I don’t know how to test this… (there is another problem that I get and can’t understand, but that is written in the other post…)

P.S. thank you for the help is greatly appreciated! I hope to get this thing done and publish a way to use zig in the embbedded world with already available C stack! That should be very helpful for zig newbies like me!

1 Like

Hey @simoneruffini I’m going to take a look at your repo today! Did adding elf.link_gc_sections = true; clear up the issue about unused symbols getting included? So are the only remaining issues essentially concerns about the firmware programs being functionally equivalent between compilers/linkers? I’m doing a deep dive currently on porting existing C/C++ embedded code to the Zig compiler, so I’ll hopefully have some more satisfying answers to this question in the coming weeks.

The TLDR is:

  • LLVM fully supports these architectures from a CPU instruction standpoint, in fact ARM’s own fancy, not-free compiler is based on LLVM
  • However, we’re a tiny bit in uncharted waters using Zig (LLVM’s) linker. I have less concerns about the linker itself, and more about annoying subtleties in how Zig’s linker interprets .LD files vs GCC.
1 Like

I was able to compile your example : test
By adding @haydenridd linker option and -0g to the C sources files, the final binary size is 16KB.
I proposed a pull request on your repository to update to 0.12 and adding these options

1 Like