OBJCOPY(1)

πŸ“¦ version 2.45, in ubuntu questing

πŸŽ€ objcopy πŸŽ€

objcopy is part of the GNU Development Tools crew, and it is iconic for copying and translating object files πŸ’…βœ¨

πŸ“– NAME

objcopy - copy and translate object files πŸ¦‹

🌸 SYNOPSIS

objcopy [-F bfdname|--target=bfdname]
        [-I bfdname|--input-target=bfdname]
        [-O bfdname|--output-target=bfdname]
        [-B bfdarch|--binary-architecture=bfdarch]
        [-S|--strip-all]
        [-g|--strip-debug]
        [--strip-unneeded]
        [-K symbolname|--keep-symbol=symbolname]
        [--keep-file-symbols]
        [--keep-section-symbols]
        [-N symbolname|--strip-symbol=symbolname]
        [--strip-unneeded-symbol=symbolname]
        [-G symbolname|--keep-global-symbol=symbolname]
        [--localize-hidden]
        [-L symbolname|--localize-symbol=symbolname]
        [--globalize-symbol=symbolname]
        [--globalize-symbols=filename]
        [-W symbolname|--weaken-symbol=symbolname]
        [-w|--wildcard]
        [-x|--discard-all]
        [-X|--discard-locals]
        [-b byte|--byte=byte]
        [-i [breadth]|--interleave[=breadth]]
        [--interleave-width=width]
        [-j sectionpattern|--only-section=sectionpattern]
        [-R sectionpattern|--remove-section=sectionpattern]
        [--keep-section=sectionpattern]
        [--remove-relocations=sectionpattern]
        [--strip-section-headers]
        [-p|--preserve-dates]
        [-D|--enable-deterministic-archives]
        [-U|--disable-deterministic-archives]
        [--debugging]
        [--gap-fill=val]
        [--pad-to=address]
        [--set-start=val]
        [--adjust-start=incr]
        [--change-addresses=incr]
        [--change-section-address sectionpattern{=,+,-}val]
        [--change-section-lma sectionpattern{=,+,-}val]
        [--change-section-vma sectionpattern{=,+,-}val]
        [--change-warnings] [--no-change-warnings]
        [--set-section-flags sectionpattern=flags]
        [--set-section-alignment sectionpattern=align]
        [--add-section sectionname=filename]
        [--dump-section sectionname=filename]
        [--update-section sectionname=filename]
        [--rename-section oldname=newname[,flags]]
        [--long-section-names {enable,disable,keep}]
        [--change-leading-char] [--remove-leading-char]
        [--reverse-bytes=num]
        [--srec-len=ival] [--srec-forceS3]
        [--redefine-sym old=new]
        [--redefine-syms=filename]
        [--weaken]
        [--keep-symbols=filename]
        [--strip-symbols=filename]
        [--strip-unneeded-symbols=filename]
        [--keep-global-symbols=filename]
        [--localize-symbols=filename]
        [--weaken-symbols=filename]
        [--add-symbol name=[section:]value[,flags]]
        [--alt-machine-code=index]
        [--prefix-symbols=string]
        [--prefix-sections=string]
        [--prefix-alloc-sections=string]
        [--add-gnu-debuglink=path-to-file]
        [--only-keep-debug]
        [--strip-dwo]
        [--extract-dwo]
        [--extract-symbol]
        [--writable-text]
        [--readonly-text]
        [--pure]
        [--impure]
        [--file-alignment=num]
        [--heap=reserve[,commit]]
        [--image-base=address]
        [--section-alignment=num]
        [--stack=reserve[,commit]]
        [--subsystem=which:major.minor]
        [--compress-debug-sections]
        [--decompress-debug-sections]
        [--elf-stt-common=val]
        [--merge-notes]
        [--no-merge-notes]
        [--verilog-data-width=val]
        [-v|--verbose]
        [-V|--version]
        [--help] [--info]
        infile [outfile]

πŸ’… DESCRIPTION

okay so, the GNU objcopy utility copies the contents of one object file into another 🫢 It uses the GNU BFD Library to read and write the object files, and the destination file can be in a different format from the source. How it behaves is controlled entirely by your command-line options. Also, objcopy should be able to copy a fully linked file between any two formats, but copying a relocatable object file between any two formats may not work as expected πŸ‘€

