https://github.com/akkartik/mu/blob/master/subx/apps/handle.subx
  1 # A sketch of Mu-style handles or kinda-safe pointers, that add a modicum of
  2 # checking to dynamically allocated memory.
  3 #
  4 # This approach avoids using 'allocate' directly in favor of two primitives:
  5 #   - 'new', which allocates some space (the 'payload'), stores the address
  6 #     along with an opaque 'alloc id' in a 'handle', and prepends the same
  7 #     alloc id to the payload.
  8 #   - 'lookup', which checks that the alloc id at the start of a handle matches
  9 #     the alloc id at the start of the payload before returning the address.
 10 #
 11 # Layout of a handle:
 12 #   offset 0: alloc id
 13 #   offset 4: address
 14 
 15 == code
 16 #   instruction                     effective address                                                   register    displacement    immediate
 17 # . op          subop               mod             rm32          base        index         scale       r32
 18 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 19 
 20 # main:
 21     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
 22     # syscall(exit, Num-test-failures)
 23     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 24     b8/copy-to-EAX  1/imm32/exit
 25     cd/syscall  0x80/imm8
 26 
 27 new:  # ad : (address allocation-descriptor), n : int, out : (address handle)
 28     # . prolog
 29     55/push-EBP
 30     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 31     # . save registers
 32     50/push-EAX
 33     51/push-ECX
 34     52/push-EDX
 35     # ECX = n+4
 36     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
 37     81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               4/imm32           # add to ECX
 38     # EAX = allocate(ad, ECX)
 39     # . . push args
 40     51/push-ECX
 41     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 42     # . . call
 43     e8/call  allocate/disp32
 44     # . . discard args
 45     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 46     # EDX = out
 47     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
 48     # out->address = EAX
 49     89/copy                         1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDX+4)
 50     # if (EAX == 0) out->alloc_id = 0, return
 51     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
 52     75/jump-if-not-equal  $new:continue/disp8
 53     c7          0/subop/copy        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               0/imm32           # copy to *EDX
 54     eb/jump  $new:end/disp8
 55 $new:continue:
 56     # otherwise:
 57     # ECX = *Next-alloc-id
 58     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           1/r32/ECX   Next-alloc-id/disp32              # copy *Next-alloc-id to ECX
 59     # *EAX = *Next-alloc-id/ECX
 60     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
 61     # out->alloc_id = *Next-alloc-id
 62     89/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EDX
 63     # increment *Next-alloc-id
 64     ff          0/subop/increment   0/mod/indirect  5/rm32/.disp32            .             .           .           Next-alloc-id/disp32              # increment *Next-alloc-id
 65 $new:end:
 66     # . restore registers
 67     5a/pop-to-EDX
 68     59/pop-to-ECX
 69     58/pop-to-EAX
 70     # . epilog
 71     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 72     5d/pop-to-EBP
 73     c3/return
 74 
 75 test-new:  # - this test uses the bottom of the stack segment as scratch space
 76     # . prolog
 77     55/push-EBP
 78     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 79     # *Next-alloc-id = 0x34
 80     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .     Next-alloc-id/disp32  0x34/imm32        # copy to *Next-alloc-id
 81     # var ad/EAX : (address allocation-descriptor) = {0x0b000000, 0x0b00000a}
 82     68/push  0x0b00000a/imm32/limit
 83     68/push  0x0b000000/imm32/curr
 84     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
 85     # var handle/ECX = {0, 0}
 86     68/push  0/imm32/address
 87     68/push  0/imm32/alloc-id
 88     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 89     # new(ad, 2, handle/ECX)
 90     # . . push args
 91     51/push-ECX
 92     68/push  2/imm32/size
 93     50/push-EAX
 94     # . . call
 95     e8/call  new/disp32
 96     # . . discard args
 97     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 98     # check-ints-equal(handle->alloc_id, 0x34, msg)
 99     # . . push args
100     68/push  "F - test-new: alloc id of handle"/imm32
101     68/push  0x34/imm32
102     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
103     # . . call
104     e8/call  check-ints-equal/disp32
105     # . . discard args
106     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
107     # check-ints-equal(handle->address, 0x0b000000, msg)
108     # . . push args
109     68/push  "F - test-new: address of handle"/imm32
110     68/push  0x0b000000/imm32
111     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
112     # . . call
113     e8/call  check-ints-equal/disp32
114     # . . discard args
115     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
116     # check-ints-equal(*handle->address, 0x34, msg)
117     # . . push args
118     68/push  "F - test-new: alloc id of payload"/imm32
119     68/push  0x34/imm32
120     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
121     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
122     # . . call
123     e8/call  check-ints-equal/disp32
124     # . . discard args
125     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
126     # check-ints-equal(*Next-alloc-id, 0x35)
127     # . . push args
128     68/push  "F - test-new: next alloc id"/imm32
129     68/push  0x35/imm32
130     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-alloc-id/disp32              # copy to *Next-alloc-id
131     # . . call
132     e8/call  check-ints-equal/disp32
133     # . . discard args
134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
135     # clean up
136     # . *Next-alloc-id = 1
137     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               1/imm32           # copy to *EAX
138     # . epilog
139     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
140     5d/pop-to-EBP
141     c3/return
142 
143 test-new-failure:
144     # . prolog
145     55/push-EBP
146     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
147     # . *Next-alloc-id = 0x34
148     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-alloc-id/disp32  0x34/imm32  # copy to *Next-alloc-id
149     # define an allocation-descriptor with no space left
150     # . var ad/EAX : (address allocation-descriptor) = {0x10, 0x10}
151     68/push  0x10/imm32/limit
152     68/push  0x10/imm32/curr
153     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
154     # . var handle/ECX = {random, random}
155     68/push  1234/imm32/address
156     68/push  5678/imm32/alloc-id
157     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
158     # try to allocate
159     # . new(ad, 2, handle/ECX)
160     # . . push args
161     51/push-ECX
162     68/push  2/imm32/size
163     50/push-EAX
164     # . . call
165     e8/call  new/disp32
166     # . . discard args
167     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
168     # handle should be cleared
169     # . check-ints-equal(handle->alloc_id, 0, msg)
170     # . . push args
171     68/push  "F - test-new-failure: alloc id of handle"/imm32
172     68/push  0/imm32
173     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
174     # . . call
175     e8/call  check-ints-equal/disp32
176     # . . discard args
177     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
178     # . check-ints-equal(handle->address, 0, msg)
179     # . . push args
180     68/push  "F - test-new-failure: address of handle"/imm32
181     68/push  0/imm32
182     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
183     # . . call
184     e8/call  check-ints-equal/disp32
185     # . . discard args
186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
187     # Next-alloc-id should be unmodified
188     # . check-ints-equal(*Next-alloc-id, 0x34)
189     # . . push args
190     68/push  "F - test-new-failure: next alloc id"/imm32
191     68/push  0x34/imm32
192     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-alloc-id/disp32              # copy to *Next-alloc-id
193     # . . call
194     e8/call  check-ints-equal/disp32
195     # . . discard args
196     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
197     # clean up
198     # . *Next-alloc-id = 1
199     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               1/imm32           # copy to *EAX
200     # . epilog
201     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
202     5d/pop-to-EBP
203     c3/return
204 
205 == data
206 
207 # Monotonically increasing counter for calls to 'new'
208 Next-alloc-id:
209     01 00 00 00  # 1
210 
211 # . . vim:nowrap:textwidth=0