Unlinking

I got the idea this morning that I could actually “unlink” a WASM program. By this I mean that I could remove the bindings generated by the compiler between function names and code and substitute new bindings that referred to external code. Doing this with the standard library/interpreter of many languages would be awesome.

This approach, if successful, has a couple of benefits. First, it means that the number of bytes in a program is far less. It’s not clear that this saves much besides the network time, but it can’t hurt. The program still has to link once it gets downloaded, so it might not buy you much time–in fact it could be worse although I seriously doubt it.

The second benefit is more important to me. It allows me to provide my own version the go starded library or the python standard library or whatever. This should offer some help on the security front as well as allowing us to detect problematic binaries early: they simply won’t link if they, for example, have reference in them to os.Open() in the go standard library, since my version will not have that function.

My first big tutorial on parigot will be using the “vvv” example. This example is for “Vinny’s Vintage Vinyl”. I got the first semi-real client side of that working today. Here is the proto definition:

syntax = "proto3";
package demo.vvv;
option go_package = "demo/vvv/proto/gen/demo/vvv;vvv";
import "google/protobuf/timestamp.proto";

// parigot: WasmServiceName=VinnysStore
service Store {
  rpc BestOfAllTime(BestOfAllTimeRequest) returns(BestOfAllTimeResponse);
  rpc Revenue(RevenueRequest) returns (RevenueResponse);
  rpc SoldItem(SoldItemRequest) returns (SoldItemResponse);
}

message SoldItemRequest {
  float amount = 1;
  google.protobuf.Timestamp when = 2;
}

message SoldItemResponse {
}


message Media {
  string title = 1;
}

message BestOfAllTimeRequest {
}

message BestOfAllTimeResponse {
  Media media = 1;
}

message RevenueRequest{
  string date = 1;
}
message RevenueResponse{
  float revenue = 1;
}

Here is the main:

func main() {
	flag.Parse()
	logger, err := log.LocateLog()
	if err != nil {
		//abandon ship, can't get logger to even say what happened
		abi.Exit(1)
	}
	logger.LogDebug("starting up", "")
	vinnysStore, err := vvv.LocateStore()
	if err != nil {
		logger.LogFatal("could not find the store:"+err.Error(), "")
	}
	t := abi.Now()
	logger.LogDebug(fmt.Sprintf("time is now %d ", t), "")
	vinnysStore.SoldItem(&vvv.SoldItemRequest{
		Amount: 14.99,
		When:   timestamppb.New(time.Now()),
	})
	best, err := vinnysStore.BestOfAllTime()
	if err != nil {
		logger.LogFatal("could not reach the BestOfAllTime call:"+err.Error(), "")
	}
	logger.LogDebug("best of all time:"+best.GetMedia().GetTitle(), "")
}

I call my binary transformation program “surgery”. I added an “operation” called unlink and tried it on the wasm file resulting from the above.

Here is the output stats from surgery that are relevant here:

2022/10/17 15:45:08      surgery: input file: 1.8M bytes, output file: 209.5K bytes
2022/10/17 15:45:08      surgery: total time 1.7 seconds

Yay!