#Ultra

1 messages · Page 3 of 1

sterile kayak
#

instead of something like ```
call whatever@plt

everything below this is linker-generated

.section .plt
pushq GLOBAL_OFFSET_TABLE(%rip)
pushq $<got index I think? might be relocation index>
jmp *(_GLOBAL_OFFSET_TABLE + 8)(%rip)
whatever@plt:
jmp *whatever@GOTPCREL(%rip)

#

not sure I got the push order and got indices correct but you get the idea

#

the got entry by default points to the pushq stuff

prime wraith
#

what is this doing exactly?

dusky kraken
#

i think the indirection is done by prefilling the JUMP_SLOT got entries with a thunk that jumps to what the 0th got entry points to, not within the plt thunks themselves (or at least on a test binary the thunks are the same regardless of -z now and -z lazy)

#

i think the plt thunks are there purely to save space

#

since a relative call insn takes less space than the pc-rel call through the got

sterile kayak
#

that rtld code is supposed to fill the got entry and do a tail call to the resolved function

#

so on future calls the thunk jumps directly to the resolved functions

prime wraith
#

so the last line jumps to the pushq code or?

sterile kayak
#

yeah

prime wraith
#

how does it know which whatever we came from?

sterile kayak
ember reef
#

pushq $<got index I think? might be relocation index>

#

that presumably

prime wraith
#

so thats per-plt-entry code?

ember reef
#

yeah

prime wraith
#

any reason why we need an extra jump?

ember reef
#

when the dynamic linker resolves it it overwrites the initial instructions with a jump directly to the actual function

#

so you dont pay the cost of the pushes on subsequent calls

prime wraith
#

ohh

sterile kayak
ember reef
sterile kayak
prime wraith
#

so much code just to avoid relocating at load time

#

is it really that slow

ember reef
#

windows doesnt do late binding

#

works fine

ember reef
# ember reef that makes no sense

if the callers call into this code stub in the plt and you dont want to ever change that call instruction (bc itd COW text which is bad), your only choice is to change the code in the plt entry

sterile kayak
# ember reef that makes no sense

before resolution:

  • caller calls plt thunk
  • plt thunk jumps to address in got, which is the resolution thunk
  • resolution thunk pushes symbol id and jumps to rtld
    when already resolved:
  • caller calls plt thunk
  • plt thunk jumps to address in got, which is the resolved symbol address
#

there is no instruction overwriting involved in any of this

ember reef
#

weirdly large number of steps

#

i guess i got confused from the SunOS paper where they would overwrite instructions in their PLT

#

must not be done in ELF

sterile kayak
#

the only thing that changes between pre resolution and post resolution is whether the got (aka address array) contains the resolution thunk address or the resolved address

prime wraith
#

why cant that jump be inlined as a call into the caller?

sterile kayak
sterile kayak
prime wraith
#

strange

ember reef
#

wdym inlined as a call into the caller

sterile kayak
#

I believe the reason has to do with the fact it's impossible on x86

prime wraith
ember reef
#

that would COW the text section

sterile kayak
prime wraith
#

the GOT offset is fixed so no it wouldnt lol

ember reef
#

thought you meant changed by the dynamic linker

sterile kayak
#

ah wait no there is a reason for this

prime wraith
#

nah like just copy what plt does and replace jmp with a call

sterile kayak
#

its because the compiler doesn't know whether an extern comes from another object or from a dso

#

and a pc rel indirect call has a different instruction size from a direct call so the linker can't transform it

prime wraith
#

so basically it does it so it doesnt have to use absolute calls for local symbols?

#

i mean it could just pad with nops

sterile kayak
#

without the direct call to plt thunk stuff, the compiler would have to pessimistically assume all calls to external functions are to dsos

#

and so even module-local calls would use pc rel indirect through got

ember reef
#

dont linkers move around code all the time

prime wraith
#

just patch .text at link time?

ember reef
#

lto and stuff

prime wraith
#

transform into relative call and add nops

sterile kayak
ember reef
#

i mean it was never "that" hard to relocate part of a section to make more room for instructions or vice versa

#

could have done that in the 60s

dusky kraken
#

i hate the discord mobile app

prime wraith
#

i mean msvc's linker still struggles with basic shit

dusky kraken
prime wraith
#

im not even sure they have LTO

sterile kayak
#

sure it's technically possible, that's what linker relaxations do

dusky kraken
sterile kayak
#

aka you need the puts@plt code

dusky kraken
#

i mean the point is that the jump is only at one place in the final file

sterile kayak
#

that isn't really important though? nothing breaks if it's at multiple places

dusky kraken
#

if you call puts more than 10 times it saves memory

sterile kayak
#

oh fair yeah

#

brain fart

dusky kraken
#

i do wonder what is the difference for most programs though

#

since i assume there's a mix of rarely called stuff where the plt thunk wastes memory and stuff called very often that saves memory

ember reef
#

anyone know how hot relinking works

#

property of some dev environments where you can change a source file, recompile the program while its running and have it relink dynamically and immediately observe your changes

#

the only thing i can think of is that it requires a quiescent point in a system library where the reloading can occur safely, like an event loop

#

but then theres multithreading

sterile kayak
#

i'm guessing it suspends all threads and uses ptrace or whatever other native debugging interface to call into rtld

#

that's not based on any knowledge though, just how i'd implement it

ember reef
#

the issue is that you cant really be executing any of the code that changed while this happens

sterile kayak
#

well yeah that's why all threads are suspended

ember reef
#

far too difficult to figure out what to do there

#

suspending them doesnt help

#

because you unsuspend them and now theyre in the middle of some changed code

#

with stuff in the wrong registers and in the wrong place and the function they were executing might not even exist anymore and so on

sterile kayak
#

for stuff like this you need to handle changing stored function pointers anyway

ember reef
#

you basically need the threads to all pass through a quiescent point

sterile kayak
#

you can just treat it as having a function pointer stored in rip

dusky kraken
#

could load the code at a new address and replace the got entry for it (assuming you built without -fvisibility=hidden or -Bsymbolic or -fno-semantic-interposition)

ember reef
#

thats true

#

you could keep the old code and put the new code somewhere new and then you only call into it next time

#

yeah thats probably how they do it

sterile kayak
#

ah yeah

#

and then you wouldn't have to deal with the function pointers stuff either

#

or just pointers to symbols in general

ember reef
#

well but the thing is ive seen it demoed where the new code is called into without ever calling it. like, the thing that made me wonder about it was a wwdc demo in like 2003 where they showed off xcode being able to do this with C++ projects, and the thing i noticed was that they could change the main function and have the changes materialize

#

they did call a get next event type thing inside the main function though

#

so it could be a combination

dusky kraken
#

i mean since it's their ecosystem they could be doing compiler magic

ember reef
#

at the time they were using stock gcc

dusky kraken
#

hard to say without seeing the generated binary

#

ah hmm

ember reef
#

but they were using their own linker

ember reef
#

sounds

#

nearly impossibleish

#

so i might have just misinterpreted what i saw

dusky kraken
#

yeah the stack frames would likely not match up

#

if it was really just stock gcc

ember reef
#

id say impossible but nothing is IMPOSSIBLE

#

you could probably have some 200kloc static analysis engine that could make it all work out

#

sounds impractical though

prime wraith
#

wait does nasm not support CFI

#

bruh

wide ether
#

just use gas

prime wraith
#

does it work well with intel syntax?

carmine token
#

For hand written asm yes

#

But gcc's Intel asm generation is not very robust

red parcel
#

just use gas syntax tbh

prime wraith
#

do I have to invoke as separately to compile those, or can i just feed clang .S files and it figures it out?

red parcel
#

it doesn't matter

#

you can feed clang with the .S

prime wraith
#

how does that work?

red parcel
#

idk, but it does :^)

prime wraith
#

does clang have like a GAS support module or something

red parcel
#

these are the make rules I have, so literally the same for both S and c

#

and it compiles fine

prime wraith
#

interesting how that works

red parcel
#

probably using the file extension

#

and clang does have an assembler I am pretty sure

prime wraith
#

well that makes it easier than nasm becase i can just pipe all the files into the same target in cmake

red parcel
#

apparently llvm has its own assembler

wide ether
#

llvms assembler is fucked in some parts

red parcel
#

tbh my asm is super simple

wide ether
#

iirc it couldn't properly encode far jumps

red parcel
#

but I do use inline asm with clang that has cfi macros

wide ether
#

it would not emit the correct opcodes

prime wraith
red parcel
#

maybe it just uses the system as

#

or something

wide ether
#

it uses binutils as

prime wraith
#

that means it must match your target and stuff tho

#

like how do u cross compile

wide ether
#

cross-binutils

prime wraith
#

yeah but clang has to be aware of it somehow

wide ether
#

you only need it to work on your host afaik

#

oh wait

#

llvm-as?

#

idk i forgot how exactly it works

red parcel
#

doesn't that just use the llvm assembler tho

wide ether
#

i dont know

#

i just remember the clang as being broken for some reason

#

maybe it's fixed now

red parcel
#

maybe it was fixed since tho

prime wraith
#

ill give it a try who knows

wide ether
#

just use the integrated as until you encounter issues

prime wraith
#

ye

wide ether
#

btw i had some cmake code for configurable modules/builtins

#

if you want to look at it

prime wraith
#

too late

wide ether
prime wraith
#

lol

errant temple
#

I think it depends on the target

#

just like the target can choose which linker to use (by default at least)

prime wraith
sterile kayak
#

it's avl

prime wraith
#

ah

proper tulip
#

me when i inline avl.h inside vmm.c

prime wraith
#

left tons of comments about how the kernel symbol table works so i dont forget lol

#

actually its slightly wrong because if a token is longer than 128 chars then the length is encoded as a ULEB128 byte (aka as a sequence of bytes)

wide ether
#

finally someone documenting their stuff

prime wraith
#

lol

#

yeah there is basically 0 documentation

wide ether
#

reading impls with 0 comments at all makes learning from code hard

#

cuz you don't know why it does it, just that it does it

prime wraith
#

yep

ornate torrent
#

(unless C++ with nested templates or smth)

prime wraith
#

linux supports it because of rust probably

#

but yeah the actual encoding is

if name_len <= leb128_max(1):
    repr.append(name_len)
else:
    repr.append(LEB128_HAS_NEXT_BYTE | to_uleb128(name_len, 0))
    repr.append(to_uleb128(name_len, 1))
obtuse sparrow
ornate torrent
#

what is it? uintptr_t minus the uint?

obtuse sparrow
#

void* trl

prime wraith
ornate torrent
#

%tx

prime wraith
#

i dont see t there

ornate torrent
prime wraith
#

ah wait i do but thats ptrdiff

ornate torrent
#

eh, uintptr ≈ ptrdiff

prime wraith
#

uhh lol not really

#

ptrdiff is signed

ornate torrent
#

the t specifies width

#

it it like l and ll

prime wraith
#

attribute(format) will still complain

ornate torrent
#

huh, for me it doesn't

prime wraith
#

strange because it definitely complains about signedness mismatch usually

#

but if it works it works

ornate torrent
#

or do you try printing uintptr_t's as %ti?

prime wraith
#

It says "unsigned version of ptrdiff_t"

#

But doesnt name such type lol

obtuse sparrow
#

uptrdiff_t

prime wraith
#

alright uhh

#

CFI time

#

but first ill make helpers for accessing the symbol table

#

or more like, just one called lookup_symbol for now

prime wraith
#

Waiiit

#

i just realized

#

.long in GAS is fucking U32

#

my thing has been wrong all along

#

who tf thought of that

versed obsidian
prime wraith
#

like

#

they invented .quad

#

but .long is kept as 32 bit

prime wraith
#

i can never get binary search right first try

thorny kelp
prime wraith
#

wdym nooo

#

it literally workd lmfao

thorny kelp
#

oh lmfao

thorny kelp
#

but im dumb

prime wraith
#

i always mix up which value ends up as lower bound etc

#

but its ok i figured it out

thorny kelp
#

i see

prime wraith
#

now that i think about it, when unwinding the current task stack, i have to jump to an asm stub to save all registers of the previous call frame don't I

#

because CFI depends on the state of the registers matching the point in time its unwinding right

sterile kayak
#

yeah

vestal zealot
#

yes it does

prime wraith
#

well shouldnt be too hard but still anoying

#

thanks

prime wraith
#

@sterile kayak do u think this would be good enough?

sterile kayak
#

Should be, yeah

prime wraith
#

after this the frame should point to the rip after this call which is where unwinding should start hopefully

prime wraith
sterile kayak
#

Which ones?

prime wraith
#

also u seem to manually align the functions, is this also needed?

sterile kayak
#

Ah yeah that's string.S, it's part of the common sources that are compiled into the vDSO so .hidden is necessary to ensure mem* doesn't get exported

prime wraith
#

ah

sterile kayak
prime wraith
#

ah cool

#

thanks a lot

#

for some reason objdump seems to not care about my .size and disassembles garbage after the function

sterile kayak
#

Yeah objdump ignores .size

prime wraith
#

ah

sterile kayak
#

Is it really garbage though? Both the assembler and linker should fill the gap with nops

prime wraith
sterile kayak
#

Huh, weird

prime wraith
#

yeah lol

#

i have a setting that fills .text with 0xCC in my linker script

#

maybe its that

#

but why it has 00 before that idk

sterile kayak
#

Maybe the fill value is 32 bit?

prime wraith
#

hmm

sterile kayak
#

Actually no because it'd have to be big endian for that to make sense

prime wraith
#

right

#

yeah

#

it is the fill byte

#

i just changed it to 0xEE

#

and it changed as well

prime wraith
#

its literally big endian too

sterile kayak
#

That's hilarious

#

Why is it big endian on x86

prime wraith
#

lmfao

#

now this makes more sense

prime wraith
#

props to the gnu linker team

red parcel
#

Me when I just force frame pointers instead of doing all of this :^)

prime wraith
#

me when i say goodbye to 10-15% of perf meme

red parcel
#

Where did you see that?

prime wraith
#

but yeah its probably more practical

prime wraith
red parcel
#

Just last year both Ubuntu and fedora enabled globally to compile with frame pointers

prime wraith
#

also like, with a frame pointer based unwinder u have to manually detect stack changes or iret frames etc

#

with cfi it just does it for u

red parcel
#

Tbh, at that point why not just use gdb?

prime wraith
#

idk, real hw and stuff? its more for fun than anything

red parcel
#

Yeah but at this rate you will take forever before having any actual code in the kernel KurisuSip

prime wraith
#

thats fine lol

red parcel
#

For real hardware I have a gdb stub

#

In general it's nice once you have threads since you can debug at a thread level instead of a core level

prime wraith
#

And it will take me tons of time to get to usb

red parcel
#

I wonder how hard is xdi

#

or whatever that xhci debug interface is called

#

I assume its simpler than full usb

prime wraith
#

No clue, probably not that easy

red parcel
#

linux has support for it for early console iirc :^)

prime wraith
#

Does it require like special devices and stuff

red parcel
#

I have no idea tbh

#

I never actually used it

#

it might require a crossed usb 3 cable

#

or that might be for the intel jtag stuff

#

EARLY_PRINTK_USB_XDBC

prime wraith
#

ah

red parcel
#

Documentation/driver-api/usb/usb3-debug-port.rst

#

on one hand it says ```
When DbC is initialized and enabled, it will present a debug
device through the debug port (normally the first USB3
super-speed port). The debug device is fully compliant with
the USB framework and provides the equivalent of a very high
performance full-duplex serial link between the debug target
(the system under debugging) and a debug host.

