From 3168192de567815c511e384707a3a5063b81189b Mon Sep 17 00:00:00 2001 From: Frederik Carlier Date: Fri, 12 Jan 2024 01:03:37 -0800 Subject: [PATCH] [ObjC]: Make type encoding safe in symbol names (#77797) Type encodings are part of symbol names in the Objective C ABI. Replace characters which are reseved in symbol names: - ELF: avoid including '@' characters in type encodings - Windows: avoid including '=' characters in type encodings --- clang/lib/CodeGen/CGObjCGNU.cpp | 30 ++++++++++-------- clang/test/CodeGenObjC/dllstorage.m | 18 +++++------ clang/test/CodeGenObjC/encode-test-6.m | 42 ++++++++++++++++++++------ 3 files changed, 59 insertions(+), 31 deletions(-) diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index cd1a0b6a130f..9cc7f32815f7 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -1431,12 +1431,24 @@ class CGObjCGNUstep2 : public CGObjCGNUstep { const std::string &TypeEncoding) override { return GetConstantSelector(Sel, TypeEncoding); } + std::string GetSymbolNameForTypeEncoding(const std::string &TypeEncoding) { + std::string MangledTypes = std::string(TypeEncoding); + // @ is used as a special character in ELF symbol names (used for symbol + // versioning), so mangle the name to not include it. Replace it with a + // character that is not a valid type encoding character (and, being + // non-printable, never will be!) + if (CGM.getTriple().isOSBinFormatELF()) + std::replace(MangledTypes.begin(), MangledTypes.end(), '@', '\1'); + // = in dll exported names causes lld to fail when linking on Windows. + if (CGM.getTriple().isOSWindows()) + std::replace(MangledTypes.begin(), MangledTypes.end(), '=', '\2'); + return MangledTypes; + } llvm::Constant *GetTypeString(llvm::StringRef TypeEncoding) { if (TypeEncoding.empty()) return NULLPtr; - std::string MangledTypes = std::string(TypeEncoding); - std::replace(MangledTypes.begin(), MangledTypes.end(), - '@', '\1'); + std::string MangledTypes = + GetSymbolNameForTypeEncoding(std::string(TypeEncoding)); std::string TypesVarName = ".objc_sel_types_" + MangledTypes; auto *TypesGlobal = TheModule.getGlobalVariable(TypesVarName); if (!TypesGlobal) { @@ -1453,13 +1465,7 @@ class CGObjCGNUstep2 : public CGObjCGNUstep { } llvm::Constant *GetConstantSelector(Selector Sel, const std::string &TypeEncoding) override { - // @ is used as a special character in symbol names (used for symbol - // versioning), so mangle the name to not include it. Replace it with a - // character that is not a valid type encoding character (and, being - // non-printable, never will be!) - std::string MangledTypes = TypeEncoding; - std::replace(MangledTypes.begin(), MangledTypes.end(), - '@', '\1'); + std::string MangledTypes = GetSymbolNameForTypeEncoding(TypeEncoding); auto SelVarName = (StringRef(".objc_selector_") + Sel.getAsString() + "_" + MangledTypes).str(); if (auto *GV = TheModule.getNamedGlobal(SelVarName)) @@ -1671,9 +1677,7 @@ class CGObjCGNUstep2 : public CGObjCGNUstep { const ObjCIvarDecl *Ivar) override { std::string TypeEncoding; CGM.getContext().getObjCEncodingForType(Ivar->getType(), TypeEncoding); - // Prevent the @ from being interpreted as a symbol version. - std::replace(TypeEncoding.begin(), TypeEncoding.end(), - '@', '\1'); + TypeEncoding = GetSymbolNameForTypeEncoding(TypeEncoding); const std::string Name = "__objc_ivar_offset_" + ID->getNameAsString() + '.' + Ivar->getNameAsString() + '.' + TypeEncoding; return Name; diff --git a/clang/test/CodeGenObjC/dllstorage.m b/clang/test/CodeGenObjC/dllstorage.m index 0dbf1881caa9..f45eb7bb6aee 100644 --- a/clang/test/CodeGenObjC/dllstorage.m +++ b/clang/test/CodeGenObjC/dllstorage.m @@ -35,7 +35,7 @@ __declspec(dllexport) // CHECK-IR-DAG: @"OBJC_IVAR_$_J._ivar" = global i32 -// CHECK-NF-DAG: @"__objc_ivar_offset_J._ivar.\01" = hidden global i32 +// CHECK-NF-DAG: @"__objc_ivar_offset_J._ivar.@" = hidden global i32 @interface K : J @end @@ -56,7 +56,7 @@ __declspec(dllexport) // CHECK-IR-DAG: @"OBJC_IVAR_$_K._ivar" = global i32 -// CHECK-NF-DAG: @"__objc_ivar_offset_K._ivar.\01" = hidden global i32 +// CHECK-NF-DAG: @"__objc_ivar_offset_K._ivar.@" = hidden global i32 __declspec(dllexport) @interface L : K @@ -94,11 +94,11 @@ __declspec(dllexport) // CHECK-IR-DAG: @"OBJC_IVAR_$_L._package" = global i32 // CHECK-IR-DAG: @"OBJC_IVAR_$_L._private" = global i32 -// CHECK-NF-DAG: @"__objc_ivar_offset_L._none.\01" = hidden global i32 -// CHECK-NF-DAG: @"__objc_ivar_offset_L._public.\01" = dso_local dllexport global i32 -// CHECK-NF-DAG: @"__objc_ivar_offset_L._protected.\01" = dso_local dllexport global i32 -// CHECK-NF-DAG: @"__objc_ivar_offset_L._package.\01" = hidden global i32 -// CHECK-NF-DAG: @"__objc_ivar_offset_L._private.\01" = hidden global i32 +// CHECK-NF-DAG: @"__objc_ivar_offset_L._none.@" = hidden global i32 +// CHECK-NF-DAG: @"__objc_ivar_offset_L._public.@" = dso_local dllexport global i32 +// CHECK-NF-DAG: @"__objc_ivar_offset_L._protected.@" = dso_local dllexport global i32 +// CHECK-NF-DAG: @"__objc_ivar_offset_L._package.@" = hidden global i32 +// CHECK-NF-DAG: @"__objc_ivar_offset_L._private.@" = hidden global i32 __declspec(dllimport) @interface M : I { @@ -112,7 +112,7 @@ __declspec(dllimport) // CHECK-IR-DAG: @"OBJC_IVAR_$_M._ivar" = external dllimport global i32 // CHECK-NF-DAG: @"$_OBJC_REF_CLASS_M" = external dllimport global ptr -// CHECK-NF-DAG: @"__objc_ivar_offset_M._ivar.\01" = external global i32 +// CHECK-NF-DAG: @"__objc_ivar_offset_M._ivar.@" = external global i32 __declspec(dllexport) __attribute__((__objc_exception__)) @@ -151,7 +151,7 @@ id f(Q *q) { // CHECK-IR-DAG: @"OBJC_IVAR_$_M._ivar" = external dllimport global i32 -// CHECK-NF-DAG: @"__objc_ivar_offset_M._ivar.\01" = external global i32 +// CHECK-NF-DAG: @"__objc_ivar_offset_M._ivar.@" = external global i32 int g(void) { @autoreleasepool { diff --git a/clang/test/CodeGenObjC/encode-test-6.m b/clang/test/CodeGenObjC/encode-test-6.m index 261eb7fb3368..c32f8f24c000 100644 --- a/clang/test/CodeGenObjC/encode-test-6.m +++ b/clang/test/CodeGenObjC/encode-test-6.m @@ -1,5 +1,10 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o %t %s -// RUN: FileCheck < %t %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s | FileCheck -check-prefix CHECK-DWARF %s + +// RUN: %clang_cc1 -triple x86_64-w64-windows-gnu -emit-llvm -fobjc-runtime=gnustep-2.0 -o - %s | FileCheck -check-prefix CHECK-MINGW %s + +// RUN: %clang_cc1 -triple x86_64-windows-msvc -emit-llvm -fobjc-runtime=gnustep-2.0 -o - %s | FileCheck -check-prefix CHECK-MSVC %s + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fobjc-runtime=gnustep-2.0 -o - %s | FileCheck -check-prefix CHECK-ELF %s typedef struct {} Z; @@ -13,8 +18,17 @@ typedef struct {} Z; -(void)foo:(Z)a: (char*)b : (Z)c : (double) d {} @end -// CHECK: private unnamed_addr constant [14 x i8] c"v16@0:8{?=}16 -// CHECK: private unnamed_addr constant [26 x i8] c"v32@0:8{?=}16*16{?=}24d24 +// CHECK-DWARF: private unnamed_addr constant [14 x i8] c"v16@0:8{?=}16 +// CHECK-DWARF: private unnamed_addr constant [26 x i8] c"v32@0:8{?=}16*16{?=}24d24 + +// CHECK-MINGW: @".objc_sel_types_v16@0:8{?\02}16" = linkonce_odr hidden constant [14 x i8] c"v16@0:8{?=}16\00" +// CHECK-MINGW: @".objc_sel_types_v32@0:8{?\02}16*16{?\02}24d24" = linkonce_odr hidden constant [26 x i8] c"v32@0:8{?=}16*16{?=}24d24\00" + +// CHECK-MSVC: @".objc_sel_types_v20@0:8{?\02}16" = linkonce_odr hidden constant [14 x i8] c"v20@0:8{?=}16\00" +// CHECK-MSVC: @".objc_sel_types_v40@0:8{?\02}16*20{?\02}28d32" = linkonce_odr hidden constant [26 x i8] c"v40@0:8{?=}16*20{?=}28d32\00" + +// CHECK-ELF: @".objc_sel_types_v16\010:8{?=}16" = linkonce_odr hidden constant [14 x i8] c"v16@0:8{?=}16\00" +// CHECK-ELF: @".objc_sel_types_v32\010:8{?=}16*16{?=}24d24" = linkonce_odr hidden constant [26 x i8] c"v32@0:8{?=}16*16{?=}24d24\00" @interface NSObject @end @@ -31,7 +45,10 @@ typedef BABugExample BABugExampleRedefinition; @synthesize property = _property; @end -// CHECK: private unnamed_addr constant [8 x i8] c"@16 +// CHECK-DWARF: private unnamed_addr constant [8 x i8] c"@16 +// CHECK-MINGW: @".objc_sel_types_@16@0:8" = linkonce_odr hidden constant [8 x i8] c"@16@0:8\00" +// CHECK-MSVC: @".objc_sel_types_@16@0:8" = linkonce_odr hidden constant [8 x i8] c"@16@0:8\00" +// CHECK-ELF @".objc_sel_types_\0116\010:8" = linkonce_odr hidden constant [8 x i8] c"@16@0:8\00" @class SCNCamera; typedef SCNCamera C3DCamera; @@ -48,7 +65,10 @@ typedef struct C3DCameraStorage _storage; } @end -// CHECK: private unnamed_addr constant [39 x i8] c"{?=\22presentationInstance\22@\22SCNCamera\22}\00" +// CHECK-DWARF: private unnamed_addr constant [39 x i8] c"{?=\22presentationInstance\22@\22SCNCamera\22}\00" +// CHECK-MINGW: @"__objc_ivar_offset_SCNCamera._storage.{?\02@}" +// CHECK-MSVC: @"__objc_ivar_offset_SCNCamera._storage.{?\02@}" +// CHECK-ELF: @"__objc_ivar_offset_SCNCamera._storage.{?=\01}" int i; typeof(@encode(typeof(i))) e = @encode(typeof(i)); @@ -56,6 +76,10 @@ const char * Test(void) { return e; } -// CHECK: @e ={{.*}} global [2 x i8] c"i\00", align 1 -// CHECK: define{{.*}} ptr @Test() -// CHECK: ret ptr @e +// CHECK-DWARF: @e ={{.*}} global [2 x i8] c"i\00", align 1 +// CHECK-DWARF: define{{.*}} ptr @Test() +// CHECK-DWARF: ret ptr @e + +// CHECK-MSVC: @e = dso_local global [2 x i8] c"i\00", align 1 +// CHECK-MINGW: @e = dso_local global [2 x i8] c"i\00", align 1 +// CHECK-ELF: @e = global [2 x i8] c"i\00", align 1