diff --git a/llvm/docs/CommandGuide/llvm-ar.rst b/llvm/docs/CommandGuide/llvm-ar.rst --- a/llvm/docs/CommandGuide/llvm-ar.rst +++ b/llvm/docs/CommandGuide/llvm-ar.rst @@ -294,6 +294,25 @@ Display the version of the :program:`llvm-ar` executable. +.. option:: -X mode + + Specifies the type of object file :program:`llvm-ar` will recognise. The mode must be + one of the following: + + 32 + Process only 32-bit object files. + 64 + Process only 64-bit object files. + 32_64 + Process both 32-bit and 64-bit object files. + any + Process all object files. + + The default is to process 32-bit object files (ignore 64-bit objects). The mode can also + be set with the OBJECT_MODE environment variable. For example, OBJECT_MODE=64 causes ar to + process any 64-bit objects and ignore 32-bit objects. The -X flag overrides the OBJECT_MODE + variable. + .. option:: @ Read command-line options and commands from response file ````. diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -479,3 +479,11 @@ for directory in ('/CodeGen/X86', '/DebugInfo', '/DebugInfo/X86', '/DebugInfo/Generic', '/LTO/X86', '/Linker'): exclude_unsupported_files_for_aix(config.test_source_root + directory) +# Some tools support an environment variable "OBJECT_MODE" on AIX OS, which +# controls the kind of objects they will support. If there is no "OBJECT_MODE" +# environment variable specified, the default behaviour is to support 32-bit +# objects only. In order to not affect most test cases, which expect to support +# 32-bit and 64-bit objects by default, set the environment variable +# "OBJECT_MODE" to 'any' by default on AIX OS. +if 'system-aix' in config.available_features: + config.environment['OBJECT_MODE'] = 'any' diff --git a/llvm/test/tools/llvm-ar/Inputs/bitcode-sym32.ll b/llvm/test/tools/llvm-ar/Inputs/bitcode-sym32.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-ar/Inputs/bitcode-sym32.ll @@ -0,0 +1,14 @@ +target triple = "powerpcle-unknown-linux-gnu" + +@C32 = dso_local global i32 5, align 4 +@undef_var32 = external dso_local global i32, align 4 + +define dso_local i32 @foo32(i32 %i) #0 { +entry: + %i.addr = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4 + %0 = load i32, i32* %i.addr, align 4 + %1 = load i32, i32* @undef_var32, align 4 + %add = add nsw i32 %0, %1 + ret i32 %add +} diff --git a/llvm/test/tools/llvm-ar/Inputs/bitcode-sym64.ll b/llvm/test/tools/llvm-ar/Inputs/bitcode-sym64.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-ar/Inputs/bitcode-sym64.ll @@ -0,0 +1,12 @@ +target triple = "powerpc64le-unknown-linux-gnu" + +@C64 = dso_local global i32 5, align 4 +@static_var64 = internal global i32 2, align 4 + +define dso_local signext i32 @bar64() #0 { +entry: + %0 = load i32, i32* @static_var64, align 4 + %1 = load i32, i32* @C64, align 4 + %add = add nsw i32 %0, %1 + ret i32 %add +} diff --git a/llvm/test/tools/llvm-ar/invalid-option-X.test b/llvm/test/tools/llvm-ar/invalid-option-X.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-ar/invalid-option-X.test @@ -0,0 +1,5 @@ +# REQUIRES: !system-aix +## Test that the -X option is not supported on non-AIX OS. + +# RUN: not llvm-ar -q -X32 %t.a xcoff32.o 2>&1 | FileCheck %s +# CHECK: error: -X32 option not supported on non AIX OS diff --git a/llvm/test/tools/llvm-ar/option-X.test b/llvm/test/tools/llvm-ar/option-X.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-ar/option-X.test @@ -0,0 +1,385 @@ +# REQUIRES: system-aix +## Test the "-X" option. +## The option specifies the type of object file llvm-ar will operate on. + +# RUN: rm -rf %t && mkdir %t && cd %t +# RUN: unset OBJECT_MODE +# RUN: yaml2obj --docnum=1 -DCLASS=ELFCLASS32 %s -o elf32.o +# RUN: yaml2obj --docnum=1 -DCLASS=ELFCLASS64 %s -o elf64.o + +# RUN: yaml2obj --docnum=2 -DFLAG=0x1DF %s -o xcoff32.o +# RUN: yaml2obj --docnum=2 -DFLAG=0x1F7 %s -o xcoff64.o + +## Test default -X option when creating a new archive. +# RUN: llvm-ar -q -c archive-default.a xcoff32.o elf32.o xcoff64.o elf64.o 2>&1 | \ +# RUN: FileCheck %s --check-prefixes=WARN-XCOFF64,WARN-ELF64 +# RUN: llvm-ar -t -Xany archive-default.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32 + +## Test -X32 option when creating a new archive. +# RUN: llvm-ar -q -c -X 32 archive-32.a xcoff32.o elf32.o xcoff64.o elf64.o 2>&1 | \ +# RUN: FileCheck %s --check-prefixes=WARN-XCOFF64,WARN-ELF64 +# RUN: llvm-ar -t -Xany archive-32.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32 + +## Test -X option will override the "OBJECT_MODE" environment variable. +# RUN: env OBJECT_MODE=64 llvm-ar -q -c -X32 archive-32-env1.a xcoff32.o elf32.o xcoff64.o elf64.o 2>&1 | \ +# RUN: FileCheck %s --check-prefixes=WARN-XCOFF64,WARN-ELF64 +# RUN: llvm-ar -t -Xany archive-32-env1.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32 + +# RUN: env OBJECT_MODE=32_64 llvm-ar -q -c -X32 archive-32-env2.a xcoff32.o elf32.o xcoff64.o elf64.o 2>&1 | \ +# RUN: FileCheck %s --check-prefixes=WARN-XCOFF64,WARN-ELF64 +# RUN: llvm-ar -t -Xany archive-32-env2.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32 + +# RUN: env OBJECT_MODE=any llvm-ar -q -c -X32 archive-32-env3.a xcoff32.o elf32.o xcoff64.o elf64.o 2>&1 | \ +# RUN: FileCheck %s --check-prefixes=WARN-XCOFF64,WARN-ELF64 +# RUN: llvm-ar -t -Xany archive-32-env3.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32 + +## Test -X64 option when creating a new archive. +# RUN: llvm-ar -q -c -X 64 archive-64.a xcoff32.o elf32.o xcoff64.o elf64.o 2>&1 | \ +# RUN: FileCheck %s --check-prefixes=WARN-XCOFF32,WARN-ELF32 +# RUN: llvm-ar -t -Xany archive-64.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ64 + +## Test -X32_64 option when creating a new archive. +## RUN: llvm-ar -q -c -X32_64 archive-32_64.a xcoff32.o elf32.o xcoff64.o elf64.o +## RUN: llvm-ar -t -Xany archive-32_64.a | \ +## RUN: FileCheck %s --check-prefixes=OBJ32_64 + +## Test -Xany option when creating a new archive. +# RUN: llvm-ar -q -c -Xany archive-any.a xcoff32.o elf32.o xcoff64.o elf64.o +# RUN: llvm-ar -t -Xany archive-any.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32_64 + +## Test envionment "OBJECT_MODE" when creating a new archive. +# RUN: env OBJECT_MODE=32 llvm-ar -q -c archive-env32.a xcoff32.o elf32.o xcoff64.o elf64.o 2>&1 | \ +# RUN: FileCheck %s --check-prefixes=WARN-XCOFF64,WARN-ELF64 +# RUN: llvm-ar -t -Xany archive-env32.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32 + +# RUN: env OBJECT_MODE=64 llvm-ar -q -c archive-env64.a xcoff32.o elf32.o xcoff64.o elf64.o 2>&1 | \ +# RUN: FileCheck %s --check-prefixes=WARN-XCOFF32,WARN-ELF32 +# RUN: llvm-ar -t -Xany archive-env64.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ64 + +# RUN: env OBJECT_MODE=32_64 llvm-ar -q -c archive-env32_64.a xcoff32.o elf32.o xcoff64.o elf64.o +# RUN: llvm-ar -t -Xany archive-env32_64.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32_64 + +# RUN: env OBJECT_MODE=any llvm-ar -q -c archive-envany.a xcoff32.o elf32.o xcoff64.o elf64.o +# RUN: llvm-ar -t -Xany archive-envany.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32_64 + +## Test -X option for print operation. +# RUN: llvm-ar -t archive-any.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32 + +# RUN: llvm-ar -t -X32 archive-any.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32 + +# RUN: llvm-ar -t -X64 archive-any.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ64 + +# RUN: llvm-ar -t -X32_64 archive-any.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32_64 + +# RUN: llvm-ar -t -Xany archive-any.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32_64 + +## Test -X option for extract operation. +# RUN: llvm-ar --output=32 -x -X32 archive-any.a +# RUN: cmp xcoff32.o 32/xcoff32.o +# RUN: cmp elf32.o 32/elf32.o +# RUN: not ls 32/coff64.o +# RUN: not ls 32/elf64.o + +# RUN: llvm-ar --output=64 -x -X64 archive-any.a +# RUN: cmp xcoff64.o 64/xcoff64.o +# RUN: cmp elf64.o 64/elf64.o +# RUN: not ls 64/xcoff32.o +# RUN: not ls 64/elf32.o + +# RUN: llvm-ar --output=32_64 -x -X32_64 archive-any.a +# RUN: cmp xcoff32.o 32_64/xcoff32.o +# RUN: cmp elf32.o 32_64/elf32.o +# RUN: cmp xcoff64.o 32_64/xcoff64.o +# RUN: cmp elf64.o 32_64/elf64.o + +# RUN: llvm-ar --output=any -x -Xany archive-any.a +# RUN: cmp xcoff32.o any/xcoff32.o +# RUN: cmp elf32.o any/elf32.o +# RUN: cmp xcoff64.o any/xcoff64.o +# RUN: cmp elf64.o any/elf64.o + +## Extract a 64-bit object file with option -X32 (or default object mode). +# RUN: not llvm-ar --output=err64 -x archive-any.a xcoff64.o 2>&1 | \ +# RUN: FileCheck %s -DFILE=xcoff64.o --check-prefixes=ERR64 +# RUN: not llvm-ar --output=err64 -x -X32 archive-any.a xcoff64.o 2>&1 | \ +# RUN: FileCheck %s -DFILE=xcoff64.o --check-prefixes=ERR64 + +# ERR64: llvm-ar: error: '[[FILE]]' was not found +# RUN: not ls err64/xcoff64.o + +## Extract a 32-bit object file with option -X64. +# RUN: not llvm-ar --output=err32 -x -X64 archive-any.a xcoff32.o 2>&1 | \ +# RUN: FileCheck %s -DFILE=xcoff32.o --check-prefixes=ERR32 + +# ERR32: llvm-ar: error: '[[FILE]]' was not found +# RUN: not ls err32/xcoff32.o + +## Test deleting an object file from a big archive file. +# RUN: cp archive-any.a archive.a +# RUN: llvm-ar -d -X32 archive.a xcoff64.o elf64.o +# RUN: llvm-ar -t -Xany archive.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32_64 + +# RUN: cp archive-any.a archive.a +# RUN: llvm-ar -d -X64 archive.a xcoff64.o elf64.o +# RUN: llvm-ar -t -Xany archive.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32 + +# RUN: cp archive-any.a archive.a +# RUN: llvm-ar -d -Xany archive.a xcoff64.o elf64.o +# RUN: llvm-ar -t -Xany archive.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32 + +# Test replace/insert an object file in a big archive file. +# RUN: cp xcoff32.o xcoff.o +# RUN: llvm-ar -q -c archive-rep.a xcoff.o + +## xcoff.o is a 64-bit object file here. +# RUN: yaml2obj --docnum=2 -DFLAG=0x1F7 %s -o xcoff.o + +## Without -X64, -X32_64 or -Xany, nothing changed here, +## since xcoff.o is a 64-bit object file in command line, but +## the xcoff.o member in archive-rep.a is a 32-bit object file. +# RUN: llvm-ar -r archive-rep.a xcoff.o +# RUN: llvm-ar -t -Xany archive-rep.a | \ +# RUN: FileCheck %s --check-prefixes=REP +# RUN: llvm-nm -Xany --print-armap archive-rep.a | \ +# RUN: FileCheck %s --check-prefixes=SYM32 + +## With options -X64, -X32_64 or -Xany, the old 32-bit xcoff.o +## member is still in the archive +## and a new 64-bit object file xcoff.o is added to the archive. +# RUN: llvm-ar -r -X64 archive-rep.a xcoff.o +# RUN: llvm-ar -t -Xany archive-rep.a | \ +# RUN: FileCheck %s --check-prefixes=REP,REP-DUP +# RUN: llvm-nm -Xany --print-armap archive-rep.a | \ +# RUN: FileCheck %s --check-prefixes=SYM32_64 + +# REP: xcoff.o +# REP-DUP-NEXT: xcoff.o +# REP-EMPTY: + +## Test move member. +# RUN: cp archive-any.a archive.a +## Do not move 64-bit object without options -X64, -X32_64, Xany. +# RUN: llvm-ar -ma elf32.o archive.a xcoff64.o 2>&1 | \ +# RUN: FileCheck %s --check-prefix=WARN-XCOFF64 + +# RUN: llvm-ar -t -Xany archive.a | \ +# RUN: FileCheck %s --check-prefixes=OBJ32_64 + +## Do not move 32-bit object with -X64. +# RUN: llvm-ar -ma -X64 elf64.o archive.a xcoff32.o 2>&1 | \ +# RUN: FileCheck %s --check-prefix=WARN-XCOFF32 + +# WARN-XCOFF32: warning: 'xcoff32.o' is not valid with the current object file mode +# WARN-XCOFF64: warning: 'xcoff64.o' is not valid with the current object file mode +# WARN-ELF32: warning: 'elf32.o' is not valid with the current object file mode +# WARN-ELF64: warning: 'elf64.o' is not valid with the current object file mode + +# RUN: llvm-ar -ma -X64 elf64.o archive.a xcoff64.o +# RUN: llvm-ar -t -Xany archive.a | \ +# RUN: FileCheck %s --check-prefixes=MOVE64 + +# RUN: cp archive-any.a archive.a +# RUN: llvm-ar -ma -X32_64 elf64.o archive.a xcoff64.o +# RUN: llvm-ar -t -Xany archive.a | \ +# RUN: FileCheck %s --check-prefixes=MOVE64 + +# RUN: cp archive-any.a archive.a +# RUN: llvm-ar -ma -Xany elf64.o archive.a xcoff64.o +# RUN: llvm-ar -t -Xany archive.a | \ +# RUN: FileCheck %s --check-prefixes=MOVE64 + +# MOVE64: xcoff32.o +# MOVE64-NEXT: elf32.o +# MOVE64-NEXT: elf64.o +# MOVE64-NEXT: xcoff64.o +# MOVE64-EMPTY: + +# RUN: cp archive-any.a archive.a +# RUN: llvm-ar -ma elf32.o archive.a xcoff32.o +# RUN: llvm-ar -t -Xany archive.a | \ +# RUN: FileCheck %s --check-prefixes=MOVE32 + +# RUN: cp archive-any.a archive.a +# RUN: llvm-ar -ma -X32 elf32.o archive.a xcoff32.o +# RUN: llvm-ar -t -Xany archive.a | \ +# RUN: FileCheck %s --check-prefixes=MOVE32 + +# RUN: cp archive-any.a archive.a +# RUN: llvm-ar -ma -X32_64 elf32.o archive.a xcoff32.o +# RUN: llvm-ar -t -Xany archive.a | \ +# RUN: FileCheck %s --check-prefixes=MOVE32 + +# RUN: cp archive-any.a archive.a +# RUN: llvm-ar -ma -Xany elf32.o archive.a xcoff32.o +# RUN: llvm-ar -t -Xany archive.a | \ +# RUN: FileCheck %s --check-prefixes=MOVE32 + +# MOVE32: elf32.o +# MOVE32-NEXT: xcoff32.o +# MOVE32-NEXT: xcoff64.o +# MOVE32-NEXT: elf64.o +# MOVE32-EMPTY: + +## Move after a file with a bitness that doesn't match the object mode. +# RUN: not llvm-ar -ma xcoff64.o archive-any.a xcoff32.o 2>&1 | \ +# RUN: FileCheck %s --check-prefixes=ERR-INSERT-POINT + +# RUN: not llvm-ar -X32 -ma xcoff64.o archive-any.a xcoff32.o 2>&1 | \ +# RUN: FileCheck %s --check-prefixes=ERR-INSERT-POINT + +# RUN: not llvm-ar -X64 -ma xcoff32.o archive-any.a xcoff64.o 2>&1 | \ +# RUN: FileCheck %s --check-prefixes=ERR-INSERT-POINT + +# ERR-INSERT-POINT: error: insertion point not found + +## Test move operation for archive when there are two object files with +## the same name, but different bitness in archive. +## There are two files named xcoff.o in archive-rep.a (the first one in +## the archive is 32-bit, the second one is 64-bit). +# RUN: cp archive-rep.a archive-rep-mov32.a +# RUN: llvm-ar -Xany -q archive-rep-mov32.a elf32.o elf64.o +## Move elf32.o to after the 32-bit xcoff.o. +# RUN: llvm-ar -X32 -ma xcoff.o archive-rep-mov32.a elf32.o +# RUN: llvm-ar -t -Xany archive-rep-mov32.a | \ +# RUN: FileCheck %s --check-prefixes=MOVE32-DUP + +# MOVE32-DUP: xcoff.o +# MOVE32-DUP-NEXT: elf32.o +# MOVE32-DUP-NEXT: xcoff.o +# MOVE32-DUP-NEXT: elf64.o + +# RUN: cp archive-rep.a archive-rep-mov64.a +# RUN: llvm-ar -Xany -q archive-rep-mov64.a elf32.o elf64.o +## Move elf64.o to after the 64-bit xcoff.o. +# RUN: llvm-ar -X64 -ma xcoff.o archive-rep-mov64.a elf64.o +# RUN: llvm-ar -t -Xany archive-rep-mov64.a | \ +# RUN: FileCheck %s --check-prefixes=MOVE64-DUP + +# MOVE64-DUP: xcoff.o +# MOVE64-DUP-NEXT: xcoff.o +# MOVE64-DUP-NEXT: elf64.o +# MOVE64-DUP-NEXT: elf32.o + +# OBJ32: xcoff32.o +# OBJ32-NEXT: elf32.o +# OBJ32-EMPTY: + +# OBJ64: xcoff64.o +# OBJ64-NEXT: elf64.o +# OBJ64-EMPTY: + +# OBJ32_64: xcoff32.o +# OBJ32_64-NEXT: elf32.o +# OBJ32_64-NEXT: xcoff64.o +# OBJ32_64-NEXT: elf64.o +# OBJ32_64-EMPTY: + +# SYM32: var_0x1DF +# SYM32-NOT: var_0x1F7 + +# SYM32_64: var_0x1DF +# SYM32_64: var_0x1F7 + +## Test -X option with other output formats. + +# RUN: llvm-as -o 32.bc %p/Inputs/bitcode-sym32.ll +# RUN: llvm-as -o 64.bc %p/Inputs/bitcode-sym64.ll +# RUN: yaml2obj --docnum=3 %s -o macho32.o +# RUN: yaml2obj --docnum=4 %s -o macho64.o +# RUN: yaml2obj --docnum=5 %s -o wasm.o +# RUN: yaml2obj --docnum=6 %s -o coff.o + +# RUN: llvm-ar -q -c archive-other32.a coff.o 32.bc 64.bc wasm.o macho32.o macho64.o 2>&1 | \ +# RUN: FileCheck %s --check-prefixes=WARN-64 +# RUN: llvm-ar -t -Xany archive-other32.a | \ +# RUN: FileCheck %s --check-prefixes=OTHER32 + +# WARN-64: warning: '64.bc' is not valid with the current object file mode +# WARN-64-NEXT: warning: 'macho64.o' is not valid with the current object file mode + +# OTHER32: coff.o +# OTHER32-NEXT: 32.bc +# OTHER32-NEXT: wasm.o +# OTHER32-NEXT: macho32.o +# OTHER32-EMPTY: + +# RUN: llvm-ar -q -c -X64 archive-other64.a coff.o 32.bc 64.bc wasm.o macho32.o macho64.o 2>&1 | \ +# RUN: FileCheck %s --check-prefixes=WARN-32 +# RUN: llvm-ar -t -Xany archive-other64.a | \ +# RUN: FileCheck %s --check-prefixes=OTHER64 + +# WARN-32: warning: 'coff.o' is not valid with the current object file mode +# WARN-32-NEXT: warning: '32.bc' is not valid with the current object file mode +# WARN-32-NEXT: warning: 'wasm.o' is not valid with the current object file mode +# WARN-32-NEXT: warning: 'macho32.o' is not valid with the current object file mode + +# OTHER64: 64.bc +# OTHER64-NEXT: macho64.o +# OTHER64-EMPTY: + + +--- !ELF +FileHeader: + Class: [[CLASS]] + Data: ELFDATA2LSB + Type: ET_REL +Symbols: + - Name: [[CLASS]]_var + +--- !XCOFF +FileHeader: + MagicNumber: [[FLAG]] +Symbols: + - Name: var_[[FLAG]] + +--- !mach-o +FileHeader: + magic: 0xFEEDFACE + cputype: 0x00000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 0 + sizeofcmds: 0 + flags: 0x00002000 + +## mach-o 64 bit +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x0100000C + cpusubtype: 0x00000000 + filetype: 0x00000001 + ncmds: 0 + sizeofcmds: 0 + flags: 0x00000000 + reserved: 0x00000000 + +--- !WASM +FileHeader: + Version: 0x00000001 + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_ARMNT + Characteristics: [ ] +sections: +symbols: diff --git a/llvm/tools/llvm-ar/llvm-ar.cpp b/llvm/tools/llvm-ar/llvm-ar.cpp --- a/llvm/tools/llvm-ar/llvm-ar.cpp +++ b/llvm/tools/llvm-ar/llvm-ar.cpp @@ -18,10 +18,14 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/COFFImportFile.h" +#include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/IRObjectFile.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/SymbolicFile.h" +#include "llvm/Object/TapiFile.h" +#include "llvm/Object/Wasm.h" #include "llvm/Object/XCOFFObjectFile.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/CommandLine.h" @@ -55,6 +59,7 @@ #endif using namespace llvm; +using namespace llvm::object; // The name this program was invoked as. static StringRef ToolName; @@ -91,6 +96,7 @@ =windows - windows --thin - create a thin archive --version - print the version and exit + -X{32|64|32_64|any} - object mode (only for AIX OS) @ - read options from OPERATIONS: @@ -184,6 +190,10 @@ }); } +static void warn(Twine Message) { + WithColor::warning(errs(), ToolName) << Message << "\n"; +} + static SmallVector PositionalArgs; static bool MRI; @@ -209,6 +219,10 @@ CreateSymTab ///< Create a symbol table in an existing archive }; +enum class BitModeTy { Bit32, Bit64, Bit32_64, Any, Unknown }; + +static BitModeTy BitMode = BitModeTy::Bit32; + // Modifiers to follow operation to vary behavior static bool AddAfter = false; ///< 'a' modifier static bool AddBefore = false; ///< 'b' modifier @@ -632,6 +646,71 @@ llvm_unreachable("Missing entry in covered switch."); } +static bool is64BitSymbolicFile(SymbolicFile &Obj) { + if (auto *IRObj = dyn_cast(&Obj)) + return Triple(IRObj->getTargetTriple()).isArch64Bit(); + if (isa(Obj) || isa(Obj)) + return false; + if (XCOFFObjectFile *XCOFFObj = dyn_cast(&Obj)) + return XCOFFObj->is64Bit(); + if (isa(Obj)) + return false; + if (TapiFile *Tapi = dyn_cast(&Obj)) + return Tapi->is64Bit(); + if (MachOObjectFile *MachO = dyn_cast(&Obj)) + return MachO->is64Bit(); + if (ELFObjectFileBase *ElfO = dyn_cast(&Obj)) + return ElfO->getBytesInAddress() == 8; + + fail("unsupported file format"); +} + +static bool isValidInBitMode(Binary &Bin) { + if (BitMode == BitModeTy::Bit32_64 || BitMode == BitModeTy::Any) + return true; + + if (SymbolicFile *SymFile = dyn_cast(&Bin)) { + bool Is64Bit = is64BitSymbolicFile(*SymFile); + if ((Is64Bit && (BitMode == BitModeTy::Bit32)) || + (!Is64Bit && (BitMode == BitModeTy::Bit64))) + return false; + } + // In AIX "ar", non-object files are always considered to have a valid bit + // mode. + return true; +} + +Expected> getAsBinary(const NewArchiveMember &NM, + LLVMContext *Context) { + auto BinaryOrErr = createBinary(NM.Buf->getMemBufferRef(), Context); + if (BinaryOrErr) + return std::move(*BinaryOrErr); + return BinaryOrErr.takeError(); +} + +Expected> getAsBinary(const Archive::Child &C, + LLVMContext *Context) { + return C.getAsBinary(Context); +} + +template static bool isValidInBitMode(const A &Member) { + if (object::Archive::getDefaultKindForHost() != object::Archive::K_AIXBIG) + return true; + LLVMContext Context; + Expected> BinOrErr = getAsBinary(Member, &Context); + // In AIX "ar", if there is a non-object file member, it is never ignored due + // to the bit mode setting. + if (!BinOrErr) { + consumeError(BinOrErr.takeError()); + return true; + } + return isValidInBitMode(*BinOrErr.get()); +} + +static void warnInvalidObjectForFileMode(Twine Name) { + warn("'" + Name + "' is not valid with the current object file mode"); +} + static void performReadOperation(ArchiveOperation Operation, object::Archive *OldArchive) { if (Operation == Extract && OldArchive->isThin()) @@ -646,6 +725,10 @@ failIfError(NameOrErr.takeError()); StringRef Name = NameOrErr.get(); + // Check whether to ignore this object due to its bitness. + if (!isValidInBitMode(C)) + continue; + if (Filter) { auto I = find_if(Members, [Name](StringRef Path) { return comparePaths(Name, Path); @@ -722,8 +805,7 @@ Members.push_back(std::move(*NMOrErr)); } -static void addMember(std::vector &Members, - StringRef FileName, bool FlattenArchive = false) { +static NewArchiveMember getArchiveMember(StringRef FileName) { Expected NMOrErr = NewArchiveMember::getFile(FileName, Deterministic); failIfError(NMOrErr.takeError(), FileName); @@ -743,9 +825,24 @@ PathOrErr ? *PathOrErr : sys::path::convert_to_slash(FileName)); } } + return std::move(*NMOrErr); +} + +static void addMember(std::vector &Members, + NewArchiveMember &NM) { + Members.push_back(std::move(NM)); +} + +static void addMember(std::vector &Members, + StringRef FileName, bool FlattenArchive = false) { + NewArchiveMember NM = getArchiveMember(FileName); + if (!isValidInBitMode(NM)) { + warnInvalidObjectForFileMode(FileName); + return; + } if (FlattenArchive && - identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { + identify_magic(NM.Buf->getBuffer()) == file_magic::archive) { object::Archive &Lib = readLibrary(FileName); // When creating thin archives, only flatten if the member is also thin. if (!Thin || Lib.isThin()) { @@ -757,7 +854,7 @@ return; } } - Members.push_back(std::move(*NMOrErr)); + Members.push_back(std::move(NM)); } enum InsertAction { @@ -773,6 +870,9 @@ StringRef Name, std::vector::iterator &Pos, StringMap &MemberCount) { + if (!isValidInBitMode(Member)) + return IA_AddOldMember; + if (Operation == QuickAppend || Members.empty()) return IA_AddOldMember; auto MI = find_if( @@ -834,7 +934,7 @@ Expected NameOrErr = Child.getName(); failIfError(NameOrErr.takeError()); std::string Name = std::string(NameOrErr.get()); - if (comparePaths(Name, RelPos)) { + if (comparePaths(Name, RelPos) && isValidInBitMode(Child)) { assert(AddAfter || AddBefore); if (AddBefore) InsertPos = Pos; @@ -845,12 +945,25 @@ std::vector::iterator MemberI = Members.end(); InsertAction Action = computeInsertAction(Operation, Child, Name, MemberI, MemberCount); + + auto HandleNewMember = [](auto Member, auto &Members, auto &Child) { + NewArchiveMember NM = getArchiveMember(*Member); + if (isValidInBitMode(NM)) + addMember(Members, NM); + else { + // If a new member is not a valid object for the bit mode, add + // the old member back. + warnInvalidObjectForFileMode(*Member); + addChildMember(Members, Child, /*FlattenArchive=*/Thin); + } + }; + switch (Action) { case IA_AddOldMember: addChildMember(Ret, Child, /*FlattenArchive=*/Thin); break; case IA_AddNewMember: - addMember(Ret, *MemberI); + HandleNewMember(MemberI, Ret, Child); break; case IA_Delete: break; @@ -858,7 +971,7 @@ addChildMember(Moved, Child, /*FlattenArchive=*/Thin); break; case IA_MoveNewMember: - addMember(Moved, *MemberI); + HandleNewMember(MemberI, Moved, Child); break; } // When processing elements with the count param, we need to preserve the @@ -1043,8 +1156,7 @@ } else { if (!Create) { // Produce a warning if we should and we're creating the archive - WithColor::warning(errs(), ToolName) - << "creating " << ArchiveName << "\n"; + warn("creating " + ArchiveName); } } @@ -1155,6 +1267,15 @@ return false; } +static BitModeTy getBitMode(const char *RawBitMode) { + return StringSwitch(RawBitMode) + .Case("32", BitModeTy::Bit32) + .Case("64", BitModeTy::Bit64) + .Case("32_64", BitModeTy::Bit32_64) + .Case("any", BitModeTy::Any) + .Default(BitModeTy::Unknown); +} + static const char *matchFlagWithArg(StringRef Expected, ArrayRef::iterator &ArgIt, ArrayRef Args) { @@ -1204,6 +1325,14 @@ cl::ExpandResponseFiles(Saver, getRspQuoting(makeArrayRef(argv, argc)), Argv); + // Get BitMode from enviorment variable "OBJECT_MODE" for AIX OS, if + // specified. + if (object::Archive::getDefaultKindForHost() == object::Archive::K_AIXBIG) { + BitMode = getBitMode(getenv("OBJECT_MODE")); + if (BitMode == BitModeTy::Unknown) + BitMode = BitModeTy::Bit32; + } + for (ArrayRef::iterator ArgIt = Argv.begin(); ArgIt != Argv.end(); ++ArgIt) { const char *Match = nullptr; @@ -1258,6 +1387,19 @@ matchFlagWithArg("rsp-quoting", ArgIt, Argv)) continue; + if (strncmp(*ArgIt, "-X", 2) == 0) { + if (object::Archive::getDefaultKindForHost() == + object::Archive::K_AIXBIG) { + Match = *(*ArgIt + 2) != '\0' ? *ArgIt + 2 : *(++ArgIt); + BitMode = getBitMode(Match); + if (BitMode == BitModeTy::Unknown) + fail(Twine("invalid bit mode: ") + Match); + continue; + } else { + fail(Twine(*ArgIt) + " option not supported on non AIX OS"); + } + } + Options += *ArgIt + 1; }