Skip to content

Commit 9d5edfd

Browse files
alx32Alex B
andauthored
[llvm-objdump][macho] Add support for ObjC relative method lists (#85477)
For Mach-O, ld64 supports the -fobjc-relative-method-lists flag which changes the format in which method lists are generated. The format uses delta encoding vs the original direct-pointer encoding. This change adds support to llvm-objdump and llvm-otool for decoding/dumping of method lists in the delta format. Previously, if a binary with this information format was passed to the tooling, it would output invalid information, trying to parse the delta lists as pointer lists. After this change, the tooling will output correct information if a binary in this format is encountered. The output format is closest feasible match to XCode 15.1's otool output. Tests are included for both 32bit and 64bit binaries. The code style was matched as close as possible to existing implementation of parsing non-delta method lists. Diff between llvm-objdump and XCode 15.1 otool: ![image](https://github.com/llvm/llvm-project/assets/103613512/2277e3ff-d59c-4fff-b93a-e0587ee740a6) Note: This is a retry of this PR: #84250 On the original PR, the armv7+armv8 builds were failing due to absolute offsets being different. Co-authored-by: Alex B <[email protected]>
1 parent 662010e commit 9d5edfd

File tree

4 files changed

+196
-2
lines changed

4 files changed

+196
-2
lines changed
Binary file not shown.
Binary file not shown.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
RUN: llvm-objdump --macho --objc-meta-data %p/Inputs/rel-method-lists-arm64_32.dylib | FileCheck %s --check-prefix=CHK32
2+
RUN: llvm-otool -ov %p/Inputs/rel-method-lists-arm64_32.dylib | FileCheck %s --check-prefix=CHK32
3+
4+
RUN: llvm-objdump --macho --objc-meta-data %p/Inputs/rel-method-lists-arm64.dylib | FileCheck %s --check-prefix=CHK64
5+
RUN: llvm-otool -ov %p/Inputs/rel-method-lists-arm64.dylib | FileCheck %s --check-prefix=CHK64
6+
7+
CHK32: baseMethods 0x660 (struct method_list_t *)
8+
CHK32-NEXT: entsize 12 (relative)
9+
CHK32-NEXT: count 3
10+
CHK32-NEXT: name 0x144 (0x{{[0-9a-f]*}}) instance_method_00
11+
CHK32-NEXT: types 0x91 (0x{{[0-9a-f]*}}) v8@0:4
12+
CHK32-NEXT: imp 0xffffff18 (0x{{[0-9a-f]*}}) -[MyClass instance_method_00]
13+
CHK32-NEXT: name 0x13c (0x{{[0-9a-f]*}}) instance_method_01
14+
CHK32-NEXT: types 0x85 (0x{{[0-9a-f]*}}) v8@0:4
15+
CHK32-NEXT: imp 0xffffff28 (0x{{[0-9a-f]*}}) -[MyClass instance_method_01]
16+
CHK32-NEXT: name 0x134 (0x{{[0-9a-f]*}}) instance_method_02
17+
CHK32-NEXT: types 0x79 (0x{{[0-9a-f]*}}) v8@0:4
18+
CHK32-NEXT: imp 0xffffff38 (0x{{[0-9a-f]*}}) -[MyClass instance_method_02]
19+
20+
CHK32: baseMethods 0x630 (struct method_list_t *)
21+
CHK32-NEXT: entsize 12 (relative)
22+
CHK32-NEXT: count 3
23+
CHK32-NEXT: name 0x180 (0x{{[0-9a-f]*}}) class_method_00
24+
CHK32-NEXT: types 0xc1 (0x{{[0-9a-f]*}}) v8@0:4
25+
CHK32-NEXT: imp 0xffffff9c (0x{{[0-9a-f]*}}) +[MyClass class_method_00]
26+
CHK32-NEXT: name 0x178 (0x{{[0-9a-f]*}}) class_method_01
27+
CHK32-NEXT: types 0xb5 (0x{{[0-9a-f]*}}) v8@0:4
28+
CHK32-NEXT: imp 0xffffffac (0x{{[0-9a-f]*}}) +[MyClass class_method_01]
29+
CHK32-NEXT: name 0x170 (0x{{[0-9a-f]*}}) class_method_02
30+
CHK32-NEXT: types 0xa9 (0x{{[0-9a-f]*}}) v8@0:4
31+
CHK32-NEXT: imp 0xffffffbc (0x{{[0-9a-f]*}}) +[MyClass class_method_02]
32+
33+
CHK64: baseMethods 0x6e0 (struct method_list_t *)
34+
CHK64-NEXT: entsize 12 (relative)
35+
CHK64-NEXT: count 3
36+
CHK64-NEXT: name 0x188 (0x{{[0-9a-f]*}}) instance_method_00
37+
CHK64-NEXT: types 0x91 (0x{{[0-9a-f]*}}) v16@0:8
38+
CHK64-NEXT: imp 0xffffffa8 (0x{{[0-9a-f]*}}) -[MyClass instance_method_00]
39+
CHK64-NEXT: name 0x184 (0x{{[0-9a-f]*}}) instance_method_01
40+
CHK64-NEXT: types 0x85 (0x{{[0-9a-f]*}}) v16@0:8
41+
CHK64-NEXT: imp 0xffffffa0 (0x{{[0-9a-f]*}}) -[MyClass instance_method_01]
42+
CHK64-NEXT: name 0x180 (0x{{[0-9a-f]*}}) instance_method_02
43+
CHK64-NEXT: types 0x79 (0x{{[0-9a-f]*}}) v16@0:8
44+
CHK64-NEXT: imp 0xffffff98 (0x{{[0-9a-f]*}}) -[MyClass instance_method_02]
45+
46+
CHK64: baseMethods 0x6b0 (struct method_list_t *)
47+
CHK64-NEXT: entsize 12 (relative)
48+
CHK64-NEXT: count 3
49+
CHK64-NEXT: name 0x1d0 (0x{{[0-9a-f]*}}) class_method_00
50+
CHK64-NEXT: types 0xc1 (0x{{[0-9a-f]*}}) v16@0:8
51+
CHK64-NEXT: imp 0xffffffe4 (0x{{[0-9a-f]*}}) +[MyClass class_method_00]
52+
CHK64-NEXT: name 0x1cc (0x{{[0-9a-f]*}}) class_method_01
53+
CHK64-NEXT: types 0xb5 (0x{{[0-9a-f]*}}) v16@0:8
54+
CHK64-NEXT: imp 0xffffffdc (0x{{[0-9a-f]*}}) +[MyClass class_method_01]
55+
CHK64-NEXT: name 0x1c8 (0x{{[0-9a-f]*}}) class_method_02
56+
CHK64-NEXT: types 0xa9 (0x{{[0-9a-f]*}}) v16@0:8
57+
CHK64-NEXT: imp 0xffffffd4 (0x{{[0-9a-f]*}}) +[MyClass class_method_02]
58+
59+
######## Generate rel-method-lists-arm64.dylib ########
60+
// clang -c main.mm -o main.o -target arm64-apple-macos -arch arm64
61+
// ld64.ld64 -dylib -demangle -dynamic main.o -o rel-method-lists-arm64.dylib -syslibroot MacOSX14.2.sdk -segalign 0x10 -objc_relative_method_lists
62+
63+
######## Generate rel-method-lists-arm64_32.dylib ########
64+
// clang -c main.mm -o main.o -target arm64_32-apple-watchos -arch arm64_32
65+
// ld64.ld64 -dylib -demangle -dynamic main.o -o rel-method-lists-arm64_32.dylib -syslibroot WatchOS.sdk -segalign 0x10 -objc_relative_method_lists
66+
67+
// ~~~~~~~~~~~~~~~~~~~~~~~~~ main.mm ~~~~~~~~~~~~~~~~~~~~~~~~~
68+
__attribute__((objc_root_class))
69+
@interface MyClass
70+
- (void)instance_method_00;
71+
- (void)instance_method_01;
72+
- (void)instance_method_02;
73+
+ (void)class_method_00;
74+
+ (void)class_method_01;
75+
+ (void)class_method_02;
76+
@end
77+
@implementation MyClass
78+
- (void)instance_method_00 {}
79+
- (void)instance_method_01 {}
80+
- (void)instance_method_02 {}
81+
+ (void)class_method_00 {}
82+
+ (void)class_method_01 {}
83+
+ (void)class_method_02 {}
84+
@end
85+
void *_objc_empty_cache;
86+
void *_objc_empty_vtable;

llvm/tools/llvm-objdump/MachODump.cpp

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3661,6 +3661,10 @@ struct class_ro32_t {
36613661
#define RO_ROOT (1 << 1)
36623662
#define RO_HAS_CXX_STRUCTORS (1 << 2)
36633663

3664+
/* Values for method_list{64,32}_t->entsize */
3665+
#define ML_HAS_RELATIVE_PTRS (1 << 31)
3666+
#define ML_ENTSIZE_MASK 0xFFFF
3667+
36643668
struct method_list64_t {
36653669
uint32_t entsize;
36663670
uint32_t count;
@@ -3685,6 +3689,12 @@ struct method32_t {
36853689
uint32_t imp; /* IMP (32-bit pointer) */
36863690
};
36873691

3692+
struct method_relative_t {
3693+
int32_t name; /* SEL (32-bit relative) */
3694+
int32_t types; /* const char * (32-bit relative) */
3695+
int32_t imp; /* IMP (32-bit relative) */
3696+
};
3697+
36883698
struct protocol_list64_t {
36893699
uint64_t count; /* uintptr_t (a 64-bit value) */
36903700
/* struct protocol64_t * list[0]; These pointers follow inline */
@@ -3986,6 +3996,12 @@ inline void swapStruct(struct method32_t &m) {
39863996
sys::swapByteOrder(m.imp);
39873997
}
39883998

3999+
inline void swapStruct(struct method_relative_t &m) {
4000+
sys::swapByteOrder(m.name);
4001+
sys::swapByteOrder(m.types);
4002+
sys::swapByteOrder(m.imp);
4003+
}
4004+
39894005
inline void swapStruct(struct protocol_list64_t &pl) {
39904006
sys::swapByteOrder(pl.count);
39914007
}
@@ -4440,6 +4456,84 @@ static void print_layout_map32(uint32_t p, struct DisassembleInfo *info) {
44404456
print_layout_map(layout_map, left);
44414457
}
44424458

4459+
static void print_relative_method_list(uint32_t structSizeAndFlags,
4460+
uint32_t structCount, uint64_t p,
4461+
struct DisassembleInfo *info,
4462+
const char *indent,
4463+
uint32_t pointerBits) {
4464+
struct method_relative_t m;
4465+
const char *r, *name;
4466+
uint32_t offset, xoffset, left, i;
4467+
SectionRef S, xS;
4468+
4469+
assert(((structSizeAndFlags & ML_HAS_RELATIVE_PTRS) != 0) &&
4470+
"expected structSizeAndFlags to have ML_HAS_RELATIVE_PTRS flag");
4471+
4472+
outs() << indent << "\t\t entsize "
4473+
<< (structSizeAndFlags & ML_ENTSIZE_MASK) << " (relative) \n";
4474+
outs() << indent << "\t\t count " << structCount << "\n";
4475+
4476+
for (i = 0; i < structCount; i++) {
4477+
r = get_pointer_64(p, offset, left, S, info);
4478+
memset(&m, '\0', sizeof(struct method_relative_t));
4479+
if (left < sizeof(struct method_relative_t)) {
4480+
memcpy(&m, r, left);
4481+
outs() << indent << " (method_t extends past the end of the section)\n";
4482+
} else
4483+
memcpy(&m, r, sizeof(struct method_relative_t));
4484+
if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
4485+
swapStruct(m);
4486+
4487+
outs() << indent << "\t\t name " << format("0x%" PRIx32, m.name);
4488+
uint64_t relNameRefVA = p + offsetof(struct method_relative_t, name);
4489+
uint64_t absNameRefVA = relNameRefVA + m.name;
4490+
outs() << " (" << format("0x%" PRIx32, absNameRefVA) << ")";
4491+
4492+
// since this is a relative list, absNameRefVA is the address of the
4493+
// __objc_selrefs entry, so a pointer, not the actual name
4494+
const char *nameRefPtr =
4495+
get_pointer_64(absNameRefVA, xoffset, left, xS, info);
4496+
if (nameRefPtr) {
4497+
uint32_t pointerSize = pointerBits / CHAR_BIT;
4498+
if (left < pointerSize)
4499+
outs() << indent << " (nameRefPtr extends past the end of the section)";
4500+
else {
4501+
if (pointerSize == 64) {
4502+
name = get_pointer_64(*reinterpret_cast<const uint64_t *>(nameRefPtr),
4503+
xoffset, left, xS, info);
4504+
} else {
4505+
name = get_pointer_32(*reinterpret_cast<const uint32_t *>(nameRefPtr),
4506+
xoffset, left, xS, info);
4507+
}
4508+
if (name != nullptr)
4509+
outs() << format(" %.*s", left, name);
4510+
}
4511+
}
4512+
outs() << "\n";
4513+
4514+
outs() << indent << "\t\t types " << format("0x%" PRIx32, m.types);
4515+
uint64_t relTypesVA = p + offsetof(struct method_relative_t, types);
4516+
uint64_t absTypesVA = relTypesVA + m.types;
4517+
outs() << " (" << format("0x%" PRIx32, absTypesVA) << ")";
4518+
name = get_pointer_32(absTypesVA, xoffset, left, xS, info);
4519+
if (name != nullptr)
4520+
outs() << format(" %.*s", left, name);
4521+
outs() << "\n";
4522+
4523+
outs() << indent << "\t\t imp " << format("0x%" PRIx32, m.imp);
4524+
uint64_t relImpVA = p + offsetof(struct method_relative_t, imp);
4525+
uint64_t absImpVA = relImpVA + m.imp;
4526+
outs() << " (" << format("0x%" PRIx32, absImpVA) << ")";
4527+
name = GuessSymbolName(absImpVA, info->AddrMap);
4528+
if (name != nullptr)
4529+
outs() << " " << name;
4530+
outs() << "\n";
4531+
4532+
p += sizeof(struct method_relative_t);
4533+
offset += sizeof(struct method_relative_t);
4534+
}
4535+
}
4536+
44434537
static void print_method_list64_t(uint64_t p, struct DisassembleInfo *info,
44444538
const char *indent) {
44454539
struct method_list64_t ml;
@@ -4461,10 +4555,17 @@ static void print_method_list64_t(uint64_t p, struct DisassembleInfo *info,
44614555
memcpy(&ml, r, sizeof(struct method_list64_t));
44624556
if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
44634557
swapStruct(ml);
4558+
p += sizeof(struct method_list64_t);
4559+
4560+
if ((ml.entsize & ML_HAS_RELATIVE_PTRS) != 0) {
4561+
print_relative_method_list(ml.entsize, ml.count, p, info, indent,
4562+
/*pointerBits=*/64);
4563+
return;
4564+
}
4565+
44644566
outs() << indent << "\t\t entsize " << ml.entsize << "\n";
44654567
outs() << indent << "\t\t count " << ml.count << "\n";
44664568

4467-
p += sizeof(struct method_list64_t);
44684569
offset += sizeof(struct method_list64_t);
44694570
for (i = 0; i < ml.count; i++) {
44704571
r = get_pointer_64(p, offset, left, S, info);
@@ -4552,10 +4653,17 @@ static void print_method_list32_t(uint64_t p, struct DisassembleInfo *info,
45524653
memcpy(&ml, r, sizeof(struct method_list32_t));
45534654
if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
45544655
swapStruct(ml);
4656+
p += sizeof(struct method_list32_t);
4657+
4658+
if ((ml.entsize & ML_HAS_RELATIVE_PTRS) != 0) {
4659+
print_relative_method_list(ml.entsize, ml.count, p, info, indent,
4660+
/*pointerBits=*/32);
4661+
return;
4662+
}
4663+
45554664
outs() << indent << "\t\t entsize " << ml.entsize << "\n";
45564665
outs() << indent << "\t\t count " << ml.count << "\n";
45574666

4558-
p += sizeof(struct method_list32_t);
45594667
offset += sizeof(struct method_list32_t);
45604668
for (i = 0; i < ml.count; i++) {
45614669
r = get_pointer_32(p, offset, left, S, info);

0 commit comments

Comments
 (0)