Skip to content

Running strip on standalone binary removes save state #1370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
xelxebar opened this issue May 30, 2025 · 2 comments
Open

Running strip on standalone binary removes save state #1370

xelxebar opened this issue May 30, 2025 · 2 comments

Comments

@xelxebar
Copy link

Overview

Running GNU binutils strip on a qsave_program/2 standalone ELF binary removes the save state data, resulting in a binary that just drops to a dry swipl REPL, i.e.

$ prog        # Works
$ strip prog  # This should be harmless, right?
$ prog        # Now broken

The standalone binary doesn't contain debug symbols in the first place, so arguably the correct action is to just not use strip in the first place. However, there is at least one previous report on the forums, and in my case, strip is automatically run by a distribution package builder, causing very confusing breakages.

Analysis

The issue is that strip removes any data in an ELF not referenced by an ELF section, and qsave_program/2 produces a Zip archive concatenated to an ELF stub, meaning that the trailing Zip data remains anonymous:

$ binwalk prog

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             ELF, 64-bit LSB executable, AMD x86-64, version 1 (SYSV)
294522        0x47E7A         End of Zip archive, footer length: 22

but

$ cp prog prog.stripped
$ strip prog.stripped
$ cmp prog prog.stripped
cmp: EOF on plwm.stripped after byte 14392, in line 2
$ binwalk prog.stripped

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             ELF, 64-bit LSB executable, AMD x86-64, version 1 (SYSV)

This show that strip was a no-op on the ELF stub but removed everything after byte 14392 = 0x3838.

Proof of Concept Fix

The idea for mitigation is simple: just create an ELF section that references the Zip archive data. Below is a manual demonstration of the idea that I have confirmed works:

$ unzip a.out; zip -Dr '$prolog' a.zip     # Extract the zip archive
$ strip a.out; mv a.out a.stub             # Extract the ELF stub
$ objcopy --add-section .zipdata=a.zip --set-section-flags .zipdata=readonly,data a.stub plwm

Is it reasonable to patch qsave_program/2, or wherever relevant, to add such an ELF section? Other thoughts?

@JanWielemaker
Copy link
Member

This issue has been mentioned on SWI-Prolog. There might be relevant details there:

https://swi-prolog.discourse.group/t/swi-prolog-standalone-binary-vs-strip/7527/5

@JanWielemaker
Copy link
Member

I think that is a good idea. You can check that we are using ELF using

 ?- current_prolog_flag(executable_format, elf).

and verify objcopy exists using absolute_file_name(path(objcopy), Exe, [access(execute), file_errors(fail)]). Please propose a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants