Unable to find tool "swift-autolink-extract" when using embedded swift

Hello!

I'm trying to use the embedded swift examples at swift/docs/EmbeddedSwift/UserManual.md at main · apple/swift · GitHub

In particular, my compile command is based on this code:

# To build an ARMv7 ELF object file:
$ swiftc -target armv7-unknown-none-eabi -enable-experimental-feature Embedded -wmo \
  input1.swift input2.swift ... -c -o output.o

I have test.swift which simply has the following code:

@_cdecl("plusOne")
func plusOne() {
    return
}

And I'm using this command to build:

swiftc -target armv7-unknown-none-eabi -enable-experimental-feature Embedded -wmo test.swift

However, I'm getting this error: error: unableToFind(tool: "swift-autolink-extract")

The only references to swift-autolink-extract I can find is a single post on this forum Swift-autolink-extract and swift-indent missing from recent toolchains which has no replies.

How do I fix this error?

I am on macOS Sonoma 14.5, using Swift 5.9 built into Xcode 15.4.0

1 Like

Even a basic setup from swift/docs/EmbeddedSwift/UserManual.md at main · apple/swift · GitHub

swiftc test.swift -enable-experimental-feature Embedded -wmo                            

Gives this confusing error:

<unknown>:0: error: module 'Swift' cannot be imported in embedded Swift mode
<unknown>:0: error: module '_StringProcessing' cannot be imported in embedded Swift mode
<unknown>:0: error: module '_Concurrency' cannot be imported in embedded Swift mode
<unknown>:0: error: module 'SwiftOnoneSupport' cannot be imported in embedded Swift mode
error: fatalError

These are all standard library modules, so it seems like the compiler can’t import the standard library. While you’re not explicitly using the standard library, Swift automatically imports it because you’ll likely need it.

@kubamracek, do you know what might be causing these errors?

This ^ is most likely the reason. Embedded Swift only really works with "main" nightly toolchains from Swift.org - Download Swift.

1 Like

Hello, thanks for the help!

I installed the main toolchain from Swift.org - Install Swift. swift --version reports the version as:

Apple Swift version 6.0-dev (LLVM 25f401da89fc02a, Swift dbe1a982d9252c9)
Target: arm64-apple-macosx14.0

Running the same command as before but with the new toolchain,

xcrun --toolchain swift \
    swiftc \
    -target armv7-unknown-none-eabi \
    -enable-experimental-feature Embedded \
    -wmo test.swift

Gives this error, which seems to imply that armv7-unknown-none-eabi isn't a valid target. Still, different error, thats progress.

<unknown>:0: error: could not find module 'Swift' for target 'armv7-unknown-none-eabi'; found: riscv32-none-none-eabi, armv4t-none-none-eabi, riscv64-none-none-eabi, i686-unknown-none-elf, armv7-apple-none-macho, armv6m-none-none-eabi, aarch64-none-none-elf, wasm64-unknown-none-wasm, arm64-apple-macos, i686-unknown-windows-msvc, x86_64-unknown-none-elf, x86_64-unknown-windows-msvc, arm64-apple-none-macho, armv7-none-none-eabi, armv7em-none-none-eabi, arm64e-apple-macos, x86_64-apple-macos, armv6-apple-none-macho, armv6m-apple-none-macho, wasm32-unknown-none-wasm, armv7em-apple-none-macho, armv6-none-none-eabi, at: /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-06-07-a.xctoolchain/usr/lib/swift/embedded/Swift.swiftmodule

Since armv7-unknown-none-eabi seems similar enough to armv7-none-none-eabi (one of the valid options according to the error), I replaced them:

xcrun --toolchain swift \
    swiftc -target armv7-none-none-eabi \
    -enable-experimental-feature Embedded \
    -wmo test.swift

And now, it seems to be failing at the linking stage, missing a few libraries and a builtin runtime?

error: link command failed with exit code 1 (use -v to see invocation)
ld.lld: error: unable to find library -lc
ld.lld: error: unable to find library -lm
ld.lld: error: unable to find library -lclang_rt.builtins-armv7
clang: error: ld.lld command failed with exit code 1 (use -v to see invocation)

