dart_elf 1.0.1 copy "dart_elf: ^1.0.1" to clipboard
dart_elf: ^1.0.1 copied to clipboard

A pure Dart library for parsing ELF binaries.

example/dart_elf_example.dart

import 'dart:io';

import 'package:args/args.dart';
import 'package:dart_elf/dart_elf.dart';
import 'package:sprintf/sprintf.dart';

void main(List<String> arguments) {
  final parser = ArgParser()
    ..addFlag("all", abbr: "a", help: "Dump all information", defaultsTo: true)
    ..addFlag("file-header", abbr: "h", help: "Dump the file header")
    ..addFlag("program-headers",
        abbr: "l", help: "Dump the program headers", aliases: ['segments'])
    ..addFlag("section-headers",
        abbr: "S", help: "Dump the section headers", aliases: ['sections'])
    ..addFlag("symbols",
        abbr: "s", help: "Dump the symbol table", aliases: ['syms'])
    ..addFlag("notes", abbr: "n", help: "Dump notes", aliases: [])
    ..addFlag("help", abbr: "H", help: "Display this information")
    ..addFlag("version", abbr: "v", help: "Display the version number")
    ..addOption('file', help: 'ELF file to parser', mandatory: true);
  ArgResults args = parser.parse(arguments);
  if (!args.wasParsed('file') || args['help']) {
    print(parser.usage);
    exit(-1);
  }
  File file = File(args['file']);
  if (!file.existsSync()) {
    print('File not found: ${args['file']}');
    exit(-1);
  }

  ElfReader reader = ElfReader.fromRandomAccessFile(file.openSync());
  bool fileHeader = args['all'] || args['file-header'];
  bool sectionHeaders = args['all'] || args['section-headers'];
  bool programHeaders = args['all'] || args['program-headers'];
  bool symbolTables = args['all'] || args['symbols'];
  bool notes = args['all'] || args['notes'];

  if (fileHeader) {
    _printFileHeader(reader);
  }
  if (sectionHeaders) {
    _printSectionHeaders(reader);
  }
  if (programHeaders) {
    _printProgramHeaders(reader);
  }
  if (programHeaders && reader.header.shnum > 0) {
    _printSectionToSegmentMapping(reader);
  }
  if (sectionHeaders) {
    _printDynamicSections(reader);
    // TODO: Split this into a different option
    _printRelocationSections(reader);
  }
  if (symbolTables) {
    if (reader.symbolTableSection != null) {
      _printSymbolTable(reader, reader.symbolTableSection!);
    }
    if (reader.dynamicSymbolTableSection != null) {
      _printSymbolTable(reader, reader.dynamicSymbolTableSection!);
    }
  }
  if (notes) {
    for (var section in reader.sections) {
      if (section is ElfNoteSection) {
        _printNotes(reader, section);
      }
    }
  }
  exit(0);
}

void _printNotes(ElfReader reader, ElfNoteSection section) {
  print('Displaying notes found in: ${section.name}');
  print('  Owner                Data size 	Description');
  print(sprintf('  %-20.20s %#08x   %s',
      [section.noteName, section.noteSize, section.noteType.name]));
  switch (section.noteType) {
    case ElfNoteType.gnuAbiTag:
      ElfGnuAbiDescriptor d = section.readGnuAbiDescriptor();
      String os = sprintf('Unknown(%d)', [d.os]);
      if (d.os == 0) {
        os = 'Linux';
      }
      print(
          sprintf('    OS: %s, ABI: %d.%d.%d', [os, d.major, d.minor, d.sub]));
      break;
    case ElfNoteType.gnuBuildId:
      print('    Build ID: ${section.readGnuBuildId().text}');
      break;
    case ElfNoteType.gnuProperties:
      // TODO: Dump the actual properties
      print('    Properties: ${section.readHexString()}');
      break;
    case ElfNoteType.gnuHwcap:
      // TODO: Dump the hardware capabilities
      print('    Hardware Cap: ${section.readHexString()}');
      break;
    default:
      print('    ${section.readNote()}');
      break;
  }
  print('');
}

