Hello,
I've been working to wrap a C library into Nim and hit a wall. The C library does the following to get a pointer that accesses a program's data segment:
/* code from C library */
extern int __data_start;
extern int _end;
void is_nil() {
printf("(%p, %d)\t(%p, %d)", (void*)&__data_start, ( ((int*)&__data_start) == ((int*)0) ), (void*)& _end, ( ((int*)& _end) == ((int*)0) ) );
}
The C library then checks to see if both pointers (__data_start and _end) are NULL. To be exact, the C library checks to see if the pointer are equal to (int*)0.
When a C program uses this library all is just fine. When my Nim wrapper runs, the check is_nil in the underlying C library says that __data_start is (nil) and it returns true for being of equal value to 0. _end has a value and returns false for having a value equal to 0.
When test programs for this C library are compiled, I've noticed __data_start's address is at 4000 and _end is at 4070 (using the nm and objdump -d tools). When I explore the starting address of __data_start and _end in a Nim program that uses my wrapper, the address does not start at 4000.
I've tried fiddling with the following Nim compiler flags:
--clib:<library name> -d:danger -d:globalSymbols -d:nimAllocPagesViaMalloc --mm:arc -d:useMalloc --clib:atomic --hint:exec:on --hint:link:on
these flags have managed to change the address of __data_start and _end in the Nim program.
Intuition says this issue has something to do with how Nim programs are linked. That said, a quick look at /etc/nim/nim.cfg shows the following gcc flags are turned on -fsanitize=null -fsanitize-undefined-trap-on-error.
I figured this would not be an issue since Nim generates a C program that is compiled using gcc or clang. Kind of a naive assumption on my part. Any assistance would be greatly appreciated!
Araq,
I'd like to program in Nim to work with a specific family of embedded hardware devices; the C library in question is the software mechanism to access that hardware.
This post was from a first experiment with Nim's ffi functionality. I attempted to work around the issue by using Nim's ffi to get a pointer to the __data_start symbol so portions of the C library could be reimplemented. Here are the two approaches I could come up with...
{.emit: "extern int __data_start; int * test_data_start = &__data_start;" .}
var test_data_start {.importc, nodecl.}: ptr cint
proc init_device*(provider:string) : int =
echo (test_data_start == nil)
result = (test_data_start == nil)
{.emit: "extern int __data_start;" .}
var test_data_start {.importc: "__data_start".} : cint
proc init_device*(provider:string) : int =
echo (test_data_start == 0)
result = test_data_start == 0
Both attempts didn't pan out test_data_start is still set to nil or 0. Is there something wrong in the use of ffi or is there another technique available?
Thanks in advance for your time and attention.
Looks like the problem of getting a nil value for __data_start has been solved. I was able to get a non nil value by doing the following.
{.emit: "__attribute__((visibility(\"default\"))) extern int __data_start; int * test_inner_data_start = &__data_start;" .}
var test_inner_data_start {.importc, nodecl.}: ptr cint
proc test_init*(provider:string) : int =
echo (nofi_inner_data_start == nil) # returns false
Will keep moving forward with the effort.
@PMunch - Just wrapped up some additional tinkering. The library that I was using requires static linking into the final executable in order for the following line of code to work correctly.
extern int __data_start;
extern int _end;
The library I was aiming to use compiles a static library and a shared library. The existence of a shared library made things a bit confusing on my end.
It took a little extra work fiddling with the nim compiler but I was able to get the desired library and it's dependencies compiled statically into a nim program using static linking in the backend gcc/clang compiler toolchain.
The use of shared objects/libraries combined with that line of C code will never work. And given how compilation in C works, that makes sense. A shared object has no means to 'link in' the addresses to the data segment symbol of a program. The address of a program's data segment is information that is only known in a static compilation and linking context.
The use of shared objects/libraries combined with that line of C code will never work in a dynamic linking context.
Exactly.
Just because there is some C code out there, that doesn't mean that you need to "wrap" it, you can also read the code, see what it really ultimately does (usually not much; for "low level" programming it's basically stores and reads into "volatile" memory) and write Nim code that does the same.