I'm attempting to link C code which calls a Rust library that allocates on the heap by running arc-elf32-gcc lib.o example.c but am getting an undefined reference to core[47ee6e850c258048]::fmt::Formatter>::debug_lower_hex
The rust code successfully compiles using rustc example.rs --emit=obj -o lib.o --crate-type=lib -O
If I instead compile with --crate-type=cdylib or dylib I get dropping unsupported crate type cdylib for target
If I compile with --crate-type=staticlib then I get
error: no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait
error: `#[panic_handler]` function required, but not found
After implementing a global memory allocator by following embedded-alloc and specifying a panic handler similar to embedded-alloc's example, I still get an undefined reference to debug_lower_hex.
code
// example.rs which becomes lib.o
#![no_std]
extern crate alloc;
use alloc::vec::Vec;
#[no_mangle]
pub extern "C" fn rust() -> i32 {
let y = 42;
let mut vec = Vec::new();
vec.push(y);
vec.pop().unwrap()
}
// example.c
#include <stdint.h>
#include <stdarg.h>
int32_t rust(void);
int main()
{
printf("from rust get value %d", rust());
}
full error output
/opt/arc/lib/gcc/arc-elf32/10.2.0/../../../../arc-elf32/bin/ld: /opt/arc/lib/gcc/arc-elf32/10.2.0/../../../../arc-elf32/bin/ld: DWARF error: can't find .debug_ranges section.
src/lib.o: in function `_RNvXsP_NtCs6aT7ng1d0qi_4core3fmtRjNtB5_5Debug3fmtCsZt7r1wRFCo_4test':
fake.c:(.text._RNvXsP_NtCs6aT7ng1d0qi_4core3fmtRjNtB5_5Debug3fmtCsZt7r1wRFCo_4test+0xa): undefined reference to `_RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter15debug_lower_hex'
/opt/arc_tools_202009rel/lib/gcc/arc-elf32/10.2.0/../../../../arc-elf32/bin/ld: fake.c:(.text._RNvXsP_NtCs6aT7ng1d0qi_4core3fmtRjNtB5_5Debug3fmtCsZt7r1wRFCo_4test+0xa): undefined reference to `_RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter15debug_lower_hex'
/opt/arc_tools_202009rel/lib/gcc/arc-elf32/10.2.0/../../../../arc-elf32/bin/ld: GOT and PLT relocations cannot be fixed with a non dynamic linker
/opt/arc_tools_202009rel/lib/gcc/arc-elf32/10.2.0/../../../../arc-elf32/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status
demangled
src/lib.o: in function `<&usize as core[47ee6e850c258048]::fmt::Debug>::fmt':
fake.c:(.text.<&usize as core[47ee6e850c258048]::fmt::Debug>::fmt+0xa): undefined reference to `<core[47ee6e850c258048]::fmt::Formatter>::debug_lower_hex'
/opt/arc_tools/lib/gcc/arc-elf32/10.2.0/../../../../arc-elf32/bin/ld: fake.c:(.text.<&usize as core[47ee6e850c258048]::fmt::Debug>::fmt+0xa): undefined reference to `<core[47ee6e850c258048]::fmt::Formatter>::debug_lower_hex'
how did you invoked the linker? via rustc or via gcc? you probably missing a linker flag to link the core library.
debug_lower_hex is called maybe because you have somewhere something like:
format!("{:x?}", 123456);
note the ? in the format spec. maybe it's inside a #[derive(Debug)] or something. if you don't need it, you can remove it and the linker should be satisfied.
I'm invoking the linker via arc-elf32-gcc. I'm not sure how to link with core. I also get undefined reference to debug_lower_hex when using core::option::iter (no alloc):
I highly suspect it's hidden in some #[derive(Debug)] expansions, but that's not the problem to solve, instead let's try to figure out how to correctly link libcore to your C program.
I think the simplest way is to ask rustc itself. first create a dummy rust source file, I don't know about cross compiling, but for a host compiler, an empty main function is enough, i.e. fn main() {}, then just invoke rustc like this:
$ rustc --print link-args /path/to/dummy.rs
it should print out the correct linker args to link a rust program.
you can play with it around to see what is the bare minimum for a cross compiling toolchain to work.
If I do an objdump libcore.rlib I see that debug_lower_hex is defined. This led to me to a StackOverflow post with a similar issue which they resolved using -l undefined_symbol_name. If I use rustc -l _RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter15debug_lower_hex
I get a new undefined reference: _RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter25debug_tuple_field1_finish. However, if I attempt to also add -l debug_tuple_field1_finish it fails. The nm output of libcore.rlib after greping for debug_tuple_field1_finish is:
U _RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter25debug_tuple_field1_finish
U _RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter25debug_tuple_field1_finish
U _RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter25debug_tuple_field1_finish
U _RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter25debug_tuple_field1_finish
U _RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter25debug_tuple_field1_finish
U _RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter25debug_tuple_field1_finish
U _RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter25debug_tuple_field1_finish
00000000 T _RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter25debug_tuple_field1_finish
00000000 t _RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter25debug_tuple_field1_finish.localalias
U _RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter25debug_tuple_field1_finish
U _RNvMs5_NtCs6aT7ng1d0qi_4core3fmtNtB5_9Formatter25debug_tuple_field1_finish
Perhaps it failed because it's not only T and U but t? Not sure.
I see undefined symbols for debug_lower_hex and others in lib.o.
Here's the demangled output:
00000000 r global.0
00000000 r global.b
00000000 r global.c
00000000 r global.d
00000000 r global.e
00000000 r global.f
00000000 r global.h
00000000 r global.j
U _GLOBAL_OFFSET_TABLE_
00000000 t core[47ee6e850c258048]::ptr::drop_in_place::<&core[47ee6e850c258048]::option::Option<&i32>>
00000000 t core[47ee6e850c258048]::ptr::drop_in_place::<&&i32>
00000000 t core[47ee6e850c258048]::panicking::assert_failed::<core[47ee6e850c258048]::option::Option<&i32>, core[47ee6e850c258048]::option::Option<&i32>>.constprop.0
U <core[47ee6e850c258048]::fmt::Formatter>::debug_lower_hex
U <core[47ee6e850c258048]::fmt::Formatter>::debug_upper_hex
U <core[47ee6e850c258048]::fmt::Formatter>::debug_tuple_field1_finish
U <core[47ee6e850c258048]::fmt::Formatter>::write_str
U core[47ee6e850c258048]::panicking::assert_failed_inner
U <i32 as core[47ee6e850c258048]::fmt::Display>::fmt
00000000 t <&core[47ee6e850c258048]::option::Option<&i32> as core[47ee6e850c258048]::fmt::Debug>::fmt
00000000 t <&&i32 as core[47ee6e850c258048]::fmt::Debug>::fmt
U <i32 as core[47ee6e850c258048]::fmt::LowerHex>::fmt
U <i32 as core[47ee6e850c258048]::fmt::UpperHex>::fmt
00000000 T rust
Which object files do you want in the sysroot?
Here's a subset of demangled libcore in rustc_codegen_gcc/build_sysroot/sysroot/lib/rustlib/arc-elf32/lib/libcore-43644bb6bb103da8.rlib
The number at the beginning (e.g. line 61) shows the line number in the nm output.
Line 61 is where debug_lower_hex is undefined (U).
If I use nm --line-numbers I see that the undefined debug_lower_hex is at fake.c:0 and the defined (T) doesn't have a line number.
61 U <core[47ee6e850c258048]::fmt::Formatter>::debug_lower_hex
62 U <core[47ee6e850c258048]::fmt::Formatter>::debug_upper_hex
63 U <core[47ee6e850c258048]::fmt::Formatter>::pad_formatted_parts
Much later (at line 6400) in libcore.rlib I see that debug_lower_hex is T (a global text symbol).
6400 00000000 T <core[47ee6e850c258048]::fmt::Formatter>::debug_lower_hex
6401 00000000 T <core[47ee6e850c258048]::fmt::Formatter>::debug_upper_hex
6402 00000000 T <core[47ee6e850c258048]::fmt::Formatter>::pad_formatted_parts
I believe the undefined reference to core functions like debug_lower_hex was because I had --emit=obj for rustc (found from r/rust): rustc src/lib.rs -o src/lib.o --crate-type=staticlib -O
However, when I attempt to compile with arc-elf32-gcc example.c lib.o
I get an undefined reference to __ucmpti2 which is a compiler builtin from LLVM's compiler-rt that's only included for 128 bit targets (CRT_HAS_128BIT in ucmpti2.c).
It's not just __ucmpti2 but some other builtins like __negti2 from the unimplemented Rust compiler builtins which are built for the host processor (X86-64) then when I attempt to link with the object file I get the error: error adding symbols: file format not recognized
After removing compiler-builtins/c from build_sysroot.sh I no longer get the file format not recognized error.
However, I still get an undefined reference to __ucmpti2 in the (demangled) function: <core[47ee6e850c258048]::fmt::num::Octal as core[47ee6e850c258048]::fmt::num::GenericRadix>::fmt_int::<u128> which seems like core/src/fmt/nums.rs fmt_int
Is it possible to disable 128 bit support in rustc_codegen_gcc?
If I modify cargo build in build_sysroot/build_sysroot.sh to use the CC=arc-elf32-gcc environment variable to override the C compiler I still getting an undefined reference to __ucmpti2. I believe there's still calls to __ucmpti2 because of gcc_icmp in src/int.rs. If I attempt to enable 128 bit support in LLVM's compiler-builtins via CRT_HAS_128_BIT I get error: unable to emulate 'TI' where TI stands for Tetra Integer aka 16 byte int.
Another thing to note is I'm using an older version of rustc_codegen_gcc (from 2023-06-19, commit 1bbee3e217d75e7bc3bfe5d8c1b35e776fce96e6). I'm not sure if there were any changes since then to fix these issues.
Undefined reference to ucmpti2 full error output
arc-elf32-gcc -mcpu=archs -specs=qemu.specs -fno-builtin -o example example.c lib.o
/opt/arc_tools_202009rel/lib/gcc/arc-elf32/10.2.0/../../../../arc-elf32/bin/ld: /opt/arc_tools_202009rel/lib/gcc/arc-elf32/10.2.0/../
../../../arc-elf32/bin/ld: DWARF error: can't find .debug_ranges section.
src/lib.o(core-43644bb6bb103da8.core.47ee6e850c258048-cgu.0.rcgu.o): in function `_RINvYNtNtNtCs6aT7ng1d0qi_4core3fmt3num5OctalNtB5_1
2GenericRadix7fmt_intoEB9_.localalias':
fake.c:(.text._RINvYNtNtNtCs6aT7ng1d0qi_4core3fmt3num5OctalNtB5_12GenericRadix7fmt_intoEB9_+0xfa): undefined reference to `__ucmpti2'
/opt/arc_tools_202009rel/lib/gcc/arc-elf32/10.2.0/../../../../arc-elf32/bin/ld: fake.c:(.text._RINvYNtNtNtCs6aT7ng1d0qi_4core3fmt3num
5OctalNtB5_12GenericRadix7fmt_intoEB9_+0xfa): undefined reference to `__ucmpti2'
/opt/arc_tools_202009rel/lib/gcc/arc-elf32/10.2.0/../../../../arc-elf32/bin/ld: GOT and PLT relocations cannot be fixed with a non dy
namic linker
/opt/arc_tools_202009rel/lib/gcc/arc-elf32/10.2.0/../../../../arc-elf32/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status
To disable 128-bit integers, it should be enough to send true as the last argument here instead of target_info.supports_128bit_int() and use UInt64t/Int64t for these types.
Thanks, those changes fixed the undefined reference to __ucmpti2. I'm now getting an undefined reference to __atomic_load_8 (a compiler builtin from LLVM) for core[47ee6e850c258048]::sync::atomic::atomic_load::<i64>. Similar to the 128 bit ints is there a way to avoid using atomic instructions? For example, use only a subset of core where atomic instructions aren't required.
Other issues I've resolved (added to help other people who get similar issues).
- Fixed an undefined reference to `rust_eh_personality` by copying the `eh_personality` function from [alloc_example.rs](https://github.com/rust-lang/rustc_codegen_gcc/blob/f3b82df8f8f59238bf9ba9aafa4896e8a72c888e/example/alloc_example.rs).
__atomic_load_8 is also a GCC compiler builtin, but I guess it's not available on some platforms.
I don't think there's a way to disable atomic operations.
I'll look into fixing these issues, but for now, since there's no way to disable 128-bit atomic operations, you could try changing this line to:
let atomic_load = self.context.get_builtin_function(&format!("__atomic_load_{}", std::cmp::min(4, size.bytes())));
and I guess you'll have to change a few others operations.
To avoid file format not recognized error for compiler builtins, modify build_sysroot.sh to use AR="arc-elf32-ar" CC="arc-elf32-gcc" cargo build --target $TARGET_JSON --features compiler_builtins/c.
I'm currently working on fixing the endianness handling of 128-bit non-native integers, so I'll add CI for this and hopefully, will fix the undefined reference errors you got at the same time.