OSDN Git Service

[Object] Support reading 64-bit MIPS ELF archives
authorSimon Atanasyan <simon@atanasyan.com>
Tue, 17 Feb 2015 18:54:22 +0000 (18:54 +0000)
committerSimon Atanasyan <simon@atanasyan.com>
Tue, 17 Feb 2015 18:54:22 +0000 (18:54 +0000)
The 64-bit MIPS ELF archive file format is used by MIPS64 targets.
The main difference from a regular archive file is the symbol table format:
1. ar_name is equal to "/SYM64/"
2. number of symbols and offsets are 64-bit integers

http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf
Page 96

The patch allows reading of such archive files by llvm-nm, llvm-objdump
and other tools. But it does not support archive files with number of symbols
and/or offsets exceed 2^32. I think it is a rather rare case requires more
significant modification of `Archive` class code.

http://reviews.llvm.org/D7546

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@229520 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/Object/Archive.h
lib/Object/Archive.cpp
test/Object/Inputs/archive-test.a-irix6-mips64el [new file with mode: 0644]
test/Object/nm-irix6.test [new file with mode: 0644]

index e3b7a2a..4f8e281 100644 (file)
@@ -178,6 +178,7 @@ public:
 
   enum Kind {
     K_GNU,
+    K_MIPS64,
     K_BSD,
     K_COFF
   };
index 305dab0..43b0771 100644 (file)
@@ -162,7 +162,7 @@ ErrorOr<StringRef> Archive::Child::getName() const {
       return object_error::parse_failed;
 
     // GNU long file names end with a /.
-    if (Parent->kind() == K_GNU) {
+    if (Parent->kind() == K_GNU || Parent->kind() == K_MIPS64) {
       StringRef::size_type End = StringRef(addr).find('/');
       return StringRef(addr, End);
     }
@@ -273,8 +273,16 @@ Archive::Archive(MemoryBufferRef Source, std::error_code &ec)
     return;
   }
 
-  if (Name == "/") {
+  // MIPS 64-bit ELF archives use a special format of a symbol table.
+  // This format is marked by `ar_name` field equals to "/SYM64/".
+  // For detailed description see page 96 in the following document:
+  // http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf
+
+  bool has64SymTable = false;
+  if (Name == "/" || Name == "/SYM64/") {
     SymbolTable = i;
+    if (Name == "/SYM64/")
+      has64SymTable = true;
 
     ++i;
     if (i == e) {
@@ -285,7 +293,7 @@ Archive::Archive(MemoryBufferRef Source, std::error_code &ec)
   }
 
   if (Name == "//") {
-    Format = K_GNU;
+    Format = has64SymTable ? K_MIPS64 : K_GNU;
     StringTable = i;
     ++i;
     FirstRegular = i;
@@ -294,7 +302,7 @@ Archive::Archive(MemoryBufferRef Source, std::error_code &ec)
   }
 
   if (Name[0] != '/') {
-    Format = K_GNU;
+    Format = has64SymTable ? K_MIPS64 : K_GNU;
     FirstRegular = i;
     ec = object_error::success;
     return;
@@ -348,11 +356,18 @@ StringRef Archive::Symbol::getName() const {
 
 ErrorOr<Archive::child_iterator> Archive::Symbol::getMember() const {
   const char *Buf = Parent->SymbolTable->getBuffer().begin();
-  const char *Offsets = Buf + 4;
+  const char *Offsets = Buf;
+  if (Parent->kind() == K_MIPS64)
+    Offsets += sizeof(uint64_t);
+  else
+    Offsets += sizeof(uint32_t);
   uint32_t Offset = 0;
   if (Parent->kind() == K_GNU) {
     Offset =
         *(reinterpret_cast<const support::ubig32_t *>(Offsets) + SymbolIndex);
+  } else if (Parent->kind() == K_MIPS64) {
+    Offset =
+        *(reinterpret_cast<const support::ubig64_t *>(Offsets) + SymbolIndex);
   } else if (Parent->kind() == K_BSD) {
     // The SymbolIndex is an index into the ranlib structs that start at
     // Offsets (the first uint32_t is the number of bytes of the ranlib
@@ -449,6 +464,9 @@ Archive::symbol_iterator Archive::symbol_begin() const {
     uint32_t symbol_count = 0;
     symbol_count = *reinterpret_cast<const support::ubig32_t*>(buf);
     buf += sizeof(uint32_t) + (symbol_count * (sizeof(uint32_t)));
+  } else if (kind() == K_MIPS64) {
+    uint64_t symbol_count = *reinterpret_cast<const support::ubig64_t *>(buf);
+    buf += sizeof(uint64_t) + (symbol_count * (sizeof(uint64_t)));
   } else if (kind() == K_BSD) {
     // The __.SYMDEF or "__.SYMDEF SORTED" member starts with a uint32_t
     // which is the number of bytes of ranlib structs that follow.  The ranlib
@@ -486,6 +504,8 @@ Archive::symbol_iterator Archive::symbol_end() const {
   uint32_t symbol_count = 0;
   if (kind() == K_GNU) {
     symbol_count = *reinterpret_cast<const support::ubig32_t*>(buf);
+  } else if (kind() == K_MIPS64) {
+    symbol_count = *reinterpret_cast<const support::ubig64_t*>(buf);
   } else if (kind() == K_BSD) {
     symbol_count = (*reinterpret_cast<const support::ulittle32_t *>(buf)) /
                    (sizeof(uint32_t) * 2);
diff --git a/test/Object/Inputs/archive-test.a-irix6-mips64el b/test/Object/Inputs/archive-test.a-irix6-mips64el
new file mode 100644 (file)
index 0000000..ccc2634
Binary files /dev/null and b/test/Object/Inputs/archive-test.a-irix6-mips64el differ
diff --git a/test/Object/nm-irix6.test b/test/Object/nm-irix6.test
new file mode 100644 (file)
index 0000000..047665c
--- /dev/null
@@ -0,0 +1,27 @@
+# Check reading IRIX 6.0 64-bit archive file.
+RUN: llvm-nm %p/Inputs/archive-test.a-irix6-mips64el | FileCheck %s
+
+CHECK:      f1.o:
+CHECK-NEXT: 00000028 T f1
+CHECK-NEXT: 00000000 d s_d
+CHECK-NEXT: 00000000 t s_foo
+
+CHECK:      f2.o:
+CHECK-NEXT: 00000028 T f2
+CHECK-NEXT: 00000000 d s_d
+CHECK-NEXT: 00000000 t s_foo
+
+CHECK:      f3.o:
+CHECK-NEXT: 00000028 T f3
+CHECK-NEXT: 00000000 d s_d
+CHECK-NEXT: 00000000 t s_foo
+
+CHECK:      f4.o:
+CHECK-NEXT: 00000028 T f4
+CHECK-NEXT: 00000000 d s_d
+CHECK-NEXT: 00000000 t s_foo
+
+CHECK:      f5.o:
+CHECK-NEXT: 00000028 T f5
+CHECK-NEXT: 00000000 d s_d
+CHECK-NEXT: 00000000 t s_foo