Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARM integrated assembler generates incorrect nop opcode when switching from arm to thumb mode #18393

Closed
llvmbot opened this issue Nov 22, 2013 · 6 comments
Labels
backend:ARM bugzilla Issues migrated from bugzilla

Comments

@llvmbot
Copy link
Collaborator

llvmbot commented Nov 22, 2013

Bugzilla Link 18019
Resolution FIXED
Resolved on Sep 25, 2017 11:50
Version trunk
OS Windows NT
Blocks #19300
Reporter LLVM Bugzilla Contributor
CC @fhahn,@rengolin

Extended Description

I am seeing a problem with the way nops are emitted in the integrated assembler for ARM. When switching from arm to thumb mode in an assembly file we still emit the arm nop opcode. Look at this small example:

$ cat align.s
.syntax unified
.code 16
foo:
add r0, r0
.align 3
add r0, r0

$ llvm-mc -triple armv7-none-linux align.s -filetype=obj -o t.o && llvm-objdump -triple thumbv7 -d t.o
t.o: file format ELF32-arm

Disassembly of section .text:
foo:
0: 00 44 add r0,
r0
2: 00 f0 20 e3 blx
#​4195904
6: 00 00 movs r0,
r0
8: 00 44 add r0,
r0

This shows that we have actually emitted an arm nop (e320f000) instead of a thumb nop. Unfortunately, this encodes to a thumb branch which causes bad things to happen when compiling assembly code with align directives.

The ARMAsmBackend class is responsible for emitting these nops. It keeps track of whether it should emit arm or thumb nop. The first problem is that MCElfStreamer does not pass on the .code 16 directive to the ARMAsmBackend class (using handleAssemblerFlag). In the example above we start assembling in arm mode (because of the -triple) and so the ARMAsmBackend always thinks we are in arm mode and it emits the wrong opcode.

We actually can assemble this example correctly for darwin because the MCMachOStreamer does pass on the directives. It looks like we need to modify the MCElfStreamer to pass the assembler directives down to the ARMAsmBackend to match the behavior of the MCMachOStreamer.

Unfortunately, this change will not solve the full problem, even though the integrated assembler works correctly for MachO in this example:

$ llvm-mc -triple armv7-apple-darwin align.s -filetype=obj -o t.o && llvm-objdump -triple thumbv7 -d t.o
t.o: file format Mach-O arm

Disassembly of section __TEXT,__text:
foo:
0: 00 44 add r0,
r0
2: 00 bf nop
4: 00 bf nop
6: 00 bf nop
8: 00 44 add r0,
r0

The problem is that the nops are written after the assembly is complete when it writes the MCAlignFragment to the output. The ARMAsmBackend writes nops using the last mode it knew about. So it can write bad nop data if the nops are in a location that is a mode that does not match the bit stored in the backend. We can see the problem by simply adding a .code 32 directive to the end of the example:

$ echo ".code 32" >> align.s
$ llvm-mc -triple armv7-apple-darwin align.s -filetype=obj -o t.o && llvm-objdump -triple thumbv7 -d t.o
t.o: file format Mach-O arm

Disassembly of section __TEXT,__text:
foo:
0: 00 44 add r0,
r0
2: 00 f0 20 e3 blx
#​4195904
6: 00 00 movs r0,
r0
8: 00 44 add r0,
r0

It seems that the MCAlignFragment needs to know if it is aligning in thumb mode or in arm mode. How should we solve this problem? Should we store the current mode in the fragment when assembling the file and use that mode when writing nop data?

@llvmbot
Copy link
Collaborator Author

llvmbot commented Nov 23, 2013

@llvmbot
Copy link
Collaborator Author

llvmbot commented Nov 26, 2013

Partial fix committed in r195677: http://llvm.org/viewvc/llvm-project?view=revision&revision=195677

Still need a fix for code that switches modes multiple times in the same file because we just emit noops using the last mode.

@llvmbot
Copy link
Collaborator Author

llvmbot commented Feb 21, 2014

Still need a fix for code that switches modes multiple times in the same
file because we just emit noops using the last mode.

The fixes for llvm/llvm-bugzilla-archive#18303 show how to fix this, right? We want to keep the current MCSubtargetInfo in the MCAlignFragment so that the back end can emit NOPs appropriately?

@llvmbot
Copy link
Collaborator Author

llvmbot commented Feb 21, 2014

Still need a fix for code that switches modes multiple times in the same
file because we just emit noops using the last mode.

The fixes for llvm/llvm-bugzilla-archive#18303 show how to fix this, right? We want to keep the
current MCSubtargetInfo in the MCAlignFragment so that the back end can emit
NOPs appropriately?

Yes, that is correct. We now have a clear path on how to fix this bug, which is to keep the MCSubtargetInfo in the fragment and pass it to the MCAsmInfo when generating noop data.

I briefly attempted the fix, but I got stuck because the noops are generated not just for the MCAlignFragment, but for potentially any MCFragment because of how bundling is handled. The bundle handling caused the change to not be as simple as I'd hoped and I never got around to investigating it further for the best implementation.

@fhahn
Copy link
Contributor

fhahn commented Sep 24, 2017

llvm-mc from the current master produces thumb nops for the example:

t.o: file format ELF32-arm-little

Disassembly of section .text:
foo:
0: 00 44 add r0, r0
2: 00 bf nop
4: 00 bf nop
6: 00 bf nop
8: 00 44 add r0, r0

@rengolin
Copy link
Member

mentioned in issue #19300

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 9, 2021
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:ARM bugzilla Issues migrated from bugzilla
Projects
None yet
Development

No branches or pull requests

3 participants