Commit 1d777e9958

Andrew Kelley <andrew@ziglang.org>
2020-10-05 02:48:07
add --image-base support
Based on #6121 by Jay Petacat.
1 parent da596b7
lib/std/build.zig
@@ -1232,6 +1232,9 @@ pub const LibExeObjStep = struct {
     installed_path: ?[]const u8,
     install_step: ?*InstallArtifactStep,
 
+    /// Base address for an executable image.
+    image_base: ?u64 = null,
+
     libc_file: ?[]const u8 = null,
 
     valgrind_support: ?bool = null,
@@ -2041,6 +2044,11 @@ pub const LibExeObjStep = struct {
             try zig_args.append("--pkg-end");
         }
 
+        if (self.image_base) |image_base| {
+            try zig_args.append("--image-base");
+            try zig_args.append(image_base);
+        }
+
         if (self.filter) |filter| {
             try zig_args.append("--test-filter");
             try zig_args.append(filter);
src/link/Coff.zig
@@ -22,10 +22,10 @@ const minimum_text_block_size = 64 * allocation_padding;
 
 const section_alignment = 4096;
 const file_alignment = 512;
-const image_base = 0x400_000;
+const default_image_base = 0x400_000;
 const section_table_size = 2 * 40;
 comptime {
-    assert(mem.isAligned(image_base, section_alignment));
+    assert(mem.isAligned(default_image_base, section_alignment));
 }
 
 pub const base_tag: link.File.Tag = .coff;
@@ -55,7 +55,7 @@ offset_table: std.ArrayListUnmanaged(u64) = .{},
 /// Free list of offset table indices
 offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
 
-/// Virtual address of the entry point procedure relative to `image_base`
+/// Virtual address of the entry point procedure relative to image base.
 entry_addr: ?u32 = null,
 
 /// 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
 
     self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
     const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);
-    self.offset_table_virtual_address = image_base + section_data_relative_virtual_address;
+    self.offset_table_virtual_address = default_image_base + section_data_relative_virtual_address;
     self.offset_table_size = default_offset_table_size;
     self.section_table_offset = section_table_offset;
-    self.text_section_virtual_address = image_base + section_data_relative_virtual_address + section_alignment;
+    self.text_section_virtual_address = default_image_base + section_data_relative_virtual_address + section_alignment;
     self.text_section_size = default_size_of_code;
 
     // Size of file when loaded in memory
-    const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment);
+    const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + default_size_of_code, section_alignment);
 
     mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
     index += 2;
@@ -234,11 +234,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
             index += 4;
 
             // Image base address
-            mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base);
+            mem.writeIntLittle(u32, hdr_data[index..][0..4], default_image_base);
             index += 4;
         } else {
             // Image base address
-            mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base);
+            mem.writeIntLittle(u64, hdr_data[index..][0..8], default_image_base);
             index += 8;
         }
 
@@ -328,7 +328,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
         mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
         index += 4;
         // Virtual address (u32)
-        mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base);
+        mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - default_image_base);
         index += 4;
     } else {
         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
         mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
         index += 4;
         // Virtual address (u32)
-        mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base);
+        mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - default_image_base);
         index += 4;
     } else {
         mem.set(u8, hdr_data[index..][0..8], 0);
@@ -601,7 +601,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
 
             // Write .text new virtual address
             self.text_section_virtual_address = self.text_section_virtual_address + va_offset;
-            mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base);
+            mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - default_image_base);
             try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
 
             // Fix the VAs in the offset table
@@ -716,7 +716,7 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl,
             }
         }
         if (mem.eql(u8, exp.options.name, "_start")) {
-            self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base;
+            self.entry_addr = decl.link.coff.getVAddr(self.*) - default_image_base;
         } else {
             try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
             module.failed_exports.putAssumeCapacityNoClobber(
@@ -754,7 +754,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
     }
 
     if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {
-        const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment);
+        const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + self.text_section_size, section_alignment);
         var buf: [4]u8 = undefined;
         mem.writeIntLittle(u32, &buf, new_size_of_image);
         try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
@@ -832,6 +832,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
         }
         try man.addOptionalFile(module_obj_path);
         man.hash.addOptional(self.base.options.stack_size_override);
+        man.hash.addOptional(self.base.options.image_base_override);
         man.hash.addListOfBytes(self.base.options.extra_lld_args);
         man.hash.addListOfBytes(self.base.options.lib_dirs);
         man.hash.add(self.base.options.is_compiler_rt_or_libc);
@@ -914,6 +915,9 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
             const stack_size = self.base.options.stack_size_override orelse 16777216;
             try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
         }
+        if (self.base.options.image_base_override) |image_base| {
+            try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{image_base}));
+        }
 
         if (target.cpu.arch == .i386) {
             try argv.append("-MACHINE:X86");
src/link/Elf.zig
@@ -1284,6 +1284,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
         // We can skip hashing libc and libc++ components that we are in charge of building from Zig
         // installation sources because they are always a product of the compiler version + target information.
         man.hash.add(stack_size);
+        man.hash.addOptional(self.base.options.image_base_override);
         man.hash.add(gc_sections);
         man.hash.add(self.base.options.eh_frame_hdr);
         man.hash.add(self.base.options.emit_relocs);
@@ -1353,6 +1354,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
         try argv.append(try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size}));
     }
 
