Linking errors with Bindgen and CMake

Hello,

I'm attempting to get pico-tflmicro bindings for the Raspberry Pi Pico. I've actually successfully created the bindings, and from what I can tell, my CMake build is working properly; however, for some weird reason, when I try running/building the model I'm getting the following errors...

rust-lld: error: undefined symbol: getModel
          >>> referenced by hello_world.rs:44 (src/bin/hello_world.rs:44)
          >>>               /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/hello_world-591127861856ae71.2uos94dh9jm3yjgu.rcgu.o:(hello_world::____embassy_main_task::_$u7b$$u7b$closure$u7d$$u7d$::h048d0f64de96e722)

          rust-lld: error: undefined symbol: createEmptyResolver
          >>> referenced by hello_world.rs:45 (src/bin/hello_world.rs:45)
          >>>               /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/hello_world-591127861856ae71.2uos94dh9jm3yjgu.rcgu.o:(hello_world::____embassy_main_task::_$u7b$$u7b$closure$u7d$$u7d$::h048d0f64de96e722)

          rust-lld: error: undefined symbol: addResolver
          >>> referenced by hello_world.rs:46 (src/bin/hello_world.rs:46)
          >>>               /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/hello_world-591127861856ae71.2uos94dh9jm3yjgu.rcgu.o:(hello_world::____embassy_main_task::_$u7b$$u7b$closure$u7d$$u7d$::h048d0f64de96e722)

          rust-lld: error: undefined symbol: getInterpreter
          >>> referenced by hello_world.rs:47 (src/bin/hello_world.rs:47)
          >>>               /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/hello_world-591127861856ae71.2uos94dh9jm3yjgu.rcgu.o:(hello_world::____embassy_main_task::_$u7b$$u7b$closure$u7d$$u7d$::h048d0f64de96e722)

          rust-lld: error: undefined symbol: getTensorInput
          >>> referenced by hello_world.rs:49 (src/bin/hello_world.rs:49)
          >>>               /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/hello_world-591127861856ae71.2uos94dh9jm3yjgu.rcgu.o:(hello_world::____embassy_main_task::_$u7b$$u7b$closure$u7d$$u7d$::h048d0f64de96e722)

This is weird, as the library I'm linking to statically has the following definitions.

wrapped.h

#ifndef WRAPPED_TF_HEADERS
#define WRAPPED_TF_HEADERS
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/micro_log.h"
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/micro/system_setup.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/c/common.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct MicroInterpreter MicroInterpreter;
typedef struct Model Model;
typedef struct MicroMutableOpResolver MicroMutableOpResolver;

enum AddableResolver {
  AddAbs,
  AddAdd,
  AddAddN,
  AddArgMax,
  AddArgMin,
  AddAssignVariable,
  AddAveragePool2D,
  AddBatchMatMul,
  AddBatchToSpaceNd,
  AddBroadcastArgs,
  AddBroadcastTo,
  AddCallOnce,
  AddCast,
  AddCeil,
  AddCircularBuffer,
  AddConcatenation,
  AddConv2D,
  AddCos,
  AddCumSum,
  AddDelay,
  AddDepthToSpace,
  AddDepthwiseConv2D,
  AddDequantize,
  AddDetectionPostprocess,
  AddDiv,
  AddEmbeddingLookup,
  AddEnergy,
  AddElu,
  AddEqual,
  AddEthosU,
  AddExp,
  AddExpandDims,
  AddFftAutoScale,
  AddFill,
  AddFilterBank,
  AddFilterBankLog,
  AddFilterBankSquareRoot,
  AddFilterBankSpectralSubtraction,
  AddFloor,
  AddFloorDiv,
  AddFloorMod,
  AddFramer,
  AddFullyConnected,
  AddGather,
  AddGatherNd,
  AddGreater,
  AddGreaterEqual,
  AddHardSwish,
  AddIf,
  AddIrfft,
  AddL2Normalization,
  AddL2Pool2D,
  AddLeakyRelu,
  AddLess,
  AddLessEqual,
  AddLog,
  AddLogicalAnd,
  AddLogicalNot,
  AddLogicalOr,
  AddLogistic,
  AddLogSoftmax,
  AddMaximum,
  AddMaxPool2D,
  AddMirrorPad,
  AddMean,
  AddMinimum,
  AddMul,
  AddNeg,
  AddNotEqual,
  AddOverlapAdd,
  AddPack,
  AddPad,
  AddPadV2,
  AddPCAN,
  AddPrelu,
  AddQuantize,
  AddReadVariable,
  AddReduceMax,
  AddRelu,
  AddRelu6,
  AddReshape,
  AddResizeBilinear,
  AddResizeNearestNeighbor,
  AddRfft,
  AddRound,
  AddRsqrt,
  AddSelectV2,
  AddShape,
  AddSin,
  AddSlice,
  AddSoftmax,
  AddSpaceToBatchNd,
  AddSpaceToDepth,
  AddSplit,
  AddSplitV,
  AddSqueeze,
  AddSqrt,
  AddSquare,
  AddSquaredDifference,
  AddStridedSlice,
  AddStacker,
  AddSub,
  AddSum,
  AddSvdf,
  AddTanh,
  AddTransposeConv,
  AddTranspose,
  AddUnpack,
  AddUnidirectionalSequenceLSTM,
  AddVarHandle,
  AddWhile,
  AddWindow,
  AddZerosLike
};

