David Peixotto
2013-Nov-21 17:55 UTC
[LLVMdev] ARM integrated assembler generates incorrect nop opcode when switching from arm to thumb mode
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? -- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Apparently Analagous Threads
- [LLVMdev] Win32 COFF Support - Patch 3
- [LLVMdev] R_ARM_ABS32 disassembly with integrated-as
- [LLVMdev] Thumb call relocation for the Runtime dynamic linker (RuntimeDyldELF.cpp)
- [LLVMdev] Thumb-2 code generation error in Apple LLVM at all optimization levels
- [LLVMdev] [Patch] Let MC/ELF generate Thumb/Thumb-2 are properly