Compare commits

...

20 Commits

Author SHA1 Message Date
Bananymous 755d41ca4e LibC: Add pw_passwd and pw_gecos to passwd structure
This information is available in /etc/passwd either way so why not
expose it to the user. Practically all UNIX-likes have these either way
2025-06-02 12:23:06 +03:00
Bananymous bbff9f89b0 BuildSystem: Don't invoke ninja directly, but use cmake --build 2025-06-02 11:45:06 +03:00
Bananymous fdcb38ac1f Shell: Accept '\r' as enter
This happens with some terminals and i don't want to bother with fixing
my termios :D
2025-06-02 11:42:06 +03:00
Bananymous fac742c038 test-sort: Add qsort test 2025-06-02 11:41:34 +03:00
Bananymous 5a6b43fc90 LibC: Remove debug printing from getnameinfo 2025-06-02 11:39:18 +03:00
Bananymous 317f413746 LibC: Implement very hacky posix_memalign
I did not even test this and i know the code is super hacky. I could not
bother with writing proper code for this xD
2025-06-02 11:39:18 +03:00
Bananymous 895909b7fa LibC: Add CMSG_* definitions to sys/socket.h 2025-06-02 11:39:18 +03:00
Bananymous 2ee8b6c8b4 LibC: Add more definitions to netinet/in.h
These are non-standard but a lot of UNIX-likes have them networking
software attempts to use them
2025-06-02 11:39:18 +03:00
Bananymous 022bb69782 LibC: Implement inet_aton
This is not POSIX but IMO it makes sense to have the counter part to
inet_ntoa
2025-06-02 11:39:18 +03:00
Bananymous be6da3e0db BuildSystem: Compile gcc with threading support 2025-06-02 11:39:18 +03:00
Bananymous 1f07e02298 BuildSystem: Fix cmake toolchain file processor 2025-06-02 11:39:18 +03:00
Bananymous 7a645b8555 Kernel: Add SMP message StackTrace
This event is sent when user presses ctrl+{F1-F12} and it will dump the
corresponding processor's stack trace. This is really helpful for
detecting deadlocks in the system
2025-06-02 11:39:18 +03:00
Bananymous c5b0d0235f Kenrel: Allow Processor::send_smp_message to send event to current CPU 2025-06-02 11:39:18 +03:00
Bananymous b7948551ff userspace: Add empty libm and libpthread
These making porting stuff easier. I could not find a way to tell CMake
that the system does not have threads library
2025-06-02 11:39:18 +03:00
Bananymous e109b5cff6 Kernel: Remove unnecessary locks from Pipe
Inode already locks its own mutex when read/write is called there is no
need to explicitly lock them in read_impl/write_impl
2025-06-02 11:39:18 +03:00
Bananymous 9883fb7bf6 Kernel: Rewrite epoll notifying system
This removes the need to lock epoll's mutex when notifying epoll. This
prevents a ton of deadlocks when epoll is notified from an interrupt
handler or otherwise with interrupts disabled
2025-06-02 11:39:18 +03:00
Bananymous e9f8471a28 BAN: Return UTF::invalid from byte_length instead of 0 2025-06-02 11:39:18 +03:00
Bananymous 4656b11256 LibC: actually use ATEXIT_MAX for atexit limit 2025-06-02 10:43:49 +03:00
Bananymous f2ccab2df7 ports/openssl: Fix openssl
New unistd.h definitions (or something) makes openssl think recvmsg is
available, disable it manually.
2025-06-01 19:51:33 +03:00
Bananymous b2e3aefa72 Kernel: Don't crash when terminating process with signal 2025-06-01 16:59:02 +03:00
31 changed files with 414 additions and 127 deletions

View File

@ -18,7 +18,7 @@ namespace BAN::UTF8
return 3;
if ((first_byte & 0xF8) == 0xF0)
return 4;
return 0;
return UTF8::invalid;
}
template<typename T> requires (sizeof(T) == 1)

View File

@ -98,7 +98,9 @@ namespace Kernel
private:
ThreadBlocker m_thread_blocker;
SpinLock m_ready_lock;
BAN::HashMap<BAN::RefPtr<Inode>, uint32_t, InodeRefPtrHash> m_ready_events;
BAN::HashMap<BAN::RefPtr<Inode>, uint32_t, InodeRefPtrHash> m_processing_events;
BAN::HashMap<BAN::RefPtr<Inode>, ListenEventList, InodeRefPtrHash> m_listening_events;
};

View File

