feat(asm): emit linkable ELF and Mach-O objects with external symbols

Assisted-by: Qwen 3.8 Max Preview
This commit is contained in:
2026-07-16 20:52:20 +02:00
parent b914c0e390
commit 900c9772b1
9 changed files with 1165 additions and 56 deletions
+40 -9
View File
@@ -28,7 +28,7 @@ import (
// version is the release version, stamped at build time via
// -ldflags "-X main.version=…" (defaulting to the current release).
var version = "0.10.0"
var version = "0.11.0"
func main() {
if len(os.Args) < 2 {
@@ -93,6 +93,7 @@ Examples:
gasm fmt reformat every .s below the current directory
gasm lint go-flac/*.s run static checks over the kernels
gasm asm -o k.bin kern_amd64.s
gasm asm --format elf -o k.o kern_amd64.s
`, version)
}
@@ -339,17 +340,24 @@ hover, document symbols, diagnostics and semantic-token highlighting.
}
func cmdAsm(args []string) int {
fs := newCommand("asm", "gasm asm [-o out.bin] <file>", `
fs := newCommand("asm", "gasm asm [--format raw|elf|macho] [-o out] <file>", `
Assemble FILE (amd64) without the Go toolchain: every TEXT function is
encoded to machine code — scalar, VEX/AVX2 and EVEX/AVX-512 instructions,
FP/SP frame mapping, local labels and file-local static symbols (GLOBL/DATA)
resolved RIP-relative — and printed as a hex dump. With -o the concatenated
image (functions followed by the data section) is written to a file instead.
resolved RIP-relative — and printed as a hex dump.
With -o the output is written to a file instead. The --format flag selects
what is written: raw (the default) concatenates the functions and the data
section into one self-consistent image; elf and macho emit a relocatable
object (.text/.data sections, a symbol table and one PC32 relocation per
static-symbol reference) that links with the system toolchain — references
to symbols no GLOBL in the file defines become undefined external symbols.
`)
out := fs.String("o", "", "write the concatenated machine code to this file")
out := fs.String("o", "", "write the output to this file")
format := fs.String("format", "raw", "output format: raw (concatenated image), elf or macho (relocatable object)")
fs.Parse(args)
if fs.NArg() != 1 {
fmt.Fprintln(os.Stderr, "usage: gasm asm [-o out.bin] <file>")
fmt.Fprintln(os.Stderr, "usage: gasm asm [--format raw|elf|macho] [-o out] <file>")
return 2
}
path := fs.Arg(0)
@@ -420,12 +428,35 @@ image (functions followed by the data section) is written to a file instead.
}
}
if *out != "" {
all := img.Bytes()
if err := os.WriteFile(*out, all, 0o644); err != nil {
var obj []byte
var err error
var kind string
switch *format {
case "raw":
if len(img.Externals) > 0 {
fmt.Fprintf(os.Stderr, "gasm asm: external symbol %q needs an object file (use --format elf or --format macho)\n", img.Externals[0])
return 1
}
obj, kind = img.Bytes(), "raw image"
case "elf":
obj, err = img.ELFObject()
kind = "ELF object"
case "macho":
obj, err = img.MachOObject()
kind = "Mach-O object"
default:
fmt.Fprintf(os.Stderr, "gasm asm: unknown format %q (want raw, elf or macho)\n", *format)
return 2
}
if err != nil {
fmt.Fprintln(os.Stderr, "gasm asm:", err)
return 1
}
fmt.Printf("wrote %d bytes to %s\n", len(all), *out)
if err := os.WriteFile(*out, obj, 0o644); err != nil {
fmt.Fprintln(os.Stderr, "gasm asm:", err)
return 1
}
fmt.Printf("wrote %d bytes to %s (%s)\n", len(obj), *out, kind)
}
return 0
}