const Model* getModel(const void* buffer, size_t len);
void destroyModel(const Model* model);
MicroInterpreter* getInterpreter(const Model* model, MicroMutableOpResolver* resolver, int kTensorArenaSize);
void destroyInterpreter(MicroInterpreter* interpreter);
TfLiteStatus allocateTensors(MicroInterpreter* interpreter);
MicroMutableOpResolver* createEmptyResolver();
void destroyResolver(MicroMutableOpResolver* resolver);
TfLiteStatus addCustomResolver(MicroMutableOpResolver* resolver, char* name, TFLMRegistration* registration);
TfLiteStatus addResolver(MicroMutableOpResolver* resolver, AddableResolver resolverToAdd);
TfLiteTensor* getTensorInput(MicroInterpreter* interpreter, size_t n);
TfLiteStatus invokeInterpreter(MicroInterpreter* interpreter);
TfLiteTensor* getTensorOutput(MicroInterpreter* interpreter, size_t n);


#ifdef __cplusplus
}
#endif
#endif

wrapped.cpp

#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/micro_log.h"
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/micro/system_setup.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/c/common.h"
#include "wrapped.h"

extern "C" {
  const Model* getModel(const void* buffer, size_t len) {
    auto verifier = flatbuffers::Verifier((uint8_t *)buffer, len);
    if (!::tflite::VerifyModelBuffer(verifier)) {
        return NULL;
    }

    const tflite::Model* model = ::tflite::GetModel(buffer);
    return reinterpret_cast<const Model*>(model);
  }

  void destroyModel(const Model* model) {
    delete reinterpret_cast<const tflite::Model*>(model);
  }

  MicroInterpreter* getInterpreter(const Model* model, MicroMutableOpResolver* resolver, int kTensorArenaSize) {
    const tflite::Model* tflite_model = reinterpret_cast<const tflite::Model*>(model);
    tflite::MicroMutableOpResolver<128>* tflite_resolver = reinterpret_cast<tflite::MicroMutableOpResolver<128>*>(resolver);

    uint8_t tensor_arena[kTensorArenaSize];
    tflite::MicroInterpreter* interpreter = nullptr;

    static tflite::MicroInterpreter static_interpreter(tflite_model, *tflite_resolver, tensor_arena, kTensorArenaSize);
    interpreter = &static_interpreter;

    return reinterpret_cast<MicroInterpreter*>(interpreter);
}

  void destroyInterpreter(MicroInterpreter* interpreter) {
    delete reinterpret_cast<tflite::MicroInterpreter*>(interpreter);
  }

  TfLiteStatus allocateTensors(MicroInterpreter* interpreter) {
    tflite::MicroInterpreter* tflite_interpreter = reinterpret_cast<tflite::MicroInterpreter*>(interpreter);

    if (tflite_interpreter == nullptr) {
      printf("Got NULL!\n");
      return kTfLiteError;
    }

    printf("Allocated Tensors!\n");
    TfLiteStatus allocate_status = tflite_interpreter->AllocateTensors();
    return allocate_status;
  }

  MicroMutableOpResolver* createEmptyResolver() {
    static tflite::MicroMutableOpResolver<128>* resolver = new tflite::MicroMutableOpResolver<128>();
    return reinterpret_cast<MicroMutableOpResolver*>(resolver);
  }

  void destroyResolver(MicroMutableOpResolver* resolver) {
    delete reinterpret_cast<tflite::MicroMutableOpResolver<128>*>(resolver);
  }

  TfLiteStatus addCustomResolver(MicroMutableOpResolver* resolver, char* name, TFLMRegistration* registration) {
    static tflite::MicroMutableOpResolver<128>* tflite_resolver = reinterpret_cast<tflite::MicroMutableOpResolver<128>*>(resolver);
    return tflite_resolver->AddCustom(name, registration);
  }

  TfLiteStatus addResolver(MicroMutableOpResolver* resolver, AddableResolver resolverToAdd) {
    static tflite::MicroMutableOpResolver<128>* tflite_resolver = reinterpret_cast<tflite::MicroMutableOpResolver<128>*>(resolver);
    switch (resolverToAdd) {
      case AddAbs:
        return tflite_resolver->AddAbs();
        break;
      case AddAdd:
        return tflite_resolver->AddAdd();
        break;
      case AddAddN:
        return tflite_resolver->AddAddN();
        break;
      case AddArgMax:
        return tflite_resolver->AddArgMax();
        break;
      case AddArgMin:
        return tflite_resolver->AddArgMin();
        break;
      case AddAssignVariable:
        return tflite_resolver->AddAssignVariable();
        break;
      case AddAveragePool2D:
        return tflite_resolver->AddAveragePool2D();
        break;
      case AddBatchMatMul:
        return tflite_resolver->AddBatchMatMul();
        break;
      case AddBatchToSpaceNd:
        return tflite_resolver->AddBatchToSpaceNd();
        break;
      case AddBroadcastArgs:
        return tflite_resolver->AddBroadcastArgs();
        break;
      case AddBroadcastTo:
        return tflite_resolver->AddBroadcastTo();
        break;
      case AddCallOnce:
        return tflite_resolver->AddCallOnce();
        break;
      case AddCast:
        return tflite_resolver->AddCast();
        break;
      case AddCeil:
        return tflite_resolver->AddCeil();
        break;
      case AddCircularBuffer:
        return tflite_resolver->AddCircularBuffer();
        break;
      case AddConcatenation:
        return tflite_resolver->AddConcatenation();
        break;
      case AddConv2D:
        return tflite_resolver->AddConv2D();
        break;
      case AddCos:
        return tflite_resolver->AddCos();
        break;
      case AddCumSum:
        return tflite_resolver->AddCumSum();
        break;
      case AddDelay:
        return tflite_resolver->AddDelay();
        break;
      case AddDepthToSpace:
        return tflite_resolver->AddDepthToSpace();
        break;
      case AddDepthwiseConv2D:
        return tflite_resolver->AddDepthwiseConv2D();
        break;
      case AddDequantize:
        return tflite_resolver->AddDequantize();
        break;
      case AddDetectionPostprocess:
        return tflite_resolver->AddDetectionPostprocess();
        break;
      case AddDiv:
        return tflite_resolver->AddDiv();
        break;
      case AddEmbeddingLookup:
        return tflite_resolver->AddEmbeddingLookup();
        break;
      case AddEnergy:
        return tflite_resolver->AddEnergy();
        break;
      case AddElu:
        return tflite_resolver->AddElu();
        break;
      case AddEqual:
        return tflite_resolver->AddEqual();
        break;
      case AddEthosU:
        return tflite_resolver->AddEthosU();
        break;
      case AddExp:
        return tflite_resolver->AddExp();
        break;
      case AddExpandDims:
        return tflite_resolver->AddExpandDims();
        break;
      case AddFftAutoScale:
        return tflite_resolver->AddFftAutoScale();
        break;
      case AddFill:
        return tflite_resolver->AddFill();
        break;
      case AddFilterBank:
        return tflite_resolver->AddFilterBank();
        break;
      case AddFilterBankLog:
        return tflite_resolver->AddFilterBankLog();
        break;
      case AddFilterBankSquareRoot:
        return tflite_resolver->AddFilterBankSquareRoot();
        break;
      case AddFilterBankSpectralSubtraction:
        return tflite_resolver->AddFilterBankSpectralSubtraction();
        break;
      case AddFloor:
        return tflite_resolver->AddFloor();
        break;
      case AddFloorDiv:
        return tflite_resolver->AddFloorDiv();
        break;
      case AddFloorMod:
        return tflite_resolver->AddFloorMod();
        break;
      case AddFramer:
        return tflite_resolver->AddFramer();
        break;
      case AddFullyConnected:
        return tflite_resolver->AddFullyConnected();
        break;
      case AddGather:
        return tflite_resolver->AddGather();
        break;
      case AddGatherNd:
        return tflite_resolver->AddGatherNd();
        break;
      case AddGreater:
        return tflite_resolver->AddGreater();
        break;
      case AddGreaterEqual:
        return tflite_resolver->AddGreaterEqual();
        break;
      case AddHardSwish:
        return tflite_resolver->AddHardSwish();
        break;
      case AddIf:
        return tflite_resolver->AddIf();
        break;
      case AddIrfft:
        return tflite_resolver->AddIrfft();
        break;
      case AddL2Normalization:
        return tflite_resolver->AddL2Normalization();
        break;
      case AddL2Pool2D:
        return tflite_resolver->AddL2Pool2D();
        break;
      case AddLeakyRelu:
        return tflite_resolver->AddLeakyRelu();
        break;
      case AddLess:
        return tflite_resolver->AddLess();
        break;
      case AddLessEqual:
        return tflite_resolver->AddLessEqual();
        break;
      case AddLog:
        return tflite_resolver->AddLog();
        break;
      case AddLogicalAnd:
        return tflite_resolver->AddLogicalAnd();
        break;
      case AddLogicalNot:
        return tflite_resolver->AddLogicalNot();
        break;
      case AddLogicalOr:
        return tflite_resolver->AddLogicalOr();
        break;
      case AddLogistic:
        return tflite_resolver->AddLogistic();
        break;
      case AddLogSoftmax:
        return tflite_resolver->AddLogSoftmax();
        break;
      case AddMaximum:
        return tflite_resolver->AddMaximum();
        break;
      case AddMaxPool2D:
        return tflite_resolver->AddMaxPool2D();
        break;
      case AddMirrorPad:
        return tflite_resolver->AddMirrorPad();
        break;
      case AddMean:
        return tflite_resolver->AddMean();
        break;
      case AddMinimum:
        return tflite_resolver->AddMinimum();
        break;
      case AddMul:
        return tflite_resolver->AddMul();
        break;
      case AddNeg:
        return tflite_resolver->AddNeg();
        break;
      case AddNotEqual:
        return tflite_resolver->AddNotEqual();
        break;
      case AddOverlapAdd:
        return tflite_resolver->AddOverlapAdd();
        break;
      case AddPack:
        return tflite_resolver->AddPack();
        break;
      case AddPad:
        return tflite_resolver->AddPad();
        break;
      case AddPadV2:
        return tflite_resolver->AddPadV2();
        break;
      case AddPCAN:
        return tflite_resolver->AddPCAN();
        break;
      case AddPrelu:
        return tflite_resolver->AddPrelu();
        break;
      case AddQuantize:
        return tflite_resolver->AddQuantize();
        break;
      case AddReadVariable:
        return tflite_resolver->AddReadVariable();
        break;
      case AddReduceMax:
        return tflite_resolver->AddReduceMax();
        break;
      case AddRelu:
        return tflite_resolver->AddRelu();
        break;
      case AddRelu6:
        return tflite_resolver->AddRelu6();
        break;
      case AddReshape:
        return tflite_resolver->AddReshape();
        break;
      case AddResizeBilinear:
        return tflite_resolver->AddResizeBilinear();
        break;
      case AddResizeNearestNeighbor:
        return tflite_resolver->AddResizeNearestNeighbor();
        break;
      case AddRfft:
        return tflite_resolver->AddRfft();
        break;
      case AddRound:
        return tflite_resolver->AddRound();
        break;
      case AddRsqrt:
        return tflite_resolver->AddRsqrt();
        break;
      case AddSelectV2:
        return tflite_resolver->AddSelectV2();
        break;
      case AddShape:
        return tflite_resolver->AddShape();
        break;
      case AddSin:
        return tflite_resolver->AddSin();
        break;
      case AddSlice:
        return tflite_resolver->AddSlice();
        break;
      case AddSoftmax:
        return tflite_resolver->AddSoftmax();
        break;
      case AddSpaceToBatchNd:
        return tflite_resolver->AddSpaceToBatchNd();
        break;
      case AddSpaceToDepth:
        return tflite_resolver->AddSpaceToDepth();
        break;
      case AddSplit:
        return tflite_resolver->AddSplit();
        break;
      case AddSplitV:
        return tflite_resolver->AddSplitV();
        break;
      case AddSqueeze:
        return tflite_resolver->AddSqueeze();
        break;
      case AddSqrt:
        return tflite_resolver->AddSqrt();
        break;
      case AddSquare:
        return tflite_resolver->AddSquare();
        break;
      case AddSquaredDifference:
        return tflite_resolver->AddSquaredDifference();
        break;
      case AddStridedSlice:
        return tflite_resolver->AddStridedSlice();
        break;
      case AddStacker:
        return tflite_resolver->AddStacker();
        break;
      case AddSub:
        return tflite_resolver->AddSub();
        break;
      case AddSum:
        return tflite_resolver->AddSum();
        break;
      case AddSvdf:
        return tflite_resolver->AddSvdf();
        break;
      case AddTanh:
        return tflite_resolver->AddTanh();
        break;
      case AddTransposeConv:
        return tflite_resolver->AddTransposeConv();
        break;
      case AddTranspose:
        return tflite_resolver->AddTranspose();
        break;
      case AddUnpack:
        return tflite_resolver->AddUnpack();
        break;
      case AddUnidirectionalSequenceLSTM:
        return tflite_resolver->AddUnidirectionalSequenceLSTM();
        break;
      case AddVarHandle:
        return tflite_resolver->AddVarHandle();
        break;
      case AddWhile:
        return tflite_resolver->AddWhile();
        break;
      case AddWindow:
        return tflite_resolver->AddWindow();
        break;
      case AddZerosLike:
        return tflite_resolver->AddZerosLike();
        break;
      default:
        return kTfLiteError;
    }
  }

  TfLiteTensor* getTensorInput(MicroInterpreter* interpreter, size_t n) {
    tflite::MicroInterpreter* tflite_interpreter = reinterpret_cast<tflite::MicroInterpreter*>(interpreter);
    if (tflite_interpreter == nullptr) {
      return NULL;
    }

    return tflite_interpreter->input(n);
  }

  TfLiteStatus invokeInterpreter(MicroInterpreter* interpreter) {
    tflite::MicroInterpreter* tflite_interpreter = reinterpret_cast<tflite::MicroInterpreter*>(interpreter);
    if (tflite_interpreter == nullptr) {
      return kTfLiteError;
    }

    return tflite_interpreter->Invoke();
  }

  TfLiteTensor* getTensorOutput(MicroInterpreter* interpreter, size_t n) {
    tflite::MicroInterpreter* tflite_interpreter = reinterpret_cast<tflite::MicroInterpreter*>(interpreter);
    if (tflite_interpreter == nullptr) {
      return NULL;
    }

    return tflite_interpreter->output(n);
  }

}

