Skip to content

Commit fe3b621

Browse files
committed
[AIX] support write operation of big archive.
SUMMARY 1. Enable supporting the write operation of big archive. 2. the first commit come from https://reviews.llvm.org/D104367 3. refactor the first commit and implement writing symbol table. 4. fixed the bugs and add more test cases in the second commit. Reviewers: James Henderson Differential Revision: https://reviews.llvm.org/D123949
1 parent 98f82d6 commit fe3b621

40 files changed

+202
-99
lines changed

llvm/include/llvm/Object/Archive.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ class Archive : public Binary {
391391
};
392392

393393
class BigArchive : public Archive {
394+
public:
394395
/// Fixed-Length Header.
395396
struct FixLenHdr {
396397
char Magic[sizeof(BigArchiveMagic) - 1]; ///< Big archive magic string.

llvm/lib/Object/ArchiveWriter.cpp

Lines changed: 163 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "llvm/Support/Errc.h"
2626
#include "llvm/Support/ErrorHandling.h"
2727
#include "llvm/Support/Format.h"
28+
#include "llvm/Support/MathExtras.h"
2829
#include "llvm/Support/Path.h"
2930
#include "llvm/Support/SmallVectorMemoryBuffer.h"
3031
#include "llvm/Support/raw_ostream.h"
@@ -127,16 +128,20 @@ static bool isDarwin(object::Archive::Kind Kind) {
127128
Kind == object::Archive::K_DARWIN64;
128129
}
129130

131+
static bool isAIXBigArchive(object::Archive::Kind Kind) {
132+
return Kind == object::Archive::K_AIXBIG;
133+
}
134+
130135
static bool isBSDLike(object::Archive::Kind Kind) {
131136
switch (Kind) {
132137
case object::Archive::K_GNU:
133138
case object::Archive::K_GNU64:
139+
case object::Archive::K_AIXBIG:
134140
return false;
135141
case object::Archive::K_BSD:
136142
case object::Archive::K_DARWIN:
137143
case object::Archive::K_DARWIN64:
138144
return true;
139-
case object::Archive::K_AIXBIG:
140145
case object::Archive::K_COFF:
141146
break;
142147
}
@@ -189,6 +194,31 @@ printBSDMemberHeader(raw_ostream &Out, uint64_t Pos, StringRef Name,
189194
Out.write(uint8_t(0));
190195
}
191196

197+
static void
198+
printBigArchiveMemberHeader(raw_ostream &Out, StringRef Name,
199+
const sys::TimePoint<std::chrono::seconds> &ModTime,
200+
unsigned UID, unsigned GID, unsigned Perms,
201+
uint64_t Size, unsigned PrevOffset,
202+
unsigned NextOffset) {
203+
unsigned NameLen = Name.size();
204+
205+
printWithSpacePadding(Out, Size, 20); // File member size
206+
printWithSpacePadding(Out, NextOffset, 20); // Next member header offset
207+
printWithSpacePadding(Out, PrevOffset, 20); // Previous member header offset
208+
printWithSpacePadding(Out, sys::toTimeT(ModTime), 12); // File member date
209+
// The big archive format has 12 chars for uid and gid.
210+
printWithSpacePadding(Out, UID % 1000000000000, 12); // UID
211+
printWithSpacePadding(Out, GID % 1000000000000, 12); // GID
212+
printWithSpacePadding(Out, format("%o", Perms), 12); // Permission
213+
printWithSpacePadding(Out, NameLen, 4); // Name length
214+
if (NameLen) {
215+
printWithSpacePadding(Out, Name, NameLen); // Name
216+
if (NameLen % 2)
217+
Out.write(uint8_t(0)); // Null byte padding
218+
}
219+
Out << "`\n"; // Terminator
220+
}
221+
192222
static bool useStringTable(bool Thin, StringRef Name) {
193223
return Thin || Name.size() >= 16 || Name.contains('/');
194224
}
@@ -199,8 +229,8 @@ static bool is64BitKind(object::Archive::Kind Kind) {
199229
case object::Archive::K_BSD:
200230
case object::Archive::K_DARWIN:
201231
case object::Archive::K_COFF:
202-
case object::Archive::K_AIXBIG:
203232
return false;
233+
case object::Archive::K_AIXBIG:
204234
case object::Archive::K_DARWIN64:
205235
case object::Archive::K_GNU64:
206236
return true;
@@ -304,19 +334,27 @@ static uint64_t computeSymbolTableSize(object::Archive::Kind Kind,
304334
// least 4-byte aligned for 32-bit content. Opt for the larger encoding
305335
// uniformly.
306336
// We do this for all bsd formats because it simplifies aligning members.
307-
uint32_t Pad = offsetToAlignment(Size, Align(isBSDLike(Kind) ? 8 : 2));
337+
// For the big archive format, the symbol table is the last member, so there
338+
// is no need to align.
339+
uint32_t Pad = isAIXBigArchive(Kind)
340+
? 0
341+
: offsetToAlignment(Size, Align(isBSDLike(Kind) ? 8 : 2));
308342
Size += Pad;
309343
if (Padding)
310344
*Padding = Pad;
311345
return Size;
312346
}
313347

314348
static void writeSymbolTableHeader(raw_ostream &Out, object::Archive::Kind Kind,
315-
bool Deterministic, uint64_t Size) {
349+
bool Deterministic, uint64_t Size,
350+
uint64_t PrevMemberOffset = 0) {
316351
if (isBSDLike(Kind)) {
317352
const char *Name = is64BitKind(Kind) ? "__.SYMDEF_64" : "__.SYMDEF";
318353
printBSDMemberHeader(Out, Out.tell(), Name, now(Deterministic), 0, 0, 0,
319354
Size);
355+
} else if (isAIXBigArchive(Kind)) {
356+
printBigArchiveMemberHeader(Out, "", now(Deterministic), 0, 0,
357+
0, Size, PrevMemberOffset, 0);
320358
} else {
321359
const char *Name = is64BitKind(Kind) ? "/SYM64" : "";
322360
printGNUSmallMemberHeader(Out, Name, now(Deterministic), 0, 0, 0, Size);
@@ -325,7 +363,8 @@ static void writeSymbolTableHeader(raw_ostream &Out, object::Archive::Kind Kind,
325363

326364
static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind,
327365
bool Deterministic, ArrayRef<MemberData> Members,
328-
StringRef StringTable) {
366+
StringRef StringTable,
367+
uint64_t PrevMemberOffset = 0) {
329368
// We don't write a symbol table on an archive with no members -- except on
330369
// Darwin, where the linker will abort unless the archive has a symbol table.
331370
if (StringTable.empty() && !isDarwin(Kind))
@@ -338,9 +377,10 @@ static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind,
338377
uint64_t OffsetSize = is64BitKind(Kind) ? 8 : 4;
339378
uint32_t Pad;
340379
uint64_t Size = computeSymbolTableSize(Kind, NumSyms, OffsetSize, StringTable, &Pad);
341-
writeSymbolTableHeader(Out, Kind, Deterministic, Size);
380+
writeSymbolTableHeader(Out, Kind, Deterministic, Size, PrevMemberOffset);
342381

343-
uint64_t Pos = Out.tell() + Size;
382+
uint64_t Pos = isAIXBigArchive(Kind) ? sizeof(object::BigArchive::FixLenHdr)
383+
: Out.tell() + Size;
344384

345385
if (isBSDLike(Kind))
346386
printNBits(Out, Kind, NumSyms * 2 * OffsetSize);
@@ -409,9 +449,8 @@ computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
409449
bool NeedSymbols, ArrayRef<NewArchiveMember> NewMembers) {
410450
static char PaddingData[8] = {'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'};
411451

412-
// This ignores the symbol table, but we only need the value mod 8 and the
413-
// symbol table is aligned to be a multiple of 8 bytes
414-
uint64_t Pos = 0;
452+
uint64_t Pos =
453+
isAIXBigArchive(Kind) ? sizeof(object::BigArchive::FixLenHdr) : 0;
415454

416455
std::vector<MemberData> Ret;
417456
bool HasObject = false;
@@ -471,6 +510,9 @@ computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
471510
Entry.second = Entry.second > 1 ? 1 : 0;
472511
}
473512

513+
// The big archive format needs to know the offset of the previous member
514+
// header.
515+
unsigned PrevOffset = 0;
474516
for (const NewArchiveMember &M : NewMembers) {
475517
std::string Header;
476518
raw_string_ostream Out(Header);
@@ -503,8 +545,16 @@ computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
503545
std::move(StringMsg), object::object_error::parse_failed);
504546
}
505547

506-
printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, M,
507-
ModTime, Size);
548+
if (isAIXBigArchive(Kind)) {
549+
unsigned NextOffset = Pos + sizeof(object::BigArMemHdrType) +
550+
alignTo(M.MemberName.size(), 2) + alignTo(Size, 2);
551+
printBigArchiveMemberHeader(Out, M.MemberName, ModTime, M.UID, M.GID,
552+
M.Perms, Size, PrevOffset, NextOffset);
553+
PrevOffset = Pos;
554+
} else {
555+
printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, M,
556+
ModTime, Size);
557+
}
508558
Out.flush();
509559

510560
std::vector<unsigned> Symbols;
@@ -588,22 +638,25 @@ static Error writeArchiveToStream(raw_ostream &Out,
588638
return E;
589639
std::vector<MemberData> &Data = *DataOrErr;
590640

591-
if (!StringTableBuf.empty())
641+
if (!StringTableBuf.empty() && !isAIXBigArchive(Kind))
592642
Data.insert(Data.begin(), computeStringTable(StringTableBuf));
593643

594644
// We would like to detect if we need to switch to a 64-bit symbol table.
595-
if (WriteSymtab) {
596-
uint64_t MaxOffset = 8; // For the file signature.
597-
uint64_t LastOffset = MaxOffset;
598-
uint64_t NumSyms = 0;
599-
for (const auto &M : Data) {
600-
// Record the start of the member's offset
601-
LastOffset = MaxOffset;
602-
// Account for the size of each part associated with the member.
603-
MaxOffset += M.Header.size() + M.Data.size() + M.Padding.size();
604-
NumSyms += M.Symbols.size();
605-
}
645+
uint64_t LastMemberEndOffset =
646+
isAIXBigArchive(Kind) ? sizeof(object::BigArchive::FixLenHdr) : 8;
647+
uint64_t LastMemberHeaderOffset = LastMemberEndOffset;
648+
uint64_t NumSyms = 0;
649+
for (const auto &M : Data) {
650+
// Record the start of the member's offset
651+
LastMemberHeaderOffset = LastMemberEndOffset;
652+
// Account for the size of each part associated with the member.
653+
LastMemberEndOffset += M.Header.size() + M.Data.size() + M.Padding.size();
654+
NumSyms += M.Symbols.size();
655+
}
606656

657+
// The symbol table is put at the end of the big archive file. The symbol
658+
// table is at the start of the archive file for other archive formats.
659+
if (WriteSymtab && !isAIXBigArchive(Kind)) {
607660
// We assume 32-bit offsets to see if 32-bit symbols are possible or not.
608661
uint64_t SymtabSize = computeSymbolTableSize(Kind, NumSyms, 4, SymNamesBuf);
609662
auto computeSymbolTableHeaderSize =
@@ -613,7 +666,7 @@ static Error writeArchiveToStream(raw_ostream &Out,
613666
writeSymbolTableHeader(Tmp, Kind, Deterministic, SymtabSize);
614667
return TmpBuf.size();
615668
};
616-
LastOffset += computeSymbolTableHeaderSize() + SymtabSize;
669+
LastMemberHeaderOffset += computeSymbolTableHeaderSize() + SymtabSize;
617670

618671
// The SYM64 format is used when an archive's member offsets are larger than
619672
// 32-bits can hold. The need for this shift in format is detected by
@@ -627,10 +680,10 @@ static Error writeArchiveToStream(raw_ostream &Out,
627680
if (Sym64Env)
628681
StringRef(Sym64Env).getAsInteger(10, Sym64Threshold);
629682

630-
// If LastOffset isn't going to fit in a 32-bit varible we need to switch
631-
// to 64-bit. Note that the file can be larger than 4GB as long as the last
632-
// member starts before the 4GB offset.
633-
if (LastOffset >= Sym64Threshold) {
683+
// If LastMemberHeaderOffset isn't going to fit in a 32-bit varible we need
684+
// to switch to 64-bit. Note that the file can be larger than 4GB as long as
685+
// the last member starts before the 4GB offset.
686+
if (LastMemberHeaderOffset >= Sym64Threshold) {
634687
if (Kind == object::Archive::K_DARWIN)
635688
Kind = object::Archive::K_DARWIN64;
636689
else
@@ -640,15 +693,92 @@ static Error writeArchiveToStream(raw_ostream &Out,
640693

641694
if (Thin)
642695
Out << "!<thin>\n";
696+
else if (isAIXBigArchive(Kind))
697+
Out << "<bigaf>\n";
643698
else
644699
Out << "!<arch>\n";
645700

646-
if (WriteSymtab)
647-
writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf);
701+
if (!isAIXBigArchive(Kind)) {
702+
if (WriteSymtab)
703+
writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf);
704+
for (const MemberData &M : Data)
705+
Out << M.Header << M.Data << M.Padding;
706+
} else {
707+
// For the big archive (AIX) format, compute a table of member names and
708+
// offsets, used in the member table.
709+
uint64_t MemberTableNameStrTblSize = 0;
710+
std::vector<size_t> MemberOffsets;
711+
std::vector<StringRef> MemberNames;
712+
// Loop across object to find offset and names.
713+
uint64_t MemberEndOffset = sizeof(object::BigArchive::FixLenHdr);
714+
for (size_t I = 0, Size = NewMembers.size(); I != Size; ++I) {
715+
const NewArchiveMember &Member = NewMembers[I];
716+
MemberTableNameStrTblSize += Member.MemberName.size() + 1;
717+
MemberOffsets.push_back(MemberEndOffset);
718+
MemberNames.push_back(Member.MemberName);
719+
// File member name ended with "`\n". The length is included in
720+
// BigArMemHdrType.
721+
MemberEndOffset += sizeof(object::BigArMemHdrType) +
722+
alignTo(Data[I].Data.size(), 2) +
723+
alignTo(Member.MemberName.size(), 2);
724+
}
648725

649-
for (const MemberData &M : Data)
650-
Out << M.Header << M.Data << M.Padding;
726+
// AIX member table size.
727+
unsigned MemberTableSize = 20 + // Number of members field
728+
20 * MemberOffsets.size() +
729+
MemberTableNameStrTblSize;
730+
731+
unsigned GlobalSymbolOffset =
732+
(WriteSymtab && NumSyms > 0)
733+
? LastMemberEndOffset +
734+
alignTo(sizeof(object::BigArMemHdrType) + MemberTableSize, 2)
735+
: 0;
736+
737+
// Fixed Sized Header.
738+
printWithSpacePadding(Out, NewMembers.size() ? LastMemberEndOffset : 0,
739+
20); // Offset to member table
740+
// If there are no file members in the archive, there will be no global
741+
// symbol table.
742+
printWithSpacePadding(Out, NewMembers.size() ? GlobalSymbolOffset : 0, 20);
743+
printWithSpacePadding(
744+
Out, 0,
745+
20); // Offset to 64 bits global symbol table - Not supported yet
746+
printWithSpacePadding(
747+
Out, NewMembers.size() ? sizeof(object::BigArchive::FixLenHdr) : 0,
748+
20); // Offset to first archive member
749+
printWithSpacePadding(Out, NewMembers.size() ? LastMemberHeaderOffset : 0,
750+
20); // Offset to last archive member
751+
printWithSpacePadding(
752+
Out, 0,
753+
20); // Offset to first member of free list - Not supported yet
754+
755+
for (const MemberData &M : Data) {
756+
Out << M.Header << M.Data;
757+
if (M.Data.size() % 2)
758+
Out << '\0';
759+
}
651760

761+
if (NewMembers.size()) {
762+
// Member table.
763+
printBigArchiveMemberHeader(Out, "", sys::toTimePoint(0), 0, 0, 0,
764+
MemberTableSize, LastMemberHeaderOffset,
765+
GlobalSymbolOffset);
766+
printWithSpacePadding(Out, MemberOffsets.size(), 20); // Number of members
767+
for (uint64_t MemberOffset : MemberOffsets)
768+
printWithSpacePadding(Out, MemberOffset,
769+
20); // Offset to member file header.
770+
for (StringRef MemberName : MemberNames)
771+
Out << MemberName << '\0'; // Member file name, null byte padding.
772+
773+
if (MemberTableNameStrTblSize % 2)
774+
Out << '\0'; // Name table must be tail padded to an even number of
775+
// bytes.
776+
777+
if (WriteSymtab && NumSyms > 0)
778+
writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf,
779+
LastMemberEndOffset);
780+
}
781+
}
652782
Out.flush();
653783
return Error::success();
654784
}

llvm/test/Object/ar-create.test

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
XFAIL: system-aix
21
Test which operations create an archive and which don't.
32

43
RUN: touch %t

llvm/test/Object/archive-extract-dir.test

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
XFAIL: system-aix
2-
31
RUN: mkdir -p %t
42
RUN: cd %t
53
RUN: rm -rf foo

llvm/test/Object/archive-malformed-object.test

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# XFAIL: system-aix
21
## Show that the archive library emits error messages when adding malformed
32
## objects.
43

llvm/test/Object/archive-replace-pos.test

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
XFAIL: system-aix
2-
31
Test adding a member to a particular position
42

53
RUN: touch %t.foo

llvm/test/Object/archive-unknown-filetype.test

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# XFAIL: system-aix
21
## Show that the archive library does not emit an error or add any symbols to
32
## the archive symbol table, when it encounters an unknown file type, but still
43
## adds the file to the archive.

llvm/test/Object/archive-update.test

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
XFAIL: system-aix
2-
31
Test the 'u' option of llvm-ar
42

53
RUN: rm -rf %t && mkdir -p %t && cd %t

llvm/test/Object/directory.ll

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
; XFAIL: system-aix
2-
31
;RUN: rm -rf %t && mkdir -p %t
42
;RUN: not llvm-ar r %t/test.a . 2>&1 | FileCheck -DMSG=%errc_EISDIR %s
53
;CHECK: .: [[MSG]]

llvm/test/tools/llvm-ar/count.test

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# XFAIL: system-aix
21
# Test the 'N' count parameter.
32

43
# Get a temp clean cwd to extract into.

llvm/test/tools/llvm-ar/create.test

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
XFAIL: system-aix
21
## Test the creation warning and supression of that warning.
32

43
RUN: touch %t1.txt

llvm/test/tools/llvm-ar/dash-before-letter.test

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
XFAIL: system-aix
21
# Test the use of dash before key letters.
32

43
RUN: touch %t1.txt

0 commit comments

Comments
 (0)