Skip to content

Commit f9be4b3

Browse files
committed
initial implementation of the darwin_objc unstable feature
1 parent bec7474 commit f9be4b3

File tree

22 files changed

+501
-5
lines changed

22 files changed

+501
-5
lines changed

compiler/rustc_attr_parsing/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ attr_parsing_null_on_export = `export_name` may not contain null characters
116116
117117
attr_parsing_null_on_link_section = `link_section` may not contain null characters
118118
119+
attr_parsing_null_on_objc_class = `rustc_objc_class` may not contain null characters
120+
121+
attr_parsing_null_on_objc_selector = `rustc_objc_selector` may not contain null characters
122+
119123
attr_parsing_repr_ident =
120124
meta item in `repr` must be an identifier
121125

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ use super::{
1111
use crate::context::MaybeWarn::{Allow, Warn};
1212
use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage};
1313
use crate::parser::ArgParser;
14-
use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport};
14+
use crate::session_diagnostics::{
15+
NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector,
16+
};
1517

1618
pub(crate) struct OptimizeParser;
1719

@@ -157,6 +159,64 @@ impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
157159
}
158160
}
159161

162+
pub(crate) struct ObjcClassParser;
163+
164+
impl<S: Stage> SingleAttributeParser<S> for ObjcClassParser {
165+
const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];
166+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
167+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
168+
const ALLOWED_TARGETS: AllowedTargets =
169+
AllowedTargets::AllowListWarnRest(&[Allow(Target::ForeignStatic)]);
170+
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName");
171+
172+
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
173+
let Some(nv) = args.name_value() else {
174+
cx.expected_name_value(cx.attr_span, None);
175+
return None;
176+
};
177+
let Some(classname) = nv.value_as_str() else {
178+
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
179+
return None;
180+
};
181+
if classname.as_str().contains('\0') {
182+
// `#[rustc_objc_class = ...]` will be converted to a null-terminated string,
183+
// so it may not contain any null characters.
184+
cx.emit_err(NullOnObjcClass { span: cx.attr_span });
185+
return None;
186+
}
187+
Some(AttributeKind::ObjcClass { classname, span: cx.attr_span })
188+
}
189+
}
190+
191+
pub(crate) struct ObjcSelectorParser;
192+
193+
impl<S: Stage> SingleAttributeParser<S> for ObjcSelectorParser {
194+
const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];
195+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
196+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
197+
const ALLOWED_TARGETS: AllowedTargets =
198+
AllowedTargets::AllowListWarnRest(&[Allow(Target::ForeignStatic)]);
199+
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName");
200+
201+
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
202+
let Some(nv) = args.name_value() else {
203+
cx.expected_name_value(cx.attr_span, None);
204+
return None;
205+
};
206+
let Some(methname) = nv.value_as_str() else {
207+
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
208+
return None;
209+
};
210+
if methname.as_str().contains('\0') {
211+
// `#[rustc_objc_selector = ...]` will be converted to a null-terminated string,
212+
// so it may not contain any null characters.
213+
cx.emit_err(NullOnObjcSelector { span: cx.attr_span });
214+
return None;
215+
}
216+
Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span })
217+
}
218+
}
219+
160220
#[derive(Default)]
161221
pub(crate) struct NakedParser {
162222
span: Option<Span>,

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ use crate::attributes::allow_unstable::{
2121
};
2222
use crate::attributes::body::CoroutineParser;
2323
use crate::attributes::codegen_attrs::{
24-
ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser,
25-
TargetFeatureParser, TrackCallerParser, UsedParser,
24+
ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, ObjcClassParser,
25+
ObjcSelectorParser, OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser,
2626
};
2727
use crate::attributes::confusables::ConfusablesParser;
2828
use crate::attributes::deprecation::DeprecationParser;
@@ -179,6 +179,8 @@ attribute_parsers!(
179179
Single<LinkSectionParser>,
180180
Single<LinkageParser>,
181181
Single<MustUseParser>,
182+
Single<ObjcClassParser>,
183+
Single<ObjcSelectorParser>,
182184
Single<OptimizeParser>,
183185
Single<PathAttributeParser>,
184186
Single<ProcMacroDeriveParser>,

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,20 @@ pub(crate) struct NullOnLinkSection {
459459
pub span: Span,
460460
}
461461

462+
#[derive(Diagnostic)]
463+
#[diag(attr_parsing_null_on_objc_class)]
464+
pub(crate) struct NullOnObjcClass {
465+
#[primary_span]
466+
pub span: Span,
467+
}
468+
469+
#[derive(Diagnostic)]
470+
#[diag(attr_parsing_null_on_objc_selector)]
471+
pub(crate) struct NullOnObjcSelector {
472+
#[primary_span]
473+
pub span: Span,
474+
}
475+
462476
#[derive(Diagnostic)]
463477
#[diag(attr_parsing_stability_outside_std, code = E0734)]
464478
pub(crate) struct StabilityOutsideStd {

compiler/rustc_codegen_llvm/src/base.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ pub(crate) fn compile_codegen_unit(
109109
attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs);
110110
}
111111

112+
// Define Objective-C module info for 32-bit x86 macOS. Note, this generates a global
113+
// that gets added to the `llvm.compiler.used` variable, created later.
114+
let uses_objc =
115+
!cx.objc_classrefs.borrow().is_empty() || !cx.objc_selrefs.borrow().is_empty();
116+
if uses_objc && cx.tcx.sess.target.arch == "x86" && cx.tcx.sess.target.os == "macos" {
117+
cx.define_objc_module_info();
118+
}
119+
112120
// Finalize code coverage by injecting the coverage map. Note, the coverage map will
113121
// also be added to the `llvm.compiler.used` variable, created next.
114122
if cx.sess().instrument_coverage() {
@@ -132,6 +140,11 @@ pub(crate) fn compile_codegen_unit(
132140
}
133141
}
134142

143+
// Add Objective-C module flags.
144+
if uses_objc {
145+
cx.add_objc_module_flags();
146+
}
147+
135148
// Finalize debuginfo
136149
if cx.sess().opts.debuginfo != DebugInfo::None {
137150
cx.debuginfo_finalize();

compiler/rustc_codegen_llvm/src/common.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
108108
bytes_in_context(self.llcx(), bytes)
109109
}
110110

111+
pub(crate) fn null_terminate_const_bytes(&self, bytes: &[u8]) -> &'ll Value {
112+
null_terminate_bytes_in_context(self.llcx(), bytes)
113+
}
114+
111115
pub(crate) fn const_get_elt(&self, v: &'ll Value, idx: u64) -> &'ll Value {
112116
unsafe {
113117
let idx = c_uint::try_from(idx).expect("LLVMGetAggregateElement index overflow");
@@ -381,6 +385,16 @@ pub(crate) fn bytes_in_context<'ll>(llcx: &'ll llvm::Context, bytes: &[u8]) -> &
381385
}
382386
}
383387

388+
pub(crate) fn null_terminate_bytes_in_context<'ll>(
389+
llcx: &'ll llvm::Context,
390+
bytes: &[u8],
391+
) -> &'ll Value {
392+
unsafe {
393+
let ptr = bytes.as_ptr() as *const c_char;
394+
llvm::LLVMConstStringInContext2(llcx, ptr, bytes.len(), False)
395+
}
396+
}
397+
384398
pub(crate) fn named_struct<'ll>(ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value {
385399
let len = c_uint::try_from(elts.len()).expect("LLVMConstStructInContext elements len overflow");
386400
unsafe { llvm::LLVMConstNamedStruct(ty, elts.as_ptr(), len) }

