Skip to content

Commit

Permalink
Cleanup fix-macho32 script
Browse files Browse the repository at this point in the history
Fix resulting binary being corrupt due to improper file close
  • Loading branch information
Goldfish64 committed Dec 16, 2023
1 parent e8dce89 commit 8f042c1
Showing 1 changed file with 64 additions and 41 deletions.
105 changes: 64 additions & 41 deletions scripts/fix-macho32
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,55 @@ from macholib.mach_o import (
LC_DATA_IN_CODE,
)

#
# Binary specified in arguments.
#
input_path = Path(argv[1].strip())

# Extract i386 slice from FAT binary if needed
#
# Check if we even have a 32-bit binary or 32-bit slice.
#
result = subprocess.run(["lipo", input_path, "-info"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if "i386" not in result.stdout:
print("Binary is not 32-bit, nothing to do")
sys.exit(0)


#
# Extract i386 slice from FAT binary if needed.
#
print("Processing {}".format(input_path))
if "Non-fat" not in result.stdout:
print("Binary is fat, extracting 32-bit slice")
is_fat_binary = True
print("Extracting 32-bit slice from fat binary")
slice_temp_file, slice_path = tempfile.mkstemp()
slice_path = Path(slice_path)
os.close(slice_temp_file)
result = subprocess.run(["lipo", input_path, "-thin", "i386", "-output", slice_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)

# Initialize parser
original_file = BytesIO(slice_path.read_bytes())
machFile = macholib.MachO.MachO(slice_path)
subprocess.run(["lipo", input_path, "-thin", "i386", "-output", slice_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)

else:
print("Binary is pure 32-bit")
is_fat_binary = False
original_file = BytesIO(input_path.read_bytes())
machFile = macholib.MachO.MachO(input_path)
slice_path = input_path

# Strip min version command
#
# Read Mach-O binary.
#
original_file = BytesIO(slice_path.read_bytes())
machFile = macholib.MachO.MachO(slice_path)

#
# Strip LC_VERSION_MIN_MACOSX command.
#
for cmd in machFile.headers[0].commands:
if (cmd[0].cmd == LC_VERSION_MIN_MACOSX):
machFile.headers[0].changedHeaderSizeBy(-cmd[0].cmdsize)
machFile.headers[0].commands.remove(cmd)
machFile.headers[0].header.ncmds -= 1
print("Removed LC_VERSION_MIN_MACOSX")

# Strip data-in-code command if zero
#
# Strip LC_DATA_IN_CODE command if zero.
#
for cmd in machFile.headers[0].commands:
if (cmd[0].cmd == LC_DATA_IN_CODE):
if (cmd[1].datasize == 0):
Expand All @@ -58,50 +73,58 @@ for cmd in machFile.headers[0].commands:
print("Removed LC_DATA_IN_CODE")
else:
print("LC_DATA_IN_CODE data size is non-zero!")
if (is_fat_binary):
slice_path.unlink()
sys.exit(-1)

# Align symbol table
#
# Align symbol table offset.
#
symCmd = machFile.headers[0].getSymbolTableCommand()

oldSymOff = symCmd.symoff
newSymOff = (oldSymOff + 3) & ~(3)
print("Old symbol table {} new symbol table {}".format(oldSymOff, newSymOff))
print("Old symbol table offset at {}, new symbol table will be at {}".format(oldSymOff, newSymOff))
symOffDelta = newSymOff - oldSymOff

# Align string table
#
# Align string table offset.
#
oldStrOff = symCmd.stroff
newStrOff = ((oldStrOff + symOffDelta) + 3) & ~(3)
print("Old string table {} new string table {}".format(oldStrOff, newStrOff))
print("Old string table offset at {}, new string table will be at {}".format(oldStrOff, newStrOff))
strOffDelta = newStrOff - (oldStrOff + symOffDelta)

# Write header
#
# Write new offsets to symbol table command.
#
symCmd.symoff = newSymOff
symCmd.stroff = newStrOff

if is_fat_binary:
new_file = slice_path.open("wb")
else:
new_file = input_path.open("wb")

machFile.headers[0].write(new_file) # Write header to file
original_file.seek(new_file.tell()) # Seek past header

# Copy rest of file and pad accordingly
if oldSymOff > oldStrOff:
new_file.write(original_file.read(oldStrOff - original_file.tell()))
new_file.write(b"\0" * strOffDelta)
new_file.write(original_file.read(oldSymOff - original_file.tell()))
new_file.write(b"\0" * symOffDelta)
new_file.write(original_file.read())
else:
new_file.write(original_file.read(oldSymOff - original_file.tell()))
new_file.write(b"\0" * symOffDelta)
new_file.write(original_file.read(oldStrOff - original_file.tell()))
new_file.write(b"\0" * strOffDelta)
new_file.write(original_file.read())

print("Symbol table aligned for " + input_path.name)

#
# Write Mach-O header to new file.
#
with open(slice_path, "wb") as new_file:
machFile.headers[0].write(new_file)
original_file.seek(new_file.tell())

#
# Copy rest of file and pad accordingly.
#
if oldSymOff > oldStrOff:
new_file.write(original_file.read(oldStrOff - original_file.tell()))
new_file.write(b"\0" * strOffDelta)
new_file.write(original_file.read(oldSymOff - original_file.tell()))
new_file.write(b"\0" * symOffDelta)
new_file.write(original_file.read())
else:
new_file.write(original_file.read(oldSymOff - original_file.tell()))
new_file.write(b"\0" * symOffDelta)
new_file.write(original_file.read(oldStrOff - original_file.tell()))
new_file.write(b"\0" * strOffDelta)
new_file.write(original_file.read())

print("Wrote new binary to {}".format(slice_path))
if is_fat_binary:
print("Replacing 32-bit slice in fat binary")
result = subprocess.run(["lipo", input_path, "-replace", "i386", slice_path, "-output", input_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
Expand Down

0 comments on commit 8f042c1

Please sign in to comment.