Skip to content

Commit 1d777e9

Browse files
committed
add --image-base support
Based on #6121 by Jay Petacat.
1 parent da596b7 commit 1d777e9

File tree

6 files changed

+64
-19
lines changed

6 files changed

+64
-19
lines changed

lib/std/build.zig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,6 +1232,9 @@ pub const LibExeObjStep = struct {
12321232
installed_path: ?[]const u8,
12331233
install_step: ?*InstallArtifactStep,
12341234

1235+
/// Base address for an executable image.
1236+
image_base: ?u64 = null,
1237+
12351238
libc_file: ?[]const u8 = null,
12361239

12371240
valgrind_support: ?bool = null,
@@ -2041,6 +2044,11 @@ pub const LibExeObjStep = struct {
20412044
try zig_args.append("--pkg-end");
20422045
}
20432046

2047+
if (self.image_base) |image_base| {
2048+
try zig_args.append("--image-base");
2049+
try zig_args.append(image_base);
2050+
}
2051+
20442052
if (self.filter) |filter| {
20452053
try zig_args.append("--test-filter");
20462054
try zig_args.append(filter);

src/Compilation.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ pub const InitOptions = struct {
378378
is_compiler_rt_or_libc: bool = false,
379379
parent_compilation_link_libc: bool = false,
380380
stack_size_override: ?u64 = null,
381+
image_base_override: ?u64 = null,
381382
self_exe_path: ?[]const u8 = null,
382383
version: ?std.builtin.Version = null,
383384
libc_installation: ?*const LibCInstallation = null,
@@ -452,6 +453,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
452453
options.link_emit_relocs or
453454
options.output_mode == .Lib or
454455
options.lld_argv.len != 0 or
456+
options.image_base_override != null or
455457
options.linker_script != null or options.version_script != null)
456458
{
457459
break :blk true;
@@ -772,6 +774,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
772774
.z_nodelete = options.linker_z_nodelete,
773775
.z_defs = options.linker_z_defs,
774776
.stack_size_override = options.stack_size_override,
777+
.image_base_override = options.image_base_override,
775778
.linker_script = options.linker_script,
776779
.version_script = options.version_script,
777780
.gc_sections = options.linker_gc_sections,

src/link.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub const Options = struct {
4545
program_code_size_hint: u64 = 256 * 1024,
4646
entry_addr: ?u64 = null,
4747
stack_size_override: ?u64,
48+
image_base_override: ?u64,
4849
/// Set to `true` to omit debug info.
4950
strip: bool,
5051
/// If this is true then this link code is responsible for outputting an object

src/link/Coff.zig

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ const minimum_text_block_size = 64 * allocation_padding;
2222

2323
const section_alignment = 4096;
2424
const file_alignment = 512;
25-
const image_base = 0x400_000;
25+
const default_image_base = 0x400_000;
2626
const section_table_size = 2 * 40;
2727
comptime {
28-
assert(mem.isAligned(image_base, section_alignment));
28+
assert(mem.isAligned(default_image_base, section_alignment));
2929
}
3030

3131
pub const base_tag: link.File.Tag = .coff;
@@ -55,7 +55,7 @@ offset_table: std.ArrayListUnmanaged(u64) = .{},
5555
/// Free list of offset table indices
5656
offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
5757

58-
/// Virtual address of the entry point procedure relative to `image_base`
58+
/// Virtual address of the entry point procedure relative to image base.
5959
entry_addr: ?u32 = null,
6060

6161
/// Absolute virtual address of the text section when the executable is loaded in memory.
@@ -183,14 +183,14 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
183183

184184
self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
185185
const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);
186-
self.offset_table_virtual_address = image_base + section_data_relative_virtual_address;
186+
self.offset_table_virtual_address = default_image_base + section_data_relative_virtual_address;
187187
self.offset_table_size = default_offset_table_size;
188188
self.section_table_offset = section_table_offset;
189-
self.text_section_virtual_address = image_base + section_data_relative_virtual_address + section_alignment;
189+
self.text_section_virtual_address = default_image_base + section_data_relative_virtual_address + section_alignment;
190190
self.text_section_size = default_size_of_code;
191191

192192
// Size of file when loaded in memory
193-
const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment);
193+
const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + default_size_of_code, section_alignment);
194194

195195
mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
196196
index += 2;
@@ -234,11 +234,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
234234
index += 4;
235235

236236
// Image base address
237-
mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base);
237+
mem.writeIntLittle(u32, hdr_data[index..][0..4], default_image_base);
238238
index += 4;
239239
} else {
240240
// Image base address
241-
mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base);
241+
mem.writeIntLittle(u64, hdr_data[index..][0..8], default_image_base);
242242
index += 8;
243243
}
244244

@@ -328,7 +328,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
328328
mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
329329
index += 4;
330330
// Virtual address (u32)
331-
mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base);
331+
mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - default_image_base);
332332
index += 4;
333333
} else {
334334
mem.set(u8, hdr_data[index..][0..8], 0);
@@ -354,7 +354,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
354354
mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
355355
index += 4;
356356
// Virtual address (u32)
357-
mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base);
357+
mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - default_image_base);
358358
index += 4;
359359
} else {
360360
mem.set(u8, hdr_data[index..][0..8], 0);
@@ -601,7 +601,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
601601

602602
// Write .text new virtual address
603603
self.text_section_virtual_address = self.text_section_virtual_address + va_offset;
604-
mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base);
604+
mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - default_image_base);
605605
try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
606606