How should I go about fixing this? A quick search for the first error gave me this reddit post: Reddit - Dive into anything which says that i can manually link some library, but I'm not exactly sure what to link.

And now, it seems to be failing at the linking stage, missing a few libraries and a builtin runtime?

Great, so the compilation worked. Getting an embedded firmware to link is really dependent on what exactly are you trying to get as a result -- what is the hardware you're targeting? Are you trying to use some vendor's SDK, toolchain, prebuilt libraries, and/or startup code?

Given how complex linking is for embedded software, I recommend splitting up the compilation and linking and controlling both separately, i.e. calling swiftc with -c to only produce an object file, and then calling whatever linker you want to use with whatever else you need to link in.

In the GitHub - apple/swift-embedded-examples: A collection of example projects using Embedded Swift repo, there are a few examples of two approaches:

Hi! I'm aware about -c to compile to an object file, except nothing was working so I thought I'd ask for help using build scripts that look more like the ones on the github.

This reply is a little bit long (sorry about that), so I'll try my best to split it into sections

Using -c

Running the command but with -c, it successfully outputs a .o file

xcrun --toolchain swift \
    swiftc -c \
    -o test.o \
    -target armv7-none-none-eabi \
    -enable-experimental-feature Embedded \
    -wmo test.swift

Which is of type ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (GNU/Linux), not stripped

What I'm doing

I'm trying to use Infineon's CY8CPROTO-062-4343W microcontroller. Its build process is a little bit complicated, needing an app named ModusToolbox with loads of custom Make scripts. An example project is here

My plan mainly follows that second example you provided. I want to build my swift code to an object file and the header file, then include the object file inside the directory where the Make scripts will automatically compile it (with Infineon's custom GCC) along with the other boilerplate C code.

Errors using vendor's GCC

ModusToolbox uses a custom build of gcc-arm to build the executable. Compiling a C file, it outputs files of type ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), not stripped. It seems quite similar to the one that swiftc outputs (bar the "SYSV" instead of "GNU/Linux"), but when I compile my test.o file I get a slew of confusing error messages

Confusing error messages
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: warning: test.o uses 32-bit enums yet the output is to use variable-size enums; use of enum values across objects may fail
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/lib/libc.a(libc_a-exit.o): in function `exit':
(.text.exit+0x28): undefined reference to `_exit'
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/lib/libc.a(libc_a-stack_protector.o): in function `__stack_chk_fail':
(.text.__stack_chk_fail+0x50): undefined reference to `_exit'
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: test.o: in function `$ss17swift_allocObject8metadata12requiredSize0E13AlignmentMaskSpys04HeapC0VGSpys13ClassMetadataVG_S2itF':
test.o:(.text+0xd4): undefined reference to `posix_memalign'
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: test.o: in function `$ss15swift_slowAllocySvSgSi_SitF':
test.o:(.text+0x16dc): undefined reference to `posix_memalign'
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/lib/libc.a(libc_a-sbrkr.o): in function `_sbrk_r':
(.text._sbrk_r+0x18): undefined reference to `_sbrk'
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/lib/libc.a(libc_a-closer.o): in function `_close_r':
(.text._close_r+0x18): undefined reference to `_close'
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/lib/libc.a(libc_a-lseekr.o): in function `_lseek_r':
(.text._lseek_r+0x24): undefined reference to `_lseek'
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/lib/libc.a(libc_a-readr.o): in function `_read_r':
(.text._read_r+0x24): undefined reference to `_read'
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/lib/libc.a(libc_a-signalr.o): in function `_kill_r':
(.text._kill_r+0x1c): undefined reference to `_kill'
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/lib/libc.a(libc_a-signalr.o): in function `_getpid_r':
(.text._getpid_r+0x4): undefined reference to `_getpid'
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/lib/libc.a(libc_a-writer.o): in function `_write_r':
(.text._write_r+0x24): undefined reference to `_write'
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/lib/libc.a(libc_a-fstatr.o): in function `_fstat_r':
(.text._fstat_r+0x1c): undefined reference to `_fstat'
/Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/bin/ld: /Applications/ModusToolbox/tools_3.2/gcc/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/lib/libc.a(libc_a-isattyr.o): in function `_isatty_r':
(.text._isatty_r+0x18): undefined reference to `_isatty'
collect2: error: ld returned 1 exit status

