Calling rust executable in bash on Sculpt

Dear Genodians,

I have build GitHub - atopia/ripgrep-goa successfully using goa and copied all depot content from /home/user/ripgrep-goa/var/depot to depot on the active *GENODE GPT partition.

The following deploy file for sculpt runs the ripgrep tool successfully as a component with the expected output:

[init -> runtime -> ripgrep] 3:            say hello to Genode!
[init -> runtime] child "ripgrep" exited with exit value 0

On the other hand, calling the tool as a binary in the “system shell” scenario gives the following error:

[init -> runtime -> system_shell -> /bin/bash -> 1] Warning: invalid executable binary format: /rw/depot/user/bin/x86_64/ripgrep-goa/2023-08-16/rg

Bash prints:

system:/> /rw/depot/user/bin/x86_64/ripgrep-goa/2023-08-16/rg --help
bash: /rw/depot/user/bin/x86_64/ripgrep-goa/2023-08-16/rg: cannot execute binary file: Exec format error

The deploy file:

<config arch="x86_64" info="simple desktop environment">

	<common_routes>
		<service name="ROM" label_last="ld.lib.so"> <parent/> </service>
		<service name="CPU">   <parent/> </service>
		<service name="PD">    <parent/> </service>
		<service name="LOG">   <parent/> </service>
		<service name="Timer"> <parent/> </service>
		<service name="File_system">
			<child name="default_fs_rw"/>
		</service>
	</common_routes>

	<start name="recall_fs" pkg="genodelabs/pkg/recall_fs/2025-04-28"/>
	<start name="black_hole" pkg="genodelabs/pkg/black_hole/2025-04-28"/>

	<start name="fs_rom" priority="-2" pkg="jschlatow/pkg/fs_rom/2025-04-09"/>

	<start name="ripgrep" priority="-2" pkg="user/pkg/ripgrep-goa/2023-08-16"/>

	<start name="system_shell" priority="-2" pkg="genodelabs/pkg/system_shell/2025-04-28">
		<route>
			<service name="Gui"> <child name="wm"/> </service>
			<service name="File_system" label_prefix="config ->"> <parent identity="config"/> </service>
			<service name="File_system" label_prefix="report ->"> <parent identity="report"/> </service>
			<service name="File_system" label_prefix="target ->"> <child name="default_fs_rw"/> </service>
			<service name="File_system" label_prefix="fonts ->">  <child name="fonts_fs"/> </service>
			<service name="ROM" label="vimrc"> <parent label="config -> vimrc"/> </service>
			<service name="ROM" label="clipboard"> <child name="wm"/> </service>
			<service name="Report" label="clipboard"> <child name="wm"/> </service>
			<service name="RM"> <parent/> </service>
			<service name="ROM" label_prefix="/bin/bash -> /rw/depot/user/bin/x86_64"> <child name="fs_rom"/> </service>
		</route>
	</start>

	<start name="window_layouter" priority="-1" pkg="genodelabs/pkg/window_layouter/2025-04-28">
		<route>
			<service name="Gui">                           <child name="wm"/> </service>
			<service name="ROM" label="window_list">       <child name="wm"/> </service>
			<service name="ROM" label="focus_request">     <child name="wm"/> </service>
			<service name="ROM" label="hover">             <child name="wm"/> </service>
			<service name="ROM" label="decorator_margins"> <child name="wm"/> </service>
			<service name="Report">                        <child name="wm"/> </service>
			<service name="File_system" label_prefix="recall ->">
				<child name="recall_fs"/>
			</service>
		</route>
	</start>

	<start name="themed_decorator" priority="-1" pkg="genodelabs/pkg/themed_decorator/2025-04-28">
		<route>
			<service name="ROM" label="window_layout"> <child name="wm"/> </service>
			<service name="ROM" label="pointer">       <child name="wm"/> </service>
			<service name="Report">                    <child name="wm"/> </service>
			<service name="Gui">                       <child name="wm"/> </service>
		</route>
	</start>

	<start name="wm" priority="-1" pkg="genodelabs/pkg/wm/2025-04-28">
		<route>
			<service name="Gui" label="focus"> <parent label="focus"/> </service>
			<service name="Gui">                           <parent/> </service>
			<service name="Report" label_last="shape">     <parent/> </service>
			<service name="Report" label_last="clipboard"> <parent/> </service>
			<service name="ROM"    label_last="clipboard"> <parent/> </service>
		</route>
	</start>

	<start name="fonts_fs" priority="-2" pkg="genodelabs/pkg/fonts_fs/2025-04-28">
		<route>
			<service name="ROM" label="config">
				<parent label="config -> managed/fonts"/>
			</service>
		</route>
	</start>

	<start name="wm_backdrop" priority="-2" pkg="genodelabs/pkg/sticks_blue_backdrop/2025-04-28">
		<route>
			<service name="Gui"> <parent label="backdrop"/> </service>
		</route>
		<config>
			<libc/>
			<vfs>
				<rom name="genode_logo.png"/>
				<rom name="grid.png"/>
				<rom name="sticks_blue.png"/>
			</vfs>
			<fill color="#223344" />
			<image png="sticks_blue.png" scale="zoom" anchor="bottom_left" alpha="200"/>
			<image png="grid.png"        tiled="yes" alpha="200" />
			<image png="genode_logo.png" anchor="bottom_right" alpha="150"
			                             xpos="-20" ypos="-20" />
		</config>
	</start>