#

on the other hand it says

#

DbC has been designed to log early printk messages

#

ok it does have a mini usb driver for it

#

its a single file with less than 1k lines, so it can't be that bad

#

yeah its basically the most minimal xhci driver, it allocates a single ring and does bulk transfers to the device

#

it is more complex than I had hoped tho

prime wraith
#

Ah damn

red parcel
#

technically, its a dma logging api :^)

prime wraith
#

Virtio console too

stuck agate
prime wraith
wide ether
#

are you doing 32-bit?

obtuse sparrow
#

I really hate GAS's .long crap

wide ether
#

do you know about the .align memes

prime wraith
wide ether
#

on x86 .align 8 means align to 8 byte boundary

#

on risc-v or arm, .align 3 means align to 1 << 3 byte boundary

#

that's why i use .balign instead

prime wraith
#

ah

#

lmao

#

im using balign as well because i stole it from monkuous' code but good to know align sucks

wide ether
#

lol

dusky kraken
wide ether
#

true

#

imo balign is more explicit, but p2align also makes sense because you rarely want an alignment that isn't a power of 2

red parcel
#

Why would you ever want p2align in the assembler tho

#

As in, just put it the number

#

And you can always do it yourself with 1<<x

prime wraith
#

Can you even align to non power of 2?

red parcel
#

