-
Notifications
You must be signed in to change notification settings - Fork 0
Home
This is a continuation of README from rpi0-cross-compile project.
Let's investigate more. Build hello-world
application in two steps (compilation+linking):
arm-linux-gnueabihf-gcc -c -marm -mcpu=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -o hello-world.o hello-world.c
arm-linux-gnueabihf-gcc -marm -mcpu=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -o hello-world hello-world.o
and examine object file:
$ arm-linux-gnueabihf-readelf -A hello-world.o
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "6KZ"
Tag_CPU_arch: v6KZ
Tag_ARM_ISA_use: Yes
Tag_THUMB_ISA_use: Thumb-1
Tag_FP_arch: VFPv2
Tag_ABI_PCS_wchar_t: 4
Tag_ABI_FP_denormal: Needed
Tag_ABI_FP_exceptions: Needed
Tag_ABI_FP_number_model: IEEE 754
Tag_ABI_align_needed: 8-byte
Tag_ABI_align_preserved: 8-byte, except leaf SP
Tag_ABI_enum_size: int
Tag_ABI_VFP_args: VFP registers
Tag_ABI_optimization_goals: Aggressive Debug
Tag_CPU_unaligned_access: v6
Tag_Virtualization_use: TrustZone
The CPU architecture of object file is (almost) correct but architecture of final executable is wrong:
$ arm-linux-gnueabihf-readelf -A hello-world
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "7-A"
Tag_CPU_arch: v7
Tag_CPU_arch_profile: Application
Tag_ARM_ISA_use: Yes
Tag_THUMB_ISA_use: Thumb-2
Tag_FP_arch: VFPv3-D16
Tag_ABI_PCS_wchar_t: 4
Tag_ABI_FP_rounding: Needed
Tag_ABI_FP_denormal: Needed
Tag_ABI_FP_exceptions: Needed
Tag_ABI_FP_number_model: IEEE 754
Tag_ABI_align_needed: 8-byte
Tag_ABI_align_preserved: 8-byte, except leaf SP
Tag_ABI_enum_size: int
Tag_ABI_VFP_args: VFP registers
Tag_CPU_unaligned_access: v6
Tag_Virtualization_use: TrustZone
We can easily list all object files and libraries used by linker by passing --trace
option:
$ arm-linux-gnueabihf-gcc -marm -mcpu=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -Wl,--trace -o hello-world hello-world.o
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/../../../../arm-linux-gnueabihf/lib/Scrt1.o
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/../../../../arm-linux-gnueabihf/lib/crti.o
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/crtbeginS.o
hello-world.o
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/libgcc.a
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/libgcc_s.so
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/../../../../arm-linux-gnueabihf/lib/libgcc_s.so.1
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/libgcc.a
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/../../../../arm-linux-gnueabihf/lib/libc.so
/usr/arm-linux-gnueabihf/lib/libc.so.6
/usr/arm-linux-gnueabihf/lib/libc_nonshared.a
/usr/arm-linux-gnueabihf/lib/ld-linux-armhf.so.3
/usr/arm-linux-gnueabihf/lib/libc_nonshared.a
/usr/arm-linux-gnueabihf/lib/ld-linux-armhf.so.3
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/libgcc.a
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/libgcc_s.so
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/../../../../arm-linux-gnueabihf/lib/libgcc_s.so.1
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/libgcc.a
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/crtendS.o
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/../../../../arm-linux-gnueabihf/lib/crtn.o
and then investigate crtbeginS.o
for example:
$ arm-linux-gnueabihf-readelf -A /usr/lib/gcc-cross/arm-linux-gnueabihf/11/crtbeginS.o
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "7-A"
Tag_CPU_arch: v7
Tag_CPU_arch_profile: Application
Tag_ARM_ISA_use: Yes
Tag_THUMB_ISA_use: Thumb-2
Tag_FP_arch: VFPv3-D16
Tag_ABI_PCS_wchar_t: 4
Tag_ABI_FP_denormal: Needed
Tag_ABI_FP_exceptions: Needed
Tag_ABI_FP_number_model: IEEE 754
Tag_ABI_align_needed: 8-byte
Tag_ABI_align_preserved: 8-byte, except leaf SP
Tag_ABI_enum_size: int
Tag_ABI_VFP_args: VFP registers
Tag_ABI_optimization_goals: Aggressive Speed
Tag_CPU_unaligned_access: v6
As we see the linker uses object files with wrong CPU architecture. Consequently the final executable has wrong CPU architecture also 😕.
The solution is to use proper libraries and object files taken from Raspberry Pi OS and then specify path to these files using --sysroot
option.
There're many ways to do this. Here we're using files from Docker image. See Preparing SYSROOT page for more info.
Assuming that sysroot is prepared in /home/vscode/sysroot
directory:
$ arm-linux-gnueabihf-gcc --sysroot=/home/vscode/sysroot -L=/usr/lib -L=/usr/lib/gcc -marm -mcpu=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -Wl,--trace -o hello-world hello-world.o
/usr/lib/gcc-cross/arm-linux-gnueabihf/12/../../../../arm-linux-gnueabihf/lib/Scrt1.o
/usr/lib/gcc-cross/arm-linux-gnueabihf/12/../../../../arm-linux-gnueabihf/lib/crti.o
/usr/lib/gcc-cross/arm-linux-gnueabihf/12/crtbeginS.o
hello-world.o
/home/vscode/sysroot/usr/lib/gcc/libgcc.a
/home/vscode/sysroot/usr/lib/gcc/libgcc_s.so
/home/vscode/sysroot/usr/lib/libgcc_s.so.1
/home/vscode/sysroot/usr/lib/gcc/libgcc.a
/home/vscode/sysroot/usr/lib/libc.so
/home/vscode/sysroot/lib/arm-linux-gnueabihf/libc.so.6
/home/vscode/sysroot/usr/lib/arm-linux-gnueabihf/libc_nonshared.a
/home/vscode/sysroot/lib/ld-linux-armhf.so.3
/home/vscode/sysroot/usr/lib/arm-linux-gnueabihf/libc_nonshared.a
/home/vscode/sysroot/lib/ld-linux-armhf.so.3
/home/vscode/sysroot/usr/lib/gcc/libgcc.a
/home/vscode/sysroot/usr/lib/gcc/libgcc_s.so
/home/vscode/sysroot/usr/lib/libgcc_s.so.1
/home/vscode/sysroot/usr/lib/gcc/libgcc.a
/usr/lib/gcc-cross/arm-linux-gnueabihf/12/crtendS.o
/usr/lib/gcc-cross/arm-linux-gnueabihf/12/../../../../arm-linux-gnueabihf/lib/crtn.o
Options -L=/usr/lib -L=/usr/lib/gcc
specifies library search paths relative to sysroot.
All libraries are taken from sysroot but startup files aren't 😥.
There's no easy way - such as command line option - to specify custom startup files.
Due to searching procedure of startup files we have to rename or move preinstalled ones to another location.
See Startup files explained page for more info.
The safest way to do this is to use dpkg-divert
command:
dpkg-divert --rename /usr/lib/gcc-cross/arm-linux-gnueabihf/12/../../../../arm-linux-gnueabihf/lib/Scrt1.o
dpkg-divert --rename /usr/lib/gcc-cross/arm-linux-gnueabihf/12/../../../../arm-linux-gnueabihf/lib/crti.o
dpkg-divert --rename /usr/lib/gcc-cross/arm-linux-gnueabihf/12/crtbeginS.o
dpkg-divert --rename /usr/lib/gcc-cross/arm-linux-gnueabihf/12/crtendS.o
dpkg-divert --rename /usr/lib/gcc-cross/arm-linux-gnueabihf/12/../../../../arm-linux-gnueabihf/lib/crtn.o
Important
Use sudo
command to invoke dpkg-divert
.
Now we may try to compile hello-world
again:
$ arm-linux-gnueabihf-gcc --sysroot=/home/vscode/sysroot -L=/usr/lib -L=/usr/lib/gcc -marm -mcpu=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -Wl,--trace -o hello-world hello-world.o
/home/vscode/sysroot/lib/arm-linux-gnueabihf/Scrt1.o
/home/vscode/sysroot/lib/arm-linux-gnueabihf/crti.o
/home/vscode/sysroot/lib/arm-linux-gnueabihf/crtbeginS.o
hello-world.o
/home/vscode/sysroot/usr/lib/gcc/libgcc.a
/home/vscode/sysroot/usr/lib/gcc/libgcc_s.so
/home/vscode/sysroot/usr/lib/libgcc_s.so.1
/home/vscode/sysroot/usr/lib/gcc/libgcc.a
/home/vscode/sysroot/usr/lib/libc.so
/home/vscode/sysroot/lib/arm-linux-gnueabihf/libc.so.6
/home/vscode/sysroot/usr/lib/arm-linux-gnueabihf/libc_nonshared.a
/home/vscode/sysroot/lib/ld-linux-armhf.so.3
/home/vscode/sysroot/usr/lib/arm-linux-gnueabihf/libc_nonshared.a
/home/vscode/sysroot/lib/ld-linux-armhf.so.3
/home/vscode/sysroot/usr/lib/gcc/libgcc.a
/home/vscode/sysroot/usr/lib/gcc/libgcc_s.so
/home/vscode/sysroot/usr/lib/libgcc_s.so.1
/home/vscode/sysroot/usr/lib/gcc/libgcc.a
/home/vscode/sysroot/lib/arm-linux-gnueabihf/crtendS.o
/home/vscode/sysroot/lib/arm-linux-gnueabihf/crtn.o
As we see. Now all files are taken from sysroot and CPU architecture of generated binary is finally correct 😃:
$ arm-linux-gnueabihf-readelf -A hello-world
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "6KZ"
Tag_CPU_arch: v6KZ
Tag_ARM_ISA_use: Yes
Tag_THUMB_ISA_use: Thumb-1
Tag_FP_arch: VFPv2
Tag_ABI_PCS_wchar_t: 4
Tag_ABI_FP_rounding: Needed
Tag_ABI_FP_denormal: Needed
Tag_ABI_FP_exceptions: Needed
Tag_ABI_FP_number_model: IEEE 754
Tag_ABI_align_needed: 8-byte
Tag_ABI_align_preserved: 8-byte, except leaf SP
Tag_ABI_enum_size: int
Tag_ABI_VFP_args: VFP registers
Tag_CPU_unaligned_access: v6
Tag_Virtualization_use: TrustZone
hello-world
is ready to work on real hardware. You may also use QEMU to test it:
$ qemu-arm-static -cpu arm1176 -L /home/vscode/sysroot ./hello-world
Hello, World!
Are names of startup files hardcoded in GCC? Well, no. You may specify your own filenames via so-called specification strings or just spcecs.
In general specs is a set of rules specyfying arguments for subprocesses (compiler, linker etc.) invoked by GCC.
Without going into details let's display default specs by invoking GCC with -dumpspecs
option:
arm-linux-gnueabihf-gcc -dumpspecs
*asm:
%{mbig-endian:-EB} %{mlittle-endian:-EL} %(asm_cpu_spec) %{mapcs-*:-mapcs-%*} %(subtarget_asm_float_spec) %{mthumb-interwork:-mthumb-interwork} %{mfloat-abi=*} %{!mfpu=auto: %{mfpu=*}} %(subtarget_extra_asm_spec)
*asm_debug:
%{%:debug-level-gt(0):%{gstabs*:--gstabs;:%{g*:%{%:dwarf-version-gt(4):--gdwarf-5;%:dwarf-version-gt(3):--gdwarf-4;%:dwarf-version-gt(2):--gdwarf-3;:--gdwarf2}}}} %{ffile-prefix-map=*:--debug-prefix-map %*} %{fdebug-prefix-map=*:--debug-prefix-map %*}
*asm_debug_option:
*asm_final:
%{gsplit-dwarf:
objcopy --extract-dwo %{c:%{o*:%*}%{!o*:%w%b%O}}%{!c:%U%O} %b.dwo
objcopy --strip-dwo %{c:%{o*:%*}%{!o*:%w%b%O}}%{!c:%U%O} }
*asm_options:
%{-target-help:%:print-asm-header()} %{v} %{w:-W} %{I*} %(asm_debug_option) %{gz|gz=zlib:--compress-debug-sections=zlib} %{gz=none:--compress-debug-sections=none} %{gz=zlib-gnu:--compress-debug-sections=zlib-gnu} %a %Y %{c:%W{o*}%{!o*:-o %w%b%O}}%{!c:-o %d%w%u%O}
*invoke_as:
%{!fwpa*: %{fcompare-debug=*|fdump-final-insns=*:%:compare-debug-dump-opt()} %{!S:-o %|.s |
as %(asm_options) %m.s %A } }
*cpp:
%(subtarget_cpp_spec)
*cpp_options:
%(cpp_unique_options) %1 %{m*} %{std*&ansi&trigraphs} %{W*&pedantic*} %{w} %{f*} %{g*:%{%:debug-level-gt(0):%{g*} %{!fno-working-directory:-fworking-directory}}} %{O*} %{undef} %{save-temps*:-fpch-preprocess} %(distro_defaults)
*cpp_debug_options:
%<dumpdir %<dumpbase %<dumpbase-ext %{d*} %:dumps()
*cpp_unique_options:
%{!Q:-quiet} %{nostdinc*} %{C} %{CC} %{v} %@{I*&F*} %{P} %I %{MD:-MD %{!o:%b.d}%{o*:%.d%*}} %{MMD:-MMD %{!o:%b.d}%{o*:%.d%*}} %{M} %{MM} %{MF*} %{MG} %{MP} %{MQ*} %{MT*} %{Mmodules} %{Mno-modules} %{!E:%{!M:%{!MM:%{!MT:%{!MQ:%{MD|MMD:%{o*:-MQ %*}}}}}}} %{remap} %{%:debug-level-gt(2):-dD} %{!iplugindir*:%{fplugin*:%:find-plugindir()}} %{H} %C %{D*&U*&A*} %{i*} %Z %i %{E|M|MM:%W{o*}}
*trad_capable_cpp:
cc1 -E %{traditional|traditional-cpp:-traditional-cpp}
*cc1:
%{!mandroid|tno-android-cc:%{profile:-p} %{%:sanitize(address):-funwind-tables} ;:%{profile:-p} %{%:sanitize(address):-funwind-tables} %{!mglibc:%{!muclibc:%{!mbionic: -mbionic}}} %{!fno-pic:%{!fno-PIC:%{!fpic:%{!fPIC: -fPIC}}}}}
*cc1_options:
%{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}} %{!iplugindir*:%{fplugin*:%:find-plugindir()}} %1 %{!Q:-quiet} %(cpp_debug_options) %{m*} %{aux-info*} %{g*} %{O*} %{W*&pedantic*} %{w} %{std*&ansi&trigraphs} %{v:-version} %{pg:-p} %{p} %{f*} %{undef} %{Qn:-fno-ident} %{Qy:} %{-help:--help} %{-target-help:--target-help} %{-version:--version} %{-help=*:--help=%*} %{!fsyntax-only:%{S:%W{o*}%{!o*:-o %w%b.s}}} %{fsyntax-only:-o %j} %{-param*} %{coverage:-fprofile-arcs -ftest-coverage} %{fprofile-arcs|fprofile-generate*|coverage: %{!fprofile-update=single: %{pthread:-fprofile-update=prefer-atomic}}}
*cc1plus:
%{!mandroid|tno-android-cc:;:%{!fexceptions:%{!fno-exceptions: -fno-exceptions}} %{!frtti:%{!fno-rtti: -fno-rtti}}}
*link_gcc_c_sequence:
%{static|static-pie:--start-group} %G %{!nolibc:%L} %{static|static-pie:--end-group}%{!static:%{!static-pie:%G}}
*distro_defaults:
*link_ssp:
%{fstack-protector|fstack-protector-all|fstack-protector-strong|fstack-protector-explicit:}
*endfile:
%{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s} %{!mandroid|tno-android-ld:%{!static:%{fvtable-verify=none:%s; fvtable-verify=preinit:vtv_end_preinit.o%s; fvtable-verify=std:vtv_end.o%s}} %{static:crtend.o%s; shared|static-pie|!no-pie:crtendS.o%s; :crtend.o%s} crtn.o%s ;:%{shared: crtend_so%O%s;: crtend_android%O%s}}
*link:
%{!r:--build-id} %{!static|static-pie:--eh-frame-hdr} %{mcpu=arm8|mcpu=arm810|mcpu=strongarm*|march=armv4|mcpu=fa526|mcpu=fa626:--fix-v4bx}%{!r:%{!mbe32:%:be8_linkopt(%{mlittle-endian:little} %{mbig-endian:big} %{mbe8:be8} %{march=*:arch %*})}}%{!mandroid|tno-android-ld:%{h*} %{static:-Bstatic} %{shared:-shared} %{symbolic:-Bsymbolic} %{!static: %{rdynamic:-export-dynamic} %{!shared:-dynamic-linker %{muclibc:/lib/ld-uClibc.so.0;:%{mbionic:/system/bin/linker;:%{mmusl:/lib/ld-musl-arm%{mbig-endian:eb}%{mfloat-abi=hard:hf}%{mfdpic:-fdpic}.so.1;:%{mfloat-abi=hard:/lib/ld-linux-armhf.so.3} %{mfloat-abi=soft*:/lib/ld-linux.so.3} %{!mfloat-abi=*:/lib/ld-linux.so.3}}}}}} -X --hash-style=gnu %{!fsanitize=*:--as-needed} %{mbig-endian:-EB} %{mlittle-endian:-EL} -m armelf_linux_eabi;:%{h*} %{static:-Bstatic} %{shared:-shared} %{symbolic:-Bsymbolic} %{!static: %{rdynamic:-export-dynamic} %{!shared:-dynamic-linker %{muclibc:/lib/ld-uClibc.so.0;:%{mbionic:/system/bin/linker;:%{mmusl:/lib/ld-musl-arm%{mbig-endian:eb}%{mfloat-abi=hard:hf}%{mfdpic:-fdpic}.so.1;:%{mfloat-abi=hard:/lib/ld-linux-armhf.so.3} %{mfloat-abi=soft*:/lib/ld-linux.so.3} %{!mfloat-abi=*:/lib/ld-linux.so.3}}}}}} -X --hash-style=gnu %{!fsanitize=*:--as-needed} %{mbig-endian:-EB} %{mlittle-endian:-EL} -m armelf_linux_eabi %{shared: -Bsymbolic}}
*lib:
%{!mandroid|tno-android-ld:%{pthread:-lpthread} %{shared:-lc} %{!shared:%{profile:-lc_p}%{!profile:-lc}};:%{shared:-lc} %{!shared:%{profile:-lc_p}%{!profile:-lc}} %{!static: -ldl}}
*link_gomp:
*libgcc:
%{static|static-libgcc|static-pie:-lgcc -lgcc_eh}%{!static:%{!static-libgcc:%{!static-pie:%{!shared-libgcc:-lgcc --push-state --as-needed -lgcc_s --pop-state}%{shared-libgcc:-lgcc_s%{!shared: -lgcc}}}}}
*startfile:
%{!mandroid|tno-android-ld:%{shared:; pg|p|profile:%{static-pie:grcrt1.o%s;:gcrt1.o%s}; static:crt1.o%s; static-pie:rcrt1.o%s; !no-pie:Scrt1.o%s; :crt1.o%s} crti.o%s %{static:crtbeginT.o%s; shared|static-pie|!no-pie:crtbeginS.o%s; :crtbegin.o%s} %{fvtable-verify=none:%s; fvtable-verify=preinit:vtv_start_preinit.o%s; fvtable-verify=std:vtv_start.o%s} ;:%{shared: crtbegin_so%O%s;: %{static: crtbegin_static%O%s;: crtbegin_dynamic%O%s}}}
*cross_compile:
1
*version:
12.2.0
*multilib:
.::arm-linux-gnueabihf ;
*multilib_defaults:
*multilib_extra:
*multilib_matches:
*multilib_exclusions:
*multilib_options:
*multilib_reuse:
*linker:
collect2
*linker_plugin_file:
*lto_wrapper:
*lto_gcc:
*post_link:
*link_libgcc:
%D
*md_exec_prefix:
*md_startfile_prefix:
*md_startfile_prefix_1:
*startfile_prefix_spec:
*sysroot_spec:
--sysroot=%R
*sysroot_suffix_spec:
*sysroot_hdrs_suffix_spec:
*self_spec:
*subtarget_cpp_spec:
%{posix:-D_POSIX_SOURCE} %{pthread:-D_REENTRANT}
*asm_cpu_spec:
%{mfpu=auto:%<mfpu=auto %:asm_auto_mfpu(%{march=*: arch %*})} %{mcpu=generic-*:-march=%:rewrite_march(%{mcpu=generic-*:%*}); march=*:-march=%:rewrite_march(%{march=*:%*}); mcpu=*:-mcpu=%:rewrite_mcpu(%{mcpu=*:%*}) }
*subtarget_extra_asm_spec:
%{mabi=apcs-gnu|mabi=atpcs:-meabi=gnu;:-meabi=5} %{mcpu=arm8|mcpu=arm810|mcpu=strongarm*|march=armv4|mcpu=fa526|mcpu=fa626:--fix-v4bx}
*subtarget_asm_float_spec:
%{mapcs-float:-mfloat}
*link_command:
%{!fsyntax-only:%{!c:%{!M:%{!MM:%{!E:%{!S: %(linker) %{!fno-use-linker-plugin:%{!fno-lto: -plugin %(linker_plugin_file) -plugin-opt=%(lto_wrapper) -plugin-opt=-fresolution=%u.res %{flinker-output=*:-plugin-opt=-linker-output-known} %{!nostdlib:%{!nodefaultlibs:%:pass-through-libs(%(link_gcc_c_sequence))}} }}%{flto|flto=*:%<fcompare-debug*} %{flto} %{fno-lto} %{flto=*} %l %{static|shared|r:;!no-pie:-pie} %{fuse-ld=*:-fuse-ld=%*} %{gz|gz=zlib:--compress-debug-sections=zlib} %{gz=none:--compress-debug-sections=none} %{gz=zlib-gnu:--compress-debug-sections=zlib-gnu} %X %{o*} %{e*} %{N} %{n} %{r} %{s} %{t} %{u*} %{z} %{Z} %{!nostdlib:%{!r:%{!nostartfiles:%S}}} %{static|no-pie|static-pie:} %@{L*} %(mfwrap) %(link_libgcc) %{fvtable-verify=none:} %{fvtable-verify=std: %e-fvtable-verify=std is not supported in this configuration} %{fvtable-verify=preinit: %e-fvtable-verify=preinit is not supported in this configuration} %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):%{!shared:libasan_preinit%O%s} %{static-libasan:%{!shared:-Bstatic --whole-archive -lasan --no-whole-archive -Bdynamic}}%{!static-libasan:--push-state --no-as-needed -lasan --pop-state}} %{%:sanitize(hwaddress):%{static-libhwasan:%{!shared:-Bstatic --whole-archive -lhwasan --no-whole-archive -Bdynamic}}%{!static-libhwasan:--push-state --no-as-needed -lhwasan --pop-state}} %{%:sanitize(thread):%{!shared:libtsan_preinit%O%s} %{static-libtsan:%{!shared:-Bstatic --whole-archive -ltsan --no-whole-archive -Bdynamic}}%{!static-libtsan:--push-state --no-as-needed -ltsan --pop-state}} %{%:sanitize(leak):%{!shared:liblsan_preinit%O%s} %{static-liblsan:%{!shared:-Bstatic --whole-archive -llsan --no-whole-archive -Bdynamic}}%{!static-liblsan:--push-state --no-as-needed -llsan --pop-state}}}}} %o %{fopenacc|fopenmp|%:gt(%{ftree-parallelize-loops=*:%*} 1): %:include(libgomp.spec)%(link_gomp)} %{fgnu-tm:%:include(libitm.spec)%(link_itm)} %(mflib) %{fsplit-stack: --wrap=pthread_create} %{fprofile-arcs|fprofile-generate*|coverage:-lgcov} %{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address): %{static-libasan|static:%:include(libsanitizer.spec)%(link_libasan)} %{static:%ecannot specify -static with -fsanitize=address}} %{%:sanitize(hwaddress): %{static-libhwasan|static:%:include(libsanitizer.spec)%(link_libhwasan)} %{static:%ecannot specify -static with -fsanitize=hwaddress}} %{%:sanitize(thread): %{static-libtsan|static:%:include(libsanitizer.spec)%(link_libtsan)} %{static:%ecannot specify -static with -fsanitize=thread}} %{%:sanitize(undefined):%{static-libubsan:-Bstatic} %{!static-libubsan:--push-state --no-as-needed} -lubsan %{static-libubsan:-Bdynamic} %{!static-libubsan:--pop-state} %{static-libubsan|static:%:include(libsanitizer.spec)%(link_libubsan)}} %{%:sanitize(leak): %{static-liblsan|static:%:include(libsanitizer.spec)%(link_liblsan)}}}}} %{!nostdlib:%{!r:%{!nodefaultlibs:%(link_ssp) %(link_gcc_c_sequence)}}} %{!nostdlib:%{!r:%{!nostartfiles:%E}}} %{T*}
%(post_link) }}}}}}
The syntax of the specs is rather complicated but one thing should be rather clear. All lines like *cpp:
or *link:
begins something.
These are beginings of named spec string definitions. All lines after such directive up to the next directive or blank line are considered to be the text for the spec string.
We're focusing here on two definitions only: *startfile:
and *endfile:
.
Below are extracted definitions of these specs. Save them to the specs.txt
file.
*startfile:
%{!mandroid|tno-android-ld:%{shared:; pg|p|profile:%{static-pie:grcrt1.o%s;:gcrt1.o%s}; static:crt1.o%s; static-pie:rcrt1.o%s; !no-pie:Scrt1.o%s; :crt1.o%s} crti.o%s %{static:crtbeginT.o%s; shared|static-pie|!no-pie:crtbeginS.o%s; :crtbegin.o%s} %{fvtable-verify=none:%s; fvtable-verify=preinit:vtv_start_preinit.o%s; fvtable-verify=std:vtv_start.o%s} ;:%{shared: crtbegin_so%O%s;: %{static: crtbegin_static%O%s;: crtbegin_dynamic%O%s}}}
*endfile:
%{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s} %{!mandroid|tno-android-ld:%{!static:%{fvtable-verify=none:%s; fvtable-verify=preinit:vtv_end_preinit.o%s; fvtable-verify=std:vtv_end.o%s}} %{static:crtend.o%s; shared|static-pie|!no-pie:crtendS.o%s; :crtend.o%s} crtn.o%s ;:%{shared: crtend_so%O%s;: crtend_android%O%s}}
You can easly spot names of startup files in these definitions: crtbegin.o%s
, crti.o%s
, crtend.o%s
, crtn.o%s
etc.
The %s
suffix is just indicator to GCC to expand preceding filename to a full path.
You may replace these filenames with yours and pass modified specs to GCC
using -specs=<specs-file>
command line option.
Note
--specs
command line option may be used multiple times.
So let's modify our specs.txt
file a bit. Prefix rpi-
to all names of startup files:
sed -E -i 's/([[:alpha:]]*crt[[:alnum:]_]*)(\.o|%O)(%s)/rpi-\1%O\3/g' specs.txt
Note
The %O
suffix is replaced by GCC with .o
. E.g. crtn%O
will be converted to crtn.o
.
Then link hello-world
application again using prepared specs file:
$ arm-linux-gnueabihf-gcc -specs=specs.txt --sysroot=/home/vscode/sysroot -L=/usr/lib -L=/usr/lib/gcc -marm -mcpu=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -o hello-world hello-world.o
/usr/lib/gcc-cross/arm-linux-gnueabihf/12/../../../../arm-linux-gnueabihf/bin/ld: cannot find rpi-Scrt1.o: No such file or directory
/usr/lib/gcc-cross/arm-linux-gnueabihf/12/../../../../arm-linux-gnueabihf/bin/ld: cannot find rpi-crti.o: No such file or directory
/usr/lib/gcc-cross/arm-linux-gnueabihf/12/../../../../arm-linux-gnueabihf/bin/ld: cannot find rpi-crtbeginS.o: No such file or directory
collect2: error: ld returned 1 exit status
Linker complains about missing startup files prefixed with rpi-
. So our modified specs definitely were taken into account 👍.
To satisfy linker let's create some rpi-prefixed symlinks:
cd ~/sysroot/usr/lib
ln -sr Scrt1.so rpi-Scrt1.so
ln -sr crti.o rpi-crti.o
ln -sr crtbeginS.o rpi-crtbeginS.o
ln -sr Scrt1.o rpi-Scrt1.o
ln -sr crtendS.o rpi-crtendS.o
ln -sr crtn.o rpi-crtn.o
And then invoke linker again:
$ arm-linux-gnueabihf-gcc -specs=specs.txt --sysroot=/home/vscode/sysroot -L=/usr/lib -L=/usr/lib/gcc -marm -mcpu=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -Wl,--trace -o hello-world hello-world.o
/home/vscode/sysroot/lib/arm-linux-gnueabihf/rpi-Scrt1.o
/home/vscode/sysroot/lib/arm-linux-gnueabihf/rpi-crti.o
/home/vscode/sysroot/lib/arm-linux-gnueabihf/rpi-crtbeginS.o
hello-world.o
/home/vscode/sysroot/usr/lib/gcc/libgcc.a
/home/vscode/sysroot/usr/lib/gcc/libgcc_s.so
/home/vscode/sysroot/usr/lib/libgcc_s.so.1
/home/vscode/sysroot/usr/lib/gcc/libgcc.a
/home/vscode/sysroot/usr/lib/libc.so
/home/vscode/sysroot/lib/arm-linux-gnueabihf/libc.so.6
/home/vscode/sysroot/usr/lib/arm-linux-gnueabihf/libc_nonshared.a
/home/vscode/sysroot/lib/ld-linux-armhf.so.3
/home/vscode/sysroot/usr/lib/arm-linux-gnueabihf/libc_nonshared.a
/home/vscode/sysroot/lib/ld-linux-armhf.so.3
/home/vscode/sysroot/usr/lib/gcc/libgcc.a
/home/vscode/sysroot/usr/lib/gcc/libgcc_s.so
/home/vscode/sysroot/usr/lib/libgcc_s.so.1
/home/vscode/sysroot/usr/lib/gcc/libgcc.a
/home/vscode/sysroot/lib/arm-linux-gnueabihf/rpi-crtendS.o
/home/vscode/sysroot/lib/arm-linux-gnueabihf/rpi-crtn.o
Now everything is going fine 😆. You have idea now how to specify non-default names of startup files in GCC.
Note
With specs it is possible to use startup files from custom directory also.
Just instead of %s
suffix specify full path to startup file.
In such case consider using getenv
function and specify custom directory
via environment variable:
%:getenv(RPIDIR /objects/)crtn%O
Here we covering only one little aspect of specs in GCC. Please refer to the following links for more information:
- Two articles (so far) on Geoff Wozniak's blog:
- GCC Documentation: Specifying Subprocesses and the Switches to Pass to Them.
- Using specs in CMake project page.