+    if (self.base.options.image_base_override) |image_base| {
+        try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base}));
+    }
+
     if (self.base.options.linker_script) |linker_script| {
         try argv.append("-T");
         try argv.append(linker_script);
src/Compilation.zig
@@ -378,6 +378,7 @@ pub const InitOptions = struct {
     is_compiler_rt_or_libc: bool = false,
     parent_compilation_link_libc: bool = false,
     stack_size_override: ?u64 = null,
+    image_base_override: ?u64 = null,
     self_exe_path: ?[]const u8 = null,
     version: ?std.builtin.Version = null,
     libc_installation: ?*const LibCInstallation = null,
@@ -452,6 +453,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
                 options.link_emit_relocs or
                 options.output_mode == .Lib or
                 options.lld_argv.len != 0 or
+                options.image_base_override != null or
                 options.linker_script != null or options.version_script != null)
             {
                 break :blk true;
@@ -772,6 +774,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
             .z_nodelete = options.linker_z_nodelete,
             .z_defs = options.linker_z_defs,
             .stack_size_override = options.stack_size_override,
+            .image_base_override = options.image_base_override,
             .linker_script = options.linker_script,
             .version_script = options.version_script,
             .gc_sections = options.linker_gc_sections,
src/link.zig
@@ -45,6 +45,7 @@ pub const Options = struct {
     program_code_size_hint: u64 = 256 * 1024,
     entry_addr: ?u64 = null,
     stack_size_override: ?u64,
+    image_base_override: ?u64,
     /// Set to `true` to omit debug info.
     strip: bool,
     /// If this is true then this link code is responsible for outputting an object
src/main.zig
@@ -279,6 +279,7 @@ const usage_build_generic =
     \\  -Bsymbolic                     Bind global references locally
     \\  --subsystem [subsystem]        (windows) /SUBSYSTEM:<subsystem> to the linker\n"
     \\  --stack [size]                 Override default stack size
+    \\  --image-base [addr]            Set base address for executable image
     \\  -framework [name]              (darwin) link against framework
     \\  -F[dir]                        (darwin) add search path for frameworks
     \\
@@ -435,6 +436,7 @@ fn buildOutputType(
     var linker_z_defs = false;
     var test_evented_io = false;
     var stack_size_override: ?u64 = null;
+    var image_base_override: ?u64 = null;
     var use_llvm: ?bool = null;
     var use_lld: ?bool = null;
     var use_clang: ?bool = null;
@@ -628,9 +630,11 @@ fn buildOutputType(
                     } else if (mem.eql(u8, arg, "--stack")) {
                         if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
                         i += 1;
-                        stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| {
-                            fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
-                        };
+                        stack_size_override = parseAnyBaseInt(args[i]);
+                    } else if (mem.eql(u8, arg, "--image-base")) {
+                        if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+                        i += 1;
+                        image_base_override = parseAnyBaseInt(args[i]);
                     } else if (mem.eql(u8, arg, "--name")) {
                         if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
                         i += 1;
@@ -1147,9 +1151,13 @@ fn buildOutputType(
                     if (i >= linker_args.items.len) {
                         fatal("expected linker arg after '{}'", .{arg});
                     }
-                    stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| {
-                        fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
-                    };
+                    stack_size_override = parseAnyBaseInt(linker_args.items[i]);
+                } else if (mem.eql(u8, arg, "--image-base")) {
+                    i += 1;
+                    if (i >= linker_args.items.len) {
+                        fatal("expected linker arg after '{}'", .{arg});
+                    }
+                    image_base_override = parseAnyBaseInt(linker_args.items[i]);
                 } else {
                     warn("unsupported linker arg: {}", .{arg});
                 }
@@ -1595,6 +1603,7 @@ fn buildOutputType(
         .link_eh_frame_hdr = link_eh_frame_hdr,
         .link_emit_relocs = link_emit_relocs,
         .stack_size_override = stack_size_override,
+        .image_base_override = image_base_override,
         .strip = strip,
         .single_threaded = single_threaded,
         .function_sections = function_sections,
@@ -3051,3 +3060,18 @@ pub fn cleanExit() void {
         process.exit(0);
     }
 }
+
+fn parseAnyBaseInt(prefixed_bytes: []const u8) u64 {
+    const base: u8 = if (mem.startsWith(u8, prefixed_bytes, "0x"))
+        16
+    else if (mem.startsWith(u8, prefixed_bytes, "0o"))
+        8
+    else if (mem.startsWith(u8, prefixed_bytes, "0b"))
+        2
+    else
+        @as(u8, 10);
+    const bytes = if (base == 10) prefixed_bytes else prefixed_bytes[2..];
+    return std.fmt.parseInt(u64, bytes, base) catch |err| {
+        fatal("unable to parse '{}': {}", .{ prefixed_bytes, @errorName(err) });
+    };
+}