Merge pull request #258 from haberman/better-tests

New testing infrastructure, and fixes for several ELF/DWARF bugs
diff --git a/src/bloaty.cc b/src/bloaty.cc
index 78147ea..62fcbb3 100644
--- a/src/bloaty.cc
+++ b/src/bloaty.cc
@@ -65,6 +65,7 @@
 // otherwise.  We would make this thread_local but that's not supported on OS X
 // right now.
 int verbose_level = 0;
+ShowDomain show = ShowDomain::kShowBoth;
 
 struct DataSourceDefinition {
   DataSource number;
@@ -1062,13 +1063,13 @@
 uint64_t debug_fileoff = -1;
 
 bool RangeSink::ContainsVerboseVMAddr(uint64_t vmaddr, uint64_t vmsize) {
-  return options_.verbose_level() > 2 ||
+  return options_.verbose_level() > 1 ||
          (options_.has_debug_vmaddr() && options_.debug_vmaddr() >= vmaddr &&
           options_.debug_vmaddr() < (vmaddr + vmsize));
 }
 
 bool RangeSink::ContainsVerboseFileOffset(uint64_t fileoff, uint64_t filesize) {
-  return options_.verbose_level() > 2 ||
+  return options_.verbose_level() > 1 ||
          (options_.has_debug_fileoff() && options_.debug_fileoff() >= fileoff &&
           options_.debug_fileoff() < (fileoff + filesize));
 }
@@ -1184,7 +1185,7 @@
         WARN("File range ($0, $1) for label $2 extends beyond base map",
              file_offset, file_range.size(), label);
       }
-    } else if (verbose_level > 2) {
+    } else if (verbose_level > 1) {
       printf("No label found for vmaddr %" PRIx64 "\n", label_from_vmaddr);
     }
   }
@@ -1214,7 +1215,7 @@
         WARN("File range ($0, $1) for label $2 extends beyond base map",
              file_offset, file_range.size(), label);
       }
-    } else if (verbose_level > 2) {
+    } else if (verbose_level > 1) {
       printf("No label found for file range [%" PRIx64 ", %zx]\n",
              from_file_offset, from_file_range.size());
     }
@@ -1238,11 +1239,11 @@
       bool ok = pair.first->vm_map.AddRangeWithTranslation(
           addr, size, label, translator_->vm_map, verbose,
           &pair.first->file_map);
-      if (!ok && verbose_level > 0) {
+      if (!ok && verbose_level > 1) {
         WARN("VM range ($0, $1) for label $2 extends beyond base map", addr,
              size, label);
       }
-    } else if (verbose_level > 2) {
+    } else if (verbose_level > 1) {
       printf("No label found for vmaddr %" PRIx64 "\n", label_from_vmaddr);
     }
   }
@@ -1765,11 +1766,16 @@
   (void)filesize;
   assert(filesize == file->file_data().data().size());
 
-  if (verbose_level > 0) {
-    printf("FILE MAP:\n");
-    maps.PrintFileMaps();
-    printf("VM MAP:\n");
-    maps.PrintVMMaps();
+  if (verbose_level > 0 || options_.dump_raw_map()) {
+    printf("Maps for %s:\n\n", filename.c_str());
+    if (show != ShowDomain::kShowVM) {
+      printf("FILE MAP:\n");
+      maps.PrintFileMaps();
+    }
+    if (show != ShowDomain::kShowFile) {
+      printf("VM MAP:\n");
+      maps.PrintVMMaps();
+    }
   }
 }
 
@@ -2071,6 +2077,8 @@
       output_options->output_format = OutputFormat::kCSV;
     } else if (args.TryParseFlag("--tsv")) {
       output_options->output_format = OutputFormat::kTSV;
+    } else if (args.TryParseFlag("--raw-map")) {
+      options->set_dump_raw_map(true);
     } else if (args.TryParseOption("-c", &option)) {
       std::ifstream input_file(std::string(option), std::ios::in);
       if (!input_file.is_open()) {
@@ -2119,11 +2127,11 @@
     } else if (args.TryParseOption("--domain", &option)) {
       has_domain = true;
       if (option == "vm") {
-        output_options->show = ShowDomain::kShowVM;
+        show = output_options->show = ShowDomain::kShowVM;
       } else if (option == "file") {
-        output_options->show = ShowDomain::kShowFile;
+        show = output_options->show = ShowDomain::kShowFile;
       } else if (option == "both") {
-        output_options->show = ShowDomain::kShowBoth;
+        show = output_options->show = ShowDomain::kShowBoth;
       } else {
         THROWF("unknown value for --domain: $0", option);
       }
diff --git a/src/bloaty.proto b/src/bloaty.proto
index 59db641..1679328 100644
--- a/src/bloaty.proto
+++ b/src/bloaty.proto
@@ -72,6 +72,9 @@
 
   // Regex with which to filter names in the data sources.
   optional string source_filter = 13;
+
+  // Dump raw memory map instead of printing normal output.
+  optional bool dump_raw_map = 14;
 }
 
 // A custom data source allows users to create their own label space by
diff --git a/src/dwarf.cc b/src/dwarf.cc
index b1d0bec..3fc8446 100644
--- a/src/dwarf.cc
+++ b/src/dwarf.cc
@@ -184,6 +184,16 @@
     }
   }
 
+  uint64_t GetMaxAddress() const {
+    if (address_size_ == 8) {
+      return UINT64_MAX;
+    } else if (address_size_ == 4) {
+      return UINT32_MAX;
+    } else {
+      BLOATY_UNREACHABLE();
+    }
+  }
+
   // Reads an "initial length" as specified in many DWARF headers.  This
   // contains either a 32-bit or a 64-bit length, and signals whether we are
   // using the 32-bit or 64-bit DWARF format (so it sets dwarf64 appropriately).
@@ -1081,16 +1091,20 @@
 void ReadRangeList(const DIEReader& die_reader, uint64_t low_pc,
                    string_view name, RangeSink* sink, string_view* data) {
   std::string name_str(name);
+  uint64_t max_address = die_reader.unit_sizes().GetMaxAddress();
   while (true) {
     uint64_t start, end;
     start = die_reader.unit_sizes().ReadAddress(data);
     end = die_reader.unit_sizes().ReadAddress(data);
     if (start == 0 && end == 0) {
       return;
+    } else if (start == max_address) {
+      low_pc = end;
+    } else {
+      uint64_t size = end - start;
+      sink->AddVMRangeIgnoreDuplicate("dwarf_rangelist", low_pc + start, size,
+                                      name_str);
     }
-    uint64_t size = end - start;
-    sink->AddVMRangeIgnoreDuplicate("dwarf_rangelist", low_pc + start, size,
-                                    name_str);
   }
 }
 
