cd /news/developer-tools/fix-crostini-antigravity · home topics developer-tools article
[ARTICLE · art-9514] src=gist.github.com ↗ pub= topic=developer-tools verified=true sentiment=· neutral

fix crostini antigravity

This article provides a Python script that patches the "Antigravity" Linux ARM64 binary for use in Chrome OS's Crostini environment. The patch modifies TCMalloc memory allocation parameters to use a 39-bit virtual address space instead of the default 48-bit, which fixes an `MmapAligned()` crash. The script works by scanning for specific instruction patterns in the binary's `google_malloc` section and rewriting them to adjust address masks and bounds.

read5 min views14 publishedMay 22, 2026

patch_va39

  This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.

Learn more about bidirectional Unicode characters

Show hidden characters

#!/usr/bin/env python3

"""

VA39 patch for the Antigravity linux_arm64 binary on Chromebook/Crostini.

Scans instruction patterns to patch TCMalloc for a 39-bit

virtual address space instead of the default 48-bit, fixing

the MmapAligned() crash. """

import

hashlib

import

shutil

import

struct

import

sys

from

pathlib

import

Path

src

Path ( sys . argv [ 1 ] if

len ( sys . argv )

1

else

"/usr/local/bin/agy"

) dst

Path ( str ( src ) +

".va39"

) if

not

src . exists

(): raise

SystemExit ( f"Input binary does not exist: { src } "

) print ( f"Input binary : { src } "

) print ( f"SHA256 in : { hashlib . sha256 ( src . read_bytes ()). hexdigest

()
}

\n "

) shutil . copyfile ( src , dst

) data

bytearray ( dst . read_bytes

()) def

get ( off

): return

struct . unpack_from ( "<I" , data , off )[ 0

] def

put ( off , word

): struct . pack_into ( "<I" , data , off , word

) lo , hi

0 , len ( data

) if

data [: 4

b" \x7f ELF" :

e_shoff

struct . unpack_from ( "<Q" , data , 40 )[ 0

] e_shentsize

struct . unpack_from ( "<H" , data , 58 )[ 0

] e_shnum

struct . unpack_from ( "<H" , data , 60 )[ 0

] e_shstrndx

struct . unpack_from ( "<H" , data , 62 )[ 0

] shstr_base

e_shoff

e_shstrndx

e_shentsize

shstr_off

struct . unpack_from ( "<Q" , data , shstr_base

24 )[ 0

] for

i

in

range ( e_shnum

): base

e_shoff

i

e_shentsize

sh_name

struct . unpack_from ( "<I" , data , base )[ 0

] sh_offset

struct . unpack_from ( "<Q" , data , base

24 )[ 0

] sh_size

struct . unpack_from ( "<Q" , data , base

32 )[ 0

] try :

nend

data . index ( b" \x00 " , shstr_off

sh_name

) section

data [ shstr_off

sh_name : nend ]. decode ( "utf-8"

) if

section

"google_malloc" :

lo , hi

sh_offset , sh_offset

sh_size

print ( f"Found google_malloc section: 0x { lo :x }

  • 0x { hi :x } "

) break

except

ValueError :

pass

ubfx_count , lsl_count

0 , 0

for

off

in

range ( lo , hi , 4

): w

get ( off

) if ( w

&

0x7F800000

0x53000000 :

immr , imms

( w

16 ) &

0x3F , ( w

10 ) &

0x3F

if

immr

42

and

imms

44 :

put ( off , ( w

&

~ (( 0x3F

<<

16 ) | ( 0x3F

<<

10 ))) | ( 35

<<

16 ) | ( 37

<<

10

)) ubfx_count

+=

1

elif

immr

22

and

imms

21 :

put ( off , ( w

&

~ (( 0x3F

<<

16 ) | ( 0x3F

<<

10 ))) | ( 29

<<

16 ) | ( 28

<<

10

)) lsl_count

+=

1

print

(
f"[1] ubfx patches : 
{

ubfx_count } \n

    lsl patches  : 
{

lsl_count } "

) mask_count

0

for

off

in

range ( lo , hi

4 , 4

): if

get ( off

0x92D3800A

and

get ( off

4

0xF2E0000A :

put ( off , 0x9280000A

) put ( off

4 , 0xD35DFD4A

) mask_count

+=

1

print

(
f"[2] Random mask  : 
{

mask_count } "

) mmap_count

0

for

off

in

range ( lo , hi , 4

): if

get ( off

0xF2E00029 :

put ( off , 0xD3596129

) mmap_count

+=

1

print

(
f"[3] MmapAligned  : 
{

mmap_count } "

) word_rewrites

{ 0xD2C20009 : 0xD2C00409 , 0xD2C2000A : 0xD2C0040A , 0xF2C20008 : 0xF2DFF408 ,

0xF2C20009 : 0xF2DFF409 , 0xD2C10009 : 0xD2C00209 , 0xD2C1000A : 0xD2C0020A ,

0xF2C38008 : 0xF2DFF708 , 0xF2C38009 : 0xF2DFF709 , 0x92560A6C : 0x925D0A6C ,

0x92560A6A : 0x925D0A6A , 0xD2C3000D : 0xD2C0060D , 0xD2C3000C : 0xD2C0060C ,

0xD2C08008 : 0xD2C00108 ,

} counts

{ old : 0

for

old

in

word_rewrites

} for

off

in

range ( lo , hi , 4

): w

get ( off

) if

w

in

word_rewrites :

put ( off , word_rewrites [ w

]) counts [ w ] +=

1

print

(
f"[4] Tag constants: 
{

sum ( counts . values

())
}

words rewritten"

) dst . write_bytes ( data

) dst . chmod ( 0o755

) print ( f" \n SHA256 out : { hashlib . sha256 ( dst . read_bytes ()). hexdigest

()
}

"

) print ( f"Output : { dst } \n "

) if

ubfx_count

0

or

mask_count

0 :

print ( "WARNING: No patches applied - binary structure may have changed."

) else :

print ( "Patch looks complete. To replace original and test:"

) print ( f" sudo mv { dst

}
 
{

src } "

) print ( f"
{ src

}
 --version"
)
── more in #developer-tools 4 stories · sorted by recency
── more on @chromebook 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/fix-crostini-antigra…] indexed:0 read:5min 2026-05-22 ·