mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
IJPL-172978 +docs
(cherry picked from commit d6238e51184e5161690a182a4b94af5a18be1307) IJ-CR-151551 GitOrigin-RevId: 05377185001ef9d26691d2c7f71a273dd271b969
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9f38e13c1b
commit
85e522304a
@@ -168,7 +168,15 @@ open class MacDistributionCustomizer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.jetbrains.intellij.build.NativeBinaryDownloader.getLauncher
|
||||
* Generates a UUID to be used as an identity of an IDE Mach-O image.
|
||||
*
|
||||
* For a native macOS app, the Apple linker (ld) sets the build UUID based on a hash of the built code. But for an IDE it doesn't work that way.
|
||||
* For different IDEs, the source code can be different, but a [org.jetbrains.intellij.build.NativeBinaryDownloader.getLauncher] binary may be exactly the same.
|
||||
* So, the different IDEs may get the same UUIDs.
|
||||
* And according to [the technote](https://developer.apple.com/documentation/technotes/tn3178-checking-for-and-resolving-build-uuid-problems), this may lead to the troubles:
|
||||
* > Each distinct Mach-O image must have its own unique build UUID.
|
||||
* > If you have two apps with different bundle IDs and the same main executable UUID, you might encounter weird problems with those subsystems.
|
||||
* > For example, the network subsystem might apply constraints for one of your apps to the other app.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
open fun getDistributionUUID(context: BuildContext, currentUuid: UUID?): UUID {
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.jetbrains.intellij.build.MacDistributionCustomizer
|
||||
import org.jetbrains.intellij.build.io.runProcess
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.nio.channels.SeekableByteChannel
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardOpenOption
|
||||
@@ -20,53 +21,97 @@ import java.util.*
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
class MachOUuid(private val executable: Path, private val customizer: MacDistributionCustomizer, private val context: BuildContext) {
|
||||
/**
|
||||
* The 64-bit mach header appears at the very beginning of object files for 64-bit architectures:
|
||||
* ```
|
||||
* struct mach_header_64 {
|
||||
* uint32_t magic; /* mach magic number identifier */
|
||||
* cpu_type_t cputype; /* cpu specifier */
|
||||
* cpu_subtype_t cpusubtype; /* machine specifier */
|
||||
* uint32_t filetype; /* type of file */
|
||||
* uint32_t ncmds; /* number of load commands */
|
||||
* uint32_t sizeofcmds; /* the size of all the load commands */
|
||||
* uint32_t flags; /* flags */
|
||||
* uint32_t reserved; /* reserved */
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* The load commands directly follow the mach_header:
|
||||
* ```
|
||||
* struct load_command {
|
||||
* uint32_t cmd; /* type of load command */
|
||||
* uint32_t cmdsize; /* total size of command in bytes */
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* See https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h
|
||||
*/
|
||||
private companion object {
|
||||
const val LC_UUID = 0x1b
|
||||
const val LC_UUID: Int = 0x1b
|
||||
|
||||
/**
|
||||
* [Integer.toHexString] is "feedfacf"
|
||||
*/
|
||||
const val MH_MAGIC_64: Int = -0x1120531
|
||||
|
||||
/**
|
||||
* `mach_header_64.magic` size is four bytes (`uint32_t`)
|
||||
*/
|
||||
const val MACH_HEADER_64_MAGIC_SIZE_IN_BYTES: Int = 4
|
||||
|
||||
/**
|
||||
* The start position of `mach_header_64.ncmds`
|
||||
*/
|
||||
const val MACH_HEADER_64_NCMDS_POSITION_IN_BYTES: Long = 16
|
||||
|
||||
/**
|
||||
* `load_command.cmd`(`uint32_t`) + `load_command.cmdsize`(`uint32_t`)
|
||||
*/
|
||||
const val LOAD_COMMAND_HEADER_SIZE_IN_BYTES: Int = 8
|
||||
|
||||
/**
|
||||
* The start position of `load_command`s, right after the `mach_header_64`
|
||||
*/
|
||||
const val LOAD_COMMANDS_POSITION_IN_BYTES: Long = 32
|
||||
}
|
||||
|
||||
private val canBeSignedLocally: Boolean = context.options.isInDevelopmentMode && SystemInfoRt.isMac
|
||||
|
||||
private fun checkMagic(channel: SeekableByteChannel, buffer: ByteBuffer) {
|
||||
channel.read(buffer)
|
||||
buffer.flip()
|
||||
val magic = buffer.getInt()
|
||||
check(magic == MH_MAGIC_64) { "Not a valid 64-bit Mach-O file: 0x" + Integer.toHexString(magic) }
|
||||
buffer.clear()
|
||||
}
|
||||
|
||||
private fun readNumberOfLoadCommands(channel: SeekableByteChannel, buffer: ByteBuffer): Int {
|
||||
channel.position(MACH_HEADER_64_NCMDS_POSITION_IN_BYTES)
|
||||
channel.read(buffer)
|
||||
buffer.flip()
|
||||
return buffer.getInt()
|
||||
}
|
||||
|
||||
suspend fun patch() {
|
||||
val canBeSignedLocally = context.options.isInDevelopmentMode && SystemInfoRt.isMac
|
||||
Files.newByteChannel(executable, StandardOpenOption.READ, StandardOpenOption.WRITE).use { channel ->
|
||||
var buffer = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
|
||||
channel.read(buffer)
|
||||
buffer.flip()
|
||||
val magic = buffer.getInt()
|
||||
check(magic == -0x1120531) { "Not a valid 64-bit Mach-O file: 0x" + Integer.toHexString(magic) }
|
||||
buffer.clear()
|
||||
channel.position(16)
|
||||
channel.read(buffer)
|
||||
buffer.flip()
|
||||
val nCmds = buffer.getInt()
|
||||
buffer = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN)
|
||||
channel.position(32)
|
||||
var buffer = ByteBuffer.allocate(MACH_HEADER_64_MAGIC_SIZE_IN_BYTES).order(ByteOrder.LITTLE_ENDIAN)
|
||||
checkMagic(channel, buffer)
|
||||
val nCmds = readNumberOfLoadCommands(channel, buffer)
|
||||
buffer = ByteBuffer.allocate(LOAD_COMMAND_HEADER_SIZE_IN_BYTES).order(ByteOrder.LITTLE_ENDIAN)
|
||||
channel.position(LOAD_COMMANDS_POSITION_IN_BYTES)
|
||||
(0..<nCmds).forEach {
|
||||
buffer.clear()
|
||||
channel.read(buffer)
|
||||
buffer.flip()
|
||||
val cmd = buffer.getInt()
|
||||
val cmdSize = buffer.getInt()
|
||||
val cmdBodySize = cmdSize - LOAD_COMMAND_HEADER_SIZE_IN_BYTES
|
||||
if (cmd == LC_UUID) {
|
||||
buffer = ByteBuffer.allocate(16).order(ByteOrder.BIG_ENDIAN)
|
||||
channel.read(buffer)
|
||||
buffer.flip()
|
||||
val msb = buffer.getLong()
|
||||
val lsb = buffer.getLong()
|
||||
val currentUuid = UUID(msb, lsb)
|
||||
context.messages.info("current UUID of $executable: $currentUuid")
|
||||
buffer.clear()
|
||||
val newUuid = customizer.getDistributionUUID(context, currentUuid)
|
||||
buffer.putLong(newUuid.mostSignificantBits)
|
||||
buffer.putLong(newUuid.leastSignificantBits)
|
||||
buffer.flip()
|
||||
if (context.isMacCodeSignEnabled || canBeSignedLocally) {
|
||||
channel.position(channel.position() - 16)
|
||||
channel.write(buffer)
|
||||
context.messages.info("new UUID of $executable: $newUuid")
|
||||
}
|
||||
patchUUID(channel, cmdBodySize)
|
||||
return@use
|
||||
}
|
||||
else {
|
||||
channel.position(channel.position() + cmdSize - 8)
|
||||
channel.position(channel.position() + cmdBodySize)
|
||||
}
|
||||
}
|
||||
context.messages.error("LC_UUID not found in $executable")
|
||||
@@ -76,4 +121,24 @@ class MachOUuid(private val executable: Path, private val customizer: MacDistrib
|
||||
runProcess(listOf("codesign", "--sign", "-", "--force", executable.toString()), inheritOut = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun patchUUID(channel: SeekableByteChannel, cmdBodySize: Int) {
|
||||
val buffer = ByteBuffer.allocate(cmdBodySize).order(ByteOrder.BIG_ENDIAN)
|
||||
channel.read(buffer)
|
||||
buffer.flip()
|
||||
val mostSigBits = buffer.getLong()
|
||||
val leastSigBits = buffer.getLong()
|
||||
val currentUuid = UUID(mostSigBits, leastSigBits)
|
||||
context.messages.info("current UUID of $executable: $currentUuid")
|
||||
buffer.clear()
|
||||
val newUuid = customizer.getDistributionUUID(context, currentUuid)
|
||||
buffer.putLong(newUuid.mostSignificantBits)
|
||||
buffer.putLong(newUuid.leastSignificantBits)
|
||||
buffer.flip()
|
||||
if (context.isMacCodeSignEnabled || canBeSignedLocally) {
|
||||
channel.position(channel.position() - cmdBodySize)
|
||||
channel.write(buffer)
|
||||
context.messages.info("new UUID of $executable: $newUuid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user