Skip to content

Commit

Permalink
cvtres: Add support for defining an external symbol (/DEFINE:symbol o…
Browse files Browse the repository at this point in the history
…ption of cvtres.exe)
  • Loading branch information
squeek502 committed Nov 5, 2024
1 parent 472ef48 commit 8767cc4
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 2 deletions.
41 changes: 39 additions & 2 deletions src/cvtres.zig
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ pub const CoffOptions = struct {
reproducible: bool = true,
/// If true, the MEM_WRITE flag will not be set in the .rsrc section header
read_only: bool = false,
/// If non-null, a symbol with this name and storage class EXTERNAL will be added to the symbol table.
define_external_symbol: ?[]const u8 = null,
};

pub fn writeCoff(allocator: Allocator, writer: anytype, resources: []const Resource, options: CoffOptions) !void {
Expand All @@ -186,12 +188,13 @@ pub fn writeCoff(allocator: Allocator, writer: anytype, resources: []const Resou
const flags = std.coff.CoffHeaderFlags{
.@"32BIT_MACHINE" = 1,
};
const number_of_symbols = 5 + @as(u32, @intCast(resources.len)) + @intFromBool(options.define_external_symbol != null);
const coff_header = std.coff.CoffHeader{
.machine = machine_type,
.number_of_sections = 2,
.time_date_stamp = @as(u32, @truncate(@as(u64, @bitCast(timestamp)))),
.pointer_to_symbol_table = pointer_to_symbol_table,
.number_of_symbols = 5 + @as(u32, @intCast(resources.len)),
.number_of_symbols = number_of_symbols,
.size_of_optional_header = size_of_optional_header,
.flags = flags,
};
Expand Down Expand Up @@ -304,8 +307,42 @@ pub fn writeCoff(allocator: Allocator, writer: anytype, resources: []const Resou
try writeSymbol(writer, resource_symbol);
}

const string_table_byte_count = 4;
var string_table: std.ArrayListUnmanaged(u8) = .empty;
defer string_table.deinit(allocator);

if (options.define_external_symbol) |external_symbol_name| {
const name_bytes: [8]u8 = name_bytes: {
if (external_symbol_name.len > 8) {
// 4 due to the initial string table byte length field
const string_table_offset: u32 = @intCast(4 + string_table.items.len);
try string_table.appendSlice(allocator, external_symbol_name);
try string_table.append(allocator, 0);
var bytes = [_]u8{0} ** 8;
std.mem.writeInt(u32, bytes[4..8], string_table_offset, .little);
break :name_bytes bytes;
} else {
var symbol_shortname = [_]u8{0} ** 8;
@memcpy(symbol_shortname[0..external_symbol_name.len], external_symbol_name);
break :name_bytes symbol_shortname;
}
};

try writeSymbol(writer, .{
.name = name_bytes,
.value = 0,
.section_number = .ABSOLUTE,
.type = .{
.base_type = .NULL,
.complex_type = .NULL,
},
.storage_class = .EXTERNAL,
.number_of_aux_symbols = 0,
});
}

const string_table_byte_count: u32 = @intCast(4 + string_table.items.len);
try writer.writeInt(u32, string_table_byte_count, .little);
try writer.writeAll(string_table.items);
}

fn writeSymbol(writer: anytype, symbol: std.coff.Symbol) !void {
Expand Down
8 changes: 8 additions & 0 deletions test/fuzzy_cvtres.zig
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,19 @@ test "cvtres fuzz" {
else => unreachable,
};

const random_symbol_define: ?[]const u8 = switch (rand.uintLessThan(u8, 3)) {
0 => null,
1 => "short", // fits within 8 bytes
2 => "longerthan8", // needs to go in the string table
else => unreachable,
};

try utils.expectSameCvtResOutput(allocator, res_buffer.items, .{
.cwd = tmp.dir,
.cwd_path = tmp_path,
.target = random_target,
.read_only = rand.boolean(),
.define_external_symbol = random_symbol_define,
});
}
}
12 changes: 12 additions & 0 deletions test/utils.zig
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ pub const GetCvtResResultOptions = struct {
output_path: ?[]const u8 = null,
target: std.coff.MachineType = .X64,
read_only: bool = false,
define_external_symbol: ?[]const u8 = null,
};

pub const ResinatorCvtResResult = struct {
Expand Down Expand Up @@ -298,6 +299,7 @@ pub fn getResinatorCvtResResult(allocator: Allocator, res_source: []const u8, op
resinator.cvtres.writeCoff(allocator, buf.writer(), resources, .{
.target = options.target,
.read_only = options.read_only,
.define_external_symbol = options.define_external_symbol,
}) catch |err| {
buf.deinit();
return .{
Expand Down Expand Up @@ -354,6 +356,16 @@ pub fn getWin32CvtResResultFromFile(allocator: Allocator, input_path: []const u8
if (options.read_only) {
try argv.append("/READONLY");
}
const define_arg: ?[]const u8 = define_arg: {
if (options.define_external_symbol) |symbol_name| {
const define_arg = try std.fmt.allocPrint(allocator, "/DEFINE:{s}", .{symbol_name});
errdefer allocator.free(define_arg);
try argv.append(define_arg);
break :define_arg define_arg;
}
break :define_arg null;
};
defer if (define_arg) |v| allocator.free(v);

try argv.append(input_path);

Expand Down

0 comments on commit 8767cc4

Please sign in to comment.