Home / Alt manpages / llvm-objcopy-18(1)

  • llvm-objcopy-18(1)
  • User command
  • linux

Copy, Inspect and Strip ELF Files with llvm-objcopy-18

Run llvm-objcopy-18 with only an input path and no output, and it edits that file in place, a default that has surprised people mid-build. This guide keeps you clear of that trap while making a semantic copy of an object file, stripping debug sections into a separate output, extracting one section, and producing a raw binary. The original input stays untouched throughout. The installed command is LLVM 18.1.3 from the llvm-18 package.

Allow about 15 minutes. You need a shell, a readable ELF object or executable, and enough free space for each output. None of this needs elevated privileges when the files sit in a directory you can already write to. Do not reach for sudo merely because the input is an executable: use it only when your chosen input or destination is genuinely protected, and check the resulting ownership afterwards.

1. Check the installed command and input

Start by confirming which executable will run and record its version:

$ command -v llvm-objcopy-18
$ llvm-objcopy-18 --version
llvm-objcopy, compatible with GNU objcopy
Ubuntu LLVM version 18.1.3

Pick an input you can restore or replace. llvm-objcopy accepts object files, executables and archives, and it can infer the format for you. This inspection step is read-only:

$ file /path/to/input.elf
$ llvm-readelf-18 -S /path/to/input.elf

Note the section names you plan to use, such as .text or .debug_info. Do not copy a section name from a different binary and assume it exists here, a missing section is an error, not a harmless no-op.

Checkpoint

The input path is readable, the format is known, and the section listing contains the names the next command will use.

2. Make a separate semantic copy

The basic form is an input followed by an output:

$ llvm-objcopy-18 /path/to/input.elf /path/to/work/input-copy.elf
$ file /path/to/work/input-copy.elf

This is a semantic object-file copy, not a byte-for-byte clone. Archive headers and other metadata can be normalised, so do not use cmp as your success test. Check that the output is the expected kind of object and inspect it with the tool that will actually consume it. With no output path at all, remember, the manual says the input gets modified in place: treat that form as destructive and avoid it in scripts unless in-place replacement is exactly what you want.

If the destination already exists, decide deliberately whether replacement is acceptable. A safer pattern writes a new name, inspects it, then moves it into place only after a successful check:

$ llvm-objcopy-18 /path/to/input.elf /path/to/work/input.elf.new
$ llvm-readelf-18 -h /path/to/work/input.elf.new
$ mv -- /path/to/work/input.elf.new /path/to/work/input.elf

That final mv changes state and can replace an existing destination. Keep the original until the replacement has passed your application-specific test. If the copy fails, remove only the .new file, and only after checking it really is the incomplete output.

3. Remove debug data into another file

Use --strip-debug, also written -g, to remove debug sections while keeping the normal executable or object contents:

$ llvm-objcopy-18 --strip-debug \
    /path/to/input.elf /path/to/work/input-nodebug.elf
$ llvm-readelf-18 -S /path/to/work/input-nodebug.elf

Compare the section lists before and after. Debug information may already be absent from a file that was stripped earlier, so a successful command is no guarantee of a smaller output. If you need to remove symbols too, --strip-all is far more aggressive: on ELF it removes all symbols and non-allocatable sections outside segments, subject to the exceptions this installed manpage documents. That can break debugging and tooling, so treat it as a separate, deliberate decision.

Warning

Never test a stripping command first on the only copy of a production binary. Keep a known-good input and test the resulting file with the loader or application before replacing anything.

4. Dump one section for inspection

--dump-section writes a named section to a separate file. The dump destination is independent of the normal output argument:

$ llvm-objcopy-18 --dump-section \
    .text=/path/to/work/text.bin /path/to/input.elf
$ wc -c /path/to/work/text.bin

On a typical x86-64 executable, this produces a non-empty binary file. Its contents are not text and should never be opened in an editor. Use a byte-oriented tool such as od or xxd for a short preview instead:

$ od -Ax -tx1 -N 32 /path/to/work/text.bin

The command still runs its ordinary copying and editing operation if you also supply an output file, but keeping the dump as a focused, read-only step makes scripts far easier to review. If the section is not present, go back to the section listing rather than guessing at a replacement name.

5. Produce a raw binary deliberately

Set the output target to binary when another tool needs the input's loadable memory image with no ELF headers, symbols or relocation information:

$ llvm-objcopy-18 --output-target=binary \
    /path/to/input.elf /path/to/work/image.bin
$ file /path/to/work/image.bin
$ wc -c /path/to/work/image.bin

Expect file to report the output as generic data. The binary starts at the address of the first loadable section, and symbols and relocation information are simply gone, so this is not a general route to a runnable Linux executable. Firmware, bootloader and emulator workflows often need an explicit load address on top; check the target system's documented layout before reaching for options such as --pad-to or --gap-fill.

The inverse works too. With --input-target=binary, the file gets embedded in an ELF relocatable object and receives generated _binary_... start, end and size symbols based on the input path. Verify the generated object with llvm-readelf-18 -s before you link it into anything.

6. Diagnose failures without changing the input

A non-zero exit status means the command reported an error. Capture it immediately when scripting:

if llvm-objcopy-18 --strip-debug "$input" "$output"; then
    llvm-readelf-18 -h "$output"
else
    status=$?
    printf 'llvm-objcopy failed with status %s\n' "$status" >&2
    exit "$status"
fi
  • Unreadable input, or a destination you cannot write. Check permissions before anything else.
  • A section name that does not exist. Go back to the section listing rather than guessing.
  • An option belonging to another object format. ELF-specific options can error out or be silently ignored for non-ELF objects.
  • A format mismatch. Inspect the file first, and only use --input-target or --output-target once you have verified the required format name. The installed manpage records a known issue here: some values other than binary and ihex given to --input-target or --target may be ignored while the tool guesses the input format instead.

Keep diagnostics separate from binary output. Use - as the input and llvm-objcopy-18 reads standard input; use - as the output and it writes standard output. Redirecting binary output straight into a terminal is noisy and can corrupt a pipeline, so send it to a file or a program that actually expects bytes.

Done means

  • Version confirmed. The version output identifies Ubuntu LLVM 18.1.3.
  • Original preserved. The original input remains available and was never modified in place.
  • Outputs inspected. Each output was checked with file or llvm-readelf-18.
  • Operations kept deliberate. Stripping, section dumping and raw-binary conversion were each used only for their stated purpose.
  • Reversible throughout. Any replacement stays reversible because the known-good input was kept.