Skip to content

Reapply [llvm-ar] Use COFF archive format for COFF targets. #82642 with fixes for no symbol table handling. #82898

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion llvm/docs/CommandGuide/llvm-ar.rst
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ Other

.. option:: --format=<type>

This option allows for default, gnu, darwin or bsd ``<type>`` to be selected.
This option allows for default, gnu, darwin, bsd or coff ``<type>`` to be selected.
When creating an ``archive`` with the default ``<type>``, :program:``llvm-ar``
will attempt to infer it from the input files and fallback to the default
toolchain target if unable to do so.
Expand Down
3 changes: 3 additions & 0 deletions llvm/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ Changes to the LLVM tools
if it's not specified with the ``--format`` argument and cannot be inferred from
input files.

* llvm-ar now allows specifying COFF archive format with ``--format`` argument
and uses it by default for COFF targets.

* llvm-objcopy now supports ``--set-symbol-visibility`` and
``--set-symbols-visibility`` options for ELF input to change the
visibility of symbols.
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Object/Archive.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ class Archive : public Binary {
Kind kind() const { return (Kind)Format; }
bool isThin() const { return IsThin; }
static object::Archive::Kind getDefaultKind();
static object::Archive::Kind getDefaultKindForTriple(Triple &T);

child_iterator child_begin(Error &Err, bool SkipInternal = true) const;
child_iterator child_end() const;
Expand Down
15 changes: 11 additions & 4 deletions llvm/lib/Object/Archive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -969,12 +969,19 @@ Archive::Archive(MemoryBufferRef Source, Error &Err)
Err = Error::success();
}

object::Archive::Kind Archive::getDefaultKindForTriple(Triple &T) {
if (T.isOSDarwin())
return object::Archive::K_DARWIN;
if (T.isOSAIX())
return object::Archive::K_AIXBIG;
if (T.isOSWindows())
return object::Archive::K_COFF;
return object::Archive::K_GNU;
}

object::Archive::Kind Archive::getDefaultKind() {
Triple HostTriple(sys::getDefaultTargetTriple());
return HostTriple.isOSDarwin()
? object::Archive::K_DARWIN
: (HostTriple.isOSAIX() ? object::Archive::K_AIXBIG
: object::Archive::K_GNU);
return getDefaultKindForTriple(HostTriple);
}