void _printSymbolTable(ElfReader reader, ElfSymbolTableSection section) {
  print(sprintf('Symbol table \'%s\' contains %d entries:',
      [section.name, section.symbols.length]));

  print('   Num:    Value  Size Type         Bind    Vis         Ndx  Name');
  ElfStringTable stringTable = section.name == '.dynsym'
      ? reader.dynamicStringTable!
      : reader.stringTable!;
  for (var i = 0; i < section.symbols.length; i++) {
    var symbol = section.symbols[i];
    print(sprintf('%6d: %08x %5d %-12.12s %-7.7s %-8.8s %6d     %s', [
      i,
      symbol.value,
      symbol.size,
      symbol.type.name,
      symbol.binding.name,
      symbol.visibility.name,
      symbol.shndx,
      stringTable.at(symbol.nindex),
    ]));
  }
  print('');
}

void _printRelocationSections(ElfReader reader) {
  for (var section in reader.sections) {
    if (section is ElfRelocationSection) {
      print(sprintf(
          'Relocation section \'%s\' at offset %#0x contains %d entries:',
          [section.name, section.header.offset, section.entries.length]));
      print('Offset    Info      Type             Value     Name');
      ElfSymbolTableSection? symbolTable = reader.dynamicSymbolTableSection;
      for (ElfRelocation h in section.entries) {
        var tid = _getRelocationType(reader.header, h.info);
        var idx = _getRelocationIndex(reader.header, h.info);
        var type = ElfRelocationType.byArchitecture(reader.header.arch, tid);
        var binding = symbolTable?.symbols[idx];
        var value = binding?.value ?? -1;
        var nindex = binding?.nindex ?? -1;
        var addend = h.addend != null ? '+ ${h.addend!.toRadixString(10)}' : '';
        var name = reader.dynamicStringTable?.at(nindex) ?? '-';
        print(sprintf('%08x  %08x  %-15.15s  %08x  %s %s',
            [h.offset, h.info, type.name, value, name, addend]));
      }
      print('');
    }
  }
}

int _getRelocationType(ElfFileHeader header, int info) {
  return header.word == ElfWordSize.word32Bit ? info & 0xff : info & 0xffffffff;
}

int _getRelocationIndex(ElfFileHeader header, int info) {
  return info >> (header.word == ElfWordSize.word32Bit ? 8 : 32);
}

void _printDynamicSections(ElfReader reader) {
  for (var section in reader.sections) {
    if (section is ElfDynamicSection) {
      int stringTableSize = 0;
      ElfStringTable? stringTable;

      // first try to load the string table for the dynamic section
      for (ElfDynamicEntry entry in section.entries) {
        if (entry.tag == ElfDynamicTag.strsz.id) {
          stringTableSize = entry.value;
        }
        if (entry.tag == ElfDynamicTag.strtab.id) {
          int offset = reader.toFileOffset(entry.value);
          stringTable = ElfStringTable(reader.buffer, offset, stringTableSize);
        }
      }
      print(sprintf('Dynamic section at offset %#x contains %d entries:',
          [section.header.offset, section.entries.length]));
      print('  Tag           Type              Name/Value');
      for (ElfDynamicEntry entry in section.entries) {
        int tag = entry.tag;
        String name = ElfDynamicTag.byId(entry.tag).name;
        String value = '0x${entry.value.toRadixString(16)}';
        if (tag == ElfDynamicTag.needed.id) {
          var name = stringTable?.at(entry.value) ?? '(missing strtab)';
          value = 'Shared library: [$name]';
        }
        if (tag == ElfDynamicTag.strsz.id ||
            tag == ElfDynamicTag.initarraysz.id ||
            tag == ElfDynamicTag.finiarraysz.id ||
            tag == ElfDynamicTag.pltrelsz.id ||
            tag == ElfDynamicTag.relsz.id ||
            tag == ElfDynamicTag.relent.id) {
          value = '${entry.value} (bytes)';
        }
        if (tag == ElfDynamicTag.verneednum.id) {
          value = entry.value.toRadixString(10);
        }
        print(sprintf('%#010x    %-16s  %s', [tag, name, value]));
      }
      print('');
    }
  }
}