607607
// Fix the VAs in the offset table
@@ -716,7 +716,7 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl,
716716
}
717717
}
718718
if (mem.eql(u8, exp.options.name, "_start")) {
719-
self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base;
719+
self.entry_addr = decl.link.coff.getVAddr(self.*) - default_image_base;
720720
} else {
721721
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
722722
module.failed_exports.putAssumeCapacityNoClobber(
@@ -754,7 +754,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
754754
}
755755

756756
if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {
757-
const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment);
757+
const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + self.text_section_size, section_alignment);
758758
var buf: [4]u8 = undefined;
759759
mem.writeIntLittle(u32, &buf, new_size_of_image);
760760
try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
@@ -832,6 +832,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
832832
}
833833
try man.addOptionalFile(module_obj_path);
834834
man.hash.addOptional(self.base.options.stack_size_override);
835+
man.hash.addOptional(self.base.options.image_base_override);
835836
man.hash.addListOfBytes(self.base.options.extra_lld_args);
836837
man.hash.addListOfBytes(self.base.options.lib_dirs);
837838
man.hash.add(self.base.options.is_compiler_rt_or_libc);
@@ -914,6 +915,9 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
914915
const stack_size = self.base.options.stack_size_override orelse 16777216;
915916
try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
916917
}
918+
if (self.base.options.image_base_override) |image_base| {
919+
try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{image_base}));
920+
}
917921

918922
if (target.cpu.arch == .i386) {
919923
try argv.append("-MACHINE:X86");

src/link/Elf.zig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
12841284
// We can skip hashing libc and libc++ components that we are in charge of building from Zig
12851285
// installation sources because they are always a product of the compiler version + target information.
12861286
man.hash.add(stack_size);
1287+
man.hash.addOptional(self.base.options.image_base_override);
12871288
man.hash.add(gc_sections);
12881289
man.hash.add(self.base.options.eh_frame_hdr);
12891290
man.hash.add(self.base.options.emit_relocs);
@@ -1353,6 +1354,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13531354
try argv.append(try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size}));
13541355
}
13551356

1357+
if (self.base.options.image_base_override) |image_base| {
1358+
try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base}));
1359+
}
1360+
13561361
if (self.base.options.linker_script) |linker_script| {
13571362
try argv.append("-T");
13581363
try argv.append(linker_script);

src/main.zig

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ const usage_build_generic =
279279
\\ -Bsymbolic Bind global references locally
280280
\\ --subsystem [subsystem] (windows) /SUBSYSTEM:<subsystem> to the linker\n"
281281
\\ --stack [size] Override default stack size
282+
\\ --image-base [addr] Set base address for executable image
282283
\\ -framework [name] (darwin) link against framework
283284
\\ -F[dir] (darwin) add search path for frameworks
284285
\\
@@ -435,6 +436,7 @@ fn buildOutputType(
435436
var linker_z_defs = false;
436437
var test_evented_io = false;
437438
var stack_size_override: ?u64 = null;
439+
var image_base_override: ?u64 = null;
438440
var use_llvm: ?bool = null;
439441
var use_lld: ?bool = null;
440442
var use_clang: ?bool = null;
@@ -628,9 +630,11 @@ fn buildOutputType(
628630
} else if (mem.eql(u8, arg, "--stack")) {
629631
if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
630632
i += 1;
631-
stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| {
632-
fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
633-
};
633+
stack_size_override = parseAnyBaseInt(args[i]);
634+
} else if (mem.eql(u8, arg, "--image-base")) {
635+
if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
636+
i += 1;
637+
image_base_override = parseAnyBaseInt(args[i]);
634638
} else if (mem.eql(u8, arg, "--name")) {
635639
if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
636640
i += 1;
@@ -1147,9 +1151,13 @@ fn buildOutputType(
11471151
if (i >= linker_args.items.len) {
11481152
fatal("expected linker arg after '{}'", .{arg});
11491153
}
1150-
stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| {
1151-
fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
1152-
};
1154+
stack_size_override = parseAnyBaseInt(linker_args.items[i]);
1155+
} else if (mem.eql(u8, arg, "--image-base")) {
1156+
i += 1;
1157+
if (i >= linker_args.items.len) {
1158+
fatal("expected linker arg after '{}'", .{arg});
1159+
}
1160+
image_base_override = parseAnyBaseInt(linker_args.items[i]);
11531161
} else {
11541162
warn("unsupported linker arg: {}", .{arg});
11551163
}
@@ -1595,6 +1603,7 @@ fn buildOutputType(
15951603
.link_eh_frame_hdr = link_eh_frame_hdr,
15961604
.link_emit_relocs = link_emit_relocs,
15971605
.stack_size_override = stack_size_override,
1606+
.image_base_override = image_base_override,
15981607
.strip = strip,
15991608
.single_threaded = single_threaded,
16001609
.function_sections = function_sections,
@@ -3051,3 +3060,18 @@ pub fn cleanExit() void {
30513060
process.exit(0);
30523061
}
30533062
}
3063+
3064+
fn parseAnyBaseInt(prefixed_bytes: []const u8) u64 {
3065+
const base: u8 = if (mem.startsWith(u8, prefixed_bytes, "0x"))
3066+
16
3067+
else if (mem.startsWith(u8, prefixed_bytes, "0o"))
3068+
8
3069+
else if (mem.startsWith(u8, prefixed_bytes, "0b"))
3070+
2
3071+
else
3072+
@as(u8, 10);
3073+
const bytes = if (base == 10) prefixed_bytes else prefixed_bytes[2..];
3074+
return std.fmt.parseInt(u64, bytes, base) catch |err| {
3075+
fatal("unable to parse '{}': {}", .{ prefixed_bytes, @errorName(err) });
3076+
};
3077+
}

0 commit comments

Comments
 (0)