No reason why you couldn't

wide ether
prime wraith
#

why would u do that?

wide ether
#

idk

#

imagine a char buffer

#

something like utsname

#

idk

#

there has to be a use for that

prime wraith
#

hmm

carmine token
#

i don't think that works

#

no, it doesn't

#
test.S: Assembler messages:
test.S:1: Error: alignment not a power of 2
#

the point of balign is not allowing non-2-powers but allowing the user to specify the alignment in a different way

prime wraith
#

Makes sense

red parcel
#

It's stupid to not allow any alignment tbh

#

I have had hardware where I need 3 byte alignment

vestal zealot
wide ether
#

that's unrelated imo

#

just because it's not efficient doesn't mean it should be impossible

carmine token
#

It also doesn't mix very well

#

If you have something that needs to be aligned to a page, something that needs to be aligned to 3 and something that needs to be aligned to 5 you suddenly need 0xF000 alignment

vestal zealot
#

^ This is a good reason why we only do power-of-2 alignment

vestal zealot
wide ether
#

63 bit pointers :D

thorny kelp
red parcel
#

some proprietary asic, it had a 24bit data bus and while not all the command descriptors were a 3 byte product in size, they all had to start on a 3 byte product

#

now tbf, that is not a good argument for an assembler

#

because I didn't really need to use an assembler to load the data into it nicely

thorny kelp
#

interesting

prime wraith
#

im doing CFI

#

so far DWARF is very convenient

#

and eh_frame_hdr is super helpeful too

#

i think this entire unwinder will be like 500 loc tops

#

and its arch agnostic for the most part

#

the spec is impossible to understand without actually looking at the data

#

but thats fine

#

progress

#

the eh_frame location is encoded as a signed pc-relative 4 byte value

#

so the decoder seems to be working

#

also why the hell does it not generate fallthrough warnings at -Wall -Wextra -Werror

ornate torrent
#

Are you writing:

