Aligned Instruction Bundling#
Overview#
Aligned instruction bundling partitions the instructions in a section into fixed-size, naturally aligned groups called bundles, and guarantees that no instruction ever crosses a bundle boundary. Consecutive instructions can also be grouped so that the assembler guarantees the whole group resides within a single bundle.
Bundling is a building block for software-based fault isolation (SFI) and sandboxing schemes. Forcing every instruction to begin at one of a statically known set of offsets gives the instruction stream a single canonical decoding: control flow cannot jump into the middle of an instruction to manufacture a different, unchecked instruction sequence. When combined with masking of indirect branch targets to bundle-aligned addresses, this constrains all control flow to a statically verifiable set of locations and instructions. Bundling is used by the x86-64 implementation of Lightweight Fault Isolation (LFI).
Note
The current LLVM implementation supports bundling only for x86 ELF targets. Other targets and object file formats reject the directives below rather than emit unbundled code silently.
.bundle_align_mode#
.bundle_align_mode abs-expr
Enables aligned bundle mode and sets the bundle size to 2^abs-expr bytes,
where abs-expr is a power-of-two exponent between 1 and 30 (as for the
.p2align directive). For example, .bundle_align_mode 5 selects 32-byte
bundles. Unlike GNU as, .bundle_align_mode 0 is rejected: bundling cannot be
turned off once enabled.
While bundling is enabled, the assembler ensures that no single instruction spans a boundary between two bundles. When an instruction would not fit in the space remaining in the current bundle, that space is filled with no-op instructions so the instruction starts at the beginning of the next bundle.
Enabling bundle mode also raises the alignment of every text section that receives instructions to at least the bundle size.
Once enabled, bundle mode stays in effect for the rest of the file and its bundle size is fixed.
.bundle_lock and .bundle_unlock#
.bundle_lock [align_to_end]
...instructions...
.bundle_unlock
A .bundle_lock / .bundle_unlock pair encloses a sequence of
instructions that must all be placed in a single bundle. The assembler inserts
padding before the sequence, if necessary, so that the entire group lands
within one bundle rather than straddling a boundary.
The enclosed sequence must fit within a single bundle – it is an error if the total size of the locked instructions exceeds the bundle size.
Both directives are only valid in an executable section after bundle mode has
been enabled with .bundle_align_mode. A .bundle_unlock must be matched
by a preceding .bundle_lock, and a section may not be switched while a
.bundle_lock is open. A group may not end in a bare instruction prefix,
which the assembler could not keep attached to the instruction it modifies.
align_to_end#
By default a locked group is padded at the front so that it starts far enough
into the bundle to fit. With the align_to_end option the group is instead
padded so that its last instruction ends exactly on a bundle boundary.
Padding#
By default, bundle padding is emitted as no-op instructions, and neither an instruction nor a padding no-op is ever allowed to cross a bundle boundary.
Prefix padding (x86)#
On x86 the assembler can instead absorb some of the required padding into neighboring instructions by prepending otherwise-ignored instruction prefixes to them, avoiding standalone no-ops. This is controlled by:
- --x86-pad-max-prefix-size=<N>#
Maximum number of prefixes the assembler may add to an instruction for padding.
0(the default) disables prefix padding, so only no-op instructions are used.
Padding is only ever traded for instruction bytes within the bundle that holds it, so this cannot move an instruction or a locked group across a boundary. It does change where labels land, so it may break assumptions about labels corresponding to particular instructions.