Skip to content

Commit b843b2f

Browse files
committed
[Mach-O] Merge all S_CSTRING_LITERALS sections
__TEXT,__cstring is one of such sections.
1 parent a4cc7f3 commit b843b2f

File tree

3 files changed

+115
-111
lines changed

3 files changed

+115
-111
lines changed

macho/input-files.cc

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ split_regular_sections(Context<E> &ctx, ObjectFile<E> &file) {
166166

167167
for (i64 i = 0; i < file.sections.size(); i++)
168168
if (InputSection<E> *isec = file.sections[i].get())
169-
if (!isec->hdr.match("__TEXT", "__cstring"))
169+
if (isec->hdr.type != S_CSTRING_LITERALS)
170170
vec[i].isec = isec;
171171

172172
// Find all symbols whose type is N_SECT.
@@ -226,12 +226,14 @@ template <typename E>
226226
void ObjectFile<E>::split_subsections_via_symbols(Context<E> &ctx) {
227227
sym_to_subsec.resize(mach_syms.size());
228228

229-
auto add = [&](InputSection<E> &isec, u32 offset, u32 size, u8 p2align) {
229+
auto add = [&](InputSection<E> &isec, u32 offset, u32 size, u8 p2align,
230+
bool is_cstring) {
230231
Subsection<E> *subsec = new Subsection<E>{
231232
.isec = isec,
232233
.input_offset = offset,
233234
.input_size = size,
234235
.input_addr = (u32)(isec.hdr.addr + offset),
236+
.is_cstring = is_cstring,
235237
.p2align = p2align,
236238
};
237239

@@ -244,22 +246,22 @@ void ObjectFile<E>::split_subsections_via_symbols(Context<E> &ctx) {
244246
InputSection<E> &isec = *info.isec;
245247
for (SplitRegion &r : info.regions) {
246248
if (!r.is_alt_entry)
247-
add(isec, r.offset, r.size, isec.hdr.p2align);
249+
add(isec, r.offset, r.size, isec.hdr.p2align, false);
248250
if (r.symidx != -1)
249251
sym_to_subsec[r.symidx] = subsections.back();
250252
}
251253
}
252254

253255
// Split __cstring section.
254256
for (std::unique_ptr<InputSection<E>> &isec : sections) {
255-
if (isec && isec->hdr.match("__TEXT", "__cstring")) {
257+
if (isec && isec->hdr.type == S_CSTRING_LITERALS) {
256258
std::string_view str = isec->contents;
257259
size_t pos = 0;
258260

259261
while (pos < str.size()) {
260262
size_t end = str.find('\0', pos);
261263
if (end == str.npos)
262-
Fatal(ctx) << *this << " corruupted __TEXT,__cstring";
264+
Fatal(ctx) << *this << " corruupted cstring section: " << *isec;
263265

264266
end = str.find_first_not_of('\0', end);
265267
if (end == str.npos)
@@ -268,7 +270,7 @@ void ObjectFile<E>::split_subsections_via_symbols(Context<E> &ctx) {
268270
// A constant string in __cstring has no alignment info, so we
269271
// need to infer it.
270272
u8 p2align = std::min<u8>(isec->hdr.p2align, std::countr_zero(pos));
271-
add(*isec, pos, end - pos, p2align);
273+
add(*isec, pos, end - pos, p2align, true);
272274
pos = end;
273275
}
274276
}
@@ -422,7 +424,8 @@ void ObjectFile<E>::parse_compact_unwind(Context<E> &ctx, MachSection &hdr) {
422424
UnwindRecord<E> &dst = unwind_records[idx];
423425

424426
auto error = [&] {
425-
Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i;
427+
Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i
428+
<< " " << *this->syms[r.idx];
426429
};
427430

428431
if (r.is_pcrel || r.p2size != 3 || r.type)

macho/main.cc

Lines changed: 104 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -287,105 +287,6 @@ static void claim_unresolved_symbols(Context<E> &ctx) {
287287
});
288288
}
289289

290-
template <typename E>
291-
static void merge_cstring_sections(Context<E> &ctx) {
292-
Timer t(ctx, "merge_cstring_sections");
293-
294-
struct Entry {
295-
Entry(Subsection<E> *subsec) : owner(subsec) {}
296-
297-
Entry(const Entry &other) :
298-
owner(other.owner.load()), p2align(other.p2align.load()) {}
299-
300-
std::atomic<Subsection<E> *> owner = nullptr;
301-
std::atomic_uint8_t p2align = 0;
302-
};
303-
304-
struct SubsecRef {
305-
Subsection<E> &subsec;
306-
u64 hash = 0;
307-
Entry *ent = nullptr;
308-
};
309-
310-
std::vector<std::vector<SubsecRef>> vec(ctx.objs.size());
311-
312-
// Estimate the number of unique strings.
313-
HyperLogLog estimator;
314-
315-
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
316-
ObjectFile<E> *file = ctx.objs[i];
317-
HyperLogLog e;
318-
319-
for (Subsection<E> *subsec : file->subsections) {
320-
if (&subsec->isec.osec == ctx.cstring) {
321-
std::string_view str = subsec->get_contents();
322-
u64 h = hash_string(str);
323-
vec[i].push_back({*subsec, h, nullptr});
324-
estimator.insert(h);
325-
}
326-
}
327-
estimator.merge(e);
328-
});
329-
330-
// Create a hash map large enough to hold all strings.
331-
ConcurrentMap<Entry> map(estimator.get_cardinality() * 3 / 2);
332-
333-
// Insert all strings into the hash table.
334-
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
335-
ObjectFile<E> *file = ctx.objs[i];
336-
337-
for (i64 j = 0; j < vec[i].size(); j++) {
338-
SubsecRef &ref = vec[i][j];
339-
std::string_view s = ref.subsec.get_contents();
340-
ref.ent = map.insert(s, ref.hash, {&ref.subsec}).first;
341-
342-
Subsection<E> *existing = ref.ent->owner;
343-
while (existing->isec.file.priority < file->priority &&
344-
!ref.ent->owner.compare_exchange_weak(existing, &ref.subsec));
345-
346-
update_maximum(ref.ent->p2align, ref.subsec.p2align.load());
347-
}
348-
});
349-
350-
// Decide who will become the owner for each subsection.
351-
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
352-
for (i64 j = 0; j < vec[i].size(); j++) {
353-
SubsecRef &ref = vec[i][j];
354-
if (ref.ent->owner != &ref.subsec) {
355-
ref.subsec.is_coalesced = true;
356-
ref.subsec.replacer = ref.ent->owner;
357-
358-
static Counter counter("num_merged_strings");
359-
counter++;
360-
}
361-
}
362-
});
363-
364-
// Merge strings
365-
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
366-
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
367-
if (isec)
368-
for (Relocation<E> &r : isec->rels)
369-
if (r.subsec && r.subsec->is_coalesced)
370-
r.subsec = r.subsec->replacer;
371-
});
372-
373-
auto replace = [&](InputFile<E> *file) {
374-
for (Symbol<E> *sym : file->syms)
375-
if (sym->subsec && sym->subsec->is_coalesced)
376-
sym->subsec = sym->subsec->replacer;
377-
};
378-
379-
tbb::parallel_for_each(ctx.objs, replace);
380-
tbb::parallel_for_each(ctx.dylibs, replace);
381-
382-
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
383-
std::erase_if(file->subsections, [](Subsection<E> *subsec) {
384-
return subsec->is_coalesced;
385-
});
386-
});
387-
}
388-
389290
template <typename E>
390291
static Chunk<E> *find_section(Context<E> &ctx, std::string_view segname,
391292
std::string_view sectname) {
@@ -446,6 +347,109 @@ static void create_synthetic_chunks(Context<E> &ctx) {
446347
sort(seg->chunks, compare_chunks<E>);
447348
}
448349

350+
template <typename E>
351+
static void uniquify_cstrings(Context<E> &ctx, OutputSection<E> &osec) {
352+
Timer t(ctx, "uniquify_cstrings");
353+
354+
struct Entry {
355+
Entry(Subsection<E> *subsec) : owner(subsec) {}
356+
357+
Entry(const Entry &other) :
358+
owner(other.owner.load()), p2align(other.p2align.load()) {}
359+
360+
std::atomic<Subsection<E> *> owner = nullptr;
361+
std::atomic_uint8_t p2align = 0;
362+
};
363+
364+
struct SubsecRef {
365+
Subsection<E> *subsec = nullptr;
366+
u64 hash = 0;
367+
Entry *ent = nullptr;
368+
};
369+
370+
std::vector<SubsecRef> vec(osec.members.size());
371+
372+
// Estimate the number of unique strings.
373+
tbb::enumerable_thread_specific<HyperLogLog> estimators;
374+
375+
tbb::parallel_for((i64)0, (i64)osec.members.size(), [&](i64 i) {
376+
Subsection<E> *subsec = osec.members[i];
377+
if (subsec->is_cstring) {
378+
u64 h = hash_string(subsec->get_contents());
379+
vec[i].subsec = subsec;
380+
vec[i].hash = h;
381+
estimators.local().insert(h);
382+
}
383+
});
384+
385+
HyperLogLog estimator;
386+
for (HyperLogLog &e : estimators)
387+
estimator.merge(e);
388+
389+
// Create a hash map large enough to hold all strings.
390+
ConcurrentMap<Entry> map(estimator.get_cardinality() * 3 / 2);
391+
392+
// Insert all strings into the hash table.
393+
tbb::parallel_for_each(vec, [&](SubsecRef &ref) {
394+
if (ref.subsec) {
395+
std::string_view s = ref.subsec->get_contents();
396+
ref.ent = map.insert(s, ref.hash, {ref.subsec}).first;
397+
398+
Subsection<E> *existing = ref.ent->owner;
399+
while (existing->isec.file.priority < ref.subsec->isec.file.priority &&
400+
!ref.ent->owner.compare_exchange_weak(existing, ref.subsec));
401+
402+
update_maximum(ref.ent->p2align, ref.subsec->p2align.load());
403+
}
404+
});
405+
406+
// Decide who will become the owner for each subsection.
407+
tbb::parallel_for_each(vec, [&](SubsecRef &ref) {
408+
if (ref.subsec && ref.subsec != ref.ent->owner) {
409+
ref.subsec->is_coalesced = true;
410+
ref.subsec->replacer = ref.ent->owner;
411+
}
412+
});
413+
414+
static Counter counter("num_merged_strings");
415+
counter += std::erase_if(osec.members, [](Subsection<E> *subsec) {
416+
return subsec->is_coalesced;
417+
});
418+
}
419+
420+
template <typename E>
421+
static void merge_cstring_sections(Context<E> &ctx) {
422+
Timer t(ctx, "merge_cstring_sections");
423+
424+
for (Chunk<E> *chunk : ctx.chunks)
425+
if (chunk->is_output_section && chunk->hdr.type == S_CSTRING_LITERALS)
426+
uniquify_cstrings(ctx, *(OutputSection<E> *)chunk);
427+
428+
// Rewrite relocations and symbols.
429+
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
430+
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
431+
if (isec)
432+
for (Relocation<E> &r : isec->rels)
433+
if (r.subsec && r.subsec->is_coalesced)
434+
r.subsec = r.subsec->replacer;
435+
});
436+
437+
auto replace = [&](InputFile<E> *file) {
438+
for (Symbol<E> *sym : file->syms)
439+
if (sym && sym->subsec && sym->subsec->is_coalesced)
440+
sym->subsec = sym->subsec->replacer;
441+
};
442+
443+
tbb::parallel_for_each(ctx.objs, replace);
444+
tbb::parallel_for_each(ctx.dylibs, replace);
445+
446+
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
447+
std::erase_if(file->subsections, [](Subsection<E> *subsec) {
448+
return subsec->is_coalesced;
449+
});
450+
});
451+
}
452+
449453
template <typename E>
450454
static void scan_unwind_info(Context<E> &ctx) {
451455
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
@@ -930,12 +934,11 @@ static int do_main(int argc, char **argv) {
930934

931935
claim_unresolved_symbols(ctx);
932936

933-
merge_cstring_sections(ctx);
934-
935937
if (ctx.arg.dead_strip)
936938
dead_strip(ctx);
937939

938940
create_synthetic_chunks(ctx);
941+
merge_cstring_sections(ctx);
939942

940943
for (ObjectFile<E> *file : ctx.objs)
941944
file->check_duplicate_symbols(ctx);

macho/mold.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ class Subsection {
243243

244244
std::atomic_uint8_t p2align = 0;
245245
std::atomic_bool is_alive = true;
246+
bool is_cstring : 1 = false;
246247
bool is_coalesced : 1 = false;
247248
bool added_to_osec : 1 = false;
248249
};
@@ -821,11 +822,9 @@ struct Context {
821822
text = OutputSection<E>::get_instance(*this, "__TEXT", "__text");
822823
data = OutputSection<E>::get_instance(*this, "__DATA", "__data");
823824
bss = OutputSection<E>::get_instance(*this, "__DATA", "__bss");
824-
cstring = OutputSection<E>::get_instance(*this, "__TEXT", "__cstring");
825825
common = OutputSection<E>::get_instance(*this, "__DATA", "__common");
826826

827827
bss->hdr.type = S_ZEROFILL;
828-
cstring->hdr.type = S_CSTRING_LITERALS;
829828
common->hdr.type = S_ZEROFILL;
830829
}
831830

@@ -959,7 +958,6 @@ struct Context {
959958
OutputSection<E> *text = nullptr;
960959
OutputSection<E> *data = nullptr;
961960
OutputSection<E> *bss = nullptr;
962-
OutputSection<E> *cstring = nullptr;
963961
OutputSection<E> *common = nullptr;
964962
};
965963

0 commit comments

Comments
 (0)