129 lines
3.7 KiB
Go
129 lines
3.7 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package asm
|
|
|
|
import (
|
|
"bytes"
|
|
"debug/elf"
|
|
"encoding/binary"
|
|
"testing"
|
|
|
|
"sourcedock.dev/petrbalvin/gasm-devkit/parser"
|
|
)
|
|
|
|
// TestELFRISCVObjectDataRelocation checks that a symbol-valued DATA field
|
|
// ("DATA s+0(SB)/8, $other(SB)") reaches the RISC-V ELF object as a
|
|
// .rela.data entry: an R_RISCV_64 (R_RISCV_32 for a width-4 field) at the
|
|
// field's offset within .data, against the named symbol, external targets
|
|
// included.
|
|
func TestELFRISCVObjectDataRelocation(t *testing.T) {
|
|
f, errs := parser.Parse("t_riscv64.s", `#include "textflag.h"
|
|
TEXT ·Keep(SB), NOSPLIT, $0-0
|
|
RET
|
|
GLOBL holder(SB), NOPTR, $32
|
|
DATA holder+0(SB)/8, $·Keep+5(SB)
|
|
DATA holder+8(SB)/8, $holder(SB)
|
|
DATA holder+16(SB)/8, $extvar(SB)
|
|
DATA holder+24(SB)/4, $Keep(SB)
|
|
`)
|
|
if len(errs) > 0 {
|
|
t.Fatalf("parse: %v", errs)
|
|
}
|
|
img, err := AssembleFileRISCV(f)
|
|
if err != nil {
|
|
t.Fatalf("AssembleFileRISCV: %v", err)
|
|
}
|
|
obj, err := img.ELFRISCVObject()
|
|
if err != nil {
|
|
t.Fatalf("ELFRISCVObject: %v", err)
|
|
}
|
|
checkELFSectionAccounting(t, obj)
|
|
ef, err := elf.NewFile(bytes.NewReader(obj))
|
|
if err != nil {
|
|
t.Fatalf("parse emitted object: %v", err)
|
|
}
|
|
defer ef.Close()
|
|
relaData := ef.Section(".rela.data")
|
|
if relaData == nil {
|
|
t.Fatal("missing .rela.data section")
|
|
}
|
|
if relaData.Type != elf.SHT_RELA {
|
|
t.Errorf(".rela.data type = %v, want SHT_RELA", relaData.Type)
|
|
}
|
|
if relaData.Link == 0 || ef.Sections[relaData.Link].Name != ".symtab" {
|
|
t.Errorf(".rela.data sh_link = %d, want the .symtab index", relaData.Link)
|
|
}
|
|
if ef.Sections[relaData.Info].Name != ".data" {
|
|
t.Errorf(".rela.data sh_info = %d, want the .data index", relaData.Info)
|
|
}
|
|
relas, err := relaData.Data()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var got []struct {
|
|
off uint64
|
|
sym uint32
|
|
typ uint32
|
|
addend int64
|
|
}
|
|
for i := 0; i+24 <= len(relas); i += 24 {
|
|
got = append(got, struct {
|
|
off uint64
|
|
sym uint32
|
|
typ uint32
|
|
addend int64
|
|
}{
|
|
off: binary.LittleEndian.Uint64(relas[i:]),
|
|
// r_info packs the type in the low dword and the symbol index
|
|
// in the high dword.
|
|
typ: binary.LittleEndian.Uint32(relas[i+8:]),
|
|
sym: binary.LittleEndian.Uint32(relas[i+12:]),
|
|
addend: int64(binary.LittleEndian.Uint64(relas[i+16:])),
|
|
})
|
|
}
|
|
// debug/elf hides the table's null entry, so raw index s names syms[s-1].
|
|
syms, err := ef.Symbols()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
name := func(idx uint32) string {
|
|
if idx >= 1 && int(idx) <= len(syms) {
|
|
return syms[idx-1].Name
|
|
}
|
|
return ""
|
|
}
|
|
// The offsets are data-section-relative: the field's DATA offset plus
|
|
// the symbol's position in .data (the layout aligns each symbol to 16).
|
|
base := uint64(0)
|
|
for _, d := range img.DataSyms {
|
|
if d.Name == "holder" {
|
|
base = uint64(d.Offset)
|
|
}
|
|
}
|
|
want := []struct {
|
|
off uint64
|
|
typ uint32
|
|
addend int64
|
|
target string
|
|
}{
|
|
{off: base + 0, typ: uint32(elf.R_RISCV_64), addend: 5, target: "Keep"},
|
|
{off: base + 8, typ: uint32(elf.R_RISCV_64), addend: 0, target: "holder"},
|
|
{off: base + 16, typ: uint32(elf.R_RISCV_64), addend: 0, target: "extvar"},
|
|
{off: base + 24, typ: uint32(elf.R_RISCV_32), addend: 0, target: "Keep"},
|
|
}
|
|
if len(got) != len(want) {
|
|
t.Fatalf(".rela.data entries = %d, want %d", len(got), len(want))
|
|
}
|
|
for i, w := range want {
|
|
g := got[i]
|
|
if g.off != w.off || g.typ != w.typ || g.addend != w.addend {
|
|
t.Errorf("entry %d = {off %d typ %d addend %d}, want {off %d typ %d addend %d}",
|
|
i, g.off, g.typ, g.addend, w.off, w.typ, w.addend)
|
|
}
|
|
if n := name(g.sym); n != w.target {
|
|
t.Errorf("entry %d names %q, want %q", i, n, w.target)
|
|
}
|
|
}
|
|
}
|