Issue 180358
Summary [ARM] calls to `hidden` functions that cross the thumb/arm boundary miscompile
Labels new issue
Assignees
Reporter folkertdev
    The example below has two declarations, that link to identical (modulo names) assembly code and directives. One is marked `dso_local`, the other is marked `hidden`. These declarations are meant to be in "arm mode" while the remainder of the file is in "thumb mode". The `-thumb-mode` target feature makes this clear, and is applied to both declarations.

```llvm
target triple = "thumbv4t-unknown-none-eabi"

module asm ".balign 4"
module asm ".arm"
module asm ".type globalfn1, %function"
module asm "globalfn1:"
module asm "bx lr"
module asm ".size globalfn1, . - globalfn1"
module asm ".thumb"

module asm ".balign 4"
module asm ".arm"
module asm ".type globalfn2, %function"
module asm "globalfn2:"
module asm "bx lr"
module asm ".size globalfn2, . - globalfn2"
module asm ".thumb"

define dso_local void @entry() unnamed_addr #2 {
start:
  tail call void @globalfn1() #4
  tail call void @globalfn2() #4
  ret void
}

declare hidden void @globalfn1() unnamed_addr #3
declare dso_local void @globalfn2() unnamed_addr #3

attributes #2 = { nounwind "frame-pointer"="all" "target-cpu"="generic" "target-features"="+soft-float,+strict-align" }
attributes #3 = { nounwind "frame-pointer"="all" "target-cpu"="generic" "target-features"="+soft-float,+strict-align,-thumb-mode" }
attributes #4 = { nounwind }
```

If you compile this with 

```
clang-20 -target thumbv4t-unknown-none-eabi -mthumb a32.ll -o a32.elf -nostdlib -ffreestanding 
arm-none-eabi-objdump -d a32.elf
```

(I can't get godbolt to do this, unfortunately. clang-20 is what I have, but the underlying issue is still present in rust nightly, which uses LLVM 22)

You'll see that the hidden function is called without a thunk, and the dso_local function does get a thunk. 

```
a32.elf:     file format elf32-littlearm


Disassembly of section .text:

000200e4 <globalfn1>:
   200e4:	e12fff1e 	bx	lr

000200e8 <globalfn2>:
   200e8:	e12fff1e 	bx	lr

000200ec <entry>:
   200ec:	b580      	push	{r7, lr}
   200ee:	af00 	add	r7, sp, #0
   200f0:	f7ff fff8 	bl	200e4 <globalfn1>
 200f4:	f000 f804 	bl	20100 <__Thumbv4ABSLongBXThunk_globalfn2>
 200f8:	bc80      	pop	{r7}
   200fa:	bc01      	pop	{r0}
 200fc:	4686      	mov	lr, r0
   200fe:	4770      	bx	lr

00020100 <__Thumbv4ABSLongBXThunk_globalfn2>:
   20100:	4778      	bx	pc
 20102:	e7fd      	b.n	20100 <__Thumbv4ABSLongBXThunk_globalfn2>
 20104:	e51ff004 	ldr	pc, [pc, #-4]	@ 20108 <__Thumbv4ABSLongBXThunk_globalfn2+0x8>
   20108:	000200e8 	.word	0x000200e8
```

You can flip the order of the assembly, make them both hidden or not, and the correlation just seems to be that a hidden function is always emitted as a thumb symbol. That is a miscompilation.

This problem is relevant for rust because we use hidden functions when lowering naked functions to work around LTO issues. There may be other workarounds, but still I believe this is a real issue that should be fixed.

cc https://github.com/rust-lang/rust/issues/151946
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to