| Issue |
181481
|
| Summary |
[M68k] Backend cannot handle 32-bit offsets for memory loads and jumps
|
| Labels |
new issue
|
| Assignees |
|
| Reporter |
glaubitz
|
The M68k backend in LLVM is currently unable to handle 32-bits offsets when accessing variables, the Global Offset Table (GOT) or performing jumps or branches.
This limitation manifests itself with relocation overflows during the linking stage when cross-building the Rust standard library for m68k:
```
error: linking with `m68k-linux-gnu-gcc` failed: exit status: 1
|
= note: "m68k-linux-gnu-gcc" "-Wl,--version-script=<sysroot>-std/m68k-unknown-linux-gnu/dist/deps/rustckykvCU/list" "-Wl,--no-undefined-version" "<sysroot>-std/m68k-unknown-linux-gnu/dist/deps/rustckykvCU/symbols.o" "<1 object files omitted>" "<sysroot>-std/m68k-unknown-linux-gnu/dist/deps/rustckykvCU/rmeta.o" "<1 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "/home/glaubitz/rust/build/x86_64-unknown-linux-gnu/stage1-std/m68k-unknown-linux-gnu/dist/deps/{libpanic_unwind-77235aa50c2dad80,libobject-9ccd64f2b398ef61,libmemchr-51675612ec3f9b1a,libaddr2line-be4ba8aa2f8107b9,libgimli-721f648e76343a29,libcfg_if-0476d72014bc2329,librustc_demangle-a3ef1ab21cc21b4e,libstd_detect-e20ceae0536f707c,libhashbrown-79dd6a1c287b1d3f,librustc_std_workspace_alloc-2abb32acfc7d6a9d,libminiz_oxide-092dea6961788a3a,libadler2-646ae9e24acdebd3,libunwind-1c438d547da72ffc,liblibc-15facccc1f6f6f2e,librustc_std_workspace_core-71b4ff10d953591e,liballoc-9cd45f701e3429e8,libcore-c8955a6bea6dd621,libcompiler_builtins-e3d7974447656a72}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-L" "<sysroot>-std/m68k-unknown-linux-gnu/dist/deps/rustckykvCU/raw-dylibs" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "<sysroot>-std/m68k-unknown-linux-gnu/dist/build/compiler_builtins-4ba9a904933f69c9/out" "-L" "<sysroot>/lib/rustlib/m68k-unknown-linux-gnu/lib" "-o" "<sysroot>-std/m68k-unknown-linux-gnu/dist/deps/libstd-6dcbe9f27896f529.so" "-shared" "-Wl,-soname=libstd-6dcbe9f27896f529.so" "-Wl,-z,relro,-z,now" "-Wl,-O1" "-nodefaultlibs" "-Wl,-z,origin" "-Wl,-rpath,$ORIGIN/../lib"
= note: some arguments are omitted. use `--verbose` to show all linker arguments
= note: /home/glaubitz/rust/build/x86_64-unknown-linux-gnu/stage1-std/m68k-unknown-linux-gnu/dist/deps/std-6dcbe9f27896f529.std.b85495982ea7a217-cgu.0.rcgu.o: in function `<str>::trim_matches::<<char>::is_whitespace>':
std.b85495982ea7a217-cgu.0:(.text._RINvMNtCs5oVtzFPLrop_4core3stre12trim_matchesNvMNtNtB5_4char7methodsc13is_whitespaceECsfPbyEyQg7yZ_3std+0x30): relocation truncated to fit: R_68K_GOT16 against symbol `core::unicode::unicode_data::white_space::WHITESPACE_MAP' defined in .rodata._RNvNtNtNtCs5oVtzFPLrop_4core7unicode12unicode_data11white_space14WHITESPACE_MAP section in /home/glaubitz/rust/build/x86_64-unknown-linux-gnu/stage1-std/m68k-unknown-linux-gnu/dist/deps/libcore-c8955a6bea6dd621.rlib(core-c8955a6bea6dd621.core.3eebc72cebe2d82f-cgu.0.rcgu.o)
std.b85495982ea7a217-cgu.0:(.text._RINvMNtCs5oVtzFPLrop_4core3stre12trim_matchesNvMNtNtB5_4char7methodsc13is_whitespaceECsfPbyEyQg7yZ_3std+0x17a): relocation truncated to fit: R_68K_GOT16 against symbol `core::unicode::unicode_data::white_space::WHITESPACE_MAP' defined in .rodata._RNvNtNtNtCs5oVtzFPLrop_4core7unicode12unicode_data11white_space14WHITESPACE_MAP section in /home/glaubitz/rust/build/x86_64-unknown-linux-gnu/stage1-std/m68k-unknown-linux-gnu/dist/deps/libcore-c8955a6bea6dd621.rlib(core-c8955a6bea6dd621.core.3eebc72cebe2d82f-cgu.0.rcgu.o)
std.b85495982ea7a217-cgu.0:(.text._RINvMNtCs5oVtzFPLrop_4core3stre12trim_matchesNvMNtNtB5_4char7methodsc13is_whitespaceECsfPbyEyQg7yZ_3std+0x1bc): relocation truncated to fit: R_68K_GOT16 against symbol `core::unicode::unicode_data::white_space::WHITESPACE_MAP' defined in .rodata._RNvNtNtNtCs5oVtzFPLrop_4core7unicode12unicode_data11white_space14WHITESPACE_MAP section in /home/glaubitz/rust/build/x86_64-unknown-linux-gnu/stage1-std/m68k-unknown-linux-gnu/dist/deps/libcore-c8955a6bea6dd621.rlib(core-c8955a6bea6dd621.core.3eebc72cebe2d82f-cgu.0.rcgu.o)
std.b85495982ea7a217-cgu.0:(.text._RINvMNtCs5oVtzFPLrop_4core3stre12trim_matchesNvMNtNtB5_4char7methodsc13is_whitespaceECsfPbyEyQg7yZ_3std+0x30c): relocation truncated to fit: R_68K_GOT16 against symbol `core::unicode::unicode_data::white_space::WHITESPACE_MAP' defined in .rodata._RNvNtNtNtCs5oVtzFPLrop_4core7unicode12unicode_data11white_space14WHITESPACE_MAP section in /home/glaubitz/rust/build/x86_64-unknown-linux-gnu/stage1-std/m68k-unknown-linux-gnu/dist/deps/libcore-c8955a6bea6dd621.rlib(core-c8955a6bea6dd621.core.3eebc72cebe2d82f-cgu.0.rcgu.o)
/home/glaubitz/rust/build/x86_64-unknown-linux-gnu/stage1-std/m68k-unknown-linux-gnu/dist/deps/std-6dcbe9f27896f529.std.b85495982ea7a217-cgu.0.rcgu.o: in function `<core::cell::once::OnceCell<core::result::Result<core::option::Option<alloc::boxed::Box<addr2line::unit::DwoUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::BigEndian>>>>, gimli::read::Error>>>::try_init::<<core::cell::once::OnceCell<core::result::Result<core::option::Option<alloc::boxed::Box<addr2line::unit::DwoUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::BigEndian>>>>, gimli::read::Error>>>::get_or_init<<addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::BigEndian>>>::dwarf_and_unit::{closure#2}>::{closure#0}, !>':
std.b85495982ea7a217-cgu.0:(.text.unlikely._RINvMNtNtCs5oVtzFPLrop_4core4cell4onceINtB3_8OnceCellINtNtB7_6result6ResultINtNtB7_6option6OptionINtNtCsloHXYFdziy6_5alloc5boxed3BoxINtNtCs7pgN5QWC0eG_9addr2line4unit7DwoUnitINtNtNtCsjaO4XSAUGZc_5gimli4read12endian_slice11EndianSliceNtNtB2T_9endianity9BigEndianEEEENtB2R_5ErrorEE8try_initNCINvB2_11get_or_initNCNvMB29_INtB29_7ResUnitB2M_E14dwarf_and_units0_0E0zECsfPbyEyQg7yZ_3std+0x22): relocation truncated to fit: R_68K_PC16 against `.text._RINvNtCs5oVtzFPLrop_4core3ptr13drop_in_placeINtNtB4_6result6ResultRIBH_INtNtB4_6option6OptionINtNtCsloHXYFdziy6_5alloc5boxed3BoxINtNtCs7pgN5QWC0eG_9addr2line4unit7DwoUnitINtNtNtCsjaO4XSAUGZc_5gimli4read12endian_slice11EndianSliceNtNtB2P_9endianity9BigEndianEEEENtB2N_5ErrorETB12_B13_EEECsfPbyEyQg7yZ_3std'
std.b85495982ea7a217-cgu.0:(.text.unlikely._RINvMNtNtCs5oVtzFPLrop_4core4cell4onceINtB3_8OnceCellINtNtB7_6result6ResultINtNtB7_6option6OptionINtNtCsloHXYFdziy6_5alloc5boxed3BoxINtNtCs7pgN5QWC0eG_9addr2line4unit7DwoUnitINtNtNtCsjaO4XSAUGZc_5gimli4read12endian_slice11EndianSliceNtNtB2T_9endianity9BigEndianEEEENtB2R_5ErrorEE8try_initNCINvB2_11get_or_initNCNvMB29_INtB29_7ResUnitB2M_E14dwarf_and_units0_0E0zECsfPbyEyQg7yZ_3std+0x26): relocation truncated to fit: R_68K_PC16 against `.data.rel.ro..Lanon.79cdddedb29d35a38ed66d3588ec17e0.2'
std.b85495982ea7a217-cgu.0:(.text.unlikely._RINvMNtNtCs5oVtzFPLrop_4core4cell4onceINtB3_8OnceCellINtNtB7_6result6ResultINtNtB7_6option6OptionINtNtCsloHXYFdziy6_5alloc5boxed3BoxINtNtCs7pgN5QWC0eG_9addr2line4unit7DwoUnitINtNtNtCsjaO4XSAUGZc_5gimli4read12endian_slice11EndianSliceNtNtB2T_9endianity9BigEndianEEEENtB2R_5ErrorEE8try_initNCINvB2_11get_or_initNCNvMB29_INtB29_7ResUnitB2M_E14dwarf_and_units0_0E0zECsfPbyEyQg7yZ_3std+0x2e): relocation truncated to fit: R_68K_PC16 against `.rodata..Lanon.79cdddedb29d35a38ed66d3588ec17e0.0'
/home/glaubitz/rust/build/x86_64-unknown-linux-gnu/stage1-std/m68k-unknown-linux-gnu/dist/deps/std-6dcbe9f27896f529.std.b85495982ea7a217-cgu.0.rcgu.o: in function `<core::cell::once::OnceCell<core::result::Result<core::option::Option<alloc::boxed::Box<addr2line::unit::DwoUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::BigEndian>>>>, gimli::read::Error>>>::try_init::<<core::cell::once::OnceCell<core::result::Result<core::option::Option<alloc::boxed::Box<addr2line::unit::DwoUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::BigEndian>>>>, gimli::read::Error>>>::get_or_init<<addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::BigEndian>>>::dwarf_and_unit::{closure#4}>::{closure#0}, !>':
std.b85495982ea7a217-cgu.0:(.text.unlikely._RINvMNtNtCs5oVtzFPLrop_4core4cell4onceINtB3_8OnceCellINtNtB7_6result6ResultINtNtB7_6option6OptionINtNtCsloHXYFdziy6_5alloc5boxed3BoxINtNtCs7pgN5QWC0eG_9addr2line4unit7DwoUnitINtNtNtCsjaO4XSAUGZc_5gimli4read12endian_slice11EndianSliceNtNtB2T_9endianity9BigEndianEEEENtB2R_5ErrorEE8try_initNCINvB2_11get_or_initNCNvMB29_INtB29_7ResUnitB2M_E14dwarf_and_units2_0E0zECsfPbyEyQg7yZ_3std+0x56): relocation truncated to fit: R_68K_PC16 against `.text._RINvNtCs5oVtzFPLrop_4core3ptr13drop_in_placeINtNtB4_6result6ResultRIBH_INtNtB4_6option6OptionINtNtCsloHXYFdziy6_5alloc5boxed3BoxINtNtCs7pgN5QWC0eG_9addr2line4unit7DwoUnitINtNtNtCsjaO4XSAUGZc_5gimli4read12endian_slice11EndianSliceNtNtB2P_9endianity9BigEndianEEEENtB2N_5ErrorETB12_B13_EEECsfPbyEyQg7yZ_3std'
std.b85495982ea7a217-cgu.0:(.text.unlikely._RINvMNtNtCs5oVtzFPLrop_4core4cell4onceINtB3_8OnceCellINtNtB7_6result6ResultINtNtB7_6option6OptionINtNtCsloHXYFdziy6_5alloc5boxed3BoxINtNtCs7pgN5QWC0eG_9addr2line4unit7DwoUnitINtNtNtCsjaO4XSAUGZc_5gimli4read12endian_slice11EndianSliceNtNtB2T_9endianity9BigEndianEEEENtB2R_5ErrorEE8try_initNCINvB2_11get_or_initNCNvMB29_INtB29_7ResUnitB2M_E14dwarf_and_units2_0E0zECsfPbyEyQg7yZ_3std+0x5a): relocation truncated to fit: R_68K_PC16 against `.data.rel.ro..Lanon.79cdddedb29d35a38ed66d3588ec17e0.2'
std.b85495982ea7a217-cgu.0:(.text.unlikely._RINvMNtNtCs5oVtzFPLrop_4core4cell4onceINtB3_8OnceCellINtNtB7_6result6ResultINtNtB7_6option6OptionINtNtCsloHXYFdziy6_5alloc5boxed3BoxINtNtCs7pgN5QWC0eG_9addr2line4unit7DwoUnitINtNtNtCsjaO4XSAUGZc_5gimli4read12endian_slice11EndianSliceNtNtB2T_9endianity9BigEndianEEEENtB2R_5ErrorEE8try_initNCINvB2_11get_or_initNCNvMB29_INtB29_7ResUnitB2M_E14dwarf_and_units2_0E0zECsfPbyEyQg7yZ_3std+0x62): relocation truncated to fit: R_68K_PC16 against `.rodata..Lanon.79cdddedb29d35a38ed66d3588ec17e0.0'
/home/glaubitz/rust/build/x86_64-unknown-linux-gnu/stage1-std/m68k-unknown-linux-gnu/dist/deps/std-6dcbe9f27896f529.std.b85495982ea7a217-cgu.0.rcgu.o: in function `<core::cell::once::OnceCell<core::result::Result<addr2line::function::Function<gimli::read::endian_slice::EndianSlice<gimli::endianity::BigEndian>>, gimli::read::Error>>>::try_init::<<core::cell::once::OnceCell<core::result::Result<addr2line::function::Function<gimli::read::endian_slice::EndianSlice<gimli::endianity::BigEndian>>, gimli::read::Error>>>::get_or_init<<addr2line::function::LazyFunction<gimli::read::endian_slice::EndianSlice<gimli::endianity::BigEndian>>>::borrow::{closure#0}>::{closure#0}, !>':
std.b85495982ea7a217-cgu.0:(.text.unlikely._RINvMNtNtCs5oVtzFPLrop_4core4cell4onceINtB3_8OnceCellINtNtB7_6result6ResultINtNtCs7pgN5QWC0eG_9addr2line8function8FunctionINtNtNtCsjaO4XSAUGZc_5gimli4read12endian_slice11EndianSliceNtNtB23_9endianity9BigEndianEENtB21_5ErrorEE8try_initNCINvB2_11get_or_initNCNvMs_B1e_INtB1e_12LazyFunctionB1W_E6borrow0E0zECsfPbyEyQg7yZ_3std+0x1fc): additional relocation overflows omitted from the output
collect2: error: ld returned 1 exit status
```
A first attempt to address this issue was made in https://github.com/llvm/llvm-project/pull/119803, the proposed changes in this pull request are incomplete though and they also make the wrong assumption that the main problem is missing support for large Global Offset Tables (GOT) using the `-mxgot` compiler flag.
Further investigation with the help of Google Gemini showed that not only the GOT is affected by this problem but also memory loads for accessing variables as well as jump or branch instructions. In all cases, the limitation is that the M68k backend in LLVM currently does not generate 32-bit offsets such that anything that is further away than 16-bit offset is not reachable and results in the aforementioned relocation overflow errors during the linking stage.
The [GCC manual states](https://gcc.gnu.org/onlinedocs/gcc/M680x0-Options.html) that in order to generate 32-bit offsets, the parameters `-fPIC` (classic M680x0 CPUs) or `-mxgot` (Coldfire CPUs) have to be used.
Quoting:
> When generating position-independent code for ColdFire, generate code that works if the GOT has more than 8192 entries. This code is larger and slower than code generated without this option. On M680x0 processors, this option is not needed; `-fPIC` suffices.
> GCC normally uses a single instruction to load values from the GOT. While this is relatively efficient, it only works if the GOT is smaller than about 64k. Anything larger causes the linker to report an error such as:
```
relocation truncated to fit: R_68K_GOT16O foobar
```
> If this happens, you should recompile your code with `-mxgot`. It should then work with very large GOTs. However, code generated with `-mxgot` is less efficient, since it takes 4 instructions to fetch the value of a global symbol.
> Note that some linkers, including newer versions of the GNU linker, can create multiple GOTs and sort GOT entries. If you have such a linker, you should only need to use `-mxgot` when compiling a single object file that accesses more than 8192 GOT entries. Very few do.
> These options have no effect unless GCC is generating position-independent code.
This differentation between M680x0 and Coldfire CPUs was not taken into account [in the first proposed patch](https://github.com/llvm/llvm-project/pull/119803) to add support for 32-bit offsets. It made the assumption that both M680x0 and ColdFire CPUs use the `-mxgot` flag which is not correct as only ColdFire CPUs use the inefficient, four-instruction method to access the GOT. Classic M680x0 CPUs can just use 32-bit relative offsets directy.
Here is a comparision between classic M680x0 and ColdFire CPUs when accessing local variables:
- M680x0 using `-fPIC`:
```asm
move.l my_global_int@GOT(%a5),%a0 ; 32 Bit
move.l (%a0),%d0
```
- ColdFire using `-fPIC`:
```asm
move.l my_global_int@GOT(%a5),%a0 ; 16 Bit
move.l (%a0),%d0
```
- ColdFire `-fPIC -mxgot`:
```asm
move.l %a5,%a0
add.l #my_global_int@GOT,%a0 ; 32 Bit
move.l (%a0),%a0
move.l (%a0),%d0
```
So, it's very clear that the `-mxgot` flag is required for ColdFire CPUs only and should not be used for classic M680x0 CPUs.
As mentioned above, the problem has been investigated with the help of Google Gemini. As a result, a patch was generated with the help of Google Gemini to add support for 32-bit offsets in the M68k backend. The resulting patch is very rough and enforces the use of `-mxgot` both for classic M680x0 and ColdFire CPUs which means that the implementation is inefficient for traditional M680x0 CPUs. Nevertheless, the patch allows to successfully cross-compile the Rust standard library for M68k meaning it provides enough information what changes in the backend are necessary to support handling 32-bit offsets.
Here is the LLVM patch:
```diff
diff --git a/llvm/lib/Target/M68k/M68k.td b/llvm/lib/Target/M68k/M68k.td
index dfa44a423a..cafae7e42e 100644
--- a/llvm/lib/Tar<truncated>Please see the issue for the entire body.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs