#access violation when returning across abi boundary
1 messages · Page 1 of 1 (latest)
it points to the return call, return bapi.getNull().inner;, defined as
return ByondValue{
.inner = .{
.type = .Null,
.data = .{ .ref = 0 },
},
};
pub const ByondValueType = enum(u1c) {...}`
pub const CByondValue = extern struct {
type: ByondValueType,
junk1: u1c = undefined,
junk2: u1c = undefined,
junk3: u1c = undefined,
data: extern union {
ref: u4c,
num: f32,
},
};
pub const u1c = u8;
pub const s1c = i8;
pub const u2c = c_ushort;
pub const s2c = c_short;
pub const u4c = c_uint;
pub const s4c = c_int;
pub const s8c = c_longlong;
pub const u8c = c_ulonglong;
header definition
typedef u1c ByondValueType;
typedef union ByondValueData {
u4c ref; //!< 4-byte reference ID
float num; //!< floating-point number
} ByondValueData;
typedef struct CByondValue {
ByondValueType type; //!< 1-byte intrinsic data type
u1c junk1, junk2, junk3; //!< padding
ByondValueData data; //!< 4-byte reference ID or floating point number
} CByondValue;
typedef unsigned char u1c;
typedef signed char s1c;
typedef unsigned short u2c;
typedef signed short s2c;
#ifdef DM_64BIT
typedef unsigned int u4c;
typedef signed int s4c;
#else
typedef unsigned long u4c;
typedef signed long s4c;
#endif
#if defined(i386) || defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__X86__)
#define _X86
#define _X86ORX64
#define DM_32BIT
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) || defined(_M_X64) || defined(_WIN64) || defined(WIN64)
#define _X64
#define _X86ORX64
#define DM_64BIT
#elif defined(__arm__) || defined(_M_ARM)
#define _ARM
#if defined(__LP64__) || defined(_LP64)
#define DM_64BIT
#else
#define DM_32BIT
#endif
#endif
is ByondValue here an alias for CByoneValue in the translated code?
wrapper one-value struct
can I see its definition?
and also the function type in c, as well as the definition line of the zig function
just this + a stupid amount of functions
pub const ByondValue = struct {
inner: ByondValueRaw = undefined,
ah okay so ur returning the inner field, which is the translated type, right?
example fn which works
/**
* Returns a reference to an existing string ID, but does not create a new string ID.
* Blocks if not on the main thread.
* @param str Null-terminated string
* @return ID of string; NONE if string does not exist
*/
DUNGPUB u4c Byond_GetStrId(char const *str);
/* dll export stuff */
#ifdef WIN32
#define DUNGPUB __declspec(dllimport)
#define BYOND_EXPORT __declspec(dllexport) // for functions in user-defined DLLs for use with call_ext()
#else // unix/g++, combine with -fvisibility=hidden to hide non-exported symbols
#define DUNGPUB
#define BYOND_EXPORT __attribute__ ((visibility("default"))) // for functions in user-defined .so libraries for use with call_ext()
#endif
ye
can I see the disassembly around the segfault?
sure but you'll have to baby me through it as im still new to windbg
the assembly button right of source just toggles how to step through code
tbh I don't rly know windbg very well either, but try clicking the "assembly" button in the toolbar
oh rip
uhhhh
"disassembly" on the left hand side of the window
oh
ewllll time to check the deinit i guess
actually just have the entire function that'd prbably make things easier
no, this happens after the deinit call
can u send a screenshot of the "registers" tab?
oh
also, are u targeting 32-bit x86? 
school wifi i beg of theeeeeeeeee
-Dtarget=x86-windows
ig the program ur plugging into is 32-bit for some reason?
the provided .lib doesnt have 64 bit bindings
if i try to compile as -Dtarget=x86_64-windows it just doesnt
theres a tiny change its header mismatch
BYOND public API version 516.1651
whereas the server reports 516.1669
but surely a minor wouldnt break like this??
oh hang on I misread the asm
ill check if theres a newer versino and diff
is this the function ur exposing from zig? if so, can I see the zig-side definition?
wait, no, it can't be cuz it doesn't return the right type 
can I see the fn line for the zig function in which this segfault occurs, as well as the c-side declaration of that function?
1664 so not NEWEST newest but eh
?
so, the function in which this segfault occurs is called from some c code, right? what type does it say the function should have?
I want to double check that the way you're defining it in zig matches what the c code expects
SCHOOL INTERNET, PLEASE. I BEG.
if you're asking about the dll function thats benig called from the engine, .
if you mean bapi.getNull() its .
the diff is the wrong way around but the shittty router handling my connection will implode if i try to wgetpaste again
oops, missed that msg
no its zig-side only
getNull() just returns a struct as value
init is called from c tho right? and that's the function that's segfaulting, not getNull
what's the type the c code expects for init?
its called from the byond engine, declared in the dm language
call_ext(SANLIB, "byond:init")()
im merely provided the header and .lib to build with
i hate this internet i wrote this ages ago
uhhh, okay, can u give me an example of a c function called through this system that u know works?
like an example from the docs or smth ig
@leaden trail
ill check the docs
agony
wont load for me though
but iirc theres an example in there
official ref
Thank you eduroam
OH you have to open the section manually apparebtlky
bigger example in call_ext doc
@leaden trail
Ill b home in half an hour ISH, can hop in VC then
or just provide stuff way smootherly
ofc it's fucking eduroam 😭
eduroam networks always suck, which is weird because afaik they're run by the school/uni themselves, and eduroam in theory should only be handing auth?? but in my experience they're consistently awful
anyway
the example in the docs uses BYOND_EXPORT, how is that macro defined?
oh it's here, okay
declspec(dllimport) shouldn't affect ABI afaik so that's fine
btw this code never actually exports the init function, where does that happen?
Root. Zig
using @export?
SM like export const whatever = import.init
cant paste verbatim rn
oh uhhh, that's... I'm surprised that even runs tbh
Wasn't supposed to be a reply
Lmao
is there a reason u don't simply do export fn init where it's originally defined?
exports are strewn around the codebase and zig doesn't have multiroot
and they're not inclyded by default because dead code
so just reference them in a comptime block in root.zig
FTR id rather define as export where it isis and not do this crossrefing
WIL try
id recommend either doing that or using @export
exporting a const alias to a function is weird and unlikely to work reliably
it should probably be banned tbh ^^'
is @export also lazyincluded or can i do sm like
@export(thing)
fn thing() void {}
and not worry about root files?
*putting things IN root file
s
@leaden trail didnt help
root.zig
comptime {
const core = @import("_exports/core.zig");
@export(&core.init, .{ .name = "init" });
}
_exports/core.zig
pub fn init() callconv(.c) bapi.ByondValueRaw {
[...]
}
@export goes in a comptime block. those aren't lazy, but files themselves are, so it still won't work unless the file is explicitly used in the module root
can u try with pub export fn init and _ = @import("exports/core.zig").init;?
doubt it'll change anything tbh
but worth a shot
kk
after that, I think it's probably time to start trying to create a minimal repro to pin down whether this actually is an abi issue
do u have any other of these functions that are called the same way? and if so, do any of them work?
?
if not, start by writing a little hello world or smth and see if that works
none that i got to yet
yea :)
i do know that calling its noreturn fn DOES work
but thats uhhh not desirable to have as return of everything
since it- hold on cp time
that works fine
nope still no worky
i'll try to replace it with a short noop in root
doesnt work
can i like.. throw a too-big struct at it through a pointer and see how much it memsets?
to tell if its a mismatch of sizes
WAIT okay this is schizophrenic give me a second ill try a thing
okay its not breaking where i hoped it wouldnt
good
wait im a dumbass
so i still dont know how to fix it
but i just realized i DO actually pass the byondvalues and not just cstrs
to dll functions
.@"/turf/stacked" = bapi.callGlobalByID(sref.byond.proc_text2path, &[_]bapi.ByondValueRaw{bapi.writeStr("/turf/stacked").inner}),
that gives and returns bvals
i'll bitcast that and print whats in there
huh
okay soeven if i replace the bval with one double the intended size it doesnt crash (on lib calls)
i'll check how many bits it writes to
FFFFFFFFFFFFFFFF00000000FFFFFF00 if told to clear a u128
so does match the expected layout
i'll just redefine the fields as u32
since the header already defines these as "n chars of size"
MOTHERFUCKER
ok now it works
somehow
pub export fn init() callconv(.c) u64 {
return 0;
how DO extern structs work even
i hope its not adding padding behind my back
replacing extern struct with packed struct works.. okay
it does prohibit me from using extern unions in it though
yep its extern
@leaden trail changing this from extern to packed magically fixes everything
similarly reverting re-crashes even if i nuke the inner union
yikes
probably an msvc moment
if u feel like trying to make a repro it's probably worth reporting
a c program that calls a zig function that returns a struct is probably enough to repro it? idk
sadly dont know enough c for that
nor just.. how dlls work in general lol
literally just c struct foo { // fields go here }; struct foo zigFn(); int main() { struct foo x = zigFn(); return 0; }
I expect DLLs don't have to be involved, u should be able to just add the c file to ur build command and have it statically link
hm, i'll try
i find it funny though how its not ME thats at fault or even the engine i work with
but the language i write in itself
tbf this is likely msvc being weird
oh wait actually
#1425901317884350567
what happens if u target x86-windows-msvc?
checking rn
it's possible the msvc ABI is different from mingw; zig targets mingw by default iirc
so when linking to stuff built with msvc u gotta explicitly specify that
uhhhh yeah u will need msvc installed
😭
thank u john gentoo for --oneshot
okay gentoo doesnt have msvc this is getting annoying UGHHHHH gcc can you mimic
oh right we're doing zig
uhhhhhh
can you just like run this for me and send back the result file lmao
const thing = extern struct{
type: u8,
junk1: u8 = undefined,
junk2: u8 = undefined,
junk3: u8 = undefined,
data: u32,
};
pub export fn init() callconv(.c) thing {
return .{ .type = 0x2A, .data = 42 };
}
@leaden trail ?
unless you're not on pc or w/e
I don't have msvc either 
I don't do much windows dev
if ur on Linux u may have to install it in wine or smth
pain
I'm on Windows with MSVC installed if you need something built? Although I'm not sure exactly what
i need a .dll that exports.. that chunk of code 6 messages up
to check if its msvc playing tricks on me with externs
because on -Dtarget=x86-windows, that piece of code crashes, but replacing the extern with a packed magically fixes stuff
which it shouldnt since theres no reason for padding here
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const root = b.addModule("root", .{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
const lib = b.addLibrary(.{
.name = "bepis",
.root_module = root,
.linkage = .dynamic,
});
b.installArtifact(lib);
}
i think thats all u need for build.zig
danke shoo
ok thank god it also breaks
@dawn cedar your contributions to the field of science will not be forgotten
for any newcomers
that crashes
but returning a u64 or a packed doesnt
Do you know the definition of thing on the other side of the ABI boundary. The thread is quite big, but I understand you are communicating with external DLL
buried at the start of thread
.
this one?
ye
are you sure the padding is actually padding, you are setting things to undefined
packed structs dont' really support undefined fields (they actually get zeroed or something when you assign to other fields)
I should note that all structures in C generally do the equivent of Zig's extern struct.
packed struct is a very different thing entirely.
heck packed struct can give garbage data to the DLL as well because the layout is completely different
its defined in header as "junk" and similarly throwingi t at a "clear this struct out" function of the provided supplementary also leaves these be
u1c junk1, junk2, junk3; //!< padding
yeah but is this reverse engineered stuff? It might be they just don't know what it is
officially provided header file
even more scary lol
🤷♂️ bug the engine dev
Currently it's bugging you lol
DId you try setting those junk things to 0 anyways?
Assert oriented programming
i mean i can try i guess
. aslisted here it ignores these anyways
but maybe externs leave holes in allocation or whatever lol
idk anything about byond, but it could be they do touch the junks or use them for something internally or it's reserved space and ByondValueTYpe is actually u32 or something
in which case if they are not set 0 you get some humongous value as type
i mean at that point we're entering "dont trust the header" territory
and thats a whole other can of worms
well that's everytime you have to deal with properitary binary ABI
crashes
https://docs.rs/byondapi-sys/latest/byondapi_sys/struct.CByondValue.html this rust binding is equal to your extern struct, so it should work
well, again, it doesnt
im not really sure why are we snooping for issues with the structure itself when ive already said packeds just work
just having it as extern doesnt
What I'm saying the packed probably doesn't actually work
it's just workarounding whatever is your real issue by luck
im not really sure how much luck there is to be had here
its not like im sending garbage data in and it freaking out over that, if i do try to send an invalid ref in, it handles that
bad ref error in log
and besides @winter bear didnt you say that packed was suppsoed to be identical in layout to extern?
in this specific case i mean
if the layout doesnt change and the data doesnt change, then what does?\
In this case they should be equivalent yes, which I find strange. Extern struct is what you should use, but either it gets compiled wrong for some reason or there's mismatching ABI.
you can even check / assert this by creating same variable using both packed and extern and doing this:
std.mem.eql(u8, std.mem.asBytes(&b), std.mem.asBytes(&a));
do you compile to 32 bit by chance?
-Dtarget=x86-windows
no other bindings provided
gtg for a bit, theres a tiny repro which annoingly crashes regardless in #1425901317884350567
which zig version are you using as well?
newest
"newset" meaning tip of zig main (for anything sensical give the commit), or "newest" tagged release? (which next week will be something different, rendering your thread answer obsolete if you don't actually name the version.
1.15.1
1.15.1 doesn't exist. You must mean 0.15.1