With this CMakelists.txt


cmake_minimum_required(VERSION 3.12)

project(exe C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)

add_library(pico-tflmicro-wrapped STATIC)

target_include_directories(pico-tflmicro-wrapped
  PUBLIC
  ${CMAKE_CURRENT_LIST_DIR}/src
)

set_target_properties(
  pico-tflmicro-wrapped
  PROPERTIES
  COMPILE_FLAGS -Os
  COMPILE_FLAGS -fno-rtti
  COMPILE_FLAGS -fno-exceptions
  COMPILE_FLAGS -fno-threadsafe-statics
  COMPILE_FLAGS -nostdlib
)

target_link_libraries(
  pico-tflmicro-wrapped
  pico-tflmicro
)

target_sources(pico-tflmicro
  PRIVATE
  ${CMAKE_CURRENT_LIST_DIR}/src/wrapped.cpp
  ${CMAKE_CURRENT_LIST_DIR}/src/wrapped.h
)


add_executable(exe "")

target_include_directories(exe
  PRIVATE
  ${CMAKE_CURRENT_LIST_DIR}/.
)

set_target_properties(
  exe
  PROPERTIES
  COMPILE_FLAGS -fno-rtti
  COMPILE_FLAGS -fno-exceptions
  COMPILE_FLAGS -fno-threadsafe-statics
  COMPILE_FLAGS -nostdlib
)

