Skip to content

cmd/utils: Add architecture resolution and cross-arch name handling#1786

Open
DaliborKr wants to merge 10 commits into
containers:mainfrom
DaliborKr:pr06-cmd-utils
Open

cmd/utils: Add architecture resolution and cross-arch name handling#1786
DaliborKr wants to merge 10 commits into
containers:mainfrom
DaliborKr:pr06-cmd-utils

Conversation

@DaliborKr
Copy link
Copy Markdown
Collaborator

This is PR 6/10 in a series adding cross-architecture container support
using QEMU and binfmt_misc.

Depends on: #1784 (pkg/skopeo: Extend Image struct and add cross-architecture operations)
Please review #1784 first. The new commits in this PR are:

  • cmd/utils: cmd/utils: Add architecture resolution from --arch flag, image tags, and names
  • cmd/utils: Update resolveContainerAndImageNames for cross-arch support

Summary

The --arch flag value and architecture embedded in image tags (e.g., fedora-toolbox:42-aarch64) need to be resolved into an architecture ID that the rest of the create/enter/run commands can use. When both sources are present, they must not conflict. The architecture in tag detection applies only to images from distributions that Toolbx explicitly supports (see [1]), to avoid a false architecture approach on custom images where a dash-separated component might not represent an architecture, since there is no standard set regarding preserving architecture in the tag (see detailed explanation at [2]).

Cross-architecture images and containers also need distinct names to coexist with native ones. The naming convention appends the OCI architecture name as a suffix (e.g., image -> fedora-toolbox:42-arm64, container -> fedora-toolbox-43-arm64). The reasoning behind this convention is described in the Podman discussion at [2].

Add:

  • resolveArchitectureID() that parses the --arch flag, detects architecture from image tags, and validates consistency if both are set. If none of them is set, it falls back to the host architecture, which represents a native behavior.

  • resolveImageNameWithArchitectureSuffix() that appends an architecture suffix to supported distro image names when the target differs from the host

  • Updated resolveContainerAndImageNames() with an archID parameter, auto-suffixing container names for non-native architectures

[1] https://containertoolbx.org/distros/
[2] containers/podman#27780 (comment)

The existing RunContextWithExitCode() wraps all errors from
exec.Command in generic "failed to invoke" messages, which prevents
callers from distinguishing between actual error types.

Add RunContextWithExitCode2() and RunWithExitCode2() that return
errors with their original types intact, including for ExitError.
This allows callers to use errors.Is() and errors.As() to handle
specific failure modes. For example, detecting a missing skopeo binary
(exec.ErrNotFound) or an ENOEXEC error from inside non native
containers, when an emulator is not set correctly.

These new functions are meant to replace its original versions in the
future.

containers#1780

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
@DaliborKr DaliborKr requested a review from debarshiray as a code owner April 24, 2026 06:07
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces cross-architecture support by adding a new architecture package to manage host and container architectures, detect QEMU emulation requirements, and handle binfmt_misc registration. Core commands like create, enter, and run are updated to be architecture-aware, and skopeo integration now supports architecture overrides. Review feedback suggests improving function naming in the shell package for better clarity, following idiomatic Go conventions for error messages, and adding support for the s390x architecture.

Comment thread src/pkg/shell/shell.go
Comment on lines +84 to +88
func RunContextWithExitCode2(ctx context.Context,
name string,
stdin io.Reader,
stdout, stderr io.Writer,
arg ...string) (int, error) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The name RunContextWithExitCode2 is not descriptive and follows a poor naming convention. It would be better to use a name that clearly indicates the difference in behavior compared to RunContextWithExitCode, such as RunContextWithExitError, since this version returns the error even when it is an exec.ExitError.

