Skip to content

Commit 72ab5b6

Browse files
committed
Fix copying c extensions on darwin
1 parent 9497899 commit 72ab5b6

File tree

6 files changed

+93
-57
lines changed

6 files changed

+93
-57
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/ElfFile.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,9 @@ public void changeOrAddDependency(String oldName, String newName) throws IOExcep
109109
public byte[] write() throws IOException {
110110
return tempfile.readAllBytes();
111111
}
112+
113+
@Override
114+
protected void fixup(TruffleFile copy) {
115+
// Nothing to do
116+
}
112117
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/MachODylibCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public void setName(String name) {
114114
int nameLen = nameBytes.length;
115115
int paddedLen = (nameLen & ~(Long.BYTES - 1)) + Long.BYTES;
116116
byte[] paddedNameBytes = new byte[paddedLen];
117-
System.arraycopy(nameBytes, 0, paddedNameBytes, 0, paddedLen);
117+
System.arraycopy(nameBytes, 0, paddedNameBytes, 0, nameLen);
118118
this.paddedName = paddedNameBytes;
119119
this.cmdSize = MachODylibCommand.SIZE + paddedLen;
120120
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/MachOFile.java

Lines changed: 77 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,18 @@
4646
import java.util.List;
4747

4848
import com.oracle.graal.python.runtime.PythonContext;
49+
import com.oracle.truffle.api.TruffleFile;
50+
import com.oracle.truffle.api.TruffleLanguage.Env;
4951

5052
final class MachOFile extends SharedObject {
53+
private final PythonContext context;
5154
private final ByteBuffer buffer;
5255
private final MachOHeader mh;
5356
private final List<MachOLoadCommand> loadCommands;
5457
private int emptySpace;
5558

5659
MachOFile(byte[] f, PythonContext context) throws IOException {
60+
this.context = context;
5761
this.buffer = ByteBuffer.wrap(f);
5862
this.mh = MachOHeader.read(buffer);
5963
this.loadCommands = new ArrayList<>();
@@ -68,93 +72,111 @@ final class MachOFile extends SharedObject {
6872
this.emptySpace = zeroBytes;
6973
}
7074

71-
public void removeCodeSignature() {
75+
private void removeCommand(MachOLoadCommand cmd) {
76+
loadCommands.remove(cmd);
77+
mh.nCmds -= 1;
78+
mh.sizeOfCmds -= cmd.cmdSize;
79+
emptySpace += cmd.cmdSize;
80+
}
81+
82+
private void addCommand(MachODylibCommand cmd) throws IOException {
83+
if (cmd.cmdSize > emptySpace) {
84+
throw new IOException(String.format("Not enough empty space add new cmd with string %s.", cmd.getName()));
85+
}
86+
var newBuffer = ByteBuffer.allocate(cmd.cmdSize);
87+
newBuffer.order(buffer.order());
88+
cmd.put(newBuffer);
89+
newBuffer.position(0);
90+
loadCommands.add(MachOLoadCommand.get(newBuffer));
91+
mh.nCmds += 1;
92+
mh.sizeOfCmds += cmd.cmdSize;
93+
emptySpace -= cmd.cmdSize;
94+
}
95+
96+
private void removeCodeSignature() {
7297
for (int i = 0; i < loadCommands.size(); ++i) {
7398
var cmd = loadCommands.get(i);
7499
if (cmd.cmd == MachOLoadCommand.LC_CODE_SIGNATURE) {
75-
loadCommands.remove(i);
76-
emptySpace += cmd.cmdSize;
77-
break;
100+
removeCommand(cmd);
101+
LOGGER.fine(() -> String.format("Removing code LC_CODE_SIGNATURE. New empty space is %d", emptySpace));
78102
}
79103
}
80104
}
81105

82-
@Override
83-
public void setId(String newId) throws IOException {
84-
removeCodeSignature();
85-
MachOLoadCommand oldIdCommand = null;
106+
private void removeId() {
86107
for (int i = 0; i < loadCommands.size(); ++i) {
87108
var cmd = loadCommands.get(i);
88109
if (cmd.cmd == MachODylibCommand.LC_ID_DYLIB) {
89-
oldIdCommand = cmd;
90-
break;
110+
removeCommand(cmd);
91111
}
92112
}
93-
94-
MachODylibCommand newCmd;
95-
if (oldIdCommand != null) {
96-
newCmd = MachODylibCommand.get(oldIdCommand.content);
97-
} else {
98-
newCmd = new MachODylibCommand(MachODylibCommand.LC_ID_DYLIB, MachODylibCommand.SIZE, new byte[0], MachODylibCommand.SIZE, 0, 0, 0);
99-
}
100-
newCmd.setName(newId);
101-
102-
if ((oldIdCommand != null && newCmd.cmdSize - oldIdCommand.cmdSize > emptySpace) || newCmd.cmdSize > emptySpace) {
103-
throw new IOException("Not enough empty space to change ID");
104-
}
105-
106-
loadCommands.remove(oldIdCommand);
107-
108-
var newBuffer = ByteBuffer.allocate(newCmd.cmdSize);
109-
newBuffer.order(buffer.order());
110-
newCmd.put(newBuffer);
111-
loadCommands.add(MachOLoadCommand.get(newBuffer));
112-
113-
if (oldIdCommand != null) {
114-
mh.sizeOfCmds -= oldIdCommand.cmdSize;
115-
emptySpace += oldIdCommand.cmdSize;
116-
}
117-
emptySpace -= newCmd.cmdSize;
118-
mh.sizeOfCmds += newCmd.cmdSize;
119113
}
120114

121-
@Override
122-
public void changeOrAddDependency(String oldName, String newName) throws IOException {
123-
removeCodeSignature();
124-
115+
private void removeLoad(String oldName) {
125116
for (int i = 0; i < loadCommands.size(); ++i) {
126117
var cmd = loadCommands.get(i);
127118
if (cmd.cmd == MachODylibCommand.LC_LOAD_DYLIB) {
128119
var loadCmd = MachODylibCommand.get(cmd.content);
129120
if (loadCmd.getName().equals(oldName)) {
130-
loadCommands.remove(i);
131-
emptySpace += cmd.cmdSize;
132-
break;
121+
removeCommand(cmd);
122+
LOGGER.fine(() -> String.format("Removing LC_LOAD_DYLIB %s. New empty space is %d.", oldName, emptySpace));
133123
}
134124
}
135125
}
126+
}
136127

137-
var newCmd = new MachODylibCommand(MachODylibCommand.LC_LOAD_DYLIB, MachODylibCommand.SIZE, new byte[0], MachODylibCommand.SIZE, 0, 0, 0);
138-
newCmd.setName(newName);
128+
@Override
129+
public void setId(String newId) throws IOException {
130+
removeCodeSignature();
131+
removeId();
139132

140-
if (newCmd.cmdSize > emptySpace) {
141-
throw new IOException("Not enough empty space to add dependency");
142-
}
133+
var newCmd = new MachODylibCommand(MachODylibCommand.LC_ID_DYLIB, MachODylibCommand.SIZE, new byte[0], MachODylibCommand.SIZE, 0, 0, 0);
134+
newCmd.setName(newId);
135+
addCommand(newCmd);
143136

144-
var newBuffer = ByteBuffer.allocate(newCmd.cmdSize);
145-
newBuffer.order(buffer.order());
146-
newCmd.put(newBuffer);
147-
loadCommands.add(MachOLoadCommand.get(newBuffer));
137+
LOGGER.fine(() -> String.format("Added LC_ID_DYLIB %s. New empty space is %d.", newId, emptySpace));
138+
}
148139

149-
mh.nCmds += 1;
150-
mh.sizeOfCmds += newCmd.cmdSize;
151-
emptySpace -= newCmd.cmdSize;
140+
@Override
141+
public void changeOrAddDependency(String oldName, String newName) throws IOException {
142+
removeCodeSignature();
143+
removeLoad(oldName);
144+
145+
var newCmd = new MachODylibCommand(MachODylibCommand.LC_LOAD_DYLIB, MachODylibCommand.SIZE, new byte[0], MachODylibCommand.SIZE, 0, 0, 0);
146+
newCmd.setName(newName);
147+
addCommand(newCmd);
148+
149+
LOGGER.fine(() -> String.format("Added LC_LOAD_DYLIB %s. New empty space is %d.", newName, emptySpace));
152150
}
153151

154152
@Override
155153
public byte[] write() {
156154
buffer.position(0);
157155
mh.put(buffer);
156+
for (var cmd : loadCommands) {
157+
cmd.put(buffer);
158+
}
158159
return buffer.array();
159160
}
161+
162+
private String getCodesign() {
163+
Env env = context.getEnv();
164+
var path = env.getEnvironment().getOrDefault("PATH", "").split(env.getPathSeparator());
165+
var i = 0;
166+
TruffleFile codesign;
167+
do {
168+
codesign = env.getPublicTruffleFile(path[i++]).resolve("codesign");
169+
} while (!codesign.isExecutable() && i < path.length);
170+
return codesign.toString();
171+
}
172+
173+
@Override
174+
protected void fixup(TruffleFile copy) throws IOException, InterruptedException {
175+
var pb = newProcessBuilder(context);
176+
pb.command(getCodesign(), "--force", "--sign", "-", copy.getAbsoluteFile().getPath());
177+
var proc = pb.start();
178+
if (proc.waitFor() != 0) {
179+
throw new IOException("Failed to run `codesign` command. Make sure you have it on your PATH.");
180+
}
181+
}
160182
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/NativeLibraryLocator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ private static void replicate(TruffleFile original, TruffleFile copy, PythonCont
213213
try (var os = copy.newOutputStream(StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
214214
os.write(o.write());
215215
}
216+
o.fixup(copy);
216217
}
217218

218219
private static void walk(TruffleFile dir, String suffix, String capiOriginalName, PythonContext context, int capiSlot, BiFunction<TruffleFile, String, TruffleFile> f)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/PEFile.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,9 @@ public void changeOrAddDependency(String oldName, String newName) throws IOExcep
8989
public byte[] write() throws IOException {
9090
return tempfile.readAllBytes();
9191
}
92+
93+
@Override
94+
protected void fixup(TruffleFile copy) {
95+
// TODO: Maybe this should be signed again?
96+
}
9297
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/SharedObject.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,9 @@ static SharedObject open(TruffleFile file, PythonContext context) throws IOExcep
7070
}
7171
}
7272

73+
protected static final TruffleLogger LOGGER = PythonLanguage.getLogger("NativeLibraryLocator");
74+
7375
protected static final class LoggingOutputStream extends OutputStream {
74-
private static final TruffleLogger LOGGER = PythonLanguage.getLogger("NativeLibraryLocator");
7576
private final StringBuilder sb = new StringBuilder();
7677

7778
@Override
@@ -97,4 +98,6 @@ protected static TruffleProcessBuilder newProcessBuilder(PythonContext context)
9798
pb.redirectError(pb.createRedirectToStream(new LoggingOutputStream()));
9899
return pb;
99100
}
101+
102+
protected abstract void fixup(TruffleFile copy) throws IOException, InterruptedException;
100103
}

0 commit comments

Comments
 (0)