switch (foo) {
case BAR:
    do_something();
case BAZ:
    do_something();
    break;

or:

switch (foo) {
case BAR:
case BAZ:
    do_something();
    break;
}

?
AFAIK only the former will generate fallthrough warnings.

prime wraith
#

the former

#

with multiple logic lines too

#

it didnt seem to give a fuck

ornate torrent
#

can you paste the code snippet here?

prime wraith
#

i already fixed both instances but let me craft something

ornate torrent
#

also wtf this thread has existed for like five days but already has more messages than my thread.

prime wraith
#

it had random discussions

ornate torrent
#

tru

#

#osdev-misc-0-4

prime wraith
#

am I abusing warn on too much trl

#

its my favorite macro

ornate torrent
#

Yes.

#

Eventually you will just filter out the warnings as noise.

prime wraith
#

probably

#

would that not happen to normal text messages?

gentle sandal
#

I don't think you'll have warnings in normal execution though

prime wraith
#

you shouldnt yeah

#

its mostly nice for quickly tracking down the source

gentle sandal
#

Imo its just cluttering the source rather then logs, but not too bad

ornate torrent
#

I say, let the caller complain when your function fails, except in extraordinary circumstances.

prime wraith
#

yeah but then i have to go and put printfs to figure out which line failed

ornate torrent
#

sure

#

but IME more often than not, with some inner knowledge of the code, that can be guessed anyways.

prime wraith
#

but yeah ill prbably remove msot of these

gentle sandal
#

I mean it allows for very simple alteration to turn all warnings into asserts

prime wraith
# ornate torrent can you paste the code snippet here?

e.g.

  switch (scaling) {
  case DW_EH_PE_absptr:
      *out_value = 0;
      break;
  case DW_EH_PE_pcrel:
      *out_value = (ptr_t)*cursor;
      break;
  case DW_EH_PE_textrel:
      *out_value = (ptr_t)g_linker_symbol_text_begin; // no warning here
  case DW_EH_PE_datarel:
      *out_value = (ptr_t)g_linker_symbol_eh_frame_hdr_begin;
      break;
  case DW_EH_PE_funcrel:
  case DW_EH_PE_aligned:
  default:
      pr_warn("unhandled DWARF scaling %d\n", scaling);
      return ENOSYS;
  }
gentle sandal
#

Can remove all the warnings that indicate code errors after you're more stable

ornate torrent
#

if you OOM, it's not very interesting to ask yourself 'where did I OOM? where did I OOM?' rather, 'who is leaking memory?' WARN_ON doesn't help you here.

prime wraith
#

my clang is broken

gentle sandal
#

Gcc wins again

prime wraith
#

i checked with make VERBOSE=1 it does pass all those flags so idk

#

--target=x86_64-none-none -std=gnu17 -ffreestanding -ggdb3 -Wall -Wextra -Werror -mcmodel=kernel -mno-red-zone -mno-80387 -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -fasynchronous-unwind-tables

gentle sandal
prime wraith
#

lol literally nothing in the DWARF/LSB core explains the format of this field

#

absolute value? ok? How many fucking bytes

#

Seems to decode fine with DW_EH_PE_absptr

#

ill just leave it at that

#

actually no it's a 4 byte value for some reason

#

okay i guess lmfao

#

also success

#

off to bed now

#

tomorrow we execute dwarf CFA opcodes

#

to actually unwind the frame

prime wraith
#

mine is version 1 tho

#

okay i take my words back about how good this format is

#

they somehow failed to specify the width of a very important field and u have to use weird heuristics to figure it out

#

So this shit works as follows: you check how many bytes it took to decode the PC value, then you memcpy this same amount of bytes, but without applying any augmentation

#

and that gives u the length

#

what a stupid format jesus

#

there are literally augmentation types that arent constant size so this heuristic works only because luck

#
      if (fc->fde_encoding)
        encoded_ptr_size = size_of_encoded_value (fc->fde_encoding);

      fc->pc_begin = get_encoded_value (start, fc->fde_encoding, section);
      start += encoded_ptr_size;

      /* FIXME: It appears that sometimes the final pc_range value is
         encoded in less than encoded_ptr_size bytes.  See the x86_64
         run of the "objcopy on compressed debug sections" test for an
         example of this.  */
      SAFE_BYTE_GET_AND_INC (fc->pc_range, start, encoded_ptr_size, end);

objdump code

ornate jasper
#

i should work on cfi stuff at some point tbh. the zig std even already has it i just need to hook into/swap out out the register context

prime wraith
#

good thing zig does it for u

#

this is cancer

ornate jasper
#

yeah lol

#

if you want it for reference i can dig up the source for how zig does it btw

prime wraith
#

yeah sure that would be nice

#

if u can find how it decodes the pc_range value

ornate jasper
#

the only issue with the std version is it only accepts it if you have posix ucontext or windows RtlCaptureContext

#

(the build system lets me swap out the std though so its fine just annoying)

prime wraith
#

whats a ucontext?

ornate jasper
#

a struct for this function

proper tulip
#
        const pc_range = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{
            .pc_rel_base = 0,
            .follow_indirect = false,
        }) orelse return bad();```
prime wraith
#

bruh i see new syscalls every day

proper tulip
#

Frame Description Entry

#

idk if that's the right thing

prime wraith
#

can u explain in C terms

#

what it does

proper tulip
#

i have no idea what this is supposed to do

#

i have no idea how this format works 😁

#

am i even looking at the right thing?

prime wraith
#

you are yeah

proper tulip
#

interesting, so yeah, it seems to get the encoding from the CIE (Common Information Entry)

prime wraith
#

if im reading this right this will blow up

ornate jasper
proper tulip
#

i linked that above

prime wraith
#

it reuses the same encoding for pc_range as it does for pc_begin

#

but pc_range is a raw value

ornate jasper
#

ah yeah cool

prime wraith
#

so it will probably decode a bogus value here

#

that is if i read it correctly

ornate jasper
#

if theres no base it just intCasts it to u64

#

and actually theres a bug here those intcasts should probably be bitcasts, i think i already opened an issue on zig for that tho

proper tulip
#

it does pc_rel_base = 0 and follow_indirect = false

#

so it should give you a raw value

ornate jasper
#

ah yeah in the args

#

the ctx is a local struct to the call (probably so args can be optional without it being pain)

prime wraith
#

for example for my kernel it would read it as EH.PE.sdata4

#

because thats the encoding for pc_start

ornate jasper
#

in which case itd read it as an i32

proper tulip
#

yes, there's also a rel_mask

#

idk what that would be

ornate jasper
#

and then depending on the fde pointer encoding either add nothing or add the passed pc_rel_base which is 0 so also adding nothing

#

and then the last big if in the function goes to the false path because follow_indirect was set false in the args

prime wraith
#

in this case rel_mask will be datarel i think

#

so it will end up with a huge value

#

or sorry pcrel

proper tulip
#

hmm

#

oh yeah

#

then pc_base = 0

#

so you end up with a raw value

#

that's good, right?

prime wraith
#

pc_start is encoded as DW_EH_PE_pcrel | DW_EH_PE_sdata4

#

those are ORed values

proper tulip
#

yeah you have a type_mask and rel_mask

ornate jasper
#

yeah pc_rel_base was passed in as 0 to the function for this call

#

so it adds 0 which is fine

prime wraith
#

ohhhh

#

smart

#

good workaround

proper tulip
#

there's also

    if ((enc & EH.PE.indirect) > 0 and ctx.follow_indirect) {
        if (@sizeOf(usize) != addr_size_bytes) {
            // See the documentation for `follow_indirect`
            return error.NonNativeIndirection;
        }

        const native_ptr = cast(usize, ptr) orelse return error.PointerOverflow;
        return switch (addr_size_bytes) {
            2, 4, 8 => return @as(*const usize, @ptrFromInt(native_ptr)).*,
            else => return error.UnsupportedAddrSize,
        };
    } else {
        return ptr;
    }```
#

and follow_indirect is false

#

so it just returns the actual raw value

ornate jasper
#

so that falls through to return ptr yeah

prime wraith
#

would it sign-extend this i32 tho?

#

i hope not

ornate jasper
#

