I am building a static lib (.a) from some Rust code, and this .a file will be used (linked) by an iOS app. However, that .a file contains tons of symbols - Rust functions that are only to be used internally instead of exposing to the iOS app. Indeed, the Rust code uses #[no_mangle] pub extern "C" fn to expose the apis that will be used by the iOS app. All other functions will never be used and should not appear in the final app.
Therefore, I wonder how can I remove all symbols except "no_mangle extern" ones in Rust's staticlib?
Are you actually running nm or objdump on the final app? Just because a symbol appears in a static library doesn't really have any bearing on whether it ends up in the final executable binary...
For android: Yes, the .so file extracted from the release apk file. I see lots of such symbols.
For ios: Yes and no. From previous experience (C++ part of app already on app store) I guess they still exist; and by logic thinking, since I forbid xcode to strip symbols (to allow Flutter find the C-compatible ones by string), and since I look at .a and see lots of should-be-stripped symbols, I think they should exist there.
Make sure you understand the difference between stripping symbols and link-time optimization (lto). LTO (which Xcode does in its release profile) will, depending on the optimizer profile i.e. speed or size (speed is the default for an iOS project), either inline code and remove unused code or just remove unused code, respectively. Symbols are metadata and are simply about providing visibility into text/data in the executable for linking and debugging. So they generally get carried up until the point where they can be discarded, which is usually the assembly of the final executable binary.
You can check your iOS app by performing an app store build and then looking in <DerivedData>/Products/App Store-iphoneos/Foo.app/[Foo | Frameworks/Bar.framework/Bar]
For iOS: Try to pass --gc-sections to the linker. This is what rustc normally does. It removes all functions that are not reachable from an exported symbol.
For Android: Do you use dylib or cdylib as crate type? You should use the later as the former keeps all functions necessary for linking a rust crate against it and keeps the crate metdata (which contains a lot of useful information for reverse engineering programs, including all names for all public and private items, source spans, ...) Cdylib omits the crate metadata and only exports #[no_mangle] functions.