authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-04 17:48:07-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-04 17:59:44-07:00
log1d777e99588be047ec4d7650f048b043d6e52da0
treec6155fb3f8b3d9f6218942bbce293bab355d2bf8
parentda596b7e4febc95ec5249c9f489166944fbe69b9

add --image-base support

Based on #6121 by Jay Petacat.

6 files changed, 64 insertions(+), 19 deletions(-)

lib/std/build.zig+8
...@@ -1232,6 +1232,9 @@ pub const LibExeObjStep = struct {...@@ -1232,6 +1232,9 @@ pub const LibExeObjStep = struct {
1232 installed_path: ?[]const u8,1232 installed_path: ?[]const u8,
1233 install_step: ?*InstallArtifactStep,1233 install_step: ?*InstallArtifactStep,
12341234
1235 /// Base address for an executable image.
1236 image_base: ?u64 = null,
1237
1235 libc_file: ?[]const u8 = null,1238 libc_file: ?[]const u8 = null,
12361239
1237 valgrind_support: ?bool = null,1240 valgrind_support: ?bool = null,
...@@ -2041,6 +2044,11 @@ pub const LibExeObjStep = struct {...@@ -2041,6 +2044,11 @@ pub const LibExeObjStep = struct {
2041 try zig_args.append("--pkg-end");2044 try zig_args.append("--pkg-end");
2042 }2045 }
20432046
2047 if (self.image_base) |image_base| {
2048 try zig_args.append("--image-base");
2049 try zig_args.append(image_base);
2050 }
2051
2044 if (self.filter) |filter| {2052 if (self.filter) |filter| {
2045 try zig_args.append("--test-filter");2053 try zig_args.append("--test-filter");
2046 try zig_args.append(filter);2054 try zig_args.append(filter);
src/Compilation.zig+3
...@@ -378,6 +378,7 @@ pub const InitOptions = struct {...@@ -378,6 +378,7 @@ pub const InitOptions = struct {
378 is_compiler_rt_or_libc: bool = false,378 is_compiler_rt_or_libc: bool = false,
379 parent_compilation_link_libc: bool = false,379 parent_compilation_link_libc: bool = false,
380 stack_size_override: ?u64 = null,380 stack_size_override: ?u64 = null,
381 image_base_override: ?u64 = null,
381 self_exe_path: ?[]const u8 = null,382 self_exe_path: ?[]const u8 = null,
382 version: ?std.builtin.Version = null,383 version: ?std.builtin.Version = null,
383 libc_installation: ?*const LibCInstallation = null,384 libc_installation: ?*const LibCInstallation = null,
...@@ -452,6 +453,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -452,6 +453,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
452 options.link_emit_relocs or453 options.link_emit_relocs or
453 options.output_mode == .Lib or454 options.output_mode == .Lib or
454 options.lld_argv.len != 0 or455 options.lld_argv.len != 0 or
456 options.image_base_override != null or
455 options.linker_script != null or options.version_script != null)457 options.linker_script != null or options.version_script != null)
456 {458 {
457 break :blk true;459 break :blk true;
...@@ -772,6 +774,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -772,6 +774,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
772 .z_nodelete = options.linker_z_nodelete,774 .z_nodelete = options.linker_z_nodelete,
773 .z_defs = options.linker_z_defs,775 .z_defs = options.linker_z_defs,
774 .stack_size_override = options.stack_size_override,776 .stack_size_override = options.stack_size_override,
777 .image_base_override = options.image_base_override,
775 .linker_script = options.linker_script,778 .linker_script = options.linker_script,
776 .version_script = options.version_script,779 .version_script = options.version_script,
777 .gc_sections = options.linker_gc_sections,780 .gc_sections = options.linker_gc_sections,
src/link.zig+1
...@@ -45,6 +45,7 @@ pub const Options = struct {...@@ -45,6 +45,7 @@ pub const Options = struct {
45 program_code_size_hint: u64 = 256 * 1024,45 program_code_size_hint: u64 = 256 * 1024,
46 entry_addr: ?u64 = null,46 entry_addr: ?u64 = null,
47 stack_size_override: ?u64,47 stack_size_override: ?u64,
48 image_base_override: ?u64,
48 /// Set to `true` to omit debug info.49 /// Set to `true` to omit debug info.
49 strip: bool,50 strip: bool,
50 /// If this is true then this link code is responsible for outputting an object51 /// If this is true then this link code is responsible for outputting an object
src/link/Coff.zig+17-13
...@@ -22,10 +22,10 @@ const minimum_text_block_size = 64 * allocation_padding;...@@ -22,10 +22,10 @@ const minimum_text_block_size = 64 * allocation_padding;
2222
23const section_alignment = 4096;23const section_alignment = 4096;
24const file_alignment = 512;24const file_alignment = 512;
25const image_base = 0x400_000;25const default_image_base = 0x400_000;
26const section_table_size = 2 * 40;26const section_table_size = 2 * 40;
27comptime {27comptime {
28 assert(mem.isAligned(image_base, section_alignment));28 assert(mem.isAligned(default_image_base, section_alignment));
29}29}
3030
31pub const base_tag: link.File.Tag = .coff;31pub const base_tag: link.File.Tag = .coff;
...@@ -55,7 +55,7 @@ offset_table: std.ArrayListUnmanaged(u64) = .{},...@@ -55,7 +55,7 @@ offset_table: std.ArrayListUnmanaged(u64) = .{},
55/// Free list of offset table indices55/// Free list of offset table indices
56offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},56offset_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.
59entry_addr: ?u32 = null,59entry_addr: ?u32 = null,
6060
61/// Absolute virtual address of the text section when the executable is loaded in memory.61/// 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...@@ -183,14 +183,14 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
183183
184 self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);184 self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
185 const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);185 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;
187 self.offset_table_size = default_offset_table_size;187 self.offset_table_size = default_offset_table_size;
188 self.section_table_offset = section_table_offset;188 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;
190 self.text_section_size = default_size_of_code;190 self.text_section_size = default_size_of_code;
191191
192 // Size of file when loaded in memory192 // 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
195 mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);195 mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
196 index += 2;196 index += 2;
...@@ -234,11 +234,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -234,11 +234,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
234 index += 4;234 index += 4;
235235
236 // Image base address236 // 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);
238 index += 4;238 index += 4;
239 } else {239 } else {
240 // Image base address240 // 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);
242 index += 8;242 index += 8;
243 }243 }
244244
...@@ -328,7 +328,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -328,7 +328,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
328 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);328 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
329 index += 4;329 index += 4;
330 // Virtual address (u32)330 // 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);
332 index += 4;332 index += 4;
333 } else {333 } else {
334 mem.set(u8, hdr_data[index..][0..8], 0);334 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...@@ -354,7 +354,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
354 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);354 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
355 index += 4;355 index += 4;
356 // Virtual address (u32)356 // 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);
358 index += 4;358 index += 4;
359 } else {359 } else {
360 mem.set(u8, hdr_data[index..][0..8], 0);360 mem.set(u8, hdr_data[index..][0..8], 0);
...@@ -601,7 +601,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {...@@ -601,7 +601,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
601601
602 // Write .text new virtual address602 // Write .text new virtual address
603 self.text_section_virtual_address = self.text_section_virtual_address + va_offset;603 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);
605 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);605 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
606606
607 // Fix the VAs in the offset table607 // Fix the VAs in the offset table
...@@ -716,7 +716,7 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl,...@@ -716,7 +716,7 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl,
716 }716 }
717 }717 }
718 if (mem.eql(u8, exp.options.name, "_start")) {718 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;
720 } else {720 } else {
721 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);721 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
722 module.failed_exports.putAssumeCapacityNoClobber(722 module.failed_exports.putAssumeCapacityNoClobber(
...@@ -754,7 +754,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {...@@ -754,7 +754,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
754 }754 }
755755
756 if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {756 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);
758 var buf: [4]u8 = undefined;758 var buf: [4]u8 = undefined;
759 mem.writeIntLittle(u32, &buf, new_size_of_image);759 mem.writeIntLittle(u32, &buf, new_size_of_image);
760 try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);760 try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
...@@ -832,6 +832,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -832,6 +832,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
832 }832 }
833 try man.addOptionalFile(module_obj_path);833 try man.addOptionalFile(module_obj_path);
834 man.hash.addOptional(self.base.options.stack_size_override);834 man.hash.addOptional(self.base.options.stack_size_override);
835 man.hash.addOptional(self.base.options.image_base_override);
835 man.hash.addListOfBytes(self.base.options.extra_lld_args);836 man.hash.addListOfBytes(self.base.options.extra_lld_args);
836 man.hash.addListOfBytes(self.base.options.lib_dirs);837 man.hash.addListOfBytes(self.base.options.lib_dirs);
837 man.hash.add(self.base.options.is_compiler_rt_or_libc);838 man.hash.add(self.base.options.is_compiler_rt_or_libc);
...@@ -914,6 +915,9 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -914,6 +915,9 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
914 const stack_size = self.base.options.stack_size_override orelse 16777216;915 const stack_size = self.base.options.stack_size_override orelse 16777216;
915 try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));916 try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
916 }917 }
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
918 if (target.cpu.arch == .i386) {922 if (target.cpu.arch == .i386) {
919 try argv.append("-MACHINE:X86");923 try argv.append("-MACHINE:X86");
src/link/Elf.zig+5
...@@ -1284,6 +1284,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1284,6 +1284,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1284 // We can skip hashing libc and libc++ components that we are in charge of building from Zig1284 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
1285 // installation sources because they are always a product of the compiler version + target information.1285 // installation sources because they are always a product of the compiler version + target information.
1286 man.hash.add(stack_size);1286 man.hash.add(stack_size);
1287 man.hash.addOptional(self.base.options.image_base_override);
1287 man.hash.add(gc_sections);1288 man.hash.add(gc_sections);
1288 man.hash.add(self.base.options.eh_frame_hdr);1289 man.hash.add(self.base.options.eh_frame_hdr);
1289 man.hash.add(self.base.options.emit_relocs);1290 man.hash.add(self.base.options.emit_relocs);
...@@ -1353,6 +1354,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1353,6 +1354,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1353 try argv.append(try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size}));1354 try argv.append(try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size}));
1354 }1355 }
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
1356 if (self.base.options.linker_script) |linker_script| {1361 if (self.base.options.linker_script) |linker_script| {
1357 try argv.append("-T");1362 try argv.append("-T");
1358 try argv.append(linker_script);1363 try argv.append(linker_script);
src/main.zig+30-6
...@@ -279,6 +279,7 @@ const usage_build_generic =...@@ -279,6 +279,7 @@ const usage_build_generic =
279 \\ -Bsymbolic Bind global references locally279 \\ -Bsymbolic Bind global references locally
280 \\ --subsystem [subsystem] (windows) /SUBSYSTEM:<subsystem> to the linker\n"280 \\ --subsystem [subsystem] (windows) /SUBSYSTEM:<subsystem> to the linker\n"
281 \\ --stack [size] Override default stack size281 \\ --stack [size] Override default stack size
282 \\ --image-base [addr] Set base address for executable image
282 \\ -framework [name] (darwin) link against framework283 \\ -framework [name] (darwin) link against framework
283 \\ -F[dir] (darwin) add search path for frameworks284 \\ -F[dir] (darwin) add search path for frameworks
284 \\285 \\
...@@ -435,6 +436,7 @@ fn buildOutputType(...@@ -435,6 +436,7 @@ fn buildOutputType(
435 var linker_z_defs = false;436 var linker_z_defs = false;
436 var test_evented_io = false;437 var test_evented_io = false;
437 var stack_size_override: ?u64 = null;438 var stack_size_override: ?u64 = null;
439 var image_base_override: ?u64 = null;
438 var use_llvm: ?bool = null;440 var use_llvm: ?bool = null;
439 var use_lld: ?bool = null;441 var use_lld: ?bool = null;
440 var use_clang: ?bool = null;442 var use_clang: ?bool = null;
...@@ -628,9 +630,11 @@ fn buildOutputType(...@@ -628,9 +630,11 @@ fn buildOutputType(
628 } else if (mem.eql(u8, arg, "--stack")) {630 } else if (mem.eql(u8, arg, "--stack")) {
629 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});631 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
630 i += 1;632 i += 1;
631 stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| {633 stack_size_override = parseAnyBaseInt(args[i]);
632 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });634 } else if (mem.eql(u8, arg, "--image-base")) {
633 };635 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
636 i += 1;
637 image_base_override = parseAnyBaseInt(args[i]);
634 } else if (mem.eql(u8, arg, "--name")) {638 } else if (mem.eql(u8, arg, "--name")) {
635 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});639 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
636 i += 1;640 i += 1;
...@@ -1147,9 +1151,13 @@ fn buildOutputType(...@@ -1147,9 +1151,13 @@ fn buildOutputType(
1147 if (i >= linker_args.items.len) {1151 if (i >= linker_args.items.len) {
1148 fatal("expected linker arg after '{}'", .{arg});1152 fatal("expected linker arg after '{}'", .{arg});
1149 }1153 }
1150 stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| {1154 stack_size_override = parseAnyBaseInt(linker_args.items[i]);
1151 fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });1155 } else if (mem.eql(u8, arg, "--image-base")) {
1152 };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]);
1153 } else {1161 } else {
1154 warn("unsupported linker arg: {}", .{arg});1162 warn("unsupported linker arg: {}", .{arg});
1155 }1163 }
...@@ -1595,6 +1603,7 @@ fn buildOutputType(...@@ -1595,6 +1603,7 @@ fn buildOutputType(
1595 .link_eh_frame_hdr = link_eh_frame_hdr,1603 .link_eh_frame_hdr = link_eh_frame_hdr,
1596 .link_emit_relocs = link_emit_relocs,1604 .link_emit_relocs = link_emit_relocs,
1597 .stack_size_override = stack_size_override,1605 .stack_size_override = stack_size_override,
1606 .image_base_override = image_base_override,
1598 .strip = strip,1607 .strip = strip,
1599 .single_threaded = single_threaded,1608 .single_threaded = single_threaded,
1600 .function_sections = function_sections,1609 .function_sections = function_sections,
...@@ -3051,3 +3060,18 @@ pub fn cleanExit() void {...@@ -3051,3 +3060,18 @@ pub fn cleanExit() void {
3051 process.exit(0);3060 process.exit(0);
3052 }3061 }
3053}3062}
3063
3064fn 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}