#c #go #arm #cgo #clips
#c #Вперед #arm #cgo #клипы
Вопрос:
Что я делаю
Я создаю приложение go, которое должно запускаться на процессоре CortexA9 и использует 3 разные общие библиотеки C / C (я использую оболочку C для вызова кода C ), при вызове одной из них (механизм правил Clips http://www.clipsrules.net /) Я случайно получаю SIGSEGV, Segmentation fault
, сама библиотека довольно старая и хорошо протестирована, также тот же код хорошо работает на amd64 arch. Место, где возникает ошибка, выглядит довольно случайным, если я перепишу код, чтобы избежать определенных строк кода, это произойдет в середине другой функции.
Как я создаю библиотеку
Я использую arm-buildroot-linux-gnueabi
набор инструментов и arm-buildroot-linux-gnueabihf-gcc
как компилятор C, поэтому он построен с помощью этого CFLAGS: -Wall -std=c99 -O3 -fno-strict-aliasing -fPIC -march=armv7-a -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard --sysroot=${some path}
Если я создаю Clips как отдельный двоичный файл (с помощью этого набора инструментов) и выполняю некоторые правила для него, он работает хорошо.
Как я создаю приложение golang
Я создаю приложение на docker golang: 1.16-buster, где находится мой toolchanin
go clean amp;amp; env CGO_CFLAGS="-march=armv7-a -mfpu=neon -mfloat-abi=hard -mtune=cortex-a9 --sysroot={soem path}" CGO_ENABLED=1 GOOS="linux" GOARM=7 GOARCH=arm go build --tags=with_libs,arm -o ./out/$app_name .
CC установлен в arm-buildroot-linux-gnueabihf-gcc
Для добавления библиотеки в проект у меня есть arm.go
файл:
// build arm
package clips
// #cgo LDFLAGS: -L../../libs/arm -lclips -lm -Wl,-rpath -Wl,../libs/arm
import "C"
Что вы ожидали увидеть?
Я не ожидаю увидеть никаких неожиданных сигналов.
Что вы увидели вместо этого?
Во время работы приложения, когда я выполняю вызовы cgo из библиотеки so, я получаю это (из gdb): Thread 1 "myapp-arm" received signal SIGSEGV, Segmentation fault.
gdb: обратная трассировка:
#0 0x76d6fa6c in PrintTemplateFact (theEnv=0x4c8b90, logicalName=0x76d79184 "FactPPForm", theFact=0x4c6dd8, seperateLines=0, ignoreDefaults=0) at /myapp/clips_source/tmpltutl.c:387
#1 0x76cbfa70 in PrintFact (theEnv=0x4c8b90, logicalName=0x76d79184 "FactPPForm", factPtr=0x4c6dd8, seperateLines=0, ignoreDefaults=0) at /myapp/clips_source/factmngr.c:445
#2 0x76cbf704 in PrintFactWithIdentifier (theEnv=0x4c8b90, logicalName=0x76d79184 "FactPPForm", factPtr=0x4c6dd8) at /myapp/clips_source/factmngr.c:328
#3 0x76cc1950 in EnvGetFactPPForm (theEnv=0x4c8b90, buffer=0x535290 "f--1 (attribute (id ) (comment ) (code ) (path", bufferLength=1023, theFact=0x4c6dd8) at /myapp/clips_source/factmngr.c:1590
#4 0x002661e8 in _cgo_4cadad88d928_Cfunc_EnvGetFactPPForm ()
#5 0x000823d8 in runtime.asmcgocall () at /usr/local/go/src/runtime/asm_arm.s:596
#6 0x00a16050 in ?? ()
сбой gdb:
...
0x76d6fa3c < 616>: mov r2, r1
0x76d6fa40 < 620>: ldr r1, [r11, #-60] ; 0xffffffc4
0x76d6fa44 < 624>: ldr r0, [r11, #-56] ; 0xffffffc8
0x76d6fa48 < 628>: bl 0x76c68edc <PrintAtom@plt>
0x76d6fa4c < 632>: b 0x76d6fad0 <PrintTemplateFact 764>
0x76d6fa50 < 636>: ldr r3, [r11, #-8]
0x76d6fa54 < 640>: lsl r3, r3, #3
0x76d6fa58 < 644>: ldr r2, [r11, #-24] ; 0xffffffe8
0x76d6fa5c < 648>: add r3, r2, r3
0x76d6fa60 < 652>: ldr r3, [r3, #4]
0x76d6fa64 < 656>: str r3, [r11, #-28] ; 0xffffffe4
0x76d6fa68 < 660>: ldr r3, [r11, #-28] ; 0xffffffe4
=> 0x76d6fa6c < 664>: ldr r3, [r3, #4]
0x76d6fa70 < 668>: cmp r3, #0
0x76d6fa74 < 672>: ble 0x76d6fad0 <PrintTemplateFact 764>
0x76d6fa78 < 676>: ldr r3, [pc, #232] ; 0x76d6fb68 <PrintTemplateFact 916>
0x76d6fa7c < 680>: add r3, pc, r3
0x76d6fa80 < 684>: mov r2, r3
0x76d6fa84 < 688>: ldr r1, [r11, #-60] ; 0xffffffc4
0x76d6fa88 < 692>: ldr r0, [r11, #-56] ; 0xffffffc8
0x76d6fa8c < 696>: bl 0x76c68c3c <EnvPrintRouter@plt>
0x76d6fa90 < 700>: ldr r3, [r11, #-8]
...
Редактировать:
Я даже могу получить SEGFAULT с помощью этого кода:
func SelfHealthCheck(deftemplate string) {
env := C.CreateEnvironment()
// Create deftemplate
cconstruct := C.CString(deftemplate)
defer C.free(unsafe.Pointer(cconstruct))
if C.EnvBuild(env, cconstruct) != 1 {
log.Println("error")
return
}
// Obtain pointer on it
cname := C.CString("attribute")
defer C.free(unsafe.Pointer(cname))
tplptr := C.EnvFindDeftemplate(env, cname)
if tplptr == nil {
log.Println("error")
return
}
// Create fact
factptr := C.EnvCreateFact(env, tplptr)
// print it
var bufsize C.size_t = 1024
buf := (*C.char)(C.malloc(C.sizeof_char * bufsize))
defer C.free(unsafe.Pointer(buf))
C.EnvGetFactPPForm(env, buf, bufsize-1, unsafe.Pointer(factptr))
log.Infoln(C.GoString(buf))
}
Комментарии:
1. Похоже
theFact->theProposition
, что значение было нулевым (или недействительным).2. Звучит как обычная старая ошибка неопределенного поведения. Опубликуйте этот
PrintTemplateFact
источник.3. Это не null, оно ссылается на недопустимый адрес памяти, и тот же код работает на amd64 arch.
4. метод @Lundin довольно большой, и он работает, когда Clips компилируется как двоичный файл stabdalone. Но в любом случае: pastebin.com/NRfiGHTp
5. кстати, исходники clips доступны здесь: sourceforge.net/projects/clipsrules/files/CLIPS/6.31 но, как я уже упоминал, это старый и хорошо проверенный проект.