target_sources(exe
  PRIVATE
  ${CMAKE_CURRENT_LIST_DIR}/empty.c
)

target_link_libraries(
  exe
  pico-tflmicro-wrapped
)

And this build.rs

// Build procedure is based on esp-idf-sys: https://github.com/esp-rs/esp-idf-sys/blob/155299bde700905fd2ddb040d7a13fb73559ac68/build/native/cargo_driver.rs

use std::path::PathBuf;

use anyhow::{Context, Error};
use embuild::build::LinkArgsBuilder;
use embuild::cmake::file_api::codemodel::target::CompileGroup;
use embuild::cmake::file_api::codemodel::Language;
use embuild::cmake::file_api::{ObjKind, Query};
use embuild::cmake::Config;
use embuild::{bindgen, cargo};

#[allow(unused_macros)]
macro_rules! p {
    ($($tokens: tt)*) => {
        println!("cargo:warning={}", format!($($tokens)*))
    }
}

pub fn get_sysroot(target: &str) -> Result<String, anyhow::Error> {
    let mut sysroot = cc::Build::new();
    let sysroot = sysroot
        .target(target)
        .get_compiler()
        .to_command()
        .arg("--print-sysroot")
        .output()
        .context("Couldn't find target GCC executable.")
        .and_then(|output| {
            if output.status.success() {
                Ok(String::from_utf8(output.stdout)?)
            } else {
                panic!("Couldn't read output from GCC.")
            }
        })?;

    Ok(sysroot.trim().to_string())
}

