Skip to content

Commit c6a916d

Browse files
alx32yuxuanchen1997
authored andcommitted
[lld-macho] Fix erasing category names for ObjC categories (#99400)
Summary: We were already not deleting category names for Swift classes as those names can be reused by other parts. However, I have come across a corner case where this also happens for ObjC categories - so we can't delete category names for them either. TODO remains to optimize this behavior for both ObjC and Swift. Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D60251277
1 parent 878ef2e commit c6a916d

File tree

2 files changed

+315
-5
lines changed

2 files changed

+315
-5
lines changed

lld/MachO/ObjC.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,12 +1303,16 @@ void ObjcCategoryMerger::eraseMergedCategories() {
13031303
continue;
13041304

13051305
eraseISec(catInfo.catBodyIsec);
1306-
// We can't erase 'catLayout.nameOffset' for Swift categories because the
1307-
// name will be referenced for generating relative offsets
1308-
// See usages of 'l_.str.11.SimpleClass' in objc-category-merging-swift.s
1306+
1307+
// We can't erase 'catLayout.nameOffset' for either Swift or ObjC
1308+
// categories because the name will sometimes also be used for other
1309+
// purposes.
1310+
// For Swift, see usages of 'l_.str.11.SimpleClass' in
1311+
// objc-category-merging-swift.s
1312+
// For ObjC, see usages of 'l_OBJC_CLASS_NAME_.1' in
1313+
// objc-category-merging-erase-objc-name-test.s
13091314
// TODO: handle the above in a smarter way
1310-
if (catInfo.sourceLanguage != SourceLanguage::Swift)
1311-
tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec, catLayout.nameOffset);
1315+
13121316
tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
13131317
catLayout.instanceMethodsOffset);
13141318
tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
; REQUIRES: aarch64
2+
3+
; Here we test that if we defined a protocol MyTestProtocol and also a category MyTestProtocol
4+
; then when merging the category into the base class (and deleting the category), we don't
5+
; delete the 'MyTestProtocol' name
6+
7+
; RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o erase-objc-name.o %s
8+
; RUN: %lld -arch arm64 -dylib -o erase-objc-name.dylib erase-objc-name.o -objc_category_merging
9+
; RUN: llvm-objdump --objc-meta-data --macho erase-objc-name.dylib | FileCheck %s --check-prefixes=MERGE_CATS
10+
11+
; === Check merge categories enabled ===
12+
; Check that the original categories are not there
13+
; MERGE_CATS-NOT: __OBJC_$_CATEGORY_MyBaseClass_$_Category01
14+
; MERGE_CATS-NOT: __OBJC_$_CATEGORY_MyBaseClass_$_Category02
15+
16+
; Check that we get the expected output - most importantly that the protocol is named `MyTestProtocol`
17+
; MERGE_CATS: Contents of (__DATA_CONST,__objc_classlist) section
18+
; MERGE_CATS-NEXT: _OBJC_CLASS_$_MyBaseClass
19+
; MERGE_CATS-NEXT: isa {{.*}} _OBJC_METACLASS_$_MyBaseClass
20+
; MERGE_CATS-NEXT: superclass {{.*}}
21+
; MERGE_CATS-NEXT: cache {{.*}}
22+
; MERGE_CATS-NEXT: vtable {{.*}}
23+
; MERGE_CATS-NEXT: data {{.*}} (struct class_ro_t *)
24+
; MERGE_CATS-NEXT: flags {{.*}} RO_ROOT
25+
; MERGE_CATS-NEXT: instanceStart 0
26+
; MERGE_CATS-NEXT: instanceSize 0
27+
; MERGE_CATS-NEXT: reserved {{.*}}
28+
; MERGE_CATS-NEXT: ivarLayout {{.*}}
29+
; MERGE_CATS-NEXT: name {{.*}} MyBaseClass
30+
; MERGE_CATS-NEXT: baseMethods {{.*}} (struct method_list_t *)
31+
; MERGE_CATS-NEXT: entsize 24
32+
; MERGE_CATS-NEXT: count 2
33+
; MERGE_CATS-NEXT: name {{.*}} getValue
34+
; MERGE_CATS-NEXT: types {{.*}} i16@0:8
35+
; MERGE_CATS-NEXT: imp -[MyBaseClass(MyTestProtocol) getValue]
36+
; MERGE_CATS-NEXT: name {{.*}} baseInstanceMethod
37+
; MERGE_CATS-NEXT: types {{.*}} v16@0:8
38+
; MERGE_CATS-NEXT: imp -[MyBaseClass baseInstanceMethod]
39+
; MERGE_CATS-NEXT: baseProtocols {{.*}}
40+
; MERGE_CATS-NEXT: count 1
41+
; MERGE_CATS-NEXT: list[0] {{.*}} (struct protocol_t *)
42+
; MERGE_CATS-NEXT: isa {{.*}}
43+
; MERGE_CATS-NEXT: name {{.*}} MyTestProtocol
44+
; MERGE_CATS-NEXT: protocols {{.*}}
45+
; MERGE_CATS-NEXT: instanceMethods {{.*}} (struct method_list_t *)
46+
; MERGE_CATS-NEXT: entsize 24
47+
; MERGE_CATS-NEXT: count 1
48+
; MERGE_CATS-NEXT: name {{.*}} getValue
49+
; MERGE_CATS-NEXT: types {{.*}} i16@0:8
50+
; MERGE_CATS-NEXT: imp {{.*}}
51+
; MERGE_CATS-NEXT: classMethods {{.*}} (struct method_list_t *)
52+
; MERGE_CATS-NEXT: optionalInstanceMethods {{.*}}
53+
; MERGE_CATS-NEXT: optionalClassMethods {{.*}}
54+
; MERGE_CATS-NEXT: instanceProperties {{.*}}
55+
; MERGE_CATS-NEXT: ivars {{.*}}
56+
; MERGE_CATS-NEXT: weakIvarLayout {{.*}}
57+
; MERGE_CATS-NEXT: baseProperties {{.*}}
58+
; MERGE_CATS-NEXT: Meta Class
59+
; MERGE_CATS-NEXT: isa {{.*}} _OBJC_METACLASS_$_MyBaseClass
60+
; MERGE_CATS-NEXT: superclass {{.*}} _OBJC_CLASS_$_MyBaseClass
61+
; MERGE_CATS-NEXT: cache {{.*}}
62+
; MERGE_CATS-NEXT: vtable {{.*}}
63+
; MERGE_CATS-NEXT: data {{.*}} (struct class_ro_t *)
64+
; MERGE_CATS-NEXT: flags {{.*}} RO_META RO_ROOT
65+
; MERGE_CATS-NEXT: instanceStart 40
66+
; MERGE_CATS-NEXT: instanceSize 40
67+
; MERGE_CATS-NEXT: reserved {{.*}}
68+
; MERGE_CATS-NEXT: ivarLayout {{.*}}
69+
; MERGE_CATS-NEXT: name {{.*}} MyBaseClass
70+
; MERGE_CATS-NEXT: baseMethods {{.*}} (struct method_list_t *)
71+
; MERGE_CATS-NEXT: baseProtocols {{.*}}
72+
; MERGE_CATS-NEXT: count 1
73+
; MERGE_CATS-NEXT: list[0] {{.*}} (struct protocol_t *)
74+
; MERGE_CATS-NEXT: isa {{.*}}
75+
; MERGE_CATS-NEXT: name {{.*}} MyTestProtocol
76+
; MERGE_CATS-NEXT: protocols {{.*}}
77+
; MERGE_CATS-NEXT: instanceMethods {{.*}} (struct method_list_t *)
78+
; MERGE_CATS-NEXT: entsize 24
79+
; MERGE_CATS-NEXT: count 1
80+
; MERGE_CATS-NEXT: name {{.*}} getValue
81+
; MERGE_CATS-NEXT: types {{.*}} i16@0:8
82+
; MERGE_CATS-NEXT: imp {{.*}}
83+
; MERGE_CATS-NEXT: classMethods {{.*}} (struct method_list_t *)
84+
; MERGE_CATS-NEXT: optionalInstanceMethods {{.*}}
85+
; MERGE_CATS-NEXT: optionalClassMethods {{.*}}
86+
; MERGE_CATS-NEXT: instanceProperties {{.*}}
87+
; MERGE_CATS-NEXT: ivars {{.*}}
88+
; MERGE_CATS-NEXT: weakIvarLayout {{.*}}
89+
; MERGE_CATS-NEXT: baseProperties {{.*}}
90+
; MERGE_CATS-NEXT: Contents of (__DATA_CONST,__objc_protolist) section
91+
; MERGE_CATS-NEXT: {{.*}} {{.*}} __OBJC_PROTOCOL_$_MyTestProtocol
92+
; MERGE_CATS-NEXT: Contents of (__DATA_CONST,__objc_imageinfo) section
93+
; MERGE_CATS-NEXT: version 0
94+
; MERGE_CATS-NEXT: flags {{.*}} OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES
95+
96+
97+
; ================== repro.sh ====================
98+
; # Write the Objective-C code to a file
99+
; cat << EOF > MyClass.m
100+
; @protocol MyTestProtocol
101+
; - (int)getValue;
102+
; @end
103+
;
104+
; __attribute__((objc_root_class))
105+
; @interface MyBaseClass
106+
; - (void)baseInstanceMethod;
107+
; @end
108+
;
109+
; @implementation MyBaseClass
110+
; - (void)baseInstanceMethod {}
111+
; @end
112+
;
113+
; @interface MyBaseClass (MyTestProtocol) <MyTestProtocol>
114+
; @end
115+
;
116+
; @implementation MyBaseClass (MyTestProtocol)
117+
;
118+
; - (int)getValue {
119+
; return 0x30;
120+
; }
121+
;
122+
; @end
123+
; EOF
124+
;
125+
; # Compile the Objective-C file to assembly
126+
; xcrun clang -S -arch arm64 MyClass.m -o MyClass.s
127+
; ==============================================
128+
129+
130+
.section __TEXT,__text,regular,pure_instructions
131+
.p2align 2 ; -- Begin function -[MyBaseClass baseInstanceMethod]
132+
"-[MyBaseClass baseInstanceMethod]": ; @"\01-[MyBaseClass baseInstanceMethod]"
133+
.cfi_startproc
134+
; %bb.0:
135+
sub sp, sp, #16
136+
.cfi_def_cfa_offset 16
137+
str x0, [sp, #8]
138+
str x1, [sp]
139+
add sp, sp, #16
140+
ret
141+
.cfi_endproc
142+
; -- End function
143+
.p2align 2 ; -- Begin function -[MyBaseClass(MyTestProtocol) getValue]
144+
"-[MyBaseClass(MyTestProtocol) getValue]": ; @"\01-[MyBaseClass(MyTestProtocol) getValue]"
145+
.cfi_startproc
146+
; %bb.0:
147+
sub sp, sp, #16
148+
.cfi_def_cfa_offset 16
149+
str x0, [sp, #8]
150+
str x1, [sp]
151+
mov w0, #48 ; =0x30
152+
add sp, sp, #16
153+
ret
154+
.cfi_endproc
155+
; -- End function
156+
.section __DATA,__objc_data
157+
.globl _OBJC_CLASS_$_MyBaseClass ; @"OBJC_CLASS_$_MyBaseClass"
158+
.p2align 3, 0x0
159+
_OBJC_CLASS_$_MyBaseClass:
160+
.quad _OBJC_METACLASS_$_MyBaseClass
161+
.quad 0
162+
.quad __objc_empty_cache
163+
.quad 0
164+
.quad __OBJC_CLASS_RO_$_MyBaseClass
165+
.globl _OBJC_METACLASS_$_MyBaseClass ; @"OBJC_METACLASS_$_MyBaseClass"
166+
.p2align 3, 0x0
167+
_OBJC_METACLASS_$_MyBaseClass:
168+
.quad _OBJC_METACLASS_$_MyBaseClass
169+
.quad _OBJC_CLASS_$_MyBaseClass
170+
.quad __objc_empty_cache
171+
.quad 0
172+
.quad __OBJC_METACLASS_RO_$_MyBaseClass
173+
.section __TEXT,__objc_classname,cstring_literals
174+
l_OBJC_CLASS_NAME_: ; @OBJC_CLASS_NAME_
175+
.asciz "MyBaseClass"
176+
.section __DATA,__objc_const
177+
.p2align 3, 0x0 ; @"_OBJC_METACLASS_RO_$_MyBaseClass"
178+
__OBJC_METACLASS_RO_$_MyBaseClass:
179+
.long 131 ; 0x83
180+
.long 40 ; 0x28
181+
.long 40 ; 0x28
182+
.space 4
183+
.quad 0
184+
.quad l_OBJC_CLASS_NAME_
185+
.quad 0
186+
.quad 0
187+
.quad 0
188+
.quad 0
189+
.quad 0
190+
.section __TEXT,__objc_methname,cstring_literals
191+
l_OBJC_METH_VAR_NAME_: ; @OBJC_METH_VAR_NAME_
192+
.asciz "baseInstanceMethod"
193+
.section __TEXT,__objc_methtype,cstring_literals
194+
l_OBJC_METH_VAR_TYPE_: ; @OBJC_METH_VAR_TYPE_
195+
.asciz "v16@0:8"
196+
.section __DATA,__objc_const
197+
.p2align 3, 0x0 ; @"_OBJC_$_INSTANCE_METHODS_MyBaseClass"
198+
__OBJC_$_INSTANCE_METHODS_MyBaseClass:
199+
.long 24 ; 0x18
200+
.long 1 ; 0x1
201+
.quad l_OBJC_METH_VAR_NAME_
202+
.quad l_OBJC_METH_VAR_TYPE_
203+
.quad "-[MyBaseClass baseInstanceMethod]"
204+
.p2align 3, 0x0 ; @"_OBJC_CLASS_RO_$_MyBaseClass"
205+
__OBJC_CLASS_RO_$_MyBaseClass:
206+
.long 130 ; 0x82
207+
.long 0 ; 0x0
208+
.long 0 ; 0x0
209+
.space 4
210+
.quad 0
211+
.quad l_OBJC_CLASS_NAME_
212+
.quad __OBJC_$_INSTANCE_METHODS_MyBaseClass
213+
.quad 0
214+
.quad 0
215+
.quad 0
216+
.quad 0
217+
.section __TEXT,__objc_classname,cstring_literals
218+
l_OBJC_CLASS_NAME_.1: ; @OBJC_CLASS_NAME_.1
219+
.asciz "MyTestProtocol"
220+
.section __TEXT,__objc_methname,cstring_literals
221+
l_OBJC_METH_VAR_NAME_.2: ; @OBJC_METH_VAR_NAME_.2
222+
.asciz "getValue"
223+
.section __TEXT,__objc_methtype,cstring_literals
224+
l_OBJC_METH_VAR_TYPE_.3: ; @OBJC_METH_VAR_TYPE_.3
225+
.asciz "i16@0:8"
226+
.section __DATA,__objc_const
227+
.p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_MyTestProtocol"
228+
__OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_MyTestProtocol:
229+
.long 24 ; 0x18
230+
.long 1 ; 0x1
231+
.quad l_OBJC_METH_VAR_NAME_.2
232+
.quad l_OBJC_METH_VAR_TYPE_.3
233+
.quad "-[MyBaseClass(MyTestProtocol) getValue]"
234+
.p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_INSTANCE_METHODS_MyTestProtocol"
235+
__OBJC_$_PROTOCOL_INSTANCE_METHODS_MyTestProtocol:
236+
.long 24 ; 0x18
237+
.long 1 ; 0x1
238+
.quad l_OBJC_METH_VAR_NAME_.2
239+
.quad l_OBJC_METH_VAR_TYPE_.3
240+
.quad 0
241+
.p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_METHOD_TYPES_MyTestProtocol"
242+
__OBJC_$_PROTOCOL_METHOD_TYPES_MyTestProtocol:
243+
.quad l_OBJC_METH_VAR_TYPE_.3
244+
.private_extern __OBJC_PROTOCOL_$_MyTestProtocol ; @"_OBJC_PROTOCOL_$_MyTestProtocol"
245+
.section __DATA,__data
246+
.globl __OBJC_PROTOCOL_$_MyTestProtocol
247+
.weak_definition __OBJC_PROTOCOL_$_MyTestProtocol
248+
.p2align 3, 0x0
249+
__OBJC_PROTOCOL_$_MyTestProtocol:
250+
.quad 0
251+
.quad l_OBJC_CLASS_NAME_.1
252+
.quad 0
253+
.quad __OBJC_$_PROTOCOL_INSTANCE_METHODS_MyTestProtocol
254+
.quad 0
255+
.quad 0
256+
.quad 0
257+
.quad 0
258+
.long 96 ; 0x60
259+
.long 0 ; 0x0
260+
.quad __OBJC_$_PROTOCOL_METHOD_TYPES_MyTestProtocol
261+
.quad 0
262+
.quad 0
263+
.private_extern __OBJC_LABEL_PROTOCOL_$_MyTestProtocol ; @"_OBJC_LABEL_PROTOCOL_$_MyTestProtocol"
264+
.section __DATA,__objc_protolist,coalesced,no_dead_strip
265+
.globl __OBJC_LABEL_PROTOCOL_$_MyTestProtocol
266+
.weak_definition __OBJC_LABEL_PROTOCOL_$_MyTestProtocol
267+
.p2align 3, 0x0
268+
__OBJC_LABEL_PROTOCOL_$_MyTestProtocol:
269+
.quad __OBJC_PROTOCOL_$_MyTestProtocol
270+
.section __DATA,__objc_const
271+
.p2align 3, 0x0 ; @"_OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_MyTestProtocol"
272+
__OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_MyTestProtocol:
273+
.quad 1 ; 0x1
274+
.quad __OBJC_PROTOCOL_$_MyTestProtocol
275+
.quad 0
276+
.p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_MyBaseClass_$_MyTestProtocol"
277+
__OBJC_$_CATEGORY_MyBaseClass_$_MyTestProtocol:
278+
.quad l_OBJC_CLASS_NAME_.1
279+
.quad _OBJC_CLASS_$_MyBaseClass
280+
.quad __OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_MyTestProtocol
281+
.quad 0
282+
.quad __OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_MyTestProtocol
283+
.quad 0
284+
.quad 0
285+
.long 64 ; 0x40
286+
.space 4
287+
.section __DATA,__objc_classlist,regular,no_dead_strip
288+
.p2align 3, 0x0 ; @"OBJC_LABEL_CLASS_$"
289+
l_OBJC_LABEL_CLASS_$:
290+
.quad _OBJC_CLASS_$_MyBaseClass
291+
.section __DATA,__objc_catlist,regular,no_dead_strip
292+
.p2align 3, 0x0 ; @"OBJC_LABEL_CATEGORY_$"
293+
l_OBJC_LABEL_CATEGORY_$:
294+
.quad __OBJC_$_CATEGORY_MyBaseClass_$_MyTestProtocol
295+
.no_dead_strip __OBJC_PROTOCOL_$_MyTestProtocol
296+
.no_dead_strip __OBJC_LABEL_PROTOCOL_$_MyTestProtocol
297+
.section __DATA,__objc_imageinfo,regular,no_dead_strip
298+
L_OBJC_IMAGE_INFO:
299+
.long 0
300+
.long 64
301+
302+
__objc_empty_cache:
303+
_$sBOWV:
304+
.quad 0
305+
306+
.subsections_via_symbols

0 commit comments

Comments
 (0)