Skip to content

Commit

Permalink
[lld-macho] Use DO_BIND_ADD_ADDR_IMM_SCALED for bind opcodes
Browse files Browse the repository at this point in the history
Implement pass 3 of bind opcodes from ld64 (which supports both 32-bit and 64-bit).
Pass 3 implementation condenses BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB opcode
to BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED.  This change is already behind an
O2 flag so it shouldn't impact current performance. I verified ld64's output with x86_64 LLD
and they were both emitting the same optimized bind opcodes (although in a slightly different
order). Tested with arm64_32 LLD and compared that with x86 LLD that the order of the bind
opcodes are the same (offset values are different which should be expected).

Reviewed By: int3, #lld-macho

Differential Revision: https://reviews.llvm.org/D106128
  • Loading branch information
thevinster committed Jul 19, 2021
1 parent de3ea51 commit 321b2be
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 24 deletions.
20 changes: 20 additions & 0 deletions lld/MachO/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,23 @@ static void optimizeOpcodes(std::vector<BindIR> &opcodes) {
if (i == opcodes.size())
opcodes[pWrite] = opcodes[i - 1];
opcodes.resize(pWrite + 1);

// Pass 3: Use immediate encodings
// Every binding is the size of one pointer. If the next binding is a
// multiple of wordSize away that is within BIND_IMMEDIATE_MASK, the
// opcode can be scaled by wordSize into a single byte and dyld will
// expand it to the correct address.
for (BindIR *p = &opcodes[0]; p->opcode != BIND_OPCODE_DONE; ++p) {
// It's unclear why the check needs to be less than BIND_IMMEDIATE_MASK,
// but ld64 currently does this. This could be a potential bug, but
// for now, perform the same behavior to prevent mysterious bugs.
if ((p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) &&
((p->data / target->wordSize) < BIND_IMMEDIATE_MASK) &&
((p->data % target->wordSize) == 0)) {
p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED;
p->data /= target->wordSize;
}
}
}

static void flushOpcodes(const BindIR &op, raw_svector_ostream &os) {
Expand All @@ -383,6 +400,9 @@ static void flushOpcodes(const BindIR &op, raw_svector_ostream &os) {
encodeULEB128(op.consecutiveCount, os);
encodeULEB128(op.data, os);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
os << static_cast<uint8_t>(op.opcode | op.data);
break;
default:
llvm_unreachable("cannot bind to an unrecognized symbol");
}
Expand Down
130 changes: 106 additions & 24 deletions lld/test/MachO/bind-opcodes.s
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# REQUIRES: x86
# REQUIRES: x86, arm
# RUN: rm -rf %t; split-file %s %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/foo.s -o %t/foo.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin --defsym PTR64=0 %t/test.s -o %t/test.o
# RUN: %lld -O2 -dylib %t/foo.o -o %t/libfoo.dylib
# RUN: %lld -O2 -lSystem %t/test.o %t/libfoo.dylib -o %t/test
# RUN: %lld -O2 -lSystem %t/test.o %t/libfoo.dylib -o %t/test-x86_64

## Test:
## Test (64-bit):
## 1/ We emit exactly one BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM per symbol.
## 2/ Combine BIND_OPCODE_DO_BIND and BIND_OPCODE_ADD_ADDR_ULEB pairs.
## 3/ Compact BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
# RUN: obj2yaml %t/test | FileCheck %s
## 4/ Use BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED if possible.
# RUN: obj2yaml %t/test-x86_64 | FileCheck %s

# CHECK: BindOpcodes:
# CHECK-NEXT: Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
Expand Down Expand Up @@ -54,9 +55,8 @@
# CHECK-NEXT: Imm: 0
# CHECK-NEXT: ULEBExtraData: [ 0xFFFFFFFFFFFFEFD0 ]
# CHECK-NEXT: Symbol: ''
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
# CHECK-NEXT: Imm: 0
# CHECK-NEXT: ULEBExtraData: [ 0x8 ]
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
# CHECK-NEXT: Imm: 1
# CHECK-NEXT: Symbol: ''
# CHECK-NEXT: Opcode: BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
# CHECK-NEXT: Imm: 0
Expand All @@ -69,16 +69,86 @@
# CHECK-NEXT: Imm: 0
# CHECK-NEXT: Symbol: ''

# RUN: llvm-objdump --macho --bind %t/test | FileCheck %s --check-prefix=BIND
# RUN: llvm-mc -filetype=obj -triple=arm64_32-apple-darwin %t/foo.s -o %t/foo.o
# RUN: llvm-mc -filetype=obj -triple=arm64_32-apple-darwin --defsym PTR32=0 %t/test.s -o %t/test.o
# RUN: %lld -arch arm64_32 -O2 -dylib %t/foo.o -o %t/libfoo.dylib
# RUN: %lld -arch arm64_32 -O2 -dylib %t/test.o %t/libfoo.dylib -o %t/libtest-arm64_32.dylib

## Test (32-bit):
## 1/ We emit exactly one BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM per symbol.
## 2/ Combine BIND_OPCODE_DO_BIND and BIND_OPCODE_ADD_ADDR_ULEB pairs.
## 3/ Compact BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
## 4/ Use BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED if possible.
# RUN: obj2yaml %t/libtest-arm64_32.dylib | FileCheck %s --check-prefix=CHECK32

# CHECK32: BindOpcodes:
# CHECK32-NEXT: Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
# CHECK32-NEXT: Imm: 0
# CHECK32-NEXT: Symbol: _foo
# CHECK32-NEXT: Opcode: BIND_OPCODE_SET_TYPE_IMM
# CHECK32-NEXT: Imm: 1
# CHECK32-NEXT: Symbol: ''
# CHECK32-NEXT: Opcode: BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
# CHECK32-NEXT: Imm: 1
# CHECK32-NEXT: Symbol: ''
# CHECK32-NEXT: Opcode: BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
# CHECK32-NEXT: Imm: 1
# CHECK32-NEXT: ULEBExtraData: [ 0x0 ]
# CHECK32-NEXT: Symbol: ''
# CHECK32-NEXT: Opcode: BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
# CHECK32-NEXT: Imm: 0
# CHECK32-NEXT: ULEBExtraData: [ 0x2, 0x4 ]
# CHECK32-NEXT: Symbol: ''
# CHECK32-NEXT: Opcode: BIND_OPCODE_SET_ADDEND_SLEB
# CHECK32-NEXT: Imm: 0
# CHECK32-NEXT: SLEBExtraData: [ 1 ]
# CHECK32-NEXT: Symbol: ''
# CHECK32-NEXT: Opcode: BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
# CHECK32-NEXT: Imm: 0
# CHECK32-NEXT: ULEBExtraData: [ 0x1004 ]
# CHECK32-NEXT: Symbol: ''
# CHECK32-NEXT: Opcode: BIND_OPCODE_SET_ADDEND_SLEB
# CHECK32-NEXT: Imm: 0
# CHECK32-NEXT: SLEBExtraData: [ 0 ]
# CHECK32-NEXT: Symbol: ''
# CHECK32-NEXT: Opcode: BIND_OPCODE_DO_BIND
# CHECK32-NEXT: Imm: 0
# CHECK32-NEXT: Symbol: ''
# CHECK32-NEXT: Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
# CHECK32-NEXT: Imm: 0
# CHECK32-NEXT: Symbol: _bar
# CHECK32-NEXT: Opcode: BIND_OPCODE_SET_TYPE_IMM
# CHECK32-NEXT: Imm: 1
# CHECK32-NEXT: Symbol: ''
# CHECK32-NEXT: Opcode: BIND_OPCODE_ADD_ADDR_ULEB
# CHECK32-NEXT: Imm: 0
# CHECK32-NEXT: ULEBExtraData: [ 0xFFFFFFFFFFFFEFE8 ]
# CHECK32-NEXT: Symbol: ''
# CHECK32-NEXT: Opcode: BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
# CHECK32-NEXT: Imm: 1
# CHECK32-NEXT: Symbol: ''
# CHECK32-NEXT: Opcode: BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
# CHECK32-NEXT: Imm: 0
# CHECK32-NEXT: ULEBExtraData: [ 0x1004 ]
# CHECK32-NEXT: Symbol: ''
# CHECK32-NEXT: Opcode: BIND_OPCODE_DO_BIND
# CHECK32-NEXT: Imm: 0
# CHECK32-NEXT: Symbol: ''
# CHECK32-NEXT: Opcode: BIND_OPCODE_DONE
# CHECK32-NEXT: Imm: 0
# CHECK32-NEXT: Symbol: ''

# RUN: llvm-objdump --macho --bind %t/test-x86_64 | FileCheck %s -D#PTR=8 --check-prefix=BIND
# RUN: llvm-objdump --macho --bind %t/libtest-arm64_32.dylib | FileCheck %s -D#PTR=4 --check-prefix=BIND
# BIND: Bind table:
# BIND-NEXT: segment section address type addend dylib symbol
# BIND-NEXT: __DATA __data 0x100001000 pointer 0 libfoo _foo
# BIND-NEXT: __DATA __data 0x100001010 pointer 0 libfoo _foo
# BIND-NEXT: __DATA __data 0x100001020 pointer 1 libfoo _foo
# BIND-NEXT: __DATA __data 0x100002030 pointer 0 libfoo _foo
# BIND-NEXT: __DATA __data 0x100001008 pointer 0 libfoo _bar
# BIND-NEXT: __DATA __data 0x100001018 pointer 0 libfoo _bar
# BIND-NEXT: __DATA __data 0x100002028 pointer 0 libfoo _bar
# BIND-NEXT: segment section address type addend dylib symbol
# BIND-NEXT: __DATA __data 0x[[#%X,DATA:]] pointer 0 libfoo _foo
# BIND-NEXT: __DATA __data 0x[[#%.8X,DATA + mul(PTR, 2)]] pointer 0 libfoo _foo
# BIND-NEXT: __DATA __data 0x[[#%.8X,DATA + mul(PTR, 4)]] pointer 1 libfoo _foo
# BIND-NEXT: __DATA __data 0x[[#%.8X,DATA + 4096 + mul(PTR, 6)]] pointer 0 libfoo _foo
# BIND-NEXT: __DATA __data 0x[[#%.8X,DATA + PTR]] pointer 0 libfoo _bar
# BIND-NEXT: __DATA __data 0x[[#%.8X,DATA + mul(PTR, 3)]] pointer 0 libfoo _bar
# BIND-NEXT: __DATA __data 0x[[#%.8X,DATA + 4096 + mul(PTR, 5)]] pointer 0 libfoo _bar
# BIND-EMPTY:

#--- foo.s
Expand All @@ -89,15 +159,27 @@ _bar:
.space 4

#--- test.s
.ifdef PTR64
.macro ptr val
.quad \val
.endm
.endif

.ifdef PTR32
.macro ptr val
.int \val
.endm
.endif

.data
.quad _foo
.quad _bar
.quad _foo
.quad _bar
.quad _foo+1
ptr _foo
ptr _bar
ptr _foo
ptr _bar
ptr _foo+1
.zero 0x1000
.quad _bar
.quad _foo
ptr _bar
ptr _foo

.globl _main
.text
Expand Down

0 comments on commit 321b2be

Please sign in to comment.