Skip to content

Commit 85a2c50

Browse files
cjacekmstorsjo
authored andcommitted
[llvm-nm] Print EC symbol map.
This is useful for examining ARM64EC static libraries and allows better llvm-lib testing. Changes to Archive class will also be useful for LLD to support ARM64EC, where it will need to use one map or the other, depending on linking target (or both, in case of ARM64X, but separately as they are in different namespaces). Reviewed By: jhenderson, efriedma Differential Revision: https://reviews.llvm.org/D146534
1 parent eb56ef3 commit 85a2c50

File tree

5 files changed

+247
-30
lines changed

5 files changed

+247
-30
lines changed

llvm/include/llvm/Object/Archive.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ class Archive : public Binary {
302302
StringRef getName() const;
303303
Expected<Child> getMember() const;
304304
Symbol getNext() const;
305+
bool isECSymbol() const;
305306
};
306307

307308
class symbol_iterator {
@@ -352,6 +353,8 @@ class Archive : public Binary {
352353
return make_range(symbol_begin(), symbol_end());
353354
}
354355

356+
Expected<iterator_range<symbol_iterator>> ec_symbols() const;
357+
355358
static bool classof(Binary const *v) { return v->isArchive(); }
356359

357360
// check if a symbol is in the archive
@@ -362,6 +365,7 @@ class Archive : public Binary {
362365
StringRef getSymbolTable() const { return SymbolTable; }
363366
StringRef getStringTable() const { return StringTable; }
364367
uint32_t getNumberOfSymbols() const;
368+
uint32_t getNumberOfECSymbols() const;
365369
virtual uint64_t getFirstChildOffset() const { return getArchiveMagicLen(); }
366370

367371
std::vector<std::unique_ptr<MemoryBuffer>> takeThinBuffers() {
@@ -377,6 +381,7 @@ class Archive : public Binary {
377381
void setFirstRegular(const Child &C);
378382

379383
StringRef SymbolTable;
384+
StringRef ECSymbolTable;
380385
StringRef StringTable;
381386

382387
private:

llvm/lib/Object/Archive.cpp

Lines changed: 109 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,34 @@ Archive::Archive(MemoryBufferRef Source, Error &Err)
933933
StringTable = BufOrErr.get();
934934
if (Increment())
935935
return;
936+
937+
if (I == E) {
938+
setFirstRegular(*C);
939+
Err = Error::success();
940+
return;
941+
}
942+
943+
NameOrErr = C->getRawName();
944+
if (!NameOrErr) {
945+
Err = NameOrErr.takeError();
946+
return;
947+
}
948+
Name = NameOrErr.get();
949+
}
950+
951+
if (Name == "/<ECSYMBOLS>/") {
952+
// ARM64EC-aware libraries contain an additional special member with
953+
// an EC symbol map after the string table. Its format is similar to a
954+
// regular symbol map, except it doesn't contain member offsets. Its indexes
955+
// refer to member offsets from the regular symbol table instead.
956+
Expected<StringRef> BufOrErr = C->getBuffer();
957+
if (!BufOrErr) {
958+
Err = BufOrErr.takeError();
959+
return;
960+
}
961+
ECSymbolTable = BufOrErr.get();
962+
if (Increment())
963+
return;
936964
}
937965

938966
setFirstRegular(*C);
@@ -967,7 +995,17 @@ Archive::child_iterator Archive::child_end() const {
967995
return child_iterator::end(Child(nullptr, nullptr, nullptr));
968996
}
969997

998+
bool Archive::Symbol::isECSymbol() const {
999+
// Symbols use SymbolCount..SymbolCount+getNumberOfECSymbols() for EC symbol
1000+
// indexes.
1001+
uint32_t SymbolCount = Parent->getNumberOfSymbols();
1002+
return SymbolCount <= SymbolIndex &&
1003+
SymbolIndex < SymbolCount + Parent->getNumberOfECSymbols();
1004+
}
1005+
9701006
StringRef Archive::Symbol::getName() const {
1007+
if (isECSymbol())
1008+
return Parent->ECSymbolTable.begin() + StringIndex;
9711009
return Parent->getSymbolTable().begin() + StringIndex;
9721010
}
9731011

@@ -1006,15 +1044,24 @@ Expected<Archive::Child> Archive::Symbol::getMember() const {
10061044
Buf += MemberCount * 4 + 4;
10071045

10081046
uint32_t SymbolCount = read32le(Buf);
1009-
if (SymbolIndex >= SymbolCount)
1047+
uint16_t OffsetIndex;
1048+
if (SymbolIndex < SymbolCount) {
1049+
// Skip SymbolCount to get to the indices table.
1050+
const char *Indices = Buf + 4;
1051+
1052+
// Get the index of the offset in the file member offset table for this
1053+
// symbol.
1054+
OffsetIndex = read16le(Indices + SymbolIndex * 2);
1055+
} else if (isECSymbol()) {
1056+
// Skip SymbolCount to get to the indices table.
1057+
const char *Indices = Parent->ECSymbolTable.begin() + 4;
1058+
1059+
// Get the index of the offset in the file member offset table for this
1060+
// symbol.
1061+
OffsetIndex = read16le(Indices + (SymbolIndex - SymbolCount) * 2);
1062+
} else {
10101063
return errorCodeToError(object_error::parse_failed);
1011-
1012-
// Skip SymbolCount to get to the indices table.
1013-
const char *Indices = Buf + 4;
1014-
1015-
// Get the index of the offset in the file member offset table for this
1016-
// symbol.
1017-
uint16_t OffsetIndex = read16le(Indices + SymbolIndex * 2);
1064+
}
10181065
// Subtract 1 since OffsetIndex is 1 based.
10191066
--OffsetIndex;
10201067

@@ -1063,6 +1110,9 @@ Archive::Symbol Archive::Symbol::getNext() const {
10631110
t.StringIndex -= CurRanStrx;
10641111
t.StringIndex += NextRanStrx;
10651112
}
1113+
} else if (t.isECSymbol()) {
1114+
// Go to one past next null.
1115+
t.StringIndex = Parent->ECSymbolTable.find('\0', t.StringIndex) + 1;
10661116
} else {
10671117
// Go to one past next null.
10681118
t.StringIndex = Parent->getSymbolTable().find('\0', t.StringIndex) + 1;
@@ -1133,6 +1183,51 @@ Archive::symbol_iterator Archive::symbol_end() const {
11331183
return symbol_iterator(Symbol(this, getNumberOfSymbols(), 0));
11341184
}
11351185

1186+
Expected<iterator_range<Archive::symbol_iterator>> Archive::ec_symbols() const {
1187+
uint32_t Count = 0;
1188+
1189+
// Validate EC symbol table.
1190+
if (!ECSymbolTable.empty()) {
1191+
if (ECSymbolTable.size() < sizeof(uint32_t))
1192+
return malformedError("invalid EC symbols size (" +
1193+
Twine(ECSymbolTable.size()) + ")");
1194+
if (SymbolTable.size() < sizeof(uint32_t))
1195+
return malformedError("invalid symbols size (" +
1196+
Twine(ECSymbolTable.size()) + ")");
1197+
1198+
Count = read32le(ECSymbolTable.begin());
1199+
size_t StringIndex = sizeof(uint32_t) + Count * sizeof(uint16_t);
1200+
if (ECSymbolTable.size() < StringIndex)
1201+
return malformedError("invalid EC symbols size. Size was " +
1202+
Twine(ECSymbolTable.size()) + ", but expected " +
1203+
Twine(StringIndex));
1204+
1205+
uint32_t MemberCount = read32le(SymbolTable.begin());
1206+
const char *Indexes = ECSymbolTable.begin() + sizeof(uint32_t);
1207+
1208+
for (uint32_t i = 0; i < Count; ++i) {
1209+
uint16_t Index = read16le(Indexes + i * sizeof(uint16_t));
1210+
if (!Index)
1211+
return malformedError("invalid EC symbol index 0");
1212+
if (Index > MemberCount)
1213+
return malformedError("invalid EC symbol index " + Twine(Index) +
1214+
" is larger than member count " +
1215+
Twine(MemberCount));
1216+
1217+
StringIndex = ECSymbolTable.find('\0', StringIndex);
1218+
if (StringIndex == StringRef::npos)
1219+
return malformedError("malformed EC symbol names: not null-terminated");
1220+
++StringIndex;
1221+
}
1222+
}
1223+
1224+
uint32_t SymbolCount = getNumberOfSymbols();
1225+
return make_range(
1226+
symbol_iterator(Symbol(this, SymbolCount,
1227+
sizeof(uint32_t) + Count * sizeof(uint16_t))),
1228+
symbol_iterator(Symbol(this, SymbolCount + Count, 0)));
1229+
}
1230+
11361231
uint32_t Archive::getNumberOfSymbols() const {
11371232
if (!hasSymbolTable())
11381233
return 0;
@@ -1151,6 +1246,12 @@ uint32_t Archive::getNumberOfSymbols() const {
11511246
return read32le(buf);
11521247
}
11531248

1249+
uint32_t Archive::getNumberOfECSymbols() const {
1250+
if (ECSymbolTable.size() < sizeof(uint32_t))
1251+
return 0;
1252+
return read32le(ECSymbolTable.begin());
1253+
}
1254+
11541255
Expected<std::optional<Archive::Child>> Archive::findSym(StringRef name) const {
11551256
Archive::symbol_iterator bs = symbol_begin();
11561257
Archive::symbol_iterator es = symbol_end();

llvm/test/tools/llvm-lib/ecsymbols.test

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
# Check that llvm-lib can list the members of an archive which contains the
1+
# Check that llvm-lib doesn't list the members of an archive which contains the
22
# special member /<ECSYMBOLS>/.
33

44
# RUN: yaml2obj %s -o %t.lib
5-
# RUN: llvm-lib /list %t.lib | FileCheck %s
6-
7-
# CHECK: /<ECSYMBOLS>/
5+
# RUN: llvm-lib /list %t.lib | FileCheck --check-prefix=NOEC --allow-empty %s
6+
# NOEC-NOT: ECSYMBOLS
87

98
--- !Arch
109
Members:
@@ -23,3 +22,12 @@ Members:
2322
# RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj -o x64-foo.o %S/Inputs/b.s
2423
# RUN: llvm-lib -machine:arm64ec -out:foo.lib arm64-foo.o arm64ec-foo.o x64-foo.o
2524
# RUN: grep -q '/<ECSYMBOLS>/' foo.lib
25+
26+
# RUN: llvm-nm --print-armap foo.lib | FileCheck %s
27+
# CHECK: Archive map
28+
# CHECK-NEXT: a in arm64-foo.o
29+
# CHECK-EMPTY:
30+
# CHECK-NEXT: Archive EC map
31+
# CHECK-NEXT: a in arm64ec-foo.o
32+
# CHECK-NEXT: b in x64-foo.o
33+
# CHECK-EMPTY:
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Check that llvm-lib doesn't list the members of an archive which contains the
2+
# special member /<ECSYMBOLS>/.
3+
4+
# RUN: yaml2obj --docnum=1 %s -o %t.lib
5+
# RUN: llvm-nm --print-armap %t.lib 2>&1 | FileCheck --check-prefix=NM1 %s
6+
# NM1: truncated or malformed archive (invalid EC symbols size (3))
7+
8+
--- !Arch
9+
Members:
10+
- Name: '/'
11+
Size: '0'
12+
- Name: '/'
13+
Size: '0'
14+
- Name: '/<ECSYMBOLS>/'
15+
Size: '3'
16+
Content: 010203
17+
PaddingByte: 0
18+
...
19+
20+
# RUN: yaml2obj --docnum=2 %s -o %t.lib
21+
# RUN: llvm-nm --print-armap %t.lib 2>&1 | FileCheck --check-prefix=NM2 %s
22+
# NM2: truncated or malformed archive (invalid EC symbols size. Size was 7, but expected 8)
23+
24+
--- !Arch
25+
Members:
26+
- Name: '/'
27+
Size: '0'
28+
- Name: '/'
29+
Size: '12'
30+
Content: 010000001000000000000000
31+
- Name: '/<ECSYMBOLS>/'
32+
Size: '7'
33+
Content: 02000000010001
34+
PaddingByte: 0
35+
...
36+
37+
# RUN: yaml2obj --docnum=3 %s -o %t.lib
38+
# RUN: llvm-nm --print-armap %t.lib 2>&1 | FileCheck --check-prefix=NM3 %s
39+
# NM3: truncated or malformed archive (invalid EC symbol index 2 is larger than member count 1)
40+
41+
--- !Arch
42+
Members:
43+
- Name: '/'
44+
Size: '0'
45+
- Name: '/'
46+
Size: '12'
47+
Content: 010000001000000000000000
48+
- Name: '/<ECSYMBOLS>/'
49+
Size: '8'
50+
Content: 0100000002006100
51+
- Name: 'a.obj'
52+
Size: '0'
53+
...
54+
55+
56+
# RUN: yaml2obj --docnum=4 %s -o %t.lib
57+
# RUN: llvm-nm --print-armap %t.lib 2>&1 | FileCheck --check-prefix=NM4 %s
58+
# NM4: truncated or malformed archive (invalid EC symbol index 0)
59+
60+
--- !Arch
61+
Members:
62+
- Name: '/'
63+
Size: '0'
64+
- Name: '/'
65+
Size: '12'
66+
Content: 010000001000000000000000
67+
- Name: '/<ECSYMBOLS>/'
68+
Size: '8'
69+
Content: 0100000000006100
70+
- Name: 'a.obj'
71+
Size: '0'
72+
...
73+
74+
# RUN: yaml2obj --docnum=5 %s -o %t.lib
75+
# RUN: llvm-nm --print-armap %t.lib 2>&1 | FileCheck --check-prefix=NM5 %s
76+
# NM5: truncated or malformed archive (malformed EC symbol names: not null-terminated)
77+
78+
--- !Arch
79+
Members:
80+
- Name: '/'
81+
Size: '0'
82+
- Name: '/'
83+
Size: '12'
84+
Content: 010000001000000000000000
85+
- Name: '/<ECSYMBOLS>/'
86+
Size: '8'
87+
Content: 0100000001006161
88+
- Name: 'a.obj'
89+
Size: '0'
90+
...

llvm/tools/llvm-nm/llvm-nm.cpp

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1954,26 +1954,39 @@ static bool checkMachOAndArchFlags(SymbolicFile *O, StringRef Filename) {
19541954
return true;
19551955
}
19561956

1957+
static void printArchiveMap(iterator_range<Archive::symbol_iterator> &map,
1958+
StringRef Filename) {
1959+
for (auto I : map) {
1960+
Expected<Archive::Child> C = I.getMember();
1961+
if (!C) {
1962+
error(C.takeError(), Filename);
1963+
break;
1964+
}
1965+
Expected<StringRef> FileNameOrErr = C->getName();
1966+
if (!FileNameOrErr) {
1967+
error(FileNameOrErr.takeError(), Filename);
1968+
break;
1969+
}
1970+
StringRef SymName = I.getName();
1971+
outs() << SymName << " in " << FileNameOrErr.get() << "\n";
1972+
}
1973+
1974+
outs() << "\n";
1975+
}
1976+
19571977
static void dumpArchiveMap(Archive *A, StringRef Filename) {
1958-
Archive::symbol_iterator I = A->symbol_begin();
1959-
Archive::symbol_iterator E = A->symbol_end();
1960-
if (I != E) {
1978+
auto Map = A->symbols();
1979+
if (!Map.empty()) {
19611980
outs() << "Archive map\n";
1962-
for (; I != E; ++I) {
1963-
Expected<Archive::Child> C = I->getMember();
1964-
if (!C) {
1965-
error(C.takeError(), Filename);
1966-
break;
1967-
}
1968-
Expected<StringRef> FileNameOrErr = C->getName();
1969-
if (!FileNameOrErr) {
1970-
error(FileNameOrErr.takeError(), Filename);
1971-
break;
1972-
}
1973-
StringRef SymName = I->getName();
1974-
outs() << SymName << " in " << FileNameOrErr.get() << "\n";
1975-
}
1976-
outs() << "\n";
1981+
printArchiveMap(Map, Filename);
1982+
}
1983+
1984+
auto ECMap = A->ec_symbols();
1985+
if (!ECMap) {
1986+
warn(ECMap.takeError(), Filename);
1987+
} else if (!ECMap->empty()) {
1988+
outs() << "Archive EC map\n";
1989+
printArchiveMap(*ECMap, Filename);
19771990
}
19781991
}
19791992

0 commit comments

Comments
 (0)