Which seem to boil down to:

  • The file made by swiftc uses 32 bit enums, but the gcc uses variable-size enums
  • Undefined references to a lot of symbols like _exit, posix_memalign, _sbrk, _close, _lseek, _read, _kill, _getpid, _write, _fstat, _isatty

Errors with .o output

When I tested outputting an object file outside of embedded swift, I needed to pass the -emit-library flag so that the output doesn't contain a main function. However, including that flag in my build command causes those linking errors again

  • The file made by swiftc uses 32 bit enums, but the gcc uses variable-size enums

You'll need to use compiler flags to match the GCC ABI in this aspect. See e.g. https://github.com/apple/swift-embedded-examples/blob/main/pico-blink-sdk/CMakeLists.txt#L13, -Xcc -fshort-enums is probably what you want.

  • Undefined references to a lot of symbols like _exit, posix_memalign, _sbrk, _close, _lseek, _read, _kill, _getpid, _write, _fstat, _isatty

You'll want -Xfrontend -function-sections to enable section-based dead-code stripping. See e.g. https://github.com/apple/swift-embedded-examples/blob/main/pico-blink-sdk/CMakeLists.txt#L14.

I needed to pass the -emit-library flag so that the output doesn't contain a main function.

Try -parse-as-library instead.

Thanks for the help, especially during a busy WWDC week.

Your suggestions for -Xcc -fshort-enums and -parse-as-library worked! They fix the enum and linking errors. I also dug around the ModusToolbox build commands and found a slew of flags that they also include.

I added the flags you suggested, and fed Infineon's flags to the compiler through -Xcc, excluding the -I include flags, since I'm testing this command out in a seperate directory and I want to figure out how to build the file before working out the vendor's SDK.

xcrun --toolchain swift \
    swiftc -c \
    -o test.o \
    -parse-as-library \
    -target armv7-none-none-eabi \
    -enable-experimental-feature Embedded \
    -emit-clang-header-path test.h \
    -wmo test.swift \
    -Xfrontend -function-sections \
    -Xcc -fshort-enums \
    ....
Full command with Infineon flags
xcrun --toolchain swift \
    swiftc -c \
    -o test.o \
    -parse-as-library \
    -target armv7-none-none-eabi \
    -enable-experimental-feature Embedded \
    -emit-clang-header-path test.h \
    -wmo test.swift \
    -Xfrontend -function-sections \
    -Xcc -fshort-enums \
    -Xcc "-m32" \
    -Xcc "-mthumb" \
    -Xcc "-mcpu=cortex-m4" \
    -Xcc "-Og" \
    -Xcc "-mfloat-abi=softfp" \
    -Xcc "-mfpu=fpv4-sp-d16" \
    -Xcc "-ffunction-sections" \
    -Xcc "-fdata-sections" \
    -Xcc "-march=armv7-a" \
    -Xcc "-mtune=cortex-m4" \
    -Xcc "-ftree-vectorize" \
    -Xcc "-ffast-math" \
    -Xcc "-g" \
    -Xcc "-Wall" \
    -Xcc "-pipe" \
    -Xcc "-DCOMPONENT_4343W" \
    -Xcc "-DCOMPONENT_APP_CY8CPROTO_062_4343W" \
    -Xcc "-DCOMPONENT_CAT1" \
    -Xcc "-DCOMPONENT_CAT1A" \
    -Xcc "-DCOMPONENT_CM0P_SLEEP" \
    -Xcc "-DCOMPONENT_CM4" \
    -Xcc "-DCOMPONENT_CM4_0" \
    -Xcc "-DCOMPONENT_Debug" \
    -Xcc "-DCOMPONENT_GCC_ARM" \
    -Xcc "-DCOMPONENT_HCI_UART" \
    -Xcc "-DCOMPONENT_MURATA_1DX" \
    -Xcc "-DCOMPONENT_MW_CAT1CM0P" \
    -Xcc "-DCOMPONENT_MW_CMSIS" \
    -Xcc "-DCOMPONENT_MW_CORE_LIB" \
    -Xcc "-DCOMPONENT_MW_CORE_MAKE" \
    -Xcc "-DCOMPONENT_MW_MTB_HAL_CAT1" \
    -Xcc "-DCOMPONENT_MW_MTB_PDL_CAT1" \
    -Xcc "-DCOMPONENT_MW_RECIPE_MAKE_CAT1A" \
    -Xcc "-DCOMPONENT_MW_RETARGET_IO" \
    -Xcc "-DCOMPONENT_PSOC6_02" \
    -Xcc "-DCOMPONENT_SOFTFP" \
    -Xcc "-DCOMPONENT_WIFI_INTERFACE_SDIO" \
    -Xcc "-DCORE_NAME_CM4_0=1" \
    -Xcc "-DCY8C624ABZI_S2D44" \
    -Xcc "-DCY_APPNAME_mtb_example_hal_hello_world" \
    -Xcc "-DCY_SUPPORTS_DEVICE_VALIDATION" \
    -Xcc "-DCY_TARGET_BOARD=APP_CY8CPROTO_062_4343W" \
    -Xcc "-DCY_USING_HAL" \
    -Xcc "-DDEBUG" \
    -Xcc "-DTARGET_APP_CY8CPROTO_062_4343W" 