the trick is to pass context info that isnt the real context but makes the same logic work

proper tulip
#

it reads an i32, sign extends it to i64 and bit casts it to u64

#

oh wait sorry

ornate jasper
#

oh that wasnt meant to reply

proper tulip
#

i thought this was a raw union, not a tagged one

ornate jasper
#

intCast panics if you try to cast a negative to unsigned

prime wraith
#

its only a theoretical problem if u have a frame thats at least 1 << 31 long

proper tulip
#

it extends it to 64 bits first

#

so 1 << 63 :^)

ornate jasper
#

(well on safe modes it does, on unsafe modes intcast is undefined behavior)

prime wraith
proper tulip
#

well it reads it as a 32 bit integer

#

so yeah you're right, sorry

prime wraith
#

ah

ornate jasper
#

if its sdata4 it does sign extend to 64 bits

proper tulip
#

but i think it "ensures" it's not negative

#

by the means of @intCast

prime wraith
#

then it will panic/die for very large frames

#

when it shouldnt technically

ornate jasper
#

i already have an open issue for zig's debug stuff breaking for kernel mode things with higher half sized numbers

#

thats a zig issue here

proper tulip
#

it goes from i32->i64->u64 where the last one is a checked conversion to ensure it's within range of u64

ornate jasper
#

yeah thats a bug that comes from this having only been tested officially for normal usermode stuff

ornate jasper
#

because if the frame is that big it cant fit in signed 32-bits anyway

#

so thats an issue with the format

prime wraith
#

the thing is its not stored as a signed

#

its stored as a u32

ornate jasper
#

then it should be udata4

#

no?

prime wraith
#

ok let me go back

#

pc_start has a special encoding

proper tulip
#

i don't think you have to worry about that

prime wraith
#

using the dwarf format

proper tulip
#

they might just change that if needed

#

they as in

prime wraith
#

but pc_range DOES NOT

proper tulip
#

the piece of code generating the dwarf info

prime wraith
#

its a raw value

#

but its bit width is the same as pc_start

#

does that make sense?

ornate jasper
#

so yeah thats a bug here

#

in zig

prime wraith
#

well technically not

#

they tried to work around the crappy encoding using base 0 pcrel

#

it will work for smaller frames

#

where i32 == u32

ornate jasper
#

yep

prime wraith
#

what u should be doing there is checking how many bytes it took to decode pc_start, and then just memcpy that many bytes into an integer

#

and the format of pc_start is known

ornate jasper
#

so the core issue is that pc_range should never use a signed encoding

prime wraith
#

id say the core issue is it tries to decode a value thats not even encoded

ornate jasper
#

ok fair lol

prime wraith
#

but the fact that its not encoded is like

#

ridiculous

ornate jasper
#

dwarf moment ig

prime wraith
#

yep

#

i guess various implementations found different solutions for parsing it

#

literally all other fields are: hardcoded encoding (e.g. ULEB128), encoding specified in a field, encoding specified in an augmentation

#

this field: uhhh just memcpy it based on heuristics

ornate jasper
#

rip

prime wraith
#

llvm is interesting

#

they discard the rel value as well

ornate jasper
#

what does the 0x0F work out to?

prime wraith
#

s32

#

so basically same as zig

ornate jasper
#

ah ok

#

might be why zig does what it does then

prime wraith
#

yeah thats probably where they got it from tbh lol

prime wraith
ornate jasper
#

so the & 0x0F just makes it relative to nothing

prime wraith
#

basically yeah, literally same as zig, except they mask it in a different way

#

although in llvm this will not sign extend probably

#

so they will get the right value anyway

#

ah fuck no nvm

  case DW_EH_PE_sdata4:
    // Sign extend from signed 32-bit value.
    result = (pint_t)(int32_t)get32(addr);
    p += 4;
    addr = (pint_t) p;
    break;
#

well maybe if LLVM can afford to just interpreter this field like that then i can too

ornate jasper
#

yeah

#

somehow a segment being big enough to trip that seems unlikely lol

prime wraith
#

i mean in a kernel thats higher half u cant possibly have that anyway

#

its limited to 2gb

#

unless u place it further but then u cant mcmodel=kernel

ornate jasper
#

yeah

#

didnt actually know that was part of mcmodel=kernel lol

#

still dont entirely understand what that does

prime wraith
#

also i just realized

this

*out_value += is_signed ? value.as_i32 : value.as_u32;

is different from this

if (is_signed)
    *out_value += value.as_i32;
else
    *out_value += value.as_u32;
#

im still not sure how

#

but one breaks and other doesnt

ornate jasper
#

which one breaks?

prime wraith
#

the former

#

im assuming its because it doesnt get sign extended

#

but idk why

proper tulip
#

they are in fact different

#

part of me says it makes sense

ornate jasper
#

the whole ternary has to have the same type

proper tulip
#

but i can't tell you why either tf

#

yeah that basically

ornate jasper
#

whereas the if lets the two use different coercions

proper tulip
#

but i don't see why it would be different

#

the ternary should have the same value

#

as the individual ones

prime wraith
#

maybe it first casts to u32 because thats the rhs side, and then upcasts to u64 so no sign extension?

proper tulip
#

beacuse it's part of += anyway

ornate jasper
#

whereas the if does a direct cast from signed to u64

#

if you put manual casts in the ternary does that fix it?

prime wraith
#

thats the only explanation i can think of

prime wraith
#

let me try

#

actually an i32 to u64 cast might trigger a warning but lets see

ornate jasper
#

you might only need one manual cast

#

but idk

prime wraith
#

*out_value += is_signed ? (u64)value.as_i32 : value.as_u32;

#

this seems to produce the right result

ornate jasper
#

ok yeah it was a result type priority thing

prime wraith
#

when i thought i knew C crying_sunglasses

ornate jasper
#

where the compiler finding a common type for the ternary branches didnt take the += into account

prime wraith
#

basically yeah

#

okay some other libunwind

#

maybe its me who's retarded?

ornate jasper
#

maybe

prime wraith
#

maybe what they were trying to say here is in fact that code

#

aka ignore the upper 4 bits

ornate jasper
#

i mean absolute value doesnt mean raw to me

#

just not relative

prime wraith
#

then objdump is wrong

ornate jasper
#

lmao

prime wraith
#

so far llvm, zig and this libunwind seem to do this

#

and objdump is the only one that disagrees

prime wraith
ornate jasper
#

based on that spec text id have ended up with the zig impl personally

prime wraith
#

yeah like

#

this is not even the official spec btw

ornate jasper
#

what does the official spec say?

prime wraith
#

even better

ornate jasper
#

lmao thanks spec

prime wraith
#

oh okay a "number of bytes"

#

computer go parse a number

prime wraith
#

ok im actually like

#

insanely bad at binary search

#

im always off by one no matter what i do

