| ... | @@ -94,35 +94,40 @@ pub const ElfFile = struct { | ... | @@ -94,35 +94,40 @@ pub const ElfFile = struct { |
| 94 | | 94 | |
| 95 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. | 95 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 96 | /// Same order as in the file. | 96 | /// Same order as in the file. |
| 97 | sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, | 97 | sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){}, |
| 98 | shdr_table_offset: ?u64 = null, | 98 | shdr_table_offset: ?u64 = null, |
| 99 | | 99 | |
| 100 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. | 100 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 101 | /// Same order as in the file. | 101 | /// Same order as in the file. |
| 102 | program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{}, | 102 | program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){}, |
| 103 | phdr_table_offset: ?u64 = null, | 103 | phdr_table_offset: ?u64 = null, |
| 104 | /// The index into the program headers of a PT_LOAD program header with Read and Execute flags | 104 | /// The index into the program headers of a PT_LOAD program header with Read and Execute flags |
| 105 | phdr_load_re_index: ?u16 = null, | 105 | phdr_load_re_index: ?u16 = null, |
| | 106 | /// The index into the program headers of the global offset table. |
| | 107 | /// It needs PT_LOAD and Read flags. |
| | 108 | phdr_got_index: ?u16 = null, |
| 106 | entry_addr: ?u64 = null, | 109 | entry_addr: ?u64 = null, |
| 107 | | 110 | |
| 108 | shstrtab: std.ArrayListUnmanaged(u8) = .{}, | 111 | shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, |
| 109 | shstrtab_index: ?u16 = null, | 112 | shstrtab_index: ?u16 = null, |
| 110 | | 113 | |
| 111 | text_section_index: ?u16 = null, | 114 | text_section_index: ?u16 = null, |
| 112 | symtab_section_index: ?u16 = null, | 115 | symtab_section_index: ?u16 = null, |
| | 116 | got_section_index: ?u16 = null, |
| 113 | | 117 | |
| 114 | /// The same order as in the file | 118 | /// The same order as in the file |
| 115 | symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, | 119 | symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){}, |
| 116 | | 120 | |
| 117 | /// Same order as in the file. | 121 | /// Same order as in the file. The value is the absolute vaddr value. |
| 118 | offset_table: std.ArrayListUnmanaged(aoeu) = .{}, | 122 | /// If the vaddr of the executable program header changes, the entire |
| | 123 | /// offset table needs to be rewritten. |
| | 124 | offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){}, |
| 119 | | 125 | |
| 120 | /// This means the entire read-only executable program code needs to be rewritten. | | |
| 121 | phdr_load_re_dirty: bool = false, | | |
| 122 | phdr_table_dirty: bool = false, | 126 | phdr_table_dirty: bool = false, |
| 123 | shdr_table_dirty: bool = false, | 127 | shdr_table_dirty: bool = false, |
| 124 | shstrtab_dirty: bool = false, | 128 | shstrtab_dirty: bool = false, |
| 125 | symtab_dirty: bool = false, | 129 | offset_table_count_dirty: bool = false, |
| | 130 | symbol_count_dirty: bool = false, |
| 126 | | 131 | |
| 127 | error_flags: ErrorFlags = ErrorFlags{}, | 132 | error_flags: ErrorFlags = ErrorFlags{}, |
| 128 | | 133 | |
| ... | @@ -130,18 +135,25 @@ pub const ElfFile = struct { | ... | @@ -130,18 +135,25 @@ pub const ElfFile = struct { |
| 130 | no_entry_point_found: bool = false, | 135 | no_entry_point_found: bool = false, |
| 131 | }; | 136 | }; |
| 132 | | 137 | |
| 133 | /// TODO it's too bad this optional takes up double the memory it should | | |
| 134 | pub const Decl = struct { | 138 | pub const Decl = struct { |
| 135 | /// Each decl always gets a local symbol with the fully qualified name. | 139 | /// Each decl always gets a local symbol with the fully qualified name. |
| 136 | /// The vaddr and size are found here directly. | 140 | /// The vaddr and size are found here directly. |
| 137 | /// The file offset is found by computing the vaddr offset from the section vaddr | 141 | /// The file offset is found by computing the vaddr offset from the section vaddr |
| 138 | /// the symbol references, and adding that to the file offset of the section. | 142 | /// the symbol references, and adding that to the file offset of the section. |
| 139 | local_sym_index: ?usize = null, | 143 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or |
| | 144 | /// offset table entry. |
| | 145 | local_sym_index: u32, |
| | 146 | /// This field is undefined for symbols with size = 0. |
| | 147 | offset_table_index: u32, |
| | 148 | |
| | 149 | pub const empty = Decl{ |
| | 150 | .local_sym_index = 0, |
| | 151 | .offset_table_index = undefined, |
| | 152 | }; |
| 140 | }; | 153 | }; |
| 141 | | 154 | |
| 142 | /// TODO it's too bad this optional takes up double the memory it should | | |
| 143 | pub const Export = struct { | 155 | pub const Export = struct { |
| 144 | sym_index: ?usize = null, | 156 | sym_index: usize, |
| 145 | }; | 157 | }; |
| 146 | | 158 | |
| 147 | pub fn deinit(self: *ElfFile) void { | 159 | pub fn deinit(self: *ElfFile) void { |
| ... | @@ -250,33 +262,57 @@ pub const ElfFile = struct { | ... | @@ -250,33 +262,57 @@ pub const ElfFile = struct { |
| 250 | .p32 => true, | 262 | .p32 => true, |
| 251 | .p64 => false, | 263 | .p64 => false, |
| 252 | }; | 264 | }; |
| | 265 | const ptr_size: u8 = switch (self.ptr_width) { |
| | 266 | .p32 => 4, |
| | 267 | .p64 => 8, |
| | 268 | }; |
| 253 | if (self.phdr_load_re_index == null) { | 269 | if (self.phdr_load_re_index == null) { |
| 254 | self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len); | 270 | self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len); |
| 255 | const file_size = self.options.program_code_size_hint; | 271 | const file_size = self.options.program_code_size_hint; |
| 256 | const p_align = 0x1000; | 272 | const p_align = 0x1000; |
| 257 | const off = self.findFreeSpace(file_size, p_align); | 273 | const off = self.findFreeSpace(file_size, p_align); |
| 258 | //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 274 | //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 259 | try self.program_headers.append(.{ | 275 | try self.program_headers.append(self.allocator, .{ |
| 260 | .p_type = elf.PT_LOAD, | 276 | .p_type = elf.PT_LOAD, |
| 261 | .p_offset = off, | 277 | .p_offset = off, |
| 262 | .p_filesz = file_size, | 278 | .p_filesz = file_size, |
| 263 | .p_vaddr = default_entry_addr, | 279 | .p_vaddr = default_entry_addr, |
| 264 | .p_paddr = default_entry_addr, | 280 | .p_paddr = default_entry_addr, |
| 265 | .p_memsz = 0, | 281 | .p_memsz = file_size, |
| 266 | .p_align = p_align, | 282 | .p_align = p_align, |
| 267 | .p_flags = elf.PF_X | elf.PF_R, | 283 | .p_flags = elf.PF_X | elf.PF_R, |
| 268 | }); | 284 | }); |
| 269 | self.entry_addr = null; | 285 | self.entry_addr = null; |
| 270 | self.phdr_load_re_dirty = true; | 286 | self.phdr_table_dirty = true; |
| | 287 | } |
| | 288 | if (self.phdr_got_index == null) { |
| | 289 | self.phdr_got_index = @intCast(u16, self.program_headers.items.len); |
| | 290 | const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint; |
| | 291 | const off = self.findFreeSpace(file_size, ptr_size); |
| | 292 | //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| | 293 | // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. |
| | 294 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something |
| | 295 | // else in virtual memory. |
| | 296 | const default_got_addr = 0x80000000; |
| | 297 | try self.program_headers.append(self.allocator, .{ |
| | 298 | .p_type = elf.PT_LOAD, |
| | 299 | .p_offset = off, |
| | 300 | .p_filesz = file_size, |
| | 301 | .p_vaddr = default_got_addr, |
| | 302 | .p_paddr = default_got_addr, |
| | 303 | .p_memsz = file_size, |
| | 304 | .p_align = ptr_size, |
| | 305 | .p_flags = elf.PF_R, |
| | 306 | }); |
| 271 | self.phdr_table_dirty = true; | 307 | self.phdr_table_dirty = true; |
| 272 | } | 308 | } |
| 273 | if (self.shstrtab_index == null) { | 309 | if (self.shstrtab_index == null) { |
| 274 | self.shstrtab_index = @intCast(u16, self.sections.items.len); | 310 | self.shstrtab_index = @intCast(u16, self.sections.items.len); |
| 275 | assert(self.shstrtab.items.len == 0); | 311 | assert(self.shstrtab.items.len == 0); |
| 276 | try self.shstrtab.append(0); // need a 0 at position 0 | 312 | try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0 |
| 277 | const off = self.findFreeSpace(self.shstrtab.items.len, 1); | 313 | const off = self.findFreeSpace(self.shstrtab.items.len, 1); |
| 278 | //std.debug.warn("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len }); | 314 | //std.debug.warn("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len }); |
| 279 | try self.sections.append(.{ | 315 | try self.sections.append(self.allocator, .{ |
| 280 | .sh_name = try self.makeString(".shstrtab"), | 316 | .sh_name = try self.makeString(".shstrtab"), |
| 281 | .sh_type = elf.SHT_STRTAB, | 317 | .sh_type = elf.SHT_STRTAB, |
| 282 | .sh_flags = 0, | 318 | .sh_flags = 0, |
| ... | @@ -295,7 +331,7 @@ pub const ElfFile = struct { | ... | @@ -295,7 +331,7 @@ pub const ElfFile = struct { |
| 295 | self.text_section_index = @intCast(u16, self.sections.items.len); | 331 | self.text_section_index = @intCast(u16, self.sections.items.len); |
| 296 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; | 332 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| 297 | | 333 | |
| 298 | try self.sections.append(.{ | 334 | try self.sections.append(self.allocator, .{ |
| 299 | .sh_name = try self.makeString(".text"), | 335 | .sh_name = try self.makeString(".text"), |
| 300 | .sh_type = elf.SHT_PROGBITS, | 336 | .sh_type = elf.SHT_PROGBITS, |
| 301 | .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, | 337 | .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, |
| ... | @@ -309,6 +345,24 @@ pub const ElfFile = struct { | ... | @@ -309,6 +345,24 @@ pub const ElfFile = struct { |
| 309 | }); | 345 | }); |
| 310 | self.shdr_table_dirty = true; | 346 | self.shdr_table_dirty = true; |
| 311 | } | 347 | } |
| | 348 | if (self.got_section_index == null) { |
| | 349 | self.got_section_index = @intCast(u16, self.sections.items.len); |
| | 350 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; |
| | 351 | |
| | 352 | try self.sections.append(self.allocator, .{ |
| | 353 | .sh_name = try self.makeString(".got"), |
| | 354 | .sh_type = elf.SHT_PROGBITS, |
| | 355 | .sh_flags = elf.SHF_ALLOC, |
| | 356 | .sh_addr = phdr.p_vaddr, |
| | 357 | .sh_offset = phdr.p_offset, |
| | 358 | .sh_size = phdr.p_filesz, |
| | 359 | .sh_link = 0, |
| | 360 | .sh_info = 0, |
| | 361 | .sh_addralign = phdr.p_align, |
| | 362 | .sh_entsize = ptr_size, |
| | 363 | }); |
| | 364 | self.shdr_table_dirty = true; |
| | 365 | } |
| 312 | if (self.symtab_section_index == null) { | 366 | if (self.symtab_section_index == null) { |
| 313 | self.symtab_section_index = @intCast(u16, self.sections.items.len); | 367 | self.symtab_section_index = @intCast(u16, self.sections.items.len); |
| 314 | const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); | 368 | const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); |
| ... | @@ -317,7 +371,7 @@ pub const ElfFile = struct { | ... | @@ -317,7 +371,7 @@ pub const ElfFile = struct { |
| 317 | const off = self.findFreeSpace(file_size, min_align); | 371 | const off = self.findFreeSpace(file_size, min_align); |
| 318 | //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 372 | //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 319 | | 373 | |
| 320 | try self.sections.append(.{ | 374 | try self.sections.append(self.allocator, .{ |
| 321 | .sh_name = try self.makeString(".symtab"), | 375 | .sh_name = try self.makeString(".symtab"), |
| 322 | .sh_type = elf.SHT_SYMTAB, | 376 | .sh_type = elf.SHT_SYMTAB, |
| 323 | .sh_flags = 0, | 377 | .sh_flags = 0, |
| ... | @@ -330,14 +384,14 @@ pub const ElfFile = struct { | ... | @@ -330,14 +384,14 @@ pub const ElfFile = struct { |
| 330 | .sh_addralign = min_align, | 384 | .sh_addralign = min_align, |
| 331 | .sh_entsize = each_size, | 385 | .sh_entsize = each_size, |
| 332 | }); | 386 | }); |
| 333 | self.symtab_dirty = true; | | |
| 334 | self.shdr_table_dirty = true; | 387 | self.shdr_table_dirty = true; |
| | 388 | try self.writeAllSymbols(); |
| 335 | } | 389 | } |
| 336 | const shsize: u64 = switch (ptr_width) { | 390 | const shsize: u64 = switch (self.ptr_width) { |
| 337 | .p32 => @sizeOf(elf.Elf32_Shdr), | 391 | .p32 => @sizeOf(elf.Elf32_Shdr), |
| 338 | .p64 => @sizeOf(elf.Elf64_Shdr), | 392 | .p64 => @sizeOf(elf.Elf64_Shdr), |
| 339 | }; | 393 | }; |
| 340 | const shalign: u16 = switch (ptr_width) { | 394 | const shalign: u16 = switch (self.ptr_width) { |
| 341 | .p32 => @alignOf(elf.Elf32_Shdr), | 395 | .p32 => @alignOf(elf.Elf32_Shdr), |
| 342 | .p64 => @alignOf(elf.Elf64_Shdr), | 396 | .p64 => @alignOf(elf.Elf64_Shdr), |
| 343 | }; | 397 | }; |
| ... | @@ -345,11 +399,11 @@ pub const ElfFile = struct { | ... | @@ -345,11 +399,11 @@ pub const ElfFile = struct { |
| 345 | self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign); | 399 | self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign); |
| 346 | self.shdr_table_dirty = true; | 400 | self.shdr_table_dirty = true; |
| 347 | } | 401 | } |
| 348 | const phsize: u64 = switch (ptr_width) { | 402 | const phsize: u64 = switch (self.ptr_width) { |
| 349 | .p32 => @sizeOf(elf.Elf32_Phdr), | 403 | .p32 => @sizeOf(elf.Elf32_Phdr), |
| 350 | .p64 => @sizeOf(elf.Elf64_Phdr), | 404 | .p64 => @sizeOf(elf.Elf64_Phdr), |
| 351 | }; | 405 | }; |
| 352 | const phalign: u16 = switch (ptr_width) { | 406 | const phalign: u16 = switch (self.ptr_width) { |
| 353 | .p32 => @alignOf(elf.Elf32_Phdr), | 407 | .p32 => @alignOf(elf.Elf32_Phdr), |
| 354 | .p64 => @alignOf(elf.Elf64_Phdr), | 408 | .p64 => @alignOf(elf.Elf64_Phdr), |
| 355 | }; | 409 | }; |
| ... | @@ -399,7 +453,7 @@ pub const ElfFile = struct { | ... | @@ -399,7 +453,7 @@ pub const ElfFile = struct { |
| 399 | try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); | 453 | try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); |
| 400 | }, | 454 | }, |
| 401 | } | 455 | } |
| 402 | self.phdr_table_offset = false; | 456 | self.phdr_table_dirty = false; |
| 403 | } | 457 | } |
| 404 | | 458 | |
| 405 | { | 459 | { |
| ... | @@ -432,11 +486,10 @@ pub const ElfFile = struct { | ... | @@ -432,11 +486,10 @@ pub const ElfFile = struct { |
| 432 | self.shdr_table_offset = self.findFreeSpace(needed_size, phalign); | 486 | self.shdr_table_offset = self.findFreeSpace(needed_size, phalign); |
| 433 | } | 487 | } |
| 434 | | 488 | |
| 435 | const allocator = self.sections.allocator; | | |
| 436 | switch (self.ptr_width) { | 489 | switch (self.ptr_width) { |
| 437 | .p32 => { | 490 | .p32 => { |
| 438 | const buf = try allocator.alloc(elf.Elf32_Shdr, self.sections.items.len); | 491 | const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len); |
| 439 | defer allocator.free(buf); | 492 | defer self.allocator.free(buf); |
| 440 | | 493 | |
| 441 | for (buf) |*shdr, i| { | 494 | for (buf) |*shdr, i| { |
| 442 | shdr.* = sectHeaderTo32(self.sections.items[i]); | 495 | shdr.* = sectHeaderTo32(self.sections.items[i]); |
| ... | @@ -447,8 +500,8 @@ pub const ElfFile = struct { | ... | @@ -447,8 +500,8 @@ pub const ElfFile = struct { |
| 447 | try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); | 500 | try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); |
| 448 | }, | 501 | }, |
| 449 | .p64 => { | 502 | .p64 => { |
| 450 | const buf = try allocator.alloc(elf.Elf64_Shdr, self.sections.items.len); | 503 | const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len); |
| 451 | defer allocator.free(buf); | 504 | defer self.allocator.free(buf); |
| 452 | | 505 | |
| 453 | for (buf) |*shdr, i| { | 506 | for (buf) |*shdr, i| { |
| 454 | shdr.* = self.sections.items[i]; | 507 | shdr.* = self.sections.items[i]; |
| ... | @@ -460,6 +513,7 @@ pub const ElfFile = struct { | ... | @@ -460,6 +513,7 @@ pub const ElfFile = struct { |
| 460 | try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); | 513 | try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); |
| 461 | }, | 514 | }, |
| 462 | } | 515 | } |
| | 516 | self.shdr_table_dirty = false; |
| 463 | } | 517 | } |
| 464 | if (self.entry_addr == null and self.options.output_mode == .Exe) { | 518 | if (self.entry_addr == null and self.options.output_mode == .Exe) { |
| 465 | self.error_flags.no_entry_point_found = true; | 519 | self.error_flags.no_entry_point_found = true; |
| ... | @@ -470,11 +524,11 @@ pub const ElfFile = struct { | ... | @@ -470,11 +524,11 @@ pub const ElfFile = struct { |
| 470 | // TODO find end pos and truncate | 524 | // TODO find end pos and truncate |
| 471 | | 525 | |
| 472 | // The point of flush() is to commit changes, so nothing should be dirty after this. | 526 | // The point of flush() is to commit changes, so nothing should be dirty after this. |
| 473 | assert(!self.phdr_load_re_dirty); | | |
| 474 | assert(!self.phdr_table_dirty); | 527 | assert(!self.phdr_table_dirty); |
| 475 | assert(!self.shdr_table_dirty); | 528 | assert(!self.shdr_table_dirty); |
| 476 | assert(!self.shstrtab_dirty); | 529 | assert(!self.shstrtab_dirty); |
| 477 | assert(!self.symtab_dirty); | 530 | assert(!self.symbol_count_dirty); |
| | 531 | assert(!self.offset_table_count_dirty); |
| 478 | } | 532 | } |
| 479 | | 533 | |
| 480 | fn writeElfHeader(self: *ElfFile) !void { | 534 | fn writeElfHeader(self: *ElfFile) !void { |
| ... | @@ -608,6 +662,7 @@ pub const ElfFile = struct { | ... | @@ -608,6 +662,7 @@ pub const ElfFile = struct { |
| 608 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; | 662 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| 609 | const shdr = &self.sections.items[self.text_section_index.?]; | 663 | const shdr = &self.sections.items[self.text_section_index.?]; |
| 610 | | 664 | |
| | 665 | // TODO Also detect virtual address collisions. |
| 611 | const text_capacity = self.allocatedSize(shdr.sh_offset); | 666 | const text_capacity = self.allocatedSize(shdr.sh_offset); |
| 612 | // TODO instead of looping here, maintain a free list and a pointer to the end. | 667 | // TODO instead of looping here, maintain a free list and a pointer to the end. |
| 613 | const end_vaddr = blk: { | 668 | const end_vaddr = blk: { |
| ... | @@ -664,7 +719,7 @@ pub const ElfFile = struct { | ... | @@ -664,7 +719,7 @@ pub const ElfFile = struct { |
| 664 | defer code.deinit(); | 719 | defer code.deinit(); |
| 665 | | 720 | |
| 666 | const typed_value = decl.typed_value.most_recent.typed_value; | 721 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 667 | const err_msg = try codegen.generateSymbol(typed_value, module, &code, module.allocator); | 722 | const err_msg = try codegen.generateSymbol(typed_value, module, &code); |
| 668 | if (err_msg != null) |em| { | 723 | if (err_msg != null) |em| { |
| 669 | decl.analysis = .codegen_failure; | 724 | decl.analysis = .codegen_failure; |
| 670 | _ = try module.failed_decls.put(decl, em); | 725 | _ = try module.failed_decls.put(decl, em); |
| ... | @@ -678,26 +733,31 @@ pub const ElfFile = struct { | ... | @@ -678,26 +733,31 @@ pub const ElfFile = struct { |
| 678 | else => elf.STT_OBJECT, | 733 | else => elf.STT_OBJECT, |
| 679 | }; | 734 | }; |
| 680 | | 735 | |
| 681 | if (decl.link.local_sym_index) |local_sym_index| { | 736 | if (decl.link.local_sym_index != 0) { |
| 682 | const local_sym = &self.symbols.items[local_sym_index]; | 737 | const local_sym = &self.symbols.items[decl.link.local_sym_index]; |
| 683 | const existing_block = self.findAllocatedTextBlock(local_sym); | 738 | const existing_block = self.findAllocatedTextBlock(local_sym); |
| 684 | const file_offset = if (code_size > existing_block.size_capacity) fo: { | 739 | const file_offset = if (code_size > existing_block.size_capacity) fo: { |
| 685 | const new_block = self.allocateTextBlock(code_size); | 740 | const new_block = try self.allocateTextBlock(code_size); |
| 686 | local_sym.st_value = new_block.vaddr; | 741 | local_sym.st_value = new_block.vaddr; |
| 687 | local_sym.st_size = code_size; | 742 | local_sym.st_size = code_size; |
| | 743 | |
| | 744 | try self.writeOffsetTableEntry(decl.link.offset_table_index); |
| | 745 | |
| 688 | break :fo new_block.file_offset; | 746 | break :fo new_block.file_offset; |
| 689 | } else existing_block.file_offset; | 747 | } else existing_block.file_offset; |
| 690 | local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(u8, decl.name)); | 748 | local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(u8, decl.name)); |
| 691 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; | 749 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; |
| 692 | // TODO this write could be avoided if no fields of the symbol were changed. | 750 | // TODO this write could be avoided if no fields of the symbol were changed. |
| 693 | try self.writeSymbol(local_sym_index); | 751 | try self.writeSymbol(decl.link.local_sym_index); |
| 694 | break :blk file_offset; | 752 | break :blk file_offset; |
| 695 | } else { | 753 | } else { |
| 696 | try self.symbols.ensureCapacity(self.symbols.items.len + 1); | 754 | try self.symbols.ensureCapacity(self.symbols.items.len + 1); |
| | 755 | try self.offset_table.ensureCapacity(self.offset_table.items.len + 1); |
| 697 | const decl_name = mem.spanZ(u8, decl.name); | 756 | const decl_name = mem.spanZ(u8, decl.name); |
| 698 | const name_str_index = try self.makeString(decl_name); | 757 | const name_str_index = try self.makeString(decl_name); |
| 699 | const new_block = self.allocateTextBlock(code_size); | 758 | const new_block = try self.allocateTextBlock(code_size); |
| 700 | const local_sym_index = self.symbols.items.len; | 759 | const local_sym_index = self.symbols.items.len; |
| | 760 | const offset_table_index = self.offset_table.items.len; |
| 701 | | 761 | |
| 702 | self.symbols.appendAssumeCapacity(self.allocator, .{ | 762 | self.symbols.appendAssumeCapacity(self.allocator, .{ |
| 703 | .st_name = name_str_index, | 763 | .st_name = name_str_index, |
| ... | @@ -708,10 +768,17 @@ pub const ElfFile = struct { | ... | @@ -708,10 +768,17 @@ pub const ElfFile = struct { |
| 708 | .st_size = code_size, | 768 | .st_size = code_size, |
| 709 | }); | 769 | }); |
| 710 | errdefer self.symbols.shrink(self.symbols.items.len - 1); | 770 | errdefer self.symbols.shrink(self.symbols.items.len - 1); |
| | 771 | self.offset_table.appendAssumeCapacity(self.allocator, new_block.vaddr); |
| | 772 | errdefer self.offset_table.shrink(self.offset_table.items.len - 1); |
| 711 | try self.writeSymbol(local_sym_index); | 773 | try self.writeSymbol(local_sym_index); |
| | 774 | try self.writeOffsetTableEntry(offset_table_index); |
| 712 | | 775 | |
| 713 | self.symbol_count_dirty = true; | 776 | self.symbol_count_dirty = true; |
| 714 | decl.link.local_sym_index = local_sym_index; | 777 | self.offset_table_count_dirty = true; |
| | 778 | decl.link = .{ |
| | 779 | .local_sym_index = local_sym_index, |
| | 780 | .offset_table_index = offset_table_index, |
| | 781 | }; |
| 715 | | 782 | |
| 716 | break :blk new_block.file_offset; | 783 | break :blk new_block.file_offset; |
| 717 | } | 784 | } |
| ... | @@ -839,6 +906,45 @@ pub const ElfFile = struct { | ... | @@ -839,6 +906,45 @@ pub const ElfFile = struct { |
| 839 | } | 906 | } |
| 840 | } | 907 | } |
| 841 | | 908 | |
| | 909 | fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void { |
| | 910 | const shdr = &self.sections.items[self.got_section_index.?]; |
| | 911 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; |
| | 912 | if (self.offset_table_count_dirty) { |
| | 913 | // TODO Also detect virtual address collisions. |
| | 914 | const allocated_size = self.allocatedSize(shdr.sh_offset); |
| | 915 | const needed_size = self.symbols.items.len * shdr.sh_entsize; |
| | 916 | if (needed_size > allocated_size) { |
| | 917 | // Must move the entire got section. |
| | 918 | const new_offset = self.findFreeSpace(needed_size, shdr.sh_entsize); |
| | 919 | const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, shdr.sh_size); |
| | 920 | if (amt != text_size) return error.InputOutput; |
| | 921 | shdr.sh_offset = new_offset; |
| | 922 | } |
| | 923 | shdr.sh_size = needed_size; |
| | 924 | phdr.p_memsz = needed_size; |
| | 925 | phdr.p_filesz = needed_size; |
| | 926 | |
| | 927 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty |
| | 928 | self.phdr_table_dirty = true; // TODO look into making only the one program header dirty |
| | 929 | |
| | 930 | self.offset_table_count_dirty = false; |
| | 931 | } |
| | 932 | const endian = self.options.target.cpu.arch.endian(); |
| | 933 | const off = shdr.sh_offset + shdr.sh_entsize * index; |
| | 934 | switch (self.ptr_width) { |
| | 935 | .p32 => { |
| | 936 | var buf: [4]u8 = undefined; |
| | 937 | mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); |
| | 938 | try self.file.pwriteAll(&buf, off); |
| | 939 | }, |
| | 940 | .p64 => { |
| | 941 | var buf: [8]u8 = undefined; |
| | 942 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); |
| | 943 | try self.file.pwriteAll(&buf, off); |
| | 944 | }, |
| | 945 | } |
| | 946 | } |
| | 947 | |
| 842 | fn writeSymbol(self: *ElfFile, index: usize) !void { | 948 | fn writeSymbol(self: *ElfFile, index: usize) !void { |
| 843 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; | 949 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; |
| 844 | // Make sure we are not pointlessly writing symbol data that will have to get relocated | 950 | // Make sure we are not pointlessly writing symbol data that will have to get relocated |
| ... | @@ -849,6 +955,9 @@ pub const ElfFile = struct { | ... | @@ -849,6 +955,9 @@ pub const ElfFile = struct { |
| 849 | if (needed_size > allocated_size) { | 955 | if (needed_size > allocated_size) { |
| 850 | return self.writeAllSymbols(); | 956 | return self.writeAllSymbols(); |
| 851 | } | 957 | } |
| | 958 | syms_sect.sh_info = @intCast(u32, self.symbols.items.len); |
| | 959 | self.shdr_table_dirty = true; // TODO look into only writing one section |
| | 960 | self.symbol_count_dirty = false; |
| 852 | } | 961 | } |
| 853 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | 962 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| 854 | switch (self.ptr_width) { | 963 | switch (self.ptr_width) { |
| ... | @@ -896,12 +1005,13 @@ pub const ElfFile = struct { | ... | @@ -896,12 +1005,13 @@ pub const ElfFile = struct { |
| 896 | //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size }); | 1005 | //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size }); |
| 897 | syms_sect.sh_size = needed_size; | 1006 | syms_sect.sh_size = needed_size; |
| 898 | syms_sect.sh_info = @intCast(u32, self.symbols.items.len); | 1007 | syms_sect.sh_info = @intCast(u32, self.symbols.items.len); |
| 899 | const allocator = self.symbols.allocator; | 1008 | self.symbol_count_dirty = false; |
| | 1009 | self.shdr_table_dirty = true; // TODO look into only writing one section |
| 900 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | 1010 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| 901 | switch (self.ptr_width) { | 1011 | switch (self.ptr_width) { |
| 902 | .p32 => { | 1012 | .p32 => { |
| 903 | const buf = try allocator.alloc(elf.Elf32_Sym, self.symbols.items.len); | 1013 | const buf = try self.allocator.alloc(elf.Elf32_Sym, self.symbols.items.len); |
| 904 | defer allocator.free(buf); | 1014 | defer self.allocator.free(buf); |
| 905 | | 1015 | |
| 906 | for (buf) |*sym, i| { | 1016 | for (buf) |*sym, i| { |
| 907 | sym.* = .{ | 1017 | sym.* = .{ |
| ... | @@ -919,8 +1029,8 @@ pub const ElfFile = struct { | ... | @@ -919,8 +1029,8 @@ pub const ElfFile = struct { |
| 919 | try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset); | 1029 | try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset); |
| 920 | }, | 1030 | }, |
| 921 | .p64 => { | 1031 | .p64 => { |
| 922 | const buf = try allocator.alloc(elf.Elf64_Sym, self.symbols.items.len); | 1032 | const buf = try self.allocator.alloc(elf.Elf64_Sym, self.symbols.items.len); |
| 923 | defer allocator.free(buf); | 1033 | defer self.allocator.free(buf); |
| 924 | | 1034 | |
| 925 | for (buf) |*sym, i| { | 1035 | for (buf) |*sym, i| { |
| 926 | sym.* = .{ | 1036 | sym.* = .{ |
| ... | @@ -961,12 +1071,11 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El | ... | @@ -961,12 +1071,11 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El |
| 961 | .allocator = allocator, | 1071 | .allocator = allocator, |
| 962 | .file = file, | 1072 | .file = file, |
| 963 | .options = options, | 1073 | .options = options, |
| 964 | .ptr_width = switch (self.options.target.cpu.arch.ptrBitWidth()) { | 1074 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { |
| 965 | 32 => .p32, | 1075 | 32 => .p32, |
| 966 | 64 => .p64, | 1076 | 64 => .p64, |
| 967 | else => return error.UnsupportedELFArchitecture, | 1077 | else => return error.UnsupportedELFArchitecture, |
| 968 | }, | 1078 | }, |
| 969 | .symtab_dirty = true, | | |
| 970 | .shdr_table_dirty = true, | 1079 | .shdr_table_dirty = true, |
| 971 | }; | 1080 | }; |
| 972 | errdefer self.deinit(); | 1081 | errdefer self.deinit(); |
| ... | @@ -1018,7 +1127,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf | ... | @@ -1018,7 +1127,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf |
| 1018 | .allocator = allocator, | 1127 | .allocator = allocator, |
| 1019 | .file = file, | 1128 | .file = file, |
| 1020 | .options = options, | 1129 | .options = options, |
| 1021 | .ptr_width = switch (self.options.target.cpu.arch.ptrBitWidth()) { | 1130 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { |
| 1022 | 32 => .p32, | 1131 | 32 => .p32, |
| 1023 | 64 => .p64, | 1132 | 64 => .p64, |
| 1024 | else => return error.UnsupportedELFArchitecture, | 1133 | else => return error.UnsupportedELFArchitecture, |