📄 apcsintro.html
字号:
<!doctype html public "-//W3C//DTD HTML 3.2//EN"><html><head><title>APCS introduction</title><meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" /><meta http-equiv="content-language" content="en" /><meta name="resource-type" content="document"><meta name="copyright" content="This document copyright 2001 by Richard Murray. Use for non-profit and education purposes explicitly granted."><meta name="author" content="Richard Murray"><meta name="rating" content="general"></head><!-- /assembler/apcsintro.html --><!-- --><!-- (C) Copyright 2001 Richard Murray --><!-- Designed by Richard Murray --><!-- rmurray@heyrick.co.uk --><!-- --><body bgcolor="#f0f0f0" text="#000000" link="#0022dd" vlink="#002288"><table border = "0" width="100%"> <tr> <td align=center width=100> <img src="arm3.gif" width=79 height=78 align = middle> </td> <td> <h1 align="center"><font color="#800080">APCS introduction</font></h1> <h3 align="center"><font color="#800080">(ARM Procedure Call Standard)</font></h3> </td> <td align=center width=100> <img src="arm3.gif" width=79 height=78 align = middle> </td></table><p> <p><h2>Introduction</h2><i>APCS</i>, or ARM Procedure Call Standard, provides a mechanism for writing tightly definedroutines which may be interwoven with other routines. The most notable point about this is thatthere is no definition to <i>where</i> these routines come from. Some may be compiled C, somefrom compiled Pascal, and yet others written in assembler.<p>The APCS defines:<ul> <li> restrictions on the use of registers <br> <br> <li> conventions for using the stack <br> <br> <li> passing/returning arguments between function calls <br> <br> <li> the format of a stack-based structure which may be 'backtraced' to provide a list of functions (and parameters given) from the failure point backwards to the program entry</ul><p>The APCS is not a single given standard, but is a collection of standards which are similar butdiffer in certain situations. For example, APCS-R (used on RISC OS as we know it) says thatflags set on function entry should be reset on function exit. Under the 32 bit definition, it isnot always possible to know the entry flags (there is no USR_CPSR) so you do not need to restorethem. As you may expect, there is no compatibility between the versions. Code which expects theflags to be restored is likely to misbehave if they are not restored...<p>If you are developing an ARM based system, then there is no requirement to implement APCS. It isrecommended, as it is not difficult to implement, and it allows for a variety of benefits.<br>But, the here and now. APCS <i>must</i> be used if you are writing assembler code to hook intocompiled C. The compiler will expect certain conditions, and these <i>must</i> be met in youradd-in code. A good example is APCS defines that a1 to a4 may be corrupted, but v1 to v6 mustbe preserved.<br>By now, I'm sure you are scratching your head and saying 'a-what? v-what?'. So here is the APCS-Rregister definition...<p><h2>Registers</h2>APCS defines the registers using different names to our usual R0 to R14. With the power of theassembler preprocessor, you can define R0 etc, but it is just as well to learn the APCS namesin case you are modifying code written by others.<center><table> <tr bgcolor="#cccccc" align="center"> <td colspan = 3>Register names</td> </tr> <tr bgcolor="#dddddd"> <td><code>Reg # </code></td> <td><code>APCS </code></td> <td>Meaning</td> <tr> <td><code>R0</code></td> <td><code>a1</code></td> <td>Working registers</td> </tr> <tr> <td><code>R1</code></td> <td><code>a2</code></td> <td align = center>"</td> </tr> <tr> <td><code>R2</code></td> <td><code>a3</code></td> <td align = center>"</td> </tr> <tr> <td><code>R3</code></td> <td><code>a4</code></td> <td align = center>"</td> </tr> <tr> <td><code>R4</code></td> <td><code>v1</code></td> <td>Must be preserved</td> </tr> <tr> <td><code>R5</code></td> <td><code>v2</code></td> <td align = center>"</td> </tr> <tr> <td><code>R6</code></td> <td><code>v3</code></td> <td align = center>"</td> </tr> <tr> <td><code>R7</code></td> <td><code>v4</code></td> <td align = center>"</td> </tr> <tr> <td><code>R8</code></td> <td><code>v5</code></td> <td align = center>"</td> </tr> <tr> <td><code>R9</code></td> <td><code>v6</code></td> <td align = center>"</td> </tr> <tr> <td><code>R10</code></td> <td><code>sl</code></td> <td>Stack Limit</td> </tr> <tr> <td><code>R11</code></td> <td><code>fp</code></td> <td>Frame Pointer</td> </tr> <tr> <td><code>R12</code></td> <td><code>ip</code></td> <td> </td> </tr> <tr> <td><code>R13</code></td> <td><code>sp</code></td> <td>Stack Pointer</td> </tr> <tr> <td><code>R14</code></td> <td><code>lr</code></td> <td>Link Register</td> </tr> <tr> <td><code>R15</code></td> <td><code>pc</code></td> <td>Program Counter</td> </tr></table></center><p> <p>These names are not defined by standard in Acorn's <i>objasm</i> (version 2.00), though laterversions of objasm, and other assemblers (such as Nick Roberts' <i>ASM</i>) define them foryou.<br>To define a register name, you typically use the <code>RN</code> directive, at the very start ofyour program:<pre>a1 RN 0a2 RN 1a3 RN 2 ...etc...r13 RN 13sp RN 13r14 RN 14lr RN r14pc RN 15</pre>That example shows us two important things:<ol> <li> That registers can be multiply defined - you can have <i>both</i> 'r13' and 'sp'. <li> That registers can be defined from previously defined registers - 'lr' was defined from the setting of 'r14'.<br> (this is correct for <i>objasm</i>, other assemblers may not do this)</ol><h2>Design criteria</h2><ul> <li> Function calling should be fast, small, and easy to optimise (by compilers) <br> <br> <li> It should be able to cope with multiple stacks <br> <br> <li> It should be easy to write re-entrant and relocatable code; primarily with the writeable data seperated from the code <br> <br> <li> But above all, it should be simple so that assembler programmers may use it's facilities, and debuggers may be able to trace through the program fairly easily</ul><p><h2>Conformity</h2>A part of a program which conforms to APCS while making a call to an external function is knownas "conforming".<br>A program which conforms to the APCS at all times during it's execution (typically, a programgenerated by a compiler) is known as "strictly conforming".<br>The protocol would indicate that, provided you observe the correct entry and exit parameters, youmay do whatever you need within the confines of your own function, and still remain conformant.This, sometimes, is necessary, such as when writing SWI veneers that utilise a large number ofregisters for the actual SWI call.<p><h2>The stack</h2>The stack is a linked list of 'frames' which are linked through what is known as a 'backtracestructure'. This structure is stored at the high end of each frame.<br>Each block of the stack is allocated in descending address order. The register <code>sp</code>will <i>always</i> point to the lowest used address in the most recent frame. This fits in withthe tradition of a fully descending stack.<br>In APCS-R, the register <code>sl</code> refers to a stack limit, below which you cannotdecrement <code>sp</code>.<br>The memory that resides between the current stack point, and the current stack limit, shouldcontain nothing that is to be relied upon as another APCS function, when called, may well set upa stack block for itself.<p>There may be multiple stack chunks. These may be located at any address in memory, there is noconvention here. This, typically, would be used to provide multiple stacks for the same codewhich is executing in a re-entrant manner; an anology here is FileCore which provides itsservices to the currently available FileCore filing systems (ADFS, RAMFS, IDEFS, SCSIFS, etc) bysimply setting up 'state' information and calling the same pieces of code as is required.<p><h2>Backtrace</h2>The register <code>fp</code> (frame pointer) should be zero, or it should point to the last in alist of stack backtrace structures which will provide a means of 'unwinding' the program totrace backwards through the functions called.<p>The structure is:<pre> save code pointer [fp] <i>fp points here</i> return link value [fp, #-4] return sp value [fp, #-8] return fp value [fp, #-12] <i>points to next structure</i> [saved v7] [saved v6] [saved v5] [saved v4] [saved v3] [saved v2] [saved v1] [saved a4] [saved a3] [saved a2] [saved a1] [saved f7] three words [saved f6] three words [saved f5] three words [saved f4] three words</pre>The structure contains between four and twenty-seven words, those in square brackets beingoptional values. The only thing that can be said is that if they do exist, then they exist in thegiven order (ie, saved f4 will be lower in memory than saved a3, but a2-f5 might not exist).<br>The floating point values are stored in 'internal format' and are three words (12 bytes).<p>The fp register points to the stack backtrace structure for the currently executing function. Thereturn fp value should be zero, or a pointer to a stack backtrace structure created by thefunction which called the current function. The return fp value in this structure is a pointer tothe stack backtrace structure for the function that called the function that called the currentfunction; and so on back until the first function.<p>The return link value, return sp value, and return fp value are reloaded into pc, sp, and fpwhen the function exits.<p><pre> #include <stdio.h> void one(void); void two(void); void zero(void); int main(void) { one(); return 0; } void one(void) { zero(); two(); return; } void two(void) { printf("main...one...two\n"); return; } void zero(void) { return; } At the point of printing a message on the screen, our example APCS backtrace structure would be: fp ----> two_structure return link return sp return fp ----> one_structure ... return link return sp return fp ----> main_structure ... return link return sp return fp ----> 0 ...</pre>Therefore, we can examine fp and see the structure for function 'two', which would point to thestructure for function 'one', which would point to the structure for 'main', which points tozero to end. In this way, we can wind our way backward through the program and determine how wecame to be at our current crash point.<br>It is worth pointing out the 'zero' function, as that has executed and been done with by the timewe do our printing, so it was in the backtrace structure once, but it is no longer.<p>It is also worth pointing out that an APCS structure as the above is unlikely ever to begenerated for the code given. The reason for this is that functions which do not call any otherfunctions do not require full APCS headers.<br>For your perusal, this is the code generated by Norcroft C v4.00 for the above code...<pre><font size = "-1"> AREA |C$$code|, CODE, READONLY IMPORT |__main||x$codeseg| B |__main| DCB &6d,&61,&69,&6e DCB &00,&00,&00,&00 DCD &ff000008 IMPORT |x$stack_overflow|
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -