From 0cb34615d9d1cdce408df1fc8ecc4368566e2ec6 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Sun, 7 Nov 2021 18:59:17 +0900 Subject: [PATCH] [Mach-O] wip --- macho/mold.h | 5 +++-- macho/output-chunks.cc | 48 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/macho/mold.h b/macho/mold.h index 26d82b5d..83cf22ee 100644 --- a/macho/mold.h +++ b/macho/mold.h @@ -277,13 +277,14 @@ class RebaseEncoder { public: RebaseEncoder(); void add(i64 seg_idx, i64 offset); + void flush(); void finish(); std::vector buf; private: - i64 last_seg = -1; - i64 last_off = 0; + i64 cur_seg = -1; + i64 cur_off = 0; i64 times = 0; }; diff --git a/macho/output-chunks.cc b/macho/output-chunks.cc index 644f0cf3..c1dce436 100644 --- a/macho/output-chunks.cc +++ b/macho/output-chunks.cc @@ -422,12 +422,54 @@ RebaseEncoder::RebaseEncoder() { void RebaseEncoder::add(i64 seg_idx, i64 offset) { assert(seg_idx < 16); - buf.push_back(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | seg_idx); - encode_uleb(buf, offset); - buf.push_back(REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1); + + // Accumulate consecutive base relocations + if (seg_idx == cur_seg && offset == cur_off) { + cur_off += 8; + times++; + return; + } + + // Flush the accumulated base relocations + flush(); + + // Advance the cursor + if (seg_idx != cur_seg) { + buf.push_back(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | seg_idx); + encode_uleb(buf, offset); + } else { + i64 dist = offset - cur_off; + assert(dist >= 0); + + if (dist % 8 == 0 && dist < 128) { + buf.push_back(REBASE_OPCODE_ADD_ADDR_IMM_SCALED | (dist >> 3)); + } else { + buf.push_back(REBASE_OPCODE_ADD_ADDR_ULEB); + encode_uleb(buf, dist); + } + } + + cur_seg = seg_idx; + cur_off = offset + 8; + times = 1; +} + +void RebaseEncoder::flush() { + if (times == 0) + return; + + if (times < 16) { + buf.push_back(REBASE_OPCODE_DO_REBASE_IMM_TIMES | times); + } else { + buf.push_back(REBASE_OPCODE_DO_REBASE_ULEB_TIMES); + encode_uleb(buf, times); + } + + times = 0; } void RebaseEncoder::finish() { + flush(); buf.push_back(REBASE_OPCODE_DONE); }