Skip to content

Commit 3c5e840

Browse files
authored
Merge pull request ziglang#19714 from ziglang/elf-merge-strings
link/elf: implement string merging
2 parents e0d7a98 + da55af1 commit 3c5e840

File tree

18 files changed

+1368
-343
lines changed

18 files changed

+1368
-343
lines changed

src/link/Elf.zig

Lines changed: 305 additions & 32 deletions
Large diffs are not rendered by default.

src/link/Elf/Atom.zig

Lines changed: 187 additions & 114 deletions
Large diffs are not rendered by default.

src/link/Elf/LinkerDefined.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) !void {
6060
if (file_ptr.index() != self.index) continue;
6161
global.flags.output_symtab = true;
6262
if (global.isLocal(elf_file)) {
63-
try global.setOutputSymtabIndex(self.output_symtab_ctx.nlocals, elf_file);
63+
try global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
6464
self.output_symtab_ctx.nlocals += 1;
6565
} else {
66-
try global.setOutputSymtabIndex(self.output_symtab_ctx.nglobals, elf_file);
66+
try global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file);
6767
self.output_symtab_ctx.nglobals += 1;
6868
}
6969
self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1;

src/link/Elf/Object.zig

Lines changed: 230 additions & 19 deletions
Large diffs are not rendered by default.

src/link/Elf/SharedObject.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void {
231231

232232
const global = elf_file.symbol(index);
233233
if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) {
234-
global.value = this_sym.st_value;
234+
global.value = @intCast(this_sym.st_value);
235235
global.atom_index = 0;
236236
global.esym_index = esym_index;
237237
global.version_index = self.versyms.items[esym_index];
@@ -269,7 +269,7 @@ pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) !void {
269269
if (file_ptr.index() != self.index) continue;
270270
if (global.isLocal(elf_file)) continue;
271271
global.flags.output_symtab = true;
272-
try global.setOutputSymtabIndex(self.output_symtab_ctx.nglobals, elf_file);
272+
try global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file);
273273
self.output_symtab_ctx.nglobals += 1;
274274
self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1;
275275
}

src/link/Elf/Symbol.zig

Lines changed: 67 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Represents a defined symbol.
22

33
/// Allocated address value of this symbol.
4-
value: u64 = 0,
4+
value: i64 = 0,
55

66
/// Offset into the linker's string table.
77
name_offset: u32 = 0,
@@ -14,7 +14,7 @@ file_index: File.Index = 0,
1414
/// Use `atom` to get the pointer to the atom.
1515
atom_index: Atom.Index = 0,
1616

17-
/// Assigned output section index for this atom.
17+
/// Assigned output section index for this symbol.
1818
output_section_index: u32 = 0,
1919

2020
/// Index of the source symbol this symbol references.
@@ -33,7 +33,8 @@ extra_index: u32 = 0,
3333
pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool {
3434
const file_ptr = symbol.file(elf_file).?;
3535
if (file_ptr == .shared_object) return symbol.elfSym(elf_file).st_shndx == elf.SHN_ABS;
36-
return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.outputShndx() == null and
36+
return !symbol.flags.import and symbol.atom(elf_file) == null and
37+
symbol.mergeSubsection(elf_file) == null and symbol.outputShndx() == null and
3738
file_ptr != .linker_defined;
3839
}
3940

@@ -70,6 +71,12 @@ pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom {
7071
return elf_file.atom(symbol.atom_index);
7172
}
7273

74+
pub fn mergeSubsection(symbol: Symbol, elf_file: *Elf) ?*MergeSubsection {
75+
if (!symbol.flags.merge_subsection) return null;
76+
const extras = symbol.extra(elf_file).?;
77+
return elf_file.mergeSubsection(extras.subsection);
78+
}
79+
7380
pub fn file(symbol: Symbol, elf_file: *Elf) ?File {
7481
return elf_file.file(symbol.file_index);
7582
}
@@ -92,7 +99,11 @@ pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 {
9299
return file_ptr.symbolRank(sym, in_archive);
93100
}
94101

