This PR implements strdup, strndup and wcsdup for libzigc
Contributes towards #30978
(deleted):libzigc into master
This PR implements strdup, strndup and wcsdup for libzigc
Contributes towards #30978
@ -294,0 +299,4 @@
}
fnstrndup(s:[*:0]constc_char,n:usize)callconv(.c)?[*:0]c_char{
returnstd.heap.c_allocator.dupeZ(c_char,std.mem.span(s)[0..strnlen(s,n)])catchreturnnull;
the std.mem.span is redundant here, no?
You're right, thanks!
Do not use c_allocator for this: it may allocate an oversized buffer and return an pointer offset past the actual start of the allocation. Any logic which needs to get a free-able pointer should instead call std.c.malloc directly to get a buffer of the required size.
2cc77f6c46
b359fff873
@mlugg wrote in #31144 (comment):
Do not use
c_allocatorfor this: it may allocate an oversized buffer and return an pointer offset past the actual start of the allocation. Any logic which needs to get afree-able pointer should instead callstd.c.mallocdirectly to get a buffer of the required size.
Thank you for the guidance. Corrected in b359fff873
@ -292,2 +295,4 @@
}
fnstrdup(s:[*:0]constc_char)callconv(.c)?[*:0]c_char{
if(!builtin.link_libc)returnnull;
??
It's because we test libzigc without linking libc, so the malloc call below would be a compile error---but this is a hacky solution and also makes these functions untestable, so I think needs to be changed. Seems to me that we should just test libzigc with libc linked, although that could lead to weirdness due to there being two copies of these functions...
Both hacks are unacceptable to me. The simplest way to tackle this problem is to treat malloc as a dependency of this function, and therefore it must be implemented first.
I called the second thing a hack, but it's not a hack, it's the only correct way to test this code. The libc when targeting windows-gnu for example, provides some libc functionality but requires calling functions from ucrtbase.dll, including _errno for example.
There won't be any "weirdness due to there being two copies of these functions" because the extra copy will be deleted along with providing the Zig implementation.
Anyway we have malloc now after #31177
Hmm actually there is indeed a problem. What we want is to run zig test lib/c.zig ... with all the additional libc components added (i.e. not yet ported musl .c files). However, the logic that knows how to set up that command line is using the Compilation API directly, in musl.zig, mingw.zig, etc.
Instead, what we get if we adjust the build script to link libc, is lib/c.zig acting as the user application, with an additional copy of lib/c.zig + companion files, just like @mlugg pointed out.
So we either have to special case the zig libc unit test compilation unit, which I am loathe to do, or come up with another unit testing strategy, such as testing libc from outside the ABI boundary.
A good solution would be doing what compiler_rt does for unit testing, which is set the symbol linkage to "internal" when builtin.is_test is true. That is currently not done in zig libc because sometimes zig libc is imported directly into the Zig Compilation Unit of the user application, which might be a unit test.
This leads me to a solution: we can set symbol linkage to internal if it's a test and lib/c.zig is the root source file.
That condition is not currently detectable, because the root module (@import("root")) in a test is the test runner, not the module being tested.
Dammit I was so close:
-/// Determines the symbol's visibility to other objects.
-/// For WebAssembly this allows the symbol to be resolved to other modules, but will not
-/// export it to the host runtime.
-pub const visibility: std.builtin.SymbolVisibility = .hidden;
+/// It is possible that this libc is being linked into a different test
+/// compilation, as opposed to being tested itself. In such case,
+/// `builtin.link_libc` will be `true` along with `builtin.is_test`.
+///
+/// When we don't have a complete libc, `builtin.link_libc` will be `false` and
+/// we will be missing externally provided symbols, such as `_errno` from
+/// ucrtbase.dll. In such case, we must avoid analyzing otherwise exported
+/// functions because it would cause undefined symbol usage.
+const skip_export = builtin.is_test and !builtin.link_libc;
pub inline fn symbol(comptime func: *const anyopaque, comptime name: []const u8) void {
- // Normally, libc goes into a static archive, making all symbols
- // overridable. However, Zig supports including the libc functions as part
- // of the Zig Compilation Unit, so to support this use case we make all
- // symbols weak.
- @export(func, .{ .name = name, .linkage = .weak, .visibility = visibility });
+ if (skip_export) return;
+
+ @export(func, .{
+ .name = name,
+ // Normally, libc goes into a static archive, making all symbols
+ // overridable. However, Zig supports including the libc functions as part
+ // of the Zig Compilation Unit, so to support this use case we make all
+ // symbols weak.
+ .linkage = .weak,
+ // For WebAssembly, hidden visibility allows the symbol to be resolved to
+ // other modules, but will not export it to the host runtime.
+ .visibility = .hidden,
+ });
}
It doesn't work because symbol(&foo, "foo") still causes foo to be analyzed, even when skip_export is true.
cant you just do comptime func: anytype and take the address inside the function behind the condition?
No it still gets typed checked in that case. Plus there's a lack of type safety there (if you pass &func it silently does the wrong thing). I can just move the logic to the comptime block.
thats somewhat surprising to me that it still gets checked, i wouldnt expect that to happen since the & isnt reached. good point on type safety though
OK, @ivel_santos after #31177 lands you will be unblocked. Here is some guidance:
You still can't use malloc and other libc functions for internal libc unit tests, because those depend, and will always depend, on external libc bits that might not be available when unit testing, such as _errno.
However what you can do, is extract the lower level functions out and make them pub and then call them from zig libc internal implementation code. In other words, you can't call malloc but you can call malloc_inner (extracting malloc_inner from malloc left as an exercise to the contributor).
0e243af2e9
a0922337ff
@ -292,2 +296,4 @@
}
fnstrdup(s:[*:0]constc_char)callconv(.c)?[*:0]c_char{
returnstrndup(s,std.mem.len(s));
calculates length 2x instead of 1x
Do not call strndup anymore on 502a92a44b to calculate length only 1x. Sorry for my english
a0922337ff
502a92a44b
man page says:
RETURN VALUE
On success, the strdup() function returns a pointer to the duplicated string. It returns NULL if insufficient mem‐
ory was available, with errno set to indicate the error.
ERRORS
ENOMEM Insufficient memory available to allocate duplicate string.
this means you do have to set errno, which means you should call actual malloc and you have to exempt these functions from being exported when unit testing, just like in c/malloc.zig
since these tests test the external-facing libc symbols, they would be better to be contributions to libc-test than unit tests.
@andrewrk wrote in #31144 (comment):
man page says:
RETURN VALUE On success, the strdup() function returns a pointer to the duplicated string. It returns NULL if insufficient mem‐ ory was available, with errno set to indicate the error. ERRORS ENOMEM Insufficient memory available to allocate duplicate string.this means you do have to set errno, which means you should call actual
mallocand you have to exempt these functions from being exported when unit testing, just like in c/malloc.zigsince these tests test the external-facing libc symbols, they would be better to be contributions to libc-test than unit tests.
Oh, I saw this at the begining of this PR, but completely forgot about it. Sorry.
it's no problem.
btw we can have zig unit tests to test the external libc surface area. I think that would belong in std tests, perhaps in lib/std/c/test.zig. and we can simply not import that file when not linking libc. that would be a reasonable place to move your unit tests to. then they will be run as part of std lib testing rather than zigc testing.
502a92a44b
5659a17e46
@andrewrk wrote in #31144 (comment):
You still can't use
mallocand other libc functions for internal libc unit tests, because those depend, and will always depend, on external libc bits that might not be available when unit testing, such as_errno.
Tried to cope with this implementing _errno on libzigc in 5659a17e46 such the It would remove the dependency on external errno location from malloc.zig, such that it would adress this:
@andrewrk wrote in #31144 (comentário):
man page says:
RETURN VALUE On success, the strdup() function returns a pointer to the duplicated string. It returns NULL if insufficient mem‐ ory was available, with errno set to indicate the error. ERRORS ENOMEM Insufficient memory available to allocate duplicate string.this means you do have to set errno, which means you should call actual
mallocand you have to exempt these functions from being exported when unit testing, just like in c/malloc.zig
since these tests test the external-facing libc symbols, they would be better to be contributions to libc-test than unit tests.
This is a different, incompatible implementation of errno, putting it in a different place. The code this deletes has it in the pthread->self() struct.
I don't see any discussion of those changes being considered in the writeup.
If you want to work on errno, I suggest doing that in a different PR. I have requested changes for the function signature, behavior, and name of _errno as well.
These string functions are already unblocked. Just move the tests to test outside the libc boundary as I mentioned. They are more useful there anyway.
ff5b98aed8
7319d69f6a
7319d69f6a
1c98ac1e72
No due date set.
No dependencies set.
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?