Suggested change
func RunContextWithExitCode2(ctx context.Context,
name string,
stdin io.Reader,
stdout, stderr io.Writer,
arg ...string) (int, error) {
func RunContextWithExitError(ctx context.Context,
name string,
stdin io.Reader,
stdout, stderr io.Writer,
arg ...string) (int, error) {

Comment thread src/pkg/shell/shell.go
return exitCode, err
}

func RunWithExitCode2(name string, stdin io.Reader, stdout, stderr io.Writer, arg ...string) (int, error) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to RunContextWithExitCode2, the name RunWithExitCode2 should be improved to reflect its behavior of returning the error on non-zero exit codes.

Suggested change
func RunWithExitCode2(name string, stdin io.Reader, stdout, stderr io.Writer, arg ...string) (int, error) {
func RunWithExitError(name string, stdin io.Reader, stdout, stderr io.Writer, arg ...string) (int, error) {

Comment thread src/pkg/skopeo/skopeo.go

args = append(args, targetWithTransport)

if _, err := shell.RunContextWithExitCode2(ctx, "skopeo", nil, &stdout, nil, args...); err != nil {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If RunContextWithExitCode2 is renamed to RunContextWithExitError, this call should be updated accordingly.

Suggested change
if _, err := shell.RunContextWithExitCode2(ctx, "skopeo", nil, &stdout, nil, args...); err != nil {
if _, err := shell.RunContextWithExitError(ctx, "skopeo", nil, &stdout, nil, args...); err != nil {

Comment on lines +57 to +82
var supportedArchitectures = map[int]Architecture{
Aarch64: {
ID: Aarch64,
NameBinfmt: "aarch64",
NameOCI: "arm64",
Aliases: []string{"aarch64", "arm64"},
ELFMagic: []byte{0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xb7, 0x00},
ELFMask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff},
},
Ppc64le: {
ID: Ppc64le,
NameBinfmt: "ppc64le",
NameOCI: "ppc64le",
Aliases: []string{"ppc64le"},
ELFMagic: []byte{0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x15, 0x00},
ELFMask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x00},
},
X86_64: {
ID: X86_64,
NameBinfmt: "x86_64",
NameOCI: "amd64",
Aliases: []string{"x86_64", "amd64"},
ELFMagic: []byte{0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00},
ELFMask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff},
},
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The supportedArchitectures map is missing s390x, which is a primary architecture supported by Fedora and RHEL. Consider adding support for it to ensure parity with the distributions Toolbx targets.

}

if foundQemuBinaryPath == "" {
err := fmt.Errorf("The host system does not have the required support: No %s statically linked QEMU emulator binary found", archNameDebug)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In Go, error messages should generally be lowercase and not end with punctuation to follow idiomatic style and consistency with the rest of the codebase.

Suggested change
err := fmt.Errorf("The host system does not have the required support: No %s statically linked QEMU emulator binary found", archNameDebug)
err := fmt.Errorf("the host system does not have the required support: no %s statically linked QEMU emulator binary found", archNameDebug)

}

if !validateBinfmtRegistration(archID, false) {
err := fmt.Errorf("The host system does not have the required support: No %s binfmt_misc registration found", archNameDebug)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Idiomatic Go error messages should start with a lowercase letter.

Suggested change
err := fmt.Errorf("The host system does not have the required support: No %s binfmt_misc registration found", archNameDebug)
err := fmt.Errorf("the host system does not have the required support: no %s binfmt_misc registration found", archNameDebug)

Comment thread src/pkg/architecture/architecture.go Outdated

if isStaticallyLinkedELF(interpreterPath) {
if !validateBinfmtRegistration(archID, true) {
return fmt.Errorf("The host system does not have the required support: No %s binfmt_misc registration found", archNameDebug)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Idiomatic Go error messages should start with a lowercase letter.

Suggested change
return fmt.Errorf("The host system does not have the required support: No %s binfmt_misc registration found", archNameDebug)
return fmt.Errorf("the host system does not have the required support: no %s binfmt_misc registration found", archNameDebug)

Comment thread src/pkg/architecture/architecture.go Outdated
logrus.Debugf("Found valid QEMU binary at %s", qemuPath)

if !validateBinfmtRegistration(archID, true) {
return fmt.Errorf("The host system does not have the required support: No %s binfmt_misc registration found", archNameDebug)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Idiomatic Go error messages should start with a lowercase letter.

Suggested change
return fmt.Errorf("The host system does not have the required support: No %s binfmt_misc registration found", archNameDebug)
return fmt.Errorf("the host system does not have the required support: no %s binfmt_misc registration found", archNameDebug)

Comment thread src/pkg/architecture/architecture.go Outdated
}
}

return fmt.Errorf("The host system does not have the required support: No %s statically linked QEMU emulator binary found", archNameDebug)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Idiomatic Go error messages should start with a lowercase letter.

Suggested change
return fmt.Errorf("The host system does not have the required support: No %s statically linked QEMU emulator binary found", archNameDebug)
return fmt.Errorf("the host system does not have the required support: no %s statically linked QEMU emulator binary found", archNameDebug)

reg := getDefaultRegistration(archID, interpreterPath)
if reg == nil {
logrus.Debugf("Unable to register binfmt_misc for architecture '%s'", GetArchNameOCI(archID))
return fmt.Errorf("Toolbx does not support architecture '%s'", GetArchNameOCI(archID))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Idiomatic Go error messages should start with a lowercase letter.

Suggested change
return fmt.Errorf("Toolbx does not support architecture '%s'", GetArchNameOCI(archID))
return fmt.Errorf("toolbx does not support architecture '%s'", GetArchNameOCI(archID))

@softwarefactory-project-zuul
Copy link
Copy Markdown

DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request Apr 24, 2026
…and names

Add resolveArchitectureID(), which combines the --arch command-line
flag with architecture detection from image tag suffixes
(e.g., "fedora-toolbox:42-aarch64"). This detection applies only
to images from distributions that Toolbx explicitly supports (see [1]),
to avoid a false architecture approach on custom images where a
dash-separated component might not represent an architecture, since
there is no standard set regarding preserving architecture in the tag
(see detailed explanation at [2]). When both sources specify an
architecture, it validates that they do not conflict.

Add resolveImageNameWithArchitectureSuffix(), which appends the OCI
architecture name to supported distro image references when the target
architecture differs from the host, to ensure the local Toolbx images
naming convention [2]. Again, this applies only to supported distros.

[1] https://containertoolbx.org/distros/
[2] containers/podman#27780 (comment)

containers#1786

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request Apr 24, 2026
Change resolveContainerAndImageNames() to accept an archID parameter.
When the target architecture is non-native, and the container name was
auto-generated (was not set by a user), append the architecture suffix
to the container name (e.g., "fedora-toolbox-arm64") to distinguish it
from native containers.

Temporarily update the callers of resolveContainerAndImageNames() to
pass in architecture.HostArchID to the updated signature, to maintain
a default native behavior. Once implemented, the --arch argument in
the callers will pass the actual architecture information.

containers#1786

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
@softwarefactory-project-zuul
Copy link
Copy Markdown

DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request Apr 24, 2026
Add the --arch flag to the 'create' command, allowing users to create
Toolbx containers for architectures different from the host
(e.g., 'toolbox create --arch arm64').

Utilize the architecture resolution pipeline in create() by using
resolveArchitectureID() (added in [1]) to determine the target
architecture from the --arch flag and image tags.

Validate host support via IsArchSupportedOnCreation() (added in [2]),
which checks for the required QEMU emulator and binfmt_misc
registration.

Pass architecture ID to resolveContainerAndImageNames() (updated in [1])
so that non-native containers get architecture-suffixed names.

Update pullImage() to handle cross-architecture image pulling: when the
target architecture is non-native, use skopeo.CopyOverrideArch()
(added in [3]) instead of podman.Pull(), since Podman does not support
pulling foreign architecture images into locally addressable names.
The need for this is explained in a discussion in [4].

Add a 'toolbox-arch' label to created containers to record the target
architecture in OCI format.

Extract the image pull error formatting into createErrorImagePull() in
utils.go to avoid duplication between the native and cross-arch pull
paths.

Update the createContainer() call in run.go to pass the default
architecture config via GetArchConfigDefault(), maintaining the
existing native-architecture behavior.

[1] containers#1786
[2] containers#1783
[3] containers#1784
[4] containers/podman#27780

containers#1787

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request Apr 24, 2026
…and names

Add resolveArchitectureID(), which combines the --arch command-line
flag with architecture detection from image tag suffixes
(e.g., "fedora-toolbox:42-aarch64"). This detection applies only
to images from distributions that Toolbx explicitly supports (see [1]),
to avoid a false architecture approach on custom images where a
dash-separated component might not represent an architecture, since
there is no standard set regarding preserving architecture in the tag
(see detailed explanation at [2]). When both sources specify an
architecture, it validates that they do not conflict.

Add resolveImageNameWithArchitectureSuffix(), which appends the OCI
architecture name to supported distro image references when the target
architecture differs from the host, to ensure the local Toolbx images
naming convention [2]. Again, this applies only to supported distros.

[1] https://containertoolbx.org/distros/
[2] containers/podman#27780 (comment)

containers#1786

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request Apr 24, 2026
Change resolveContainerAndImageNames() to accept an archID parameter.
When the target architecture is non-native, and the container name was
auto-generated (was not set by a user), append the architecture suffix
to the container name (e.g., "fedora-toolbox-arm64") to distinguish it
from native containers.

Temporarily update the callers of resolveContainerAndImageNames() to
pass in architecture.HostArchID to the updated signature, to maintain
a default native behavior. Once implemented, the --arch argument in
the callers will pass the actual architecture information.

containers#1786

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
@softwarefactory-project-zuul
Copy link
Copy Markdown

DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request Apr 24, 2026
Add the --arch flag to the 'create' command, allowing users to create
Toolbx containers for architectures different from the host
(e.g., 'toolbox create --arch arm64').

Utilize the architecture resolution pipeline in create() by using
resolveArchitectureID() (added in [1]) to determine the target
architecture from the --arch flag and image tags.

Validate host support via IsArchSupportedOnCreation() (added in [2]),
which checks for the required QEMU emulator and binfmt_misc
registration.

Pass architecture ID to resolveContainerAndImageNames() (updated in [1])
so that non-native containers get architecture-suffixed names.

Update pullImage() to handle cross-architecture image pulling: when the
target architecture is non-native, use skopeo.CopyOverrideArch()
(added in [3]) instead of podman.Pull(), since Podman does not support
pulling foreign architecture images into locally addressable names.
The need for this is explained in a discussion in [4].

Add a 'toolbox-arch' label to created containers to record the target
architecture in OCI format.

Extract the image pull error formatting into createErrorImagePull() in
utils.go to avoid duplication between the native and cross-arch pull
paths.

Update the createContainer() call in run.go to pass the default
architecture config via GetArchConfigDefault(), maintaining the
existing native-architecture behavior.

[1] containers#1786
[2] containers#1783
[3] containers#1784
[4] containers/podman#27780

containers#1787

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request Apr 24, 2026
…and names

Add resolveArchitectureID(), which combines the --arch command-line
flag with architecture detection from image tag suffixes
(e.g., "fedora-toolbox:42-aarch64"). This detection applies only
to images from distributions that Toolbx explicitly supports (see [1]),
to avoid a false architecture approach on custom images where a
dash-separated component might not represent an architecture, since
there is no standard set regarding preserving architecture in the tag
(see detailed explanation at [2]). When both sources specify an
architecture, it validates that they do not conflict.

Add resolveImageNameWithArchitectureSuffix(), which appends the OCI
architecture name to supported distro image references when the target
architecture differs from the host, to ensure the local Toolbx images
naming convention [2]. Again, this applies only to supported distros.

[1] https://containertoolbx.org/distros/
[2] containers/podman#27780 (comment)

containers#1786

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request Apr 24, 2026
Change resolveContainerAndImageNames() to accept an archID parameter.
When the target architecture is non-native, and the container name was
auto-generated (was not set by a user), append the architecture suffix
to the container name (e.g., "fedora-toolbox-arm64") to distinguish it
from native containers.

Temporarily update the callers of resolveContainerAndImageNames() to
pass in architecture.HostArchID to the updated signature, to maintain
a default native behavior. Once implemented, the --arch argument in
the callers will pass the actual architecture information.

containers#1786

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request Apr 24, 2026
Add the --arch flag to the 'create' command, allowing users to create
Toolbx containers for architectures different from the host
(e.g., 'toolbox create --arch arm64').

Utilize the architecture resolution pipeline in create() by using
resolveArchitectureID() (added in [1]) to determine the target
architecture from the --arch flag and image tags.

Validate host support via IsArchSupportedOnCreation() (added in [2]),
which checks for the required QEMU emulator and binfmt_misc
registration.

Pass architecture ID to resolveContainerAndImageNames() (updated in [1])
so that non-native containers get architecture-suffixed names.

Update pullImage() to handle cross-architecture image pulling: when the
target architecture is non-native, use skopeo.CopyOverrideArch()
(added in [3]) instead of podman.Pull(), since Podman does not support
pulling foreign architecture images into locally addressable names.
The need for this is explained in a discussion in [4].

Add a 'toolbox-arch' label to created containers to record the target
architecture in OCI format.

Extract the image pull error formatting into createErrorImagePull() in
utils.go to avoid duplication between the native and cross-arch pull
paths.

Update the createContainer() call in run.go to pass the default
architecture config via GetArchConfigDefault(), maintaining the
existing native-architecture behavior.

[1] containers#1786
[2] containers#1783
[3] containers#1784
[4] containers/podman#27780

containers#1787

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
@softwarefactory-project-zuul
Copy link
Copy Markdown

DaliborKr added 9 commits May 10, 2026 22:12
In /src/cmd/create.go, the same pattern of spinner creation and
nil-safe stopping is repeated.

Extract this into startSpinner() and stopSpinner() helper functions so
that future callers can use spinners without duplicating the code.
Replace the existing inline spinner code in createContainer() and
pullImage() with calls to these new helpers.

containers#1781

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
…atching

Add IsSupportedDistroImage(), which iterates over all supported distros
and checks if the image basename matches any of them. This will be used
by the architecture resolution code to decide whether to parse
architecture suffixes from image tags, as this should be done only for
natively supported images [1].

[1] Toolbx supported distributions: https://containertoolbx.org/distros/

containers#1781

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
Introduce the architecture package that represents the core of the
Toolbx cross-architecture support, which is based on user-mode emulation
using QEMU and binfmt_misc.

The Architecture struct collects all per-architecture data (ELF
magic/mask, OCI and binfmt naming, aliases, binfmt registration
parameters) into a single map. Architectures present in the
supportedArchitectures map represent the set of supported
architectures within Toolbx.

Define architecture ID constants NotSpecified, Aarch64, Ppc64le, and
X86_64, along with their supportedArchitectures entries.

Add core query functions:
- ParseArgArchValue() for resolving user-supplied architecture strings

- GetArchNameBinfmt() and GetArchNameOCI() for name
  lookups (one architecture can have multiple valid names [1])

- HasContainerNativeArch() for comparing against the host

- ImageReferenceGetArchFromTag() for extracting architecture
  from image tag suffixes like "42-aarch64" for architecture detection

Expose the HostArchID package variable, which is set in the init()
function, so the variable can be accessed in the early init() state
from every inheritor that utilizes the architecture package (HostArchID
serves as a default value for initContainer --arch flag), and the Config
struct for preserving the architecture ID and the QEMU emulator path,
through the call chain.

[1] https://itsfoss.com/arm-aarch64-x86_64/

containers#1782

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
Cross-architecture containers need QEMU binfmt_misc handlers registered
within the container so that non-native architecture binaries can be
executed via the host's kernel.

Add the Registration struct that models a binfmt_misc registration
entry, including name, magic type, offset, ELF magic/mask bytes,
interpreter path, and flags.

Add functions:
- MountBinfmtMisc() to mount the sanboxed binfmt_misc filesystem inside
  a container, which enables setting the C flag in binfmt_misc
  registration without affecting the host system. The C flag presents a
  threat of privilege escalation when registered on the host, that why
  we want to have it isolated [1]

- getDefaultRegistration() to fill a Registration struct containing all
  necessary binfmt_misc information taken from the
  architecture.supportedArchitectures data

- RegisterBinfmtMisc() to write the registration string to
  /proc/sys/fs/binfmt_misc/register, which makes the non-native binary
  perception active

- bytesToEscapedString() helper that formats byte slices into the
  \xHH-escaped string format required by the binfmt_misc register
  interface

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=21ca59b365c0

containers#1782

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
Before creating or initializing a cross-architecture container, the
system must be checked for the required QEMU emulator and binfmt_misc
registration. This prevents users from creating or running non-native
containers when their host system doesn't meet the requirements, and
provides users with an informative error message referring to the
problem.

Add IsArchSupportedOnCreation(), which searches for a statically linked
QEMU binary on the host using exec.LookPath() and verifies that a
matching binfmt_misc registration exists. It returns the path to the
QEMU binary for use during container creation, which is meant to be
passed to the init-container and registered through sandboxed
binfmt_misc within the container.

Add IsArchSupportedOnInitialization() which performs similar checks
from inside the container, looking at the interpreter path passed from
the host and falling back to standard host-mounted locations under
/run/host/usr/bin/.

Add isStaticallyLinkedELF() helper that uses debug/elf to verify a
binary is statically linked. Only a statically linked QEMU interpreter
can be used, because a dynamically linked one would cause the kernel
to attempt to resolve its host-native shared libraries (such as libc.so)
within the container, resulting in an immediate crash.

Add validateBinfmtRegistration(), which checks for the presence of
qemu-<arch> entries in binfmt_misc (or qemu-<arch>-static, since it can
differ based on the system).

containers#1783

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
Add Architecture and NameFull fields to the Skopeo Image struct so that
callers can inspect the architecture of a remote image. Move the image
size computation from the /cmd layer into GetSize() and GetSizeHuman()
methods on Image, since the skopeo package owns the layer data.

Add VerifyArchitectureMatch() method to Image that validates the image's
architecture field against an expected architecture ID. The purpose of
this function is to check whether the image architecture matches the
demanded architecture before it is pulled. Specifically, this
verification applies to the images that support only a single
architecture (they are not part of a multi-platform manifest list),
because the skopeo inspect proceeds successfully even when the value
of a flag --override-arch does not match the actual image architecture
(for a multi-architecture image the skopeo inspect with not-matching
--override-arch would fail). Like this, the user can be prevented from
incompatible images.

containers#1784

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
Change Inspect() to accept archID and authfile parameters. When the
requested architecture differs from the host's, --override-arch is
passed to skopeo, which then inspects the correct manifest in a
multi-arch image (if it exists for the given architecture, otherwise
the inspection fails). It also uses RunContextWithExitCode2() so callers
can detect a missing skopeo binary via errors.Is(err, exec.ErrNotFound),
which is only a soft dependency of the Toolbx package, as it is not
required for running native containers.

Add CopyOverrideArch(), which uses 'skopeo copy --override-arch' to pull
a specific architecture variant of a multi-arch image into Podman's
local container storage. This is used instead of 'podman pull' because
Podman does not support pulling a foreign architecture image into a
locally addressable name. The way in which the cross-arch extension
chooses the name for non-native images (and also containers) is
described in the discussion at [1]

[1] containers/podman#27780 (comment)

containers#1784

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
…and names

Add resolveArchitectureID(), which combines the --arch command-line
flag with architecture detection from image tag suffixes
(e.g., "fedora-toolbox:42-aarch64"). This detection applies only
to images from distributions that Toolbx explicitly supports (see [1]),
to avoid a false architecture approach on custom images where a
dash-separated component might not represent an architecture, since
there is no standard set regarding preserving architecture in the tag
(see detailed explanation at [2]). When both sources specify an
architecture, it validates that they do not conflict.

Add resolveImageNameWithArchitectureSuffix(), which appends the OCI
architecture name to supported distro image references when the target
architecture differs from the host, to ensure the local Toolbx images
naming convention [2]. Again, this applies only to supported distros.

[1] https://containertoolbx.org/distros/
[2] containers/podman#27780 (comment)

containers#1786

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
Change resolveContainerAndImageNames() to accept an archID parameter.
When the target architecture is non-native, and the container name was
auto-generated (was not set by a user), append the architecture suffix
to the container name (e.g., "fedora-toolbox-arm64") to distinguish it
from native containers.

Temporarily update the callers of resolveContainerAndImageNames() to
pass in architecture.HostArchID to the updated signature, to maintain
a default native behavior. Once implemented, the --arch argument in
the callers will pass the actual architecture information.

containers#1786

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request May 10, 2026
Add the --arch / -a flag to the enter command, allowing users to enter
cross-architecture containers by specifying the target architecture
(e.g., toolbox enter --arch arm64). Can be used with flags --distro
and --release, just as for container creation.

The flag value is resolved through resolveArchitectureID()
(added in [1]) and passed to resolveContainerAndImageNames()
(updated for cross-arch in [1])  so that it resolves to the
architecture-suffixed container name.

[1] containers#1786

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
DaliborKr added a commit to DaliborKr/toolbox that referenced this pull request May 10, 2026
Add the --arch / -a flag to the run command, allowing users to run
commands inside cross-architecture containers by specifying the target
architecture (e.g., toolbox run --arch arm64 uname -m). Can be used
with flags --distro and --release, just as for container creation.

The flag value is resolved through resolveArchitectureID()
(added in [1]) and passed to resolveContainerAndImageNames()
(updated for cross-arch in [1])  so that it resolves to the
architecture-suffixed container name.

[1] containers#1786

Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
@centosinfra-prod-github-app
Copy link
Copy Markdown

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

Successfully merging this pull request may close these issues.

1 participant