compiler/rustc_codegen_llvm/src/consts.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc_middle::mir::mono::MonoItem;
1616
use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
1717
use rustc_middle::ty::{self, Instance};
1818
use rustc_middle::{bug, span_bug};
19+
use rustc_span::Symbol;
1920
use tracing::{debug, instrument, trace};
2021

2122
use crate::common::CodegenCx;
@@ -331,6 +332,10 @@ impl<'ll> CodegenCx<'ll, '_> {
331332
}
332333

333334
g
335+
} else if let Some(classname) = fn_attrs.objc_class {
336+
self.get_objc_classref(classname)
337+
} else if let Some(methname) = fn_attrs.objc_selector {
338+
self.get_objc_selref(methname)
334339
} else {
335340
check_and_apply_linkage(self, fn_attrs, llty, sym, def_id)
336341
};
@@ -544,6 +549,165 @@ impl<'ll> CodegenCx<'ll, '_> {
544549
pub(crate) fn add_compiler_used_global(&mut self, global: &'ll Value) {
545550
self.compiler_used_statics.push(global);
546551
}
552+
553+
fn define_objc_classname(&self, classname: &str) -> &'ll Value {
554+
// 32-bit x86 macOS only.
555+
assert_eq!(self.tcx.sess.target.arch, "x86");
556+
assert_eq!(self.tcx.sess.target.os, "macos");
557+
558+
let llval = self.null_terminate_const_bytes(classname.as_bytes());
559+
let llty = self.val_ty(llval);
560+
let sym = self.generate_local_symbol_name("OBJC_CLASS_NAME_");
561+
let g = self.define_global(&sym, llty).unwrap_or_else(|| {
562+
bug!("symbol `{}` is already defined", sym);
563+
});
564+
set_global_alignment(self, g, self.tcx.data_layout.i8_align.abi);
565+
llvm::set_initializer(g, llval);
566+
llvm::set_linkage(g, llvm::Linkage::PrivateLinkage);
567+
llvm::set_section(g, c"__TEXT,__cstring,cstring_literals");
568+
llvm::LLVMSetGlobalConstant(g, llvm::True);
569+
llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
570+
g
571+
}
572+
573+
fn get_objc_class_t(&self) -> &'ll Type {
574+
if let Some(class_t) = self.objc_class_t.get() {
575+
return class_t;
576+
}
577+
578+
// Darwin-like targets other than 32-bit x86 macOS.
579+
assert!(self.tcx.sess.target.is_like_darwin);
580+
assert!(self.tcx.sess.target.arch != "x86" || self.tcx.sess.target.os != "macos");
581+
582+
let class_t = self.type_named_struct("struct._class_t");
583+
let els = [self.type_ptr(); 5];
584+
let packed = false;
585+
self.set_struct_body(class_t, &els, packed);
586+
587+
self.objc_class_t.set(Some(class_t));
588+
class_t
589+
}
590+
591+
fn get_objc_classref(&self, classname: Symbol) -> &'ll Value {
592+
let mut classrefs = self.objc_classrefs.borrow_mut();
593+
if let Some(classref) = classrefs.get(&classname).copied() {
594+
return classref;
595+
}
596+
597+
let g = if self.tcx.sess.target.arch == "x86" && self.tcx.sess.target.os == "macos" {
598+
// 32-bit x86 macOS.
599+
let llval = self.define_objc_classname(classname.as_str());
600+
let llty = self.type_ptr();
601+
let sym = self.generate_local_symbol_name("OBJC_CLASS_REFERENCES_");
602+
let g = self.define_global(&sym, llty).unwrap_or_else(|| {
603+
bug!("symbol `{}` is already defined", sym);
604+
});
605+
set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi);
606+
llvm::set_initializer(g, llval);
607+
llvm::set_linkage(g, llvm::Linkage::PrivateLinkage);
608+
llvm::set_section(g, c"__OBJC,__cls_refs,literal_pointers");
609+
g
610+
} else {
611+
// Darwin-like targets other than 32-bit x86 macOS.
612+
assert!(self.tcx.sess.target.is_like_darwin);
613+
let llval = {
614+
let extern_sym = format!("OBJC_CLASS_$_{}", classname.as_str());
615+
let extern_llty = self.get_objc_class_t();
616+
self.declare_global(&extern_sym, extern_llty)
617+
};
618+
let llty = self.type_ptr();
619+
let sym = self.generate_local_symbol_name("OBJC_CLASSLIST_REFERENCES_$_");
620+
let g = self.define_global(&sym, llty).unwrap_or_else(|| {
621+
bug!("symbol `{}` is already defined", sym);
622+
});
623+
set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi);
624+
llvm::set_initializer(g, llval);
625+
llvm::set_linkage(g, llvm::Linkage::InternalLinkage);
626+
llvm::set_section(g, c"__DATA,__objc_classrefs,regular");
627+
g
628+
};
629+
630+
classrefs.insert(classname, g);
631+
g
632+
}
633+
634+
fn get_objc_selref(&self, methname: Symbol) -> &'ll Value {
635+
let mut selrefs = self.objc_selrefs.borrow_mut();
636+
if let Some(selref) = selrefs.get(&methname).copied() {
637+
return selref;
638+
}
639+
640+
// Darwin-like targets only.
641+
assert!(self.tcx.sess.target.is_like_darwin);
642+
let is_x86_32_macos =
643+
self.tcx.sess.target.arch == "x86" && self.tcx.sess.target.os == "macos";
644+
645+
let methname_llval = self.null_terminate_const_bytes(methname.as_str().as_bytes());
646+
let methname_llty = self.val_ty(methname_llval);
647+
let methname_sym = self.generate_local_symbol_name("OBJC_METH_VAR_NAME_");
648+
let methname_g = self.define_global(&methname_sym, methname_llty).unwrap_or_else(|| {
649+
bug!("symbol `{}` is already defined", methname_sym);
650+
});
651+
set_global_alignment(self, methname_g, self.tcx.data_layout.i8_align.abi);
652+
llvm::set_initializer(methname_g, methname_llval);
653+
llvm::set_linkage(methname_g, llvm::Linkage::PrivateLinkage);
654+
if is_x86_32_macos {
655+
llvm::set_section(methname_g, c"__TEXT,__cstring,cstring_literals");
656+
} else {
657+
llvm::set_section(methname_g, c"__TEXT,__objc_methname,cstring_literals");
658+
}
659+
llvm::LLVMSetGlobalConstant(methname_g, llvm::True);
660+
llvm::LLVMSetUnnamedAddress(methname_g, llvm::UnnamedAddr::Global);
661+
662+
let selref_llval = methname_g;
663+
let selref_llty = self.type_ptr();
664+
let selref_sym = self.generate_local_symbol_name("OBJC_SELECTOR_REFERENCES_");
665+
let selref_g = self.define_global(&selref_sym, selref_llty).unwrap_or_else(|| {
666+
bug!("symbol `{}` is already defined", selref_sym);
667+
});
668+
set_global_alignment(self, selref_g, self.tcx.data_layout.pointer_align().abi);
669+
llvm::set_initializer(selref_g, selref_llval);
670+
if is_x86_32_macos {
671+
llvm::set_linkage(selref_g, llvm::Linkage::PrivateLinkage);
672+
llvm::set_section(selref_g, c"__OBJC,__message_refs,literal_pointers");
673+
} else {
674+
llvm::set_linkage(selref_g, llvm::Linkage::InternalLinkage);
675+
llvm::set_section(selref_g, c"__DATA,__objc_selrefs,literal_pointers");
676+
}
677+
678+
selrefs.insert(methname, selref_g);
679+
selref_g
680+
}
681+
682+
pub(crate) fn define_objc_module_info(&mut self) {
683+
// 32-bit x86 macOS only.
684+
assert_eq!(self.tcx.sess.target.arch, "x86");
685+
assert_eq!(self.tcx.sess.target.os, "macos");
686+
687+
let llty = self.type_named_struct("struct._objc_module");
688+
let i32_llty = self.type_i32();
689+
let ptr_llty = self.type_ptr();
690+
let packed = false;
691+
self.set_struct_body(llty, &[i32_llty, i32_llty, ptr_llty, ptr_llty], packed);
692+
693+
let version = self.const_uint(i32_llty, 7);
694+
let size = self.const_uint(i32_llty, 16);
695+
let name = self.define_objc_classname("");
696+
let symtab = self.const_null(ptr_llty);
697+
let packed = false;
698+
let llval = self.const_struct(&[version, size, name, symtab], packed);
699+
700+
let sym = "OBJC_MODULES";
701+
let g = self.define_global(&sym, llty).unwrap_or_else(|| {
702+
bug!("symbol `{}` is already defined", sym);
703+
});
704+
set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi);
705+
llvm::set_initializer(g, llval);
706+
llvm::set_linkage(g, llvm::Linkage::PrivateLinkage);
707+
llvm::set_section(g, c"__OBJC,__module_info,regular,no_dead_strip");
708+
709+
self.add_compiler_used_global(g);
710+
}
547711
}
548712

549713
impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> {

0 commit comments

Comments
 (0)