fn generate_bindings(compile_group: &CompileGroup, compiler: &PathBuf, target: &str) {
    let cpp_headers = get_cpp_headers(compiler).expect("Couldn't find headers");

    let sysroot = get_sysroot(target).expect("Failed to get sysroot");
    let binder = bindgen::Factory::from_cmake(compile_group)
        .unwrap()
        .with_linker(compiler)
        .with_sysroot(sysroot);

    let mut binder = binder
        .builder()
        .unwrap()
        .clang_arg(format!("-I/submodules/pico-tflmicro/src/tensorflow"))
        .derive_eq(true)
        .use_core()
        .clang_arg("-xc++")
        .clang_arg("-std=c++11")
        .allowlist_recursively(true)
        .prepend_enum_name(false)
        .impl_debug(true)
        .layout_tests(false)
        .enable_cxx_namespaces()
        .derive_default(true)
        .size_t_is_usize(true)
        .ctypes_prefix("cty")
        // Types - blacklist
        .blocklist_type("std")
        .derive_partialeq(true)
        .derive_eq(true)
        .detect_include_paths(false);

    p!("cpp headers: {:?}", cpp_headers);
    for include in cpp_headers {
        binder = binder.clang_arg(format!(
            "-I{}",
            include.into_os_string().into_string().unwrap()
        ));
    }

    let wrapper_header = "submodules/pico-tflmicro/wrapped/src/wrapped.h";

    println!("cargo:rerun-if-changed={}", wrapper_header);

    let _ = binder
        .allowlist_file(wrapper_header)
        .header(wrapper_header)
        .generate()
        .unwrap()
        .write_to_file("src/bindings.rs");
}

