Linking external libraries (Running Rust from C)

Hey, I was trying to link some rust code I wrote according to these docs, but I can’t seem to get the build system to find my library. Here is the error ld spits out:

        LD       .obj/f7/firmware.elf
-L /home/paul/Documents/git/flipperzero-firmware/lib -lrusty -Wl,--wrap,_malloc_r -Wl,--wrap,_free_r -Wl,--wrap,_calloc_r -Wl,--wrap,_realloc_r -mcpu=cortex-m4 -mthum
b -mfpu=fpv4-sp-d16 -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -Wl,--start-group -lstdc++ -lsupc++ -Wl,--end-group -Ttargets/f7/stm32wb55x
x_flash_cm4_with_bootloader.ld -Wl,-Map=.obj/f7/firmware.map,--cref -Wl,--gc-sections -Wl,--undefined=uxTopUsedPriority -n
/opt/gcc-arm-none-eabi-10.3-2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld: .obj/f7//home/paul/Documents/git/flipperzero-firmware/applic
ations/my_app/my_app.o: in function `my_app_render_callback':
/home/paul/Documents/git/flipperzero-firmware/applications/my_app/my_app.c:29: undefined reference to `hello_rust'
collect2: error: ld returned 1 exit status
make: *** [/home/paul/Documents/git/flipperzero-firmware/make/rules.mk:46: .obj/f7/firmware.elf] Error 1

Here is a patch of the changes I made:

diff --git a/applications/my_app/librusty.h b/applications/my_app/librusty.h
new file mode 100644
index 00000000..fd889df3
--- /dev/null
+++ b/applications/my_app/librusty.h
@@ -0,0 +1,2 @@
+uint16_t hello_rust(void);
+
diff --git a/applications/my_app/my_app.c b/applications/my_app/my_app.c
index 92203cc7..00ad0b23 100644
--- a/applications/my_app/my_app.c
+++ b/applications/my_app/my_app.c
@@ -1,6 +1,7 @@
 #include <furi.h>
 #include <gui/gui.h>
 #include <input/input.h>
+#include <my_app/librusty.h>
 
 #define MOUSE_MOVE_SHORT 5
 #define MOUSE_MOVE_LONG 20
@@ -25,6 +26,8 @@ coordinate position = { .x =  0, .y =  0};
 static void my_app_render_callback(Canvas* canvas, void* ctx) {
     canvas_draw_dot(canvas, position.x, position.y);
     canvas_set_font(canvas, FontSecondary);
+    uint16_t thing = hello_rust();
+    canvas_draw_str(canvas, thing, thing, "hello");
 }
 
 static void my_app_input_callback(InputEvent* input_event, void* ctx) {
@@ -34,6 +37,7 @@ static void my_app_input_callback(InputEvent* input_event, void* ctx) {
     event.type = EventTypeInput;
     event.input = *input_event;
     osMessageQueuePut(event_queue, &event, 0, osWaitForever);
+    
 }
 
 int32_t my_app(void* p) {
diff --git a/lib/librusty.a b/lib/librusty.a
new file mode 100644
index 00000000..8f86d05c
Binary files /dev/null and b/lib/librusty.a differ
diff --git a/lib/librusty.d b/lib/librusty.d
new file mode 100644
index 00000000..9394d5db
--- /dev/null
+++ b/lib/librusty.d
@@ -0,0 +1 @@
+/home/paul/Documents/git/flipper_test_rust/target/thumbv7em-none-eabihf/release/librusty.a: /home/paul/Documents/git/flipper_test_rust/src/lib.rs
diff --git a/make/base.mk b/make/base.mk
index 696c4294..b175da60 100644
--- a/make/base.mk
+++ b/make/base.mk
@@ -5,7 +5,8 @@ CPP_SOURCES		=
 ASSETS			= 
 OPENOCD_OPTS	= 
 
-CFLAGS			= 
+CFLAGS			= -L /home/paul/Documents/git/flipperzero-firmware/lib -lrusty
+
 CPPFLAGS		= 
-LDFLAGS			= 
-SVD_FILE		= 
\ No newline at end of file
+LDFLAGS			= -L /home/paul/Documents/git/flipperzero-firmware/lib -lrusty
+SVD_FILE		= 
diff --git a/make/rules.mk b/make/rules.mk
index c67a3730..0e8f79c9 100644
--- a/make/rules.mk
+++ b/make/rules.mk
@@ -42,6 +42,7 @@ all: $(OBJ_DIR)/$(PROJECT).elf $(OBJ_DIR)/$(PROJECT).hex $(OBJ_DIR)/$(PROJECT).b
 
 $(OBJ_DIR)/$(PROJECT).elf: $(OBJECTS)
 	@echo "\tLD\t" $@
+	@echo $(LDFLAGS)
 	@$(LD) $(LDFLAGS) $(OBJECTS) -o $@
 	@$(SZ) $@
 
@@ -67,6 +68,7 @@ $(OBJ_DIR)/$(PROJECT).json: $(OBJ_DIR)/$(PROJECT).dfu
 
 $(OBJ_DIR)/%.o: %.c $(OBJ_DIR)/BUILD_FLAGS
 	@echo "\tCC\t" $(subst $(PROJECT_ROOT)/, , $<)
+	@echo $(CFLAGS)
 	@$(CC) $(CFLAGS) -c $< -o $@
 
 $(OBJ_DIR)/%.o: %.s $(OBJ_DIR)/BUILD_FLAGS
diff --git a/make/toolchain.mk b/make/toolchain.mk
index d6e653d3..7f4d95b4 100644
--- a/make/toolchain.mk
+++ b/make/toolchain.mk
@@ -27,4 +27,4 @@ endif
 
 CFLAGS		+= -fdata-sections -ffunction-sections -fno-math-errno -fstack-usage -MMD -MP -MF"$(@:%.o=%.d)"
 CPPFLAGS	+= -fno-threadsafe-statics -fno-use-cxa-atexit -fno-exceptions -fno-rtti
-LDFLAGS		+= -Wl,-Map=$(OBJ_DIR)/$(PROJECT).map,--cref -Wl,--gc-sections -Wl,--undefined=uxTopUsedPriority -n
\ No newline at end of file
+LDFLAGS		+= -Wl,-Map=$(OBJ_DIR)/$(PROJECT).map,--cref -Wl,--gc-sections -Wl,--undefined=uxTopUsedPriority -n

Any help would be appreciated! I don’t know much about GCC, LD, or Make, so I’m quite lost.

Just in case anyone was curious I did find a workaround.
Basically it boils down to this: the order in which you define objects to link matters

Adding flags to make/base.mk will put the flags to early, you have to add them to make/rules.mk to get them in the right order. Put the L and l flags after $(OBJECTS). But, the fun doesn’t stop there. Now you run into an issue with multiple definitions of the same function. To work around this I added a flag to ld to allow multiple definitions. :warning: This is probably bad: -z muldefs (could differ on your system?).

After those two edits, I was able to successfully call my rust code from C :tada:

1 Like