@@ -50,6 +50,7 @@ text_section_index: ?u16 = null,
50
50
got_section_index : ? u16 = null ,
51
51
rdata_section_index : ? u16 = null ,
52
52
data_section_index : ? u16 = null ,
53
+ reloc_section_index : ? u16 = null ,
53
54
54
55
locals : std .ArrayListUnmanaged (coff .Symbol ) = .{},
55
56
globals : std .StringArrayHashMapUnmanaged (SymbolWithLoc ) = .{},
@@ -98,11 +99,16 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
98
99
/// with `Decl` `main`, and lives as long as that `Decl`.
99
100
unnamed_const_atoms : UnnamedConstTable = .{},
100
101
101
- /// A table of relocations indexed by the owning them `TextBlock `.
102
- /// Note that once we refactor `TextBlock `'s lifetime and ownership rules,
102
+ /// A table of relocations indexed by the owning them `Atom `.
103
+ /// Note that once we refactor `Atom `'s lifetime and ownership rules,
103
104
/// this will be a table indexed by index into the list of Atoms.
104
105
relocs : RelocTable = .{},
105
106
107
+ /// A table of base relocations indexed by the owning them `Atom`.
108
+ /// Note that once we refactor `Atom`'s lifetime and ownership rules,
109
+ /// this will be a table indexed by index into the list of Atoms.
110
+ base_relocs : BaseRelocationTable = .{},
111
+
106
112
pub const Reloc = struct {
107
113
@"type" : enum {
108
114
got ,
@@ -117,6 +123,7 @@ pub const Reloc = struct {
117
123
};
118
124
119
125
const RelocTable = std .AutoHashMapUnmanaged (* Atom , std .ArrayListUnmanaged (Reloc ));
126
+ const BaseRelocationTable = std .AutoHashMapUnmanaged (* Atom , std .ArrayListUnmanaged (u32 ));
120
127
const UnnamedConstTable = std .AutoHashMapUnmanaged (Module .Decl .Index , std .ArrayListUnmanaged (* Atom ));
121
128
122
129
const default_file_alignment : u16 = 0x200 ;
@@ -150,7 +157,17 @@ const Section = struct {
150
157
free_list : std .ArrayListUnmanaged (* Atom ) = .{},
151
158
};
152
159
153
- pub const PtrWidth = enum { p32 , p64 };
160
+ pub const PtrWidth = enum {
161
+ p32 ,
162
+ p64 ,
163
+
164
+ fn abiSize (pw : PtrWidth ) u4 {
165
+ return switch (pw ) {
166
+ .p32 = > 4 ,
167
+ .p64 = > 8 ,
168
+ };
169
+ }
170
+ };
154
171
pub const SrcFn = void ;
155
172
156
173
pub const Export = struct {
@@ -274,6 +291,14 @@ pub fn deinit(self: *Coff) void {
274
291
}
275
292
self .relocs .deinit (gpa );
276
293
}
294
+
295
+ {
296
+ var it = self .base_relocs .valueIterator ();
297
+ while (it .next ()) | relocs | {
298
+ relocs .deinit (gpa );
299
+ }
300
+ self .base_relocs .deinit (gpa );
301
+ }
277
302
}
278
303
279
304
fn populateMissingMetadata (self : * Coff ) ! void {
@@ -307,7 +332,7 @@ fn populateMissingMetadata(self: *Coff) !void {
307
332
308
333
if (self .got_section_index == null ) {
309
334
self .got_section_index = @intCast (u16 , self .sections .slice ().len );
310
- const file_size = @intCast (u32 , self .base .options .symbol_count_hint );
335
+ const file_size = @intCast (u32 , self .base .options .symbol_count_hint ) * self . ptr_width . abiSize () ;
311
336
const off = self .findFreeSpace (file_size , self .page_size );
312
337
log .debug ("found .got free space 0x{x} to 0x{x}" , .{ off , off + file_size });
313
338
var header = coff.SectionHeader {
@@ -378,6 +403,31 @@ fn populateMissingMetadata(self: *Coff) !void {
378
403
try self .sections .append (gpa , .{ .header = header });
379
404
}
380
405
406
+ if (self .reloc_section_index == null ) {
407
+ self .reloc_section_index = @intCast (u16 , self .sections .slice ().len );
408
+ const file_size = @intCast (u32 , self .base .options .symbol_count_hint ) * @sizeOf (coff .BaseRelocation );
409
+ const off = self .findFreeSpace (file_size , self .page_size );
410
+ log .debug ("found .reloc free space 0x{x} to 0x{x}" , .{ off , off + file_size });
411
+ var header = coff.SectionHeader {
412
+ .name = undefined ,
413
+ .virtual_size = file_size ,
414
+ .virtual_address = off ,
415
+ .size_of_raw_data = file_size ,
416
+ .pointer_to_raw_data = off ,
417
+ .pointer_to_relocations = 0 ,
418
+ .pointer_to_linenumbers = 0 ,
419
+ .number_of_relocations = 0 ,
420
+ .number_of_linenumbers = 0 ,
421
+ .flags = .{
422
+ .CNT_INITIALIZED_DATA = 1 ,
423
+ .MEM_PURGEABLE = 1 ,
424
+ .MEM_READ = 1 ,
425
+ },
426
+ };
427
+ try self .setSectionName (& header , ".reloc" );
428
+ try self .sections .append (gpa , .{ .header = header });
429
+ }
430
+
381
431
if (self .strtab_offset == null ) {
382
432
try self .strtab .buffer .append (gpa , 0 );
383
433
self .strtab_offset = self .findFreeSpace (@intCast (u32 , self .strtab .len ()), 1 );
@@ -605,6 +655,14 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
605
655
.prev_vaddr = sym .value ,
606
656
});
607
657
658
+ const target_sym = self .getSymbol (target );
659
+ switch (target_sym .section_number ) {
660
+ .UNDEFINED = > @panic ("TODO generate a binding for undefined GOT target" ),
661
+ .ABSOLUTE = > {},
662
+ .DEBUG = > unreachable , // not possible
663
+ else = > try atom .addBaseRelocation (self , 0 ),
664
+ }
665
+
608
666
return atom ;
609
667
}
610
668
@@ -1179,6 +1237,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
1179
1237
try self .resolveRelocs (atom .* );
1180
1238
}
1181
1239
}
1240
+ try self .writeBaseRelocations ();
1182
1241
1183
1242
if (self .getEntryPoint ()) | entry_sym_loc | {
1184
1243
self .entry_addr = self .getSymbol (entry_sym_loc ).value ;
@@ -1216,6 +1275,83 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v
1216
1275
log .debug ("TODO implement updateDeclLineNumber" , .{});
1217
1276
}
1218
1277
1278
+ /// TODO: note if we need to rewrite base relocations by dirtying any of the entries in the global table
1279
+ /// TODO: note that .ABSOLUTE is used as padding within each block; we could use this fact to do
1280
+ /// incremental updates and writes into the table instead of doing it all at once
1281
+ fn writeBaseRelocations (self : * Coff ) ! void {
1282
+ const gpa = self .base .allocator ;
1283
+
1284
+ var pages = std .AutoHashMap (u32 , std .ArrayList (coff .BaseRelocation )).init (gpa );
1285
+ defer {
1286
+ var it = pages .valueIterator ();
1287
+ while (it .next ()) | inner | {
1288
+ inner .deinit ();
1289
+ }
1290
+ pages .deinit ();
1291
+ }
1292
+
1293
+ var it = self .base_relocs .iterator ();
1294
+ while (it .next ()) | entry | {
1295
+ const atom = entry .key_ptr .* ;
1296
+ const offsets = entry .value_ptr .* ;
1297
+
1298
+ for (offsets .items ) | offset | {
1299
+ const sym = atom .getSymbol (self );
1300
+ const rva = sym .value + offset ;
1301
+ const page = mem .alignBackwardGeneric (u32 , rva , self .page_size );
1302
+ const gop = try pages .getOrPut (page );
1303
+ if (! gop .found_existing ) {
1304
+ gop .value_ptr .* = std .ArrayList (coff .BaseRelocation ).init (gpa );
1305
+ }
1306
+ try gop .value_ptr .append (.{
1307
+ .offset = @intCast (u12 , rva - page ),
1308
+ .@"type" = .DIR64 ,
1309
+ });
1310
+ }
1311
+ }
1312
+
1313
+ var buffer = std .ArrayList (u8 ).init (gpa );
1314
+ defer buffer .deinit ();
1315
+
1316
+ var pages_it = pages .iterator ();
1317
+ while (pages_it .next ()) | entry | {
1318
+ // Pad to required 4byte alignment
1319
+ if (! mem .isAlignedGeneric (
1320
+ usize ,
1321
+ entry .value_ptr .items .len * @sizeOf (coff .BaseRelocation ),
1322
+ @sizeOf (u32 ),
1323
+ )) {
1324
+ try entry .value_ptr .append (.{
1325
+ .offset = 0 ,
1326
+ .@"type" = .ABSOLUTE ,
1327
+ });
1328
+ }
1329
+
1330
+ const block_size = @intCast (
1331
+ u32 ,
1332
+ entry .value_ptr .items .len * @sizeOf (coff .BaseRelocation ) + @sizeOf (coff .BaseRelocationDirectoryEntry ),
1333
+ );
1334
+ try buffer .ensureUnusedCapacity (block_size );
1335
+ buffer .appendSliceAssumeCapacity (mem .asBytes (& coff.BaseRelocationDirectoryEntry {
1336
+ .page_rva = entry .key_ptr .* ,
1337
+ .block_size = block_size ,
1338
+ }));
1339
+ buffer .appendSliceAssumeCapacity (mem .sliceAsBytes (entry .value_ptr .items ));
1340
+ }
1341
+
1342
+ const header = & self .sections .items (.header )[self .reloc_section_index .? ];
1343
+ const sect_capacity = self .allocatedSize (header .pointer_to_raw_data );
1344
+ const needed_size = @intCast (u32 , buffer .items .len );
1345
+ assert (needed_size < sect_capacity ); // TODO expand .reloc section
1346
+
1347
+ try self .base .file .? .pwriteAll (buffer .items , header .pointer_to_raw_data );
1348
+
1349
+ self .data_directories [@enumToInt (coff .DirectoryEntry .BASERELOC )] = .{
1350
+ .virtual_address = header .virtual_address ,
1351
+ .size = needed_size ,
1352
+ };
1353
+ }
1354
+
1219
1355
fn writeStrtab (self : * Coff ) ! void {
1220
1356
const allocated_size = self .allocatedSize (self .strtab_offset .? );
1221
1357
const needed_size = @intCast (u32 , self .strtab .len ());
@@ -1277,8 +1413,8 @@ fn writeHeader(self: *Coff) !void {
1277
1413
writer .writeAll (mem .asBytes (& coff_header )) catch unreachable ;
1278
1414
1279
1415
const dll_flags : coff.DllFlags = .{
1280
- .HIGH_ENTROPY_VA = 0 , //@boolToInt(self.base.options.pie),
1281
- .DYNAMIC_BASE = 0 ,
1416
+ .HIGH_ENTROPY_VA = 1 , // TODO do we want to permit non-PIE builds at all?
1417
+ .DYNAMIC_BASE = 1 ,
1282
1418
.TERMINAL_SERVER_AWARE = 1 , // We are not a legacy app
1283
1419
.NX_COMPAT = 1 , // We are compatible with Data Execution Prevention
1284
1420
};
0 commit comments