</config>

I have successfully ported another more complex rust command line tool, but run into the same error. So I hope the above solves as a minimal reproducable example.

What am I missing in order to run the CLI tool in the “system shell” scenario?

Thanks!

Welcome to the forum!

The system shell executes a binary only if located under /bin. This policy is defined by the routing rules in its configuration.

I’d recommend creating a custom shell with the additions and customizations you want, e.g., including the ripgrep tool. The following pointers may hopefully be of help:

  • Section 5.1 of the Applications book (PDF) explains the inner workings of Unix-like subsystems in great detail. Specifically, on page 80, you can find the ROM-routing covered.
  • If you want skip reading and jump right into a working example of a customized shell, you may have a look at my unix project, which I use to take as a common basis for tailored Unix-like subsystems. E.g., my m68k-sdk is a much customized version of the unix project where I added a bunch of tools like sed, make, asm56000. You can follow the latter to see the places one needs to touch in order to integrate a custom tool: pkg archives, pkg runtime, and vfs. BTW, I’ve actually expanded the ROM routing rules in this project for allowing /libexec.
  • When creating a custom shell project besides your existing Goa project, you will need to configure a central depot dir where both projects can meet. For including your program into your shell, first goa export your program (installing it into the depot). Then your shell can incorporate it in its pkg archives.

Following the approach of creating your custom shell gives you three advantages over the ad-hoc attempt of sneaking your custom executable into the system shell. First, you can interactively test your program in its designated environment by running the shell via goa run. Second, as your custom shell is a Goa project you can readily package, publish, and reproduce it. I.e., you can test-drive it on a remote Sculpt machine via the Goa testbed (see Section 4.4 of the Applications book) or install it on Sculpt. Third, creating your own Unix-like environment is great fun. :smile:

2 Likes

Dear nfeske,

Thank you for your fast and detailed response!

I followed your advice and read through the applications book, reused the configuration of the m68k-sdk project expanded by the rg.tar (artifact of the atopias ripgrep-goa) providing the rg binary at /bin/rg. After a goa export, the dependencies are available for the VM running Sculpt.

Building a customized shell is fun indeed and executes every other command provided just fine. Upon executing the rust executable, the shell comes to an error though.

Upon executing it, I get the following output in the log component:

[init -> runtime -> m68k-sdk -> /bin/bash -> 1] Warning: unable to allocate linker-area region for binary
[init -> runtime -> m68k-sdk -> /bin/bash -> 1] Error: failed to load RX segment
[init -> runtime -> m68k-sdk -> /bin/bash -> 1] Error: dynamic linker failed to copy RW segment
Error: illegal READ at address 0x0 by pager_object: pd='init -> runtime -> m68k-sdk -> /bin/bash -> 1' thread='ep' ip=0xe4228

I extended the artifacts file at ripgrep-goa/artifacts at main · atopia/ripgrep-goa · GitHub by appending the line:

rg.tar: rg

I expected the same error as in the first post, since establishing the ROM session was already successful there, but getting a different error here really puzzles me.

Do you or atopia maybe have another hint?

The documentation and blog posts are really great and exiting reads, but for some errors my only approach is to constantly diff my build and configuration with existing examples like yours. I simply have not found an existing configuration for executing a rust binary in a shell on Sculpt/Genode though.

Thank you a lot for debugging this with me!

This means that the dynamic linker could not allocate the virtual-area your binary (rg) is linked to. This either means some else is there already, or the binary overlaps with something else.

Could you post the output of

objdump -p rg

here?

Thanks for assisting ssumpf:

user@localhost:~/goa-playground$ objdump -p ./atari/m68k-sdk/var/ripgrep-goa/bin/
./atari/m68k-sdk/var/ripgrep-goa/bin/x86_64/rg:     file format elf64-x86-64

