Skip to content

Commit 4cc7c74

Browse files
committed
[AIX] Align the content of an xcoff object file which has auxiliary header in big archive.
Summary: if the member file is XCOFF object file and has auxiliary header, the content of the member file need to be aligned at the MAX(maximum alignment of .text , maximum alignment of .data). The "maximum alignment of .text" and "maximum alignment of .data" are two field of auxiliary header of XCOFF object file. Reviewers: James Henderson, Stephen Peckham Differential Revision: https://reviews.llvm.org/D144872
1 parent 785e706 commit 4cc7c74

File tree

3 files changed

+286
-76
lines changed

3 files changed

+286
-76
lines changed

llvm/lib/Object/ArchiveWriter.cpp

Lines changed: 163 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@ struct MemberData {
331331
std::string Header;
332332
StringRef Data;
333333
StringRef Padding;
334+
uint64_t PreHeadPadSize = 0;
335+
std::unique_ptr<SymbolicFile> SymFile = nullptr;
334336
};
335337
} // namespace
336338

@@ -496,21 +498,66 @@ getSymbolicFile(MemoryBufferRef Buf, LLVMContext &Context) {
496498
}
497499
}
498500

499-
static Expected<bool> is64BitSymbolicFile(const StringRef &ObjStringRef) {
500-
MemoryBufferRef ObjMbf(ObjStringRef, "");
501-
// In the scenario when LLVMContext is populated SymbolicFile will contain a
502-
// reference to it, thus SymbolicFile should be destroyed first.
503-
LLVMContext Context;
504-
Expected<std::unique_ptr<SymbolicFile>> ObjOrErr =
505-
getSymbolicFile(ObjMbf, Context);
506-
if (!ObjOrErr)
507-
return ObjOrErr.takeError();
501+
static bool is64BitSymbolicFile(const SymbolicFile *SymObj) {
502+
return SymObj != nullptr ? SymObj->is64Bit() : false;
503+
}
508504

509-
// Treat non-symbolic file types as not 64-bits.
510-
if (!*ObjOrErr)
511-
return false;
505+
// Log2 of PAGESIZE(4096) on an AIX system.
506+
static const uint32_t Log2OfAIXPageSize = 12;
507+
508+
// In the AIX big archive format, since the data content follows the member file
509+
// name, if the name ends on an odd byte, an extra byte will be added for
510+
// padding. This ensures that the data within the member file starts at an even
511+
// byte.
512+
static const uint32_t MinBigArchiveMemDataAlign = 2;
513+
514+
template <typename AuxiliaryHeader>
515+
uint16_t getAuxMaxAlignment(uint16_t AuxHeaderSize, AuxiliaryHeader *AuxHeader,
516+
uint16_t Log2OfMaxAlign) {
517+
// If the member doesn't have an auxiliary header, it isn't a loadable object
518+
// and so it just needs aligning at the minimum value.
519+
if (AuxHeader == nullptr)
520+
return MinBigArchiveMemDataAlign;
521+
522+
// If the auxiliary header does not have both MaxAlignOfData and
523+
// MaxAlignOfText field, it is not a loadable shared object file, so align at
524+
// the minimum value. The 'ModuleType' member is located right after
525+
// 'MaxAlignOfData' in the AuxiliaryHeader.
526+
if (AuxHeaderSize < offsetof(AuxiliaryHeader, ModuleType))
527+
return MinBigArchiveMemDataAlign;
528+
529+
// If the XCOFF object file does not have a loader section, it is not
530+
// loadable, so align at the minimum value.
531+
if (AuxHeader->SecNumOfLoader == 0)
532+
return MinBigArchiveMemDataAlign;
533+
534+
// The content of the loadable member file needs to be aligned at MAX(maximum
535+
// alignment of .text, maximum alignment of .data) if there are both fields.
536+
// If the desired alignment is > PAGESIZE, 32-bit members are aligned on a
537+
// word boundary, while 64-bit members are aligned on a PAGESIZE(2^12=4096)
538+
// boundary.
539+
uint16_t Log2OfAlign =
540+
std::max(AuxHeader->MaxAlignOfText, AuxHeader->MaxAlignOfData);
541+
return 1 << (Log2OfAlign > Log2OfAIXPageSize ? Log2OfMaxAlign : Log2OfAlign);
542+
}
512543

513-
return (*ObjOrErr)->is64Bit();
544+
// AIX big archives may contain shared object members. The AIX OS requires these
545+
// members to be aligned if they are 64-bit and recommends it for 32-bit
546+
// members. This ensures that when these members are loaded they are aligned in
547+
// memory.
548+
static uint32_t getMemberAlignment(SymbolicFile *SymObj) {
549+
XCOFFObjectFile *XCOFFObj = dyn_cast_or_null<XCOFFObjectFile>(SymObj);
550+
if (!XCOFFObj)
551+
return MinBigArchiveMemDataAlign;
552+
553+
// If the desired alignment is > PAGESIZE, 32-bit members are aligned on a
554+
// word boundary, while 64-bit members are aligned on a PAGESIZE boundary.
555+
return XCOFFObj->is64Bit()
556+
? getAuxMaxAlignment(XCOFFObj->fileHeader64()->AuxHeaderSize,
557+
XCOFFObj->auxiliaryHeader64(),
558+
Log2OfAIXPageSize)
559+
: getAuxMaxAlignment(XCOFFObj->fileHeader32()->AuxHeaderSize,
560+
XCOFFObj->auxiliaryHeader32(), 2);
514561
}
515562

516563
static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind,
@@ -539,13 +586,8 @@ static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind,
539586
uint64_t Pos = MembersOffset;
540587
for (const MemberData &M : Members) {
541588
if (isAIXBigArchive(Kind)) {
542-
Expected<bool> Is64BitOrErr = is64BitSymbolicFile(M.Data);
543-
// If there is an error, the error will have been emitted when
544-
// 'computeMemberData' called the 'getSymbol' function, so don't need to
545-
// handle it here.
546-
if (!Is64BitOrErr)
547-
cantFail(Is64BitOrErr.takeError());
548-
if (*Is64BitOrErr != Is64Bit) {
589+
Pos += M.PreHeadPadSize;
590+
if (is64BitSymbolicFile(M.SymFile.get()) != Is64Bit) {
549591
Pos += M.Header.size() + M.Data.size() + M.Padding.size();
550592
continue;
551593
}
@@ -629,29 +671,19 @@ static bool isECObject(object::SymbolicFile &Obj) {
629671
return false;
630672
}
631673

632-
static Expected<std::vector<unsigned>>
633-
getSymbols(MemoryBufferRef Buf, uint16_t Index, raw_ostream &SymNames,
634-
SymMap *SymMap, bool &HasObject) {
635-
// In the scenario when LLVMContext is populated SymbolicFile will contain a
636-
// reference to it, thus SymbolicFile should be destroyed first.
637-
LLVMContext Context;
638-
674+
static Expected<std::vector<unsigned>> getSymbols(SymbolicFile *Obj,
675+
uint16_t Index,
676+
raw_ostream &SymNames,
677+
SymMap *SymMap) {
639678
std::vector<unsigned> Ret;
640-
Expected<std::unique_ptr<SymbolicFile>> ObjOrErr =
641-
getSymbolicFile(Buf, Context);
642-
if (!ObjOrErr)
643-
return ObjOrErr.takeError();
644679

645-
// If the member is non-symbolic file, treat it as having no symbols.
646-
if (!*ObjOrErr)
680+
if (Obj == nullptr)
647681
return Ret;
648682

649-
std::unique_ptr<object::SymbolicFile> Obj = std::move(*ObjOrErr);
650-
651683
std::map<std::string, uint16_t> *Map = nullptr;
652684
if (SymMap)
653685
Map = SymMap->UseECMap && isECObject(*Obj) ? &SymMap->ECMap : &SymMap->Map;
654-
HasObject = true;
686+
655687
for (const object::BasicSymbolRef &S : Obj->symbols()) {
656688
if (!isArchiveSymbol(S))
657689
continue;
@@ -681,9 +713,9 @@ static Expected<std::vector<MemberData>>
681713
computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
682714
object::Archive::Kind Kind, bool Thin, bool Deterministic,
683715
SymtabWritingMode NeedSymbols, SymMap *SymMap,
684-
ArrayRef<NewArchiveMember> NewMembers) {
716+
LLVMContext &Context, ArrayRef<NewArchiveMember> NewMembers) {
685717
static char PaddingData[8] = {'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'};
686-
718+
uint64_t MemHeadPadSize = 0;
687719
uint64_t Pos =
688720
isAIXBigArchive(Kind) ? sizeof(object::BigArchive::FixLenHdr) : 0;
689721

@@ -748,12 +780,16 @@ computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
748780
// The big archive format needs to know the offset of the previous member
749781
// header.
750782
uint64_t PrevOffset = 0;
783+
uint64_t NextMemHeadPadSize = 0;
784+
std::unique_ptr<SymbolicFile> CurSymFile;
785+
std::unique_ptr<SymbolicFile> NextSymFile;
751786
uint16_t Index = 0;
752-
for (const NewArchiveMember &M : NewMembers) {
787+
788+
for (auto M = NewMembers.begin(); M < NewMembers.end(); ++M) {
753789
std::string Header;
754790
raw_string_ostream Out(Header);
755791

756-
MemoryBufferRef Buf = M.Buf->getMemBufferRef();
792+
MemoryBufferRef Buf = M->Buf->getMemBufferRef();
757793
StringRef Data = Thin ? "" : Buf.getBuffer();
758794

759795
Index++;
@@ -771,41 +807,94 @@ computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
771807
sys::TimePoint<std::chrono::seconds> ModTime;
772808
if (UniqueTimestamps)
773809
// Increment timestamp for each file of a given name.
774-
ModTime = sys::toTimePoint(FilenameCount[M.MemberName]++);
810+
ModTime = sys::toTimePoint(FilenameCount[M->MemberName]++);
775811
else
776-
ModTime = M.ModTime;
812+
ModTime = M->ModTime;
777813

778814
uint64_t Size = Buf.getBufferSize() + MemberPadding;
779815
if (Size > object::Archive::MaxMemberSize) {
780816
std::string StringMsg =
781-
"File " + M.MemberName.str() + " exceeds size limit";
817+
"File " + M->MemberName.str() + " exceeds size limit";
782818
return make_error<object::GenericBinaryError>(
783819
std::move(StringMsg), object::object_error::parse_failed);
784820
}
785821

822+
if (NeedSymbols != SymtabWritingMode::NoSymtab || isAIXBigArchive(Kind)) {
823+
auto SetNextSymFile = [&NextSymFile,
824+
&Context](MemoryBufferRef Buf,
825+
StringRef MemberName) -> Error {
826+
Expected<std::unique_ptr<SymbolicFile>> SymFileOrErr =
827+
getSymbolicFile(Buf, Context);
828+
if (!SymFileOrErr)
829+
return createFileError(MemberName, SymFileOrErr.takeError());
830+
NextSymFile = std::move(*SymFileOrErr);
831+
return Error::success();
832+
};
833+
834+
if (M == NewMembers.begin())
835+
if (Error Err = SetNextSymFile(Buf, M->MemberName))
836+
return std::move(Err);
837+
838+
CurSymFile = std::move(NextSymFile);
839+
840+
if ((M + 1) != NewMembers.end())
841+
if (Error Err = SetNextSymFile((M + 1)->Buf->getMemBufferRef(),
842+
(M + 1)->MemberName))
843+
return std::move(Err);
844+
}
845+
846+
// In the big archive file format, we need to calculate and include the next
847+
// member offset and previous member offset in the file member header.
786848
if (isAIXBigArchive(Kind)) {
849+
uint64_t OffsetToMemData = Pos + sizeof(object::BigArMemHdrType) +
850+
alignTo(M->MemberName.size(), 2);
851+
852+
if (M == NewMembers.begin())
853+
NextMemHeadPadSize =
854+
alignToPowerOf2(OffsetToMemData,
855+
getMemberAlignment(CurSymFile.get())) -
856+
OffsetToMemData;
857+
858+
MemHeadPadSize = NextMemHeadPadSize;
859+
Pos += MemHeadPadSize;
787860
uint64_t NextOffset = Pos + sizeof(object::BigArMemHdrType) +
788-
alignTo(M.MemberName.size(), 2) + alignTo(Size, 2);
789-
printBigArchiveMemberHeader(Out, M.MemberName, ModTime, M.UID, M.GID,
790-
M.Perms, Size, PrevOffset, NextOffset);
861+
alignTo(M->MemberName.size(), 2) + alignTo(Size, 2);
862+
863+
// If there is another member file after this, we need to calculate the
864+
// padding before the header.
865+
if ((M + 1) != NewMembers.end()) {
866+
uint64_t OffsetToNextMemData = NextOffset +
867+
sizeof(object::BigArMemHdrType) +
868+
alignTo((M + 1)->MemberName.size(), 2);
869+
NextMemHeadPadSize =
870+
alignToPowerOf2(OffsetToNextMemData,
871+
getMemberAlignment(NextSymFile.get())) -
872+
OffsetToNextMemData;
873+
NextOffset += NextMemHeadPadSize;
874+
}
875+
printBigArchiveMemberHeader(Out, M->MemberName, ModTime, M->UID, M->GID,
876+
M->Perms, Size, PrevOffset, NextOffset);
791877
PrevOffset = Pos;
792878
} else {
793-
printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, M,
879+
printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, *M,
794880
ModTime, Size);
795881
}
796882
Out.flush();
797883

798884
std::vector<unsigned> Symbols;
799885
if (NeedSymbols != SymtabWritingMode::NoSymtab) {
800886
Expected<std::vector<unsigned>> SymbolsOrErr =
801-
getSymbols(Buf, Index, SymNames, SymMap, HasObject);
887+
getSymbols(CurSymFile.get(), Index, SymNames, SymMap);
802888
if (!SymbolsOrErr)
803-
return createFileError(M.MemberName, SymbolsOrErr.takeError());
889+
return createFileError(M->MemberName, SymbolsOrErr.takeError());
804890
Symbols = std::move(*SymbolsOrErr);
891+
if (CurSymFile)
892+
HasObject = true;
805893
}
806894

807895
Pos += Header.size() + Data.size() + Padding.size();
808-
Ret.push_back({std::move(Symbols), std::move(Header), Data, Padding});
896+
Ret.push_back({std::move(Symbols), std::move(Header), Data, Padding,
897+
MemHeadPadSize, std::move(CurSymFile)});
809898
}
810899
// If there are no symbols, emit an empty symbol table, to satisfy Solaris
811900
// tools, older versions of which expect a symbol table in a non-empty
@@ -876,10 +965,14 @@ static Error writeArchiveToStream(raw_ostream &Out,
876965
if (isCOFFArchive(Kind) && NewMembers.size() > 0xfffe)
877966
Kind = object::Archive::K_GNU;
878967

968+
// In the scenario when LLVMContext is populated SymbolicFile will contain a
969+
// reference to it, thus SymbolicFile should be destroyed first.
970+
LLVMContext Context;
971+
879972
SymMap.UseECMap = IsEC;
880973
Expected<std::vector<MemberData>> DataOrErr = computeMemberData(
881974
StringTable, SymNames, Kind, Thin, Deterministic, WriteSymtab,
882-
isCOFFArchive(Kind) ? &SymMap : nullptr, NewMembers);
975+
isCOFFArchive(Kind) ? &SymMap : nullptr, Context, NewMembers);
883976
if (Error E = DataOrErr.takeError())
884977
return E;
885978
std::vector<MemberData> &Data = *DataOrErr;
@@ -902,6 +995,7 @@ static Error writeArchiveToStream(raw_ostream &Out,
902995

903996
for (const auto &M : Data) {
904997
// Record the start of the member's offset
998+
LastMemberEndOffset += M.PreHeadPadSize;
905999
LastMemberHeaderOffset = LastMemberEndOffset;
9061000
// Account for the size of each part associated with the member.
9071001
LastMemberEndOffset += M.Header.size() + M.Data.size() + M.Padding.size();
@@ -913,13 +1007,9 @@ static Error writeArchiveToStream(raw_ostream &Out,
9131007
// members. As a big archive can have both 32-bit and 64-bit file members,
9141008
// we need to know the number of symbols in each symbol table individually.
9151009
if (isAIXBigArchive(Kind) && ShouldWriteSymtab) {
916-
Expected<bool> Is64BitOrErr = is64BitSymbolicFile(M.Data);
917-
if (Error E = Is64BitOrErr.takeError())
918-
return E;
919-
920-
if (!*Is64BitOrErr)
921-
NumSyms32 += M.Symbols.size();
922-
}
1010+
if (!is64BitSymbolicFile(M.SymFile.get()))
1011+
NumSyms32 += M.Symbols.size();
1012+
}
9231013
}
9241014

9251015
std::optional<uint64_t> HeadersSize;
@@ -1000,13 +1090,14 @@ static Error writeArchiveToStream(raw_ostream &Out,
10001090
for (size_t I = 0, Size = NewMembers.size(); I != Size; ++I) {
10011091
const NewArchiveMember &Member = NewMembers[I];
10021092
MemberTableNameStrTblSize += Member.MemberName.size() + 1;
1093+
MemberEndOffset += Data[I].PreHeadPadSize;
10031094
MemberOffsets.push_back(MemberEndOffset);
10041095
MemberNames.push_back(Member.MemberName);
10051096
// File member name ended with "`\n". The length is included in
10061097
// BigArMemHdrType.
10071098
MemberEndOffset += sizeof(object::BigArMemHdrType) +
1008-
alignTo(Data[I].Data.size(), 2) +
1009-
alignTo(Member.MemberName.size(), 2);
1099+
alignTo(Data[I].Data.size(), 2) +
1100+
alignTo(Member.MemberName.size(), 2);
10101101
}
10111102

10121103
// AIX member table size.
@@ -1021,16 +1112,11 @@ static Error writeArchiveToStream(raw_ostream &Out,
10211112

10221113
if (ShouldWriteSymtab && NumSyms)
10231114
// Generate the symbol names for the members.
1024-
for (const NewArchiveMember &M : NewMembers) {
1025-
MemoryBufferRef Buf = M.Buf->getMemBufferRef();
1026-
Expected<bool> Is64BitOrErr = is64BitSymbolicFile(Buf.getBuffer());
1027-
if (!Is64BitOrErr)
1028-
return Is64BitOrErr.takeError();
1029-
1030-
bool HasObject;
1031-
Expected<std::vector<unsigned>> SymbolsOrErr =
1032-
getSymbols(Buf, 0, *Is64BitOrErr ? SymNames64 : SymNames32, nullptr,
1033-
HasObject);
1115+
for (const auto &M : Data) {
1116+
Expected<std::vector<unsigned>> SymbolsOrErr = getSymbols(
1117+
M.SymFile.get(), 0,
1118+
is64BitSymbolicFile(M.SymFile.get()) ? SymNames64 : SymNames32,
1119+
nullptr);
10341120
if (!SymbolsOrErr)
10351121
return SymbolsOrErr.takeError();
10361122
}
@@ -1069,16 +1155,20 @@ static Error writeArchiveToStream(raw_ostream &Out,
10691155
// symbol table.
10701156
printWithSpacePadding(Out, GlobalSymbolOffset, 20);
10711157
printWithSpacePadding(Out, GlobalSymbolOffset64, 20);
1072-
printWithSpacePadding(
1073-
Out, NewMembers.size() ? sizeof(object::BigArchive::FixLenHdr) : 0,
1074-
20); // Offset to first archive member
1158+
printWithSpacePadding(Out,
1159+
NewMembers.size()
1160+
? sizeof(object::BigArchive::FixLenHdr) +
1161+
Data[0].PreHeadPadSize
1162+
: 0,
1163+
20); // Offset to first archive member
10751164
printWithSpacePadding(Out, NewMembers.size() ? LastMemberHeaderOffset : 0,
10761165
20); // Offset to last archive member
10771166
printWithSpacePadding(
10781167
Out, 0,
10791168
20); // Offset to first member of free list - Not supported yet
10801169

10811170
for (const MemberData &M : Data) {
1171+
Out << std::string(M.PreHeadPadSize, '\0');
10821172
Out << M.Header << M.Data;
10831173
if (M.Data.size() % 2)
10841174
Out << '\0';

0 commit comments

Comments
 (0)