Open craig65535 opened 1 year ago
Isn't it possible to just use Dlsym(lib, "__dso_handle")?
Note that it returns a pointer to the value.
That returns an error (symbol not found
). It's not in libSystem.B.dylib. As I said I'm not 100% familiar with this, but I think it's something the linker generates - a dynamic executable would have its own __dso_handle
.
You are correct that it's unique to each dynamic object according to this StackOverflow post. I'm not sure how to reference it in Go. I am not familiar with this API either so more research needs to be done
Something that would help here is the ability to lookup a given symbol/C-style global variable in the current process. But I don't know how feasible that is without full cgo.
How would you do this with Cgo?
unsafe.Pointer(&C.__dso_handle)
So, cgo outputs Go code that links to the C symbol.
// Code generated by cmd/cgo; DO NOT EDIT.
//line /Users/jarrettkuklis/Documents/GolandProjects/purego/test/main.go:1:1
package main
//#include <os/log.h>
import _ "unsafe"
import (
"fmt"
"unsafe"
)
func main() {
fmt.Printf("%x\n", unsafe.Pointer(&( /*line :11:37*/*_Cvar___dso_handle /*line :11:50*/)))
}
// --- _cgo_types.go ---
type _Ctype_struct_mach_header struct {
magic _Ctype_uint32_t
cputype _Ctype_int32_t
cpusubtype _Ctype_int32_t
filetype _Ctype_uint32_t
ncmds _Ctype_uint32_t
sizeofcmds _Ctype_uint32_t
flags _Ctype_uint32_t
}
//go:linkname __cgo___dso_handle __dso_handle
//go:cgo_import_static __dso_handle
var __cgo___dso_handle byte
var _Cvar___dso_handle *_Ctype_struct_mach_header = (*_Ctype_struct_mach_header)(unsafe.Pointer(&__cgo___dso_handle))
If you go ahead and copy this into a plan Go code you'll get something like this.
package main
import (
"fmt"
"unsafe"
)
type mach_header struct {
magic uint32
cputype int32
cpusubtype int32
filetype uint32
ncmds uint32
sizeofcmds uint32
flags uint32
}
//go:linkname __cgo___dso_handle __dso_handle
//go:cgo_import_static __dso_handle
var __cgo___dso_handle byte
var _Cvar___dso_handle *mach_header = (*mach_header)(unsafe.Pointer(&__cgo___dso_handle))
func main() {
fmt.Printf("%x\n", unsafe.Pointer(_Cvar___dso_handle))
}
Although this doesn't compile because the //go:cgo_import_static
is only available in Cgo. The cgo docs state that it's only used for -linkmode=external
. We want the Go linker so we set it to internal and replace the comment with //go:cgo_import_dynamic
but the single symbol version of this is not accessible either without cgo. So then I replaced it with the three argument version (//go:cgo_import_dynamic __dso_handle __dso_handle ""
).
Now it finally compiles but points to some really low address (0x680000) when Cgo version points to a higher address (0x104280000). And when you try to dereference the go version it will SIGSEGV. I think this is an issue with how //go:cgo_import_dynamic
is implemented because on amd64 it doesn't work at all _Cvar___dso_handle
points to nil.
@craig65535
I have had some time to look into this. The reason trying to load it with purego doesn't work is because _dso_handle
symbol is add by the macOS C linker and Go's linker doesn't add it.
However, the data for this symbol is still able to be obtained since it is just the information about the currently running macho file. This information can be grabbed using just the stdlib:
package main
import (
"debug/macho"
"fmt"
"log"
"os"
"structs"
)
// https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h
type Mach_header struct {
_ structs.HostLayout // ensures it matches system layout
Magic uint32
Cputype int32
Cpusubtype int32
Filetype uint32
Ncmds uint32
Sizeofcmds uint32
Flags uint32
_ uint32 // reserved
}
func main() {
dir, err := os.Executable()
if err != nil {
log.Fatal(err)
}
ex, err := macho.Open(dir) // or OpenFat if it is a fat macho
if err != nil {
log.Fatal(err)
}
header := Mach_header{
Magic: ex.FileHeader.Magic,
Cputype: int32(ex.FileHeader.Cpu),
Cpusubtype: int32(ex.FileHeader.SubCpu),
Filetype: uint32(ex.FileHeader.Type),
Ncmds: ex.FileHeader.Ncmd,
Sizeofcmds: ex.FileHeader.Cmdsz,
Flags: ex.FileHeader.Flags,
}
fmt.Println(header)
}
I checked and this results in the exact same data as the _dso_handle
symbol. You can then use &header
as the argument to _os_log_internal
. I believe this solves your issue. If so, please close this issue. Thank you.
@TotallyGamerJet I missed your update at first - thank you! I will try this and reply back here.
Hello, I thought I would try using purego to interface with macOS's unified logging framework. However I've hit a snag.
The unified logger uses a set of macros like
os_log_debug
that ultimately call_os_log_internal
. This is exported by libSystem.B.dylib - so far so good. However, the first argument to that function is&__dso_handle
, which is defined in C headers asI'm not sure how to resolve this symbol. It's not exported by libSystem.B.dylib, but rather it's specific to the running executable. I don't understand this fully, but I've read that it's actually dynamically generated by the linker.
Is there a way to reference this symbol with purego, or is this not possible?