OUTDATED, SEE EDIT BELOW

To build the object file to an executable using Infineon's GCC (this is a final step within their make process, after building all the source files to objects, it combines and links it into a single executable), I use this command (with the same flags):

Compile command
/Applications/ModusToolbox/tools_3.2/gcc/bin/arm-none-eabi-gcc \
    test.o \
    -mcpu=cortex-m4 \
    --specs=nano.specs \
    -Og \
    -mfloat-abi=softfp \
    -mfpu=fpv4-sp-d16 \
    -mthumb \
    -ffunction-sections \
    -fdata-sections \
    -ffat-lto-objects \
    -g \
    -Wall \
    -pipe \
    -DCOMPONENT_4343W \
    -DCOMPONENT_APP_CY8CPROTO_062_4343W \
    -DCOMPONENT_CAT1 \
    -DCOMPONENT_CAT1A \
    -DCOMPONENT_CM0P_SLEEP \
    -DCOMPONENT_CM4 \
    -DCOMPONENT_CM4_0 \
    -DCOMPONENT_Debug \
    -DCOMPONENT_GCC_ARM \
    -DCOMPONENT_HCI_UART \
    -DCOMPONENT_MURATA_1DX \
    -DCOMPONENT_MW_CAT1CM0P \
    -DCOMPONENT_MW_CMSIS \
    -DCOMPONENT_MW_CORE_LIB \
    -DCOMPONENT_MW_CORE_MAKE \
    -DCOMPONENT_MW_MTB_HAL_CAT1 \
    -DCOMPONENT_MW_MTB_PDL_CAT1 \
    -DCOMPONENT_MW_RECIPE_MAKE_CAT1A \
    -DCOMPONENT_MW_RETARGET_IO \
    -DCOMPONENT_PSOC6_02 \
    -DCOMPONENT_SOFTFP \
    -DCOMPONENT_WIFI_INTERFACE_SDIO \
    -DCORE_NAME_CM4_0=1 \
    -DCY8C624ABZI_S2D44 \
    -DCY_APPNAME_mtb_example_hal_hello_world \
    -DCY_SUPPORTS_DEVICE_VALIDATION \
    -DCY_TARGET_BOARD=APP_CY8CPROTO_062_4343W \
    -DCY_USING_HAL \
    -DDEBUG \
    -DTARGET_APP_CY8CPROTO_062_4343W \
    -I. \
    -Ibsps/TARGET_APP_CY8CPROTO-062-4343W \
    -Ibsps \
    -Ibsps/TARGET_APP_CY8CPROTO-062-4343W/bluetooth \
    -Ibsps/TARGET_APP_CY8CPROTO-062-4343W/config/GeneratedSource \
    -Ibsps/TARGET_APP_CY8CPROTO-062-4343W/config \
    -I../mtb_shared/retarget-io/release-v1.5.0 \
    -I../mtb_shared/cat1cm0p/release-v1.5.0/COMPONENT_CAT1A \
    -I../mtb_shared/cat1cm0p/release-v1.5.0 \
    -I../mtb_shared/cmsis/release-v5.8.0/Core/Include \
    -I../mtb_shared/cmsis/release-v5.8.0/Core \
    -I../mtb_shared/cmsis/release-v5.8.0 \
    -I../mtb_shared/core-lib/release-v1.4.1/include \
    -I../mtb_shared/core-lib/release-v1.4.1 \
    -I../mtb_shared/mtb-hal-cat1/release-v2.6.0/COMPONENT_CAT1A/include/pin_packages \
    -I../mtb_shared/mtb-hal-cat1/release-v2.6.0/COMPONENT_CAT1A/include \
    -I../mtb_shared/mtb-hal-cat1/release-v2.6.0/COMPONENT_CAT1A \
    -I../mtb_shared/mtb-hal-cat1/release-v2.6.0 \
    -I../mtb_shared/mtb-hal-cat1/release-v2.6.0/COMPONENT_CAT1A/include/triggers \
    -I../mtb_shared/mtb-hal-cat1/release-v2.6.0/include \
    -I../mtb_shared/mtb-hal-cat1/release-v2.6.0/include_pvt \
    -I../mtb_shared/mtb-hal-cat1/release-v2.6.0/source \
    -I../mtb_shared/mtb-pdl-cat1/release-v3.10.0/devices/COMPONENT_CAT1A/include \
    -I../mtb_shared/mtb-pdl-cat1/release-v3.10.0/devices/COMPONENT_CAT1A \
    -I../mtb_shared/mtb-pdl-cat1/release-v3.10.0/devices \
    -I../mtb_shared/mtb-pdl-cat1/release-v3.10.0 \
    -I../mtb_shared/mtb-pdl-cat1/release-v3.10.0/devices/COMPONENT_CAT1A/include/ip \
    -I../mtb_shared/mtb-pdl-cat1/release-v3.10.0/drivers/include \
    -I../mtb_shared/mtb-pdl-cat1/release-v3.10.0/drivers \
    -I../mtb_shared/mtb-pdl-cat1/release-v3.10.0/drivers/third_party/ethernet/include \
    -I../mtb_shared/mtb-pdl-cat1/release-v3.10.0/drivers/third_party/ethernet \
    -I../mtb_shared/mtb-pdl-cat1/release-v3.10.0/drivers/third_party