Program Header:
  INTERP off    0x000000000023d3c8 vaddr 0x000000000123c3c8 paddr 0x000000000123c3c8 align 2**0
         filesz 0x000000000000000a memsz 0x000000000000000a flags r--
    LOAD off    0x0000000000001000 vaddr 0x0000000001000000 paddr 0x0000000001000000 align 2**12
         filesz 0x0000000000313630 memsz 0x0000000000313630 flags r-x
    LOAD off    0x0000000000315000 vaddr 0x0000000001314000 paddr 0x0000000001314000 align 2**12
         filesz 0x0000000000035c94 memsz 0x0000000000036058 flags rw-
 DYNAMIC off    0x000000000034ab20 vaddr 0x0000000001349b20 paddr 0x0000000001349b20 align 2**3
         filesz 0x0000000000000174 memsz 0x0000000000000174 flags rw-
EH_FRAME off    0x000000000031435c vaddr 0x000000000131335c paddr 0x000000000131335c align 2**2
         filesz 0x00000000000002d4 memsz 0x00000000000002d4 flags r--

Dynamic Section:
  NEEDED               libc.lib.so
  NEEDED               posix.lib.so
  HASH                 0x000000000123c3d8
  STRTAB               0x000000000123d190
  SYMTAB               0x000000000123c800
  STRSZ                0x00000000000004bf
  SYMENT               0x0000000000000018
  DEBUG                0x0000000000000000
  PLTGOT               0x0000000001347cc0
  PLTRELSZ             0x0000000000000030
  PLTREL               0x0000000000000007
  JMPREL               0x00000000012819c0
  RELA                 0x000000000123d650
  RELASZ               0x0000000000044370
  RELAENT              0x0000000000000018
  BIND_NOW             0x0000000000000000
  FLAGS_1              0x0000000008000001

Given that the rg tool executes fine outside the Unix-like shell environment, I suspect that the problem is related to execve. After forking the new process, execve instructs the dynamic linker to replace all higher-level dynamically loaded parts of the running program by the new program (and its respective libraries). To the lower-level parts (dynamic linker, libc, libm, posix) stay in tact. For this to work, all programs must be linked such that the lower-level parts are loaded first and in the same order. When looking at objdump -p bash I can see that the “Dynamic section” differs from rg in this respect:

Dynamic Section:
  NEEDED               libc.lib.so
  NEEDED               libm.lib.so
  NEEDED               posix.lib.so
  HASH                 0x00000000010e2cd8
  ...

The libm is missing from the lower-level part of rg, which makes it incompatible with process it is forked from (bash). To fix this, we need to find out how to tell Goa to link our libm in addition to libc when building a rust executable via cargo.

Looking at the goa build log, I notice the libm.lib.so in the build arguments in the cargo build process:
... -C 'link-arg=-l:libc.lib.so' -C 'link-arg=-l:libm.lib.so' -C 'link-arg=-l:posix.lib.so' ...

Furthermore, I have noticed, that the executable does have libm.so.6 in it’s “Dynamic Section”, if compiled without goa for Linux. Digging in the goa source code, I found an option to garbage collect unreferenced data. Removing it by issuing:

sed -i 's/lappend ldflags -gc-sections//g' /home/user/build/goa/goa-25.04/share/goa/lib/flags.tcl

Led to the section being available in the binary:

+ objdump -p /depot/user/bin/x86_64/ripgrep-goa/2023-08-16/rg

/depot/user/bin/x86_64/ripgrep-goa/2023-08-16/rg:     file format elf64-x86-64

Program Header:
  INTERP off    0x000000000020cbd0 vaddr 0x000000000120bbd0 paddr 0x000000000120bbd0 align 2**0
         filesz 0x000000000000000a memsz 0x000000000000000a flags r--
    LOAD off    0x0000000000001000 vaddr 0x0000000001000000 paddr 0x0000000001000000 align 2**12
         filesz 0x000000000027fd8c memsz 0x000000000027fd8c flags r-x
    LOAD off    0x0000000000281000 vaddr 0x0000000001280000 paddr 0x0000000001280000 align 2**12
         filesz 0x0000000000018674 memsz 0x0000000000018ad8 flags rw-
 DYNAMIC off    0x00000000002994f0 vaddr 0x00000000012984f0 paddr 0x00000000012984f0 align 2**3
         filesz 0x0000000000000184 memsz 0x0000000000000184 flags rw-
EH_FRAME off    0x00000000002809f0 vaddr 0x000000000127f9f0 paddr 0x000000000127f9f0 align 2**2
         filesz 0x000000000000039c memsz 0x000000000000039c flags r--