pub fn get_compiler(target: &str) -> String {
    let mut compiler = cc::Build::new();
    let compiler = compiler.target(target).get_compiler();
    let compiler = compiler.path();

    compiler
        .to_str()
        .expect(&format!("Failed to find compiler for target '{}'", target))
        .to_string()
}

pub fn get_cpp_headers(compiler: &PathBuf) -> Result<Vec<PathBuf>, anyhow::Error> {
    let mut cpp_headers = cc::Build::new();
    cpp_headers
        .cpp(true)
        .no_default_flags(true)
        .compiler(compiler)
        .get_compiler()
        .to_command()
        .arg("-E")
        .arg("-Wp,-v")
        .arg("-xc++")
        .arg(".")
        .output()
        .context("Couldn't find target GCC executable.")
        .and_then(|output| {
            // We have to scrape the gcc console output to find where
            // the c++ headers are. If we only needed the c headers we
            // could use `--print-file-name=include` but that's not
            // possible.
            let gcc_out = String::from_utf8(output.stderr)?;

            // Scrape the search paths
            let search_start = gcc_out.find("search starts here").unwrap();
            let search_paths: Vec<PathBuf> = gcc_out[search_start..]
                .split('\n')
                .map(|p| PathBuf::from(p.trim()))
                .filter(|path| path.exists())
                .collect();

            Ok(search_paths)
        })
}

fn main() {
    let target = "thumbv6m-none-eabi";
    let cmake_build_dir = cargo::out_dir().join("build");

    p!("cmake build dir: {}", cmake_build_dir.display());

    // Set CMake to output API files https://cmake.org/cmake/help/git-stage/manual/cmake-file-api.7.html
    let query = Query::new(
        &cmake_build_dir,
        "cargo",
        &[ObjKind::Codemodel, ObjKind::Toolchains, ObjKind::Cache],
    )
    .unwrap();

    // Build C part
    let dst = Config::new("submodules/pico-tflmicro")
        .generator("Ninja")
        .build_target("exe")
        .build();

    p!("dst: {:?}", dst.display());
    println!("cargo:rustc-link-search={}/{}", cmake_build_dir.display(), "wrapped");
    println!("cargo:rustc-link-lib=static=pico-tflmicro-wrapped");

    // Retrieve information from CMake API files
    let replies = query.get_replies().unwrap();
    let codemodel = replies.get_codemodel().unwrap();
    let exe_target = codemodel
        .into_first_conf()
        .get_target("exe")
        .unwrap()
        .unwrap();
    let link = exe_target.link.unwrap();
    let compile_group = exe_target
        .compile_groups
        .get(0)
        .expect("Failed to get compile group");

    let link_args = LinkArgsBuilder::try_from(&link)
        .unwrap()
        .linker("arm-none-eabi-gcc")
        .working_directory(&cmake_build_dir)
        .build()
        .unwrap();
    p!("linker args {:?}", link_args);
    // link_args.output();

    let compiler = replies.get_toolchains();

    let compiler = compiler
        .and_then(|mut t| {
            t.take(Language::C)
                .ok_or_else(|| Error::msg("No C toolchain"))
        })
        .and_then(|t| {
            t.compiler
                .path
                .ok_or_else(|| Error::msg("No compiler path set"))
        })
        .context("Could not determine the compiler from cmake")
        .unwrap();

    // #[cfg(feature = "generate_bindings")]
    generate_bindings(compile_group, &compiler, target);
}

Please note that these errors will be generated even if you do not have a Raspberry Pi Pico to build.

Here is the code I've been developing for pico-tflmicro-rs. If you want to test the code just follow the instructions in the README.md.

Any help would be greatly appreciated!!

Oh my goodness, I've found the issue...

