-
Notifications
You must be signed in to change notification settings - Fork 678
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add support for reading auxv from Go runtime
Extract auxv related code to own file, and use runtime provided auxv fetching function starting from go 1.21. The old file based code is kept for old go version and tests. Signed-off-by: Paul Cacheux <paul.cacheux@datadoghq.com>
- Loading branch information
1 parent
b3432fb
commit fbff7db
Showing
4 changed files
with
175 additions
and
48 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package internal | ||
|
||
import ( | ||
"errors" | ||
"io" | ||
_ "unsafe" | ||
) | ||
|
||
type auxvPairReader interface { | ||
Close() error | ||
ReadAuxvPair() (uint64, uint64, error) | ||
} | ||
|
||
// See https://elixir.bootlin.com/linux/v6.5.5/source/include/uapi/linux/auxvec.h | ||
const ( | ||
_AT_NULL = 0 // End of vector | ||
_AT_SYSINFO_EHDR = 33 // Offset to vDSO blob in process image | ||
) | ||
|
||
//go:linkname runtime_getAuxv runtime.getAuxv | ||
func runtime_getAuxv() []uintptr | ||
|
||
type auxvRuntimeReader struct { | ||
data []uintptr | ||
index int | ||
} | ||
|
||
func (r *auxvRuntimeReader) Close() error { | ||
return nil | ||
} | ||
|
||
func (r *auxvRuntimeReader) ReadAuxvPair() (uint64, uint64, error) { | ||
if r.index >= len(r.data)+2 { | ||
return 0, 0, io.EOF | ||
} | ||
|
||
// we manually add the (_AT_NULL, _AT_NULL) pair at the end | ||
// that is not provided by the go runtime | ||
var tag, value uintptr | ||
if r.index+1 < len(r.data) { | ||
tag, value = r.data[r.index], r.data[r.index+1] | ||
} else { | ||
tag, value = _AT_NULL, _AT_NULL | ||
} | ||
r.index += 2 | ||
return uint64(tag), uint64(value), nil | ||
} | ||
|
||
func newAuxvRuntimeReader() (auxvPairReader, error) { | ||
data := runtime_getAuxv() | ||
|
||
if len(data)%2 != 0 { | ||
return nil, errors.New("malformed auxv passed from runtime") | ||
} | ||
|
||
return &auxvRuntimeReader{ | ||
data: data, | ||
index: 0, | ||
}, nil | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package internal | ||
|
||
import ( | ||
"encoding/binary" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"testing" | ||
"unsafe" | ||
|
||
"github.com/cilium/ebpf/internal/unix" | ||
) | ||
|
||
type auxvFileReader struct { | ||
file *os.File | ||
order binary.ByteOrder | ||
uintptrIs32bits bool | ||
} | ||
|
||
func (r *auxvFileReader) Close() error { | ||
return r.file.Close() | ||
} | ||
|
||
type auxvPair32 struct { | ||
Tag, Value uint32 | ||
} | ||
|
||
type auxvPair64 struct { | ||
Tag, Value uint64 | ||
} | ||
|
||
func (r *auxvFileReader) ReadAuxvPair() (tag, value uint64, _ error) { | ||
if r.uintptrIs32bits { | ||
var aux auxvPair32 | ||
if err := binary.Read(r.file, r.order, &aux); err != nil { | ||
return 0, 0, fmt.Errorf("reading auxv entry: %w", err) | ||
} | ||
return uint64(aux.Tag), uint64(aux.Value), nil | ||
} | ||
|
||
var aux auxvPair64 | ||
if err := binary.Read(r.file, r.order, &aux); err != nil { | ||
return 0, 0, fmt.Errorf("reading auxv entry: %w", err) | ||
} | ||
return aux.Tag, aux.Value, nil | ||
} | ||
|
||
func newAuxFileReader(path string, order binary.ByteOrder, uintptrIs32bits bool) (auxvPairReader, error) { | ||
// Read data from the auxiliary vector, which is normally passed directly | ||
// to the process. Go does not expose that data before go 1.21, so we must read it from procfs. | ||
// https://man7.org/linux/man-pages/man3/getauxval.3.html | ||
av, err := os.Open(path) | ||
if errors.Is(err, unix.EACCES) { | ||
return nil, fmt.Errorf("opening auxv: %w (process may not be dumpable due to file capabilities)", err) | ||
} | ||
if err != nil { | ||
return nil, fmt.Errorf("opening auxv: %w", err) | ||
} | ||
|
||
return &auxvFileReader{ | ||
file: av, | ||
order: order, | ||
uintptrIs32bits: uintptrIs32bits, | ||
}, nil | ||
} | ||
|
||
func newDefaultAuxvFileReader() (auxvPairReader, error) { | ||
const uintptrIs32bits = unsafe.Sizeof((uintptr)(0)) == 4 | ||
return newAuxFileReader("/proc/self/auxv", NativeEndian, uintptrIs32bits) | ||
} | ||
|
||
func TestAuxvBothSourcesEqual(t *testing.T) { | ||
runtimeBased, err := newAuxvRuntimeReader() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
fileBased, err := newDefaultAuxvFileReader() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
for { | ||
runtimeTag, runtimeValue, err := runtimeBased.ReadAuxvPair() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
fileTag, fileValue, err := fileBased.ReadAuxvPair() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if runtimeTag != fileTag { | ||
t.Errorf("mismatching tags: runtime=%v, file=%v", runtimeTag, fileTag) | ||
} | ||
|
||
if runtimeValue != fileValue { | ||
t.Errorf("mismatching values: runtime=%v, file=%v", runtimeValue, fileValue) | ||
} | ||
|
||
if runtimeTag == _AT_NULL { | ||
break | ||
} | ||
} | ||
} |
This file contains 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
This file contains 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