95-
pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf) u64 {
102+
pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf) i64 {
103+
if (symbol.mergeSubsection(elf_file)) |msub| {
104+
if (!msub.alive) return 0;
105+
return msub.address(elf_file) + symbol.value;
106+
}
96107
if (symbol.flags.has_copy_rel) {
97108
return symbol.copyRelAddress(elf_file);
98109
}
@@ -108,19 +119,23 @@ pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf
108119
if (!atom_ptr.flags.alive) {
109120
if (mem.eql(u8, atom_ptr.name(elf_file), ".eh_frame")) {
110121
const sym_name = symbol.name(elf_file);
122+
const sh_addr, const sh_size = blk: {
123+
const shndx = elf_file.eh_frame_section_index orelse break :blk .{ 0, 0 };
124+
const shdr = elf_file.shdrs.items[shndx];
125+
break :blk .{ shdr.sh_addr, shdr.sh_size };
126+
};
111127
if (mem.startsWith(u8, sym_name, "__EH_FRAME_BEGIN__") or
112128
mem.startsWith(u8, sym_name, "__EH_FRAME_LIST__") or
113129
mem.startsWith(u8, sym_name, ".eh_frame_seg") or
114130
symbol.elfSym(elf_file).st_type() == elf.STT_SECTION)
115131
{
116-
return elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr;
132+
return @intCast(sh_addr);
117133
}
118134

119135
if (mem.startsWith(u8, sym_name, "__FRAME_END__") or
120136
mem.startsWith(u8, sym_name, "__EH_FRAME_LIST_END__"))
121137
{
122-
const shdr = elf_file.shdrs.items[elf_file.eh_frame_section_index.?];
123-
return shdr.sh_addr + shdr.sh_size;
138+
return @intCast(sh_addr + sh_size);
124139
}
125140

126141
// TODO I think we potentially should error here
@@ -143,65 +158,57 @@ pub fn outputSymtabIndex(symbol: Symbol, elf_file: *Elf) ?u32 {
143158
return if (symbol.isLocal(elf_file)) idx + symtab_ctx.ilocal else idx + symtab_ctx.iglobal;
144159
}
145160

146-
pub fn setOutputSymtabIndex(symbol: *Symbol, index: u32, elf_file: *Elf) !void {
147-
if (symbol.extra(elf_file)) |extras| {
148-
var new_extras = extras;
149-
new_extras.symtab = index;
150-
symbol.setExtra(new_extras, elf_file);
151-
} else try symbol.addExtra(.{ .symtab = index }, elf_file);
152-
}
153-
154-
pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 {
161+
pub fn gotAddress(symbol: Symbol, elf_file: *Elf) i64 {
155162
if (!symbol.flags.has_got) return 0;
156163
const extras = symbol.extra(elf_file).?;
157164
const entry = elf_file.got.entries.items[extras.got];
158165
return entry.address(elf_file);
159166
}
160167

161-
pub fn pltGotAddress(symbol: Symbol, elf_file: *Elf) u64 {
168+
pub fn pltGotAddress(symbol: Symbol, elf_file: *Elf) i64 {
162169
if (!(symbol.flags.has_plt and symbol.flags.has_got)) return 0;
163170
const extras = symbol.extra(elf_file).?;
164171
const shdr = elf_file.shdrs.items[elf_file.plt_got_section_index.?];
165172
const cpu_arch = elf_file.getTarget().cpu.arch;
166-
return shdr.sh_addr + extras.plt_got * PltGotSection.entrySize(cpu_arch);
173+
return @intCast(shdr.sh_addr + extras.plt_got * PltGotSection.entrySize(cpu_arch));
167174
}
168175

169-
pub fn pltAddress(symbol: Symbol, elf_file: *Elf) u64 {
176+
pub fn pltAddress(symbol: Symbol, elf_file: *Elf) i64 {
170177
if (!symbol.flags.has_plt) return 0;
171178
const extras = symbol.extra(elf_file).?;
172179
const shdr = elf_file.shdrs.items[elf_file.plt_section_index.?];
173180
const cpu_arch = elf_file.getTarget().cpu.arch;
174-
return shdr.sh_addr + extras.plt * PltSection.entrySize(cpu_arch) + PltSection.preambleSize(cpu_arch);
181+
return @intCast(shdr.sh_addr + extras.plt * PltSection.entrySize(cpu_arch) + PltSection.preambleSize(cpu_arch));
175182
}
176183

177-
pub fn gotPltAddress(symbol: Symbol, elf_file: *Elf) u64 {
184+
pub fn gotPltAddress(symbol: Symbol, elf_file: *Elf) i64 {
178185
if (!symbol.flags.has_plt) return 0;
179186
const extras = symbol.extra(elf_file).?;
180187
const shdr = elf_file.shdrs.items[elf_file.got_plt_section_index.?];
181-
return shdr.sh_addr + extras.plt * 8 + GotPltSection.preamble_size;
188+
return @intCast(shdr.sh_addr + extras.plt * 8 + GotPltSection.preamble_size);
182189
}
183190

184-
pub fn copyRelAddress(symbol: Symbol, elf_file: *Elf) u64 {
191+
pub fn copyRelAddress(symbol: Symbol, elf_file: *Elf) i64 {
185192
if (!symbol.flags.has_copy_rel) return 0;
186193
const shdr = elf_file.shdrs.items[elf_file.copy_rel_section_index.?];
187-
return shdr.sh_addr + symbol.value;
194+
return @as(i64, @intCast(shdr.sh_addr)) + symbol.value;
188195
}
189196

190-
pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 {
197+
pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) i64 {
191198
if (!symbol.flags.has_tlsgd) return 0;
192199
const extras = symbol.extra(elf_file).?;
193200
const entry = elf_file.got.entries.items[extras.tlsgd];
194201
return entry.address(elf_file);
195202
}
196203

197-
pub fn gotTpAddress(symbol: Symbol, elf_file: *Elf) u64 {
204+
pub fn gotTpAddress(symbol: Symbol, elf_file: *Elf) i64 {
198205
if (!symbol.flags.has_gottp) return 0;
199206
const extras = symbol.extra(elf_file).?;
200207
const entry = elf_file.got.entries.items[extras.gottp];
201208
return entry.address(elf_file);
202209
}
203210

204-
pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) u64 {
211+
pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) i64 {
205212
if (!symbol.flags.has_tlsdesc) return 0;
206213
const extras = symbol.extra(elf_file).?;
207214
const entry = elf_file.got.entries.items[extras.tlsdesc];
@@ -221,7 +228,7 @@ pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *E
221228
return .{ .found_existing = false, .index = index };
222229
}
223230

224-
pub fn zigGotAddress(symbol: Symbol, elf_file: *Elf) u64 {
231+
pub fn zigGotAddress(symbol: Symbol, elf_file: *Elf) i64 {
225232
if (!symbol.flags.has_zig_got) return 0;
226233
const extras = symbol.extra(elf_file).?;
227234
return elf_file.zig_got.entryAddress(extras.zig_got, elf_file);
@@ -240,8 +247,31 @@ pub fn dsoAlignment(symbol: Symbol, elf_file: *Elf) !u64 {
240247
@min(alignment, try std.math.powi(u64, 2, @ctz(esym.st_value)));
241248
}
242249

243-
pub fn addExtra(symbol: *Symbol, extras: Extra, elf_file: *Elf) !void {
244-
symbol.extra_index = try elf_file.addSymbolExtra(extras);
250+
const AddExtraOpts = struct {
251+
got: ?u32 = null,
252+
plt: ?u32 = null,
253+
plt_got: ?u32 = null,
254+
dynamic: ?u32 = null,
255+
symtab: ?u32 = null,
256+
copy_rel: ?u32 = null,
257+
tlsgd: ?u32 = null,
258+
gottp: ?u32 = null,
259+
tlsdesc: ?u32 = null,
260+
zig_got: ?u32 = null,
261+
subsection: ?u32 = null,
262+
};
263+
264+
pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) !void {
265+
if (symbol.extra(elf_file) == null) {
266+
symbol.extra_index = try elf_file.addSymbolExtra(.{});
267+
}
268+
var extras = symbol.extra(elf_file).?;
269+
inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
270+
if (@field(opts, field.name)) |x| {
271+
@field(extras, field.name) = x;
272+
}
273+
}
274+
symbol.setExtra(extras, elf_file);
245275
}
246276

247277
pub fn extra(symbol: Symbol, elf_file: *Elf) ?Extra {
@@ -266,6 +296,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
266296
if (symbol.flags.has_copy_rel) break :blk @intCast(elf_file.copy_rel_section_index.?);
267297
if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF;
268298
if (elf_file.base.isRelocatable() and esym.st_shndx == elf.SHN_COMMON) break :blk elf.SHN_COMMON;
299+
if (symbol.mergeSubsection(elf_file)) |msub| break :blk @intCast(msub.mergeSection(elf_file).output_section_index);
269300
if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) break :blk elf.SHN_ABS;
270301
break :blk @intCast(symbol.outputShndx() orelse elf.SHN_UNDEF);
271302
};
@@ -284,7 +315,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
284315
out.st_info = (st_bind << 4) | st_type;
285316
out.st_other = esym.st_other;
286317
out.st_shndx = st_shndx;
287-
out.st_value = st_value;
318+
out.st_value = @intCast(st_value);
288319
out.st_size = esym.st_size;
289320
}
290321

@@ -436,6 +467,9 @@ pub const Flags = packed struct {
436467
/// TODO this is really not needed if only we operated on esyms between
437468
/// codegen and ZigObject.
438469
is_tls: bool = false,
470+
471+
/// Whether the symbol is a merge subsection.
472+
merge_subsection: bool = false,
439473
};
440474

441475
pub const Extra = struct {
@@ -449,6 +483,7 @@ pub const Extra = struct {
449483
gottp: u32 = 0,
450484
tlsdesc: u32 = 0,
451485
zig_got: u32 = 0,
486+
subsection: u32 = 0,
452487
};
453488

454489
pub const Index = u32;
@@ -465,6 +500,7 @@ const File = @import("file.zig").File;
465500
const GotSection = synthetic_sections.GotSection;
466501
const GotPltSection = synthetic_sections.GotPltSection;
467502
const LinkerDefined = @import("LinkerDefined.zig");
503+
const MergeSubsection = @import("merge_section.zig").MergeSubsection;
468504
const Object = @import("Object.zig");
469505
const PltSection = synthetic_sections.PltSection;
470506
const PltGotSection = synthetic_sections.PltGotSection;

src/link/Elf/ZigObject.zig

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) void {
343343
atom.outputShndx().?
344344
else
345345
elf.SHN_UNDEF;
346-
global.value = esym.st_value;
346+
global.value = @intCast(esym.st_value);
347347
global.atom_index = atom_index;
348348
global.esym_index = esym_index;
349349
global.file_index = self.index;
@@ -566,7 +566,7 @@ pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void {
566566
else => {},
567567
}
568568
local.flags.output_symtab = true;
569-
try local.setOutputSymtabIndex(self.output_symtab_ctx.nlocals, elf_file);
569+
try local.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
570570
self.output_symtab_ctx.nlocals += 1;
571571
self.output_symtab_ctx.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1;
572572
}
@@ -578,10 +578,10 @@ pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void {
578578
if (global.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
579579
global.flags.output_symtab = true;
580580
if (global.isLocal(elf_file)) {
581-
try global.setOutputSymtabIndex(self.output_symtab_ctx.nlocals, elf_file);
581+
try global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
582582
self.output_symtab_ctx.nlocals += 1;
583583
} else {
584-
try global.setOutputSymtabIndex(self.output_symtab_ctx.nglobals, elf_file);
584+
try global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file);
585585
self.output_symtab_ctx.nglobals += 1;
586586
}
587587
self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1;
@@ -631,7 +631,7 @@ pub fn codeAlloc(self: ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8
631631
return code;
632632
}
633633

634-
const file_offset = shdr.sh_offset + atom.value;
634+
const file_offset = shdr.sh_offset + @as(u64, @intCast(atom.value));
635635
const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
636636
const code = try gpa.alloc(u8, size);
637637
errdefer gpa.free(code);
@@ -659,7 +659,7 @@ pub fn getDeclVAddr(
659659
.r_info = (@as(u64, @intCast(this_sym.esym_index)) << 32) | r_type,
660660
.r_addend = reloc_info.addend,
661661
});
662-
return vaddr;
662+
return @intCast(vaddr);
663663
}
664664

665665
pub fn getAnonDeclVAddr(
@@ -678,7 +678,7 @@ pub fn getAnonDeclVAddr(
678678
.r_info = (@as(u64, @intCast(sym.esym_index)) << 32) | r_type,
679679
.r_addend = reloc_info.addend,
680680
});
681-
return vaddr;
681+
return @intCast(vaddr);
682682
}
683683

684684
pub fn lowerAnonDecl(
@@ -929,7 +929,7 @@ fn updateDeclCode(
929929

930930
if (old_size > 0 and elf_file.base.child_pid == null) {
931931
const capacity = atom_ptr.capacity(elf_file);
932-
const need_realloc = code.len > capacity or !required_alignment.check(atom_ptr.value);
932+
const need_realloc = code.len > capacity or !required_alignment.check(@intCast(atom_ptr.value));
933933
if (need_realloc) {
934934
try atom_ptr.grow(elf_file);
935935
log.debug("growing {} from 0x{x} to 0x{x}", .{ decl_name.fmt(&mod.intern_pool), old_vaddr, atom_ptr.value });
@@ -984,7 +984,7 @@ fn updateDeclCode(
984984

985985
const shdr = elf_file.shdrs.items[shdr_index];
986986
if (shdr.sh_type != elf.SHT_NOBITS) {
987-
const file_offset = shdr.sh_offset + atom_ptr.value;
987+
const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value));
988988
try elf_file.base.file.?.pwriteAll(code, file_offset);
989989
}
990990
}
@@ -1107,7 +1107,7 @@ pub fn updateFunc(
11071107
try self.dwarf.?.commitDeclState(
11081108
mod,
11091109
decl_index,
1110-
sym.address(.{}, elf_file),
1110+
@intCast(sym.address(.{}, elf_file)),
11111111
sym.atom(elf_file).?.size,
11121112
ds,
11131113
);
@@ -1186,7 +1186,7 @@ pub fn updateDecl(
11861186
try self.dwarf.?.commitDeclState(
11871187
mod,
11881188
decl_index,
1189-
sym.address(.{}, elf_file),
1189+
@intCast(sym.address(.{}, elf_file)),
11901190
sym.atom(elf_file).?.size,
11911191
ds,
11921192
);
@@ -1275,7 +1275,7 @@ fn updateLazySymbol(
12751275
}
12761276

12771277
const shdr = elf_file.shdrs.items[output_section_index];
1278-
const file_offset = shdr.sh_offset + atom_ptr.value;
1278+
const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value));
12791279
try elf_file.base.file.?.pwriteAll(code, file_offset);
12801280
}
12811281

@@ -1373,7 +1373,7 @@ fn lowerConst(
13731373
local_esym.st_value = 0;
13741374

13751375
const shdr = elf_file.shdrs.items[output_section_index];
1376-
const file_offset = shdr.sh_offset + atom_ptr.value;
1376+
const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value));
13771377
try elf_file.base.file.?.pwriteAll(code, file_offset);
13781378

13791379
return .{ .ok = sym_index };
@@ -1457,7 +1457,7 @@ pub fn updateExports(
14571457

14581458
const actual_esym_index = global_esym_index & symbol_mask;
14591459
const global_esym = &self.global_esyms.items(.elf_sym)[actual_esym_index];
1460-
global_esym.st_value = elf_file.symbol(sym_index).value;
1460+
global_esym.st_value = @intCast(elf_file.symbol(sym_index).value);
14611461
global_esym.st_shndx = esym.st_shndx;
14621462
global_esym.st_info = (stb_bits << 4) | stt_bits;
14631463
global_esym.st_name = name_off;

0 commit comments

Comments
 (0)