Archive::child_iterator Archive::child_begin(Error &Err,
Expand Down
28 changes: 15 additions & 13 deletions llvm/lib/Object/ArchiveWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,16 @@ object::Archive::Kind NewArchiveMember::detectKindFromObject() const {
Expected<std::unique_ptr<object::ObjectFile>> OptionalObject =
object::ObjectFile::createObjectFile(MemBufferRef);

if (OptionalObject)
return isa<object::MachOObjectFile>(**OptionalObject)
? object::Archive::K_DARWIN
: (isa<object::XCOFFObjectFile>(**OptionalObject)
? object::Archive::K_AIXBIG
: object::Archive::K_GNU);
if (OptionalObject) {
if (isa<object::MachOObjectFile>(**OptionalObject))
return object::Archive::K_DARWIN;
if (isa<object::XCOFFObjectFile>(**OptionalObject))
return object::Archive::K_AIXBIG;
if (isa<object::COFFObjectFile>(**OptionalObject) ||
isa<object::COFFImportFile>(**OptionalObject))
return object::Archive::K_COFF;
return object::Archive::K_GNU;
}

// Squelch the error in case we had a non-object file.
consumeError(OptionalObject.takeError());
Expand All @@ -80,10 +84,7 @@ object::Archive::Kind NewArchiveMember::detectKindFromObject() const {
MemBufferRef, file_magic::bitcode, &Context)) {
auto &IRObject = cast<object::IRObjectFile>(**ObjOrErr);
auto TargetTriple = Triple(IRObject.getTargetTriple());
return TargetTriple.isOSDarwin()
? object::Archive::K_DARWIN
: (TargetTriple.isOSAIX() ? object::Archive::K_AIXBIG
: object::Archive::K_GNU);
return object::Archive::getDefaultKindForTriple(TargetTriple);
} else {
// Squelch the error in case this was not a SymbolicFile.
consumeError(ObjOrErr.takeError());
Expand Down Expand Up @@ -976,10 +977,12 @@ static Error writeArchiveToStream(raw_ostream &Out,
SmallString<0> StringTableBuf;
raw_svector_ostream StringTable(StringTableBuf);
SymMap SymMap;
bool ShouldWriteSymtab = WriteSymtab != SymtabWritingMode::NoSymtab;

// COFF symbol map uses 16-bit indexes, so we can't use it if there are too
// many members.
if (isCOFFArchive(Kind) && NewMembers.size() > 0xfffe)
// many members. COFF format also requires symbol table presence, so use
// GNU format when NoSymtab is requested.
if (isCOFFArchive(Kind) && (NewMembers.size() > 0xfffe || !ShouldWriteSymtab))
Kind = object::Archive::K_GNU;

// In the scenario when LLVMContext is populated SymbolicFile will contain a
Expand Down Expand Up @@ -1008,7 +1011,6 @@ static Error writeArchiveToStream(raw_ostream &Out,
uint64_t LastMemberHeaderOffset = 0;
uint64_t NumSyms = 0;
uint64_t NumSyms32 = 0; // Store symbol number of 32-bit member files.
bool ShouldWriteSymtab = WriteSymtab != SymtabWritingMode::NoSymtab;

for (const auto &M : Data) {
// Record the start of the member's offset
Expand Down
91 changes: 91 additions & 0 deletions llvm/test/tools/llvm-ar/coff-symtab.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
Verify that llvm-ar uses COFF archive format by ensuring that archive map is sorted.

RUN: rm -rf %t.dir && split-file %s %t.dir && cd %t.dir

RUN: yaml2obj coff-symtab.yaml -o coff-symtab.obj
RUN: llvm-ar crs out.a coff-symtab.obj
RUN: llvm-nm --print-armap out.a | FileCheck %s

RUN: llvm-as coff-symtab.ll -o coff-symtab.bc
RUN: llvm-ar crs out2.a coff-symtab.bc
RUN: llvm-nm --print-armap out2.a | FileCheck %s

RUN: yaml2obj elf.yaml -o coff-symtab.o
RUN: llvm-ar crs --format coff out3.a coff-symtab.o
RUN: llvm-nm --print-armap out3.a | FileCheck %s

Create an empty archive with no symbol map, add a COFF file to it and check that the output archive is a COFF archive.

RUN: llvm-ar rcS out4.a
RUN: llvm-ar rs out4.a coff-symtab.obj
RUN: llvm-nm --print-armap out4.a | FileCheck %s

CHECK: Archive map
CHECK-NEXT: a in coff-symtab
CHECK-NEXT: b in coff-symtab
CHECK-NEXT: c in coff-symtab
CHECK-EMPTY:

#--- coff-symtab.yaml
--- !COFF
header:
Machine: IMAGE_FILE_MACHINE_UNKNOWN
Characteristics: [ ]
sections:
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: ''
symbols:
- Name: b
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: c
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: a
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
...


#--- coff-symtab.ll
target triple = "x86_64-unknown-windows-msvc"

define void @b() { ret void }
define void @c() { ret void }
define void @a() { ret void }

#--- elf.yaml
--- !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: 0x0000000000000004
Content: ''
Symbols:
- Name: b
Binding: STB_GLOBAL
Section: .text
- Name: c
Binding: STB_GLOBAL
Section: .text
- Name: a
Binding: STB_GLOBAL
Section: .text
...
32 changes: 32 additions & 0 deletions llvm/test/tools/llvm-ar/no-symtab.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## Create archives with no symtab in various formats and check that we can read them.

# RUN: yaml2obj %s -o %t.o
# RUN: rm -f %t.*.a

# RUN: llvm-ar --format=gnu rcS %t.gnu.a %t.o
# RUN: llvm-ar --format=coff rcS %t.coff.a %t.o
# RUN: llvm-ar --format=darwin rcS %t.darwin.a %t.o
# RUN: llvm-ar --format=bsd rcS %t.bsd.a %t.o
# RUN: llvm-ar --format=bigarchive rcS %t.bigarchive.a %t.o

# RUN: llvm-nm --print-armap %t.gnu.a | FileCheck %s
# RUN: llvm-nm --print-armap %t.coff.a | FileCheck %s
# RUN: llvm-nm --print-armap %t.darwin.a | FileCheck %s
# RUN: llvm-nm --print-armap %t.bsd.a | FileCheck %s
# RUN: llvm-nm --print-armap %t.bigarchive.a | FileCheck %s

# CHECK-NOT: Archive map

--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_REL
Machine: EM_X86_64
Sections:
- Name: .text
Type: SHT_PROGBITS
Symbols:
- Name: symbol
Binding: STB_GLOBAL
Section: .text
22 changes: 17 additions & 5 deletions llvm/tools/llvm-ar/llvm-ar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ static void printArHelp(StringRef ToolName) {
=darwin - darwin
=bsd - bsd
=bigarchive - big archive (AIX OS)
=coff - coff
--plugin=<string> - ignored for compatibility
-h --help - display this help and exit
--output - the directory to extract archive members to
Expand Down Expand Up @@ -193,7 +194,7 @@ static SmallVector<const char *, 256> PositionalArgs;
static bool MRI;

namespace {
enum Format { Default, GNU, BSD, DARWIN, BIGARCHIVE, Unknown };
enum Format { Default, GNU, COFF, BSD, DARWIN, BIGARCHIVE, Unknown };
}

static Format FormatType = Default;
Expand Down Expand Up @@ -1025,14 +1026,21 @@ static void performWriteOperation(ArchiveOperation Operation,
Kind = object::Archive::K_GNU;
else if (OldArchive) {
Kind = OldArchive->kind();
if (Kind == object::Archive::K_BSD) {
auto InferredKind = object::Archive::K_BSD;
std::optional<object::Archive::Kind> AltKind;
if (Kind == object::Archive::K_BSD)
AltKind = object::Archive::K_DARWIN;
else if (Kind == object::Archive::K_GNU && !OldArchive->hasSymbolTable())
// If there is no symbol table, we can't tell GNU from COFF format
// from the old archive type.
AltKind = object::Archive::K_COFF;
if (AltKind) {
auto InferredKind = Kind;
if (NewMembersP && !NewMembersP->empty())
InferredKind = NewMembersP->front().detectKindFromObject();
else if (!NewMembers.empty())
InferredKind = NewMembers.front().detectKindFromObject();
if (InferredKind == object::Archive::K_DARWIN)
Kind = object::Archive::K_DARWIN;
if (InferredKind == AltKind)
Kind = *AltKind;
}
} else if (NewMembersP)
Kind = !NewMembersP->empty() ? NewMembersP->front().detectKindFromObject()
Expand All @@ -1044,6 +1052,9 @@ static void performWriteOperation(ArchiveOperation Operation,
case GNU:
Kind = object::Archive::K_GNU;
break;
case COFF:
Kind = object::Archive::K_COFF;
break;
case BSD:
if (Thin)
fail("only the gnu format has a thin mode");
Expand Down Expand Up @@ -1376,6 +1387,7 @@ static int ar_main(int argc, char **argv) {
.Case("darwin", DARWIN)
.Case("bsd", BSD)
.Case("bigarchive", BIGARCHIVE)
.Case("coff", COFF)
.Default(Unknown);
if (FormatType == Unknown)
fail(std::string("Invalid format ") + Match);
Expand Down