Dynamic Section:
  NEEDED               libc.lib.so
  NEEDED               libm.lib.so
  NEEDED               posix.lib.so
  HASH                 0x000000000120bbe0
  STRTAB               0x000000000120ce20
  SYMTAB               0x000000000120c1f0
  STRSZ                0x00000000000005ab
  SYMENT               0x0000000000000018
  DEBUG                0x0000000000000000
  PLTGOT               0x00000000012969c0
  PLTRELSZ             0x0000000000000030
  PLTREL               0x0000000000000007
  JMPREL               0x000000000122f348
  RELA                 0x000000000120d3d0
  RELASZ               0x0000000000021f78
  RELAENT              0x0000000000000018
  BIND_NOW             0x0000000000000000
  FLAGS_1              0x0000000008000001

Anyway, the error remains the same:

[init -> runtime -> m68k-sdk -> /bin/bash -> 7] Warning: invalid executable binary format: /rw/depot/user/bin/x86_64/ripgrep-goa/2023-08-16/rg
[init -> runtime -> m68k-sdk -> vfs] /bin/bash:/rw/depot/user/bin/x86_64/ripgrep-goa/2023-08-16/rg: cannot execute binary file: Exec format error

Do you have any other hints I could check? I’m eager to solve the problem :slight_smile:

Kind regards

It doesn’t look like as if there is anything wrong with the binary to me. As @nfeske stated it might be related to fork/execve. We will have to look into this.

@r-freilach: We have found the problem and I just published a new libc with a hotfix for Sculpt 25.04. For your m68k-sdk example it should be sufficient to add ssumpf/src/libc/2025-07-09 to the m68k-sdk/pkg/m68k-sdl/archives file. Let me know how it goes. rg works here in bash.

2 Likes

Dear ssumpf,

Thank you for your efforts and very fast response times! It works just flawlessly now :slight_smile:
(Both rg in bash and the application I’m currently porting.)

Hopefully I can add my application to the genode world repository soon, so you and others may use it too.

3 Likes

Dear ssumpf,

Thank you again for your help. Sadly the day after my message, I seem to have made a change, that made the same error message reappear. Since you obviously have enough work to do, I did not want to ask for help again right away, but debugged since then. :confused:

To narrow it down, now I simply use the pkg goa-playground/unix at master · nfeske/goa-playground · GitHub with the string genodelabs/src/libc replaced by ssumpf/src/libc/2025-07-09 in the file unix/pkg/unix/archives.

Copying everything from var/depot to /depot on my Sculpt installation, I can successfully run the terminal component by adding this to the deploy file:

	<start name="fs_rom" priority="-2" pkg="jschlatow/pkg/fs_rom/2025-04-09"/>

	<start name="unix" priority="-2" pkg="user/pkg/unix/2023-08-16">
		<route>
			<service name="Gui"> <child name="wm"/> </service>
			<service name="File_system" label_prefix="config ->"> <parent identity="config"/> </service>
			<service name="File_system" label_prefix="report ->"> <parent identity="report"/> </service>
			<service name="File_system" label_prefix="target ->"> <child name="default_fs_rw"/> </service>
			<service name="File_system" label_prefix="fonts ->">  <child name="fonts_fs"/> </service>
			<service name="ROM" label="vimrc"> <parent label="config -> vimrc"/> </service>
			<service name="ROM" label="clipboard"> <child name="wm"/> </service>
			<service name="Report" label="clipboard"> <child name="wm"/> </service>
			<service name="RM"> <parent/> </service>
			<service name="ROM" label_prefix="/bin/bash -> /rw/depot/user/bin/x86_64"> <child name="fs_rom"/> </service>
		</route>
	</start>

and calling /rw/depot/user/bin/x86_64/ripgrep-goa/2023-08-16/rg in the terminal gives me:

[init -> runtime -> unix -> /bin/bash -> 1] Warning: invalid executable binary format: /rw/depot/user/bin/x86_64/ripgrep-goa/2023-08-16/rg

I don’t know why it worked before and I can not make it work again after more than 50 tries already.

So my questions would be:

  • Several binary dependencies get downloaded by goa during the build process. Do I need to manually rebuild any of these?
  • If another version of src/libc is present in the /depot, could this interfere?
  • Does the order of dependencies in the archives file play a role?

Kind regards

That should not be necessary. Does it work, when you goa run the unix scenario on your Linux host?

Not if the archives are configured correctly (i.e. mention the correct version). You can double-check by looking at the generated routes for the deployed unix scenario in /config/managed/runtime.

Yes, the order actually matters. The mentioned archives are scanned for the the required ROM modules line-by-line. The last match takes precedence.