Assembler and ABI Resources: what changed
Revision 3 to 4. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | 1 | ## The Assembler | |
| 2 | 2 | ||
| 3 | 3 | The FPC Pascal Compiler translates Pascal source code into assembly language which is then processed by an assembler running as a separate backend. Some other Pascal compilers directly generate object modules or executable programs directly, i.e. they do not require a separate assembler. | |
| 4 | 4 | ||
| 5 | 5 | An assembler is itself an executable program that translates assembly language into an object module. In most cases the object modules are passed to a linker which then produces an executable program, although in some there are additional stages (code signing for secure operating systems, conversion to a binary for embedded systems and so on). | |
| 6 | 6 | ||
| 7 | 7 | ## The ABI | |
| 8 | 8 | ||
| 9 | 9 | The interface between an executable program and the underlying operating system is referred to as the Application Binary Interface or ABI. This includes the CPU's operating mode (e.g. whether word and address sizes default to 32 or 64 bits), operand alignment, function calling conventions, system call numbers, and a selection of constants (e.g. file open modes) and structures (e.g. as returned by the stat() function). It is also usually considered to include the format of the object modules, executable and library files. | |
| 10 | 10 | ||
| 11 | 11 | Obviously the ABI is grossly different between operating systems: in general a program compiled for Windows will not run on Linux and vice versa. In addition, however, there is a significant amount of variation between different "flavours" of related operating systems, for example not only are the system call numbers different between SPARC Solaris and SPARC Linux but they are different between SPARC Linux and x86 Linux. | |
| 12 | 12 | ||
| 13 | 13 | ## Purpose of this note | |
| 14 | 14 | ||
| 15 | 15 | In most cases FPC uses the GNU assembler ("as" or "gas") as its backend. However, the assembly language syntax expected by this is different for each target CPU, sections below give examples of this. The original incentive for this was because the author (MarkMLl) found that he needed to write an assembler reader for the MIPS processor, and that there was no straightforward comparison of existing formats on which he could base new code. | |
| 16 | 16 | ||
| 17 | 17 | In addition, in some cases the details of the assembly language format or the ABI specification are only available to users registered with the relevant manufacturer, where possible links to unofficial mirrors are given below for casual reference. | |
| 18 | 18 | ||
| 19 | 19 | ## Assembler source formats | |
| 20 | 20 | ||
| 21 | 21 | Assembler source emitted by the compiler's code generator has to be (a subset of what is) acceptable to the assembler for the relevant target CPU. In addition, small portions of the RTL (e.g. prt0.as) are of necessity written in assembler, and some Pascal source files (e.g. syscall.inc) contain inline assembler which the compiler has to be able to parse before it is passed to the backend. | |
| 22 | 22 | ||
| 23 | 23 | The list of CPUs below is taken from the compiler as of late 2011. Some of these are no longer supported, or exist merely as minimal stubs. | |
| 24 | 24 | ||
| 25 | 25 | ### Alpha | |
| 26 | 26 | ||
| 27 | 27 | This compiler exists only as a minimal stub. | |
| 28 | 28 | ||
| 29 | 29 | ### ARM | |
| 30 | 30 | ||
| 31 | 31 | This fragment is from FpSysCall alias FPC_SYSCALL6 in FPC's ./rtl/linux/arm/syscall.inc: | |
| 32 | 32 | ||
| 33 | 33 | ``` pascal | |
| 34 | 34 | asm | |
| 35 | 35 | stmfd sp!,{r4,r5,r6} | |
| 36 | 36 | ldr r4,param4 | |
| 37 | 37 | ldr r5,param5 | |
| 38 | 38 | ldr r6,param6 | |
| 39 | 39 | bl FPC_SYSCALL | |
| 40 | 40 | ldmfd sp!,{r4,r5,r6} | |
| 41 | 41 | end; | |
| 42 | 42 | ``` | |
| 43 | 43 | ||
| 44 | 44 | This fragment is from ret_from_fork in Linux's ./arch/arm/kernel/entry-common.S: | |
| 45 | 45 | ||
| 46 | - | ||
| 46 | + | ```text | |
| 47 | 47 | ENTRY(ret_from_fork) | |
| 48 | 48 | bl schedule_tail | |
| 49 | 49 | get_thread_info tsk | |
| 50 | 50 | ldr r1, [tsk, #TI_FLAGS] @ check for syscall tracing | |
| 51 | 51 | mov why, #1 | |
| 52 | 52 | tst r1, #_TIF_SYSCALL_TRACE @ are we tracing syscalls? | |
| 53 | 53 | beq ret_slow_syscall | |
| 54 | 54 | mov r1, sp | |
| 55 | 55 | mov r0, #1 @ trace exit [IP = 1] | |
| 56 | 56 | bl syscall_trace | |
| 57 | 57 | b ret_slow_syscall | |
| 58 | 58 | ENDPROC(ret_from_fork) | |
| 59 | 59 | ``` | |
| 60 | 60 | ||
| 61 | 61 | Note that register names are r0, r1 etc. without a sigil, and that register assignment is right-to-left. | |
| 62 | 62 | ||
| 63 | 63 | ### AVR | |
| 64 | 64 | ||
| 65 | 65 | This fragment is from part of fpc's startup code in /rtl/embedded/avr/start.inc: | |
| 66 | 66 | ||
| 67 | 67 | ``` pascal | |
| 68 | 68 | _start: | |
| 69 | 69 | {$ifdef CPUAVR_16_REGS} | |
| 70 | 70 | clr r17 | |
| 71 | 71 | {$else CPUAVR_16_REGS} | |
| 72 | 72 | clr r1 | |
| 73 | 73 | {$endif CPUAVR_16_REGS} | |
| 74 | 74 | ||
| 75 | 75 | // load stack pointer | |
| 76 | 76 | ldi r30,lo8(_stack_top) | |
| 77 | 77 | out 0x3d,r30 | |
| 78 | 78 | ldi r30,hi8(_stack_top) | |
| 79 | 79 | out 0x3e,r30 | |
| 80 | 80 | ||
| 81 | 81 | // Initialize .data section | |
| 82 | 82 | ldi XL,lo8(_data) | |
| 83 | 83 | ldi XH,hi8(_data) | |
| 84 | 84 | ldi YH,hi8(_edata) | |
| 85 | 85 | ldi ZL,lo8(_etext) | |
| 86 | 86 | {$ifdef CPUAVR_16_REGS} | |
| 87 | 87 | ldi ZH,hi8(_etext)+(0x40) // program memory mapped to $4000 in data space | |
| 88 | 88 | {$else CPUAVR_16_REGS} | |
| 89 | 89 | ldi ZH,hi8(_etext) | |
| 90 | 90 | {$endif CPUAVR_16_REGS} | |
| 91 | 91 | ``` | |
| 92 | 92 | ||
| 93 | 93 | Note that register names are r0, r1 etc. without a sigil, and that register assignment is right-to-left. | |
| 94 | 94 | ||
| 95 | 95 | ### i386 | |
| 96 | 96 | ||
| 97 | 97 | This fragment is from FpSysCall alias FPC_SYSCALL6 in FPC's ./rtl/linux/i386/syscall.inc: | |
| 98 | 98 | ||
| 99 | 99 | ``` pascal | |
| 100 | 100 | asm | |
| 101 | 101 | push %ebx | |
| 102 | 102 | push %edx | |
| 103 | 103 | push %esi | |
| 104 | 104 | push %edi | |
| 105 | 105 | push %ebp | |
| 106 | 106 | push %ecx | |
| 107 | 107 | cmp $0, sysenter_supported | |
| 108 | 108 | jne .LSysEnter | |
| 109 | 109 | movl %edx,%ebx // param1 | |
| 110 | 110 | pop %ecx // param2 | |
| 111 | 111 | movl param3,%edx // param3 | |
| 112 | 112 | movl param4,%esi // param4 | |
| 113 | 113 | movl param5,%edi // param5 | |
| 114 | 114 | movl param6,%ebp // param6 | |
| 115 | 115 | int $0x80 | |
| 116 | 116 | jmp .LTail | |
| 117 | 117 | .LSysEnter: | |
| 118 | 118 | movl %edx,%ebx // param1 | |
| 119 | 119 | pop %ecx // param2 | |
| 120 | 120 | movl param3,%edx // param3 | |
| 121 | 121 | movl param4,%esi // param4 | |
| 122 | 122 | movl param5,%edi // param5 | |
| 123 | 123 | movl param6,%ebp // param6 | |
| 124 | 124 | call psysinfo | |
| 125 | 125 | .LTail: | |
| 126 | 126 | pop %ebp | |
| 127 | 127 | pop %edi | |
| 128 | 128 | pop %esi | |
| 129 | 129 | pop %edx | |
| 130 | 130 | pop %ebx | |
| 131 | 131 | cmpl $-4095,%eax | |
| 132 | 132 | jb .LSyscOK | |
| 133 | 133 | negl %eax | |
| 134 | 134 | call seterrno | |
| 135 | 135 | movl $-1,%eax | |
| 136 | 136 | .LSyscOK: | |
| 137 | 137 | end; | |
| 138 | 138 | ``` | |
| 139 | 139 | ||
| 140 | 140 | This fragment is from ret_from_fork in Linux's ./arch/x86/kernel/entry_32.S: | |
| 141 | 141 | ||
| 142 | 142 | ``` pascal | |
| 143 | 143 | ENTRY(ret_from_fork) | |
| 144 | 144 | CFI_STARTPROC | |
| 145 | 145 | pushl %eax | |
| 146 | 146 | CFI_ADJUST_CFA_OFFSET 4 | |
| 147 | 147 | call schedule_tail | |
| 148 | 148 | GET_THREAD_INFO(%ebp) | |
| 149 | 149 | popl %eax | |
| 150 | 150 | CFI_ADJUST_CFA_OFFSET -4 | |
| 151 | 151 | pushl $0x0202 # Reset kernel eflags | |
| 152 | 152 | CFI_ADJUST_CFA_OFFSET 4 | |
| 153 | 153 | popfl | |
| 154 | 154 | CFI_ADJUST_CFA_OFFSET -4 | |
| 155 | 155 | jmp syscall_exit | |
| 156 | 156 | CFI_ENDPROC | |
| 157 | 157 | END(ret_from_fork) | |
| 158 | 158 | ``` | |
| 159 | 159 | ||
| 160 | 160 | Note that register names are eax, ebx etc. with % as a mandatory sigil, and that register assignment is left-to-right. | |
| 161 | 161 | ||
| 162 | 162 | ### IA-64 | |
| 163 | 163 | ||
| 164 | 164 | This compiler exists only as a minimal stub. | |
| 165 | 165 | ||
| 166 | 166 | ### M68K | |
| 167 | 167 | ||
| 168 | 168 | This compiler exists in FPC v1 and has lain dormant for a while but has since inclusion in FPC 2.x in the end of 2013. | |
| 169 | 169 | ||
| 170 | 170 | ### MIPS | |
| 171 | 171 | ||
| 172 | 172 | This fragment is from FpSysCall alias FPC_SYSCALL6 in FPC's ./rtl/linux/mips/syscall.inc: | |
| 173 | 173 | ||
| 174 | 174 | ``` pascal | |
| 175 | 175 | asm | |
| 176 | 176 | sw $4,0($23) | |
| 177 | 177 | sw $5,-4($23) | |
| 178 | 178 | sw $6,-8($23) | |
| 179 | 179 | sw $7,-12($23) | |
| 180 | 180 | sw $8,-16($23) | |
| 181 | 181 | sw $9,-20($23) | |
| 182 | 182 | sw $10,-24($23) | |
| 183 | 183 | sw $11,-28($23) | |
| 184 | 184 | sw $12,-32($23) | |
| 185 | 185 | sw $13,-36($23) | |
| 186 | 186 | sw $14,-40($23) | |
| 187 | 187 | addiu $23,$23,-44 | |
| 188 | 188 | ||
| 189 | 189 | move $2,$4 | |
| 190 | 190 | move $4,$5 | |
| 191 | 191 | move $5,$6 | |
| 192 | 192 | move $6,$7 | |
| 193 | 193 | move $7,$8 | |
| 194 | 194 | move $8,$9 | |
| 195 | 195 | lw $9,0($fp) | |
| 196 | 196 | ||
| 197 | 197 | subu $29,32 | |
| 198 | 198 | sw $8, 16($29) | |
| 199 | 199 | sw $9, 20($29) | |
| 200 | 200 | syscall | |
| 201 | 201 | nop | |
| 202 | 202 | addiu $29,32 | |
| 203 | 203 | ||
| 204 | 204 | beq $7,$0,.LDone | |
| 205 | 205 | nop | |
| 206 | 206 | lui $8,%hi(fpc_threadvar_relocate_proc) | |
| 207 | 207 | addiu $8,%lo(fpc_threadvar_relocate_proc) | |
| 208 | 208 | lw $8,0($8) | |
| 209 | 209 | bne $8,$0,.LThreaded | |
| 210 | 210 | nop | |
| 211 | 211 | lui $4,%hi(Errno+4) | |
| 212 | 212 | addiu $4,%lo(Errno+4) | |
| 213 | 213 | sw $2,0($4) | |
| 214 | 214 | b .LFailed | |
| 215 | 215 | nop | |
| 216 | 216 | .LThreaded: | |
| 217 | 217 | sw $2,-4($fp)#temp#sw $4 | |
| 218 | 218 | lui $4,%hi(errno) | |
| 219 | 219 | addiu $4,$4,%lo(errno) | |
| 220 | 220 | jal $8 | |
| 221 | 221 | nop | |
| 222 | 222 | lw $8,-4($fp) | |
| 223 | 223 | sw $8,0($2) | |
| 224 | 224 | .LFailed: | |
| 225 | 225 | li $2,-1 | |
| 226 | 226 | .LDone: | |
| 227 | 227 | ||
| 228 | 228 | addiu $23,$23,44 | |
| 229 | 229 | lw $4,0($23) | |
| 230 | 230 | lw $5,-4($23) | |
| 231 | 231 | lw $6,-8($23) | |
| 232 | 232 | lw $7,-12($23) | |
| 233 | 233 | lw $8,-16($23) | |
| 234 | 234 | lw $9,-20($23) | |
| 235 | 235 | lw $10,-24($23) | |
| 236 | 236 | lw $11,-28($23) | |
| 237 | 237 | lw $12,-32($23) | |
| 238 | 238 | lw $13,-36($23) | |
| 239 | 239 | lw $14,-40($23) | |
| 240 | 240 | ||
| 241 | 241 | end; | |
| 242 | 242 | ``` | |
| 243 | 243 | ||
| 244 | 244 | This fragment is from ret_from_fork in Linux's ./arch/mips/kernel/entry.S: | |
| 245 | 245 | ||
| 246 | - | ||
| 246 | + | ```text | |
| 247 | 247 | FEXPORT(ret_from_fork) | |
| 248 | 248 | jal schedule_tail # a0 = struct task_struct *prev | |
| 249 | 249 | ||
| 250 | 250 | FEXPORT(syscall_exit) | |
| 251 | 251 | local_irq_disable # make sure need_resched and | |
| 252 | 252 | # signals dont change between | |
| 253 | 253 | # sampling and return | |
| 254 | 254 | LONG_L a2, TI_FLAGS($28) # current->work | |
| 255 | 255 | li t0, _TIF_ALLWORK_MASK | |
| 256 | 256 | and t0, a2, t0 | |
| 257 | 257 | bnez t0, syscall_exit_work | |
| 258 | 258 | ||
| 259 | 259 | FEXPORT(restore_all) # restore full frame | |
| 260 | 260 | .set noat | |
| 261 | 261 | RESTORE_TEMP | |
| 262 | 262 | RESTORE_AT | |
| 263 | 263 | RESTORE_STATIC | |
| 264 | 264 | FEXPORT(restore_partial) # restore partial frame | |
| 265 | 265 | RESTORE_SOME | |
| 266 | 266 | RESTORE_SP_AND_RET | |
| 267 | 267 | .set at | |
| 268 | 268 | ``` | |
| 269 | 269 | ||
| 270 | 270 | Note that register names are 0, 1 etc. with \$ as a mandatory sigil, and that register assignment is right-to-left; versions of GNU as from 2.18 onwards also support symbolic register names a0, a1 etc. There is a delay slot after branch etc. instructions. | |
| 271 | 271 | ||
| 272 | 272 | ### PowerPC | |
| 273 | 273 | ||
| 274 | 274 | This fragment is from FpSysCall alias FPC_SYSCALL6 in FPC's ./rtl/linux/powerpc/syscall.inc: | |
| 275 | 275 | ||
| 276 | 276 | ``` pascal | |
| 277 | 277 | asm | |
| 278 | 278 | mr r0,r3 | |
| 279 | 279 | mr r3,r4 | |
| 280 | 280 | mr r4,r5 | |
| 281 | 281 | mr r5,r6 | |
| 282 | 282 | mr r6,r7 | |
| 283 | 283 | mr r7,r8 | |
| 284 | 284 | mr r8,r9 | |
| 285 | 285 | sc | |
| 286 | 286 | bns .LDone | |
| 287 | 287 | lis r10,(fpc_threadvar_relocate_proc)@ha | |
| 288 | 288 | lwz r10,(fpc_threadvar_relocate_proc)@l(r10) | |
| 289 | 289 | cmpwi r10,0 | |
| 290 | 290 | bne .LThreaded | |
| 291 | 291 | lis r4,(Errno+4)@ha | |
| 292 | 292 | stw r3,(Errno+4)@l(r4) | |
| 293 | 293 | b .LFailed | |
| 294 | 294 | .LThreaded: | |
| 295 | 295 | stw r3,temp | |
| 296 | 296 | mflr r3 | |
| 297 | 297 | mtctr r10 | |
| 298 | 298 | lis r4,(errno)@ha | |
| 299 | 299 | stw r3,retaddress | |
| 300 | 300 | lwz r3,(errno)@l(r4) | |
| 301 | 301 | bctrl | |
| 302 | 302 | lwz r4,temp | |
| 303 | 303 | lwz r5,retaddress | |
| 304 | 304 | stw r4,0(r3) | |
| 305 | 305 | mtlr r5 | |
| 306 | 306 | .LFailed: | |
| 307 | 307 | li r3,-1 | |
| 308 | 308 | .LDone: | |
| 309 | 309 | end; | |
| 310 | 310 | ``` | |
| 311 | 311 | ||
| 312 | 312 | This fragment is from ret_from_fork in Linux's ./arch/powerpc/kernel/entry_32.S: | |
| 313 | 313 | ||
| 314 | - | ||
| 314 | + | ```text | |
| 315 | 315 | ret_from_syscall: | |
| 316 | 316 | mr r6,r3 | |
| 317 | 317 | rlwinm r12,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */ | |
| 318 | 318 | /* disable interrupts so current_thread_info()->flags can't change */ | |
| 319 | 319 | LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */ | |
| 320 | 320 | /* Note: We don't bother telling lockdep about it */ | |
| 321 | 321 | SYNC | |
| 322 | 322 | MTMSRD(r10) | |
| 323 | 323 | lwz r9,TI_FLAGS(r12) | |
| 324 | 324 | li r8,-_LAST_ERRNO | |
| 325 | 325 | andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_USER_WORK_MASK|_TIF_PERSYSCALL_MASK) | |
| 326 | 326 | bne- syscall_exit_work | |
| 327 | 327 | cmplw 0,r3,r8 | |
| 328 | 328 | blt+ syscall_exit_cont | |
| 329 | 329 | lwz r11,_CCR(r1) /* Load CR */ | |
| 330 | 330 | neg r3,r3 | |
| 331 | 331 | oris r11,r11,0x1000 /* Set SO bit in CR */ | |
| 332 | 332 | stw r11,_CCR(r1) | |
| 333 | 333 | syscall_exit_cont: | |
| 334 | 334 | lwz r8,_MSR(r1) | |
| 335 | 335 | BEGIN_FTR_SECTION | |
| 336 | 336 | lwarx r7,0,r1 | |
| 337 | 337 | END_FTR_SECTION_IFSET(CPU_FTR_NEED_PAIRED_STWCX) | |
| 338 | 338 | stwcx. r0,0,r1 /* to clear the reservation */ | |
| 339 | 339 | lwz r4,_LINK(r1) | |
| 340 | 340 | lwz r5,_CCR(r1) | |
| 341 | 341 | mtlr r4 | |
| 342 | 342 | mtcr r5 | |
| 343 | 343 | lwz r7,_NIP(r1) | |
| 344 | 344 | FIX_SRR1(r8, r0) | |
| 345 | 345 | lwz r2,GPR2(r1) | |
| 346 | 346 | lwz r1,GPR1(r1) | |
| 347 | 347 | mtspr SPRN_SRR0,r7 | |
| 348 | 348 | mtspr SPRN_SRR1,r8 | |
| 349 | 349 | SYNC | |
| 350 | 350 | RFI | |
| 351 | 351 | ||
| 352 | 352 | 66: li r3,-ENOSYS | |
| 353 | 353 | b ret_from_syscall | |
| 354 | 354 | ||
| 355 | 355 | ret_from_fork: | |
| 356 | 356 | REST_NVGPRS(r1) | |
| 357 | 357 | bl schedule_tail | |
| 358 | 358 | li r3,0 | |
| 359 | 359 | b ret_from_syscall | |
| 360 | 360 | ``` | |
| 361 | 361 | ||
| 362 | 362 | Note that register names are r0, r1 etc. without a sigil, and that register assignment is right-to-left. | |
| 363 | 363 | ||
| 364 | 364 | ### PowerPC-64 | |
| 365 | 365 | ||
| 366 | 366 | Similar to PowerPC above. | |
| 367 | 367 | ||
| 368 | 368 | ### SPARC | |
| 369 | 369 | ||
| 370 | 370 | This fragment is from FpSysCall alias FPC_SYSCALL6 in FPC's ./rtl/linux/sparc/syscall.inc: | |
| 371 | 371 | ||
| 372 | 372 | ``` pascal | |
| 373 | 373 | asm | |
| 374 | 374 | mov %i0,%g1 | |
| 375 | 375 | mov %i1,%o0 | |
| 376 | 376 | mov %i2,%o1 | |
| 377 | 377 | mov %i3,%o2 | |
| 378 | 378 | mov %i4,%o3 | |
| 379 | 379 | ld [%i6+92],%o5 | |
| 380 | 380 | mov %i5,%o4 | |
| 381 | 381 | ta 0x10 | |
| 382 | 382 | bcc .LSyscOK | |
| 383 | 383 | nop | |
| 384 | 384 | mov %o0,%l0 | |
| 385 | 385 | sethi %hi(fpc_threadvar_relocate_proc),%o2 | |
| 386 | 386 | or %o2,%lo(fpc_threadvar_relocate_proc),%o2 | |
| 387 | 387 | ld [%o2],%o3 | |
| 388 | 388 | subcc %o3,%g0,%g0 | |
| 389 | 389 | bne .LThread | |
| 390 | 390 | nop | |
| 391 | 391 | sethi %hi(Errno+4),%o0 | |
| 392 | 392 | ba .LNoThread | |
| 393 | 393 | or %o0,%lo(Errno+4),%o0 | |
| 394 | 394 | .LThread: | |
| 395 | 395 | sethi %hi(Errno),%o0 | |
| 396 | 396 | or %o0,%lo(Errno),%o0 | |
| 397 | 397 | call %o3 | |
| 398 | 398 | ld [%o0],%o0 | |
| 399 | 399 | .LNoThread: | |
| 400 | 400 | st %l0,[%o0] | |
| 401 | 401 | mov -1,%o0 | |
| 402 | 402 | .LSyscOK: | |
| 403 | 403 | mov %o0,%i0 | |
| 404 | 404 | end; | |
| 405 | 405 | ``` | |
| 406 | 406 | ||
| 407 | 407 | This fragment is from ret_from_fork in Linux's ./arch/sparc/kernel/entry.S: | |
| 408 | 408 | ||
| 409 | - | ||
| 409 | + | ```text | |
| 410 | 410 | linux_fast_syscall: | |
| 411 | 411 | andn %l7, 3, %l7 | |
| 412 | 412 | mov %i0, %o0 | |
| 413 | 413 | mov %i1, %o1 | |
| 414 | 414 | mov %i2, %o2 | |
| 415 | 415 | jmpl %l7 + %g0, %g0 | |
| 416 | 416 | mov %i3, %o3 | |
| 417 | 417 | ||
| 418 | 418 | linux_syscall_trace: | |
| 419 | 419 | add %sp, STACKFRAME_SZ, %o0 | |
| 420 | 420 | call syscall_trace | |
| 421 | 421 | mov 0, %o1 | |
| 422 | 422 | cmp %o0, 0 | |
| 423 | 423 | bne 3f | |
| 424 | 424 | mov -ENOSYS, %o0 | |
| 425 | 425 | mov %i0, %o0 | |
| 426 | 426 | mov %i1, %o1 | |
| 427 | 427 | mov %i2, %o2 | |
| 428 | 428 | mov %i3, %o3 | |
| 429 | 429 | b 2f | |
| 430 | 430 | mov %i4, %o4 | |
| 431 | 431 | ||
| 432 | 432 | .globl ret_from_fork | |
| 433 | 433 | ret_from_fork: | |
| 434 | 434 | call schedule_tail | |
| 435 | 435 | mov %g3, %o0 | |
| 436 | 436 | b ret_sys_call | |
| 437 | 437 | ld [%sp + STACKFRAME_SZ + PT_I0], %o0 | |
| 438 | 438 | ||
| 439 | 439 | /* Linux native system calls enter here... */ | |
| 440 | 440 | .align 4 | |
| 441 | 441 | .globl linux_sparc_syscall | |
| 442 | 442 | linux_sparc_syscall: | |
| 443 | 443 | sethi %hi(PSR_SYSCALL), %l4 | |
| 444 | 444 | or %l0, %l4, %l0 | |
| 445 | 445 | /* Direct access to user regs, must faster. */ | |
| 446 | 446 | cmp %g1, NR_SYSCALLS | |
| 447 | 447 | bgeu linux_sparc_ni_syscall | |
| 448 | 448 | sll %g1, 2, %l4 | |
| 449 | 449 | ld [%l7 + %l4], %l7 | |
| 450 | 450 | andcc %l7, 1, %g0 | |
| 451 | 451 | bne linux_fast_syscall | |
| 452 | 452 | /* Just do first insn from SAVE_ALL in the delay slot */ | |
| 453 | 453 | ``` | |
| 454 | 454 | ||
| 455 | 455 | Note that register names are i0, i1 etc. with % as a mandatory sigil, and that register assignment is left-to-right. Registers are windowed, and there is a delay slot after branch etc. instructions. | |
| 456 | 456 | ||
| 457 | 457 | ### VIS | |
| 458 | 458 | ||
| 459 | 459 | This compiler exists only as a minimal stub. | |
| 460 | 460 | ||
| 461 | 461 | ### x86 | |
| 462 | 462 | ||
| 463 | 463 | Refer to i386 above. | |
| 464 | 464 | ||
| 465 | 465 | ### x86-64 | |
| 466 | 466 | ||
| 467 | 467 | This fragment is from FpSysCall alias FPC_SYSCALL6 in FPC's ./rtl/linux/x86_64/syscall.inc: | |
| 468 | 468 | ||
| 469 | 469 | ``` pascal | |
| 470 | 470 | asm | |
| 471 | 471 | movq sysnr, %rax { Syscall number -> rax. } | |
| 472 | 472 | movq param1, %rdi { shift arg1 - arg5. } | |
| 473 | 473 | movq param2, %rsi | |
| 474 | 474 | movq param3, %rdx | |
| 475 | 475 | movq param4, %r10 | |
| 476 | 476 | movq param5, %r8 | |
| 477 | 477 | movq param6, %r9 | |
| 478 | 478 | syscall { Do the system call. } | |
| 479 | 479 | cmpq $-4095, %rax { Check %rax for error. } | |
| 480 | 480 | jnae .LSyscOK { Jump to error handler if error. } | |
| 481 | 481 | negq %rax | |
| 482 | 482 | movq %rax,%rdx | |
| 483 | 483 | movq fpc_threadvar_relocate_proc,%rax | |
| 484 | 484 | leaq Errno,%r11 | |
| 485 | 485 | testq %rax,%rax | |
| 486 | 486 | jne .LThread | |
| 487 | 487 | movl %edx,8(%r11) | |
| 488 | 488 | jmp .LNoThread | |
| 489 | 489 | .LThread: | |
| 490 | 490 | pushq %rdx | |
| 491 | 491 | movq (%r11),%rdi | |
| 492 | 492 | call *%rax | |
| 493 | 493 | popq %rdx | |
| 494 | 494 | movl %edx,(%rax) | |
| 495 | 495 | .LNoThread: | |
| 496 | 496 | movq $-1,%rax | |
| 497 | 497 | .LSyscOK: | |
| 498 | 498 | end; | |
| 499 | 499 | ``` | |
| 500 | 500 | ||
| 501 | 501 | This fragment is from ret_from_fork in Linux's ./arch/x86/kernel/entry_64.S: | |
| 502 | 502 | ||
| 503 | 503 | ``` pascal | |
| 504 | 504 | ENTRY(ret_from_fork) | |
| 505 | 505 | DEFAULT_FRAME | |
| 506 | 506 | ||
| 507 | 507 | LOCK ; btr $TIF_FORK,TI_flags(%r8) | |
| 508 | 508 | ||
| 509 | 509 | push kernel_eflags(%rip) | |
| 510 | 510 | CFI_ADJUST_CFA_OFFSET 8 | |
| 511 | 511 | popf # reset kernel eflags | |
| 512 | 512 | CFI_ADJUST_CFA_OFFSET -8 | |
| 513 | 513 | ||
| 514 | 514 | call schedule_tail # rdi: 'prev' task parameter | |
| 515 | 515 | ||
| 516 | 516 | GET_THREAD_INFO(%rcx) | |
| 517 | 517 | ||
| 518 | 518 | RESTORE_REST | |
| 519 | 519 | ||
| 520 | 520 | testl $3, CS-ARGOFFSET(%rsp) # from kernel_thread? | |
| 521 | 521 | je int_ret_from_sys_call | |
| 522 | 522 | ||
| 523 | 523 | testl $_TIF_IA32, TI_flags(%rcx) # 32-bit compat task needs IRET | |
| 524 | 524 | jnz int_ret_from_sys_call | |
| 525 | 525 | ||
| 526 | 526 | RESTORE_TOP_OF_STACK %rdi, -ARGOFFSET | |
| 527 | 527 | jmp ret_from_sys_call # go to the SYSRET fastpath | |
| 528 | 528 | ||
| 529 | 529 | CFI_ENDPROC | |
| 530 | 530 | END(ret_from_fork) | |
| 531 | 531 | ``` | |
| 532 | 532 | ||
| 533 | 533 | Note that register names are rax, rbx etc. with % as a mandatory sigil, and that register assignment is left-to-right. | |
| 534 | 534 | ||
| 535 | 535 | ### zSeries (S/390) | |
| 536 | 536 | ||
| 537 | 537 | As of early 2012, IBM's [[ZSeries|zSeries]] (formerly System/390) is not supported by the compiler, refer to [[ZSeries/Part 1]] for the current implementation status. Note that Linux 2.6 probably won't run on anything older than an S/390 G5. | |
| 538 | 538 | ||
| 539 | 539 | #### For the GNU assembler (gas): | |
| 540 | 540 | ||
| 541 | 541 | This fragment is from ret_from_fork in Linux's ./arch/s390/kernel/entry.S: | |
| 542 | 542 | ||
| 543 | - | ||
| 543 | + | ```text | |
| 544 | 544 | #define BASED(name) name-system_call(%r13) | |
| 545 | 545 | ||
| 546 | 546 | .globl ret_from_fork | |
| 547 | 547 | ret_from_fork: | |
| 548 | 548 | l %r13,__LC_SVC_NEW_PSW+4 | |
| 549 | 549 | l %r9,__LC_THREAD_INFO # load pointer to thread_info struct | |
| 550 | 550 | tm SP_PSW+1(%r15),0x01 # forking a kernel thread ? | |
| 551 | 551 | bo BASED(0f) | |
| 552 | 552 | st %r15,SP_R15(%r15) # store stack pointer for new kthread | |
| 553 | 553 | 0: l %r1,BASED(.Lschedtail) | |
| 554 | 554 | basr %r14,%r1 | |
| 555 | 555 | TRACE_IRQS_ON | |
| 556 | 556 | stosm __SF_EMPTY(%r15),0x03 # reenable interrupts | |
| 557 | 557 | b BASED(sysc_tracenogo) | |
| 558 | 558 | ||
| 559 | 559 | sysc_tracenogo: | |
| 560 | 560 | tm __TI_flags+2(%r9),_TIF_SYSCALL | |
| 561 | 561 | bz BASED(sysc_return) | |
| 562 | 562 | l %r1,BASED(.Ltrace_exit) | |
| 563 | 563 | la %r2,SP_PTREGS(%r15) # load pt_regs | |
| 564 | 564 | la %r14,BASED(sysc_return) | |
| 565 | 565 | br %r1 | |
| 566 | 566 | ``` | |
| 567 | 567 | ||
| 568 | 568 | Note that register names are r0, r1 etc. with % as a mandatory sigil. Older assemblers (below) arranged the operands as ordered in the binary opcode (i.e. register assignment order varies by operation), this might have changed for the GNU assembler resulting in obvious compatibility problems. | |
| 569 | 569 | ||
| 570 | 570 | #### For the IBM assembler: | |
| 571 | 571 | ||
| 572 | 572 | For the purpose of comparison and with minimal comment, here are "Hello, World!" programs compiled with GCC for different targets. | |
| 573 | 573 | ||
| 574 | 574 | ``` pascal | |
| 575 | 575 | GCCMVS on MUSIC/SP hosted by Sim/390 using the command | |
| 576 | 576 | ||
| 577 | 577 | gccmvs_v30 -S -o hello.asm ... hello.c | |
| 578 | 578 | ||
| 579 | 579 | generates this: | |
| 580 | 580 | ||
| 581 | 581 | LC0 EQU * | |
| 582 | 582 | DC C'Hello, MUSIC/SP!' | |
| 583 | 583 | DC X'15' | |
| 584 | 584 | DC X'0' | |
| 585 | 585 | DS 0F | |
| 586 | 586 | EXTRN @@CRT0 | |
| 587 | 587 | * Function main prologue | |
| 588 | 588 | MAIN PDPPRLG CINDEX=0,FRAME=92,BASER=12,ENTRY=YES | |
| 589 | 589 | B FEN0 | |
| 590 | 590 | LTORG | |
| 591 | 591 | FEN0 EQU * | |
| 592 | 592 | DROP 12 | |
| 593 | 593 | BALR 12,0 | |
| 594 | 594 | USING *,12 | |
| 595 | 595 | PG0 EQU * | |
| 596 | 596 | LR 11,1 | |
| 597 | 597 | L 10,=A(PGT0) | |
| 598 | 598 | * Function main code | |
| 599 | 599 | MVC 88(4,13),=A(LC0) | |
| 600 | 600 | LA 1,88(,13) | |
| 601 | 601 | L 15,=V(PRINTF) | |
| 602 | 602 | BALR 14,15 | |
| 603 | 603 | SLR 2,2 | |
| 604 | 604 | LR 15,2 | |
| 605 | 605 | ``` | |
| 606 | 606 | ||
| 607 | 607 | In the example above, note that registers are identified numerically (e.g. BALR 12,0); it is common to load a macro package which introduces a leading R as a sigil (i.e. BALR R12,R0). The operands are ordered as in the binary opcode (i.e. register assignment order varies by operation), this might have changed for the GNU assembler (see section above) resulting in obvious compatibility problems. | |
| 608 | 608 | ||
| 609 | 609 | ``` pascal | |
| 610 | 610 | GCC on zSeries Linux hosted by Hercules using the command | |
| 611 | 611 | ||
| 612 | 612 | gcc -S -o hello.asm hello.c | |
| 613 | 613 | ||
| 614 | 614 | apparently assuming -march=g5, generates this: | |
| 615 | 615 | ||
| 616 | 616 | .LC0: | |
| 617 | 617 | .string "Hello, Linux!" | |
| 618 | 618 | .text | |
| 619 | 619 | .align 4 | |
| 620 | 620 | .globl main | |
| 621 | 621 | .type main, @function | |
| 622 | 622 | main: | |
| 623 | 623 | .LFB2: | |
| 624 | 624 | stm %r11,%r15,44(%r15) | |
| 625 | 625 | .LCFI0: | |
| 626 | 626 | basr %r13,0 | |
| 627 | 627 | .L3: | |
| 628 | 628 | ahi %r15,-96 | |
| 629 | 629 | .LCFI1: | |
| 630 | 630 | lr %r11,%r15 | |
| 631 | 631 | .LCFI2: | |
| 632 | 632 | l %r1,.L4-.L3(%r13) | |
| 633 | 633 | lr %r2,%r1 | |
| 634 | 634 | l %r1,.L5-.L3(%r13) | |
| 635 | 635 | basr %r14,%r1 | |
| 636 | 636 | lhi %r1,0 | |
| 637 | 637 | lr %r2,%r1 | |
| 638 | 638 | l %r4,152(%r11) | |
| 639 | 639 | lm %r11,%r15,140(%r11) | |
| 640 | 640 | br %r4 | |
| 641 | 641 | .align 4 | |
| 642 | 642 | .L5: | |
| 643 | 643 | .long puts | |
| 644 | 644 | .L4: | |
| 645 | 645 | .long .LC0 | |
| 646 | 646 | ||
| 647 | 647 | I think that's still effectively generating code similar to traditional | |
| 648 | 648 | assembler's "using". | |
| 649 | 649 | ``` | |
| 650 | 650 | ||
| 651 | 651 | ``` pascal | |
| 652 | 652 | With the explicit instruction to use a later CPU: | |
| 653 | 653 | ||
| 654 | 654 | gcc -march=z900 -S -o hello.asm hello.c | |
| 655 | 655 | ||
| 656 | 656 | it generates this: | |
| 657 | 657 | ||
| 658 | 658 | .LC0: | |
| 659 | 659 | .string "Hello, Linux!" | |
| 660 | 660 | .text | |
| 661 | 661 | .align 4 | |
| 662 | 662 | .globl main | |
| 663 | 663 | .type main, @function | |
| 664 | 664 | main: | |
| 665 | 665 | .LFB2: | |
| 666 | 666 | stm %r11,%r15,44(%r15) | |
| 667 | 667 | .LCFI0: | |
| 668 | 668 | ahi %r15,-96 | |
| 669 | 669 | .LCFI1: | |
| 670 | 670 | lr %r11,%r15 | |
| 671 | 671 | .LCFI2: | |
| 672 | 672 | larl %r2,.LC0 | |
| 673 | 673 | brasl %r14,puts | |
| 674 | 674 | lhi %r1,0 | |
| 675 | 675 | lr %r2,%r1 | |
| 676 | 676 | l %r4,152(%r11) | |
| 677 | 677 | lm %r11,%r15,140(%r11) | |
| 678 | 678 | br %r4 | |
| 679 | 679 | ||
| 680 | 680 | If I'm reading that correctly, it's referring to the string directly | |
| 681 | 681 | without using a register. | |
| 682 | 682 | ``` | |
| 683 | 683 | ||
| 684 | 684 | ## ABI references | |
| 685 | 685 | ||
| 686 | 686 | The list of CPUs etc. is based on those found in the compiler (see above). | |
| 687 | 687 | ||
| 688 | 688 | ### Alpha | |
| 689 | 689 | ||
| 690 | 690 | ### ARM | |
| 691 | 691 | ||
| 692 | 692 | ### AVR | |
| 693 | 693 | ||
| 694 | 694 | Currently only the embedded target is supported by FPC. The compiler follows the register layout [1](https://gcc.gnu.org/wiki/avr-gcc#Register_Layout), frame layout [2](https://gcc.gnu.org/wiki/avr-gcc#Frame_Layout) and calling convention [3](https://gcc.gnu.org/wiki/avr-gcc#Calling_Convention) of the avr-gcc ABI. | |
| 695 | 695 | ||
| 696 | 696 | ### i386 | |
| 697 | 697 | ||
| 698 | 698 | ### IA-64 | |
| 699 | 699 | ||
| 700 | 700 | ### M68K | |
| 701 | 701 | ||
| 702 | 702 | ### MIPS | |
| 703 | 703 | ||
| 704 | 704 | MIPS has several kinds of ABI, including O32, EABI, n32, n64, nowadays the de facto one should be O32. Some ABI document for MIPS O32 ABI can be found in the internet, unfortunately due to the copyright limit, we cannot provide it here. However, this page [4](http://www.linux-mips.org/wiki/MIPSABIHistory) has some useful links. | |
| 705 | 705 | ||
| 706 | 706 | ### PowerPC | |
| 707 | 707 | ||
| 708 | 708 | ### PowerPC-64 | |
| 709 | 709 | ||
| 710 | 710 | ### SPARC | |
| 711 | 711 | ||
| 712 | 712 | SPARC is an open architecture. All design documents are freely available (no signup required) from sparc.org and opensparc.net. sparc.org has documents on the 32 bit SPARC version V8 and the 64 bit SPARC version V9. Earlier versions of the architecture, specifically V7 are available on the net (search for Solbourne). | |
| 713 | 713 | ||
| 714 | 714 | You can download the SPARC V8 Architecture Manual here: [5](http://sparc.org/standards/V8.pdf) The SPARC V8 ABI for UNIX is here: [6](http://sparc.org/standards/psABI3rd.pdf) | |
| 715 | 715 | ||
| 716 | 716 | You can download the SPARC V9 Architecture Manual here: [7](http://sparc.org/standards/SPARCV9.pdf) The SPARC V9 ABI for UNIX is here: [8](http://sparc.org/standards/SCD.2.4.1.ps.Z) Newer versions than V9 are available on opensparc.net. | |
| 717 | 717 | ||
| 718 | 718 | ### VIS | |
| 719 | 719 | ||
| 720 | 720 | ### x86 | |
| 721 | 721 | ||
| 722 | 722 | ### x86-64 | |
| 723 | 723 | ||
| 724 | 724 | ### zSeries (S/390) | |
| 725 | 725 | ||
| 726 | 726 | The classic IBM operating systems, particularly when running on older hardware (i.e. preceding the S/390 G5) historically adopt a programming style which passes function parameters in registers and blocks of memory rather than using a stack. | |
| 727 | 727 | ||
| 728 | 728 | For Linux on a 32-bit S/390, "The first 5 integer (char, short, int, long, long long) and pointer parameters are passed in registers R2, R3, R4, R5 and R6. ... long long parameters are passed in register pairs. Structures of 1, 2, 4 or 8 bytes are passed as integers. ... the first 2 floating point parameters are passed in F0 and F2. ... All other parameters are passed on the stack. If the return value is not an integer, pointer, float, double or 1, 2, 4 or 9-byte structure, a "hidden" parameter in R2 will contain the address of the return area." [9](http://linuxvm.org/present/SHARE99/S8139db.pdf) | |
| 729 | 729 | ||
| 730 | 730 | ## Other resources | |
| 731 | 731 | ||
| 732 | 732 | As a general point, there's some useful thoughts on binary disassembly at <http://chdk.wikia.com/wiki/GPL_Disassembling> for situations where IDA or equivalent aren't available. | |
| 733 | 733 | ||
| 734 | 734 | --- | |
| 735 | 735 | *From the Free Pascal wiki, page "Assembler and ABI Resources", as it stood on 2022-06-18 after a change by Alextp, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* | |
| 736 | 736 | Category: FPC internals |