objcopy makes temporary files to do its translations and deletes them afterward ✨ It does all of its translation work with BFD, so it can access every format described in BFD and is able to recognize most formats without you having to tell it explicitly. main character energy πŸ‘‘

You can use objcopy to generate S-records by setting an output target of srec (for example, -O srec) 🧚

You can also generate a raw binary file by setting an output target of binary (for example, -O binary) πŸ’œ When objcopy makes a raw binary file, it basically produces a memory dump of the contents of the input object file. All symbols and relocation information get discarded. The memory dump starts at the load address of the lowest section copied into the output file.

When you're generating an S-record or a raw binary file, it can be super helpful to use -S to remove sections holding debugging information. In some cases -R is useful for removing sections that hold info the binary file doesn't need πŸ™ˆ
Note---objcopy is not able to change the endianness of its input files. If the input format has an endianness (some formats don't), objcopy can only copy the inputs into file formats with the same endianness or with no endianness at all (for example, srec). (However, see the --reverse-bytes option.) πŸ’€

✨ OPTIONS ✨

  • infile, outfile β€” These are the input and output files, respectively. If you don't specify outfile, objcopy makes a temporary file and destructively renames the result to the name of infile 🫣
  • -I bfdname, --input-target=bfdname β€” Treat the source file's object format as bfdname instead of trying to figure it out on its own πŸ”
  • -O bfdname, --output-target=bfdname β€” Write the output file in the bfdname object format πŸ“
  • -F bfdname, --target=bfdname β€” Use bfdname as the object format for both the input and output file. Basically, the data just gets transferred from source to destination with no translation πŸ¦‹
  • -B bfdarch, --binary-architecture=bfdarch β€” This is handy when you're turning an architecture-less input file into an object file. In that case, the output architecture can be set to bfdarch. This option gets ignored if the input file has a known bfdarch. You can reach this binary data from inside a program by referencing the special symbols that get created during the conversion: _binary_objfile_start, _binary_objfile_end and _binary_objfile_size. For example, you can turn a picture file into an object file and then access it in your code through those symbols πŸ–ΌοΈβœ¨
  • -j sectionpattern, --only-section=sectionpattern β€” Copy only the sections you name from the input file to the output file. You can give this option more than once. Be careful, because using it inappropriately may make the output file unusable. Wildcard characters are accepted in sectionpattern 🎯

If the first character of sectionpattern is an exclamation point (!), then matching sections won't be copied, even if an earlier --only-section on the same command line would otherwise copy them. For example:

--only-section=.text.* --only-section=!.text.foo

This copies all sections matching .text.* but not the section .text.foo πŸ’–

  • -R sectionpattern, --remove-section=sectionpattern β€” Remove any section matching sectionpattern from the output file. You can give this option more than once. Again, using it inappropriately may make the output file unusable. Wildcard characters are accepted in sectionpattern. Using -j and -R together gives undefined behaviour, so maybe don't πŸ™…β€β™€οΈ

If the first character of sectionpattern is an exclamation point (!), then matching sections won't be removed, even if an earlier --remove-section on the same command line would otherwise remove them. For example:

--remove-section=.text.* --remove-section=!.text.foo

This removes all sections matching the pattern .text.*, but it won't remove the section .text.foo 🌈

  • --keep-section=sectionpattern β€” When removing sections from the output file, keep the sections that match sectionpattern 🫢
  • --remove-relocations=sectionpattern β€” Remove non-dynamic relocations from the output file for any section matching sectionpattern. You can give this option more than once. Be warned: using it inappropriately may make the output file unusable, and trying to remove a dynamic relocation section such as .rela.plt from an executable or shared library with --remove-relocations=.plt won't work. Wildcard characters are accepted in sectionpattern. For example:
--remove-relocations=.text.*

This removes the relocations for all sections matching the pattern .text.* 🧚

If the first character of sectionpattern is an exclamation point (!), then matching sections won't have their relocations removed, even if an earlier --remove-relocations on the same command line would otherwise remove them. For example:

--remove-relocations=.text.* --remove-relocations=!.text.foo

This removes all relocations for sections matching .text.*, but it keeps the relocations for the section .text.foo πŸ’…

  • --strip-section-headers β€” Strips section headers. This option is specific to ELF files. It implies --strip-all and --merge-notes ✨
  • -S, --strip-all β€” Don't copy relocation and symbol information from the source file. This also deletes debug sections 🧹
  • -g, --strip-debug β€” Don't copy debugging symbols or sections from the source file πŸ›
  • --strip-unneeded β€” Remove every symbol that isn't needed for relocation processing, on top of the debugging symbols and sections that --strip-debug would strip πŸ—‘οΈ
  • -K symbolname, --keep-symbol=symbolname β€” When stripping symbols, keep the symbol symbolname even if it would normally get stripped. You can give this option more than once πŸ”’
  • -N symbolname, --strip-symbol=symbolname β€” Don't copy the symbol symbolname from the source file. You can give this option more than once 🚫
  • --strip-unneeded-symbol=symbolname β€” Don't copy the symbol symbolname from the source file unless a relocation needs it. You can give this option more than once πŸ₯Ί
  • -G symbolname, --keep-global-symbol=symbolname β€” okay so this one keeps ONLY symbolname global πŸ”’βœ¨ Every other symbol gets bumped to local so nobody outside the file can see it. You can use it more than once. ⚠️ It can't be combined with --globalize-symbol or --globalize-symbols, so don't mix them up, bestie πŸ™ˆ
  • --localize-hidden β€” in an ELF object, every symbol with hidden or internal visibility gets marked as local 🫣 This one stacks on top of the symbol-specific localization options, like -L.
  • -L symbolname, --localize-symbol=symbolname β€” turns a global or weak symbol called symbolname into a local one, so it's hidden from the outside world πŸ¦‹ You can repeat it. Heads up: unique symbols are NOT converted, so they stay as they are πŸ’€
  • -W symbolname, --weaken-symbol=symbolname β€” makes the symbol symbolname weak 🌸 Repeatable, obviously.
  • --globalize-symbol=symbolname β€” gives symbolname global scoping, so it's visible outside the file it's defined in 🌍 You can use it more than once. ⚠️ It can't be used with -G or --keep-global-symbol, same vibe as before πŸ’…
  • -w, --wildcard β€” lets you use regular expressions in the symbol names inside your other command line options 🧚 The question mark (?), asterisk (*), backslash (\) and square brackets ([]) operators work ANYWHERE in the name. If the very first character is an exclamation point (!), the switch flips for that symbol. Here's the example:
-w -W !foo -W fo*

This would make objcopy weaken all symbols that start with "fo" EXCEPT for the symbol "foo" πŸ₯Ίβœ¨

  • -x, --discard-all β€” doesn't copy any non-global symbols from the source file πŸ—‘οΈ
  • -X, --discard-locals β€” doesn't copy the compiler-generated local symbols πŸ€– These usually start with L or ...
  • -b byte, --byte=byte β€” if you turned on interleaving with --interleave, this starts the range of bytes to keep at the byteth byte 🎯 byte has to be somewhere from 0 up to breadth-1, where breadth is whatever you gave to --interleave.
  • -i [breadth], --interleave[=breadth] β€” only copies a range out of every breadth bytes 🧁 Header data is left alone. Pick the starting byte with --byte, and pick how wide the range is with --interleave-width.

This one is super useful for making files to program ROM 🧠 It's usually paired with an "srec" output target. Also, objcopy will complain if you forget --byte, so don't skip it!

The default interleave breadth is 4. So with --byte set to 0, objcopy would copy the first byte out of every four bytes from the input to the output πŸ“

  • --interleave-width=width β€” used together with --interleave, this copies width bytes at a time πŸ“ The starting point comes from --byte, and the size of the whole range comes from --interleave.

The default for this is 1. Also, width plus the --byte value must not go over the interleave breadth you set with --interleave, or it's not gonna work πŸ™…β€β™€οΈ

This is giving two 16-bit flashes sharing a 32-bit bus energy πŸ”₯ You make them by passing -b 0 -i 4 --interleave-width=2 and -b 2 -i 4 --interleave-width=2 to two separate objcopy commands. If the input was '12345678', the outputs would be '1256' and '3478' respectively πŸ’œ

  • -p, --preserve-dates β€” sets the access and modification dates of the output file to match the input file πŸ“…

This one also copies the date stored in a PE format file's header, UNLESS the SOURCE_DATE_EPOCH environment variable is defined. If it is, that variable gets used as the header date instead, read as the number of seconds since the Unix epoch ⏰

  • -D, --enable-deterministic-archives β€” operates in deterministic mode πŸ” When it copies archive members and writes the archive index, it uses zero for UIDs, GIDs and timestamps, and consistent file modes for all files. Very neat and tidy πŸŽ€

If binutils was configured with --enable-deterministic-archives, this mode is ON by default. You can turn it off with the -U option, below πŸ‘‡

  • -U, --disable-deterministic-archives β€” doesn't operate in deterministic mode πŸ™…β€β™€οΈ This is the opposite of -D: archive members and the archive index keep their actual UID, GID, timestamp and file mode values.

This is the default UNLESS binutils was configured with --enable-deterministic-archives 🫢

  • --debugging β€” converts debugging information, if it's possible πŸ› It's not on by default, because only certain debugging formats are supported and the conversion can take forever ⏳
  • --gap-fill val β€” fills the gaps between sections with val 🧩 This applies to the load address (LMA) of the sections. It works by making the lower-address section bigger, and filling that extra space with val.
  • --pad-to address β€” pads the output file up to the load address address 🍰 It does this by making the last section bigger. The extra space gets filled with whatever --gap-fill says (zero by default).
  • --set-start val β€” sets the start address (a.k.a. the entry address) of the new file to val πŸšͺ Not every object file format lets you set the start address, so heads up on that one.
  • --change-start incr, --adjust-start incr β€” changes the start address (a.k.a. the entry address) by adding incr βž• Same catch: not every object file format supports setting the start address.
  • --change-addresses incr, --adjust-vma incr β€” changes the VMA and LMA addresses of ALL sections, plus the start address, by adding incr πŸ“ˆ Some object file formats don't let section addresses change freely, so keep that in mind.
⚠️ This does NOT relocate the sections! If your program expects sections to load at a certain address, and you use this to move them somewhere else, the program might fail. Yikes πŸ’€
  • --change-section-address sectionpattern{=,+,-}val, --adjust-section-vma sectionpattern{=,+,-}val β€” sets or changes BOTH the VMA and LMA addresses of any section matching sectionpattern 🎯 With =, the section address gets set to val. Otherwise, val gets added to or subtracted from the section address. Check the notes under --change-addresses too πŸ‘€ If sectionpattern doesn't match any sections in the input file, you'll get a warning, UNLESS you use --no-change-warnings.
  • --change-section-lma sectionpattern{=,+,-}val β€” sets or changes the LMA address of any sections matching sectionpattern πŸ“₯ The LMA is where the section gets loaded into memory when the program loads. Usually that's the same as the VMA (the address it lives at while the program runs), but on some systems, especially ones where the program lives in ROM, they can be different 🧸 With =, the section address is set to val. Otherwise val is added or subtracted. See the comments under --change-addresses. If nothing matches sectionpattern, you get a warning, UNLESS --no-change-warnings is used.
  • --change-section-vma sectionpattern{=,+,-}val β€” sets or changes the VMA address of any section matching sectionpattern πŸƒβ€β™€οΈ The VMA is where the section will be sitting once the program starts running. Normally it's the same as the LMA (where it gets loaded), but again, ROM-based systems can have them differ πŸͺ„ With =, the section address is set to val. Otherwise val gets added or subtracted. See the comments under --change-addresses. If nothing matches, you get a warning, UNLESS --no-change-warnings is used.

Note - changing the VMA of sections in a fully linked binary can be DANGEROUS 😭 There might be code that expects those sections to still live at their old address.

  • --change-warnings, --adjust-warnings β€” if --change-section-address, --change-section-lma or --change-section-vma is used and the section pattern doesn't match any sections, you get a warning πŸ”” This is the default behaviour.
  • --no-change-warnings, --no-adjust-warnings β€” no warning gets issued if --change-section-address or --adjust-section-lma or --adjust-section-vma is used, even when the section pattern doesn't match anything πŸ”•
  • --set-section-flags sectionpattern=flags β€” sets the flags for any sections matching sectionpattern 🏷️ The flags argument is a comma separated string of flag names. The recognized names are alloc, contents, load, noload, readonly, code, data, rom, exclude, share, debug, and large.

You CAN set the contents flag on a section that doesn't have contents yet. But clearing the contents flag on a section that DOES have contents isn't meaningful, so just remove the section instead πŸ—‘οΈ Not every flag means something for every object file format. For example, the share flag only matters for COFF format files, not ELF ones. The ELF x86-64 specific flag large corresponds to SHF_X86_64_LARGE 🧠

  • --set-section-alignment sectionpattern=align β€” sets the alignment for any sections matching sectionpattern πŸ“ align is the alignment in bytes, and it MUST be a power of two, like 1, 2, 4, 8....

Note - setting a section's alignment does NOT automatically align its LMA or VMA addresses. If those need changing too, use --change-section-lma and/or --change-section-vma. Also, changing VMAs can cause problems in fully linked binaries, where there may be code that expects the contents of the sections to still be at their old address 🫠

✨ OPTIONS ✨ (continued)

okay bestie, this is where the section stuff gets spicy 🌸

  • --add-section sectionname=filename β€” 🧚 Makes a brand new section called sectionname while the file gets copied. Its contents come from filename, and the section ends up exactly the size of that file. This only works on formats that can handle sections with arbitrary names. ngl you might also need --set-section-flags to give the new section its attributes πŸ’…
  • --dump-section sectionname=filename β€” πŸ“€ The inverse of --add-section! It takes everything inside sectionname and dumps it into filename, overwriting anything already in there. It's kinda like --only-section, except it doesn't make a formatted file. It just dumps raw binary data and skips relocations entirely. You can use it more than once πŸ¦‹
  • --update-section sectionname=filename β€” πŸ”„ Swaps out the existing contents of sectionname for whatever is in filename. The section gets resized to match the file. The section flags stay the same, and for ELF files the section-to-segment mapping stays put too. That's the bit --remove-section followed by --add-section can't do 😭 You can use this more than once as well.
Heads up: you can update and rename a section in one go! Pass the original name to --update-section, and pass the original and new names to --rename-section. Iconic multitasking πŸ‘‘
  • --add-symbol name=[section:]value[,flags] β€” ✨ Adds a new symbol called name while copying. You can do this multiple times. If you give a section, the symbol is tied to that section and is relative to it. If you don't, it becomes an ABS symbol. Naming a section that doesn't exist is a fatal error πŸ’€ The value is taken exactly as you typed it, no checking. Not every flag makes sense for every object format. By default the symbol is global. The special flag before=othersym slots the new symbol right in front of othersym. Otherwise, symbols get tacked onto the end of the symbol table in the order you wrote them πŸŽ€
  • --rename-section oldname=newname[,flags] β€” 🏷️ Renames a section from oldname to newname, and you can change its flags to flags while you're at it. The perk over using a linker script? Your output stays an object file instead of turning into a linked executable. It takes the same flags as --set-section-flags.

This one is extra helpful with binary input, because binary input always makes a section called .data. Wanna name it .rodata instead, holding binary data? Try this:

objcopy -I binary -O <output_format> -B <architecture> \
 --rename-section .data=.rodata,alloc,load,readonly,data,contents \
 <input_binary_file> <output_object_file>
  • --long-section-names {enable,disable,keep} β€” πŸ“ Decides what happens to long section names when you're working with "COFF" and "PE-COFF" object formats. The default is keep, which holds onto long names if the input has them. enable and disable force long names on or off in the output. With disable, any long names in the input get chopped short. enable only emits long names if the inputs actually have some, so it's basically the same as keep. The one mystery is whether enable might force an empty string table into the output file, and that part is left undefined πŸ€·β€β™€οΈ
  • --change-leading-char β€” πŸ”€ Some object formats stick a special character at the start of symbol names. The usual suspect is underscore, which compilers love to add before every symbol. This tells objcopy to swap the leading character for every symbol when it converts between formats. If both formats use the same leading character, this does nothing. Otherwise it adds, removes, or changes a character as needed πŸ’–
  • --remove-leading-char β€” βœ‚οΈ If a global symbol starts with the format's special leading character, this yanks it off. Underscore is the most common one, so this strips the leading underscore from all global symbols. Super handy when you're linking objects from different formats that have different naming vibes. It's different from --change-leading-char because it always changes the name when it should, no matter what format the output is πŸ¦‹
  • --reverse-bytes=num β€” πŸ” Reverses the bytes in a section that has output contents. For the swap to work, the section length has to be evenly divisible by num. The reversing happens before any interleaving. This comes in clutch for making ROM images on picky target systems. For example, some boards re-assemble 32-bit words from 8-bit ROMs in little-endian order no matter what the CPU uses, so the ROM's endianness might need changing depending on the programming model 🫢

Say a section holds these eight bytes: 12345678.

  • --reverse-bytes=2 gives 21436587
  • --reverse-bytes=4 gives 43218765
  • --reverse-bytes=2 followed by --reverse-bytes=4 on the output file gives 34127856
  • --srec-len=ival β€” πŸ“ Only matters for srec output. Sets the max length of the Srecords being produced to ival. That length includes the address, data, and crc fields πŸ“
  • --srec-forceS3 β€” 🎯 Only matters for srec output. Stops S1 and S2 records from being generated, so you get an S3-only record format πŸ’œ
  • --redefine-sym old=new β€” πŸͺ„ Renames the symbol old to new. This is great when you're trying to link two things together that you don't have source for, and their names keep colliding πŸ™ˆ
  • --redefine-syms=filename β€” πŸ“œ Runs --redefine-sym on every old new symbol pair listed in filename. The file is a flat list with one pair per line, and hash characters start line comments. You can use this more than once ✨
  • --weaken β€” πŸŒ™ Makes every global symbol in the file weak. This is useful when you're building an object that'll get linked against other objects using the linker's -R option. It only does anything with object formats that support weak symbols 🌸
  • --keep-symbols=filename β€” πŸ›‘οΈ Runs --keep-symbol on each symbol listed in filename. That file is a flat list with one symbol name per line, and hash characters start line comments. You can use this more than once πŸŽ€
  • --strip-symbols=filename β€” πŸ—‘οΈ Runs --strip-symbol on each symbol listed in filename. Same deal: one symbol name per line, hash comments, and it can be used more than once 🫧
  • --strip-unneeded-symbols=filename β€” 🧹 Runs --strip-unneeded-symbol on each symbol listed in filename. One name per line, hash comments, and multiple uses are fine πŸ’…
  • --keep-global-symbols=filename β€” 🌍 Runs --keep-global-symbol on each symbol listed in filename. One name per line, hash comments, can be repeated πŸ¦‹
  • --localize-symbols=filename β€” 🏠 Runs --localize-symbol on each symbol listed in filename. One name per line, hash comments, and you can use it more than once 🌈
  • --globalize-symbols=filename β€” πŸ“£ Runs --globalize-symbol on each symbol listed in filename. One name per line, hash comments, repeatable. Note: this one cannot be combined with the -G or --keep-global-symbol options πŸ™…β€β™€οΈ
  • --weaken-symbols=filename β€” 🫠 Runs --weaken-symbol on each symbol listed in filename. One name per line, hash comments, and you can use it more than once πŸ’œ
  • --alt-machine-code=index β€” πŸ”’ If the output architecture has alternate machine codes, this uses the indexth code instead of the default. This is useful when a machine gets an official code and the toolchain adopts it, but other apps still rely on the original code. For ELF architectures, if the index alternative doesn't exist, the value gets treated as an absolute number and stored in the e_machine field of the ELF header 🧚
  • --writable-text β€” ✍️ Marks the output text as writable. Not every object format cares about this one 🌸
  • --readonly-text β€” πŸ”’ Makes the output text write-protected. Also not meaningful for every object format 🫢
  • --pure β€” πŸ“„ Marks the output file as demand paged. Not meaningful for every object format πŸ’–
  • --impure β€” πŸŒͺ️ Marks the output file as impure. Again, only meaningful for some object formats πŸ™ˆ
  • --prefix-symbols=string β€” 🏷️ Slaps string in front of every symbol in the output file πŸŽ€
  • --prefix-sections=string β€” πŸ“š Slaps string in front of every section name in the output file ✨
  • --prefix-alloc-sections=string β€” πŸŽ€ Slaps a prefix onto the front of every allocated section name in the output file, using string as the prefix. ✨
  • --add-gnu-debuglink=path-to-file β€” πŸ¦‹ Okay so this one creates a .gnu_debuglink section that points to path-to-file and tucks it into the output file. ngl the file at path-to-file has to already exist 😭. Adding that section also embeds a checksum of the debug info file's contents into it, which is very detail-oriented of it. πŸ’…

If your debug info file gets built in one spot but won't be installed until later in a different spot, do NOT use the installed path here. πŸ™ˆ The option will fail because the installed file doesn't exist yet. Instead, keep the debug info file in the current directory and pass it without any directory components, like this:

objcopy --add-gnu-debuglink=foo.debug

At debug time, the debugger goes hunting for the separate debug info file in a bunch of known locations. The exact list depends on the distro you're using, but it usually includes:

  • The same directory as the executable.
  • A sub-directory of the directory containing the executable, called .debug.
  • A global debug directory, such as /usr/lib/debug.

As long as the debug info file has been installed into one of those spots before the debugger runs, everything should work correctly. 🌸

  • --keep-section-symbols β€” 🧚 When you're stripping a file (say with --strip-debug or --strip-unneeded), this keeps any symbols that name sections, even though they'd otherwise get stripped. ✨
  • --keep-file-symbols β€” 🧚 Same energy as above, but for symbols that name source files. When you're stripping with --strip-debug or --strip-unneeded, these get kept instead of stripped. πŸ’œ
  • --only-keep-debug β€” πŸ”₯ This strips a file by removing the contents of any sections that --strip-debug wouldn't strip, while leaving the debugging sections intact. For ELF files, all note sections in the output stay put. πŸ“
Heads up, bestie: the section headers of the stripped sections are preserved, sizes included, but their contents get tossed. The headers stick around so other tools can match the debuginfo file with the real executable, even if that executable has been moved to a different address space. 🫢

This option is meant to be paired with --add-gnu-debuglink to create a two-part executable. πŸ‘‘ One part is the stripped binary, which takes up less RAM and less space in a distribution. The other is a debugging info file, which you only need if you actually want to debug. The suggested steps to make these are:

  1. Link the executable as normal. Assuming it's called foo, then...
  2. Run objcopy --only-keep-debug foo foo.dbg to create a file containing the debugging info.
  3. Run objcopy --strip-debug foo to create a stripped executable.
  4. Run objcopy --add-gnu-debuglink=foo.dbg foo to add a link to the debugging info into the stripped executable.

Note: using .dbg as the extension for the debug info file is totally arbitrary, and the --only-keep-debug step is optional. πŸ™ˆ You could instead do this:

  1. Link the executable as normal.
  2. Copy foo to foo.full.
  3. Run objcopy --strip-debug foo.
  4. Run objcopy --add-gnu-debuglink=foo.full foo.

Basically, the file pointed to by --add-gnu-debuglink can be the full executable. It doesn't have to be a file made by --only-keep-debug. πŸ’–

Note: this switch is only meant for fully linked files. It doesn't make sense on object files, where the debugging info might be incomplete. πŸ’€ Also, the gnu_debuglink feature currently only supports one filename containing debugging info, not multiple filenames on a one-per-object-file basis.

  • --strip-dwo β€” πŸ’… Removes the contents of all DWARF .dwo sections, but leaves the other debugging sections and all symbols intact. This one is built for the compiler to use as part of the -gsplit-dwarf option, which splits debug info between the .o file and a separate .dwo file. The compiler generates all the debug info in one file, then uses --extract-dwo to copy the .dwo sections into the .dwo file, and then --strip-dwo to remove those sections from the original .o file. 🧚
  • --extract-dwo β€” 🌈 Extracts the contents of all DWARF .dwo sections. Check out the --strip-dwo option for more info. πŸŽ€
  • --file-alignment num β€” πŸ“ Sets the file alignment. Sections in the file will always start at file offsets that are multiples of num. It defaults to 512. [This option is specific to PE targets.] ✨
  • --heap reserve, --heap reserve,commit β€” 🧠 Reserves the given number of bytes of memory (and optionally commits them) to be used as heap for this program. [This option is specific to PE targets.] πŸ’œ
  • --image-base value β€” 🏠 Uses value as the base address of your program or dll. This is the lowest memory location that gets used when your program or dll is loaded. To cut down on relocations and boost dll performance, each dll should have a unique base address and not overlap any other dll. The default is 0x400000 for executables and 0x10000000 for dlls. [This option is specific to PE targets.] πŸ¦‹
  • --section-alignment num β€” πŸ“ [This option is specific to PE targets.] Sets the section alignment field in the PE header, if one is present in the binary. Sections in memory always begin at addresses that are multiples of num. It defaults to 0x1000.
Note: if a section's LMA or VMA addresses end up unaligned, and those addresses weren't set with --set-section-lma or --set-section-vma, and the file has been fully relocated, then a warning message gets issued. From there, it's up to you to decide whether the LMA and VMA need updating. ⚠️
  • --stack reserve, --stack reserve,commit β€” πŸ₯ž Reserves the given number of bytes of memory (and optionally commits them) to be used as stack for this program. [This option is specific to PE targets.] πŸ’…
  • --subsystem which, --subsystem which:major, --subsystem which:major.minor β€” πŸ–₯️ Picks the subsystem your program will run under. The legal values for which are native, windows, console, posix, efi-app, efi-bsd, efi-rtd, sal-rtd, and xbox. You can also optionally set the subsystem version. Numeric values are accepted for which too. [This option is specific to PE targets.] πŸ‘‘
  • --extract-symbol β€” πŸ” Keeps the file's section flags and symbols, but removes all section data. Specifically, this option:
    • removes the contents of all sections;
    • sets the size of every section to zero; and
    • sets the file's start address to zero.

It's used to build a .sym file for a VxWorks kernel. It can also be handy for shrinking a --just-symbols linker input file. 🌸

  • --compress-debug-sections β€” πŸ—œοΈ Compresses DWARF debug sections using zlib with SHF_COMPRESSED from the ELF ABI. Note: if compression would actually make a section larger, it doesn't get compressed. πŸ™ˆ
  • --compress-debug-sections=none, --compress-debug-sections=zlib, --compress-debug-sections=zlib-gnu, --compress-debug-sections=zlib-gabi, --compress-debug-sections=zstd β€” πŸŽ›οΈ For ELF files, these control how DWARF debug sections get compressed. --compress-debug-sections=none is the same as --decompress-debug-sections. --compress-debug-sections=zlib and --compress-debug-sections=zlib-gabi are the same as --compress-debug-sections. --compress-debug-sections=zlib-gnu compresses DWARF debug sections using the obsoleted zlib-gnu format, and the debug sections get renamed to begin with .zdebug. --compress-debug-sections=zstd compresses DWARF debug sections using zstd. Note: if compression would make a section larger, it's not compressed nor renamed. πŸ’œ
  • --decompress-debug-sections β€” 🫧 Decompresses DWARF debug sections. For a .zdebug section, the original name gets restored. ✨
  • --elf-stt-common=yes, --elf-stt-common=no β€” 🧩 For ELF files, these decide whether common symbols should be converted to the STT_COMMON or STT_OBJECT type. --elf-stt-common=yes converts the common symbol type to STT_COMMON. --elf-stt-common=no converts it to STT_OBJECT. πŸ¦‹
  • --merge-notes, --no-merge-notes β€” πŸ—’οΈ For ELF files, these either attempt or don't attempt to shrink any SHT_NOTE type sections by removing duplicate notes. 🌈
  • -V, --version β€” 🏷️ Shows the version number of objcopy. πŸ’–
  • --verilog-data-width=bytes β€” πŸ”’ For Verilog output, this controls the number of bytes converted for each output data element. The input target controls the endianness of the conversion. πŸŽ€
  • -v, --verbose β€” πŸ“£ Verbose output: lists all object files modified. For archives, objcopy -V lists all members of the archive. πŸ”₯
  • --help β€” πŸ†˜ Shows a summary of the objcopy options. 🫢
  • --info β€” πŸ—ΊοΈ Displays a list of all the architectures and object formats available. 🌍
  • @file β€” πŸ“‚ Reads command-line options from file. The options read get inserted in place of the original @file option. If file doesn't exist or can't be read, the option is treated literally and not removed. πŸ™ƒ

Options in file are separated by whitespace. To include a whitespace character inside an option, wrap the entire option in either single or double quotes. Any character (including a backslash) can be included by putting a backslash in front of the character you want. The file can contain more @file options too, and those get processed recursively. 🧚

πŸ¦‹ SEE ALSO πŸ¦‹

ld(1), objdump(1), and the Info entries for binutils. πŸ’œ

okay so the copyright bit is lowkey important, bestie 🫢 It's Copyright (c) 1991-2025 Free Software Foundation, Inc. ✨

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation. πŸ“œ There are no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. πŸ™…β€β™€οΈ

A copy of the license is included in the section entitled "GNU Free Documentation License". πŸŽ€


bestie, that's the whole page 😭🫢 binutils-2.45 πŸ¦‹ dated 2025-12-03 🌸 and it's all the OBJCOPY(1) manual, periodt πŸ’…βœ¨

πŸ“¦ where this page comes from