vestal zealot
#

I've been using it forever and it never failed me once.

#

You could even just copy-paste it and add a link to the gh repo since it's licensed MIT trl

prime wraith
#

the problem is not like a basic binary search

#

this one i could do just fine

#

its the one where i need the lower bound value

#

aka greater than or eq

#

basically where im searching in an array of pointers and i need to find the closest pointer right below

#

so that would be lower bound - 1

#

i cant for the life of me figure out which value corresponds to lb

vestal zealot
prime wraith
vestal zealot
#

My function returns the index where the value should be, in combination with a boolean that says if that value actually is there.

prime wraith
#

oh ok u do the java thing

#

i think

vestal zealot
#

Java does it with negative integers but yeah same idea.

prime wraith
#

im gonna write some code and see how it behaves under a debugger i guess, otherwise im just randomly adding ones

prime wraith
#

okay i just needed this the entire time

#

seems to work

carmine token
vestal zealot
#

I didn't know that this was what infy wanted to do

prime wraith
#

i cant really have duplicates

#

that said i found the perfect algo for floor() binary search

while (end - begin > 1) {
    i = begin + ((end - begin) / 2);

    if (data[i] <= target)
        begin = i;
    else
        end = i;
}
#

begin is always <= target in this one

#

or well, 0 if target is below the first element

red parcel
#

Reminds me of sleep sort

prime wraith
#

lol

vestal zealot
#

If you don't have duplicates in your array then my binary search would've also worked.

prime wraith
#

lol unwind opcodes with -fomit-frame-pointers are so trivial

#

it literally just keeps offsetting CFA as more stack allocations keep happening

#

if u enable frame pointers it adds a ton more stuff

#

like switching the CFA reg to RBP etc

ornate torrent
#

for >99% of compiler-generated stack frames, you don't need to care at all for any registers other than rip and cfa

#

the effect of -fomit-frame-pointers is that it opts in to a behavior where cfa is no longer preferred to be rbp

#

I'd imagine that with VLAs or any use of __builtin_alloca it's very possible for the compiler to make rbp cfa anyway.

#

Not to mention nontrivial destructors in C++, which, unless you compile with -fno-exceptions, probably require way more CFI to be able to unwind past scopes with RAII types in them.

prime wraith
#

Luckily for me its just C so I don't have to support that

sterile kayak
#

But I could be wrong about that

carmine token
#

i think that's accurate

#

but you do need to restore all registers for that to work etc

#

afaict

prime wraith
#

holy fucking shit

#

only took 700 loc

#

this is completely fp-less

#

what got me is the fact that the CFA rule/offset propagates down to the previous frame

#

took a while to figure out

#

also i wasnt offsetting the RSP in my fake reg frame to mask the ret address

#

basicaly how it works, its completely arch agnostic too, just needs a few defines and a way to suspend ctx/convert arch regs to dwarf layout

ornate torrent
#

(after having done the massive sidequest that is ACPI)

prime wraith
#

lmao thanks

#

yeah the format is absolute cancer

ornate torrent
#

ikr

prime wraith
#

i wouldnt recommend implementing it, and probably wouldnt if i knew how bad it was from the start

ornate torrent
#

do you parse it in-kernel, or do you run a prepass on it at build time (much like how I assume ksymtab works)?

prime wraith
#

parse entirely in kernel

#

binary search eh_frame_hdr to find the fde, then parse that to find cie, than execute both etc etc

ornate torrent
#

yeah... I wouldn't want that sort of stuff in my kernel

prime wraith
#

lol

#

i do bounds checking and stuff but yeah

#

u have to trust your compiler

ornate torrent
#

Previously I ran a prepass where I generated a binary myself containing much easier to parse debug information, and then linked that binary into the kernel somehow (idr exactly how, though :P).

prime wraith
#

lol yeah kinda like orc

#

i mean honestly 700 loc is not that bad considering i made it check literally everything before proceeding

#

it bails out basically on tiniest things

#

but for symbols i do preprocess myself

#

using my kallsyms ™ generator

#

thats a 850 loc python file

#

(and is also slow af)

ornate torrent
#

this thing

prime wraith
#

generating your own simpler dwarf from real dwarf sounds kinda hard so im curious

ornate torrent
ornate torrent
#

well it's not DWARF

ornate torrent
prime wraith
#

that looks massive

#

what did it do exactly?

#

I mean i see it parses dwarf

ornate torrent
#

it generates two data structures:

#
  1. a lookup table, from address to symbol name
#

(this is just an array of (address, symbol) tuples sorted by address iirc)

prime wraith
#

why are u not using it anymore btw