Where ../mtb_shared/ contains header and source files for the vendor's SDK.

It returns the same error (though without the enums error) with the undefined references, even with function-sections in both commands.


Searching around for what the errors mean, they seem to surface results from C++ about a missing standard library. Infineon's GCC is used for C, but i did get a "missing standard library" message when using the -static-stdlib flag:

<unknown>:0: error: unable to load standard library for target 'armv7-none-none-eabi'

Is that related?

EDIT: I just realised that I don't need to compile it myself, i have the .o file in the right format so I can just use Infineon's Make command to build it. It does successfully build, just not on the device. I'll play around with it and send a reply when I'm done

Hey everybody, thanks so much for the help! I managed to get Swift successfully running on the chip.

plusOne(); is code defined in Swift
Hello from Swift in the serial monitor at the bottom is a print statement from the Swift code!

For anybody in the future who references this for anything, some implementation notes:

  • You don't need to use a header file. I ended up removing the emit header file part of the build command because I realised the code compiles without it.
  • To define the function, you need to use a c declaration and make it public. Example:
@_cdecl("plusOne")
public func plusOne() { /* */ }

I'm going to try the following by myself, and maybe make a post in Community Showcase if I get it working nicely

  • Work with Infineon's SDK to control the microcontroller from Swift
  • Integrate building the swift file into the Infineon make process, so I don't need to manually compile the file before building

Once again, thank you kubamracek and filip-sakel for your time :)

1 Like

Wow, amazing, and exciting :) I'll be definitely interested in a Community Showcase on this.