Skip to content

[lld/mac] Allow -segprot having stricter initprot than maxprot on mac #107269

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions lld/MachO/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1882,9 +1882,21 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
StringRef segName = arg->getValue(0);
uint32_t maxProt = parseProtection(arg->getValue(1));
uint32_t initProt = parseProtection(arg->getValue(2));
if (maxProt != initProt && config->arch() != AK_i386)
error("invalid argument '" + arg->getAsString(args) +
"': max and init must be the same for non-i386 archs");

// FIXME: Check if this works on more platforms.
bool allowsDifferentInitAndMaxProt =
config->platform() == PLATFORM_MACOS ||
config->platform() == PLATFORM_MACCATALYST;
if (allowsDifferentInitAndMaxProt) {
if (initProt > maxProt)
error("invalid argument '" + arg->getAsString(args) +
"': init must not be more permissive than max");
} else {
if (maxProt != initProt && config->arch() != AK_i386)
error("invalid argument '" + arg->getAsString(args) +
"': max and init must be the same for non-macOS non-i386 archs");
}

if (segName == segment_names::linkEdit)
error("-segprot cannot be used to change __LINKEDIT's protections");
config->segmentProtections.push_back({segName, maxProt, initProt});
Expand Down
6 changes: 6 additions & 0 deletions lld/MachO/OutputSegment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ static uint32_t initProt(StringRef name) {
static uint32_t maxProt(StringRef name) {
assert(config->arch() != AK_i386 &&
"TODO: i386 has different maxProt requirements");
auto it = find_if(
config->segmentProtections,
[&](const SegmentProtection &segprot) { return segprot.name == name; });
if (it != config->segmentProtections.end())
return it->maxProt;

return initProt(name);
}

Expand Down
41 changes: 35 additions & 6 deletions lld/test/MachO/segprot.s
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o

## Make sure the option parser doesn't think --x and -w are flags.
# RUN: %lld -dylib -o %t %t.o -segprot FOO rwx xwr -segprot BAR --x --x -segprot BAZ -w -w
# RUN: %lld -dylib -o %t %t.o \
# RUN: -segprot FOO rwx xwr \
# RUN: -segprot BAR --x --x \
# RUN: -segprot BAZ -w -w
# RUN: llvm-readobj --macho-segment %t | FileCheck %s

# CHECK: Name: FOO
Expand Down Expand Up @@ -32,12 +35,38 @@
# CHECK-NEXT: maxprot: -w-
# CHECK-NEXT: initprot: -w-

# RUN: not %lld -dylib -o /dev/null %t.o -segprot FOO rwx rw 2>&1 | FileCheck %s --check-prefix=MISMATCH
# RUN: not %lld -dylib -o /dev/null %t.o -segprot __LINKEDIT rwx rwx 2>&1 | FileCheck %s --check-prefix=NO-LINKEDIT
# RUN: not %lld -dylib -o /dev/null %t.o -segprot FOO uhh wat 2>&1 | FileCheck %s --check-prefix=MISPARSE
# RUN: not %lld -dylib -o /dev/null %t.o -segprot FOO rwx 2>&1 | FileCheck %s --check-prefix=MISSING
# RUN: %lld -dylib -o %t.different %t.o -segprot FOO rw r
# RUN: llvm-readobj --macho-segment %t.different \
# RUN: | FileCheck %s --check-prefix=DIFFERENT

# MISMATCH: error: invalid argument '-segprot FOO rwx rw': max and init must be the same for non-i386 archs
# RUN: %no-arg-lld -arch x86_64 -platform_version "mac catalyst" 14.0.0 17.5 \
# RUN: -dylib -o /dev/null %t.o -segprot FOO rw r
# RUN: llvm-readobj --macho-segment %t.different \
# RUN: | FileCheck %s --check-prefix=DIFFERENT

# DIFFERENT: Name: FOO
# DIFFERENT-NEXT: Size:
# DIFFERENT-NEXT: vmaddr:
# DIFFERENT-NEXT: vmsize:
# DIFFERENT-NEXT: fileoff:
# DIFFERENT-NEXT: filesize:
# DIFFERENT-NEXT: maxprot: rw-
# DIFFERENT-NEXT: initprot: r--

# RUN: not %no-arg-lld -arch x86_64 -platform_version ios-simulator 14.0 15.0 \
# RUN: -dylib -o /dev/null %t.o -segprot FOO rwx rw 2>&1 \
# RUN: | FileCheck %s --check-prefix=MISMATCH
# RUN: not %lld -dylib -o /dev/null %t.o -segprot FOO r rw 2>&1 \
# RUN: | FileCheck %s --check-prefix=INITTOOPERMISSIVE
# RUN: not %lld -dylib -o /dev/null %t.o -segprot __LINKEDIT rwx rwx 2>&1 \
# RUN: | FileCheck %s --check-prefix=NO-LINKEDIT
# RUN: not %lld -dylib -o /dev/null %t.o -segprot FOO uhh wat 2>&1 \
# RUN: | FileCheck %s --check-prefix=MISPARSE
# RUN: not %lld -dylib -o /dev/null %t.o -segprot FOO rwx 2>&1 \
# RUN: | FileCheck %s --check-prefix=MISSING

# MISMATCH: error: invalid argument '-segprot FOO rwx rw': max and init must be the same for non-macOS non-i386 archs
# INITTOOPERMISSIVE: error: invalid argument '-segprot FOO r rw': init must not be more permissive than max
# NO-LINKEDIT: error: -segprot cannot be used to change __LINKEDIT's protections
# MISPARSE: error: unknown -segprot letter 'u' in uhh
# MISPARSE: error: unknown -segprot letter 'a' in wat
Expand Down
Loading