ornate torrent
#
  1. a lookup table from address to unwind information (where the unwind information tells offset to "CFA-like" and flags (unwindable/not unwindable, IRQ frame etc.)
ornate torrent
prime wraith
#

so basically u simplified dwarf by parsing it at compile time

#

i mean honstly the dwarf produced for a C kernel is so simple i dont see the need for that

ornate torrent
#

this snippet captures relatively well the build system hell that I created to accomodate stuff like this

prime wraith
#

the pain of a make user

ornate torrent
#

well you're writing a Linux "clone" aren't you? shouldn't that also include the build system?

prime wraith
#

nah lol

ornate torrent
prime wraith
#

hopefully its only abi wise

#

otherwise i would do ORC trl

#

but i've looked at orc and it's not that different from dwarf tbh lol, i'd have to look really hard to understand what exactly they were solving there

ornate torrent
prime wraith
#

but which part specifically

ornate torrent
#

IDK, you'd have to refer to the mailing lists for that one. meme

prime wraith
#

no like, which aspect of dwarf do u consider dangerous

ornate torrent
#

nothing is dangerous per se

prime wraith
#

i'd honestly argue a frame pointer unwinder is more dangerous because it won't handle NMIs correctly unless u try super hard

#

whereas this thing will because its designed to work with arbitrarily suspended execution state

ornate torrent
#

yeah

#

my old solution did handle NMI

vestal zealot
prime wraith
#

well hardware errors, watchdogs, etc

ornate torrent
#

but a IMO valid solution is 'what is NMI?'

prime wraith
#

stuff that is kinda urgent

#

i guess profiling as well

#

some virtualization stuff uses NMIs iirc?

ornate torrent
#

like the only two reasons that I can think of that you want NMI is:

  1. performance related, profiling.
  2. NMI reboot.
#

and for 2, just hold down the goddamn power button

vestal zealot
#

Is it a thing that's really mandatory for a kernel to support?

ornate torrent
prime wraith
#

well if by support u mean hlt then yeah

#

or just panic()

#

most hobby kernels do that

vestal zealot
#

Yeah ok

prime wraith
vestal zealot
#

NMI seems like a PITA to try to ever resume from anyway

prime wraith
#

just needs some carefully written code

#

and some core things to be reentrant

#

like logging and stuff related to the type of work u do in an nmi handler

vestal zealot
#

Anyway I just do -fno-omit-frame-pointer unwinding and dump the addresses to serial. As soon as either it traps while trying to read or it sees NULL it stops.

prime wraith
#

thats fair i guess

vestal zealot
#

Then my funny python script runs addr2line over it

#

Works well enough and as soon as you need an actual debugger you'd just use its unwinding instead.

#

Do you have some kind of roadmap of what you plan to do? It seems like you're starting with various utility things before you get to the actual meat of the kernel?

prime wraith
#

i have it in my brain i guess

vestal zealot
#

Like any good programmer meme

prime wraith
#

once i clean up unwind stuff ill do

  • gdt
  • vmemmap
  • buddy allocator
  • slab allocator
  • havent thought that far
#

this list will probably take me like 6 months

#

since i will keep coming up with related stuff around those subsystems that needs to be done before i implement them

#

i already technically have everything needed to implement vmemmap, all the primitives to allocate contiguous chunks and map them arbitrarily

#

but hidden in there is like an arch-agnostic tlb invalidation mechanism/api etc

#

tons of shit basically

#

i wanna have a very very stable foundation before moving forward

vestal zealot
prime wraith
#

yeah thats like a very complex thing to get right

vestal zealot
#

That should probably go after I'm done with my current goal of real disk with real FS

prime wraith
#

ill have to probably spend like 10 more hours just studying how it works high level

vestal zealot
prime wraith
#

oh i also missed the idt in the list lol the reason i even got into backtracing and symbols was because i started looking into why people did CFI in their idt handlers

#

and that got me onto like a 3 week long tangent

#

but now i have basically military grade symbols and backtracing will be very good as well

prime wraith
#

Also fuck I just remembered per cpu variables and areas

#

Not looking forward

twilit geode
#

just have every cpu map a page of memory at 0xffffbeef00000000

vestal zealot
# prime wraith Also fuck I just remembered per cpu variables and areas

I statically allocate the one for the BSP (which always gets ID 0 assigned, regardless of what hardware core it is) and put it into a CPU-local place immediately (in my case, the current isr_ctx_t stores a pointer to the cpulocal_t struct). When other processors are brought up, more of these structs are allocated to them and passed as an argument when starting them up.

prime wraith
#

Yeah but for example Linux has per cpu globals or even struct fields

#

I have no idea how those are implemented

vestal zealot
#

I imagine with similar mechanisms to how userland does TLS

prime wraith
#

Probably yeah

vestal zealot
#

However I personally think it's cleaner not to allow CPU-local globals and am generally not a super big fan of TLS

ember reef
prime wraith
#

I'm aware

#

If u wanted u could have an array indexed by lapic id

vestal zealot
#

After what I've learned from my kernel, I'd still advocate for the approach of having one struct per CPU hidden in some register like sscratch or at gs

prime wraith
#

Ez per cpu stuff

vestal zealot
prime wraith
#

I'm curious how they do per cpu struct fields

#

Probably by dynamically resizing per cpu areas

#

Still cool

#

Like u can survive without that obviously

ember reef
#

you do want other components to be able to do per-cpu things without necessarily needing to add more stuff to the big per-cpu struct

#

especially if its like a kernel module

#

important for scalability

vestal zealot
#

Well at that point we're discussing how big the scope should be, no?

prime wraith
vestal zealot
#

I can think of lots of things you need in a kernel before it seems like this would before a limiting factor.

ember reef
prime wraith
#

It being?

vestal zealot
#

And if you need CPU-local in modules that bad, just make a fast function to get the CPU ID and have a table somewhere.

#

^ This is a way to do CPU-local without TLS-like globals

ember reef
#

yeah that

#

works

#

brb while i write a config file parser in my kernel

prime wraith
#

Just do kconfig

vestal zealot
#

100% agree. I am still suffering for my poor config system choices.

#

It's cmake options now but I still don't really jive with it

prime wraith
#

Yeah id honestly just use that because it has so many frontends

ember reef
#

the macos kernel has at least one xml parser which is generated by yacc

#

in libkern

prime wraith
ember reef
#

they use it to parse .plist files

prime wraith
#

Thats userspace right

ember reef
#

no

prime wraith
#

Uhhh

ember reef
#

i said the kernel

prime wraith
#

I hope its at least a subset of XML

vestal zealot
prime wraith
#

Oh damn

#

So who parses the config then?

vestal zealot
#

This rust macro here:

macro_rules! cmake_config {
    ($name: ident, true) => {
        pub const $name: bool = true;
    };
    ($name: ident, false) => {
        pub const $name: bool = false;
    };
    ($name: ident, $value: literal) => {
        pub const $name: i32 = $value;
    };
    ($name: ident, $value: ident) => {
        pub const $name: &'static str = "$value";
    };
}
#

From a suitably horrific one-liner file:
cmake_config!(BOOT_PROTOCOL, limine);cmake_config!(SOC, generic);cmake_config!(ENABLE_ACPI, true);cmake_config!(STACK_SIZE, 16384);cmake_config!(EMBED_UBOOT, true);cmake_config!(NOMMU, false);cmake_config!(PAGE_SIZE, 4096);

prime wraith
#

So this is like runtime kernel config or?

vestal zealot
#

It's supposed to be for compile-time configuration including what features to enable for example.

#

But this of course doesn't work with Rust's #[cfg(...)]

prime wraith
#

How do u propagate these options to work at compile time

vestal zealot
# prime wraith How do u propagate these options to work at compile time

I generate a file, which Rust includes:

# Export configs in a way visible to Rust.
set(RUST_CONFIG_FILE "")
foreach(config_name ${ALL_CONFIGS})
    set(RUST_CONFIG_FILE "${RUST_CONFIG_FILE}cmake_config!(${config_name}, ${CONFIG_${config_name}});")
endforeach()
file(CONFIGURE OUTPUT "cmake_config.rs" CONTENT "${RUST_CONFIG_FILE}")
prime wraith
prime wraith
ember reef
#

its not particularly large and its 26 years old

prime wraith
#

Thats a header file tho

ember reef
#

so

prime wraith
#

But who knows maybe not

#

Xml is not exactly a trivial format

ember reef
#

it has survived like 18 years of people scouring xnu for shit thatll help them jailbreak ios

vestal zealot
#

Well currently I don't actually have any Rust that depends on config options but I'd do config::SOMETHINGIMAJIG used in expressions. This doesn't really let me do any code that depends on what needs to or needs not to compile with certain options. So I can't currently use DTB nor ACPI from Rust.

prime wraith
#

Isn't your entire os in rust?

vestal zealot
#

No, it's still majority C. I'm doing a sort of soft re-write in Rust so the policy is new code is Rust if possible but old code is not re-written just to make it Rust.

prime wraith
#

Ah

#

How are you liking rust

vestal zealot
#

Rust solves all the things I find most annoying about C:

  1. Memory bugs are much better protected against
  2. You can just have generic collections
  3. You have the legendary ? operator for error handling
#

So you could say I like it very much. This is basically my first time doing a major project in Rust.

prime wraith
#

Interesting

vestal zealot
#

There is a small stipulation, though: Rust's strictness can cause some weird issues with e.g. global variables. I made an unsafe const function today because to do safe mutex init, it either has to be in the .bss section or it must have an atomic thread fence, and I wanted a global mutex guarding some array.

prime wraith
#

Yeah I'm personally not a fan of how it does globals

vestal zealot
#

AFAIK there isn't really a way to enforce that you only use something from a compile-time context but never run-time so I marked it as unsafe to deter from accidentally not initializing a mutex properly.

vestal zealot
#

Oh and I haven't done this yet but: Have fun doing RCU in Rust meme

red parcel
#

something something globals bad

vestal zealot
red parcel
#

it reminds me of how v has that hack to have globals

#

or at least what it used to have, idk how it is now

vestal zealot
#

But the way I've been programming doesn't use globals that much so it's not too much of a problem.

red parcel
#

ok yeah if you want globals in v you have to both add -enable-globals and put them in __global

prime wraith
#

A lot of these core kernel primitives go against rust

#

Like intrusive stuff, rcus, containerof, globals

vestal zealot
#

Oh yes they do

ember reef
#

im pretty ignorant on rust because i tend to stay in my little topics and not stray too far but i should probably try doing something in it just to see right

prime wraith
#

Yeah

vestal zealot
#

Basically all primitives that integrate tightly with the scheduler in some way will be full of unsafe.

#

Fortunately, though, I've got a lot more Rust to write than just scheduler primitives so it's probably fine meme

#

I can't really make a blanket endorsement of Rust because I feel like it

prime wraith
#

Does it still have problems with oom propagation or did they fix that

vestal zealot
#

It's very much a thing where it's per person whether they like it

vestal zealot
red parcel
#

or you can be infy and implement an unwinder and catch the panic :^)