target_sources(pico-tflmicro
  PRIVATE
  ${CMAKE_CURRENT_LIST_DIR}/src/wrapped.cpp
  ${CMAKE_CURRENT_LIST_DIR}/src/wrapped.h
)

On this line I needed to change pico-tflmicro to pico-tflmicro-wrapped...

But I'm still having linking errors

  = note: rust-lld: error: undefined symbol: malloc
          >>> referenced by new_delete.cpp:13 (/Users/bjorn/git/pico-sdk/src/rp2_common/pico_standard_link/new_delete.cpp:13)
          >>>               new_delete.cpp.obj:(operator new(unsigned int)) in archive /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/libpico_tflmicro_sys-070094f974eb28c9.rlib

          rust-lld: error: undefined symbol: printf
          >>> referenced by runtime.c:224 (/Users/bjorn/git/pico-sdk/src/rp2_common/pico_runtime/runtime.c:224)
          >>>               runtime.c.obj:(__assert_func) in archive /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/libpico_tflmicro_sys-070094f974eb28c9.rlib

          rust-lld: error: undefined symbol: tflite::MicroInterpreter::~MicroInterpreter()
          >>> referenced by wrapped.cpp:31 (/Users/bjorn/Desktop/pico-tflmicro-rs/pico-tflmicro-sys/submodules/pico-tflmicro/wrapped/src/wrapped.cpp:31)
          >>>               wrapped.cpp.obj:(__tcf_0) in archive /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/libpico_tflmicro_sys-070094f974eb28c9.rlib

          rust-lld: error: undefined symbol: strcmp
          >>> referenced by micro_mutable_op_resolver.h:67 (/Users/bjorn/Desktop/pico-tflmicro-rs/pico-tflmicro-sys/submodules/pico-tflmicro/src/tensorflow/lite/micro/micro_mutable_op_resolver.h:67)
          >>>               wrapped.cpp.obj:(tflite::MicroMutableOpResolver<128u>::FindOp(char const*) const) in archive /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/libpico_tflmicro_sys-070094f974eb28c9.rlib

          rust-lld: error: undefined symbol: __cxa_guard_acquire
          >>> referenced by wrapped.cpp:31 (/Users/bjorn/Desktop/pico-tflmicro-rs/pico-tflmicro-sys/submodules/pico-tflmicro/wrapped/src/wrapped.cpp:31)
          >>>               wrapped.cpp.obj:(getInterpreter) in archive /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/libpico_tflmicro_sys-070094f974eb28c9.rlib
          >>> referenced by wrapped.cpp:55 (/Users/bjorn/Desktop/pico-tflmicro-rs/pico-tflmicro-sys/submodules/pico-tflmicro/wrapped/src/wrapped.cpp:55)
          >>>               wrapped.cpp.obj:(createEmptyResolver) in archive /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/libpico_tflmicro_sys-070094f974eb28c9.rlib
          >>> referenced by wrapped.cpp:69 (/Users/bjorn/Desktop/pico-tflmicro-rs/pico-tflmicro-sys/submodules/pico-tflmicro/wrapped/src/wrapped.cpp:69)
          >>>               wrapped.cpp.obj:(addResolver) in archive /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/libpico_tflmicro_sys-070094f974eb28c9.rlib

          rust-lld: error: undefined symbol: tflite::MicroInterpreter::MicroInterpreter(tflite::Model const*, tflite::MicroOpResolver const&, unsigned char*, unsigned int, tflite::MicroResourceVariables*, tflite::MicroProfilerInterface*, bool)
          >>> referenced by wrapped.cpp:31 (/Users/bjorn/Desktop/pico-tflmicro-rs/pico-tflmicro-sys/submodules/pico-tflmicro/wrapped/src/wrapped.cpp:31)
          >>>               wrapped.cpp.obj:(getInterpreter) in archive /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/libpico_tflmicro_sys-070094f974eb28c9.rlib
...

I've also had this error in the past, which prompted rewriting to a new codebase. Any hints would be greatly appreciated!

You can’t link a static library to any other library. The library that has your definitions is pico-tflmicro and you’re not linking to it in your build.rs.

Regarding the error in your second post, it’s because you build with -nostdlib. Maybe disabling exceptions or the -fno-threadsafe-statics causes the __cxa_guard_acquire issue. You might also need to explicitly link libstdc++ or libsupc++ in your build.rs.

1 Like
cmake_minimum_required(VERSION 3.12)

# Pull in PICO SDK (must be before project)
include(pico_sdk_import.cmake)

project(pico-tflmicro C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)

pico_sdk_init()

add_library(pico-tflmicro STATIC)

target_include_directories(pico-tflmicro
  PUBLIC
  ${CMAKE_CURRENT_LIST_DIR}/src/
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/ruy
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/gemmlowp
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/kissfft
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/flatbuffers
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/cmsis/CMSIS/Core/Include
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/flatbuffers/include
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/cmsis_nn/Include
)

