[llvm] [llvm-objdump][macho] Add support for ObjC relative method lists (PR #84250)

via llvm-commits llvm-commits at lists.llvm.org
Sun Mar 10 10:44:35 PDT 2024


https://github.com/alx32 updated https://github.com/llvm/llvm-project/pull/84250

>From c99ad35d40438ce92425f42ed3bdb9a53ce5abde Mon Sep 17 00:00:00 2001
From: Alex B <alexborcan at meta.com>
Date: Wed, 6 Mar 2024 14:31:24 -0800
Subject: [PATCH] [llvm-objdump][macho] Add support for ObjC relative method
 lists

---
 .../Inputs/rel-method-lists-arm64.dylib       | Bin 0 -> 3160 bytes
 .../Inputs/rel-method-lists-arm64_32.dylib    | Bin 0 -> 2716 bytes
 .../AArch64/macho-relative-method-lists.test  |  87 ++++++++++++
 llvm/tools/llvm-objdump/MachODump.cpp         | 134 ++++++++++++++++++
 4 files changed, 221 insertions(+)
 create mode 100755 llvm/test/tools/llvm-objdump/MachO/AArch64/Inputs/rel-method-lists-arm64.dylib
 create mode 100755 llvm/test/tools/llvm-objdump/MachO/AArch64/Inputs/rel-method-lists-arm64_32.dylib
 create mode 100644 llvm/test/tools/llvm-objdump/MachO/AArch64/macho-relative-method-lists.test

diff --git a/llvm/test/tools/llvm-objdump/MachO/AArch64/Inputs/rel-method-lists-arm64.dylib b/llvm/test/tools/llvm-objdump/MachO/AArch64/Inputs/rel-method-lists-arm64.dylib
new file mode 100755
index 0000000000000000000000000000000000000000..9b39fc98fb5d96b544894f21e1f7857891735e6f
GIT binary patch
literal 3160
zcmcguO=whC6h6L*P1>X}!3dT{h*m`H&%7BVgwjH$GXs;ViJFL!BDXJZp3TU6^M=mT
zBy<yMyDbU|S-LQwn-Xwgmsv^|ZEZyYVrdqNE(8m1;-Y%ad*{r&xs!>uiyk<0?|1IK
z- at EsmbI+Y$pFjDh3t$tVyn)({y0jVKd*~q(yo0)eqe at NWCnrKv!;jhC06{QPfGEvS
zDsX0l`dppw8BIo+jT#_qa6fF?U_K6&@~6(&%66 at 4wQPBg&gar$P&%Rp1T$>pMkDii
zPB2Y$P3H}mufzm`UxP20t2Fc7*Y+p4HQ(9tOsQN{rSfS%+;3w%9x-2m2?#g%Pl&mt
zxIRKvDwmnasO;G2co<!YUle_)#Zzzs*N_r7+2V0<OVywipTvRqX?;wDIy5m~$+Nz4
z=%_m15a*ll5q{M<hr;}&bJo8)%sdyu%m)xW&sfeWdzEwg<!YG4(Y~7b%ov}nt<S3W
zn6GFBR%q1uDty2ryoB(^e75g-ep$Yt^DQ%9+}UwH-nJ|c?|lK0Qin!H59V_teD{B2
zo-4dqco!HGPn_#*#9yLr*X+q at -*b|d>&#C1U)xqWsRFN(G*hZ-c@@`Dc$!Mdb&~$s
zKuliBvux!S2M>HyoO4T4$Oy0tVBW-*_CeohB`Y{y!9mIuns?;QsMeWpApvf<lWeFY
zXbQCrTdIfuMc&0l^qp_FecFB{J^0}I))T!ytX^JBqip~+@)3U3=nngl`z&KWj>TiN
zhj9V-ORnF?nD$Sy1C{14A=Nc9yoS8U!tKA>VQn=5YuLZIoKScF58L6nER|~ri?*Ut
z>lc-2YB;6gG?ehpS!LUaEom~PnrEfqTxxLG{CJ35LjLY&-^5$W2YPD`U`y(89i=|S
zqzhN}aI~;v$i2iN7)GTS(>!?(q>h`+^BuQh&oREF*(Vtj*Irg^Y9jBK)T!D)i;nE;
zs`eduXK!lu4#q!g_I}3Xr}aekwZipuy*Y8)0wr-wZZVcRvX`;U6MyUTF7Wqycf!$K
z9VfHL@~F8=$E0v?QuzPF82jw=tb+dN&w}Gsf;k1FU}O(v#>dreB^jAJoS(?(#*qX(
z?ZMzK^x`c&7g$p+KI67!PdiXdz;Z7R-NK<ro^|$-z)6JNm~7(b8OGw@%Z$aZHyBI4
z{K8oB<S}E(k2d7)MjuHYyvJDlkzp+Huo;VADUMsPc8FgoPY8>@sXlGN0x15 at GnV)a
zfC_!8J1erT=SF_%BIif$YcR2AKm3A&G0=3k%~P<!XlldJ^eK41#;g^d)<ekIY84Wy
z=&-w0<ODo)VJtUZOC}z!`{LMGEY)g at NAMq4W;B~uE!Icm4QnwL&;RUN#^xF-MmCf_
zv{6_ufq^fU5k7GNVz~-9uXAa at z*6=vWuv$EkH3H4x_I-)*;W7Poo{B&-`j3qeE313
P at Y7=7C&xFholn06PJ`vL

literal 0
HcmV?d00001

diff --git a/llvm/test/tools/llvm-objdump/MachO/AArch64/Inputs/rel-method-lists-arm64_32.dylib b/llvm/test/tools/llvm-objdump/MachO/AArch64/Inputs/rel-method-lists-arm64_32.dylib
new file mode 100755
index 0000000000000000000000000000000000000000..d3a339057abc349e5959a3a67077f0ae5c5bafe8
GIT binary patch
literal 2716
zcmd5-J!}+L5FYz3Kw_{RiNaD`K!PG533q2Xkrh#JxjV^Dz$W%7fC74~_m=pg-P;ql
zU|;7VNE9?kq=_g}I8s0^h=2l#!jW7fXn-PBDuakbi6SMJ at 7sNQe%{4IqGEJ&^Sya9
zvorJl?*0AOzuQH6dqiXlcnA34TOt=^P^i0)Kc%Xb<5dg{I=VF)P{KT9`jeCjoP{95
z(Ric(ML7J53CiAeGm)$;#zUq2+3#)TIl-J;Zw09w^7z<E7*l3qr-aloY}abF8kXlI
z>$n8pZSbKq$;`>fgCSCRT!C*L&Xgu?5+i6g(=q;mFWX6J(#9C)8uRr=P1PG`d>AEb
z$s)iw^pvKb8E<_(%f)JOeTHjz14GK6A=yuIoQ~N?E{0NR|19k3pZNA5)FJ5 at b<g_F
zVF}3?Rrqo(C=)Ss1lAnx3EOXALx7SoPQv#n^pyV-qvg0~oinX;jAi%^AqJ%x16ws&
zOU()_rjg2H6~23+r!- at P#`;ew+xI-b5$lq*JcBR$K{8`-4Pzo611oiSV)Cm>c_Kce
zaJ&Jh=ZLWkNjuYzXZ$pnb(;FzobNd~%XJoJ{j;{!$f>|<=JEyAvb?72Fym(3b#nf>
zAXd#=fj#H9lw133^gylc)@Kp(eUZgHB){Kn8pbI?o-UL~G0Y{vxF2}<Z3IAKeZX8#
z(mR^D&uH^H^d6j8^VM%Y{rH!aeCM}|*Z%xz`SucXjq+qBJ+i+$1_12_?*uPm6YxGH
zeF&zHi=6S3eR|9qUWYm*d9ap|>uUpYXD!oneXURKKzDg1qaMCVb9gHjc{gx<euuo;
z>2wx=Zv!s_H-WDK-vx${*uD(?WsHZQKL~sQcrWlMa2t3G_(;3Nt6IVfJG}b+n#$)B
z<3eIQB6ZvcR>O7-mz1iIwi=Q1V~6sekA`Pp7&(wk$=$#=Y7~-O&=(>xd$1jz(JP6;
zV027*=1GUZwBf!Xllac?y-E_lV-oF7zr=18x<}xrV7 at m?;9KCw`kTBnNIyVQ$6R6^
z??lqOyQMiaJw9DNR%~Xg#jmAD;qC67`r^xylHurrb-ZTKRx&BQrNhOU8MQ~nD^osF
zsTPyUiHxicpxDO)ILfwV&*6nTA6T=lBeje?<J5CZp_hHbBsl at xk9B8n=QUme{!!x<
z;F}u%0eny67r;+6-if-ih|!Pcv8Nws%)V8Kafh&GTVwWweICH2!oKhxAZDLNh_O-G
ztBS_FyLO2R`;sg;(!Q(}`h|)bs9Rz7RJY}mZ*@a=rS at t5l)PfL at XFbdQ}R(aa^ete
zK82W at c_E;Riru54CZOo?sq##BLv*JnCTFU}$x=maVzJ(>=_zAFY5yqrh8e8Gf2j)e
A#{d8T

literal 0
HcmV?d00001

diff --git a/llvm/test/tools/llvm-objdump/MachO/AArch64/macho-relative-method-lists.test b/llvm/test/tools/llvm-objdump/MachO/AArch64/macho-relative-method-lists.test
new file mode 100644
index 00000000000000..be8b7f8f3b2ce0
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/MachO/AArch64/macho-relative-method-lists.test
@@ -0,0 +1,87 @@
+RUN: llvm-objdump --macho --objc-meta-data    %p/Inputs/rel-method-lists-arm64_32.dylib | FileCheck %s --check-prefix=CHK32
+RUN: llvm-otool -ov                           %p/Inputs/rel-method-lists-arm64_32.dylib | FileCheck %s --check-prefix=CHK32
+
+RUN: llvm-objdump --macho --objc-meta-data    %p/Inputs/rel-method-lists-arm64.dylib    | FileCheck %s --check-prefix=CHK64
+RUN: llvm-otool -ov                           %p/Inputs/rel-method-lists-arm64.dylib    | FileCheck %s --check-prefix=CHK64
+
+CHK32:                 baseMethods 0x660 (struct method_list_t *)
+CHK32-NEXT:                 entsize 12 (relative)
+CHK32-NEXT:                   count 3
+CHK32-NEXT:                    name 0x144 (0x7ac) instance_method_00
+CHK32-NEXT:                   types 0x91 (0x6fd) v8 at 0:4
+CHK32-NEXT:                     imp 0xffffff18 (0x588) -[MyClass instance_method_00]
+CHK32-NEXT:                    name 0x13c (0x7b0) instance_method_01
+CHK32-NEXT:                   types 0x85 (0x6fd) v8 at 0:4
+CHK32-NEXT:                     imp 0xffffff28 (0x5a4) -[MyClass instance_method_01]
+CHK32-NEXT:                    name 0x134 (0x7b4) instance_method_02
+CHK32-NEXT:                   types 0x79 (0x6fd) v8 at 0:4
+CHK32-NEXT:                     imp 0xffffff38 (0x5c0) -[MyClass instance_method_02]
+
+CHK32:                 baseMethods 0x630 (struct method_list_t *)
+CHK32-NEXT:                 entsize 12 (relative)
+CHK32-NEXT:                   count 3
+CHK32-NEXT:                    name 0x180 (0x7b8) class_method_00
+CHK32-NEXT:                   types 0xc1 (0x6fd) v8 at 0:4
+CHK32-NEXT:                     imp 0xffffff9c (0x5dc) +[MyClass class_method_00]
+CHK32-NEXT:                    name 0x178 (0x7bc) class_method_01
+CHK32-NEXT:                   types 0xb5 (0x6fd) v8 at 0:4
+CHK32-NEXT:                     imp 0xffffffac (0x5f8) +[MyClass class_method_01]
+CHK32-NEXT:                    name 0x170 (0x7c0) class_method_02
+CHK32-NEXT:                   types 0xa9 (0x6fd) v8 at 0:4
+CHK32-NEXT:                     imp 0xffffffbc (0x614) +[MyClass class_method_02]
+
+CHK64:                  baseMethods 0x7d8 (struct method_list_t *)
+CHK64-NEXT:                  entsize 24
+CHK64-NEXT:                    count 3
+CHK64-NEXT:                     name 0x6a4 instance_method_00
+CHK64-NEXT:                    types 0x6dd v16 at 0:8
+CHK64-NEXT:                      imp -[MyClass instance_method_00]
+CHK64-NEXT:                     name 0x6b7 instance_method_01
+CHK64-NEXT:                    types 0x6dd v16 at 0:8
+CHK64-NEXT:                      imp -[MyClass instance_method_01]
+CHK64-NEXT:                     name 0x6ca instance_method_02
+CHK64-NEXT:                    types 0x6dd v16 at 0:8
+CHK64-NEXT:                      imp -[MyClass instance_method_02]
+
+CHK64:                  baseMethods 0x740 (struct method_list_t *)
+CHK64-NEXT:                  entsize 24
+CHK64-NEXT:                    count 3
+CHK64-NEXT:                     name 0x674 class_method_00
+CHK64-NEXT:                    types 0x6dd v16 at 0:8
+CHK64-NEXT:                      imp +[MyClass class_method_00]
+CHK64-NEXT:                     name 0x684 class_method_01
+CHK64-NEXT:                    types 0x6dd v16 at 0:8
+CHK64-NEXT:                      imp +[MyClass class_method_01]
+CHK64-NEXT:                     name 0x694 class_method_02
+CHK64-NEXT:                    types 0x6dd v16 at 0:8
+CHK64-NEXT:                      imp +[MyClass class_method_02]
+
+
+######## Generate rel-method-lists-arm64.dylib ########
+// clang -c main.mm -o main.o -target arm64-apple-macos -arch arm64
+// ld64.ld64 -dylib -demangle -dynamic main.o -o rel-method-lists-arm64.dylib -syslibroot MacOSX14.2.sdk -segalign 0x10 -objc_relative_method_lists
+
+######## Generate rel-method-lists-arm64_32.dylib ########
+// clang -c main.mm -o main.o -target arm64_32-apple-watchos -arch arm64_32
+// ld64.ld64 -dylib -demangle -dynamic main.o -o rel-method-lists-arm64_32.dylib -syslibroot WatchOS.sdk -segalign 0x10 -objc_relative_method_lists
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~ main.mm ~~~~~~~~~~~~~~~~~~~~~~~~~
+__attribute__((objc_root_class))
+ at interface MyClass
+- (void)instance_method_00;
+- (void)instance_method_01;
+- (void)instance_method_02;
++ (void)class_method_00;
++ (void)class_method_01;
++ (void)class_method_02;
+ at end
+ at implementation MyClass
+- (void)instance_method_00 {}
+- (void)instance_method_01 {}
+- (void)instance_method_02 {}
++ (void)class_method_00 {}
++ (void)class_method_01 {}
++ (void)class_method_02 {}
+ at end
+void *_objc_empty_cache;
+void *_objc_empty_vtable;
diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp
index 0e6935c0ac5895..ffde83d5d88f3f 100644
--- a/llvm/tools/llvm-objdump/MachODump.cpp
+++ b/llvm/tools/llvm-objdump/MachODump.cpp
@@ -3519,6 +3519,12 @@ static const char *get_pointer_64(uint64_t Address, uint32_t &offset,
   return nullptr;
 }
 
+static const char *get_value_32(uint32_t Address, uint32_t &offset,
+                                uint32_t &left, SectionRef &S,
+                                DisassembleInfo *info, bool objc_only = false) {
+  return get_pointer_64(Address, offset, left, S, info, objc_only);
+}
+
 static const char *get_pointer_32(uint32_t Address, uint32_t &offset,
                                   uint32_t &left, SectionRef &S,
                                   DisassembleInfo *info,
@@ -3661,6 +3667,10 @@ struct class_ro32_t {
 #define RO_ROOT (1 << 1)
 #define RO_HAS_CXX_STRUCTORS (1 << 2)
 
+/* Values for method_list{64,32,_delta}_t->entsize */
+#define ML_HAS_DELTAS (1 << 31)
+#define ML_ENTSIZE_MASK 0xFFFF
+
 struct method_list64_t {
   uint32_t entsize;
   uint32_t count;
@@ -3673,6 +3683,12 @@ struct method_list32_t {
   /* struct method32_t first;  These structures follow inline */
 };
 
+struct method_list_delta_t {
+  uint32_t entsize;
+  uint32_t count;
+  /* struct method_delta_t first;  These structures follow inline */
+};
+
 struct method64_t {
   uint64_t name;  /* SEL (64-bit pointer) */
   uint64_t types; /* const char * (64-bit pointer) */
@@ -3685,6 +3701,12 @@ struct method32_t {
   uint32_t imp;   /* IMP (32-bit pointer) */
 };
 
+struct method_delta_t {
+  int32_t name;  /* SEL (32-bit delta) */
+  int32_t types; /* const char * (32-bit delta) */
+  int32_t imp;   /* IMP (32-bit delta) */
+};
+
 struct protocol_list64_t {
   uint64_t count; /* uintptr_t (a 64-bit value) */
   /* struct protocol64_t * list[0];  These pointers follow inline */
@@ -3974,6 +3996,11 @@ inline void swapStruct(struct method_list32_t &ml) {
   sys::swapByteOrder(ml.count);
 }
 
+inline void swapStruct(struct method_list_delta_t &ml) {
+  sys::swapByteOrder(ml.entsize);
+  sys::swapByteOrder(ml.count);
+}
+
 inline void swapStruct(struct method64_t &m) {
   sys::swapByteOrder(m.name);
   sys::swapByteOrder(m.types);
@@ -3986,6 +4013,12 @@ inline void swapStruct(struct method32_t &m) {
   sys::swapByteOrder(m.imp);
 }
 
+inline void swapStruct(struct method_delta_t &m) {
+  sys::swapByteOrder(m.name);
+  sys::swapByteOrder(m.types);
+  sys::swapByteOrder(m.imp);
+}
+
 inline void swapStruct(struct protocol_list64_t &pl) {
   sys::swapByteOrder(pl.count);
 }
@@ -4440,8 +4473,104 @@ static void print_layout_map32(uint32_t p, struct DisassembleInfo *info) {
   print_layout_map(layout_map, left);
 }
 
+// Return true if this is a delta method list, false otherwise
+static bool print_method_list_delta_t(uint64_t p, struct DisassembleInfo *info,
+                                      const char *indent,
+                                      uint32_t pointerBits) {
+  struct method_list_delta_t ml;
+  struct method_delta_t m;
+  const char *r, *name;
+  uint32_t offset, xoffset, left, i;
+  SectionRef S, xS;
+
+  r = get_pointer_32(p, offset, left, S, info);
+  if (r == nullptr)
+    return false;
+  memset(&ml, '\0', sizeof(struct method_list_delta_t));
+  if (left < sizeof(struct method_list_delta_t)) {
+    memcpy(&ml, r, left);
+    outs() << "   (method_delta_t extends past the end of the section)\n";
+  } else
+    memcpy(&ml, r, sizeof(struct method_list_delta_t));
+  if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+    swapStruct(ml);
+  if ((ml.entsize & ML_HAS_DELTAS) == 0)
+    return false;
+
+  outs() << indent << "\t\t   entsize " << (ml.entsize & ML_ENTSIZE_MASK)
+         << " (relative) \n";
+  outs() << indent << "\t\t     count " << ml.count << "\n";
+
+  p += sizeof(struct method_list_delta_t);
+  offset += sizeof(struct method_delta_t);
+  for (i = 0; i < ml.count; i++) {
+    r = get_value_32(p, offset, left, S, info);
+    if (r == nullptr)
+      return true;
+    memset(&m, '\0', sizeof(struct method_delta_t));
+    if (left < sizeof(struct method_delta_t)) {
+      memcpy(&m, r, left);
+      outs() << indent << "   (method_t extends past the end of the section)\n";
+    } else
+      memcpy(&m, r, sizeof(struct method_delta_t));
+    if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+      swapStruct(m);
+
+    outs() << indent << "\t\t      name " << format("0x%" PRIx32, m.name);
+    uint64_t relNameRefVA = p + offsetof(struct method_delta_t, name);
+    uint64_t absNameRefVA = relNameRefVA + m.name;
+    outs() << " (" << format("0x%" PRIx32, absNameRefVA) << ")";
+
+    // since this is a delta list, absNameRefVA is the address of the
+    // __objc_selrefs entry, so a pointer, not the actual name
+    const char *nameRefPtr =
+        get_pointer_32(absNameRefVA, xoffset, left, xS, info);
+    if (nameRefPtr) {
+      uint32_t pointerSize = pointerBits / CHAR_BIT;
+      if (left < pointerSize)
+        outs() << indent << " (nameRefPtr extends past the end of the section)";
+      else {
+        uint64_t nameVA = 0;
+        memcpy(&nameVA, nameRefPtr, pointerSize);
+        name = get_pointer_32(nameVA, xoffset, left, xS, info);
+        if (name != nullptr)
+          outs() << format(" %.*s", left, name);
+      }
+    }
+    outs() << "\n";
+
+    outs() << indent << "\t\t     types " << format("0x%" PRIx32, m.types);
+    uint64_t relTypesVA = p + offsetof(struct method_delta_t, types);
+    uint64_t absTypesVA = relTypesVA + m.types;
+    outs() << " (" << format("0x%" PRIx32, absTypesVA) << ")";
+    name = get_pointer_32(absTypesVA, xoffset, left, xS, info);
+    if (name != nullptr)
+      outs() << format(" %.*s", left, name);
+    outs() << "\n";
+
+    outs() << indent << "\t\t       imp " << format("0x%" PRIx32, m.imp);
+    uint64_t relImpVA = p + offsetof(struct method_delta_t, imp);
+    uint64_t absImpVA = relImpVA + m.imp;
+    outs() << " (" << format("0x%" PRIx32, absImpVA) << ")";
+    name = GuessSymbolName(absImpVA, info->AddrMap);
+    if (name != nullptr)
+      outs() << " " << name;
+    outs() << "\n";
+
+    p += sizeof(struct method_delta_t);
+    offset += sizeof(struct method_delta_t);
+  }
+
+  return true;
+}
+
 static void print_method_list64_t(uint64_t p, struct DisassembleInfo *info,
                                   const char *indent) {
+  // Attempt to parse the method list as a delta list. If successful, return
+  // early since the parsing is complete.
+  if (print_method_list_delta_t(p, info, indent, /*pointerBits=*/64))
+    return;
+
   struct method_list64_t ml;
   struct method64_t m;
   const char *r;
@@ -4535,6 +4664,11 @@ static void print_method_list64_t(uint64_t p, struct DisassembleInfo *info,
 
 static void print_method_list32_t(uint64_t p, struct DisassembleInfo *info,
                                   const char *indent) {
+  // Attempt to parse the method list as a delta list. If successful, return
+  // early since the parsing is complete.
+  if (print_method_list_delta_t(p, info, indent, /*pointerBits=*/32))
+    return;
+
   struct method_list32_t ml;
   struct method32_t m;
   const char *r, *name;



More information about the llvm-commits mailing list