prime wraith
#

Real

#

I can do seh now

ember reef
#

soemtimes people ask me my opinion on rust for kernel dev and i keep having to say "i dont know ive never used it"

ember reef
prime wraith
#

I'm not going to its a meme

ember reef
#

whyd you implement an unwinder though

vestal zealot
#

I even went and made this little thing here:

impl From<AllocError> for Errno {
    fn from(_: AllocError) -> Self {
        Errno::ENOMEM
    }
}

So alloc errors integrate nicely with my standard EResult type

red parcel
#

tbh I don't think you can do that in C without some help from the compiler

prime wraith
#

Without frame pointers

red parcel
#

does clang have support for the msvc try/catch?

prime wraith
#

Iirc it was wonky

#

But it does

red parcel
#

ngl the main reason I force frame pointers is because my friend really doesn't want to emit cfi stuff in the jit

prime wraith
red parcel
#

so we just do the easy route of everything having frame pointers and unwinding with that whenever an exception is thrown

#

or well, we will once he will implement landing pads

prime wraith
#

That's fair

prime wraith
vestal zealot
#

You see, this is why I like Rust's ? operator so much. No wacky nonsense with try/catch that causes unexpected things to happen when something throws.

red parcel
ember reef
prime wraith
prime wraith
uneven abyss
vestal zealot
red parcel
#
for entry in stack:
    if entry in text section:
        print(entry)
#

best stack walk

uneven abyss
prime wraith
#

Like unironically

red parcel
#

tbf

prime wraith
#

The only problem is its impossible to catch signal frames

red parcel
#

especially if you don't have rodata in the text section

#

it will just work

vestal zealot
prime wraith
red parcel
#

if entry == interrupt_stub:
print("IRQ frame")

vestal zealot
#

XD

uneven abyss
prime wraith
#

Probably safer in nmis than a fp unwinder

ember reef
# prime wraith Don't tell me u have 10 level recursive unwind table for it

i only subtract the stack pointer at the start of the function. and its a risc with fixed size instructions. so basically i can just walk backwards 4 bytes at a time until i find an instruction that looks like subi sp, sp, X and the immediate value is the stack frame size. then i can add that to get to the next stack frame

vestal zealot
uneven abyss
#

like request coalescing

#

HDDs benefit from sending less requests so if you schedule requests you might have two with adjacent LBAs that you can merge

prime wraith
vestal zealot
ember reef
#

if one day it did, i would simply have to add more debug information like youre using now

prime wraith
uneven abyss
ember reef
prime wraith
vestal zealot
# uneven abyss wait wdym

I batch writes to disk in up to 64 blocks at once, and if you have a long contiguous access, you could easily (with the scatter-gather of AHCI) do multiple blocks in one ATA command.

vestal zealot
#

But I actually just do individual block reads. Or writes.

ember reef
#

on fox32 i just use a frame pointer

uneven abyss
#

which is fun

prime wraith
uneven abyss
# uneven abyss which is fun

basically if you start reading a file you can spam a bunch of read requests for that file's blocks to get them in cache

#

then further reads do less work

ember reef
vestal zealot
prime wraith
# ember reef it doesnt

Btw do u do some sort of safe stack deref when traversing it, or does it just die if a frame is bogus?

#

Like maybe propagate an error from the pf handler

vestal zealot
#

Interop between manual implementations of vtables in C and Rust traits is a thing™

uneven abyss
# vestal zealot You know, this sounds like something I could deal with if it weren't the case th...

one other cool thing about async IO is you can have an auto-boosting system where requests naturally get checked every so often (20ms for me) and boosted if enough time has passed and the queue they are boosting to has enough availability

        +-> Average completion time of level 0 is 261 ms
        +-> Average completion time of level 1 is 81 ms
        +-> Average completion time of level 2 is 90 ms
        +-> Average completion time of level 3 is 13 ms
        +-> Average completion time of level 4 is 0 ms``` like I submitted a few thousand requests here and I let all of them just naturally boost until dispatch and the timing is pretty ok
#

ignore that weird bit of prioritiy inversion with level 2, the priorities are PRNG generated so I think they're unbalanced sometimes

ember reef
vestal zealot
ember reef
#

one issue is that this doesnt work with pageable code

vestal zealot
#

Sounds like a cool optimization, just not one I'm in a happy spot to do.

ember reef
#

if a stack trace passes thru a function in a page of code that happened to be outpaged then itll suddenly end the stack trace and youll be sad

prime wraith
#

Probably wouldn't work anyway because u cant wake up the pager thread?

ember reef
#

yeah

prime wraith
#

U could force all paged kernel code to get read when attaching a debugger ig

ember reef
#

only works if you voluntarily break into the debugger

vestal zealot