target_compile_definitions(
  pico-tflmicro
  PUBLIC
  COMPILE_DEFINITIONS TF_LITE_DISABLE_X86_NEON=1
  COMPILE_DEFINITIONS TF_LITE_STATIC_MEMORY=1
  COMPILE_DEFINITIONS TF_LITE_USE_CTIME=1
  COMPILE_DEFINITIONS CMSIS_NN=1
  COMPILE_DEFINITIONS ARDUINO=1
  COMPILE_DEFINITIONS TFLITE_USE_CTIME=1
)

set_target_properties(
  pico-tflmicro
  PROPERTIES
  COMPILE_FLAGS -Os
  COMPILE_FLAGS -fno-rtti
  COMPILE_FLAGS -fno-exceptions
  COMPILE_FLAGS -fno-threadsafe-statics
  COMPILE_FLAGS -nostdlib
)

target_link_libraries(
  pico-tflmicro
  pico_stdlib
  pico_multicore
)

target_sources(pico-tflmicro
  PRIVATE
  ${CMAKE_CURRENT_LIST_DIR}/src/signal/micro/kernels/delay.cpp
  ${CMAKE_CURRENT_LIST_DIR}/src/signal/micro/kernels/delay_flexbuffers_generated_data.h
  ${CMAKE_CURRENT_LIST_DIR}/src/signal/micro/kernels/energy.cpp
  ...
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/ruy/ruy/profiler/instrumentation.h

)

add_library(pico-tflmicro_test STATIC)

target_include_directories(pico-tflmicro_test
  PUBLIC
  ${CMAKE_CURRENT_LIST_DIR}/src/
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/ruy
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/gemmlowp
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/kissfft
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/flatbuffers
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/cmsis/CMSIS/Core/Include
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/flatbuffers/include
  ${CMAKE_CURRENT_LIST_DIR}/src/third_party/cmsis_nn/Include
)

target_compile_definitions(
  pico-tflmicro_test
  PUBLIC
  COMPILE_DEFINITIONS TF_LITE_DISABLE_X86_NEON=1
  COMPILE_DEFINITIONS TF_LITE_STATIC_MEMORY=1
  COMPILE_DEFINITIONS CMSIS_NN=1
)

set_target_properties(
  pico-tflmicro_test
  PROPERTIES
  COMPILE_FLAGS -fno-rtti
  COMPILE_FLAGS -fno-exceptions
  COMPILE_FLAGS -fno-threadsafe-statics
  COMPILE_FLAGS -nostdlib
)

target_link_libraries(
  pico-tflmicro_test
  pico_stdlib
  pico_multicore
)

add_subdirectory("examples/hello_world")
add_subdirectory("wrapped")

add_subdirectory("tests/arena_allocator_non_persistent_arena_buffer_allocator_test")
add_subdirectory("tests/arena_allocator_persistent_arena_buffer_allocator_test")
add_subdirectory("tests/arena_allocator_recording_single_arena_buffer_allocator_test")
add_subdirectory("tests/arena_allocator_single_arena_buffer_allocator_test")
...
add_subdirectory("tests/testing_helpers_test")
add_subdirectory("tests/testing_util_test")

Thank you for the response!

Here is how the pico-tflmicro library is defined. It is also static, but it links to some other libraries. Is that the issue? I've tried linking directly to this and just adding the wrapped headers into the target_include_directories of pico-tflmicro, but that didn't work for me. I get the same errors.

Thank you!

I get the following error when trying to link to those libraries

  = note: rust-lld: error: unable to find library -lstdc++
          rust-lld: error: unable to find library -lc++
          rust-lld: error: unable to find library -lsupc++

with

println!("cargo:rustc-link-lib=libstdc++");
println!("cargo:rustc-link-lib=libc++");
println!("cargo:rustc-link-lib=libsupc++");

I used the crate link-cplusplus to make sure, and it was still no dice.

I disabled all of those things, but it still didn't work.

Any other ideas?

Also another discovery is that __StackOneBottom can be found in pico-sdk under pico_standard_link.

The corresponding error can be seen here

          rust-lld: error: undefined symbol: __StackOneBottom
          >>> referenced by multicore.c:124 (/Users/bjorn/git/pico-sdk/src/rp2_common/pico_multicore/multicore.c:124)
          >>>               multicore.c.obj:(multicore_launch_core1) in archive /Users/bjorn/Desktop/pico-tflmicro-rs/examples/target/thumbv6m-none-eabi/debug/deps/libpico_tflmicro_sys-070094f974eb28c9.rlib

So maybe the SDK needs to be included more verbosely somehow?

You need to use that linker script when linking your program. Something like -T/path/to/script.ld passed to the linker.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.