You don't have to do it at all if you don't want to.
You can want to do it with someone even if you don't desire it yourself.
It can be embarassing and intimate to do it with someone new.
You can be casual with it and do it with anyone.
You can keep it between just two of you.
You can do it in public, as a performance, if you want.
You can never admit you do it at all and keep it private.
You can like doing it and talk about it all the time even if you don't do it very much.
Who you choose to do it with is your choice.
You can do it in groups, or just one on one.
You can be there for other people and not participate.
Sometimes, people will pressure you do to it, because "everyone else is."
It can be bad for you if you're pressured into it.
It can be good for you to push yourself to experience new things.
Nobody has to know if you do it.
If someone films you doing it and posts it online without asking that can be violating.
I don't think this means Sex and Karaoke are somehow special in this respect, just that they're both intimate, social, deeply human things we do together and any ways that people can be good or evil or controlling or liberated by anything like that, this would apply to.
I recently found someone 1 talking about Alive Internet Theory and it's such a good name. As contrasted with the more existentially horrifying thought of Dead Internet Theory, where 'everyone on the Internet is a bot'.
I like the idea that we can choose to focus on what is (still) alive about the Internet. It's what the "Small Web" 2 and IndieWeb idea is about. That there will always be people who want to share what they know, what they're thinking, what they had for breakfast. Pictures of their cats.
And I like the idea that it's not the exclusive purview of the world of nerdy self-hosting and technologists that see any centralization, any reliance on big tech as a failure. The Alive Internet can still exist in spaces that are associated with the Dead Internet. You're primarly a target for advertising there. But the whole human is always still there, and we can still connect with each other through any media we are given.
For thousands of years the best we had was written communication that could take months or even years to arrive. The Internet still connects us in a fundamentally magic way, if we look even a few decades back in history. Publishing is instant, and global. We can write, but also sing and dance and show each other our lives in high-definition video.
So, we need to be in the open, as people, and visibly real. I guess this post is a bit of my contribution, declaring that I'm a real person, and this is my little corner of the Internet. Hello! I am Alive. 3
3
Well, for the foreseeable future. I'm not sure this will be archived forever, but if it is, consider the post date.
The Internet only dies to the bots when there's none of us left, so… stick around. It's nice here sometimes.
This isn't a fully formed thought, but I was thinking about some of the ways I have seen people write about their experience of using AI tools.
That they feel like it's "addicting", that there's a "rush" or pleasant feeling when it works. That it doesn't always work, so you have to keep trying.
This sounds an awful lot like gambling addiction. Or any other conditioning based on intermittant reinforcement. Which is more effective conditioning than consistent reinforcement.
It has nothing to do with how useful the LLM, the diffusion model, etc. actually is. The thrill of pulling the lever to see if it will pay off is what drives it.
That's why people can like it and not feel like it's any good at anything. That's why productivity measurements can tell us that these systems don't help that much, or might even hurt productivity, and people still want them. Still want to use them.
Sacrificing your professional appearance, your unique voice in writing—your compentence—at the altar of LLM supremacy is perhaps less dire than gambling away your retirement savings and mortgage payments. But I'd think it would still be seen as negative—some sort of harm.
Gambling isn't illegal, and it's not even the fundamental act of wagering that is the source of the problems with it. It's the conditioning, the addiction, the harms of what it takes away from people.
LLMs seem similar. Using an LLM itself isn't a problem or inherently harmful; relying only on it is the concern. Constantly thinking about using it, preferring it to anything else. Avoiding using your own voice, your own skills, your own professional taste in order to get another hit from the magic word machine.
I'm not sure we have a perfect solution in society for how we mitigate the harms of gambling. Some countries ban it, stigmatize it, etc. Some places are mixed; certain kinds of it are allowed (sports betting, video slots, government run lotteries) and others are prohibited (table games, private lotteries).
But I don't think there's any society that doesn't have an opinion on the harms and how to deal with them. Discussion needs to happen, and it needs to be in the broader context of society. Whether the topic is LLMs, diffusion models, other generative systems, simpler machine learning tools, or plain automation.
A society of degenerate gamblers is not desirable. A society where everyone relies on LLMs is a similar sort of extreme. I'd argue both are failures in similar ways. A society that has failed to adapt and integrate a specific technology with human nature and behavior in a way that is healthy.
I'm not sure isolating raw LLM use to some equivalent of a 'den of sin' like a casino is a good strategy, but limiting how they can be used, offered to the public, trained, etc. seems not very different than regulating how games of chance can be sold, played, and manufactured.
1
"Relinquishment" of the technology, meaning intentionally NOT further developing it or pursuing it. Like non-proliferation for nuclear weapons, the threat of the re-development and use of it always lingers, where a societal and legal framework of regulations/taboos forms a stronger foundation for healthy control.
I don't like relinquishment 1 as an approach to potential harms that can come from a technology, but a complete lassez-faire approach is not the only other option.
Computers store information. A representation of information. All of computer science stems from the insight that we can build machines which store, transform, and output information.
Whether it's punch cards, magnetic tape, wire-wrapped memory, or moden flash, DRAM, and optical discs. The representation of it—physically, as well as logically—is something imposed by a person onto the medium.
It's easy to think that there's some inherent naturalness to writing systems, or to binary representation in computers. The latin alphabet, cuneiform, and all other writing systems are ways people have imposed structure on physical objects in order to use them for storing ideas.
The mathematical insight that binary is as simple a model for structured information as you can construct has served computer science well. It's universal, simple, and infinitely flexible. It's useful. But the representation of information is not the information itself.
Or, rather. The information—the structure and organization of it—is possible to observe, but the meaning of it doesn't exist in the representation. The meaning exists in the human that reads it.
Words on a page, or spoken aloud do not magically conjure ideas into the minds of others. The language needs to be shared. No matter how obvious, simple, and straightforward the sentence is, I will not understand someone speaking Hindi. I can recognize that it's a language, that there is structure. That it's being spoken by a person, or that there is a complex arrangement of symbols that must be a writing system. But the meaning will not appear in my mind despite the information—seemingly—containing it.
And it's the same with any other information processing and storage system we have invented. The computer does not know the meaning of what it is doing. It does not know anything. The representation 0x41 0x20 0x68 0x61 0x6d might be a sequence of small numbers, it might be one large number (279716585837), or might be the text 'A ham'`. The knowledge of how to interpret it (the 'type', if you think like a programmer) is required to know which meaning to impose on the data.
And while we can 'tell' the computer that this area of memory, or this file field, database column, etc. has a specific type, we still fundamentally rely on the human seeing the representation (transformed as we have instructed) to derive meaning.
Which leads to the thought that the underlying representation doesn't contain the meaning. A distinction that seems very subtle and not that interesting except to philosophers.
Except.
Large language models, and other generative AI systems, have started to make that distinction more and more clear. They have entered into the zeitgeist of business and culture.
And they are able to produce complex derived representations. They consume everything ever written, ever drawn or photographed, and they compute and transform it. But the meaning isn't what is being modeled. 'Merely' the representation.
Words coming one after the other, following English vocabulary and grammar. Related to the prompt that solicited them. But the meaning, or lack of it, is imposed by the human that is reading them.
The meaning doesn't exist in the model. Despite all the talk by machine learning papers of finding and encoding more and more of the 'semantics', it is still fundamentally limited to processing the representation.
Meaning exists only in the communication, in the audience, the reader. You might argue that the LLM training process is where the meaning is interpreted by the system. I'm not sure. An LLM isn't all that similar to a brain. The 'magic' of them is their size and scale, that they don't need to be built to model knowledge or anything more sophisticated than 'next token'.
It is impressive how much 'merely' having an excellent way to statistically reproduce written language results in something people are willing to believe is thinking—that it is responding to their questions.
That it can replace their real friends or relationships with 'better' ones.
That comes from the people thinking it. The meaning is external to the model.
Poetic phrasing aside, I think this observation is becoming clearer and clearer to the public as they interact more with these LLM and other generative AI systems.
Alone, they do not produce meaning, art, creative expression, or otherwise. They do not know things, they cannot think.
Meaning is what we impose on a work, what we see in the information in front of us. Merely producing more information to try to find meaning in does not solve the problem, it makes it worse.
I don't have experience with generative AI systems, they're all... integrated in tools I don't use or behind some corporate interface I'm not going to touch with a 10-foot pole.
But like any curious technologist, I read about what people are doing, what they're saying they use it for, how they think you should use it best, etc.
And I'm starting to feel—not obsolete, as the proponents of generative AI systems predict—but like everyone is missing the point of writing software.
Yes, source code is a tool, a means to an end. A way of expressing the precise outlines of what software does. It's the interface between the human and the computer. The interface. Anything else built up on top of it is the software's interface to a user. And that's ultimately very important. But source code is for people to read and write.
So when you tell me that you're writing software where you can use a generative AI tool to spit out so much "boilerplate," I don't see that as a useful expenditure of engineering effort. Not to build the tool that can generate the boilerplate, not to build software with that much boilerplate required.
The goal, in my mind, across all of software engineering has been to eliminate the inessential, the boilerplate, the tedium. Papering over it with a tool is different than creating an abstraction for it.
So using generative AI feels like a rejection of the idea of writing better software. A rejection of an engineering approach. It's capitulating to the complexity and refusing to manage it, refusing to make it comprehensible, predictable, or reliable. Since the state space is so large, 'giving up' and getting an approximation of what you wanted and then massaging it into as good as you can get may be pragmatic, but it's certainly not engineering.
In the long term, if generative AI / LLM systems really are the future of software development, they're not going to be writing in human-centric programming languages, I'd expect. Why bother with all that abstraction and tooling. Just have them output a binary the CPU can run. All the structure is still in the binary, and you eliminate the tooling in-between. Programming languages are for people. LLMs deal in 'tokens'. Why wouldn't you train an LLM to produce an ELF binary from a prompt?
If that's really the end game? I'm not sure how we could really trust any software built that way. You can test it, perhaps close to exhaustively on specific cases that you care about. But without demanding an explanation of every aspect of the software, every machine instruction, you can't trust it. And even then, can a person verify the explanation? For any sufficiently useful software artifact, I expect not.
Maybe that's the goal, that software engineering is no longer necessary, that this whole technology is a paradigm shift where technology works like it does in Star Trek or the wildest imaginings of Sci Fi authors. But that seems too optimistic, too willing to throw away everything else for a world that we would hope comes to be.
More moderate aspirations predict LLMs to be tools used by the engineers, and while that's a sensible and not-quite-worldchanging prediction. I've yet to see a reason why the complexity and resource costs of LLMs justify themselves.
Building better abstractions to solve your problem. Eliminating the need for boilerplate and tedium in souce code. That is just as—if not more—powerful. And it doesn't require a cloud subscription to a GPU farm, just the good software engineers you're already paying.
Linux is a networking swiss army knife, in my experience. There's many ways to get things configured to accomplish your goals, and plenty of choices you have to make to get there.
Distributions have different tools and configuration strategies, and software that has to interact with networking has its own perspective on what it needs to do.
Basic Bridging
Linux (the kernel) supports ethernet bridging, and this support has been around for a long time. Basically, if you have two ethernet ports, bridging connects them together the same way an ethernet switch would.
In either case, a packet addressed to the broadcast MAC address (like an ARP request) that is received on one port will be forwarded to the other port.
1
When there is no such forwarding it may still flood/broadcast the packet across all the ports, and it will look at source MAC adddresses to attempt to learn the forwarding information it needs to do the job efficiently.
When a packet is addressed to a specific MAC address, the bridge (or switch) will lookup in its MAC tables the correct port to forward that (unicast) packet to. 1
With the iproute2 tooling, which is the current recommended way to interact with the Linux kernel networking subsystems, a bridge can be created like this:
ip link add type bridge br0
ip link set up br0
And adding an interface to the bridge looks like this:
ip link set dev eth0 master br0
ip link set up eth0
These two commands first create a bridge interface br0 then join eth0 to be a member of the bridge.
Adding another interface, like eth1 to the bridge will allow ethernet frames to traverse it (if the interfaces are up).
2
Though calling it 'physical' is quickly going to be metaphorical, as many kinds of interfaces that you can add to a bridge are purely software constructs.
Note that no IP layer addressing is required for ethernet bridging, as it functions at the MAC/physical layer. 2
Additionally, if the host you create the bridge on should have an IP address on the network, that address should be assigned to the bridge interface, not on any member interface. An interface that is a member of a bridge is operating only the lower layers of the networking stack; IP addressing and anything 'higher' up needs to be built on top of the bridge interface.
3
"On link" refers to how the IP address is reached. For ethernet, an on link address will be resolved to a MAC address using ARP, then the packet can is addressed to the MAC address discovered.
This configuration allows a host connected via either eth0 or eth1 to access the bridge host on 192.168.100.254. Both eth0 and eth1 are considered "on link"3 for that subnet, so without routing, only hosts that also use the 192.168.100.0/24 address range would be able to communicate.
To list bridges configured on the system:
ip link ls type bridge
And to list all the interfaces attached to a bridge:
ip link ls master [bridge interface]
Using NetworkManager (nmcli)
Most Linux distributions provide some tooling to configure networking at boot or otherwise make it persistent.
4
Other distributions use things like NetPlan, or distribution specific configuration files. I'm documenting the nmcli commands because they're useful to me beacause that's what I use at work.
On RedHat-like distributions, this is done through NetworkManager, and I'm going to assume this is most useful on a server and stick to the nmcli tool for examples. 4
Creating a bridge connection:
nmcli con add type bridge ifname br0
nmcli con up bridge-br0
The connection name will be automatically generated if not supplied, and for bridge connections, defaults to bridge-[interface name].
To add an ethernet interface to the bridge, you need to know the name of the network manager connection that controls it. In this example it is the same as the ethernet interface name, but it may not always be.
nmcli con modify eth0 connection.master br0 connection.slave-type bridge
nmcli con up eth0
libvirt Networking
5
libvirt can do many many things, including LXC container managment, connecting to other hypervisor types, and more. I am focused on the relatively simple usecase of virtualization in a datacenter without introducting software-defined networking tools, or direct access to hardware interfaces with SR-IOV or PCI passthrough.
libvirt is a baseline set of managment tools for Linux-based hypervisor systems. It is of primary interest for its ability to manage qemu-kvm virtualization. 5
A virtual machine is configured to connect to a virtual network on the hypervisor host. This is typically done by creating a TUN/TAP veth interface that connects into the virtual machine software (qemu-kvm) and joining that interface to a Linux bridge interface.
6
Routing is enabled with the forwarding sysctl parameter on the interface. This is often set as the default for systems that are intended to act as routers, but this can be set individually on each interface that should participate in routing.
This can result in either a routed network 6, where no non-vnet interfaces are bridge members and the hypervisor needs to act as a router for that network to be accessible to the outside world (or an isolated network if that is not desirable).
Or, if a physical interface on the hypervisor is added to the bridge, the virtual machines will appear on that link just like the hypervisor would. The hypervisor can have an IP address configured on the bridge, if desired, which would allow it to reach the virtual machines and the outside network the underlying physical interface is attached to, but it need not.
VLANs
7
This applies to switches as well as hosts that do bridging. Switches typically have specalized hardware to do forwarding/bridging and many ethernet ports. In large infrastructure environments VLANs also represent a way to avoid a lot of physical maintenance because they can be reconfigured without changing the actual cabling plugged into the network devices.
Needing to dedicate a physical ethernet port and its entire capacity to a single part of the network is sometimes too much. Either because the subnets will not have that much separate traffic on them or there are two many networks to interconnect relative to the inventory of ports available. 7
A VLAN is a logically separate ethernet broadcast domain. What hardware and software uses to create that is based on 802.1Q which specifies how to tag ethernet frames with a VLAN ID.
8
Thus the name "virtual" LAN.
This ID represents the specific network segment a frame is for, and therefore allows multiple network segments to be interconnected on the same physical infrastructure. 8
Typically individual hosts (as opposed to routers, firewalls, switches, or other network equipment) do not participate in VLAN insfrastructure. A switch port will be configured to be a member of the appropriate VLAN and the end device that plugs into it does not have to have any knowledge of how the larger network is organized.
However, a virtualization hypervisor is not an ordinary host, so it is useful to have it handle tagged traffic; ethernet bridging that it does is already playing the role of a virtualized switch for the VMs it hosts.
Linux, of course, supports VLAN tagging. Using iproute2 adding an interface that handles traffic for a vlan tag seen on an physical interface looks like this:
ip link add type vlan link eth0 vlan1234 vlan id 1234
This command creates a vlan1234 network interface on 'top' of eth0 which intercepts 802.1Q tagged packets with VLAN ID 1234 and presents them to the OS.
These vlan interfaces can be added as a port on a bridge like any other interface. This then allows multiple external networks to be carried on a single interface and exposed to different VMs inside the hypervisor.
libvirt and VLANs
As described above, libvirt does not interact with the underlying VLAN topology, all the configuration needs to take place outside the libvirt managed networks, and all libvirt is responsible for is connecting the VM to the bridge.
This can be done with a physical interface that sees tagged traffic, and vlan interfaces can be created within a virtual machine to have connectivity to multiple VLANs without creating separate virtual interfaces in the VM.
This does not however, filter the tagged traffic and the VM would be able to access any tagged traffic the hypervisor is sent, not just traffic for the VLAN IDs that it is configured to use.
If you fully trust you guest VMs, and the traffic overhead is not a problem, this does work. But VM1 need only create a VLAN interface for VID 4321 in order to access External Host 2 in the above diagram. The hypervisor does not have any control over which VLANs a guest has access to.
VLAN Filtering
The underlying Linux bridging code does have a feature to do this filtering. Aptly enough, it's enabled by setting vlan_filtering on the bridge interface:
ip link set dev br0 type bridge vlan_filtering 1
9
The full details are a little complex, and there is also a default pvid on the bridge interface that all ports get, to maintain a basically similar behavior for untagged traffic to the case where vlan_filtering is turned off.
This then lets the bridge interface act much more like a "managed switch" where each interface that is attached as a bridge port can be configured with a list of VLANs to participate in, along with a primary VID (for traffic received untagged on that port) as well as to specify which VID should be untagged when sent to that port. 9
ip link set dev eth0 master br0
bridge vlan add vid 1234 dev eth0
10
Use the bridge -compressvlans vlan command on its own to list the bridge ports and their VLAN configurations.
The above would add eth0 as a bridge port to br0 and specify that only VID 1234 is allowed to traverse that port, and it is a tagged VLAN. 10
The bridge interface itself also can be configured with VLANs that it participates in (for the hypervisor/host to connect to.)
In this configuration, the VM would not actually be connected to the VLAN because the vnet1 interface in the hypervisor is not configured to allow that VID (because vlan_filtering is enabled on virbr0), so even with a vlan interface configured within the VM, the VLAN traffic is not forwarded.
We can create the same situation as creating the vlan interface in the hypevisor by adding the vnet1 port with the 1234 vlan as a PVID and untagged:
bridge vlan add vid 1234 dev vnet1 pvid untagged
And this ends up working exactly the same as the separate vlan1234 interface being a member of the bridge on the hypervisor.
11
This is why I noted the -compressvlans argument earlier, without it each vlan assigned to a port is output on its own line.
In the scenario where the hypervisor is expected to handle dozens or hundreds of VLANs for different virtual machines this can provide a tremendous simplicfication of configuration, and the physical port (eth0) can even be configured with a range of VIDs connecting it to the swtiching 'backbone' of the network. 11
bridge vlan add vid 1-4094 dev eth0
Using nmcli
Enable filtering on a bridge interface:
nmcli con modfiy bridge-br0 bridge.vlan-filtering 1
Set the bridge port to be a member of all vlans (tagged):
nmcli con modify eth0 bridge-port.vlans "1-4094"
12
Typically the vnet interfaces are managed by libvirt, not NetworkManager
Set a specific port to be an untagged member of specific vlan: 12
nmcli con modify vnet1 bridge-port.vlans "1234 pvid untagged"
VLAN filtering and libvirt
I already mentioned once that libvirt doesn't support managing the vlan portion of a Linux bridge's configuration for virtual hosts.
But that doesn't mean we have to do this all manually. libvirt does support hook scripts that are invoked during the lifecycle of a guest, and we can use those along with custom guest metadata (XML) to configure vlan membership, so we can send vlans into a guest tagged or untagged, all connected to one bridge interface.
A hook script needs to be present in /etc/libvirt/hooks/qemu.d/ that is executable. I have writtten a hook script that reads domain XML metadata.
Partially! The MAC address of the vnetX interfaces created by libvirt is set to fe:… relative to the MAC address of the interface within the VM. This is due to a quirk of Linux bridging where the MAC address of the bridge is selected as the lowest numerical MAC address of any member port. This can cause the bridge MAC to change when virtual machines are started and stopped, so libvirt ensures a numerically high prefix is used on vnetX interfaces to prevent this.
This metadata is used by the hook script to line up the vnetX interface with the interface within the guest, by matching the MAC address. 13
The script then run a series of bridge vlan commands to set the VID(s) that should be configured. The untagged property will set the pvid and untagged flags for that VLAN ID, making it the 'native' VLAN for the interface from the perspective of the guest.
Creating a VM
So how do you piece that all together if it requires custom metadata?
#!/bin/bashset-xe
exportLIBVIRT_DEFAULT_URI=qemu:///system
NAME="$1"MEMORY="$2"DISK="$3"# Create guest from disk image. Do not start it.virt-install\--import\--networkmodel=virtio,mac=52:54:00:4b:32:36,bridge=br-trunk\--memory"$MEMORY"\--name"$NAME"\--disk"$DISK"\--osinfodetect=on\--noautoconsole\--noreboot
METADATA_XML=$(cat<<HERE<vlans> <vlan interface="52:54:00:4b:32:36" vid="1234"/> <vlan interface="52:54:00:4b:32:36" vid="1235" untagged="yes" /></vlans>HERE)virshmetadata"$NAME"http://evolvecellular.com/libvirt/vlan--keyvlans--set"$METADATA_XML"virshstart"$NAME"
14
You still need to customize the VLAN XML metadata and the bridge name in the network argument. And obviously the MAC address should be unique for each VM you create.
As an example, if you have an existing virtual machine image prepared (e.g. from virt-sysprep or virt-clone) the above script outlines the steps to create and start a virtual machine with VLAN configuration. 14
15
If you can assume all your virtual machines will only have a single network interface, I suppose you could leave out the interface property and alter the hook script to simplify how the interface is selected.
Of note, this does rely on having a MAC address that you know so you can't rely on libvirt to generate a random one automatically for you without further scripting the creation to dump the XML and then match the interface some other way. 15
Appendix: Visualizing what you've done
16
This isn't unique to VLAN bridging and filtering necessarily. A Linux hypervisor with a couple dozen virtual machines on different networks will have dozens of network interfaces visible to ip addr ls and friends.
This can all be a bit opaque if you're just sitting at the command line of a hypervisor and trying to look at what is connected to where. 16
I have created a script which reads through the JSON output of the iproute2 tools 17 and outputs a simple graphviz/dot diagram showing how bridges, vlan interfaces, vlan vlan filtering, bond ports, and libvirt virtual machines are interconnected.
17
The JSON output is immensely useful, and you can get it with -j. Combined with the -d details option, it's a much better interface for programmatically interacting with the tools. (Screen scraping the output is explicitly discouraged in the documentation)
Containers are pretty useful, like a chroot that's been given extra powers, to isolate networking, user ids, pids, and all sorts of other things. And without the overhead of a whole virtual machine. However, that does limit them to running software that would actually run on your actual machine.
I use a ppc64le machine as a daily driver, so lots of things are 'awkard', if they assume that x86_64 is the only thing that exists. However, there is a tool that can emulate other CPUs and run programs in a different instruction set: qemu. You don't—it turns out—even need to emulate an entire hardware platform, or need special virtualization-specific support (that's just for speed). A qemu user emulation tool can run binaries for a foreign architecture (but otherwise compatible with your OS kernel) by emulating the CPU and translating system calls.
User emulation is already a lot like a container, and can make things like certain kinds of cross compiling or development work easier (as well as making it possible to run proprietary software on your architecture of choice).
On Gentoo, you need to have app-emulation/qemu set up to include the architectures you want to emulate in the QEMU_USER_TARGETS use_expand variable. (And for using this within containers, you want the +static-user useflag set as well).
We can then run the two native ones, and see the expected output.
$ ./hello-ppc64le-dyn
Hello from main()$ ./hello-ppc64le
Hello from main()$ ./hello-x86_64
bash: ./hello-x86_64: cannot execute binary file: Exec format error$ ./hello-x86_64-dyn
bash: ./hello-x86_64-dyn: cannot execute binary file: Exec format error
The qemu-x86_64 program can run these, however.
$ qemu-x86_64./hello-x86_64
Hello from main()$ qemu-x86_64./hello-x86_64-dyn
Hello from main()
(Assuming the cross compilation setup has the right environment for the dynamically linked version)
The Linux kernel has a feature that allows configuring an interpreter for these binaries, in much the same way that you can embed a #! (shebang) line into a script: binfmt_misc.
This can be configured through various mechanisms on different distros, but on Gentoo the openrc init system supplies a /etc/init.d/binfmt service to enable that mapping by dropping a file into /etc/binfmt.d/.
So with that file in place and /etc/init.d/binfmt start (run as root, and possibly enabled as a boot service).
We can then just run the non-native executables 'normally':
$ ./hello-x86_64
Hello from main()$ ./hello-x86_64-dyn
Hello from main()
This is pretty cool, and useful on its own, but you quickly have to figure out how to install dependencies and deal with a project's build system to really use it for much other than toy examples.
But we can use the static binaries directly in a container, if we want.
$ $buildahbud--platform=linux/amd64-tlocalhost/hello:x86_64Containerfile.x86_64
STEP 1/3: FROM scratchSTEP 2/3: COPY hello-x86_64-dyn /STEP 3/3: CMD [ "/hello-x86_64-dyn" ]COMMIT localhost/hello:x86_64-dynGetting image source signaturesCopying blob 14266541c9b2 doneCopying config 6e4c2c5dfb doneWriting manifest to image destinationStoring signatures--> 6e4c2c5dfbd[Warning] one or more build args were not consumed: [TARGETARCH TARGETOS TARGETPLATFORM]Successfully tagged localhost/hello:x86_64-dyn6e4c2c5dfbd8eb283d51b4929229561fd10a1df4439d97646d6b3a29355ea95e
$ podmanrun--rm-itlocalhost/hello:x86_64-dyn
qemu-x86_64: Could not open '/lib64/ld-linux-x86-64.so.2': No such file or directory
We get something different. The dynamic executable requires the dynamic linker within the container, and we only put the executable file there.
But we are able to execute the binaries through qemu-x86_64 just fine, even within the container, whose own root filesystem doesn't have the emulator to suport our native CPU.
But the dynamic linker would work if a whole chroot-style image was present in the container.
Doing a simple podman run --rm -it docker.io/library/ubuntu:latest will pull an appropriate image for the architecture we're running on, and we can see that if we use skopeo to inspect the image locally:
A vocabulary type is a term I've come across a few times in discussions of programming; "Vocabulary types" represent a category of programming language types.
As I understand the term, a vocabulary type is one that is appropriate for use in interfaces; Whether used as a parameter, returned from a function, or otherwise exposed outside the immediate module (unit of composition of the software).
These are types that are part of the interface between the programmer and the code; they are a part of the vocabulary the programmer 'speaks' when writing software.
What is a vocabulary type?
Stepping back a bit, what is "vocabulary" in a programming context?
Within any one programming language, the vocabulary of that language includes its primitive operations and types, as well as its standard libraries. Not every programmer will learn the whole vocabulary for a language they use, just as not every English speaker will know all the words in English.
When talking specifically about types, a programming language's built-in types and the types of its standard library will be a baseline vocabulary.
There are differences between programming languages in this respect, but it is also true that most programming languages share a wide range of overlap, which starts to form the picture of vocabulary types outside of the concrete context of a single programming language, and into the abstract across all programming languages or for programming in general to have a specific vocabulary (of types).
Programming is not restricted to using only the language-provided types, so abstractions used by programmers in multiple languages, and within common problem domains also develop into an extended vocabulary. Not all programming touches on the same ideas, so not all programs will contain all vocabulary. These abstractions which appear in multiple programming languages form the basis of what a 'programming dictionary' would contain. 1
1
Are there any programming dictionaries of this sort? Do books like the Gang of Four attempt to capture some parts of this?
Is a programming language's manual at least partially a dictionary of it's vocabulary?
Is every (built-in) programming language type a "vocabulary type"?
The term would not be useful if all types were vocabulary types. But are all built-in types even vocabulary types? A type that is always available, widespread in use, and which is useful may not form a part of the vocabulary, even within a single software project.
As an example, while in python functools.partial is a type (not simply a function which partially applies anther, but an object constructed by calling it), I would argue it is not a "vocabulary type" because you would not specify it in a parameter list, return value, or as part of the structure of a library or module. The vocabulary in python would be to identify the type as "callable".
In this example, typing.Callable would be used to name a parameter or return type and the callable built-in function can be used to test if an object conforms to that requirement at runtime.
Likewise in C++, the concrete type of std::cout has a complex name involving std::basic_ostream<char> and would not normally be used in parameter lists or return value. The std::ostream type is the vocabulary type that should be used.
Vocabulary types are important because they are the preferred names for type specifications, both in code itself and when programmers are communicating with each other.
What makes a good Vocabulary Type?
It should immediately be somewhat obvious that any primitive types in a language make good candidates for a vocabulary type; almost by default anything the language supplies as built-in (whether as part of the standard library or within the language itself) will be useful to communicate between modules and between programmers when discussing the program's structure.
These types are generally stable, as robust and well-tested as possible, and strictly defined. The types provided by the language environment are necessarily always available as well. A 'good' vocabulary type should be easily accessible. In the same way that using obscure language makes working collaboratively more difficult to a broader audience, using an obscure or idiosyncratic type as a vocabulary type in your software would make it less accessible to the broadest audience of programmers. 2
2
This is not always the goal, whether in writing or in programming. An essay published for a technical audience will not use the same vocabulary as an essay published in a broadly circulated newspaper or magazine. And likewise, a specialized piece of software for complex problem domain will likely be better served by equally specialized vocabulary as compared with general programming examples that would be used for a wider audience of programmers (like educational materials)
The set of vocabulary types defines the 'dialect' of programming that you are doing. Beyond whichever specific language (whether C++, Rust, Python, Haskell, &c…), physics simulations will have a common vocabulary. Both in the regular sense of having specialized terminology and jargon, and in the sense of vocabulary types with similar purposes. e.g. A 3d vector in a Cartesian space, an IPv4 address, a payment method, &c…
Can we then design better languages and software libraries by identifying the vocabulary our modules speak and ensuring we have a coherent dialect within our software? Is this internally helpful even for the maintenance and upkeep of the module even if the vocabulary types are not exposed to the consuming software? (i.e. if the API interface uses only primitive/language-provided types, does modelling the module using an internal vocabulary improve it?)
Concrete Examples
Details in various programming languages, of course, will still differ, but I think candidates for these sorts of types are:
Simple
fixed-size integers (modular/unsigned)
Examples: uint32_t, unsigned int
fixed-size integers (two's-complement/signed)
Examples: int16_t, int, long int
IEEE754 float32 and float64 (float128? 80-bit x87 floats?)
Examples: float, double
byte (an uninterpreted unit of memory that is 8-bits in size)
I'll admit a C++ bias in the above, as that feels to me the most precise way to name the ideas to me.
Further constraining to abstract types that aren't concrete programming language primitives at all can further simplify and restrict this idea of universal vocabulary types.
Perhaps the success of JSON (now standardized in RFC 8259) is just how clearly it constrains data to a universal set of broadly applicable vocabulary types:
number
Integral or floating point, exact integers are only guaranteed interoperable within the INT32 range, and floating point representation is likely only compatible if exact float64 is used.
boolean
A value usable in a conditional context selecting between two alternatives true and false.
null
A value that is present but otherwise empty/unspecified. (Analogous to an empty optional<T>, or a nullptr-valued T*
string
A value representing a sequence of Unicode codepoints in the printable range.
array
A sequence of any type, (homgenous in that the any type is the corresponding representation).
object
A mapping from string values to any.
Though rarely will a particular use cosntrain itself so heavily unless the goal is truly broad interoperability.
vocabulary type
A category of programming language types that is appropriate for use in interfaces; it used as a parameter, returned from a function, or otherwise visible outside the immediate module (unit of composition of the software).
I live where a power outage, while not extremely common, happens often enough that I should have anything remotely valuable or sensitive hooked up to a UPS (not merely a surge protector). However, I recently relocated a bunch of my electronics, and my Talos II-based workstation currently lives in my home office without the benefit of a UPS.
I had a power outage that lasted only about 5 minutes, and appeared to be a straight blackout/cut followed by restoration. (Maintenance, cutover of a damaged circuit, whatever, no idea what happened)
This then, unfortunately, left my system unwilling to boot. The BMC LED on the mainboard would turn on at a quick flash when applying power, but the power buttons did not respond and the system would not completely turn on.
This implied the BMC, which controls power sequencing and bringup, was not functioning. After some troubleshooting to try to get a network connection into the BMC, it became apparent the BMC was at fault, somehow.
Firstly, I needed to get a serial connection to the BMC itself, which required ordering a bracket. It appears that no vendor actually specifies what pinout their brackets use, and at least on the Amazon listing I did order, the reviewer stated the pinout incorrectly.
Some jumper wires later, I got the serial connection hooked up and was able to see that on applying power, I got nothing but the SoC reset output. No u-boot output, nothing except the SoC continuously resetting.
So now I needed to find a way to reflash u-boot. The above-linked Wiki page shows a link to ASpeed (the SoC manufacturer) for a download of a "socflash" utility. However, in their desire to be as unfriendly as possible to anyone who won't pay them money, they've removed that tool from public download, and reserved it only for developer accounts (which require a sales contact to get).
There's a one-sentence statement that you can use flashrom to write the chip, with an external programmer, so that seemed like my best option, as I have a Segger J-Link which flashrom supports as a SPI programmer.
Now, to find the information I need on how to flash that chip. Since this is all fairly open, the chip is socketed, which means I was able to remove the flash chip for the BMC, and look up the pinout for it, and match that up against the flashrom documentation for using the J-link as a programmer.
Flash Chip Pin
J-Link Pin
Comments
15 (SI)
5 (TDI)
8 (SO)
13 (TDO)
15 (SCLK)
9 (TCK)
7 (CS#)
15 (RESET)
2 (VCC)
1 (VTREF)
Also connected to 3.3V bench supply
10 (GND)
4 (GND)
Also of note is that when connecting pins on the J-link using an IDC cable, the numbering is mirrored relative to the key on the connector. (Pin numbering is for the pins on the J-Link itself)
With this connected, I was able to convince flashrom to read the existing image from the flash and verify that connectivity was working and the chip was recognized:
$ ./flashrom/flashrom -p jlink_spi -r bmc-rom
flashrom v1.1-rc1-115-g61e16e5-dirty on Linux 5.3.0-gentoo (x86_64)
flashrom is free software, get the source code at https://flashrom.org
Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Macronix flash chip "MX25L25635F/MX25L25645G" (32768 kB, SPI) on jlink_spi.
Reading flash... done.
Now I just needed the flash image and some idea of how to write it.
Downloading the 1.07 firmware from the wiki, I ended up with a few files, and didn't know what to do with them.
Thankfully a few folks on the #talos-workstation channel on FreeNode were able to sort me out, providing the necessary context for me to know that the image-bmc file in the firmware download is the entire flash image combined, along with providing a link to the kernel DTS (https://git.anastas.io/dormito/br-blackbird-external/src/branch/raw-first-pass/board/bangBMC/blackbird.dts) which shows the range/offsets of the flash regions on the chip.
(Thanks to mearon, dormito, and hanetzer)
Thus I was able to construct a flashrom layout file:
Caveat with the above, I only tested writing to the u-boot section, so please use at your own risk and with a full understanding of what you are trying to accomplish.
And with some trepidation, write the u-boot section with the following command:
$ ./flashrom/flashrom -p jlink_spi -l bmc.layout -i u-boot -w image-bmc
flashrom v1.1-rc1-115-g61e16e5-dirty on Linux 5.3.0-gentoo (x86_64)
flashrom is free software, get the source code at https://flashrom.org
Using region: "u-boot".
Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Macronix flash chip "MX25L25635F/MX25L25645G" (32768 kB, SPI) on jlink_spi.
Reading old flash chip contents... done.
Erasing and writing flash chip... Erase/write done.
Verifying flash... VERIFIED.
Leading to success, thankfully.
A bit of careful re-seating of the chip into the motherboard socket and I was able to get a fully booted BMC, and the rest of my system again, no worse for wear otherwise.
Firefox's Quantum update provides some significant improvements in performance and other general underlying technology things (that I don't fully understand, as I'm not a Mozilla developer). However, in their eagerness to throw away as much work from the community as possible, they crippled the ability of addons to cutomize the interface, instead turning addons into nothing more than glorified greasemonky scripts.
A lot of the interface customizations I want are possible with userChrome.css modifications, which I'm relatively comfortable with doing, it's limited to the specific profile in use, etc. It'd be better if there was a way to automate or any sensible documentation on that, but it's open source, you read the code and figure it out.
On the other hand, Mozilla seems hell-bent on completely removing the ability to change the keyboard shortcuts in any way. There are half-baked solutions that can only change shortcuts within the page, or only work if you're not on a "secure" page, whatever the hell that is. (i.e. it's fine to change the browser's behavior in GMail, but addons aren't allow to change the browser's behavior on the addon page... as if "inconsistently and partially" customizable is what anyone actually wants)
There's some information on how to modify the sources to change keyboard shortcuts, but it doesn't appear to have been updated for the latest releases. Firefox 64, in my case.
Now, since I'm running Gentoo, it's not a terribly difficult thing to patch the source, User patches are well supported, so all I need is a patch that changes the shortcuts how I want. Still not actually configurable, but at least I can make it behave.
@@ -104,8 +104,8 @@ These items have the same accesskey but
<!ENTITY listAllTabs.label "List all tabs">
<!ENTITY tabCmd.label "New Tab">
-<!ENTITY tabCmd.accesskey "T">
-<!ENTITY tabCmd.commandkey "t">
+<!ENTITY tabCmd.accesskey "N">
+<!ENTITY tabCmd.commandkey "n">
<!-- LOCALIZATION NOTE (openLocationCmd.label): "Open Location" is only
displayed on OS X, and only on windows that aren't main browser windows, or
when there are no windows but Firefox is still running. -->
What I want is for the New Tab action to be bound to C-T not C-N and to have New Window (on the off chance I ever use it) moved to C-S-N. (Unbinding entirely the Undo Close Window accelerator. Who uses that?)
Adding rocker mouse guestures would be nice, but I can live without the rest of the changes I've used in previous versions. (Mostly holdovers from when I used Opera.)