master
  1//! Represents a defined symbol.
  2
  3/// Allocated address value of this symbol.
  4value: i64 = 0,
  5
  6/// Offset into the linker's string table.
  7name_offset: u32 = 0,
  8
  9/// Index of file where this symbol is defined.
 10file_index: File.Index = 0,
 11
 12/// Reference to Atom or merge subsection containing this symbol if any.
 13/// Use `atom` or `mergeSubsection` to get the pointer to the atom.
 14ref: Elf.Ref = .{},
 15
 16/// Assigned output section index for this symbol.
 17output_section_index: u32 = 0,
 18
 19/// Index of the source symbol this symbol references.
 20/// Use `elfSym` to pull the source symbol from the relevant file.
 21esym_index: Index = 0,
 22
 23/// Index of the source version symbol this symbol references if any.
 24/// If the symbol is unversioned it will have either VER_NDX_LOCAL or VER_NDX_GLOBAL.
 25version_index: elf.Versym = .LOCAL,
 26
 27/// Misc flags for the symbol packaged as packed struct for compression.
 28flags: Flags = .{},
 29
 30extra_index: u32 = 0,
 31
 32pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool {
 33    const file_ptr = symbol.file(elf_file).?;
 34    if (file_ptr == .shared_object) return symbol.elfSym(elf_file).st_shndx == elf.SHN_ABS;
 35    return !symbol.flags.import and symbol.atom(elf_file) == null and
 36        symbol.mergeSubsection(elf_file) == null and symbol.outputShndx(elf_file) == null and
 37        file_ptr != .linker_defined;
 38}
 39
 40pub fn outputShndx(symbol: Symbol, elf_file: *Elf) ?u32 {
 41    if (symbol.mergeSubsection(elf_file)) |msub|
 42        return if (msub.alive) msub.mergeSection(elf_file).output_section_index else null;
 43    if (symbol.atom(elf_file)) |atom_ptr|
 44        return if (atom_ptr.alive) atom_ptr.output_section_index else null;
 45    if (symbol.output_section_index == 0) return null;
 46    return symbol.output_section_index;
 47}
 48
 49pub fn isLocal(symbol: Symbol, elf_file: *Elf) bool {
 50    if (elf_file.base.isRelocatable()) return symbol.elfSym(elf_file).st_bind() == elf.STB_LOCAL;
 51    return !(symbol.flags.import or symbol.flags.@"export");
 52}
 53
 54pub fn isIFunc(symbol: Symbol, elf_file: *Elf) bool {
 55    return symbol.type(elf_file) == elf.STT_GNU_IFUNC;
 56}
 57
 58pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 {
 59    const esym = symbol.elfSym(elf_file);
 60    const file_ptr = symbol.file(elf_file).?;
 61    if (esym.st_type() == elf.STT_GNU_IFUNC and file_ptr == .shared_object) return elf.STT_FUNC;
 62    return esym.st_type();
 63}
 64
 65pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 {
 66    return switch (symbol.file(elf_file).?) {
 67        inline else => |x| x.getString(symbol.name_offset),
 68    };
 69}
 70
 71pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom {
 72    if (symbol.flags.merge_subsection) return null;
 73    const file_ptr = elf_file.file(symbol.ref.file) orelse return null;
 74    return file_ptr.atom(symbol.ref.index);
 75}
 76
 77pub fn mergeSubsection(symbol: Symbol, elf_file: *Elf) ?*Merge.Subsection {
 78    if (!symbol.flags.merge_subsection) return null;
 79    const msec = elf_file.mergeSection(symbol.ref.file);
 80    return msec.mergeSubsection(symbol.ref.index);
 81}
 82
 83pub fn file(symbol: Symbol, elf_file: *Elf) ?File {
 84    return elf_file.file(symbol.file_index);
 85}
 86
 87pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {
 88    return switch (symbol.file(elf_file).?) {
 89        .zig_object => |x| x.symtab.items(.elf_sym)[symbol.esym_index],
 90        .shared_object => |so| so.parsed.symtab[symbol.esym_index],
 91        inline else => |x| x.symtab.items[symbol.esym_index],
 92    };
 93}
 94
 95pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 {
 96    const file_ptr = symbol.file(elf_file) orelse return std.math.maxInt(u32);
 97    const sym = symbol.elfSym(elf_file);
 98    const in_archive = switch (file_ptr) {
 99        .object => |x| !x.alive,
100        else => false,
101    };
102    return file_ptr.symbolRank(sym, in_archive);
103}
104
105pub fn address(symbol: Symbol, opts: struct { plt: bool = true, trampoline: bool = true }, elf_file: *Elf) i64 {
106    if (symbol.mergeSubsection(elf_file)) |msub| {
107        if (!msub.alive) return 0;
108        return msub.address(elf_file) + symbol.value;
109    }
110    if (symbol.flags.has_copy_rel) {
111        return symbol.copyRelAddress(elf_file);
112    }
113    if (symbol.flags.has_trampoline and opts.trampoline) {
114        return symbol.trampolineAddress(elf_file);
115    }
116    if (opts.plt) {
117        if (symbol.flags.has_pltgot) {
118            assert(!symbol.flags.is_canonical);
119            // We have a non-lazy bound function pointer, use that!
120            return symbol.pltGotAddress(elf_file);
121        }
122        if (symbol.flags.has_plt) {
123            // Lazy-bound function it is!
124            return symbol.pltAddress(elf_file);
125        }
126    }
127    if (symbol.atom(elf_file)) |atom_ptr| {
128        if (!atom_ptr.alive) {
129            if (mem.eql(u8, atom_ptr.name(elf_file), ".eh_frame")) {
130                const sym_name = symbol.name(elf_file);
131                const sh_addr, const sh_size = blk: {
132                    const shndx = elf_file.section_indexes.eh_frame orelse break :blk .{ 0, 0 };
133                    const shdr = elf_file.sections.items(.shdr)[shndx];
134                    break :blk .{ shdr.sh_addr, shdr.sh_size };
135                };
136                if (mem.startsWith(u8, sym_name, "__EH_FRAME_BEGIN__") or
137                    mem.startsWith(u8, sym_name, "__EH_FRAME_LIST__") or
138                    mem.startsWith(u8, sym_name, ".eh_frame_seg") or
139                    symbol.elfSym(elf_file).st_type() == elf.STT_SECTION)
140                {
141                    return @intCast(sh_addr);
142                }
143
144                if (mem.startsWith(u8, sym_name, "__FRAME_END__") or
145                    mem.startsWith(u8, sym_name, "__EH_FRAME_LIST_END__"))
146                {
147                    return @intCast(sh_addr + sh_size);
148                }
149
150                // TODO I think we potentially should error here
151            }
152
153            return 0;
154        }
155        return atom_ptr.address(elf_file) + symbol.value;
156    }
157    return symbol.value;
158}
159
160pub fn outputSymtabIndex(symbol: Symbol, elf_file: *Elf) ?u32 {
161    if (!symbol.flags.output_symtab) return null;
162    const file_ptr = symbol.file(elf_file).?;
163    const symtab_ctx = switch (file_ptr) {
164        inline else => |x| x.output_symtab_ctx,
165    };
166    const idx = symbol.extra(elf_file).symtab;
167    return if (symbol.isLocal(elf_file)) idx + symtab_ctx.ilocal else idx + symtab_ctx.iglobal;
168}
169
170pub fn gotAddress(symbol: Symbol, elf_file: *Elf) i64 {
171    if (!symbol.flags.has_got) return 0;
172    const extras = symbol.extra(elf_file);
173    const entry = elf_file.got.entries.items[extras.got];
174    return entry.address(elf_file);
175}
176
177pub fn pltGotAddress(symbol: Symbol, elf_file: *Elf) i64 {
178    if (!symbol.flags.has_pltgot) return 0;
179    const extras = symbol.extra(elf_file);
180    const shdr = elf_file.sections.items(.shdr)[elf_file.section_indexes.plt_got.?];
181    const cpu_arch = elf_file.getTarget().cpu.arch;
182    return @intCast(shdr.sh_addr + extras.plt_got * PltGotSection.entrySize(cpu_arch));
183}
184
185pub fn pltAddress(symbol: Symbol, elf_file: *Elf) i64 {
186    if (!symbol.flags.has_plt) return 0;
187    const extras = symbol.extra(elf_file);
188    const shdr = elf_file.sections.items(.shdr)[elf_file.section_indexes.plt.?];
189    const cpu_arch = elf_file.getTarget().cpu.arch;
190    return @intCast(shdr.sh_addr + extras.plt * PltSection.entrySize(cpu_arch) + PltSection.preambleSize(cpu_arch));
191}
192
193pub fn gotPltAddress(symbol: Symbol, elf_file: *Elf) i64 {
194    if (!symbol.flags.has_plt) return 0;
195    const extras = symbol.extra(elf_file);
196    const shdr = elf_file.sections.items(.shdr)[elf_file.section_indexes.got_plt.?];
197    return @intCast(shdr.sh_addr + extras.plt * 8 + GotPltSection.preamble_size);
198}
199
200pub fn copyRelAddress(symbol: Symbol, elf_file: *Elf) i64 {
201    if (!symbol.flags.has_copy_rel) return 0;
202    const shdr = elf_file.sections.items(.shdr)[elf_file.section_indexes.copy_rel.?];
203    return @as(i64, @intCast(shdr.sh_addr)) + symbol.value;
204}
205
206pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) i64 {
207    if (!symbol.flags.has_tlsgd) return 0;
208    const extras = symbol.extra(elf_file);
209    const entry = elf_file.got.entries.items[extras.tlsgd];
210    return entry.address(elf_file);
211}
212
213pub fn gotTpAddress(symbol: Symbol, elf_file: *Elf) i64 {
214    if (!symbol.flags.has_gottp) return 0;
215    const extras = symbol.extra(elf_file);
216    const entry = elf_file.got.entries.items[extras.gottp];
217    return entry.address(elf_file);
218}
219
220pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) i64 {
221    if (!symbol.flags.has_tlsdesc) return 0;
222    const extras = symbol.extra(elf_file);
223    const entry = elf_file.got.entries.items[extras.tlsdesc];
224    return entry.address(elf_file);
225}
226
227pub fn trampolineAddress(symbol: Symbol, elf_file: *Elf) i64 {
228    if (!symbol.flags.has_trampoline) return 0;
229    const zo = elf_file.zigObjectPtr().?;
230    const index = symbol.extra(elf_file).trampoline;
231    return zo.symbol(index).address(.{}, elf_file);
232}
233
234pub fn dsoAlignment(symbol: Symbol, elf_file: *Elf) !u64 {
235    const file_ptr = symbol.file(elf_file) orelse return 0;
236    assert(file_ptr == .shared_object);
237    const shared_object = file_ptr.shared_object;
238    const esym = symbol.elfSym(elf_file);
239    const shdr = shared_object.parsed.sections[esym.st_shndx];
240    const alignment = @max(1, shdr.sh_addralign);
241    return if (esym.st_value == 0)
242        alignment
243    else
244        @min(alignment, try std.math.powi(u64, 2, @ctz(esym.st_value)));
245}
246
247const AddExtraOpts = struct {
248    got: ?u32 = null,
249    plt: ?u32 = null,
250    plt_got: ?u32 = null,
251    dynamic: ?u32 = null,
252    symtab: ?u32 = null,
253    copy_rel: ?u32 = null,
254    tlsgd: ?u32 = null,
255    gottp: ?u32 = null,
256    tlsdesc: ?u32 = null,
257    trampoline: ?u32 = null,
258};
259
260pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) void {
261    var extras = symbol.extra(elf_file);
262    inline for (@typeInfo(@TypeOf(opts)).@"struct".fields) |field| {
263        if (@field(opts, field.name)) |x| {
264            @field(extras, field.name) = x;
265        }
266    }
267    symbol.setExtra(extras, elf_file);
268}
269
270pub fn extra(symbol: Symbol, elf_file: *Elf) Extra {
271    return switch (symbol.file(elf_file).?) {
272        inline else => |x| x.symbolExtra(symbol.extra_index),
273    };
274}
275
276pub fn setExtra(symbol: Symbol, extras: Extra, elf_file: *Elf) void {
277    return switch (symbol.file(elf_file).?) {
278        inline else => |x| x.setSymbolExtra(symbol.extra_index, extras),
279    };
280}
281
282pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
283    const file_ptr = symbol.file(elf_file).?;
284    const esym = symbol.elfSym(elf_file);
285    const st_type = symbol.type(elf_file);
286    const st_bind: u8 = blk: {
287        if (symbol.isLocal(elf_file)) break :blk 0;
288        if (symbol.flags.weak) break :blk elf.STB_WEAK;
289        if (file_ptr == .shared_object) break :blk elf.STB_GLOBAL;
290        break :blk esym.st_bind();
291    };
292    const st_shndx: u16 = blk: {
293        if (symbol.flags.has_copy_rel) break :blk @intCast(elf_file.section_indexes.copy_rel.?);
294        if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF;
295        if (elf_file.base.isRelocatable() and esym.st_shndx == elf.SHN_COMMON) break :blk elf.SHN_COMMON;
296        if (symbol.mergeSubsection(elf_file)) |msub| break :blk @intCast(msub.mergeSection(elf_file).output_section_index);
297        if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) break :blk elf.SHN_ABS;
298        break :blk @intCast(symbol.outputShndx(elf_file) orelse elf.SHN_UNDEF);
299    };
300    const st_value = blk: {
301        if (symbol.flags.has_copy_rel) break :blk symbol.address(.{}, elf_file);
302        if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) {
303            if (symbol.flags.is_canonical) break :blk symbol.address(.{}, elf_file);
304            break :blk 0;
305        }
306        if (st_shndx == elf.SHN_ABS or st_shndx == elf.SHN_COMMON) break :blk symbol.address(.{ .plt = false }, elf_file);
307        const shdr = elf_file.sections.items(.shdr)[st_shndx];
308        if (shdr.sh_flags & elf.SHF_TLS != 0 and file_ptr != .linker_defined)
309            break :blk symbol.address(.{ .plt = false }, elf_file) - elf_file.tlsAddress();
310        break :blk symbol.address(.{ .plt = false, .trampoline = false }, elf_file);
311    };
312    out.st_info = (st_bind << 4) | st_type;
313    out.st_other = esym.st_other;
314    out.st_shndx = st_shndx;
315    out.st_value = @intCast(st_value);
316    out.st_size = esym.st_size;
317}
318
319const Format = struct {
320    symbol: Symbol,
321    elf_file: *Elf,
322
323    fn name(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
324        const elf_file = f.elf_file;
325        const symbol = f.symbol;
326        try writer.writeAll(symbol.name(elf_file));
327        switch (symbol.version_index.VERSION) {
328            @intFromEnum(elf.VER_NDX.LOCAL), @intFromEnum(elf.VER_NDX.GLOBAL) => {},
329            else => {
330                const file_ptr = symbol.file(elf_file).?;
331                assert(file_ptr == .shared_object);
332                const shared_object = file_ptr.shared_object;
333                try writer.print("@{s}", .{shared_object.versionString(symbol.version_index)});
334            },
335        }
336    }
337
338    fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
339        const symbol = f.symbol;
340        const elf_file = f.elf_file;
341        try writer.print("%{d} : {f} : @{x}", .{
342            symbol.esym_index,
343            symbol.fmtName(elf_file),
344            symbol.address(.{ .plt = false, .trampoline = false }, elf_file),
345        });
346        if (symbol.file(elf_file)) |file_ptr| {
347            if (symbol.isAbs(elf_file)) {
348                if (symbol.elfSym(elf_file).st_shndx == elf.SHN_UNDEF) {
349                    try writer.writeAll(" : undef");
350                } else {
351                    try writer.writeAll(" : absolute");
352                }
353            } else if (symbol.outputShndx(elf_file)) |shndx| {
354                try writer.print(" : shdr({d})", .{shndx});
355            }
356            if (symbol.atom(elf_file)) |atom_ptr| {
357                try writer.print(" : atom({d})", .{atom_ptr.atom_index});
358            }
359            var buf: [2]u8 = .{'_'} ** 2;
360            if (symbol.flags.@"export") buf[0] = 'E';
361            if (symbol.flags.import) buf[1] = 'I';
362            try writer.print(" : {s}", .{&buf});
363            if (symbol.flags.weak) try writer.writeAll(" : weak");
364            switch (file_ptr) {
365                inline else => |x| try writer.print(" : {s}({d})", .{ @tagName(file_ptr), x.index }),
366            }
367        } else try writer.writeAll(" : unresolved");
368    }
369};
370
371pub fn fmtName(symbol: Symbol, elf_file: *Elf) std.fmt.Alt(Format, Format.name) {
372    return .{ .data = .{
373        .symbol = symbol,
374        .elf_file = elf_file,
375    } };
376}
377
378pub fn fmt(symbol: Symbol, elf_file: *Elf) std.fmt.Alt(Format, Format.default) {
379    return .{ .data = .{
380        .symbol = symbol,
381        .elf_file = elf_file,
382    } };
383}
384
385pub const Flags = packed struct {
386    /// Whether the symbol is imported at runtime.
387    import: bool = false,
388
389    /// Whether the symbol is exported at runtime.
390    @"export": bool = false,
391
392    /// Whether this symbol is weak.
393    weak: bool = false,
394
395    /// Whether the symbol makes into the output symtab.
396    output_symtab: bool = false,
397
398    /// Whether the symbol has entry in dynamic symbol table.
399    has_dynamic: bool = false,
400
401    /// Whether the symbol contains GOT indirection.
402    needs_got: bool = false,
403    has_got: bool = false,
404
405    /// Whether the symbol contains PLT indirection.
406    needs_plt: bool = false,
407    has_plt: bool = false,
408    /// Whether the PLT entry is canonical.
409    is_canonical: bool = false,
410    /// Whether the PLT entry is indirected via GOT.
411    has_pltgot: bool = false,
412
413    /// Whether the symbol contains COPYREL directive.
414    needs_copy_rel: bool = false,
415    has_copy_rel: bool = false,
416
417    /// Whether the symbol contains TLSGD indirection.
418    needs_tlsgd: bool = false,
419    has_tlsgd: bool = false,
420
421    /// Whether the symbol contains GOTTP indirection.
422    needs_gottp: bool = false,
423    has_gottp: bool = false,
424
425    /// Whether the symbol contains TLSDESC indirection.
426    needs_tlsdesc: bool = false,
427    has_tlsdesc: bool = false,
428
429    /// Whether the symbol is a merge subsection.
430    merge_subsection: bool = false,
431
432    /// ZigObject specific flags
433    /// Whether the symbol has a trampoline.
434    has_trampoline: bool = false,
435
436    /// Whether the symbol is a TLS variable.
437    is_tls: bool = false,
438};
439
440pub const Extra = struct {
441    got: u32 = 0,
442    plt: u32 = 0,
443    plt_got: u32 = 0,
444    dynamic: u32 = 0,
445    symtab: u32 = 0,
446    copy_rel: u32 = 0,
447    tlsgd: u32 = 0,
448    gottp: u32 = 0,
449    tlsdesc: u32 = 0,
450    trampoline: u32 = 0,
451};
452
453pub const Index = u32;
454
455const assert = std.debug.assert;
456const elf = std.elf;
457const mem = std.mem;
458const std = @import("std");
459const synthetic_sections = @import("synthetic_sections.zig");
460
461const Atom = @import("Atom.zig");
462const Elf = @import("../Elf.zig");
463const File = @import("file.zig").File;
464const GotSection = synthetic_sections.GotSection;
465const GotPltSection = synthetic_sections.GotPltSection;
466const LinkerDefined = @import("LinkerDefined.zig");
467const Merge = @import("Merge.zig");
468const Object = @import("Object.zig");
469const PltSection = synthetic_sections.PltSection;
470const PltGotSection = synthetic_sections.PltGotSection;
471const SharedObject = @import("SharedObject.zig");
472const Symbol = @This();
473const ZigGotSection = synthetic_sections.ZigGotSection;