Safely edit ELF headers with x86_64-w64-mingw32-elfedit
You will change one ELF header field in a disposable copy, then prove the result with readelf. The same method applies to machine type, file type, OSABI, ABI version and the supported x86 program-property bits. x86_64-w64-mingw32ucrt-elfedit is an alias for the same installed tool.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need the binutils-mingw-w64-x86-64 package, a readable ELF file and enough free space for a copy. The examples are ordinary user commands. No sudo is needed unless the file or its directory is not accessible to your account.
Safety boundary
elfedit edits files in place. Work on a copy until you have checked the result, and keep the original available for recovery.
1. Check the installed command and version
Start by confirming which binary is being run and which package supplied it. This guide was tested with Binutils 2.41.90.20240122, from package version 2.41.90.20240122-1ubuntu1+11.4. Option details can differ in other releases.
$ command -v x86_64-w64-mingw32-elfedit
/usr/bin/x86_64-w64-mingw32-elfedit
$ x86_64-w64-mingw32-elfedit --version
GNU elfedit (GNU Binutils) 2.41.90.20240122
$ dpkg-query -W -f='${Package} ${Version}\n' binutils-mingw-w64-x86-64
binutils-mingw-w64-x86-64 2.41.90.20240122-1ubuntu1+11.4
The companion name is worth checking when a script comes from a UCRT toolchain:
$ command -v x86_64-w64-mingw32ucrt-elfedit
/usr/bin/x86_64-w64-mingw32ucrt-elfedit
If that second path is absent, use the canonical command shown in the first example. The installed manpages document both names with the same options.
2. Inspect the original without changing it
Choose a real ELF file and record the fields you intend to change. Replace /path/to/input.elf with your own path. readelf -h only reads the file.
$ readelf -h /path/to/input.elf
...
For a quick check, these lines expose the useful header values:
$ readelf -h /path/to/input.elf | sed -n '/Class:/p;/Machine:/p;/Type:/p;/OS\/ABI:/p;/ABI Version:/p'
Class: ELF64
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
Your output may use different values. Do not copy the sample values as assumptions about your file. The input filters in the next step are optional, but they are a useful guard when a script should edit only a known class of ELF file.
3. Make a protected working copy
Copy the file to a new name before editing. The destination must be on a filesystem where you can write.
$ cp --preserve=all /path/to/input.elf /path/to/input.elf.working
$ readelf -h /path/to/input.elf.working | sed -n '/OS\/ABI:/p'
OS/ABI: UNIX - System V
That original file is your recovery point. If the edited copy is wrong, discard the working copy and make a fresh one from the original. Do not replace a deployed binary merely because elfedit returned status zero; inspect the header and test the consumer first.
4. Change one header field
Every successful edit needs at least one output option. This example changes the OSABI in an x86-64 dynamically linked ELF file to FreeBSD, while the input options restrict the match to that expected machine and file type:
$ x86_64-w64-mingw32-elfedit \
--input-mach=x86-64 \
--input-type=dyn \
--output-osabi=FreeBSD \
/path/to/input.elf.working
The command prints nothing on success. Verify the changed copy:
$ readelf -h /path/to/input.elf.working | sed -n '/Machine:/p;/Type:/p;/OS\/ABI:/p'
Type: DYN (Position-Independent Executable file)
OS/ABI: UNIX - FreeBSD
Machine: Advanced Micro Devices X86-64
The available header outputs are --output-mach, --output-type, --output-osabi and --output-abiversion. Supported machine names include i386 and x86-64; file types are rel, exec and dyn; ABI version must be between 0 and 255. Input options match a property and do not change it.
5. Treat feature-bit edits as a separate decision
The command can enable or disable the x86 program-property features ibt, shstk, lam_u48 and lam_u57. These options apply to exec or dyn files whose machine is i386 or x86-64, and the local manpage says they require host mmap support.
$ x86_64-w64-mingw32-elfedit \
--input-mach=x86-64 \
--enable-x86-feature=ibt \
/path/to/input.elf.working
Do not run that example just to see what happens. A feature bit can affect loader or hardware-policy decisions, and the command still edits the file in place. Use it only when the target format, runtime and deployment plan explicitly require the change. To clear a bit on a deliberate test copy, use the corresponding --disable-x86-feature option.
6. Handle failures without guessing
A missing output option is a command-line error. An invalid feature is also rejected; for example, the installed binary reports Unknown x86 feature: not-a-feature and returns status 1. Treat any non-zero status as a failed edit and inspect the working file before using it.
$ x86_64-w64-mingw32-elfedit \
--input-mach=x86-64 \
--input-type=exec \
--output-osabi=GNU \
/path/to/input.elf.working
$ printf 'elfedit status: %s\n' "$?"
elfedit status: 0
A zero status means the requested operation completed. It does not certify that the resulting ELF is suitable for its loader, operating system or security policy. If an input filter does not match, check the actual header with readelf -h and adjust the filter deliberately. Do not remove filters merely to silence a failed batch job.
7. Keep or undo the result deliberately
After verification, compare the original and working files and test the consumer that will read the ELF. If the change is not wanted, restore by starting again from the original:
$ cmp -s /path/to/input.elf /path/to/input.elf.working; printf 'cmp status: %s\n' "$?"
cmp status: 1
$ cp --preserve=all /path/to/input.elf /path/to/input.elf.working
The non-zero cmp result is expected after a successful header edit. Copying the untouched original over the working copy undoes the test without modifying the original. If you have already edited the only copy, recovery requires another known-good copy or a fresh rebuild. There is no transaction or undo database in elfedit.
Done means
- The installed command and Binutils version were checked.
- The original ELF was inspected and kept unchanged.
- The edit was made on a separate working copy with deliberate input filters.
readelf -hconfirms the requested header value.- Feature bits were changed only for a documented, tested requirement.
- A known-good original remains available for recovery.