Hex-Rays' blog

Igor’s Tip of the Week #134: ARM BL jumps – Hex Rays

Written by Igor Skochinsky | Mar 30, 2023

If you ever looked at IDA ARM module’s processor-specific settings, you may have been puzzled by the option “Disable BL jumps detection”.

What is it and when to use it?

Background

The ARM instruction set initially used fixed-width 32-bit instructions. The relative branch instruction, B, allocated 24 bits for the offset, giving it a range of ±32MB.

Some time later, ARM introduced a a compact 16-bit encoding for a subset of instructions, called Thumb. Because most relative branches occur in the same function, the ±2KB range available for 16-bit B instructions was usually enough. In case longer distance was needed, a longer instruction sequence would have to be generated.

Some compiler writers realized, that the BL instruction, normally used for function calls, can be used for simple branches as well. On ARM, the function calls do not use the stack, so the only side effect of BL as opposed to simple branch is that it sets the LR register to the address following the BL instruction. If the LR is saved at the start of the current function, it does not matter that if LR is clobbered by the intermediate BL instructions, since it can be restored from the saved area to return to the caller. The BL is encoded as pair of 16-bit instructions, which gives it a range of ±4MB.

A later extension of the Thumb, called Thumb-2, introduced a 32-bit version of B, giving it a range of ±16MB, so there is less need of such tricks in code compiled for modern processors which support Thumb-2. However, old code still needs to be analyzed sometimes, so it may be necessary to support such usage of BL

Example

Here’s an example of a Thumb mode program which looks a little strange…

IDA has created a function because of the BL instruction which normally implies a function call. But we see that func is not complete, so most likely sub_C is actually its continuation and BL is used only as a branch. Also, func saves LR on the stack, so BL clobbering it does not matter.

Marking single instructions

If the BL-as-branch approach is used only in few cases, you can handle them manually. For this, place the cursor on the line with BL and use Edit > Other > Force BL jump menu item. IDA will take this into account and indicate that this BL does not continue to the next instruction by adding a dashed comment line after it

You can then delete the wrongly created function and extend or recreate the original one which had been truncated.

Changing analysis behavior

If the binary has multiple functions which use this technique, it may be worth it to let the analyzer check each BL destination before creating functions. For this, turn off Disable BL jumps detection in the processor specific options and reanalyze the program. Note that you will likely have to delete the wrongly created functions, so it may be better to reload the file, changing the options in the initial Load File dialog.

To set this by default, change ARM_DISABLE_BL_JUMPS value in ida.cfg.

In cases where the BL jumps detection fails  (it marks a BL as a jump where it should be a call, or vice versa), you can always override its decision using Force BL jump and Force BL call menu options. In case you discover a specific code pattern and need to script it, you can also use IDC functions force_bl_jump(ea) and force_bl_call(ea).