void _printFileHeader(ElfReader reader) {
  ElfFileHeader header = reader.header;
  var magic = header.data
      .sublist(0, 16)
      .map((e) => e.toRadixString(16).padLeft(2, '0'))
      .join(' ');
  print('ELF Header:');
  print(' Magic:   $magic');
  print(' Class:                     ${header.word.id}');
  print(' Data:                      ${header.type}');
  print(' Version:                   ${header.version}');
  print(' OS/ABI:                    ${header.abi.description}');
  print(' ABI Version:               ${header.abiVersion}');
  print(' Type:                      ${header.type.description}');
  print(' Machine:                   ${header.arch.description}');
  print(' Version:                   ${header.version}');
  print(' Entry point address:       0x${header.entry.toRadixString(16)}');
  print(' Start of program headers:  ${header.phoff} (bytes into file)');
  print(' Start of section headers:  ${header.shoff} (bytes into file)');
  print(' Flags:                     0x${header.flags.toRadixString(16)}');
  print(' Size of this header:       ${header.data.length} (bytes)');
  print(' Size of program headers:   ${header.phentsize} (bytes)');
  print(' Number of program headers: ${header.phnum}');
  print(' Size of section headers:   ${header.shentsize} (bytes)');
  print(' Number of section headers: ${header.shnum}');
  print(' Header string table index: ${header.shstrndx}');
  print('');
}

void _printSectionHeaders(ElfReader reader) {
  print('Section Headers:');
  print(' [ Nr] Name              Type             Address           Offset');
  print('       Size              EntSize          Flags  Link  Info  Align');
  int count = 0;
  for (var section in reader.sections) {
    print(sprintf(' [%3i] %-16.16s %-16.16s %16.16x %16.16x', [
      count++,
      section.name,
      section.type.name,
      section.header.addr,
      section.header.offset
    ]));
    print(sprintf('       %16.16x %16.16i %10s %2i %2i %12i', [
      section.header.size,
      section.header.entsize,
      ElfSectionHeaderFlags.format(section.header.flags),
      section.header.link,
      section.header.info,
      section.header.align
    ]));
  }
  print('''Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), E (exclude)''');
  print('');
}

void _printProgramHeaders(ElfReader reader) {
  print('Program Headers:');
  print('  Type             Offset           VirtAddr           PhysAddr');
  print('                   FileSiz          MemSiz             Flags  Align');
  for (var segment in reader.segments) {
    print(sprintf('  %-16s %0#16x %0#16x %0#16x', [
      segment.header.type.name,
      segment.header.offset,
      segment.header.vaddr,
      segment.header.paddr
    ]));
    print(sprintf('  %-16s %0#16x %0#16x %-6s %0#x', [
      '',
      segment.header.fsize,
      segment.header.msize,
      ElfSegmentPermissions.format(segment.header.flags),
      segment.header.align,
    ]));
    if (segment.header.type == ElfSegmentType.interp) {
      var val = String.fromCharCodes(segment.data().takeWhile((v) => v != 0));
      print('    [Requesting program interpreter: $val]');
    }
  }
  print('');
}

void _printSectionToSegmentMapping(ElfReader reader) {
  print('Section to Segment mapping:');
  print('  Segment Sections...');
  int count = 0;
  for (var segment in reader.segments) {
    List<String> names = [];
    for (var section in reader.sections) {
      if (_sectionInSegment(section, segment) && section.name.isNotEmpty) {
        names.add(section.name);
      }
    }
    print(sprintf('   %3d   %s', [count++, names.join(' ')]));
  }
  print('');
}

bool _sectionInSegment(ElfSection section, ElfSegment segment) {
  ElfSectionType sectionType = section.type;

  // TODO: Make this align with ELF internal
  if (sectionType != ElfSectionType.nobits) {
    if (section.header.offset >= segment.header.offset &&
        section.header.offset - segment.header.offset <=
            segment.header.fsize - 1) {
      return true;
    }
  }

  return false;
}
0
likes
140
pub points
7%
popularity

Publisher

verified publisherlibdbm.com

A pure Dart library for parsing ELF binaries.

Repository (GitHub)
View/report issues

Documentation

API reference

License

Apache-2.0 (license)

Dependencies

args, sprintf

More

Packages that depend on dart_elf