2 changes: 2 additions & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,8 @@ ENUM_LANGOPT(ExtendIntArgs, ExtendArgsKind, 1, ExtendArgsKind::ExtendTo32,

VALUE_LANGOPT(FuchsiaAPILevel, 32, 0, "Fuchsia API level")

LANGOPT(HashSectionNames, 32, 0, "Set for -fhash-section-names=N. N defines both hash size and threshold size. 0 := no hashing.")

#undef LANGOPT
#undef COMPATIBLE_LANGOPT
#undef BENIGN_LANGOPT
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ class LangOptions : public LangOptionsBase {
ExtendTo64
};

std::string HashedSectionNamesOutputFile;

public:
/// The used language standard.
LangStandard::Kind LangStd;
Expand Down Expand Up @@ -388,6 +390,14 @@ class LangOptions : public LangOptionsBase {
void set##Name(Type Value) { Name = static_cast<unsigned>(Value); }
#include "clang/Basic/LangOptions.def"

unsigned hashSectionNames() const {
return HashSectionNames;
}

std::string getHashedSectionNamesOutputFile() const {
return HashedSectionNamesOutputFile;
}

/// Are we compiling a module interface (.cppm or module map)?
bool isCompilingModule() const {
return getCompilingModule() != CMK_None;
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -6463,3 +6463,10 @@ def _SLASH_Ze : CLFlag<"Ze">;
def _SLASH_Zg : CLFlag<"Zg">;
def _SLASH_ZI : CLFlag<"ZI">;
def _SLASH_ZW : CLJoined<"ZW">;
def fhash_section_names_EQ : Joined<["-"],"fhash-section-names=">, Group<f_Group>,
Flags<[CC1Option,CC1AsOption]>, MarshallingInfoInt<LangOpts<"HashSectionNames">, "0">, MetaVarName<"<N>">,
HelpText<"Hash section names longer than N charachters. Defaults to \"0\" (no hashing).">;
def fhashed_section_names_EQ : Joined<["-"],"fhashed-section-names=">,
Flags<[NoXarchOption, CC1Option, CC1AsOption]>, MetaVarName<"<path>">,
MarshallingInfoString<LangOpts<"HashedSectionNamesOutputFile">, [{"none"}]>,
HelpText<"Output hashed section names to <path>.">;
4 changes: 4 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,8 @@ class Sema final {
QualType ResultTy,
ArrayRef<QualType> Args);

void hashSectionNameForLabel(StringRef& Name);

public:
/// The maximum alignment, same as in llvm::Value. We duplicate them here
/// because that allows us not to duplicate the constants in clang code,
Expand Down Expand Up @@ -4362,6 +4364,8 @@ class Sema final {
SourceLocation *ArgLocation = nullptr);
llvm::Error isValidSectionSpecifier(StringRef Str);
bool checkSectionName(SourceLocation LiteralLoc, StringRef Str);
void hashSectionNameForSectionAttr(StringRef& Label);
void hashSectionNameForAsmLabelAttr(StringRef& Label);
bool checkTargetAttr(SourceLocation LiteralLoc, StringRef Str);
bool checkTargetClonesAttrString(SourceLocation LiteralLoc, StringRef Str,
const StringLiteral *Literal,
Expand Down
20 changes: 20 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5439,6 +5439,26 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
options::OPT_fno_unique_section_names, true))
CmdArgs.push_back("-fno-unique-section-names");

if (Args.hasArg(options::OPT_fhash_section_names_EQ)) {
auto *Arg = Args.getLastArg(options::OPT_fhash_section_names_EQ);
unsigned Value = 0;
if (StringRef(Arg->getValue()).getAsInteger(10, Value))
D.Diag(diag::err_drv_invalid_int_value)
<< Arg->getAsString(Args) << Arg->getValue();
// Treat =0 as unspecified
if (Value)
CmdArgs.push_back(Args.MakeArgString(
"-fhash-section-names=" + Twine(Value)));
}

if (Args.hasArg(options::OPT_fhashed_section_names_EQ)) {
auto *Arg = Args.getLastArg(options::OPT_fhashed_section_names_EQ);
auto Value = StringRef(Arg->getValue());
// TODO: if Value is unspecified, then use the output file with extension '.sn'
CmdArgs.push_back(Args.MakeArgString(
"-fhashed-section-names=" + Twine(Value)));
}

if (Args.hasFlag(options::OPT_funique_internal_linkage_names,
options::OPT_fno_unique_internal_linkage_names, false))
CmdArgs.push_back("-funique-internal-linkage-names");
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ add_clang_library(clangSema
SemaOpenMP.cpp
SemaOverload.cpp
SemaPseudoObject.cpp
SemaSection.cpp
SemaStmt.cpp
SemaStmtAsm.cpp
SemaStmtAttr.cpp
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@ void Sema::ActOnPragmaClangSection(SourceLocation PragmaLoc,
return;
}

// Possibly hash long section name based on "-fhash-long-section-names=N"
hashSectionNameForSectionAttr(SecName);

if (UnifySection(SecName, SectionFlags, PragmaLoc))
return;

Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7443,6 +7443,13 @@ NamedDecl *Sema::ActOnVariableDeclarator(
}
}

// Handle MachO Section start / end attribute - e.g. "section$start$__RODATA$mysection"
if (Context.getTargetInfo().getTriple().isOSDarwin()
&& (Label.startswith("section$start$") || Label.startswith("section$end$"))
&& std::count_if(Label.begin(), Label.end(), [](char c){return c == '$';}) >= 3) {
hashSectionNameForAsmLabelAttr(Label);
}

NewVD->addAttr(AsmLabelAttr::Create(Context, Label,
/*IsLiteralLabel=*/true,
SE->getStrTokenLoc(0)));
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3140,8 +3140,9 @@ llvm::Error Sema::isValidSectionSpecifier(StringRef SecName) {
StringRef Segment, Section;
unsigned TAA, StubSize;
bool HasTAA;
unsigned N = Context.getLangOpts().hashSectionNames();
return llvm::MCSectionMachO::ParseSectionSpecifier(SecName, Segment, Section,
TAA, HasTAA, StubSize);
TAA, HasTAA, StubSize, N > 0);
}

bool Sema::checkSectionName(SourceLocation LiteralLoc, StringRef SecName) {
Expand All @@ -3164,6 +3165,9 @@ static void handleSectionAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
if (!S.checkSectionName(LiteralLoc, Str))
return;

// Possibly hash long section name based on "-fhash-long-section-names=N"
S.hashSectionNameForSectionAttr(Str);

SectionAttr *NewAttr = S.mergeSectionAttr(D, AL, Str);
if (NewAttr) {
D->addAttr(NewAttr);
Expand Down
187 changes: 187 additions & 0 deletions clang/lib/Sema/SemaSection.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// Copyright (c) 2021 Friedt Professional Engineering Services, Inc
//
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <stdio.h>

#include <mutex>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "clang/Basic/TargetInfo.h"
#include "clang/Sema/Sema.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/Support/Base64.h"
#include "llvm/Support/SHA256.h"

using namespace clang;
using namespace llvm;

namespace std {
template <> struct hash<StringRef> {
std::size_t operator()(const StringRef &k) const { return hash_value(k); }
};
} // namespace std

std::mutex HashMutex;
// Provide storage for Hash strings
static std::unordered_set<std::string> HashStorage;
// Cache known hashes (i.e. "Section" -> "Hash")
static std::unordered_map<StringRef, StringRef> HashForward;

// the algorithm used for hashing long section names
static std::string hashit(const StringRef &Name, unsigned N) {
SHA256 Hasher;
Hasher.update(Name);
return encodeBase64(Hasher.result()).substr(0, N);
}

static void writeHashedSectionNameToFile(const std::string &filename,
const StringRef &Section,
const StringRef &Hash,
bool IsWindows) {
FILE *fp;

fp = fopen(filename.c_str(), "a");
if (NULL == fp) {
return;
}

fprintf(fp, "%s\t%s%s", Section.str().c_str(), Hash.str().c_str(),
IsWindows ? "\r\n" : "\n");
fclose(fp);
}

static StringRef hashSectionName(const StringRef &Section, unsigned N,
const std::string &OutputFile,
bool IsWindows) {
StringRef Hash;

assert(N > 0);

auto it = HashForward.find(Section);
if (HashForward.end() != it) {
Hash = it->second;
return Hash;
}

std::string hash = hashit(Section, N);
HashStorage.insert(hash);
Hash = StringRef(*HashStorage.find(hash));
HashForward[Section] = Hash;

writeHashedSectionNameToFile(OutputFile, Section, Hash, IsWindows);

return Hash;
}

// replace 'from' with 'to' inside of str
static void replace(std::string &str, const StringRef &from,
const StringRef &to) {
size_t start_pos = str.find(from.str());
if (start_pos == std::string::npos)
return;
str.replace(start_pos, from.size(), to.str());
}

// Common code for transforming Attributes

static void
hashForAttrCommon(unsigned N, bool IsDarwin, bool IsWindows,
StringRef (*GetDarwinSection)(const StringRef &),
std::unordered_map<StringRef, std::string> &NameCache,
const std::string &OutputFileName, StringRef &Name) {
StringRef Hash;
StringRef Section = Name;

if (IsDarwin) {
Section = GetDarwinSection(Name);
}

if (N == 0 || Section.size() < N) {
return;
}

std::lock_guard<decltype(HashMutex)> lock(HashMutex);

Hash = hashSectionName(Section, N, OutputFileName, IsWindows);
if (Hash == Section) {
return;
}

std::string tmp = Name.str();
replace(tmp, Section, Hash);
NameCache[Name] = tmp;
Name = StringRef(NameCache[Name]);
}

// Code for transforming SectionlAttr
// for __attribute__(section("__RODATA,ThisSectionNameIsTooLong"))
// => __attribute__(section("__RODATA,ip9RNVxH27rCS+Ix"))

static StringRef getMachOSectionFromSectionAttr(const StringRef &Name) {
StringRef Segment, Section;
unsigned TAA, StubSize;
bool HasTAA;

auto err = MCSectionMachO::ParseSectionSpecifier(Name, Segment, Section, TAA,
HasTAA, StubSize, true);
assert(!err && "MCSectionMachO::ParseSectionSpecifier() failed");

return Section;
}

void Sema::hashSectionNameForSectionAttr(StringRef &Name) {
static std::unordered_map<StringRef, std::string> NameCache;
unsigned N = Context.getLangOpts().hashSectionNames();
bool IsDarwin = Context.getTargetInfo().getTriple().isOSDarwin();
bool IsWindows = Context.getTargetInfo().getTriple().isOSWindows();
auto GetDarwinSection = getMachOSectionFromSectionAttr;
std::string OutputFileName =
Context.getLangOpts().getHashedSectionNamesOutputFile();

hashForAttrCommon(N, IsDarwin, IsWindows, GetDarwinSection, NameCache,
OutputFileName, Name);
}

// Code for transforming AsmLabelAttr
// e.g. __asm("section$start$__RODATA$ThisSectionNameIsTooLong")
// => __asm("section$start$__RODATA$ip9RNVxH27rCS+Ix")

static StringRef getMachOSectionFromMachOAsmLabelAttr(const StringRef &Name) {
size_t a, b;

for (a = 0, b = 0; a < Name.size() && b < 3; ++a) {
if (Name[a] == '$') {
++b;
}
}

for (b = a; b < Name.size(); ++b) {
if (Name[b] == '$') {
break;
}
}

if (b >= Name.size()) {
b = StringRef::npos;
}

return Name.substr(a, b);
}

void Sema::hashSectionNameForAsmLabelAttr(StringRef &Name) {
static std::unordered_map<StringRef, std::string> NameCache;
unsigned N = Context.getLangOpts().hashSectionNames();
bool IsDarwin = Context.getTargetInfo().getTriple().isOSDarwin();
bool IsWindows = Context.getTargetInfo().getTriple().isOSWindows();
auto GetDarwinSection = getMachOSectionFromMachOAsmLabelAttr;
std::string OutputFileName =
Context.getLangOpts().getHashedSectionNamesOutputFile();

hashForAttrCommon(N, IsDarwin, IsWindows, GetDarwinSection, NameCache,
OutputFileName, Name);
}
24 changes: 24 additions & 0 deletions clang/test/SemaCXX/attr-section-hashed-macos.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
__attribute__((section("__RODATA,SectOK")))
int bar;

__attribute__((section("__RODATA,ThisSectionNameIsTooLong")))
int foo;

void baz() {
extern int start_foo[] __asm("section$start$__RODATA$ThisSectionNameIsTooLong");
extern int end_foo[] __asm("section$end$__RODATA$ThisSectionNameIsTooLong");

const int *bim = start_foo;
const int *bap = end_foo;
}

#pragma clang attribute section("__RODATA,ThisSectionNameIsTooLong")

// RUN: %clang_cc1 -fhash-section-names=16 -emit-llvm -o - %s | FileCheck %s
// REQUIRES: system-darwin
// CHECK: @bar = global i32 0, section "__RODATA,SectOK"
// CHECK: @foo = global i32 0, section "__RODATA,ip9RNVxH27rCS+Ix"
// CHECK: store i32* getelementptr inbounds ([0 x i32], [0 x i32]* @"\01section$start$__RODATA$ip9RNVxH27rCS+Ix"
// CHECK: store i32* getelementptr inbounds ([0 x i32], [0 x i32]* @"\01section$end$__RODATA$ip9RNVxH27rCS+Ix"

// TODO: test for #pragma
14 changes: 14 additions & 0 deletions clang/test/SemaCXX/attr-section-hashed.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
__attribute__((section("__RODATA,SectOK")))
int bar;

__attribute__((section("__RODATA,ThisSectionNameIsTooLong")))
int foo;

#pragma clang attribute section("__RODATA,ThisSectionNameIsTooLong")

// RUN: %clang_cc1 -fhash-section-names=16 -emit-llvm -o - %s | FileCheck %s
// REQUIRES: system-darwin
// CHECK: @bar = global i32 0, section "__RODATA,SectOK"
// CHECK: @foo = global i32 0, section "__RODATA,ip9RNVxH27rCS+Ix"

// TODO: test for #pragma
3 changes: 2 additions & 1 deletion llvm/include/llvm/MC/MCSectionMachO.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ class MCSectionMachO final : public MCSection {
StringRef &Section, // Out.
unsigned &TAA, // Out.
bool &TAAParsed, // Out.
unsigned &StubSize); // Out.
unsigned &StubSize, // Out.
bool HashedSectionName = false); // In.

void PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
raw_ostream &OS,
Expand Down
5 changes: 3 additions & 2 deletions llvm/lib/MC/MCSectionMachO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ Error MCSectionMachO::ParseSectionSpecifier(StringRef Spec, // In.
StringRef &Section, // Out.
unsigned &TAA, // Out.
bool &TAAParsed, // Out.
unsigned &StubSize) { // Out.
unsigned &StubSize, // Out.
bool HashedSectionName) { // In.
TAAParsed = false;

SmallVector<StringRef, 5> SplitSpec;
Expand All @@ -201,7 +202,7 @@ Error MCSectionMachO::ParseSectionSpecifier(StringRef Spec, // In.
"and section separated by a comma");

// Verify that the section is not too long.
if (Section.size() > 16)
if (!HashedSectionName && Section.size() > 16)
return createStringError(inconvertibleErrorCode(),
"mach-o section specifier requires a section "
"whose length is between 1 and 16 characters");
Expand Down