diff --git a/src/elf.cc b/src/elf.cc
index eb0e263..355210b 100644
--- a/src/elf.cc
+++ b/src/elf.cc
@@ -1065,6 +1065,7 @@
                            contents);
           } else if (report_by == kReportBySectionName) {
             sink->AddRange("elf_section", name, full_addr, vmsize, contents);
+            sink->AddFileRange("elf_section_header", name, section.range());
           } else if (report_by == kReportByEscapedSectionName) {
             if (!sink->IsBaseMap()) {
               sink->AddFileRangeForFileRange("elf_section", contents,
@@ -1091,56 +1092,79 @@
   kReportByEscapedSegmentName,
 };
 
+std::string GetSegmentName(const ElfFile::Segment& segment, Elf64_Xword i,
+                           ReportSegmentsBy report_by) {
+  const auto& header = segment.header();
+
+  // Include the segment index in the label, to support embedded.
+  //
+  // Including the index in the segment label differentiates
+  // segments with the same access control (e.g. RWX vs RW). In
+  // ELF files built for embedded microcontroller projects, a
+  // segment is used for each distinct type of memory. In simple
+  // cases, there is a segment for the flash (which will store
+  // code and read-only data) and a segment for RAM (which
+  // usually stores globals, stacks, and maybe a heap). In more
+  // involved projects, there may be special segments for faster
+  // RAM (e.g. core coupled RAM or CCRAM), or there may even be
+  // memory overlays to support manual paging of code from flash
+  // (which may be slow) into RAM.
+  std::string name(absl::StrCat("LOAD #", i, " ["));
+
+  if (header.p_flags & PF_R) {
+    name += 'R';
+  }
+
+  if (header.p_flags & PF_W) {
+    name += 'W';
+  }
+
+  if (header.p_flags & PF_X) {
+    name += 'X';
+  }
+
+  name += ']';
+
+  if (report_by == kReportByEscapedSegmentName) {
+    return absl::StrCat("[", name, "]");
+  } else {
+    return name;
+  }
+}
+
 static void DoReadELFSegments(RangeSink* sink, ReportSegmentsBy report_by) {
+  if (!sink->IsBaseMap()) {
+    ForEachElf(sink->input_file(), sink,
+               [=](const ElfFile& elf, string_view /*filename*/,
+                   uint32_t /*index_base*/) {
+                 for (Elf64_Xword i = 0; i < elf.header().e_phnum; i++) {
+                   ElfFile::Segment segment;
+                   elf.ReadSegment(i, &segment);
+                   std::string name = GetSegmentName(segment, i, report_by);
+
+                   sink->AddFileRange("elf_segment_header", name,
+                                      segment.range());
+                 }
+               });
+  }
+
   ForEachElf(sink->input_file(), sink,
              [=](const ElfFile& elf, string_view /*filename*/,
                  uint32_t /*index_base*/) {
                for (Elf64_Xword i = 0; i < elf.header().e_phnum; i++) {
                  ElfFile::Segment segment;
                  elf.ReadSegment(i, &segment);
-                 const auto& header = segment.header();
+                 std::string name = GetSegmentName(segment, i, report_by);
 
-                 if (header.p_type != PT_LOAD) {
+                 if (segment.header().p_type != PT_LOAD) {
                    continue;
                  }
 
-                 // Include the segment index in the label, to support embedded.
-                 //
-                 // Including the index in the segment label differentiates
-                 // segments with the same access control (e.g. RWX vs RW). In
-                 // ELF files built for embedded microcontroller projects, a
-                 // segment is used for each distinct type of memory. In simple
-                 // cases, there is a segment for the flash (which will store
-                 // code and read-only data) and a segment for RAM (which
-                 // usually stores globals, stacks, and maybe a heap). In more
-                 // involved projects, there may be special segments for faster
-                 // RAM (e.g. core coupled RAM or CCRAM), or there may even be
-                 // memory overlays to support manual paging of code from flash
-                 // (which may be slow) into RAM.
-                 std::string name(absl::StrCat("LOAD #", i, " ["));
-
-                 if (header.p_flags & PF_R) {
-                   name += 'R';
-                 }
-
-                 if (header.p_flags & PF_W) {
-                   name += 'W';
-                 }
-
-                 if (header.p_flags & PF_X) {
-                   name += 'X';
-                 }
-
-                 name += ']';
-
-                 if (report_by == kReportByEscapedSegmentName) {
-                   name = absl::StrCat("[", name, "]");
-                 }
-
-                 sink->AddRange("elf_segment", name, header.p_vaddr,
-                                header.p_memsz, segment.contents());
+                 sink->AddRange("elf_segment", name, segment.header().p_vaddr,
+                                segment.header().p_memsz, segment.contents());
                }
              });
+
   ForEachElf(sink->input_file(), sink,
              [=](const ElfFile& elf, string_view /*filename*/,
                  uint32_t /*index_base*/) {
@@ -1148,10 +1172,10 @@
                  ElfFile::Segment segment;
                  elf.ReadSegment(i, &segment);
                  const auto& header = segment.header();
-                 if(header.p_type != PT_TLS) continue;
+                 if (header.p_type != PT_TLS) continue;
                  std::string name = "TLS";
-                 sink->AddRange("elf_segment", "TLS", header.p_vaddr, header.p_memsz,
-                                segment.contents());
+                 sink->AddRange("elf_segment", "TLS", header.p_vaddr,
+                                header.p_memsz, segment.contents());
                }
              });
 }
@@ -1276,6 +1300,9 @@
 
   void ProcessFile(const std::vector<RangeSink*>& sinks) const override {
     for (auto sink : sinks) {
+      if (verbose_level > 1) {
+        printf("Scanning source %d\n", (int)sink->data_source());
+      }
       switch (sink->data_source()) {
         case DataSource::kSegments:
           ReadELFSegments(sink);
diff --git a/src/main.cc b/src/main.cc
index f2c5ec2..c6c04f8 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -40,6 +40,8 @@
     return 1;
   }
 
-  output.Print(output_options, &std::cout);
+  if (!options.dump_raw_map()) {
+    output.Print(output_options, &std::cout);
+  }
   return 0;
 }
diff --git a/src/range_map.cc b/src/range_map.cc
index 08e1f18..71e4f0b 100644
--- a/src/range_map.cc
+++ b/src/range_map.cc
@@ -172,7 +172,7 @@
       iter->second.size = new_size;
       CheckConsistency(iter);
     }
-  } else if (verbose_level > 1) {
+  } else if (verbose_level > 2) {
     printf("  skipping existing mapping (%s)\n",
            EntryDebugString(iter).c_str());
   }
diff --git a/tests/dwarf/range_lists/base-addr-selection.test b/tests/dwarf/range_lists/base-addr-selection.test
new file mode 100644
index 0000000..8efc530
--- /dev/null
+++ b/tests/dwarf/range_lists/base-addr-selection.test
@@ -0,0 +1,97 @@
+# Test for range list that contains a "base address selection" entry.
+# When the start address is the max address (eg. UINT64_MAX), it signals
+# a special kind of entry where the end address signifies the new base
+# address.
+
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_DYN
+  Machine:         EM_X86_64
+  Entry:           0x1040
+ProgramHeaders:
+  - Type:            PT_LOAD
+    Flags:           [ PF_X, PF_R ]
+    FirstSec:        .text
+    LastSec:         .text
+    VAddr:           0x1000
+    Align:           0x1000
+Sections:
+  - Name:            .note.gnu.build-id
+    Type:            SHT_NOTE
+    Notes:
+      - Name:            GNU
+        Desc:            6CF422D909772A0FB5400518A689D9F15F14BF57
+        Type:            0x3  # NT_GNU_BUILD_ID
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1000
+    AddressAlign:    0x10
+    Content:         31ED4989D15E4889E24883E4F050544C8D053A010000488D0DD3000000488D3DC1000000FF15762F0000F40F1F440000488D3DB12F0000488D05AA2F00004839F87415488B054E2F00004885C07409FFE00F1F8000000000C30F1F8000000000488D3D812F0000488D357A2F00004829FE4889F048C1EE3F48C1F8034801C648D1FE7414488B05252F00004885C07408FFE0660F1F440000C30F1F8000000000803D412F000000752F5548833D062F0000004889E5740C488B3D222F0000E82DFFFFFFE868FFFFFFC605192F0000015DC30F1F8000000000C30F1F8000000000E97BFFFFFF554889E5B8010000005DC341574C8D3DDF2C000041564989D641554989F541544189FC55488D2DD02C0000534C29FD4883EC08E8A3FEFFFF48C1FD03741B31DB0F1F004C89F24C89EE4489E741FF14DF4883C3014839DD75EA4883C4085B5D415C415D415E415FC30F1F00C3
+...
+
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_DYN
+  Machine:         EM_X86_64
+  Entry:           0x1040
+Sections:
+  - Name:            .note.gnu.build-id
+    Type:            SHT_NOTE
+    Notes:
+      - Name:            GNU
+        Desc:            6CF422D909772A0FB5400518A689D9F15F14BF57
+        Type:            0x3  # NT_GNU_BUILD_ID
+DWARF:
+  debug_str:
+    - test.c
+  debug_abbrev:
+    - ID:              0
+      Table:
+        - Code:            0x1
+          Tag:             DW_TAG_compile_unit
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_ranges
+              Form:            DW_FORM_sec_offset
+            - Attribute:       DW_AT_low_pc
+              Form:            DW_FORM_addr
+  debug_ranges:
+    - Offset:          0x0
+      AddrSize:        0x8
+      Entries:
+        # This is the "base address selection" entry!
+        - LowOffset:       0xffffffffffffffff   # UINT64_MAX
+          HighOffset:      0x1020
+        # Now these addresses are relative to 0x1020
+        - LowOffset:       0x22
+          HighOffset:      0x27
+  debug_info:
+    - Version:         4
+      AbbrevTableID:   0
+      AbbrOffset:      0x0
+      AddrSize:        8
+      Entries:
+        - AbbrCode:        0x1
+          Values:
+            - Value:           0x0
+            - Value:           0x0
+            - Value:           0x0
+        - AbbrCode:        0x0
+...
+
+$ bloaty 1 --debug-file=2 -d compileunits --raw-map --domain=vm
+Maps for 1:
+
+VM MAP:
+0000-1000	       4096		[-- Nothing mapped --]
+1000-1042	         66		[section .text]
+1042-1047	          5		test.c
+1047-1151	        266		[section .text]
+
diff --git a/tests/elf/sections/empty-bin-64.test b/tests/elf/sections/empty-bin-64.test
new file mode 100644
index 0000000..9df7eb3
--- /dev/null
+++ b/tests/elf/sections/empty-bin-64.test
@@ -0,0 +1,70 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x1
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x1
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x1
+  - Name:            .comment
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_MERGE, SHF_STRINGS ]
+    AddressAlign:    0x1
+    EntSize:         0x1
+    Content:         004743433A202844656269616E2031302E322E312D362B6275696C6432292031302E322E3120323032313031313000
+  - Name:            .note.GNU-stack
+    Type:            SHT_PROGBITS
+    AddressAlign:    0x1
+Symbols:
+  - Name:            'null'
+    Type:            STT_FILE
+    Index:           SHN_ABS
+  - Name:            .text
+    Type:            STT_SECTION
+    Section:         .text
+  - Name:            .data
+    Type:            STT_SECTION
+    Section:         .data
+  - Name:            .bss
+    Type:            STT_SECTION
+    Section:         .bss
+  - Name:            .note.GNU-stack
+    Type:            STT_SECTION
+    Section:         .note.GNU-stack
+  - Name:            .comment
+    Type:            STT_SECTION
+    Section:         .comment
+...
+
+$ bloaty --raw-map 1
+Maps for 1:
+
+FILE MAP:
+000-040	         64		[ELF Headers]
+040-070	         48		.comment
+070-118	        168		.symtab
+118-148	         48		.strtab
+148-190	         72		.shstrtab
+190-1d0	         64		[ELF Headers]
+1d0-210	         64		.text
+210-250	         64		.data
+250-290	         64		.bss
+290-2d0	         64		.comment
+2d0-310	         64		.note.GNU-stack
+310-350	         64		.symtab
+350-390	         64		.strtab
+390-3d0	         64		.shstrtab
+
+VM MAP:
+
diff --git a/tests/elf/sections/empty-obj-64.test b/tests/elf/sections/empty-obj-64.test
new file mode 100644
index 0000000..fa94d1e
--- /dev/null
+++ b/tests/elf/sections/empty-obj-64.test
@@ -0,0 +1,70 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x1
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x1
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x1
+  - Name:            .comment
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_MERGE, SHF_STRINGS ]
+    AddressAlign:    0x1
+    EntSize:         0x1
+    Content:         004743433A202844656269616E2031302E322E312D362B6275696C6432292031302E322E3120323032313031313000
+  - Name:            .note.GNU-stack
+    Type:            SHT_PROGBITS
+    AddressAlign:    0x1
+Symbols:
+  - Name:            'null'
+    Type:            STT_FILE
+    Index:           SHN_ABS
+  - Name:            .text
+    Type:            STT_SECTION
+    Section:         .text
+  - Name:            .data
+    Type:            STT_SECTION
+    Section:         .data
+  - Name:            .bss
+    Type:            STT_SECTION
+    Section:         .bss
+  - Name:            .note.GNU-stack
+    Type:            STT_SECTION
+    Section:         .note.GNU-stack
+  - Name:            .comment
+    Type:            STT_SECTION
+    Section:         .comment
+...
+
+$ bloaty --raw-map 1
+Maps for 1:
+
+FILE MAP:
+000-040	         64		[ELF Headers]
+040-070	         48		.comment
+070-118	        168		.symtab
+118-148	         48		.strtab
+148-190	         72		.shstrtab
+190-1d0	         64		[ELF Headers]
+1d0-210	         64		.text
+210-250	         64		.data
+250-290	         64		.bss
+290-2d0	         64		.comment
+2d0-310	         64		.note.GNU-stack
+310-350	         64		.symtab
+350-390	         64		.strtab
+390-3d0	         64		.shstrtab
+
+VM MAP:
+
diff --git a/tests/elf/sections/musl-static-bin.test b/tests/elf/sections/musl-static-bin.test
new file mode 100644
index 0000000..06c46ce
--- /dev/null
+++ b/tests/elf/sections/musl-static-bin.test
@@ -0,0 +1,591 @@
+# A fully statically-linked binary linked with musl.
+# Program text was:
+#
+#   char x[123] = {0};
+#   char y[456] = {1, 2, 3};
+#   const char z[789] = {1, 2, 3};
+#
+#   int main() {
+#     volatile long addr = (long)&x;
+#     addr = (long)&y;
+#     addr = (long)&z;
+#     return 5;
+#   }
+#
+# This example uses musl because it can produce fully statically linked
+# binaries of reasonable size.
+#
+# This binary does not successfully run due to bugs in obj2yaml
+# (or possibly yaml2obj). The binary did not fully round-trip successfully.
+
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+  Entry:           0x000000000040105A
+ProgramHeaders:
+  - Type:            PT_LOAD
+    Flags:           [ PF_R ]
+    VAddr:           0x0000000000400000
+    Align:           0x0000000000001000
+  - Type:            PT_LOAD
+    Flags:           [ PF_X, PF_R ]
+    FirstSec:        .init
+    LastSec:         .fini
+    VAddr:           0x0000000000401000
+    Align:           0x0000000000001000
+  - Type:            PT_LOAD
+    Flags:           [ PF_R ]
+    FirstSec:        .rodata
+    LastSec:         .eh_frame
+    VAddr:           0x0000000000402000
+    Align:           0x0000000000001000
+  - Type:            PT_LOAD
+    Flags:           [ PF_W, PF_R ]
+    FirstSec:        .init_array
+    LastSec:         .bss
+    VAddr:           0x0000000000403FE8
+    FileSize:        0x228
+    MemSize:         0x510
+    Align:           0x0000000000001000
+  - Type:            PT_GNU_STACK
+    Flags:           [ PF_W, PF_R ]
+    Align:           0x0000000000000010
+  - Type:            PT_GNU_RELRO
+    Flags:           [ PF_R ]
+    FirstSec:        .init_array
+    LastSec:         .got
+    VAddr:           0x0000000000403FE8
+Sections:
+  - Name:            .init
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x0000000000401000
+    AddressAlign:    0x0000000000000001
+    Content:         5058C3
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x0000000000401010
+    AddressAlign:    0x0000000000000010
+    Content:         5589FDE858040000E86304000031C0E84C04000089EFE8E50700000F1F440000488D050932000048894424F8488D05FD2F000048894424F8488D05B10F000048894424F8B805000000C34831ED4889E7488D3599EFBFFF4883E4F0E800000000488B37488D57084531C949C7C06918400048C7C10010400048C7C730104000E99C030000662E0F1F8400000000006690488D3D69310000488D05623100004839F8741548C7C0000000004885C07409FFE00F1F8000000000C30F1F8000000000488D3D39310000488D35323100004829FE4889F048C1EE3F48C1F8034801C648D1FE741448C7C0000000004885C07408FFE0660F1F440000C30F1F8000000000803D0931000000752F5548833DD62E0000004889E5740C488B3DF22E0000E8CDEEBFFFE868FFFFFFC605E1300000015DC30F1F8000000000C30F1F8000000000E97BFFFFFF662E0F1F84000000000090C3662E0F1F8400000000000F1F440000C3662E0F1F8400000000000F1F4400004881EC580100004889FA31C0B9260000004C8D44242048833A004C89C7F348AB48C7C0484340004889100F84D801000031C0660F1F4400004889C14883C00148833CC20075F2488D04CD100000004801D0488B104889050D3100004883C0084885D20F84B00100000F1F8400000000004883FA257708488B0848894CD420488B50084883C0104885D275E5488B8C24A0000000488B842420010000488B54245048890DB13000004885C074074889058D300000488915D63000004885F60F840D01000048C7C0C842400048C7C2D04240004889304889320FB60684C0741C4883C601660F1F4400003C2F75034889320FB6064883C60184C075EE4C89C7E83E030000488BBC24E8000000E8E1FEFFFF488B84248000000048394424780F847E000000660FEFC04889E7B80700000031D248C744241000000000BE030000000F110424C744241002000000C7442408010000000F0585C07901F44889FA4C8D44241841B902000000BE02800000488D3D22100000F6420620740B4C89C80F054885C07901F44883C2084939D075E6C605CE2F0000014881C458010000C30F1F4000488B84249000000048398424880000000F856CFFFFFF4883BC24D8000000000F855DFFFFFFEBCD660F1F840000000000488BB424180100004885F60F85E2FEFFFF48C7C2C8424000488D05AF0F000048890248C7C2D0424000488902E9F9FEFFFF0F1F8000000000B808000000E93CFEFFFF660F1F44000048C705352F000000000000E98BFEFFFF0F1F84000000000055534883EC08E845FCFFFF48C7C3E83F400048C7C5F03F40004839EB730D6690FF134883C3084839EB72F54883C4085B5DC3662E0F1F8400000000000F1F400041554863C641544C8D6CC2084989D4554889C5534889FB4883EC08E8A0FFFFFF89EF4C89EA4C89E6FFD389C7E8EFFBFFFF662E0F1F8400000000000F1F44000041554863C64989FD4154488D7CC2084989D455488B324889C5E832FDFFFF4C89E289EE4C89EF488D0593FFFFFF5D415C415DFFE0662E0F1F8400000000006690C3662E0F1F8400000000000F1F44000055534883EC0848C7C3F83F400048C7C5F03F40004839EB76140F1F80000000004883EB0831C0FF134839EB77F34883C40831C05B5DE9AF030000660F1F440000534889FB48893FE88D03000085C078547507C605072E000001C7433802000000B8DA000000488D3D043000000F05894330488D05202E0000488983A8000000488D838800000048898388000000488B05AC2D000048895B104889432031C048895B185BC3B8FFFFFFFF5BC30F1F44000041554989FD415455534883EC08488B05B42D00004C8B25B52D0000488B1D9E2D0000488D840738FFFFFF49F7DC4921C44885DB7436488D6F080F1F80000000004C89E0482B4328488B73084C89E748894500488B53104883C508482B7B28E894020000488B1B4885DB75D5488B05662D0000498945004C89E04D896C24084883C4085B5D415C415DC30F1F800000000055534883EC08488B4F284C8B5F184885C90F8411020000448B0D2E2C0000488B7F204C89D831DB31F64531C0BD00008000EB230F1F44000083FA020F84AF01000083FA070F856A0100004989C04801F84883E90174178B1083FA0675DB4C89DE482B70104801F84883E90175E984DB740744890DD42B00004D85C00F84A7010000498B402049037010488D0D102D0000498B50284889350D2D00004889050E2D0000498B403048C705972C000001000000488905082D000048890D712C00004801D6488D48FF48F7DE4821CE4801D6488D90DF000000488935DB2C0000488935E42C00004883F807771548C705CB2C000008000000BAE7000000B8080000004801D6488905372C0000488D3DD02C00004883E6F84889351D2C00004881FE50010000762141BA220000004531C9B80900000031FF49C7C0FFFFFFFFBA030000000F054889C7E826FEFFFF4889004889C74889C3E84101000085C00F88BC0000000F84AA000000C7433802000000B8DA000000488D3DB72D00000F05894330488D05D32B0000488983A8000000488D838800000048898388000000488B055F2B000048895B104889432048895B184883C4085B5DC381FA51E574640F858DFEFFFF488B50284589CA4C39D20F867DFEFFFF4881FA00008000BB01000000480F47D54989D1E965FEFFFF0F1F84000000000048C7C2000000004885D20F844DFEFFFF4889D6482B7010E941FEFFFF0F1F4000C605092B000001E94AFFFFFFF44883C4085B5DC30F1F4000488B15892B0000488B35722B0000488B05832B0000E97DFEFFFF662E0F1F8400000000000F1F40004863FFB8E70000000F05BA3C000000904889D00F05EBF94889F84883FA087214F7C707000000740CA448FFCAF7C70700000075F44889D148C1E903F348A583E2077405A4FFCA75FBC34889FEBF02100000B89E0000000F05C3
+  - Name:            .fini
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x0000000000401869
+    AddressAlign:    0x0000000000000001
+    Content:         5058C3
+  - Name:            .rodata
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x0000000000402000
+    AddressAlign:    0x0000000000000020
+    Content:         0102030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002F6465762F6E756C6C00
+  - Name:            .eh_frame
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x0000000000402320
+    AddressAlign:    0x0000000000000008
+    Content:         1400000000000000017A5200017810011B0C070890010000100000001C000000F0ECFFFF2A0000000000000000000000
+  - Name:            .init_array
+    Type:            SHT_INIT_ARRAY
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x0000000000403FE8
+    AddressAlign:    0x0000000000000008
+    EntSize:         0x0000000000000008
+    Content:         '5011400000000000'
+  - Name:            .fini_array
+    Type:            SHT_FINI_ARRAY
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x0000000000403FF0
+    AddressAlign:    0x0000000000000008
+    EntSize:         0x0000000000000008
+    Content:         '1011400000000000'
+  - Name:            .got
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x0000000000403FF8
+    AddressAlign:    0x0000000000000008
+    Content:         '0000000000000000'
+  - Name:            .got.plt
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x0000000000404000
+    AddressAlign:    0x0000000000000008
+    EntSize:         0x0000000000000008
+    Content:         '000000000000000000000000000000000000000000000000'
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x0000000000404020
+    AddressAlign:    0x0000000000000020
+    Content:         '20404000000000000000000000000000000000000000000000000000000000000102030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000200'
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x0000000000404220
+    AddressAlign:    0x0000000000000020
+    Size:            0x00000000000002D8
+  - Name:            .comment
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_MERGE, SHF_STRINGS ]
+    AddressAlign:    0x0000000000000001
+    EntSize:         0x0000000000000001
+    Content:         4743433A202844656269616E2031302E322E312D33292031302E322E31203230323031323234004743433A202844656269616E2031302E322E312D362B6275696C6432292031302E322E3120323032313031313000
+Symbols:
+  - Name:            dummy
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x0000000000401470
+    Size:            0x0000000000000001
+  - Name:            libc_exit_fini
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x0000000000401480
+    Size:            0x000000000000003A
+  - Name:            deregister_tm_clones
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x00000000004010A0
+  - Name:            register_tm_clones
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x00000000004010D0
+  - Name:            __do_global_dtors_aux
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x0000000000401110
+  - Name:            completed.0
+    Type:            STT_OBJECT
+    Section:         .bss
+    Value:           0x0000000000404220
+    Size:            0x0000000000000001
+  - Name:            __do_global_dtors_aux_fini_array_entry
+    Type:            STT_OBJECT
+    Section:         .fini_array
+    Value:           0x0000000000403FF0
+  - Name:            frame_dummy
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x0000000000401150
+  - Name:            __frame_dummy_init_array_entry
+    Type:            STT_OBJECT
+    Section:         .init_array
+    Value:           0x0000000000403FE8
+  - Name:            'dummy (1)'
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x0000000000401160
+    Size:            0x0000000000000001
+  - Name:            dummy1
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x0000000000401170
+    Size:            0x0000000000000001
+  - Name:            libc_start_init
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x00000000004013B0
+    Size:            0x0000000000000032
+  - Name:            libc_start_main_stage2
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x00000000004013F0
+    Size:            0x0000000000000031
+  - Name:            static_init_tls
+    Type:            STT_FUNC
+    Section:         .text
+    Value:           0x00000000004015C0
+    Size:            0x0000000000000242
+  - Name:            main_tls
+    Type:            STT_OBJECT
+    Section:         .bss
+    Value:           0x0000000000404360
+    Size:            0x0000000000000030
+  - Name:            builtin_tls
+    Type:            STT_OBJECT
+    Section:         .bss
+    Value:           0x00000000004043A0
+    Size:            0x0000000000000150
+  - Name:            __FRAME_END__
+    Type:            STT_OBJECT
+    Section:         .eh_frame
+    Value:           0x000000000040234C
+  - Name:            __fini_array_end
+    Section:         .fini_array
+    Value:           0x0000000000403FF8
+  - Name:            __fini_array_start
+    Section:         .fini_array
+    Value:           0x0000000000403FF0
+  - Name:            __init_array_end
+    Section:         .init_array
+    Value:           0x0000000000403FF0
+  - Name:            _GLOBAL_OFFSET_TABLE_
+    Type:            STT_OBJECT
+    Section:         .got.plt
+    Value:           0x0000000000404000
+  - Name:            __init_array_start
+    Section:         .init_array
+    Value:           0x0000000000403FE8
+  - Name:            .init
+    Type:            STT_SECTION
+    Section:         .init
+    Value:           0x0000000000401000
+  - Name:            .text
+    Type:            STT_SECTION
+    Section:         .text
+    Value:           0x0000000000401010
+  - Name:            .fini
+    Type:            STT_SECTION
+    Section:         .fini
+    Value:           0x0000000000401869
+  - Name:            .rodata
+    Type:            STT_SECTION
+    Section:         .rodata
+    Value:           0x0000000000402000
+  - Name:            .eh_frame
+    Type:            STT_SECTION
+    Section:         .eh_frame
+    Value:           0x0000000000402320
+  - Name:            .init_array
+    Type:            STT_SECTION
+    Section:         .init_array
+    Value:           0x0000000000403FE8
+  - Name:            .fini_array
+    Type:            STT_SECTION
+    Section:         .fini_array
+    Value:           0x0000000000403FF0
+  - Name:            .got
+    Type:            STT_SECTION
+    Section:         .got
+    Value:           0x0000000000403FF8
+  - Name:            .got.plt
+    Type:            STT_SECTION
+    Section:         .got.plt
+    Value:           0x0000000000404000
+  - Name:            .data
+    Type:            STT_SECTION
+    Section:         .data
+    Value:           0x0000000000404020
+  - Name:            .bss
+    Type:            STT_SECTION
+    Section:         .bss
+    Value:           0x0000000000404220
+  - Name:            .comment
+    Type:            STT_SECTION
+    Section:         .comment
+  - Name:            __thread_list_lock
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Value:           0x00000000004044F0
+    Size:            0x0000000000000004
+    Other:           [ STV_HIDDEN ]
+  - Name:            __init_libc
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000401180
+    Size:            0x0000000000000228
+    Other:           [ STV_HIDDEN ]
+  - Name:            x
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000404240
+    Size:            0x000000000000007B
+  - Name:            __hwcap
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Value:           0x00000000004042D8
+    Size:            0x0000000000000008
+    Other:           [ STV_HIDDEN ]
+  - Name:            memcpy
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000401827
+  - Name:            __TMC_END__
+    Type:            STT_OBJECT
+    Section:         .data
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000404210
+    Other:           [ STV_HIDDEN ]
+  - Name:            __libc
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Value:           0x00000000004042E0
+    Size:            0x0000000000000068
+    Other:           [ STV_HIDDEN ]
+  - Name:            __dso_handle
+    Type:            STT_OBJECT
+    Section:         .data
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000404020
+    Other:           [ STV_HIDDEN ]
+  - Name:            __set_thread_area
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000401859
+    Other:           [ STV_HIDDEN ]
+  - Name:            __copy_tls
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000401530
+    Size:            0x0000000000000089
+    Other:           [ STV_HIDDEN ]
+  - Name:            _environ
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_WEAK
+    Value:           0x0000000000404348
+    Size:            0x0000000000000008
+  - Name:            __environ
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000404348
+    Size:            0x0000000000000008
+  - Name:            _Exit
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000401810
+    Size:            0x0000000000000017
+  - Name:            __init_tls
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_WEAK
+    Value:           0x00000000004015C0
+    Size:            0x0000000000000242
+    Other:           [ STV_HIDDEN ]
+  - Name:            _init
+    Section:         .init
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000401000
+  - Name:            __funcs_on_exit
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_WEAK
+    Value:           0x0000000000401470
+    Size:            0x0000000000000001
+    Other:           [ STV_HIDDEN ]
+  - Name:            __memcpy_fwd
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000401827
+    Other:           [ STV_HIDDEN ]
+  - Name:            environ
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_WEAK
+    Value:           0x0000000000404348
+    Size:            0x0000000000000008
+  - Name:            z
+    Type:            STT_OBJECT
+    Section:         .rodata
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000402000
+    Size:            0x0000000000000315
+  - Name:            ___environ
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_WEAK
+    Value:           0x0000000000404348
+    Size:            0x0000000000000008
+  - Name:            __progname
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Value:           0x00000000004042D0
+    Size:            0x0000000000000008
+  - Name:            _start
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x000000000040105A
+  - Name:            _start_c
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000401070
+    Size:            0x0000000000000024
+  - Name:            program_invocation_short_name
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_WEAK
+    Value:           0x00000000004042D0
+    Size:            0x0000000000000008
+  - Name:            __libc_start_init
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_WEAK
+    Value:           0x00000000004013B0
+    Size:            0x0000000000000032
+    Other:           [ STV_HIDDEN ]
+  - Name:            __init_tp
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x00000000004014C0
+    Size:            0x000000000000006B
+    Other:           [ STV_HIDDEN ]
+  - Name:            __init_ssp
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_WEAK
+    Value:           0x0000000000401170
+    Size:            0x0000000000000001
+    Other:           [ STV_HIDDEN ]
+  - Name:            __bss_start
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000404210
+  - Name:            main
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000401030
+    Size:            0x000000000000002A
+  - Name:            __stdio_exit
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_WEAK
+    Value:           0x0000000000401470
+    Size:            0x0000000000000001
+  - Name:            y
+    Type:            STT_OBJECT
+    Section:         .data
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000404040
+    Size:            0x00000000000001C8
+  - Name:            _fini
+    Section:         .fini
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000401869
+  - Name:            __libc_exit_fini
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_WEAK
+    Value:           0x0000000000401480
+    Size:            0x000000000000003A
+    Other:           [ STV_HIDDEN ]
+  - Name:            _edata
+    Section:         .data
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000404210
+  - Name:            _end
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Value:           0x00000000004044F8
+  - Name:            exit
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000401010
+    Size:            0x000000000000001B
+  - Name:            __libc_start_main
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000401430
+    Size:            0x0000000000000034
+  - Name:            program_invocation_name
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_WEAK
+    Value:           0x00000000004042C8
+    Size:            0x0000000000000008
+  - Name:            __default_stacksize
+    Type:            STT_OBJECT
+    Section:         .data
+    Binding:         STB_GLOBAL
+    Value:           0x000000000040420C
+    Size:            0x0000000000000004
+    Other:           [ STV_HIDDEN ]
+  - Name:            __default_guardsize
+    Type:            STT_OBJECT
+    Section:         .data
+    Binding:         STB_GLOBAL
+    Value:           0x0000000000404208
+    Size:            0x0000000000000004
+    Other:           [ STV_HIDDEN ]
+  - Name:            __sysinfo
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Value:           0x00000000004042C0
+    Size:            0x0000000000000008
+    Other:           [ STV_HIDDEN ]
+  - Name:            __progname_full
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Value:           0x00000000004042C8
+    Size:            0x0000000000000008
+...
+
+$ bloaty --raw-map 1
+Maps for 1:
+
+FILE MAP:
+0000-0040	         64		[ELF Headers]
+0040-0078	         56		[LOAD #0 [R]]
+0078-00b0	         56		[LOAD #1 [RX]]
+00b0-00e8	         56		[LOAD #2 [R]]
+00e8-0120	         56		[LOAD #3 [RW]]
+0120-0158	         56		[LOAD #4 [RW]]
+0158-0190	         56		[LOAD #5 [R]]
+0190-01a0	         16		.init
+01a0-09f9	       2137		.text
+09f9-0a00	          7		.fini
+0a00-0d20	        800		.rodata
+0d20-0d50	         48		.eh_frame
+0d50-0d58	          8		.init_array
+0d58-0d60	          8		.fini_array
+0d60-0d68	          8		.got
+0d68-0d80	         24		.got.plt
+0d80-0f78	        504		.data
+0f78-0f80	          8		.data
+0f80-0fd8	         88		.comment
+0fd8-1710	       1848		.symtab
+1710-1a43	        819		.strtab
+1a43-1ac0	        125		.shstrtab
+1ac0-1b00	         64		[ELF Headers]
+1b00-1b40	         64		.init
+1b40-1b80	         64		.text
+1b80-1bc0	         64		.fini
+1bc0-1c00	         64		.rodata
+1c00-1c40	         64		.eh_frame
+1c40-1c80	         64		.init_array
+1c80-1cc0	         64		.fini_array
+1cc0-1d00	         64		.got
+1d00-1d40	         64		.got.plt
+1d40-1d80	         64		.data
+1d80-1dc0	         64		.bss
+1dc0-1e00	         64		.comment
+1e00-1e40	         64		.symtab
+1e40-1e80	         64		.strtab
+1e80-1ec0	         64		.shstrtab
+
+VM MAP:
+000000-401000	    4198400		[-- Nothing mapped --]
+401000-401010	         16		.init
+401010-401869	       2137		.text
+401869-40186c	          3		.fini
+40186c-402000	       1940		[-- Nothing mapped --]
+402000-402320	        800		.rodata
+402320-402350	         48		.eh_frame
+402350-403fe8	       7320		[-- Nothing mapped --]
+403fe8-403ff0	          8		.init_array
+403ff0-403ff8	          8		.fini_array
+403ff8-404000	          8		.got
+404000-404020	         32		.got.plt
+404020-404220	        512		.data
+404220-4044f8	        728		.bss
+
diff --git a/tests/elf/sections/normal-obj.test b/tests/elf/sections/normal-obj.test
new file mode 100644
index 0000000..1473507
--- /dev/null
+++ b/tests/elf/sections/normal-obj.test
@@ -0,0 +1,125 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x0000000000000010
+    Content:         554889E5B8050000005DC3
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000010
+    Size:            0x000000000000007B
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x0000000000000010
+    Content:         '010203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+  - Name:            .rodata
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    AddressAlign:    0x0000000000000010
+    Content:         '010203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+  - Name:            .comment
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_MERGE, SHF_STRINGS ]
+    AddressAlign:    0x0000000000000001
+    EntSize:         0x0000000000000001
+    Content:         0044656269616E20636C616E672076657273696F6E2031312E302E312D3200
+  - Name:            .note.GNU-stack
+    Type:            SHT_PROGBITS
+    AddressAlign:    0x0000000000000001
+  - Name:            .eh_frame
+    Type:            SHT_X86_64_UNWIND
+    Flags:           [ SHF_ALLOC ]
+    AddressAlign:    0x0000000000000008
+    Content:         1400000000000000017A5200017810011B0C0708900100001C0000001C000000000000000B00000000410E108602430D06460C0708000000
+  - Name:            .rela.eh_frame
+    Type:            SHT_RELA
+    Link:            .symtab
+    AddressAlign:    0x0000000000000008
+    Info:            .eh_frame
+    Relocations:
+      - Offset:          0x0000000000000020
+        Symbol:          .text
+        Type:            R_X86_64_PC32
+  - Name:            .llvm_addrsig
+    Type:            SHT_LLVM_ADDRSIG
+    Flags:           [ SHF_EXCLUDE ]
+    Link:            .symtab
+    AddressAlign:    0x0000000000000001
+    Symbols:         [  ]
+Symbols:
+  - Name:            test.c
+    Type:            STT_FILE
+    Index:           SHN_ABS
+  - Name:            .text
+    Type:            STT_SECTION
+    Section:         .text
+  - Name:            foobar
+    Type:            STT_FUNC
+    Section:         .text
+    Binding:         STB_GLOBAL
+    Size:            0x000000000000000B
+  - Name:            x
+    Type:            STT_OBJECT
+    Section:         .bss
+    Binding:         STB_GLOBAL
+    Size:            0x000000000000007B
+  - Name:            y
+    Type:            STT_OBJECT
+    Section:         .data
+    Binding:         STB_GLOBAL
+    Size:            0x00000000000001C8
+  - Name:            z
+    Type:            STT_OBJECT
+    Section:         .rodata
+    Binding:         STB_GLOBAL
+    Size:            0x0000000000000315
+...
+
+$ bloaty --raw-map 1
+Maps for 1:
+
+FILE MAP:
+000-040	         64		[ELF Headers]
+040-050	         16		.text
+050-220	        464		.data
+220-535	        789		.rodata
+535-558	         35		.comment
+558-590	         56		.eh_frame
+590-5a8	         24		.rela.eh_frame
+5a8-650	        168		.symtab
+650-66b	         27		.strtab
+66b-6d8	        109		.shstrtab
+6d8-718	         64		[ELF Headers]
+718-758	         64		.text
+758-798	         64		.bss
+798-7d8	         64		.data
+7d8-818	         64		.rodata
+818-858	         64		.comment
+858-898	         64		.note.GNU-stack
+898-8d8	         64		.eh_frame
+8d8-918	         64		.rela.eh_frame
+918-958	         64		.llvm_addrsig
+958-998	         64		.symtab
+998-9d8	         64		.strtab
+9d8-a18	         64		.shstrtab
+
+VM MAP:
+00000000000-10000000000	 1099511627776		[-- Nothing mapped --]
+10000000000-1000000000b	         11		.text
+1000000000b-20000000000	 1099511627765		[-- Nothing mapped --]
+20000000000-2000000007b	        123		.bss
+2000000007b-30000000000	 1099511627653		[-- Nothing mapped --]
+30000000000-300000001c8	        456		.data
+300000001c8-40000000000	 1099511627320		[-- Nothing mapped --]
+40000000000-40000000315	        789		.rodata
+40000000315-70000000000	 3298534882539		[-- Nothing mapped --]
+70000000000-70000000038	         56		.eh_frame
+
diff --git a/tests/elf/sections/shn-xindex.test b/tests/elf/sections/shn-xindex.test
new file mode 100644
index 0000000..ed068f3
--- /dev/null
+++ b/tests/elf/sections/shn-xindex.test
@@ -0,0 +1,36 @@
+# Tests two special cases for the ELF format:
+#   - If the section count in the ELF headers is 0, then, the true value
+#     is read out of section 0.
+#   - If the section string index in the ELF headers is SHN_XINDEX, the
+#     true value is read out of section 0.
+#
+# These special cases are documented here:
+#  https://docs.oracle.com/cd/E19683-01/817-3677/chapter6-94076/index.html
+
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+  EShNum:          0
+  EShStrNdx:       0xffff ## SHN_XINDEX
+Sections:
+  - Type: SHT_NULL
+    Link: .shstrtab
+    Size: 0x3
+...
+
+$ bloaty --raw-map 1
+Maps for 1:
+
+FILE MAP:
+000-040	         64		[ELF Headers]
+040-041	          1		.strtab
+041-058	         23		.shstrtab
+058-098	         64		[ELF Headers]
+098-0d8	         64		.strtab
+0d8-118	         64		.shstrtab
+
+VM MAP:
+
diff --git a/tests/tester.py b/tests/tester.py
new file mode 100755
index 0000000..556d579
--- /dev/null
+++ b/tests/tester.py
@@ -0,0 +1,72 @@
+#!/usr/bin/python3
+
+import os
+import glob
+import re
+import sys
+import subprocess
+import shutil
+import tempfile
+
+successes = 0
+failures = []
+
+def TestFile(filename):
+  cwd = os.getcwd()
+  abspath = os.path.abspath(filename)
+  global successes
+  global failures
+  with open(abspath) as f:
+    print(filename)
+    tmpdir = tempfile.mkdtemp(prefix="/tmp/bloaty-")
+    contents = f.read()
+    file_count = len(re.findall(r'^---', contents, flags=re.MULTILINE))
+    for i in range(1, file_count + 1):
+      subprocess.check_call('yaml2obj {0} --docnum={1} > {1}'.format(abspath, str(i)), shell=True, cwd=tmpdir)
+    lines = contents.splitlines()
+    while len(lines[0]) == 0 or lines[0][0] != '$':
+      lines.pop(0)
+    bloaty_invocation = os.path.join(cwd, lines[0][2:]) + ' > actual'
+    lines.pop(0)
+    expected_output = "\n".join(lines) + "\n"
+    failure = None
+    if subprocess.call(bloaty_invocation, shell=True, cwd=tmpdir) != 0:
+      failure = "CRASHED"
+    else:
+      with open(os.path.join(tmpdir, 'expected'), 'w') as expected:
+        expected.write(expected_output)
+      if subprocess.call('diff -u expected actual', shell=True, cwd=tmpdir) != 0:
+        failure = "FAILED"
+
+    if failure:
+      print("{}: {}".format(failure, filename))
+      print("{}: output in {}".format(failure, tmpdir))
+      failures.append((filename, tmpdir))
+    else:
+      successes += 1
+      shutil.rmtree(tmpdir)
+
+def TestArg(arg):
+  if os.path.isdir(arg):
+    files = sorted(glob.glob(os.path.join(arg, "**/*.test"), recursive=True))
+    for file in files:
+      TestFile(file)
+  else:
+    TestFile(arg)
+
+args = sys.argv[1:]
+
+if not args:
+  print("Usage: tester.py <FILE-OR-DIR> ...")
+  sys.exit(1)
+
+for arg in sys.argv[1:]:
+  TestArg(arg)
+
+if not failures:
+  print("SUCCESS: {} test(s) passed".format(successes))
+else:
+  print("\nFAILURE: {} tests passed, {} tests failed:".format(successes, len(failures)))
+  for failure in failures:
+    print("  - {} (output in {})".format(*failure))
+  sys.exit(1)