For arc-elf32 target get undefined reference to `debug_lower_hex` when using alloc crate

  • I've gotten code using core to run on the arc-elf32 target using rustc_codegen_gcc (as discussed in a previous forum post)
  • 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.

The sysroot is compiled with -Csymbol-mangling-version=v0, so you will need to add this to your rustc command in order to use the same name mangling.

1 Like

After adding -Csymbol-mangling-version=v0 to the rustc command I still get an undefined reference to debug_lower_hex.

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):

#![no_std]

#[no_mangle]
pub extern "C" fn rust() -> i32 {
    let x: Option<i32> = Some(4);
    assert_eq!(x.iter().next(), Some(&4));
    return x.unwrap()
}

However if I just use core::option like

#![no_std]

#[no_mangle]
pub extern "C" fn rust() -> i32 {
    let x: Option<i32> = Some(4);
    assert_eq!(x.is_some(), true);
    return x.unwrap()
}

I can link with this and get the correct value.

Both rg "x\?" and rg -i debug return nothing.

What's the output of nm lib.o? Do you see any line with undefined symbols (U)?

Edit: It would also be interesting to run nm on the object files of the sysroot.

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

@antoyo any thoughts on how to fix the undefined reference? Thanks for all your work on rustc_codegen_gcc.

Not sure. Are you able to compile the same example using the normal Rust toolchain?
It seems to be a problem of compiling Rust with C.

From what I could see elsewhere, it could be a question of argument order. Try to reverse the arguments like so:

arc-elf32-gcc example.c lib.o

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

Can you tell me which function calls __ucmpti2?

Also, not sure if it's going to help, but you could try removing --features compiler_builtins/c here.

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?

After adding a println! to rustc_codegen_gcc's gcc_icmp (then running ./build.sh --release) I can see that __ucmpti2 function calls are being created for: fmt_u128, exp_u128, u128_div_rem (in compiler-builtins) among others.

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.

Thanks after changing __atomic_load and __atomic_store to use

std::cmp::min(4, size.bytes())

I successfully linked with and used libcore.

Multi-part solution:

1 Like

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.

1 Like