@ -33,6 +33,7 @@ namespace Kernel
FlushTLB,
NewThread,
UnblockThread,
StackTrace,
};
SMPMessage* next { nullptr };
Type type;
@ -45,6 +46,7 @@ namespace Kernel
} flush_tlb;
SchedulerQueue::Node* new_thread;
SchedulerQueue::Node* unblock_thread;
bool dummy;
};
};

View File

@ -29,18 +29,26 @@ namespace Kernel
{
case EPOLL_CTL_ADD:
{
if (it == m_listening_events.end())
bool contains_inode = (it != m_listening_events.end());
if (!contains_inode)
it = TRY(m_listening_events.emplace(inode));
if (it->value.has_fd(fd))
return BAN::Error::from_errno(EEXIST);
TRY(m_ready_events.reserve(m_listening_events.size()));
TRY(inode->add_epoll(this));
{
SpinLockGuard _(m_ready_lock);
TRY(m_ready_events.reserve(m_listening_events.size()));
}
TRY(m_processing_events.reserve(m_listening_events.size()));
if (!contains_inode)
TRY(inode->add_epoll(this));
it->value.add_fd(fd, event);
auto ready_it = m_ready_events.find(inode);
if (ready_it == m_ready_events.end())
ready_it = MUST(m_ready_events.insert(inode, 0));
ready_it->value |= event.events;
auto processing_it = m_processing_events.find(inode);
if (processing_it == m_processing_events.end())
processing_it = MUST(m_processing_events.insert(inode, 0));
processing_it->value |= event.events;
return {};
}
@ -50,12 +58,13 @@ namespace Kernel
return BAN::Error::from_errno(ENOENT);
if (!it->value.has_fd(fd))
return BAN::Error::from_errno(ENOENT);
it->value.events[fd] = event;
auto ready_it = m_ready_events.find(inode);
if (ready_it == m_ready_events.end())
ready_it = MUST(m_ready_events.insert(inode, 0));
ready_it->value |= event.events;
auto processing_it = m_processing_events.find(inode);
if (processing_it == m_processing_events.end())
processing_it = MUST(m_processing_events.insert(inode, 0));
processing_it->value |= event.events;
return {};
}
@ -68,7 +77,10 @@ namespace Kernel
it->value.remove_fd(fd);
if (it->value.empty())
{
inode->del_epoll(this);
m_listening_events.remove(it);
m_processing_events.remove(inode);
SpinLockGuard _(m_ready_lock);
m_ready_events.remove(inode);
}
return {};
@ -83,53 +95,76 @@ namespace Kernel
if (event_span.empty())
return BAN::Error::from_errno(EINVAL);
size_t count = 0;
size_t event_count = 0;
for (;;)
{
bool failed_lock = false;
{
LockGuard _(m_mutex);
for (auto it = m_ready_events.begin(); it != m_ready_events.end() && count < event_span.size();)
{
SpinLockGuard _(m_ready_lock);
while (!m_ready_events.empty())
{
auto [inode, events] = *m_ready_events.begin();
m_ready_events.remove(m_ready_events.begin());
ASSERT(events);
if (auto it = m_processing_events.find(inode); it != m_processing_events.end())
it->value |= events;
else
MUST(m_processing_events.insert(inode, events));
}
}
for (auto it = m_processing_events.begin(); it != m_processing_events.end() && event_count < event_span.size();)
{
auto& [inode, events] = *it;
auto& listen = m_listening_events[inode];
#define REMOVE_IT_AND_CONTINUE() \
({ \
m_processing_events.remove(it); \
it = m_processing_events.begin(); \
continue; \
})
uint32_t listen_mask = EPOLLERR | EPOLLHUP;
for (int fd = 0; fd < OPEN_MAX; fd++)
if (listen.has_fd(fd))
listen_mask |= listen.events[fd].events;
events &= listen_mask;
auto listen_it = m_listening_events.find(inode);
if (listen_it == m_listening_events.end())
REMOVE_IT_AND_CONTINUE();
auto& listen = listen_it->value;
// This prevents a possible deadlock
if (!inode->m_mutex.try_lock())
{
failed_lock = true;
continue;
uint32_t listen_mask = EPOLLERR | EPOLLHUP;
for (size_t fd = 0; fd < listen.events.size(); fd++)
if (listen.has_fd(fd))
listen_mask |= listen.events[fd].events;
events &= listen_mask;
}
#define CHECK_EVENT_BIT(mask, func) \
if ((events & mask) && !inode->func()) \
events &= ~mask;
CHECK_EVENT_BIT(EPOLLIN, can_read);
CHECK_EVENT_BIT(EPOLLOUT, can_write);
CHECK_EVENT_BIT(EPOLLERR, has_error);
CHECK_EVENT_BIT(EPOLLHUP, has_hungup);
#undef CHECK_EVENT_BIT
inode->m_mutex.unlock();
if (events == 0)
REMOVE_IT_AND_CONTINUE();
{
m_ready_events.remove(it);
it = m_ready_events.begin();
continue;
LockGuard inode_locker(inode->m_mutex);
#define CHECK_EVENT_BIT(mask, func) \
if ((events & mask) && !inode->func()) \
events &= ~mask;
CHECK_EVENT_BIT(EPOLLIN, can_read);
CHECK_EVENT_BIT(EPOLLOUT, can_write);
CHECK_EVENT_BIT(EPOLLERR, has_error);
CHECK_EVENT_BIT(EPOLLHUP, has_hungup);
#undef CHECK_EVENT_BIT
}
for (int fd = 0; fd < OPEN_MAX && count < event_span.size(); fd++)
if (events == 0)
REMOVE_IT_AND_CONTINUE();
#undef REMOVE_IT_AND_CONTINUE
for (size_t fd = 0; fd < listen.events.size() && event_count < event_span.size(); fd++)
{
if (!listen.has_fd(fd))
continue;
@ -139,7 +174,7 @@ namespace Kernel
if (new_events == 0)
continue;
event_span[count++] = {
event_span[event_count++] = {
.events = new_events,
.data = listen_event.data,
};
@ -155,32 +190,29 @@ namespace Kernel
}
}
if (count)
if (event_count > 0)
break;
const uint64_t current_ns = SystemTimer::get().ns_since_boot();
if (current_ns >= waketime_ns)
break;
if (failed_lock)
continue;
const uint64_t timeout_ns = BAN::Math::min<uint64_t>(100'000'000, waketime_ns - current_ns);
TRY(Thread::current().block_or_eintr_or_timeout_ns(m_thread_blocker, timeout_ns, false));
}
return count;
return event_count;
}
void Epoll::notify(BAN::RefPtr<Inode> inode, uint32_t event)
{
LockGuard _(m_mutex);
ASSERT(event);
if (!m_listening_events.contains(inode))
return;
SpinLockGuard _(m_ready_lock);
auto ready_it = m_ready_events.find(inode);
if (ready_it == m_ready_events.end())
ready_it = MUST(m_ready_events.insert(inode, 0));
ready_it->value |= event;
if (auto it = m_ready_events.find(inode); it != m_ready_events.end())
it->value |= event;
else
MUST(m_ready_events.insert(inode, event));
m_thread_blocker.unblock();
}

View File

@ -67,8 +67,6 @@ namespace Kernel
BAN::ErrorOr<size_t> Pipe::read_impl(off_t, BAN::ByteSpan buffer)
{
LockGuard _(m_mutex);
while (m_buffer_size == 0)
{
if (m_writing_count == 0)
@ -103,8 +101,6 @@ namespace Kernel
BAN::ErrorOr<size_t> Pipe::write_impl(off_t, BAN::ConstByteSpan buffer)
{
LockGuard _(m_mutex);
while (m_buffer_size >= m_buffer.size())
{
if (m_reading_count == 0)

View File

@ -114,7 +114,37 @@ namespace Kernel
auto& key_event = event.as<const LibInput::RawKeyEvent>();
if (key_event.modifier & LibInput::KeyEvent::Modifier::Pressed)
{
switch (key_event.keycode)
if (key_event.modifier & LibInput::KeyEvent::Modifier::LCtrl)
{
const auto processor_count = Processor::count();
switch (key_event.keycode)
{
#define DUMP_CPU_STACK_TRACE(idx) \
case LibInput::keycode_function(idx + 1): \
if (idx >= processor_count) \
break; \
Processor::send_smp_message(Processor::id_from_index(idx), { \
.type = Processor::SMPMessage::Type::StackTrace, \
.dummy = false, \
}); \
break
// F1-F12
DUMP_CPU_STACK_TRACE(0);
DUMP_CPU_STACK_TRACE(1);
DUMP_CPU_STACK_TRACE(2);
DUMP_CPU_STACK_TRACE(3);
DUMP_CPU_STACK_TRACE(4);
DUMP_CPU_STACK_TRACE(5);
DUMP_CPU_STACK_TRACE(6);
DUMP_CPU_STACK_TRACE(7);
DUMP_CPU_STACK_TRACE(8);
DUMP_CPU_STACK_TRACE(9);
DUMP_CPU_STACK_TRACE(10);
DUMP_CPU_STACK_TRACE(11);
#undef DUMP_CPU_STACK_TRACE
}
}
else switch (key_event.keycode)
{
case LibInput::keycode_function(1):
Processor::toggle_should_print_cpu_load();

View File

@ -244,6 +244,10 @@ namespace Kernel
case SMPMessage::Type::UnblockThread:
processor.m_scheduler->unblock_thread(message->unblock_thread);
break;
case SMPMessage::Type::StackTrace:
dwarnln("Stack trace of CPU {}", current_id().as_u32());
Debug::dump_stack_trace();
break;
}
last_handled = message;
@ -275,7 +279,6 @@ namespace Kernel
void Processor::send_smp_message(ProcessorID processor_id, const SMPMessage& message, bool send_ipi)
{
ASSERT(processor_id != current_id());
auto state = get_interrupt_state();
set_interrupt_state(InterruptState::Disabled);
@ -307,7 +310,12 @@ namespace Kernel
);
if (send_ipi)
InterruptController::get().send_ipi(processor_id);
{
if (processor_id == current_id())
handle_smp_messages();
else
InterruptController::get().send_ipi(processor_id);
}
set_interrupt_state(state);
}

View File

@ -623,6 +623,9 @@ namespace Kernel
{
Processor::set_interrupt_state(InterruptState::Disabled);
setup_process_cleanup();
// This is super hacky but prevents a crash in yield :D
if (m_signal_lock.current_processor_has_lock())
m_signal_lock.unlock(InterruptState::Disabled);
Processor::yield();
ASSERT_NOT_REACHED();
}

View File

@ -0,0 +1,12 @@
diff -ruN openssl-3.3.1/crypto/bio/bss_dgram.c openssl-3.3.1-banan_os/crypto/bio/bss_dgram.c
--- openssl-3.3.1/crypto/bio/bss_dgram.c 2024-06-04 15:53:04.000000000 +0300
+++ openssl-3.3.1-banan_os/crypto/bio/bss_dgram.c 2025-06-01 19:48:55.088806701 +0300
@@ -61,7 +61,7 @@
# define NO_RECVMMSG
# endif
# endif
-# if defined(__GNU__)
+# if defined(__GNU__) || defined(__banan_os__)
/* GNU/Hurd does not have IP_PKTINFO yet */
#undef NO_RECVMSG
#define NO_RECVMSG

View File

@ -37,8 +37,7 @@ build_target () {
echo "No target provided"
exit 1
fi
cd $BANAN_BUILD_DIR
run_fakeroot ninja $1
run_fakeroot $BANAN_CMAKE --build $BANAN_BUILD_DIR -- -j$(nproc) $1
}
build_toolchain () {

View File

@ -19,7 +19,7 @@ endif ()
set(TOOLCHAIN_PREFIX $ENV{BANAN_ROOT_DIR}/toolchain/local)
set(CMAKE_SYSTEM_NAME banan-os)
set(CMAKE_SYSTEM_PROCESSOR AMD64)
set(CMAKE_SYSTEM_PROCESSOR ${BANAN_ARCH})
set(CMAKE_SYSROOT ${BANAN_SYSROOT})

View File

@ -118,6 +118,7 @@ build_gcc () {
--prefix="$BANAN_TOOLCHAIN_PREFIX" \
--with-sysroot="$BANAN_SYSROOT" \
--enable-initfini-array \
--enable-threads=posix \
--enable-shared \
--enable-lto \
--disable-nls \

View File

@ -5,6 +5,8 @@ set(USERSPACE_LIBRARIES
LibGUI
LibImage
LibInput
LibMath
LibPthread
)
foreach(library ${USERSPACE_LIBRARIES})

View File

@ -27,28 +27,10 @@ uint16_t ntohs(uint16_t netshort)
in_addr_t inet_addr(const char* cp)
{
uint32_t a, b, c, d, n;
const int ret = sscanf(cp, "%i%n.%i%n.%i%n.%i%n",
&a, &n, &b, &n, &c, &n, &d, &n
);
if (ret < 1 || ret > 4 || cp[n] != '\0')
in_addr addr;
if (inet_aton(cp, &addr) == 0)
return INADDR_NONE;
if (ret == 1 && (a > 0xFFFFFFFF))
return INADDR_NONE;
if (ret == 2 && (a > 0xFF || b > 0xFFFFFF))
return INADDR_NONE;
if (ret == 3 && (a > 0xFF || b > 0xFF || c > 0xFFFF))
return INADDR_NONE;
if (ret == 4 && (a > 0xFF || b > 0xFF || c > 0xFF || d > 0xFF))
return INADDR_NONE;
uint32_t result = 0;
result |= (ret == 1) ? a : a << 24;
result |= (ret == 2) ? b : b << 16;
result |= (ret == 3) ? c : c << 8;
result |= (ret == 4) ? d : d << 0;
return htonl(result);
return addr.s_addr;
}
char* inet_ntoa(struct in_addr in)
@ -64,6 +46,33 @@ char* inet_ntoa(struct in_addr in)
return buffer;
}
int inet_aton(const char* cp, struct in_addr* inp)
{
uint32_t a, b, c, d, n;
const int ret = sscanf(cp, "%i%n.%i%n.%i%n.%i%n",
&a, &n, &b, &n, &c, &n, &d, &n
);
if (ret < 1 || ret > 4 || cp[n] != '\0')
return 0;
if (ret == 1 && (a > 0xFFFFFFFF))
return 0;
if (ret == 2 && (a > 0xFF || b > 0xFFFFFF))
return 0;
if (ret == 3 && (a > 0xFF || b > 0xFF || c > 0xFFFF))
return 0;
if (ret == 4 && (a > 0xFF || b > 0xFF || c > 0xFF || d > 0xFF))
return 0;
uint32_t result = 0;
result |= (ret == 1) ? a : a << 24;
result |= (ret == 2) ? b : b << 16;
result |= (ret == 3) ? c : c << 8;
result |= (ret == 4) ? d : d << 0;
inp->s_addr = htonl(result);
return 1;
}
const char* inet_ntop(int af, const void* __restrict src, char* __restrict dst, socklen_t size)
{
if (af == AF_INET)

View File

@ -1,6 +1,5 @@
#include <stddef.h>
#define ATEXIT_MAX_FUNCS 128
#include <limits.h>
struct atexit_func_entry_t
{
@ -9,12 +8,12 @@ struct atexit_func_entry_t
void* dso_handle;
};
static atexit_func_entry_t s_atexit_funcs[ATEXIT_MAX_FUNCS];
static atexit_func_entry_t s_atexit_funcs[ATEXIT_MAX];
static size_t s_atexit_func_count = 0;
extern "C" int __cxa_atexit(void(*func)(void*), void* arg, void* dso_handle)
{
if (s_atexit_func_count >= ATEXIT_MAX_FUNCS)
if (s_atexit_func_count >= ATEXIT_MAX)
return -1;
s_atexit_funcs[s_atexit_func_count++] = {
.func = func,

View File

@ -11,6 +11,7 @@ __BEGIN_DECLS
#include <bits/types/socklen_t.h>
in_addr_t inet_addr(const char* cp);
int inet_aton(const char* cp, struct in_addr* inp);
char* inet_ntoa(struct in_addr in);
const char* inet_ntop(int af, const void* __restrict src, char* __restrict dst, socklen_t size);
int inet_pton(int af, const char* __restrict src, void* __restrict dst);

View File

@ -10,20 +10,54 @@ __BEGIN_DECLS
#include <bits/inet_common.h>
#include <sys/socket.h>
#define IPPROTO_IP 1
#define IPPROTO_IPV6 2
#define IPPROTO_ICMP 3
#define IPPROTO_RAW 4
#define IPPROTO_TCP 5
#define IPPROTO_UDP 6
#define IPPROTO_IP 1
#define IPPROTO_IPV6 2
#define IPPROTO_ICMP 3
#define IPPROTO_RAW 4
#define IPPROTO_TCP 5
#define IPPROTO_UDP 6
#define IPV6_JOIN_GROUP 1
#define IPV6_LEAVE_GROUP 2
#define IPV6_MULTICAST_HOPS 3
#define IPV6_MULTICAST_IF 4
#define IPV6_MULTICAST_LOOP 5
#define IPV6_UNICAST_HOPS 6
#define IPV6_V6ONLY 7
enum
{
IP_ADD_MEMBERSHIP,
#define IP_ADD_MEMBERSHIP IP_ADD_MEMBERSHIP
IP_ADD_SOURCE_MEMBERSHIP,
#define IP_ADD_SOURCE_MEMBERSHIP IP_ADD_SOURCE_MEMBERSHIP
IP_DROP_MEMBERSHIP,
#define IP_DROP_MEMBERSHIP IP_DROP_MEMBERSHIP
IP_DROP_SOURCE_MEMBERSHIP,
#define IP_DROP_SOURCE_MEMBERSHIP IP_DROP_SOURCE_MEMBERSHIP
IP_MULTICAST_IF,
#define IP_MULTICAST_IF IP_MULTICAST_IF
IP_MULTICAST_LOOP,
#define IP_MULTICAST_LOOP IP_MULTICAST_LOOP
IP_MULTICAST_TTL,
#define IP_MULTICAST_TTL IP_MULTICAST_TTL
IP_TTL,
#define IP_TTL IP_TTL
};
enum
{
IPV6_ADD_MEMBERSHIP,
#define IPV6_ADD_MEMBERSHIP IPV6_ADD_MEMBERSHIP
IPV6_DROP_MEMBERSHIP,
#define IPV6_DROP_MEMBERSHIP IPV6_DROP_MEMBERSHIP
IPV6_JOIN_GROUP,
#define IPV6_JOIN_GROUP IPV6_JOIN_GROUP
IPV6_LEAVE_GROUP,
#define IPV6_LEAVE_GROUP IPV6_LEAVE_GROUP
IPV6_MULTICAST_HOPS,
#define IPV6_MULTICAST_HOPS IPV6_MULTICAST_HOPS
IPV6_MULTICAST_IF,
#define IPV6_MULTICAST_IF IPV6_MULTICAST_IF
IPV6_MULTICAST_LOOP,
#define IPV6_MULTICAST_LOOP IPV6_MULTICAST_LOOP
IPV6_UNICAST_HOPS,
#define IPV6_UNICAST_HOPS IPV6_UNICAST_HOPS
IPV6_V6ONLY,
#define IPV6_V6ONLY IPV6_V6ONLY
};
#define INADDR_ANY 0
#define INADDR_NONE 0xFFFFFFFF
@ -78,6 +112,19 @@ struct ipv6_mreq
unsigned ipv6mr_interface; /* Interface index. */
};
struct ip_mreq
{
struct in_addr imr_multiaddr; /* IP multicast group address. */
struct in_addr imr_interface; /* IP address of local interface. */
};
struct ip_mreq_source
{
struct in_addr imr_multiaddr; /* IP multicast group address. */
struct in_addr imr_interface; /* IP address of local interface. */
struct in_addr imr_sourceaddr; /* IP address of multicast source. */
};
__END_DECLS
#endif

View File

@ -14,11 +14,13 @@ __BEGIN_DECLS
struct passwd
{
char* pw_name; /* User's login name. */
uid_t pw_uid; /* Numerical user ID. */
gid_t pw_gid; /* Numerical group ID. */
char* pw_dir; /* Initial working directory. */
char* pw_shell; /* Program to use as shell. */
char* pw_name; /* User's login name. */
char* pw_passwd; /* User's hashed password. */
uid_t pw_uid; /* Numerical user ID. */
gid_t pw_gid; /* Numerical group ID. */
char* pw_gecos; /* User's information. */
char* pw_dir; /* Initial working directory. */
char* pw_shell; /* Program to use as shell. */
};
void endpwent(void);

View File

@ -50,16 +50,33 @@ struct cmsghdr
socklen_t cmsg_len; /* Data byte count, including the cmsghdr. */
int cmsg_level; /* Originating protocol. */
int cmsg_type; /* Protocol-specific type. */
unsigned char __cmg_data[];
};
// FIXME
#if 0
#define SCM_RIGHTS
#define SCM_RIGHTS 1
#define CMSG_DATA(cmsg)
#define CMSG_NXTHDR(mhdr, cmsg)
#define CMSG_FIRSTHDR(mhdr)
#endif
#define CMSG_DATA(cmsg) ((cmsg)->__cmg_data)
#define __CMSG_NXTHDR_ADDR(cmsg) \
((unsigned char*)(cmsg) + (cmsg)->cmsg_len)
#define __CMSG_NXTHDR_OFFSET(mhdr, cmsg) \
(__CMSG_NXTHDR_ADDR(cmsg) - (unsigned char*)(mhdr)->msg_control)
#define CMSG_NXTHDR(mhdr, cmsg) \
((size_t)(mhdr)->msg_controllen \
>= __CMSG_NXTHDR_OFFSET(mhdr, cmsg) + sizeof(struct cmsghdr) \
? (struct cmsghdr*)__CMSG_NXTHDR_ADDR(cmsg) \
: (struct cmsghdr*)0)
#define CMSG_FIRSTHDR(mhdr) \
((size_t)(mhdr)->msg_controllen >= sizeof(struct cmsghdr) \
? (struct cmsghdr*)((mhdr)->msg_control) \
: (struct cmsghdr*)0)
#define CMSG_SPACE(length) \
(socklen_t)((length) + sizeof(struct cmsghdr))
#define CMSG_LEN(length) \
(socklen_t)((length) + sizeof(struct cmsghdr))
struct linger
{

View File

@ -1,4 +1,5 @@
#include <BAN/Debug.h>
#include <BAN/Math.h>
#include <assert.h>
#include <errno.h>
@ -95,6 +96,12 @@ static bool allocate_pool(size_t pool_index)
node->prev_free = nullptr;
node->next_free = nullptr;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#pragma GCC diagnostic ignored "-Wstringop-overflow"
node->data[-1] = 0;
#pragma GCC diagnostic pop
pool.free_list = node;
return true;
@ -143,6 +150,12 @@ static void shrink_node_if_needed(malloc_pool_t& pool, malloc_node_t* node, size
next->size = node_end - (uint8_t*)next;
next->last = node->last;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#pragma GCC diagnostic ignored "-Wstringop-overflow"
next->data[-1] = 0;
#pragma GCC diagnostic pop
node->last = false;
// insert excess node to free list
@ -183,6 +196,29 @@ static void* allocate_from_pool(size_t pool_index, size_t size)
static malloc_node_t* node_from_data_pointer(void* data_pointer)
{
if (((uint8_t*)data_pointer)[-1])
{
malloc_pool_t* pool = nullptr;
for (size_t i = 0; i < s_malloc_pool_count; i++)
{
if (!s_malloc_pools[i].start)
continue;
if (data_pointer < s_malloc_pools[i].start)
continue;
if (data_pointer > s_malloc_pools[i].end())
continue;
pool = &s_malloc_pools[i];
break;
}
assert(pool);
auto* node = (malloc_node_t*)pool->start;
for (; (uint8_t*)node < pool->end(); node = node->next())
if (node->data < data_pointer && data_pointer < node->next())
return node;
assert(false);
}
return (malloc_node_t*)((uint8_t*)data_pointer - sizeof(malloc_node_t));
}
@ -332,3 +368,27 @@ void* calloc(size_t nmemb, size_t size)
memset(ptr, 0, total);
return ptr;
}
int posix_memalign(void** memptr, size_t alignment, size_t size)
{
dprintln_if(DEBUG_MALLOC, "posix_memalign({}, {})", alignment, size);
if (alignment < sizeof(void*) || alignment % sizeof(void*) || !BAN::Math::is_power_of_two(alignment / sizeof(void*)))
{
errno = EINVAL;
return -1;
}
uint8_t* unaligned = (uint8_t*)malloc(size + alignment);
if (unaligned == nullptr)
return -1;
if (auto rem = (uintptr_t)unaligned % alignment)
{
unaligned += alignment - rem;
unaligned[-1] = 1;
}
*memptr = unaligned;
return 0;
}

View File

@ -147,11 +147,9 @@ error_close_socket:
return EAI_FAIL;
}
#include <BAN/Debug.h>
int getnameinfo(const struct sockaddr* __restrict sa, socklen_t salen, char* __restrict node, socklen_t nodelen, char* __restrict service, socklen_t servicelen, int flags)
{
printf("getnameinfo(%p, %p, %p, 0x%X)\n", sa, node, service, flags);
(void)flags;
switch (sa->sa_family)
{

View File

@ -71,12 +71,14 @@ struct passwd* getpwent(void)
switch (i)
{
case 0:
s_pwent_struct.pw_name = (char*)malloc(field_len + 1);
s_pwent_struct.pw_name = strndup(ptr, field_len + 1);
if (!s_pwent_struct.pw_name)
return nullptr;
strcpy(s_pwent_struct.pw_name, ptr);
break;
case 1:
s_pwent_struct.pw_passwd = strndup(ptr, field_len + 1);
if (!s_pwent_struct.pw_passwd)
return nullptr;
break;
case 2:
ASSERT(1 <= field_len && field_len <= 9);
@ -91,18 +93,19 @@ struct passwd* getpwent(void)
s_pwent_struct.pw_gid = atoi(ptr);
break;
case 4:
s_pwent_struct.pw_gecos = strndup(ptr, field_len + 1);
if (!s_pwent_struct.pw_gecos)
return nullptr;
break;
case 5:
s_pwent_struct.pw_dir = (char*)malloc(field_len + 1);
s_pwent_struct.pw_dir = strndup(ptr, field_len + 1);
if (!s_pwent_struct.pw_dir)
return nullptr;
strcpy(s_pwent_struct.pw_dir, ptr);
break;
case 6:
s_pwent_struct.pw_shell = (char*)malloc(field_len + 1);
s_pwent_struct.pw_shell = strndup(ptr, field_len + 1);
if (!s_pwent_struct.pw_shell)
return nullptr;
strcpy(s_pwent_struct.pw_shell, ptr);
break;
}

View File

@ -560,9 +560,12 @@ int mblen(const char* s, size_t n)
case LOCALE_POSIX:
return 1;
case LOCALE_UTF8:
if (const auto bytes = BAN::UTF8::byte_length(*s); n >= bytes)
return bytes;
return -1;
const auto bytes = BAN::UTF8::byte_length(*s);
if (bytes == BAN::UTF8::invalid)
return -1;
if (n < bytes)
return -1;
return bytes;
}
ASSERT_NOT_REACHED();
}

View File

@ -176,7 +176,7 @@ namespace LibFont
uint32_t len = BAN::UTF8::byte_length(bytes[0]);
if (len == 0)
if (len == BAN::UTF8::invalid)
{
invalid_utf = true;
byte_index = 0;

View File

@ -0,0 +1,18 @@
set(SOURCES
dummy.cpp
)
add_library(libmath-static STATIC ${SOURCES})
add_library(libmath-shared SHARED ${SOURCES})
target_link_options(libmath-static PRIVATE -nolibc)
target_link_options(libmath-shared PRIVATE -nolibc)
banan_link_library(libmath-static libc)
banan_link_library(libmath-shared libc)
set_target_properties(libmath-static PROPERTIES OUTPUT_NAME libm)
set_target_properties(libmath-shared PROPERTIES OUTPUT_NAME libm)
install(TARGETS libmath-static OPTIONAL)
install(TARGETS libmath-shared OPTIONAL)

View File

View File

@ -0,0 +1,18 @@
set(SOURCES
dummy.cpp
)
add_library(libpthread-static STATIC ${SOURCES})
add_library(libpthread-shared SHARED ${SOURCES})
target_link_options(libpthread-static PRIVATE -nolibc)
target_link_options(libpthread-shared PRIVATE -nolibc)
banan_link_library(libpthread-static libc)
banan_link_library(libpthread-shared libc)
set_target_properties(libpthread-static PROPERTIES OUTPUT_NAME libpthread)
set_target_properties(libpthread-shared PROPERTIES OUTPUT_NAME libpthread)
install(TARGETS libpthread-static OPTIONAL)
install(TARGETS libpthread-shared OPTIONAL)

View File

View File

@ -500,6 +500,7 @@ BAN::Optional<BAN::String> Input::get_input(BAN::Optional<BAN::StringView> custo
fflush(stdout);
break;
case '\n':
case '\r':
{
BAN::String input;
MUST(input.append(m_buffers[m_buffer_index]));

View File

@ -748,7 +748,7 @@ Rectangle Terminal::putchar(uint8_t ch)
m_utf8_bytes[m_utf8_index++] = ch;
const size_t utf8_len = BAN::UTF8::byte_length(m_utf8_bytes[0]);
if (utf8_len == 0)
if (utf8_len == BAN::UTF8::invalid)
{
dwarnln("invalid utf8 leading byte 0x{2H}", ch);
m_utf8_index = 0;

View File

@ -39,6 +39,29 @@ bool is_sorted(BAN::Vector<T>& vec)
} \
} while (0)
#define TEST_ALGORITHM_QSORT(ms) do { \
uint64_t duration_us = 0; \
printf("qsort\n"); \
for (size_t size = 100; duration_us < ms * 1000; size *= 10) { \
BAN::Vector<unsigned> data(size, 0); \
for (auto& val : data) \
val = rand() % 100; \
uint64_t start_ns = CURRENT_NS(); \
qsort(data.data(), data.size(), sizeof(unsigned), [](const void* a, const void* b) -> int { return *(unsigned*)a - *(unsigned*)b; }); \
uint64_t stop_ns = CURRENT_NS(); \
if (!is_sorted(data)) { \
printf(" \e[31mFAILED!\e[m\n"); \
break; \
} \
duration_us = (stop_ns - start_ns) / 1'000; \
printf(" %5d.%03d ms (%zu)\n", \
(int)(duration_us / 1000), \
(int)(duration_us % 1000), \
size \
); \
} \
} while (0)
int main()
{
srand(time(0));
@ -49,4 +72,5 @@ int main()
TEST_ALGORITHM(100, BAN::sort::intro_sort);
TEST_ALGORITHM(1000, BAN::sort::sort);
TEST_ALGORITHM(1000, BAN::sort::radix_sort);
TEST_ALGORITHM_QSORT(100);
}