authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-04 11:56:31-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-03-04 11:56:31-08:00
logffb2568a9f0d038a94a23bfe776602ac9099e2a0
tree44d937a66f97457b40a2bff3183d762cd7bc2ea3
parent34ca6b7b449f065fd0f65f8cfad82140cafe4c7d
parentaffb57aad97240bf183015e6acba7eab4a9276bb
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7763 from kivikakk/zig-elf-parse

std.elf: expose parsing decoupled from std.fs.File

3 files changed, 178 insertions(+), 187 deletions(-)

lib/std/build/emit_raw.zig+3-3
......@@ -51,9 +51,9 @@ const BinaryElfOutput = struct {
5151 .segments = ArrayList(*BinaryElfSegment).init(allocator),
5252 .sections = ArrayList(*BinaryElfSection).init(allocator),
5353 };
54 const elf_hdr = try std.elf.readHeader(elf_file);
54 const elf_hdr = try std.elf.Header.read(&elf_file);
5555
56 var section_headers = elf_hdr.section_header_iterator(elf_file);
56 var section_headers = elf_hdr.section_header_iterator(&elf_file);
5757 while (try section_headers.next()) |section| {
5858 if (sectionValidForOutput(section)) {
5959 const newSection = try allocator.create(BinaryElfSection);
......@@ -67,7 +67,7 @@ const BinaryElfOutput = struct {
6767 }
6868 }
6969
70 var program_headers = elf_hdr.program_header_iterator(elf_file);
70 var program_headers = elf_hdr.program_header_iterator(&elf_file);
7171 while (try program_headers.next()) |phdr| {
7272 if (phdr.p_type == elf.PT_LOAD) {
7373 const newSegment = try allocator.create(BinaryElfSegment);
lib/std/elf.zig+173-182
......@@ -335,7 +335,7 @@ pub const ET = extern enum(u16) {
335335};
336336
337337/// All integers are native endian.
338const Header = struct {
338pub const Header = struct {
339339 endian: builtin.Endian,
340340 is_64: bool,
341341 entry: u64,
......@@ -347,187 +347,200 @@ const Header = struct {
347347 shnum: u16,
348348 shstrndx: u16,
349349
350 pub fn program_header_iterator(self: Header, file: File) ProgramHeaderIterator {
351 return .{
350 pub fn program_header_iterator(self: Header, parse_source: anytype) ProgramHeaderIterator(@TypeOf(parse_source)) {
351 return ProgramHeaderIterator(@TypeOf(parse_source)){
352352 .elf_header = self,
353 .file = file,
353 .parse_source = parse_source,
354354 };
355355 }
356356
357 pub fn section_header_iterator(self: Header, file: File) SectionHeaderIterator {
358 return .{
357 pub fn section_header_iterator(self: Header, parse_source: anytype) SectionHeaderIterator(@TypeOf(parse_source)) {
358 return SectionHeaderIterator(@TypeOf(parse_source)){
359359 .elf_header = self,
360 .file = file,
360 .parse_source = parse_source,
361361 };
362362 }
363};
364363
365pub fn readHeader(file: File) !Header {
366 var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined;
367 try preadNoEof(file, &hdr_buf, 0);
368 const hdr32 = @ptrCast(*Elf32_Ehdr, &hdr_buf);
369 const hdr64 = @ptrCast(*Elf64_Ehdr, &hdr_buf);
370 if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
371 if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion;
372
373 const endian: std.builtin.Endian = switch (hdr32.e_ident[EI_DATA]) {
374 ELFDATA2LSB => .Little,
375 ELFDATA2MSB => .Big,
376 else => return error.InvalidElfEndian,
377 };
378 const need_bswap = endian != std.builtin.endian;
364 pub fn read(parse_source: anytype) !Header {
365 var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined;
366 try parse_source.seekableStream().seekTo(0);
367 try parse_source.reader().readNoEof(&hdr_buf);
368 return Header.parse(&hdr_buf);
369 }
379370
380 const is_64 = switch (hdr32.e_ident[EI_CLASS]) {
381 ELFCLASS32 => false,
382 ELFCLASS64 => true,
383 else => return error.InvalidElfClass,
384 };
371 pub fn parse(hdr_buf: *align(@alignOf(Elf64_Ehdr)) const [@sizeOf(Elf64_Ehdr)]u8) !Header {
372 const hdr32 = @ptrCast(*const Elf32_Ehdr, hdr_buf);
373 const hdr64 = @ptrCast(*const Elf64_Ehdr, hdr_buf);
374 if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
375 if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion;
385376
386 return @as(Header, .{
387 .endian = endian,
388 .is_64 = is_64,
389 .entry = int(is_64, need_bswap, hdr32.e_entry, hdr64.e_entry),
390 .phoff = int(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff),
391 .shoff = int(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff),
392 .phentsize = int(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize),
393 .phnum = int(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum),
394 .shentsize = int(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize),
395 .shnum = int(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum),
396 .shstrndx = int(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx),
397 });
398}
377 const endian: std.builtin.Endian = switch (hdr32.e_ident[EI_DATA]) {
378 ELFDATA2LSB => .Little,
379 ELFDATA2MSB => .Big,
380 else => return error.InvalidElfEndian,
381 };
382 const need_bswap = endian != std.builtin.endian;
399383
400pub const ProgramHeaderIterator = struct {
401 elf_header: Header,
402 file: File,
403 index: usize = 0,
384 const is_64 = switch (hdr32.e_ident[EI_CLASS]) {
385 ELFCLASS32 => false,
386 ELFCLASS64 => true,
387 else => return error.InvalidElfClass,
388 };
404389
405 pub fn next(self: *ProgramHeaderIterator) !?Elf64_Phdr {
406 if (self.index >= self.elf_header.phnum) return null;
407 defer self.index += 1;
390 return @as(Header, .{
391 .endian = endian,
392 .is_64 = is_64,
393 .entry = int(is_64, need_bswap, hdr32.e_entry, hdr64.e_entry),
394 .phoff = int(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff),
395 .shoff = int(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff),
396 .phentsize = int(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize),
397 .phnum = int(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum),
398 .shentsize = int(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize),
399 .shnum = int(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum),
400 .shstrndx = int(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx),
401 });
402 }
403};
408404
409 if (self.elf_header.is_64) {
410 var phdr: Elf64_Phdr = undefined;
405pub fn ProgramHeaderIterator(ParseSource: anytype) type {
406 return struct {
407 elf_header: Header,
408 parse_source: ParseSource,
409 index: usize = 0,
410
411 pub fn next(self: *@This()) !?Elf64_Phdr {
412 if (self.index >= self.elf_header.phnum) return null;
413 defer self.index += 1;
414
415 if (self.elf_header.is_64) {
416 var phdr: Elf64_Phdr = undefined;
417 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
418 try self.parse_source.seekableStream().seekTo(offset);
419 try self.parse_source.reader().readNoEof(mem.asBytes(&phdr));
420
421 // ELF endianness matches native endianness.
422 if (self.elf_header.endian == std.builtin.endian) return phdr;
423
424 // Convert fields to native endianness.
425 return Elf64_Phdr{
426 .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
427 .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
428 .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
429 .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
430 .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
431 .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
432 .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
433 .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
434 };
435 }
436
437 var phdr: Elf32_Phdr = undefined;
411438 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
412 try preadNoEof(self.file, mem.asBytes(&phdr), offset);
413
414 // ELF endianness matches native endianness.
415 if (self.elf_header.endian == std.builtin.endian) return phdr;
416
417 // Convert fields to native endianness.
439 try self.parse_source.seekableStream().seekTo(offset);
440 try self.parse_source.reader().readNoEof(mem.asBytes(&phdr));
441
442 // ELF endianness does NOT match native endianness.
443 if (self.elf_header.endian != std.builtin.endian) {
444 // Convert fields to native endianness.
445 phdr = .{
446 .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
447 .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
448 .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
449 .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
450 .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
451 .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
452 .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
453 .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
454 };
455 }
456
457 // Convert 32-bit header to 64-bit.
418458 return Elf64_Phdr{
419 .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
420 .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
421 .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
422 .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
423 .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
424 .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
425 .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
426 .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
459 .p_type = phdr.p_type,
460 .p_offset = phdr.p_offset,
461 .p_vaddr = phdr.p_vaddr,
462 .p_paddr = phdr.p_paddr,
463 .p_filesz = phdr.p_filesz,
464 .p_memsz = phdr.p_memsz,
465 .p_flags = phdr.p_flags,
466 .p_align = phdr.p_align,
427467 };
428468 }
469 };
470}
429471
430 var phdr: Elf32_Phdr = undefined;
431 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
432 try preadNoEof(self.file, mem.asBytes(&phdr), offset);
433
434 // ELF endianness does NOT match native endianness.
435 if (self.elf_header.endian != std.builtin.endian) {
436 // Convert fields to native endianness.
437 phdr = .{
438 .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
439 .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
440 .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
441 .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
442 .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
443 .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
444 .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
445 .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
446 };
447 }
448
449 // Convert 32-bit header to 64-bit.
450 return Elf64_Phdr{
451 .p_type = phdr.p_type,
452 .p_offset = phdr.p_offset,
453 .p_vaddr = phdr.p_vaddr,
454 .p_paddr = phdr.p_paddr,
455 .p_filesz = phdr.p_filesz,
456 .p_memsz = phdr.p_memsz,
457 .p_flags = phdr.p_flags,
458 .p_align = phdr.p_align,
459 };
460 }
461};
462
463pub const SectionHeaderIterator = struct {
464 elf_header: Header,
465 file: File,
466 index: usize = 0,
467
468 pub fn next(self: *SectionHeaderIterator) !?Elf64_Shdr {
469 if (self.index >= self.elf_header.shnum) return null;
470 defer self.index += 1;
471
472 if (self.elf_header.is_64) {
473 var shdr: Elf64_Shdr = undefined;
472pub fn SectionHeaderIterator(ParseSource: anytype) type {
473 return struct {
474 elf_header: Header,
475 parse_source: ParseSource,
476 index: usize = 0,
477
478 pub fn next(self: *@This()) !?Elf64_Shdr {
479 if (self.index >= self.elf_header.shnum) return null;
480 defer self.index += 1;
481
482 if (self.elf_header.is_64) {
483 var shdr: Elf64_Shdr = undefined;
484 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
485 try self.parse_source.seekableStream().seekTo(offset);
486 try self.parse_source.reader().readNoEof(mem.asBytes(&shdr));
487
488 // ELF endianness matches native endianness.
489 if (self.elf_header.endian == std.builtin.endian) return shdr;
490
491 // Convert fields to native endianness.
492 return Elf64_Shdr{
493 .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
494 .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
495 .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
496 .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
497 .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
498 .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
499 .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
500 .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
501 .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
502 .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
503 };
504 }
505
506 var shdr: Elf32_Shdr = undefined;
474507 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
475 try preadNoEof(self.file, mem.asBytes(&shdr), offset);
476
477 // ELF endianness matches native endianness.
478 if (self.elf_header.endian == std.builtin.endian) return shdr;
479
480 // Convert fields to native endianness.
508 try self.parse_source.seekableStream().seekTo(offset);
509 try self.parse_source.reader().readNoEof(mem.asBytes(&shdr));
510
511 // ELF endianness does NOT match native endianness.
512 if (self.elf_header.endian != std.builtin.endian) {
513 // Convert fields to native endianness.
514 shdr = .{
515 .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
516 .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
517 .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
518 .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
519 .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
520 .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
521 .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
522 .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
523 .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
524 .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
525 };
526 }
527
528 // Convert 32-bit header to 64-bit.
481529 return Elf64_Shdr{
482 .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
483 .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
484 .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
485 .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
486 .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
487 .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
488 .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
489 .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
490 .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
491 .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
530 .sh_name = shdr.sh_name,
531 .sh_type = shdr.sh_type,
532 .sh_flags = shdr.sh_flags,
533 .sh_addr = shdr.sh_addr,
534 .sh_offset = shdr.sh_offset,
535 .sh_size = shdr.sh_size,
536 .sh_link = shdr.sh_link,
537 .sh_info = shdr.sh_info,
538 .sh_addralign = shdr.sh_addralign,
539 .sh_entsize = shdr.sh_entsize,
492540 };
493541 }
494
495 var shdr: Elf32_Shdr = undefined;
496 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
497 try preadNoEof(self.file, mem.asBytes(&shdr), offset);
498
499 // ELF endianness does NOT match native endianness.
500 if (self.elf_header.endian != std.builtin.endian) {
501 // Convert fields to native endianness.
502 shdr = .{
503 .sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
504 .sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
505 .sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
506 .sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
507 .sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
508 .sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
509 .sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
510 .sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
511 .sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
512 .sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
513 };
514 }
515
516 // Convert 32-bit header to 64-bit.
517 return Elf64_Shdr{
518 .sh_name = shdr.sh_name,
519 .sh_type = shdr.sh_type,
520 .sh_flags = shdr.sh_flags,
521 .sh_addr = shdr.sh_addr,
522 .sh_offset = shdr.sh_offset,
523 .sh_size = shdr.sh_size,
524 .sh_link = shdr.sh_link,
525 .sh_info = shdr.sh_info,
526 .sh_addralign = shdr.sh_addralign,
527 .sh_entsize = shdr.sh_entsize,
528 };
529 }
530};
542 };
543}
531544
532545pub fn int(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {
533546 if (is_64) {
......@@ -549,28 +562,6 @@ pub fn int32(need_bswap: bool, int_32: anytype, comptime Int64: anytype) Int64 {
549562 }
550563}
551564
552fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void {
553 var i: usize = 0;
554 while (i < buf.len) {
555 const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) {
556 error.SystemResources => return error.SystemResources,
557 error.IsDir => return error.UnableToReadElfFile,
558 error.OperationAborted => return error.UnableToReadElfFile,
559 error.BrokenPipe => return error.UnableToReadElfFile,
560 error.Unseekable => return error.UnableToReadElfFile,
561 error.ConnectionResetByPeer => return error.UnableToReadElfFile,
562 error.ConnectionTimedOut => return error.UnableToReadElfFile,
563 error.InputOutput => return error.FileSystem,
564 error.Unexpected => return error.Unexpected,
565 error.WouldBlock => return error.Unexpected,
566 error.NotOpenForReading => return error.Unexpected,
567 error.AccessDenied => return error.Unexpected,
568 };
569 if (len == 0) return error.UnexpectedEndOfFile;
570 i += len;
571 }
572}
573
574565pub const EI_NIDENT = 16;
575566
576567pub const EI_CLASS = 4;
src/test.zig+2-2
......@@ -1030,8 +1030,8 @@ pub const TestContext = struct {
10301030 var file = try tmp_dir.openFile(bin_name, .{ .read = true });
10311031 defer file.close();
10321032
1033 const header = try std.elf.readHeader(file);
1034 var iterator = header.program_header_iterator(file);
1033 const header = try std.elf.Header.read(&file);
1034 var iterator = header.program_header_iterator(&file);
10351035
10361036 var none_loaded = true;
10371037