@ -1,52 +0,0 @@ | |||
# Prerequisites | |||
*.d | |||
# Object files | |||
*.o | |||
*.ko | |||
*.obj | |||
*.elf | |||
# Linker output | |||
*.ilk | |||
*.map | |||
*.exp | |||
# Precompiled Headers | |||
*.gch | |||
*.pch | |||
# Libraries | |||
*.lib | |||
*.a | |||
*.la | |||
*.lo | |||
# Shared objects (inc. Windows DLLs) | |||
*.dll | |||
*.so | |||
*.so.* | |||
*.dylib | |||
# Executables | |||
*.exe | |||
*.out | |||
*.app | |||
*.i*86 | |||
*.x86_64 | |||
*.hex | |||
# Debug files | |||
*.dSYM/ | |||
*.su | |||
*.idb | |||
*.pdb | |||
# Kernel Module Compile Results | |||
*.mod* | |||
*.cmd | |||
.tmp_versions/ | |||
modules.order | |||
Module.symvers | |||
Mkfile.old | |||
dkms.conf |
@ -1,12 +0,0 @@ | |||
# | |||
# This file contains configuration variables for drivers. | |||
# It was generated by genhdrs.pl. Do not modify it. | |||
# | |||
package Driverhdrs; | |||
$LAB = "datalab"; | |||
$SERVER_NAME = "changeme.ics.cs.cmu.edu"; | |||
$SERVER_PORT = 8081; | |||
$COURSE_NAME = "csapp"; | |||
$AUTOGRADE_TIMEOUT = 0; | |||
1; |
@ -1,138 +0,0 @@ | |||
############################################################### | |||
# Driverlib.pm - A package of helper functions for Perl drivers | |||
# | |||
# Copyright (c) 2005 David R. O'Hallaron, All rights reserved. | |||
############################################################### | |||
package Driverlib; | |||
use Socket; | |||
# Autogenerated header file with lab-specific constants | |||
use lib "."; | |||
use Driverhdrs; | |||
require Exporter; | |||
@ISA = qw(Exporter); | |||
@EXPORT = qw( | |||
driver_post | |||
); | |||
use strict; | |||
##### | |||
# Public functions | |||
# | |||
# | |||
# driver_post - This is the routine that a driver calls when | |||
# it needs to transmit an autoresult string to the result server. | |||
# | |||
sub driver_post ($$) { | |||
my $userid = shift; # User id for this submission | |||
my $result = shift; # Autoresult string | |||
my $autograded = shift; # Set if called by an autograder | |||
# Echo the autoresult string to stdout if the driver was called | |||
# by an autograder | |||
if ($autograded) { | |||
print "\n"; | |||
print "AUTORESULT_STRING=$result\n"; | |||
return; | |||
} | |||
# If the driver was called with a specific userid, then submit | |||
# the autoresult string to the result server over the Internet. | |||
if ($userid) { | |||
my $status = submitr($Driverhdrs::SERVER_NAME, | |||
$Driverhdrs::SERVER_PORT, | |||
$Driverhdrs::COURSE_NAME, | |||
$userid, | |||
$Driverhdrs::LAB, | |||
$result); | |||
# Print the status of the transfer | |||
if (!($status =~ /OK/)) { | |||
print "$status\n"; | |||
print "Did not send autoresult string to the result server.\n"; | |||
exit(1); | |||
} | |||
print "Success: Sent autoresult string for $userid to the result server.\n"; | |||
} | |||
} | |||
##### | |||
# Private functions | |||
# | |||
# | |||
# submitr - Sends an autoresult string to the result server | |||
# | |||
sub submitr ($$$$$$) { | |||
my $hostname = shift; | |||
my $port = shift; | |||
my $course = shift; | |||
my $userid = shift; | |||
my $lab = shift; | |||
my $result = shift; | |||
my $internet_addr; | |||
my $enc_result; | |||
my $paddr; | |||
my $line; | |||
my $http_version; | |||
my $errcode; | |||
my $errmsg; | |||
# Establish the connection to the server | |||
socket(SERVER, PF_INET, SOCK_STREAM, getprotobyname('tcp')); | |||
$internet_addr = inet_aton($hostname) | |||
or die "Could not convert $hostname to an internet address: $!\n"; | |||
$paddr = sockaddr_in($port, $internet_addr); | |||
connect(SERVER, $paddr) | |||
or die "Could not connect to $hostname:$port:$!\n"; | |||
select((select(SERVER), $| = 1)[0]); # enable command buffering | |||
# Send HTTP request to server | |||
$enc_result = url_encode($result); | |||
print SERVER "GET /$course/submitr.pl/?userid=$userid&lab=$lab&result=$enc_result&submit=submit HTTP/1.0\r\n\r\n"; | |||
# Get first HTTP response line | |||
$line = <SERVER>; | |||
chomp($line); | |||
($http_version, $errcode, $errmsg) = split(/\s+/, $line); | |||
if ($errcode != 200) { | |||
return "Error: HTTP request failed with error $errcode: $errmsg"; | |||
} | |||
# Read the remaining HTTP response header lines | |||
while ($line = <SERVER>) { | |||
if ($line =~ /^\r\n/) { | |||
last; | |||
} | |||
} | |||
# Read and return the response from the result server | |||
$line = <SERVER>; | |||
chomp($line); | |||
close SERVER; | |||
return $line; | |||
} | |||
# | |||
# url_encode - Encode text string so it can be included in URI of GET request | |||
# | |||
sub url_encode ($) { | |||
my $value = shift; | |||
$value =~s/([^a-zA-Z0-9_\-.])/uc sprintf("%%%02x",ord($1))/eg; | |||
return $value; | |||
} | |||
# Always end a module with a 1 so that it returns TRUE | |||
1; | |||
@ -1,33 +0,0 @@ | |||
# | |||
# Makefile that builds btest and other helper programs for the CS:APP data lab | |||
# | |||
CC = gcc | |||
CFLAGS = -O -Wall -m32 | |||
LIBS = -lm | |||
all: btest fshow ishow | |||
btest: btest.c bits.c decl.c tests.c btest.h bits.h | |||
$(CC) $(CFLAGS) $(LIBS) -o btest bits.c btest.c decl.c tests.c | |||
fshow: fshow.c | |||
$(CC) $(CFLAGS) -o fshow fshow.c | |||
ishow: ishow.c | |||
$(CC) $(CFLAGS) -o ishow ishow.c | |||
# Forces a recompile. Used by the driver program. | |||
btestexplicit: | |||
$(CC) $(CFLAGS) $(LIBS) -o btest bits.c btest.c decl.c tests.c | |||
clean: | |||
rm -f *.o btest fshow ishow *~ | |||
oneshoot: | |||
make clean; \ | |||
./dlc bits.c; \ | |||
make btest; \ | |||
./btest bits.c; \ | |||
make clean \ | |||
@ -1,140 +0,0 @@ | |||
*********************** | |||
The CS:APP Data Lab | |||
Directions to Students | |||
*********************** | |||
Your goal is to modify your copy of bits.c so that it passes all the | |||
tests in btest without violating any of the coding guidelines. | |||
********* | |||
0. Files: | |||
********* | |||
Makefile - Makes btest, fshow, and ishow | |||
README - This file | |||
bits.c - The file you will be modifying and handing in | |||
bits.h - Header file | |||
btest.c - The main btest program | |||
btest.h - Used to build btest | |||
decl.c - Used to build btest | |||
tests.c - Used to build btest | |||
tests-header.c- Used to build btest | |||
dlc* - Rule checking compiler binary (data lab compiler) | |||
driver.pl* - Driver program that uses btest and dlc to autograde bits.c | |||
Driverhdrs.pm - Header file for optional "Beat the Prof" contest | |||
fshow.c - Utility for examining floating-point representations | |||
ishow.c - Utility for examining integer representations | |||
*********************************************************** | |||
1. Modifying bits.c and checking it for compliance with dlc | |||
*********************************************************** | |||
IMPORTANT: Carefully read the instructions in the bits.c file before | |||
you start. These give the coding rules that you will need to follow if | |||
you want full credit. | |||
Use the dlc compiler (./dlc) to automatically check your version of | |||
bits.c for compliance with the coding guidelines: | |||
unix> ./dlc bits.c | |||
dlc returns silently if there are no problems with your code. | |||
Otherwise it prints messages that flag any problems. Running dlc with | |||
the -e switch: | |||
unix> ./dlc -e bits.c | |||
causes dlc to print counts of the number of operators used by each function. | |||
Once you have a legal solution, you can test it for correctness using | |||
the ./btest program. | |||
********************* | |||
2. Testing with btest | |||
********************* | |||
The Makefile in this directory compiles your version of bits.c with | |||
additional code to create a program (or test harness) named btest. | |||
To compile and run the btest program, type: | |||
unix> make btest | |||
unix> ./btest [optional cmd line args] | |||
You will need to recompile btest each time you change your bits.c | |||
program. When moving from one platform to another, you will want to | |||
get rid of the old version of btest and generate a new one. Use the | |||
commands: | |||
unix> make clean | |||
unix> make btest | |||
Btest tests your code for correctness by running millions of test | |||
cases on each function. It tests wide swaths around well known corner | |||
cases such as Tmin and zero for integer puzzles, and zero, inf, and | |||
the boundary between denormalized and normalized numbers for floating | |||
point puzzles. When btest detects an error in one of your functions, | |||
it prints out the test that failed, the incorrect result, and the | |||
expected result, and then terminates the testing for that function. | |||
Here are the command line options for btest: | |||
unix> ./btest -h | |||
Usage: ./btest [-hg] [-r <n>] [-f <name> [-1|-2|-3 <val>]*] [-T <time limit>] | |||
-1 <val> Specify first function argument | |||
-2 <val> Specify second function argument | |||
-3 <val> Specify third function argument | |||
-f <name> Test only the named function | |||
-g Format output for autograding with no error messages | |||
-h Print this message | |||
-r <n> Give uniform weight of n for all problems | |||
-T <lim> Set timeout limit to lim | |||
Examples: | |||
Test all functions for correctness and print out error messages: | |||
unix> ./btest | |||
Test all functions in a compact form with no error messages: | |||
unix> ./btest -g | |||
Test function foo for correctness: | |||
unix> ./btest -f foo | |||
Test function foo for correctness with specific arguments: | |||
unix> ./btest -f foo -1 27 -2 0xf | |||
Btest does not check your code for compliance with the coding | |||
guidelines. Use dlc to do that. | |||
******************* | |||
3. Helper Programs | |||
******************* | |||
We have included the ishow and fshow programs to help you decipher | |||
integer and floating point representations respectively. Each takes a | |||
single decimal or hex number as an argument. To build them type: | |||
unix> make | |||
Example usages: | |||
unix> ./ishow 0x27 | |||
Hex = 0x00000027, Signed = 39, Unsigned = 39 | |||
unix> ./ishow 27 | |||
Hex = 0x0000001b, Signed = 27, Unsigned = 27 | |||
unix> ./fshow 0x15213243 | |||
Floating point value 3.255334057e-26 | |||
Bit Representation 0x15213243, sign = 0, exponent = 0x2a, fraction = 0x213243 | |||
Normalized. +1.2593463659 X 2^(-85) | |||
linux> ./fshow 15213243 | |||
Floating point value 2.131829405e-38 | |||
Bit Representation 0x00e822bb, sign = 0, exponent = 0x01, fraction = 0x6822bb | |||
Normalized. +1.8135598898 X 2^(-126) | |||
@ -1,388 +0,0 @@ | |||
/* | |||
* CS:APP Data Lab | |||
* | |||
* <Please put your name and userid here> | |||
* | |||
* bits.c - Source file with your solutions to the Lab. | |||
* This is the file you will hand in to your instructor. | |||
* | |||
* WARNING: Do not include the <stdio.h> header; it confuses the dlc | |||
* compiler. You can still use printf for debugging without including | |||
* <stdio.h>, although you might get a compiler warning. In general, | |||
* it's not good practice to ignore compiler warnings, but in this | |||
* case it's OK. | |||
*/ | |||
#if 0 | |||
/* | |||
* Instructions to Students: | |||
* | |||
* STEP 1: Read the following instructions carefully. | |||
*/ | |||
You will provide your solution to the Data Lab by | |||
editing the collection of functions in this source file. | |||
INTEGER CODING RULES: | |||
Replace the "return" statement in each function with one | |||
or more lines of C code that implements the function. Your code | |||
must conform to the following style: | |||
int Funct(arg1, arg2, ...) { | |||
/* brief description of how your implementation works */ | |||
int var1 = Expr1; | |||
... | |||
int varM = ExprM; | |||
varJ = ExprJ; | |||
... | |||
varN = ExprN; | |||
return ExprR; | |||
} | |||
Each "Expr" is an expression using ONLY the following: | |||
1. Integer constants 0 through 255 (0xFF), inclusive. You are | |||
not allowed to use big constants such as 0xffffffff. | |||
2. Function arguments and local variables (no global variables). | |||
3. Unary integer operations ! ~ | |||
4. Binary integer operations & ^ | + << >> | |||
Some of the problems restrict the set of allowed operators even further. | |||
Each "Expr" may consist of multiple operators. You are not restricted to | |||
one operator per line. | |||
You are expressly forbidden to: | |||
1. Use any control constructs such as if, do, while, for, switch, etc. | |||
2. Define or use any macros. | |||
3. Define any additional functions in this file. | |||
4. Call any functions. | |||
5. Use any other operations, such as &&, ||, -, or ?: | |||
6. Use any form of casting. | |||
7. Use any data type other than int. This implies that you | |||
cannot use arrays, structs, or unions. | |||
You may assume that your machine: | |||
1. Uses 2s complement, 32-bit representations of integers. | |||
2. Performs right shifts arithmetically. | |||
3. Has unpredictable behavior when shifting if the shift amount | |||
is less than 0 or greater than 31. | |||
EXAMPLES OF ACCEPTABLE CODING STYLE: | |||
/* | |||
* pow2plus1 - returns 2^x + 1, where 0 <= x <= 31 | |||
*/ | |||
int pow2plus1(int x) { | |||
/* exploit ability of shifts to compute powers of 2 */ | |||
return (1 << x) + 1; | |||
} | |||
/* | |||
* pow2plus4 - returns 2^x + 4, where 0 <= x <= 31 | |||
*/ | |||
int pow2plus4(int x) { | |||
/* exploit ability of shifts to compute powers of 2 */ | |||
int result = (1 << x); | |||
result += 4; | |||
return result; | |||
} | |||
FLOATING POINT CODING RULES | |||
For the problems that require you to implement floating-point operations, | |||
the coding rules are less strict. You are allowed to use looping and | |||
conditional control. You are allowed to use both ints and unsigneds. | |||
You can use arbitrary integer and unsigned constants. You can use any arithmetic, | |||
logical, or comparison operations on int or unsigned data. | |||
You are expressly forbidden to: | |||
1. Define or use any macros. | |||
2. Define any additional functions in this file. | |||
3. Call any functions. | |||
4. Use any form of casting. | |||
5. Use any data type other than int or unsigned. This means that you | |||
cannot use arrays, structs, or unions. | |||
6. Use any floating point data types, operations, or constants. | |||
NOTES: | |||
1. Use the dlc (data lab checker) compiler (described in the handout) to | |||
check the legality of your solutions. | |||
2. Each function has a maximum number of operations (integer, logical, | |||
or comparison) that you are allowed to use for your implementation | |||
of the function. The max operator count is checked by dlc. | |||
Note that assignment ('=') is not counted; you may use as many of | |||
these as you want without penalty. | |||
3. Use the btest test harness to check your functions for correctness. | |||
4. Use the BDD checker to formally verify your functions | |||
5. The maximum number of ops for each function is given in the | |||
header comment for each function. If there are any inconsistencies | |||
between the maximum ops in the writeup and in this file, consider | |||
this file the authoritative source. | |||
/* | |||
* STEP 2: Modify the following functions according the coding rules. | |||
* | |||
* IMPORTANT. TO AVOID GRADING SURPRISES: | |||
* 1. Use the dlc compiler to check that your solutions conform | |||
* to the coding rules. | |||
* 2. Use the BDD checker to formally verify that your solutions produce | |||
* the correct answers. | |||
*/ | |||
#endif | |||
/* Copyright (C) 1991-2020 Free Software Foundation, Inc. | |||
This file is part of the GNU C Library. | |||
The GNU C Library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
The GNU C Library is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with the GNU C Library; if not, see | |||
<https://www.gnu.org/licenses/>. */ | |||
/* This header is separate from features.h so that the compiler can | |||
include it implicitly at the start of every compilation. It must | |||
not itself include <features.h> or any other header that includes | |||
<features.h> because the implicit include comes before any feature | |||
test macros that may be defined in a source file before it first | |||
explicitly includes a system header. GCC knows the name of this | |||
header in order to preinclude it. */ | |||
/* glibc's intent is to support the IEC 559 math functionality, real | |||
and complex. If the GCC (4.9 and later) predefined macros | |||
specifying compiler intent are available, use them to determine | |||
whether the overall intent is to support these features; otherwise, | |||
presume an older compiler has intent to support these features and | |||
define these macros by default. */ | |||
/* wchar_t uses Unicode 10.0.0. Version 10.0 of the Unicode Standard is | |||
synchronized with ISO/IEC 10646:2017, fifth edition, plus | |||
the following additions from Amendment 1 to the fifth edition: | |||
- 56 emoji characters | |||
- 285 hentaigana | |||
- 3 additional Zanabazar Square characters */ | |||
//1 | |||
/* | |||
* bitXor - x^y using only ~ and & | |||
* Example: bitXor(4, 5) = 1 | |||
* Legal ops: ~ & | |||
* Max ops: 14 | |||
* Rating: 1 | |||
*/ | |||
int bitXor(int x, int y) { | |||
return ~(~x&~y)&~(x&y); | |||
} | |||
/* | |||
* tmin - return minimum two's complement integer | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 4 | |||
* Rating: 1 | |||
*/ | |||
int tmin(void) { | |||
return 0x1<<31; | |||
} | |||
//2 | |||
/* | |||
* isTmax - returns 1 if x is the maximum, two's complement number, | |||
* and 0 otherwise | |||
* Legal ops: ! ~ & ^ | + | |||
* Max ops: 10 | |||
* Rating: 1 | |||
*/ | |||
int isTmax(int x) { | |||
int i =x+1; | |||
x = x+i; | |||
x = ~x; | |||
i = !i; | |||
x = x+i; | |||
return !x; | |||
} | |||
/* | |||
* allOddBits - return 1 if all odd-numbered bits in word set to 1 | |||
* where bits are numbered from 0 (least significant) to 31 (most significant) | |||
* Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1 | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 12 | |||
* Rating: 2 | |||
*/ | |||
int allOddBits(int x) { | |||
int a = 0xaaaaaaaa; | |||
return !((a&x)^a); | |||
} | |||
/* | |||
* negate - return -x | |||
* Example: negate(1) = -1. | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 5 | |||
* Rating: 2 | |||
*/ | |||
int negate(int x) { | |||
return ~x+1; | |||
} | |||
//3 | |||
/* | |||
* isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters '0' to '9') | |||
* Example: isAsciiDigit(0x35) = 1. | |||
* isAsciiDigit(0x3a) = 0. | |||
* isAsciiDigit(0x05) = 0. | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 15 | |||
* Rating: 3 | |||
*/ | |||
int isAsciiDigit(int x) { | |||
//下边界为加够了就溢出,比这个大 | |||
//上边界为加不够就不溢出 | |||
int downstream = ~0x30+1; | |||
int upstream = ~0x39; | |||
int leftside = !((downstream+x)>>31); //超过0x30就符号变为0 | |||
int rightside = !!((upstream+x)>>31); //小于0x39就符号仍然为1 | |||
return leftside&rightside; | |||
} | |||
/* | |||
* conditional - same as x ? y : z | |||
* Example: conditional(2,4,5) = 4 | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 16 | |||
* Rating: 3 | |||
*/ | |||
int conditional(int x, int y, int z) { | |||
x = !!(x); | |||
x = ~x+1; | |||
return (x&y)|(~x&z); | |||
} | |||
/* | |||
* isLessOrEqual - if x <= y then return 1, else return 0 | |||
* Example: isLessOrEqual(4,5) = 1. | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 24 | |||
* Rating: 3 | |||
*/ | |||
int isLessOrEqual(int x, int y) { | |||
int tmp = ~y+1; | |||
tmp = tmp+x-1; | |||
tmp = tmp >> 31;//如果比y大,则为0,比y小等于,为1 | |||
return !!tmp; | |||
} | |||
//4 | |||
/* | |||
* logicalNeg - implement the ! operator, using all of | |||
* the legal operators except ! | |||
* Examples: logicalNeg(3) = 0, logicalNeg(0) = 1 | |||
* Legal ops: ~ & ^ | + << >> | |||
* Max ops: 12 | |||
* Rating: 4 | |||
*/ | |||
int logicalNeg(int x) { | |||
return ((x|(~x+1))>>31)+1; | |||
} | |||
/* howManyBits - return the minimum number of bits required to represent x in | |||
* two's complement | |||
* Examples: howManyBits(12) = 5 | |||
* howManyBits(298) = 10 | |||
* howManyBits(-5) = 4 | |||
* howManyBits(0) = 1 | |||
* howManyBits(-1) = 1 | |||
* howManyBits(0x80000000) = 32 | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 90 | |||
* Rating: 4 | |||
*/ | |||
int howManyBits(int x) { | |||
x = ((~(x>>31))&x)|(x>>31&~x);//左边为如果为正数,则保留;右边为如果为负数,则取反 | |||
//先从最大开始查找 | |||
int tf = !!(x>>16); //如果小于等于16位,则为0,否则为1 | |||
int b16 = tf << 4; //长度超过16位,记录值b16为16,否则为0,代表至少16位 | |||
x = x>>b16; //右移动b16位,超过16位则探讨16位以上的,少于16位则探讨16位以下的 | |||
tf = !!(x>>8); //开始探讨剩余部位是否超过8位,超过看超过多少,没超过看余下多少 | |||
int b8 = tf << 3;//长度超过8位,记录值b8为8,否则为0,代表再多至少8位 | |||
x = x>>b8; | |||
tf = !!(x>>4); | |||
int b4 = tf << 2 ; //8找4,长度超过4位,记录值b4为4,否则为0,代表再多至少4位 | |||
x = x>>b4; | |||
tf = !!(x>>2); | |||
int b2 = tf << 1; //4找2,长度超过2位,记录值b2为2,否则为0,代表再多至少2位 | |||
x = x>>b2; | |||
tf = !!(x>>1); //2找1,长度超过1位,记录值b1为1,否则为0,代表再多至少1位 | |||
int b1 = tf << 0; | |||
x = x>>b1; | |||
int b0 = x;//截断到只剩单个数字,这个数字是1就再多一位,是0就不会增加 | |||
return b0+b1+b2+b4+b8+b16+1; //正数多一位补码最高位,负数因为取反未+1,理应再加1,故两个输出表达式一致 | |||
} | |||
//float | |||
/* | |||
* floatScale2 - Return bit-level equivalent of expression 2*f for | |||
* floating point argument f. | |||
* Both the argument and result are passed as unsigned int's, but | |||
* they are to be interpreted as the bit-level representation of | |||
* single-precision floating point values. | |||
* When argument is NaN, return argument | |||
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while | |||
* Max ops: 30 | |||
* Rating: 4 | |||
*/ | |||
unsigned floatScale2(unsigned uf) { | |||
unsigned sign = (0x80000000)&uf; | |||
unsigned exp = (0x7f800000)&uf; | |||
unsigned frac = (0x007fffff)&uf; | |||
if(exp == 0x7f800000) | |||
return uf; //如果是exp全为255,frac全是0就是无穷,不是就是NaN,都直接return | |||
if(exp == 0x00000000){ | |||
if(frac == 0x00000000) | |||
return uf; //exp全为0,frac全为0,则为0,0*2=0,原样不动 | |||
return (frac<<1)|sign|exp;//exp全为0,frac不全为0,注意到非规格化极小和规格化之间是连续的,即frac第一位为1时,左移会把exp变为非全0,回到规格化 | |||
} | |||
return (exp+0x00800000)|sign|frac; | |||
} | |||
/* | |||
* floatFloat2Int - Return bit-level equivalent of expression (int) f | |||
* for floating point argument f. | |||
* Argument is passed as unsigned int, but | |||
* it is to be interpreted as the bit-level representation of a | |||
* single-precision floating point value. | |||
* Anything out of range (including NaN and infinity) should return | |||
* 0x80000000u. | |||
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while | |||
* Max ops: 30 | |||
* Rating: 4 | |||
*/ | |||
int floatFloat2Int(unsigned uf) { | |||
unsigned sign = ((0x80000000)&uf)>>31; | |||
unsigned exp = ((0x7f800000)&uf)>>23; | |||
unsigned frac = (0x007fffff)&uf; | |||
unsigned base = (frac+0x00800000);//加1后的基底(对于常规数) | |||
int bias = (exp-127)-23;//真实需要左移的 | |||
if(sign==0) | |||
sign = 1; | |||
else | |||
sign = -1; | |||
if(exp == 255) | |||
return 0x80000000u;//足够大 | |||
if(exp == 0) | |||
return 0;//足够小 | |||
if(bias <=0){ | |||
if(bias<=-24) | |||
bias = -24; | |||
return sign*(base>>(-bias)); | |||
} | |||
else{ | |||
if(bias>=9) | |||
return 0x80000000u; | |||
return sign*(base<<bias); | |||
} | |||
} | |||
// #include "floatPower2.c" |
@ -1,65 +0,0 @@ | |||
/* Copyright (C) 1991-2020 Free Software Foundation, Inc. | |||
This file is part of the GNU C Library. | |||
The GNU C Library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
The GNU C Library is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with the GNU C Library; if not, see | |||
<https://www.gnu.org/licenses/>. */ | |||
/* This header is separate from features.h so that the compiler can | |||
include it implicitly at the start of every compilation. It must | |||
not itself include <features.h> or any other header that includes | |||
<features.h> because the implicit include comes before any feature | |||
test macros that may be defined in a source file before it first | |||
explicitly includes a system header. GCC knows the name of this | |||
header in order to preinclude it. */ | |||
/* glibc's intent is to support the IEC 559 math functionality, real | |||
and complex. If the GCC (4.9 and later) predefined macros | |||
specifying compiler intent are available, use them to determine | |||
whether the overall intent is to support these features; otherwise, | |||
presume an older compiler has intent to support these features and | |||
define these macros by default. */ | |||
/* wchar_t uses Unicode 10.0.0. Version 10.0 of the Unicode Standard is | |||
synchronized with ISO/IEC 10646:2017, fifth edition, plus | |||
the following additions from Amendment 1 to the fifth edition: | |||
- 56 emoji characters | |||
- 285 hentaigana | |||
- 3 additional Zanabazar Square characters */ | |||
//1 | |||
int bitXor(int, int); | |||
int test_bitXor(int, int); | |||
int tmin(); | |||
int test_tmin(); | |||
//2 | |||
int isTmax(int); | |||
int test_isTmax(int); | |||
int allOddBits(); | |||
int test_allOddBits(); | |||
int negate(int); | |||
int test_negate(int); | |||
//3 | |||
int isAsciiDigit(int); | |||
int test_isAsciiDigit(int); | |||
int conditional(int, int, int); | |||
int test_conditional(int, int, int); | |||
int isLessOrEqual(int, int); | |||
int test_isLessOrEqual(int, int); | |||
//4 | |||
int logicalNeg(int); | |||
int test_logicalNeg(int); | |||
int howManyBits(int); | |||
int test_howManyBits(int); | |||
//float | |||
unsigned floatScale2(unsigned); | |||
unsigned test_floatScale2(unsigned); | |||
int floatFloat2Int(unsigned); | |||
int test_floatFloat2Int(unsigned); | |||
// #include "floatPower2.c" |
@ -1,583 +0,0 @@ | |||
/* | |||
* CS:APP Data Lab | |||
* | |||
* btest.c - A test harness that checks a student's solution in bits.c | |||
* for correctness. | |||
* | |||
* Copyright (c) 2001-2011, R. Bryant and D. O'Hallaron, All rights | |||
* reserved. May not be used, modified, or copied without permission. | |||
* | |||
* This is an improved version of btest that tests large windows | |||
* around zero and tmin and tmax for integer puzzles, and zero, norm, | |||
* and denorm boundaries for floating point puzzles. | |||
* | |||
* Note: not 64-bit safe. Always compile with gcc -m32 option. | |||
*/ | |||
#include <stdio.h> | |||
#include <unistd.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <limits.h> | |||
#include <signal.h> | |||
#include <setjmp.h> | |||
#include <math.h> | |||
#include "btest.h" | |||
/* Not declared in some stdlib.h files, so define here */ | |||
float strtof(const char *nptr, char **endptr); | |||
/************************* | |||
* Configuration Constants | |||
*************************/ | |||
/* Handle infinite loops by setting upper limit on execution time, in | |||
seconds */ | |||
#define TIMEOUT_LIMIT 10 | |||
/* For functions with a single argument, generate TEST_RANGE values | |||
above and below the min and max test values, and above and below | |||
zero. Functions with two or three args will use square and cube | |||
roots of this value, respectively, to avoid combinatorial | |||
explosion */ | |||
#define TEST_RANGE 500000 | |||
/* This defines the maximum size of any test value array. The | |||
gen_vals() routine creates k test values for each value of | |||
TEST_RANGE, thus MAX_TEST_VALS must be at least k*TEST_RANGE */ | |||
#define MAX_TEST_VALS 13*TEST_RANGE | |||
/********************************** | |||
* Globals defined in other modules | |||
**********************************/ | |||
/* This characterizes the set of puzzles to test. | |||
Defined in decl.c and generated from templates in ./puzzles dir */ | |||
extern test_rec test_set[]; | |||
/************************************************ | |||
* Write-once globals defined by command line args | |||
************************************************/ | |||
/* Emit results in a format for autograding, without showing | |||
and counter-examples */ | |||
static int grade = 0; | |||
/* Time out after this number of seconds */ | |||
static int timeout_limit = TIMEOUT_LIMIT; /* -T */ | |||
/* If non-NULL, test only one function (-f) */ | |||
static char* test_fname = NULL; | |||
/* Special case when only use fixed argument(s) (-1, -2, or -3) */ | |||
static int has_arg[3] = {0,0,0}; | |||
static unsigned argval[3] = {0,0,0}; | |||
/* Use fixed weight for rating, and if so, what should it be? (-r) */ | |||
static int global_rating = 0; | |||
/****************** | |||
* Helper functions | |||
******************/ | |||
/* | |||
* Signal - installs a signal handler | |||
*/ | |||
typedef void handler_t(int); | |||
handler_t *Signal(int signum, handler_t *handler) | |||
{ | |||
struct sigaction action, old_action; | |||
action.sa_handler = handler; | |||
sigemptyset(&action.sa_mask); /* block sigs of type being handled */ | |||
action.sa_flags = SA_RESTART; /* restart syscalls if possible */ | |||
if (sigaction(signum, &action, &old_action) < 0) | |||
perror("Signal error"); | |||
return (old_action.sa_handler); | |||
} | |||
/* | |||
* timeout_handler - SIGALARM hander | |||
*/ | |||
sigjmp_buf envbuf; | |||
void timeout_handler(int sig) { | |||
siglongjmp(envbuf, 1); | |||
} | |||
/* | |||
* random_val - Return random integer value between min and max | |||
*/ | |||
static int random_val(int min, int max) | |||
{ | |||
double weight = rand()/(double) RAND_MAX; | |||
int result = min * (1-weight) + max * weight; | |||
return result; | |||
} | |||
/* | |||
* gen_vals - Generate the integer values we'll use to test a function | |||
*/ | |||
static int gen_vals(int test_vals[], int min, int max, int test_range, int arg) | |||
{ | |||
int i; | |||
int test_count = 0; | |||
/* Special case: If the user has specified a specific function | |||
argument using the -1, -2, or -3 flags, then simply use this | |||
argument and return */ | |||
if (has_arg[arg]) { | |||
test_vals[0] = argval[arg]; | |||
return 1; | |||
} | |||
/* | |||
* Special case: Generate test vals for floating point functions | |||
* where the input argument is an unsigned bit-level | |||
* representation of a float. For this case we want to test the | |||
* regions around zero, the smallest normalized and largest | |||
* denormalized numbers, one, and the largest normalized number, | |||
* as well as inf and nan. | |||
*/ | |||
if ((min == 1 && max == 1)) { | |||
unsigned smallest_norm = 0x00800000; | |||
unsigned one = 0x3f800000; | |||
unsigned largest_norm = 0x7f000000; | |||
unsigned inf = 0x7f800000; | |||
unsigned nan = 0x7fc00000; | |||
unsigned sign = 0x80000000; | |||
/* Test range should be at most 1/2 the range of one exponent | |||
value */ | |||
if (test_range > (1 << 23)) { | |||
test_range = 1 << 23; | |||
} | |||
/* Functions where the input argument is an unsigned bit-level | |||
representation of a float. The number of tests generated | |||
inside this loop body is the value k referenced in the | |||
comment for the global variable MAX_TEST_VALS. */ | |||
for (i = 0; i < test_range; i++) { | |||
/* Denorms around zero */ | |||
test_vals[test_count++] = i; | |||
test_vals[test_count++] = sign | i; | |||
/* Region around norm to denorm transition */ | |||
test_vals[test_count++] = smallest_norm + i; | |||
test_vals[test_count++] = smallest_norm - i; | |||
test_vals[test_count++] = sign | (smallest_norm + i); | |||
test_vals[test_count++] = sign | (smallest_norm - i); | |||
/* Region around one */ | |||
test_vals[test_count++] = one + i; | |||
test_vals[test_count++] = one - i; | |||
test_vals[test_count++] = sign | (one + i); | |||
test_vals[test_count++] = sign | (one - i); | |||
/* Region below largest norm */ | |||
test_vals[test_count++] = largest_norm - i; | |||
test_vals[test_count++] = sign | (largest_norm - i); | |||
} | |||
/* special vals */ | |||
test_vals[test_count++] = inf; /* inf */ | |||
test_vals[test_count++] = sign | inf; /* -inf */ | |||
test_vals[test_count++] = nan; /* nan */ | |||
test_vals[test_count++] = sign | nan; /* -nan */ | |||
return test_count; | |||
} | |||
/* | |||
* Normal case: Generate test vals for integer functions | |||
*/ | |||
/* If the range is small enough, then do exhaustively */ | |||
if (max - MAX_TEST_VALS <= min) { | |||
for (i = min; i <= max; i++) | |||
test_vals[test_count++] = i; | |||
return test_count; | |||
} | |||
/* Otherwise, need to sample. Do so near the boundaries, around | |||
zero, and for some random cases. */ | |||
for (i = 0; i < test_range; i++) { | |||
/* Test around the boundaries */ | |||
test_vals[test_count++] = min + i; | |||
test_vals[test_count++] = max - i; | |||
/* If zero falls between min and max, then also test around zero */ | |||
if (i >= min && i <= max) | |||
test_vals[test_count++] = i; | |||
if (-i >= min && -i <= max) | |||
test_vals[test_count++] = -i; | |||
/* Random case between min and max */ | |||
test_vals[test_count++] = random_val(min, max); | |||
} | |||
return test_count; | |||
} | |||
/* | |||
* test_0_arg - Test a function with zero arguments | |||
*/ | |||
static int test_0_arg(funct_t f, funct_t ft, char *name) | |||
{ | |||
int r = f(); | |||
int rt = ft(); | |||
int error = (r != rt); | |||
if (error && !grade) | |||
printf("ERROR: Test %s() failed...\n...Gives %d[0x%x]. Should be %d[0x%x]\n", name, r, r, rt, rt); | |||
return error; | |||
} | |||
/* | |||
* test_1_arg - Test a function with one argument | |||
*/ | |||
static int test_1_arg(funct_t f, funct_t ft, int arg1, char *name) | |||
{ | |||
funct1_t f1 = (funct1_t) f; | |||
funct1_t f1t = (funct1_t) ft; | |||
int r, rt, error; | |||
r = f1(arg1); | |||
rt = f1t(arg1); | |||
error = (r != rt); | |||
if (error && !grade) | |||
printf("ERROR: Test %s(%d[0x%x]) failed...\n...Gives %d[0x%x]. Should be %d[0x%x]\n", name, arg1, arg1, r, r, rt, rt); | |||
return error; | |||
} | |||
/* | |||
* test_2_arg - Test a function with two arguments | |||
*/ | |||
static int test_2_arg(funct_t f, funct_t ft, int arg1, int arg2, char *name) | |||
{ | |||
funct2_t f2 = (funct2_t) f; | |||
funct2_t f2t = (funct2_t) ft; | |||
int r = f2(arg1, arg2); | |||
int rt = f2t(arg1, arg2); | |||
int error = (r != rt); | |||
if (error && !grade) | |||
printf("ERROR: Test %s(%d[0x%x],%d[0x%x]) failed...\n...Gives %d[0x%x]. Should be %d[0x%x]\n", name, arg1, arg1, arg2, arg2, r, r, rt, rt); | |||
return error; | |||
} | |||
/* | |||
* test_3_arg - Test a function with three arguments | |||
*/ | |||
static int test_3_arg(funct_t f, funct_t ft, | |||
int arg1, int arg2, int arg3, char *name) | |||
{ | |||
funct3_t f3 = (funct3_t) f; | |||
funct3_t f3t = (funct3_t) ft; | |||
int r = f3(arg1, arg2, arg3); | |||
int rt = f3t(arg1, arg2, arg3); | |||
int error = (r != rt); | |||
if (error && !grade) | |||
printf("ERROR: Test %s(%d[0x%x],%d[0x%x],%d[0x%x]) failed...\n...Gives %d[0x%x]. Should be %d[0x%x]\n", name, arg1, arg1, arg2, arg2, arg3, arg3, r, r, rt, rt); | |||
return error; | |||
} | |||
/* | |||
* test_function - Test a function. Return number of errors | |||
*/ | |||
static int test_function(test_ptr t) { | |||
int test_counts[3]; /* number of test values for each arg */ | |||
int args = t->args; /* number of function arguments */ | |||
int arg_test_range[3]; /* test range for each argument */ | |||
int i, a1, a2, a3; | |||
int errors = 0; | |||
/* These are the test values for each arg. Declared with the | |||
static attribute so that the array will be allocated in bss | |||
rather than the stack */ | |||
static int arg_test_vals[3][MAX_TEST_VALS]; | |||
/* Sanity check on the number of args */ | |||
if (args < 0 || args > 3) { | |||
printf("Configuration error: invalid number of args (%d) for function %s\n", args, t->name); | |||
exit(1); | |||
} | |||
/* Assign range of argument test vals so as to conserve the total | |||
number of tests, independent of the number of arguments */ | |||
if (args == 1) { | |||
arg_test_range[0] = TEST_RANGE; | |||
} | |||
else if (args == 2) { | |||
arg_test_range[0] = pow((double)TEST_RANGE, 0.5); /* sqrt */ | |||
arg_test_range[1] = arg_test_range[0]; | |||
} | |||
else { | |||
arg_test_range[0] = pow((double)TEST_RANGE, 0.333); /* cbrt */ | |||
arg_test_range[1] = arg_test_range[0]; | |||
arg_test_range[2] = arg_test_range[0]; | |||
} | |||
/* Sanity check on the ranges */ | |||
if (arg_test_range[0] < 1) | |||
arg_test_range[0] = 1; | |||
if (arg_test_range[1] < 1) | |||
arg_test_range[1] = 1; | |||
if (arg_test_range[2] < 1) | |||
arg_test_range[2] = 1; | |||
/* Create a test set for each argument */ | |||
for (i = 0; i < args; i++) { | |||
test_counts[i] = gen_vals(arg_test_vals[i], | |||
t->arg_ranges[i][0], /* min */ | |||
t->arg_ranges[i][1], /* max */ | |||
arg_test_range[i], | |||
i); | |||
} | |||
/* Handle timeouts in the test code */ | |||
if (timeout_limit > 0) { | |||
int rc; | |||
rc = sigsetjmp(envbuf, 1); | |||
if (rc) { | |||
/* control will reach here if there is a timeout */ | |||
errors = 1; | |||
printf("ERROR: Test %s failed.\n Timed out after %d secs (probably infinite loop)\n", t->name, timeout_limit); | |||
return errors; | |||
} | |||
alarm(timeout_limit); | |||
} | |||
/* Test function has no arguments */ | |||
if (args == 0) { | |||
errors += test_0_arg(t->solution_funct, t->test_funct, t->name); | |||
return errors; | |||
} | |||
/* | |||
* Test function has at least one argument | |||
*/ | |||
/* Iterate over the values for first argument */ | |||
for (a1 = 0; a1 < test_counts[0]; a1++) { | |||
if (args == 1) { | |||
errors += test_1_arg(t->solution_funct, | |||
t->test_funct, | |||
arg_test_vals[0][a1], | |||
t->name); | |||
/* Stop testing if there is an error */ | |||
if (errors) | |||
return errors; | |||
} | |||
else { | |||
/* if necessary, iterate over values for second argument */ | |||
for (a2 = 0; a2 < test_counts[1]; a2++) { | |||
if (args == 2) { | |||
errors += test_2_arg(t->solution_funct, | |||
t->test_funct, | |||
arg_test_vals[0][a1], | |||
arg_test_vals[1][a2], | |||
t->name); | |||
/* Stop testing if there is an error */ | |||
if (errors) | |||
return errors; | |||
} | |||
else { | |||
/* if necessary, iterate over vals for third arg */ | |||
for (a3 = 0; a3 < test_counts[2]; a3++) { | |||
errors += test_3_arg(t->solution_funct, | |||
t->test_funct, | |||
arg_test_vals[0][a1], | |||
arg_test_vals[1][a2], | |||
arg_test_vals[2][a3], | |||
t->name); | |||
/* Stop testing if there is an error */ | |||
if (errors) | |||
return errors; | |||
} /* a3 */ | |||
} | |||
} /* a2 */ | |||
} | |||
} /* a1 */ | |||
return errors; | |||
} | |||
/* | |||
* run_tests - Run series of tests. Return number of errors | |||
*/ | |||
static int run_tests() | |||
{ | |||
int i; | |||
int errors = 0; | |||
double points = 0.0; | |||
double max_points = 0.0; | |||
printf("Score\tRating\tErrors\tFunction\n"); | |||
for (i = 0; test_set[i].solution_funct; i++) { | |||
int terrors; | |||
double tscore; | |||
double tpoints; | |||
if (!test_fname || strcmp(test_set[i].name,test_fname) == 0) { | |||
int rating = global_rating ? global_rating : test_set[i].rating; | |||
terrors = test_function(&test_set[i]); | |||
errors += terrors; | |||
tscore = terrors == 0 ? 1.0 : 0.0; | |||
tpoints = rating * tscore; | |||
points += tpoints; | |||
max_points += rating; | |||
if (grade || terrors < 1) | |||
printf(" %.0f\t%d\t%d\t%s\n", | |||
tpoints, rating, terrors, test_set[i].name); | |||
} | |||
} | |||
printf("Total points: %.0f/%.0f\n", points, max_points); | |||
return errors; | |||
} | |||
/* | |||
* get_num_val - Extract hex/decimal/or float value from string | |||
*/ | |||
static int get_num_val(char *sval, unsigned *valp) { | |||
char *endp; | |||
/* See if it's an integer or floating point */ | |||
int ishex = 0; | |||
int isfloat = 0; | |||
int i; | |||
for (i = 0; sval[i]; i++) { | |||
switch (sval[i]) { | |||
case 'x': | |||
case 'X': | |||
ishex = 1; | |||
break; | |||
case 'e': | |||
case 'E': | |||
if (!ishex) | |||
isfloat = 1; | |||
break; | |||
case '.': | |||
isfloat = 1; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
if (isfloat) { | |||
float fval = strtof(sval, &endp); | |||
if (!*endp) { | |||
*valp = *(unsigned *) &fval; | |||
return 1; | |||
} | |||
return 0; | |||
} else { | |||
long long int llval = strtoll(sval, &endp, 0); | |||
long long int upperbits = llval >> 31; | |||
/* will give -1 for negative, 0 or 1 for positive */ | |||
if (!*valp && (upperbits == 0 || upperbits == -1 || upperbits == 1)) { | |||
*valp = (unsigned) llval; | |||
return 1; | |||
} | |||
return 0; | |||
} | |||
} | |||
/* | |||
* usage - Display usage info | |||
*/ | |||
static void usage(char *cmd) { | |||
printf("Usage: %s [-hg] [-r <n>] [-f <name> [-1|-2|-3 <val>]*] [-T <time limit>]\n", cmd); | |||
printf(" -1 <val> Specify first function argument\n"); | |||
printf(" -2 <val> Specify second function argument\n"); | |||
printf(" -3 <val> Specify third function argument\n"); | |||
printf(" -f <name> Test only the named function\n"); | |||
printf(" -g Compact output for grading (with no error msgs)\n"); | |||
printf(" -h Print this message\n"); | |||
printf(" -r <n> Give uniform weight of n for all problems\n"); | |||
printf(" -T <lim> Set timeout limit to lim\n"); | |||
exit(1); | |||
} | |||
/************** | |||
* Main routine | |||
**************/ | |||
int main(int argc, char *argv[]) | |||
{ | |||
char c; | |||
/* parse command line args */ | |||
while ((c = getopt(argc, argv, "hgf:r:T:1:2:3:")) != -1) | |||
switch (c) { | |||
case 'h': /* help */ | |||
usage(argv[0]); | |||
break; | |||
case 'g': /* grading option for autograder */ | |||
grade = 1; | |||
break; | |||
case 'f': /* test only one function */ | |||
test_fname = strdup(optarg); | |||
break; | |||
case 'r': /* set global rating for each problem */ | |||
global_rating = atoi(optarg); | |||
if (global_rating < 0) | |||
usage(argv[0]); | |||
break; | |||
case '1': /* Get first argument */ | |||
has_arg[0] = get_num_val(optarg, &argval[0]); | |||
if (!has_arg[0]) { | |||
printf("Bad argument '%s'\n", optarg); | |||
exit(0); | |||
} | |||
break; | |||
case '2': /* Get first argument */ | |||
has_arg[1] = get_num_val(optarg, &argval[1]); | |||
if (!has_arg[1]) { | |||
printf("Bad argument '%s'\n", optarg); | |||
exit(0); | |||
} | |||
break; | |||
case '3': /* Get first argument */ | |||
has_arg[2] = get_num_val(optarg, &argval[2]); | |||
if (!has_arg[2]) { | |||
printf("Bad argument '%s'\n", optarg); | |||
exit(0); | |||
} | |||
break; | |||
case 'T': /* Set timeout limit */ | |||
timeout_limit = atoi(optarg); | |||
break; | |||
default: | |||
usage(argv[0]); | |||
} | |||
if (timeout_limit > 0) { | |||
Signal(SIGALRM, timeout_handler); | |||
} | |||
/* test each function */ | |||
run_tests(); | |||
return 0; | |||
} |
@ -1,32 +0,0 @@ | |||
/* | |||
* CS:APP Data Lab | |||
*/ | |||
/* Declare different function types */ | |||
typedef int (*funct_t) (void); | |||
typedef int (*funct1_t)(int); | |||
typedef int (*funct2_t)(int, int); | |||
typedef int (*funct3_t)(int, int, int); | |||
/* Combine all the information about a function and its tests as structure */ | |||
typedef struct { | |||
char *name; /* String name */ | |||
funct_t solution_funct; /* Function */ | |||
funct_t test_funct; /* Test function */ | |||
int args; /* Number of function arguments */ | |||
char *ops; /* List of legal operators. Special case: "$" for floating point */ | |||
int op_limit; /* Max number of ops allowed in solution */ | |||
int rating; /* Problem rating (1 -- 4) */ | |||
int arg_ranges[3][2]; /* Argument ranges. Always defined for 3 args, even if */ | |||
/* the function takes fewer. Special case: First arg */ | |||
/* must be set to {1,1} for f.p. puzzles */ | |||
} test_rec, *test_ptr; | |||
extern test_rec test_set[]; | |||
@ -1,86 +0,0 @@ | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <limits.h> | |||
#define TMin INT_MIN | |||
#define TMax INT_MAX | |||
#include "btest.h" | |||
#include "bits.h" | |||
test_rec test_set[] = { | |||
/* Copyright (C) 1991-2020 Free Software Foundation, Inc. | |||
This file is part of the GNU C Library. | |||
The GNU C Library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
The GNU C Library is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with the GNU C Library; if not, see | |||
<https://www.gnu.org/licenses/>. */ | |||
/* This header is separate from features.h so that the compiler can | |||
include it implicitly at the start of every compilation. It must | |||
not itself include <features.h> or any other header that includes | |||
<features.h> because the implicit include comes before any feature | |||
test macros that may be defined in a source file before it first | |||
explicitly includes a system header. GCC knows the name of this | |||
header in order to preinclude it. */ | |||
/* glibc's intent is to support the IEC 559 math functionality, real | |||
and complex. If the GCC (4.9 and later) predefined macros | |||
specifying compiler intent are available, use them to determine | |||
whether the overall intent is to support these features; otherwise, | |||
presume an older compiler has intent to support these features and | |||
define these macros by default. */ | |||
/* wchar_t uses Unicode 10.0.0. Version 10.0 of the Unicode Standard is | |||
synchronized with ISO/IEC 10646:2017, fifth edition, plus | |||
the following additions from Amendment 1 to the fifth edition: | |||
- 56 emoji characters | |||
- 285 hentaigana | |||
- 3 additional Zanabazar Square characters */ | |||
//1 | |||
{"bitXor", (funct_t) bitXor, (funct_t) test_bitXor, 2, "& ~", 14, 1, | |||
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}}, | |||
{"tmin", (funct_t) tmin, (funct_t) test_tmin, 0, "! ~ & ^ | + << >>", 4, 1, | |||
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}}, | |||
//2 | |||
{"isTmax", (funct_t) isTmax, (funct_t) test_isTmax, 1, "! ~ & ^ | +", 10, 1, | |||
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}}, | |||
{"allOddBits", (funct_t) allOddBits, (funct_t) test_allOddBits, 1, | |||
"! ~ & ^ | + << >>", 12, 2, | |||
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}}, | |||
{"negate", (funct_t) negate, (funct_t) test_negate, 1, | |||
"! ~ & ^ | + << >>", 5, 2, | |||
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}}, | |||
//3 | |||
{"isAsciiDigit", (funct_t) isAsciiDigit, (funct_t) test_isAsciiDigit, 1, | |||
"! ~ & ^ | + << >>", 15, 3, | |||
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}}, | |||
{"conditional", (funct_t) conditional, (funct_t) test_conditional, 3, "! ~ & ^ | << >>", 16, 3, | |||
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}}, | |||
{"isLessOrEqual", (funct_t) isLessOrEqual, (funct_t) test_isLessOrEqual, 2, | |||
"! ~ & ^ | + << >>", 24, 3, | |||
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}}, | |||
//4 | |||
{"logicalNeg", (funct_t) logicalNeg, (funct_t) test_logicalNeg, 1, | |||
"~ & ^ | + << >>", 12, 4, | |||
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}}, | |||
{"howManyBits", (funct_t) howManyBits, (funct_t) test_howManyBits, 1, "! ~ & ^ | + << >>", 90, 4, | |||
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}}, | |||
//float | |||
{"floatScale2", (funct_t) floatScale2, (funct_t) test_floatScale2, 1, | |||
"$", 30, 4, | |||
{{1, 1},{1,1},{1,1}}}, | |||
{"floatFloat2Int", (funct_t) floatFloat2Int, (funct_t) test_floatFloat2Int, 1, | |||
"$", 30, 4, | |||
{{1, 1},{1,1},{1,1}}}, | |||
// #include "floatPower2.c" | |||
{"", NULL, NULL, 0, "", 0, 0, | |||
{{0, 0},{0,0},{0,0}}} | |||
}; |
@ -1,439 +0,0 @@ | |||
#!/usr/bin/perl | |||
####################################################################### | |||
# driver.pl - CS:APP Data Lab driver | |||
# | |||
# Copyright (c) 2004-2011, R. Bryant and D. O'Hallaron, All rights | |||
# reserved. May not be used, modified, or copied without permission. | |||
# | |||
# Note: The driver can use either btest or the BDD checker to check | |||
# puzzles for correctness. This version of the lab uses btest, which | |||
# has been extended to do better testing of both integer and | |||
# floating-point puzzles. | |||
# | |||
####################################################################### | |||
use strict 'vars'; | |||
use Getopt::Std; | |||
use lib "."; | |||
use Driverlib; | |||
# Set to 1 to use btest, 0 to use the BDD checker. | |||
my $USE_BTEST = 1; | |||
# Generic settings | |||
$| = 1; # Flush stdout each time | |||
umask(0077); # Files created by the user in tmp readable only by that user | |||
$ENV{PATH} = "/usr/local/bin:/usr/bin:/bin"; | |||
# | |||
# usage - print help message and terminate | |||
# | |||
sub usage { | |||
printf STDERR "$_[0]\n"; | |||
printf STDERR "Usage: $0 [-h] [-u \"nickname\"]\n"; | |||
printf STDERR "Options:\n"; | |||
printf STDERR " -h Print this message.\n"; | |||
printf STDERR " -u \"nickname\" Send autoresult to server, using nickname on scoreboard)\n"; | |||
die "\n"; | |||
} | |||
############## | |||
# Main routine | |||
############## | |||
my $login = getlogin() || (getpwuid($<))[0] || "unknown"; | |||
my $tmpdir = "/var/tmp/datalab.$login.$$"; | |||
my $diemsg = "The files are in $tmpdir."; | |||
my $driverfiles; | |||
my $infile; | |||
my $autograded; | |||
my $status; | |||
my $inpuzzles; | |||
my $puzzlecnt; | |||
my $line; | |||
my $blank; | |||
my $name; | |||
my $c_points; | |||
my $c_rating; | |||
my $c_errors; | |||
my $p_points; | |||
my $p_rating; | |||
my $p_errors; | |||
my $total_c_points; | |||
my $total_c_rating; | |||
my $total_p_points; | |||
my $total_p_rating; | |||
my $tops; | |||
my $tpoints; | |||
my $trating; | |||
my $foo; | |||
my $name; | |||
my $msg; | |||
my $nickname; | |||
my $autoresult; | |||
my %puzzle_c_points; | |||
my %puzzle_c_rating; | |||
my %puzzle_c_errors; | |||
my %puzzle_p_points; | |||
my %puzzle_p_ops; | |||
my %puzzle_p_maxops; | |||
my %puzzle_number; | |||
# Parse the command line arguments | |||
no strict; | |||
getopts('hu:f:A'); | |||
if ($opt_h) { | |||
usage(); | |||
} | |||
# The default input file is bits.c (change with -f) | |||
$infile = "bits.c"; | |||
$nickname = ""; | |||
##### | |||
# These are command line args that every driver must support | |||
# | |||
# Causes the driver to send an autoresult to the server on behalf of user | |||
if ($opt_u) { | |||
$nickname = $opt_u; | |||
check_nickname($nickname); | |||
} | |||
# Hidden flag that indicates that the driver was invoked by an autograder | |||
if ($opt_A) { | |||
$autograded = $opt_A; | |||
} | |||
##### | |||
# Drivers can also define an arbitary number of other command line args | |||
# | |||
# Optional hidden flag used by the autograder | |||
if ($opt_f) { | |||
$infile = $opt_f; | |||
} | |||
use strict 'vars'; | |||
################################################ | |||
# Compute the correctness and performance scores | |||
################################################ | |||
# Make sure that an executable dlc (data lab compiler) exists | |||
(-e "./dlc" and -x "./dlc") | |||
or die "$0: ERROR: No executable dlc binary.\n"; | |||
# If using the bdd checker, then make sure it exists | |||
if (!$USE_BTEST) { | |||
(-e "./bddcheck/cbit/cbit" and -x "./bddcheck/cbit/cbit") | |||
or die "$0: ERROR: No executable cbit binary.\n"; | |||
} | |||
# | |||
# Set up the contents of the scratch directory | |||
# | |||
system("mkdir $tmpdir") == 0 | |||
or die "$0: Could not make scratch directory $tmpdir.\n"; | |||
# Copy the student's work to the scratch directory | |||
unless (system("cp $infile $tmpdir/bits.c") == 0) { | |||
clean($tmpdir); | |||
die "$0: Could not copy file $infile to scratch directory $tmpdir.\n"; | |||
} | |||
# Copy the various autograding files to the scratch directory | |||
if ($USE_BTEST) { | |||
$driverfiles = "Makefile dlc btest.c decl.c tests.c btest.h bits.h"; | |||
unless (system("cp -r $driverfiles $tmpdir") == 0) { | |||
clean($tmpdir); | |||
die "$0: Could not copy autogradingfiles to $tmpdir.\n"; | |||
} | |||
} | |||
else { | |||
$driverfiles = "dlc tests.c bddcheck"; | |||
unless (system("cp -r $driverfiles $tmpdir") == 0) { | |||
clean($tmpdir); | |||
die "$0: Could not copy support files to $tmpdir.\n"; | |||
} | |||
} | |||
# Change the current working directory to the scratch directory | |||
unless (chdir($tmpdir)) { | |||
clean($tmpdir); | |||
die "$0: Could not change directory to $tmpdir.\n"; | |||
} | |||
# | |||
# Generate a zapped (for coding rules) version of bits.c. In this | |||
# zapped version of bits.c, any functions with illegal operators are | |||
# transformed to have empty function bodies. | |||
# | |||
print "1. Running './dlc -z' to identify coding rules violations.\n"; | |||
system("cp bits.c save-bits.c") == 0 | |||
or die "$0: ERROR: Could not create backup copy of bits.c. $diemsg\n"; | |||
system("./dlc -z -o zap-bits.c bits.c") == 0 | |||
or die "$0: ERROR: zapped bits.c did not compile. $diemsg\n"; | |||
# | |||
# Run btest or BDD checker to determine correctness score | |||
# | |||
if ($USE_BTEST) { | |||
print "\n2. Compiling and running './btest -g' to determine correctness score.\n"; | |||
system("cp zap-bits.c bits.c"); | |||
# Compile btest | |||
system("make btestexplicit") == 0 | |||
or die "$0: Could not make btest in $tmpdir. $diemsg\n"; | |||
# Run btest | |||
$status = system("./btest -g > btest-zapped.out 2>&1"); | |||
if ($status != 0) { | |||
die "$0: ERROR: btest check failed. $diemsg\n"; | |||
} | |||
} | |||
else { | |||
print "\n2. Running './bddcheck/check.pl -g' to determine correctness score.\n"; | |||
system("cp zap-bits.c bits.c"); | |||
$status = system("./bddcheck/check.pl -g > btest-zapped.out 2>&1"); | |||
if ($status != 0) { | |||
die "$0: ERROR: BDD check failed. $diemsg\n"; | |||
} | |||
} | |||
# | |||
# Run dlc to identify operator count violations. | |||
# | |||
print "\n3. Running './dlc -Z' to identify operator count violations.\n"; | |||
system("./dlc -Z -o Zap-bits.c save-bits.c") == 0 | |||
or die "$0: ERROR: dlc unable to generated Zapped bits.c file.\n"; | |||
# | |||
# Run btest or the bdd checker to compute performance score | |||
# | |||
if ($USE_BTEST) { | |||
print "\n4. Compiling and running './btest -g -r 2' to determine performance score.\n"; | |||
system("cp Zap-bits.c bits.c"); | |||
# Compile btest | |||
system("make btestexplicit") == 0 | |||
or die "$0: Could not make btest in $tmpdir. $diemsg\n"; | |||
print "\n"; | |||
# Run btest | |||
$status = system("./btest -g -r 2 > btest-Zapped.out 2>&1"); | |||
if ($status != 0) { | |||
die "$0: ERROR: Zapped btest failed. $diemsg\n"; | |||
} | |||
} | |||
else { | |||
print "\n4. Running './bddcheck/check.pl -g -r 2' to determine performance score.\n"; | |||
system("cp Zap-bits.c bits.c"); | |||
$status = system("./bddcheck/check.pl -g -r 2 > btest-Zapped.out 2>&1"); | |||
if ($status != 0) { | |||
die "$0: ERROR: Zapped bdd checker failed. $diemsg\n"; | |||
} | |||
} | |||
# | |||
# Run dlc to get the operator counts on the zapped input file | |||
# | |||
print "\n5. Running './dlc -e' to get operator count of each function.\n"; | |||
$status = system("./dlc -W1 -e zap-bits.c > dlc-opcount.out 2>&1"); | |||
if ($status != 0) { | |||
die "$0: ERROR: bits.c did not compile. $diemsg\n"; | |||
} | |||
################################################################# | |||
# Collect the correctness and performance results for each puzzle | |||
################################################################# | |||
# | |||
# Collect the correctness results | |||
# | |||
%puzzle_c_points = (); # Correctness score computed by btest | |||
%puzzle_c_errors = (); # Correctness error discovered by btest | |||
%puzzle_c_rating = (); # Correctness puzzle rating (max points) | |||
$inpuzzles = 0; # Becomes true when we start reading puzzle results | |||
$puzzlecnt = 0; # Each puzzle gets a unique number | |||
$total_c_points = 0; | |||
$total_c_rating = 0; | |||
open(INFILE, "$tmpdir/btest-zapped.out") | |||
or die "$0: ERROR: could not open input file $tmpdir/btest-zapped.out\n"; | |||
while ($line = <INFILE>) { | |||
chomp($line); | |||
# Notice that we're ready to read the puzzle scores | |||
if ($line =~ /^Score/) { | |||
$inpuzzles = 1; | |||
next; | |||
} | |||
# Notice that we're through reading the puzzle scores | |||
if ($line =~ /^Total/) { | |||
$inpuzzles = 0; | |||
next; | |||
} | |||
# Read and record a puzzle's name and score | |||
if ($inpuzzles) { | |||
($blank, $c_points, $c_rating, $c_errors, $name) = split(/\s+/, $line); | |||
$puzzle_c_points{$name} = $c_points; | |||
$puzzle_c_errors{$name} = $c_errors; | |||
$puzzle_c_rating{$name} = $c_rating; | |||
$puzzle_number{$name} = $puzzlecnt++; | |||
$total_c_points += $c_points; | |||
$total_c_rating += $c_rating; | |||
} | |||
} | |||
close(INFILE); | |||
# | |||
# Collect the performance results | |||
# | |||
%puzzle_p_points = (); # Performance points | |||
$inpuzzles = 0; # Becomes true when we start reading puzzle results | |||
$total_p_points = 0; | |||
$total_p_rating = 0; | |||
open(INFILE, "$tmpdir/btest-Zapped.out") | |||
or die "$0: ERROR: could not open input file $tmpdir/btest-Zapped.out\n"; | |||
while ($line = <INFILE>) { | |||
chomp($line); | |||
# Notice that we're ready to read the puzzle scores | |||
if ($line =~ /^Score/) { | |||
$inpuzzles = 1; | |||
next; | |||
} | |||
# Notice that we're through reading the puzzle scores | |||
if ($line =~ /^Total/) { | |||
$inpuzzles = 0; | |||
next; | |||
} | |||
# Read and record a puzzle's name and score | |||
if ($inpuzzles) { | |||
($blank, $p_points, $p_rating, $p_errors, $name) = split(/\s+/, $line); | |||
$puzzle_p_points{$name} = $p_points; | |||
$total_p_points += $p_points; | |||
$total_p_rating += $p_rating; | |||
} | |||
} | |||
close(INFILE); | |||
# | |||
# Collect the operator counts generated by dlc | |||
# | |||
open(INFILE, "$tmpdir/dlc-opcount.out") | |||
or die "$0: ERROR: could not open input file $tmpdir/dlc-opcount.out\n"; | |||
$tops = 0; | |||
while ($line = <INFILE>) { | |||
chomp($line); | |||
if ($line =~ /(\d+) operators/) { | |||
($foo, $foo, $foo, $name, $msg) = split(/:/, $line); | |||
$puzzle_p_ops{$name} = $1; | |||
$tops += $1; | |||
} | |||
} | |||
close(INFILE); | |||
# | |||
# Print a table of results sorted by puzzle number | |||
# | |||
print "\n"; | |||
printf("%s\t%s\n", "Correctness Results", "Perf Results"); | |||
printf("%s\t%s\t%s\t%s\t%s\t%s\n", "Points", "Rating", "Errors", | |||
"Points", "Ops", "Puzzle"); | |||
foreach $name (sort {$puzzle_number{$a} <=> $puzzle_number{$b}} | |||
keys %puzzle_number) { | |||
printf("%d\t%d\t%d\t%d\t%d\t\%s\n", | |||
$puzzle_c_points{$name}, | |||
$puzzle_c_rating{$name}, | |||
$puzzle_c_errors{$name}, | |||
$puzzle_p_points{$name}, | |||
$puzzle_p_ops{$name}, | |||
$name); | |||
} | |||
$tpoints = $total_c_points + $total_p_points; | |||
$trating = $total_c_rating + $total_p_rating; | |||
print "\nScore = $tpoints/$trating [$total_c_points/$total_c_rating Corr + $total_p_points/$total_p_rating Perf] ($tops total operators)\n"; | |||
# | |||
# Optionally send the autoresult to the contest server if the driver | |||
# was called with the -u command line flag. | |||
# | |||
if ($nickname) { | |||
# Generate the autoresult | |||
$autoresult = "$tpoints|$total_c_points|$total_p_points|$tops"; | |||
foreach $name (sort {$puzzle_number{$a} <=> $puzzle_number{$b}} | |||
keys %puzzle_number) { | |||
$autoresult .= " |$name:$puzzle_c_points{$name}:$puzzle_c_rating{$name}:$puzzle_p_points{$name}:$puzzle_p_ops{$name}"; | |||
} | |||
# Post the autoresult to the server. The Linux login id is | |||
# concatenated with the user-supplied nickname for some (very) loose | |||
# authentication of submissions. | |||
&Driverlib::driver_post("$login:$nickname", $autoresult, $autograded); | |||
} | |||
# Clean up and exit | |||
clean ($tmpdir); | |||
exit; | |||
################## | |||
# Helper functions | |||
# | |||
# | |||
# check_nickname - Check a nickname for legality | |||
# | |||
sub check_nickname { | |||
my $nickname = shift; | |||
# Nicknames can't be empty | |||
if (length($nickname) < 1) { | |||
die "$0: Error: Empty nickname.\n"; | |||
} | |||
# Nicknames can't be too long | |||
if (length($nickname) > 35) { | |||
die "$0: Error: Nickname exceeds 35 characters.\n"; | |||
} | |||
# Nicknames can have restricted set of metacharacters (e.g., no # | |||
# HTML tags) | |||
if (!($nickname =~ /^[_-\w.,'@ ]+$/)) { | |||
die "$0: Error: Illegal character in nickname. Only alphanumerics, apostrophes, commas, periods, dashes, underscores, and ampersands are allowed.\n"; | |||
} | |||
# Nicknames can't be all whitespace | |||
if ($nickname =~ /^\s*$/) { | |||
die "$0: Error: Nickname is all whitespace.\n"; | |||
} | |||
} | |||
# | |||
# clean - remove the scratch directory | |||
# | |||
sub clean { | |||
my $tmpdir = shift; | |||
system("rm -rf $tmpdir"); | |||
} | |||
@ -1,151 +0,0 @@ | |||
/* Display structure of floating-point numbers */ | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
float strtof(const char *nptr, char **endptr); | |||
#define FLOAT_SIZE 32 | |||
#define FRAC_SIZE 23 | |||
#define EXP_SIZE 8 | |||
#define BIAS ((1<<(EXP_SIZE-1))-1) | |||
#define FRAC_MASK ((1<<FRAC_SIZE)-1) | |||
#define EXP_MASK ((1<<EXP_SIZE)-1) | |||
/* Floating point helpers */ | |||
unsigned f2u(float f) | |||
{ | |||
union { | |||
unsigned u; | |||
float f; | |||
} v; | |||
v.u = 0; | |||
v.f = f; | |||
return v.u; | |||
} | |||
static float u2f(unsigned u) | |||
{ | |||
union { | |||
unsigned u; | |||
float f; | |||
} v; | |||
v.u = u; | |||
return v.f; | |||
} | |||
/* Get exponent */ | |||
unsigned get_exp(unsigned uf) | |||
{ | |||
return (uf>>FRAC_SIZE) & EXP_MASK; | |||
} | |||
/* Get fraction */ | |||
unsigned get_frac(unsigned uf) | |||
{ | |||
return uf & FRAC_MASK; | |||
} | |||
/* Get sign */ | |||
unsigned get_sign(unsigned uf) | |||
{ | |||
return (uf>>(FLOAT_SIZE-1)) & 0x1; | |||
} | |||
void show_float(unsigned uf) | |||
{ | |||
float f = u2f(uf); | |||
unsigned exp = get_exp(uf); | |||
unsigned frac = get_frac(uf); | |||
unsigned sign = get_sign(uf); | |||
printf("\nFloating point value %.10g\n", f); | |||
printf("Bit Representation 0x%.8x, sign = %x, exponent = 0x%.2x, fraction = 0x%.6x\n", | |||
uf, sign, exp, frac); | |||
if (exp == EXP_MASK) { | |||
if (frac == 0) { | |||
printf("%cInfinity\n", sign ? '-' : '+'); | |||
} else | |||
printf("Not-A-Number\n"); | |||
} else { | |||
int denorm = (exp == 0); | |||
int uexp = denorm ? 1-BIAS : exp - BIAS; | |||
int mantissa = denorm ? frac : frac + (1<<FRAC_SIZE); | |||
float fman = (float) mantissa / (float) (1<<FRAC_SIZE); | |||
printf("%s. %c%.10f X 2^(%d)\n", | |||
denorm ? "Denormalized" : "Normalized", | |||
sign ? '-' : '+', | |||
fman, uexp); | |||
} | |||
} | |||
/* Extract hex/decimal/or float value from string */ | |||
static int get_num_val(char *sval, unsigned *valp) { | |||
char *endp; | |||
/* See if it's an integer or floating point */ | |||
int ishex = 0; | |||
int isfloat = 0; | |||
int i; | |||
for (i = 0; sval[i]; i++) { | |||
switch (sval[i]) { | |||
case 'x': | |||
case 'X': | |||
ishex = 1; | |||
break; | |||
case 'e': | |||
case 'E': | |||
if (!ishex) | |||
isfloat = 1; | |||
break; | |||
case '.': | |||
isfloat = 1; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
if (isfloat) { | |||
float fval = strtof(sval, &endp); | |||
if (!*endp) { | |||
*valp = *(unsigned *) &fval; | |||
return 1; | |||
} | |||
return 0; | |||
} else { | |||
long long int llval = strtoll(sval, &endp, 0); | |||
long long int upperbits = llval >> 31; | |||
/* will give -1 for negative, 0 or 1 for positive */ | |||
if (valp && (upperbits == 0 || upperbits == -1 || upperbits == 1)) { | |||
*valp = (unsigned) llval; | |||
return 1; | |||
} | |||
return 0; | |||
} | |||
} | |||
void usage(char *fname) { | |||
printf("Usage: %s val1 val2 ...\n", fname); | |||
printf("Values may be given as hex patterns or as floating point numbers\n"); | |||
exit(0); | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
int i; | |||
unsigned uf; | |||
if (argc < 2) | |||
usage(argv[0]); | |||
for (i = 1; i < argc; i++) { | |||
char *sval = argv[i]; | |||
if (get_num_val(sval, &uf)) { | |||
show_float(uf); | |||
} else { | |||
printf("Invalid 32-bit number: '%s'\n", sval); | |||
usage(argv[0]); | |||
} | |||
} | |||
return 0; | |||
} | |||
@ -1,75 +0,0 @@ | |||
/* Display value of fixed point numbers */ | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
/* Extract hex/decimal/or float value from string */ | |||
static int get_num_val(char *sval, unsigned *valp) { | |||
char *endp; | |||
/* See if it's an integer or floating point */ | |||
int ishex = 0; | |||
int isfloat = 0; | |||
int i; | |||
for (i = 0; sval[i]; i++) { | |||
switch (sval[i]) { | |||
case 'x': | |||
case 'X': | |||
ishex = 1; | |||
break; | |||
case 'e': | |||
case 'E': | |||
if (!ishex) | |||
isfloat = 1; | |||
break; | |||
case '.': | |||
isfloat = 1; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
if (isfloat) { | |||
return 0; /* Not supposed to have a float here */ | |||
} else { | |||
long long int llval = strtoll(sval, &endp, 0); | |||
long long int upperbits = llval >> 31; | |||
/* will give -1 for negative, 0 or 1 for positive */ | |||
if (valp && (upperbits == 0 || upperbits == -1 || upperbits == 1)) { | |||
*valp = (unsigned) llval; | |||
return 1; | |||
} | |||
return 0; | |||
} | |||
} | |||
void show_int(unsigned uf) | |||
{ | |||
printf("Hex = 0x%.8x,\tSigned = %d,\tUnsigned = %u\n", | |||
uf, (int) uf, uf); | |||
} | |||
void usage(char *fname) { | |||
printf("Usage: %s val1 val2 ...\n", fname); | |||
printf("Values may be given in hex or decimal\n"); | |||
exit(0); | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
int i; | |||
unsigned uf; | |||
if (argc < 2) | |||
usage(argv[0]); | |||
for (i = 1; i < argc; i++) { | |||
char *sval = argv[i]; | |||
if (get_num_val(sval, &uf)) { | |||
show_int(uf); | |||
} else { | |||
printf("Cannot convert '%s' to 32-bit number\n", sval); | |||
} | |||
} | |||
return 0; | |||
} | |||
@ -1,124 +0,0 @@ | |||
/* Testing Code */ | |||
#include <limits.h> | |||
#include <math.h> | |||
/* Routines used by floation point test code */ | |||
/* Convert from bit level representation to floating point number */ | |||
float u2f(unsigned u) { | |||
union { | |||
unsigned u; | |||
float f; | |||
} a; | |||
a.u = u; | |||
return a.f; | |||
} | |||
/* Convert from floating point number to bit-level representation */ | |||
unsigned f2u(float f) { | |||
union { | |||
unsigned u; | |||
float f; | |||
} a; | |||
a.f = f; | |||
return a.u; | |||
} | |||
/* Copyright (C) 1991-2020 Free Software Foundation, Inc. | |||
This file is part of the GNU C Library. | |||
The GNU C Library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
The GNU C Library is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with the GNU C Library; if not, see | |||
<https://www.gnu.org/licenses/>. */ | |||
/* This header is separate from features.h so that the compiler can | |||
include it implicitly at the start of every compilation. It must | |||
not itself include <features.h> or any other header that includes | |||
<features.h> because the implicit include comes before any feature | |||
test macros that may be defined in a source file before it first | |||
explicitly includes a system header. GCC knows the name of this | |||
header in order to preinclude it. */ | |||
/* glibc's intent is to support the IEC 559 math functionality, real | |||
and complex. If the GCC (4.9 and later) predefined macros | |||
specifying compiler intent are available, use them to determine | |||
whether the overall intent is to support these features; otherwise, | |||
presume an older compiler has intent to support these features and | |||
define these macros by default. */ | |||
/* wchar_t uses Unicode 10.0.0. Version 10.0 of the Unicode Standard is | |||
synchronized with ISO/IEC 10646:2017, fifth edition, plus | |||
the following additions from Amendment 1 to the fifth edition: | |||
- 56 emoji characters | |||
- 285 hentaigana | |||
- 3 additional Zanabazar Square characters */ | |||
//1 | |||
int test_bitXor(int x, int y) | |||
{ | |||
return x^y; | |||
} | |||
int test_tmin(void) { | |||
return 0x80000000; | |||
} | |||
//2 | |||
int test_isTmax(int x) { | |||
return x == 0x7FFFFFFF; | |||
} | |||
int test_allOddBits(int x) { | |||
int i; | |||
for (i = 1; i < 32; i+=2) | |||
if ((x & (1<<i)) == 0) | |||
return 0; | |||
return 1; | |||
} | |||
int test_negate(int x) { | |||
return -x; | |||
} | |||
//3 | |||
int test_isAsciiDigit(int x) { | |||
return (0x30 <= x) && (x <= 0x39); | |||
} | |||
int test_conditional(int x, int y, int z) | |||
{ | |||
return x?y:z; | |||
} | |||
int test_isLessOrEqual(int x, int y) | |||
{ | |||
return x <= y; | |||
} | |||
//4 | |||
int test_logicalNeg(int x) | |||
{ | |||
return !x; | |||
} | |||
int test_howManyBits(int x) { | |||
unsigned int a, cnt; | |||
x = x<0 ? -x-1 : x; | |||
a = (unsigned int)x; | |||
for (cnt=0; a; a>>=1, cnt++) | |||
; | |||
return (int)(cnt + 1); | |||
} | |||
//float | |||
unsigned test_floatScale2(unsigned uf) { | |||
float f = u2f(uf); | |||
float tf = 2*f; | |||
if (isnan(f)) | |||
return uf; | |||
else | |||
return f2u(tf); | |||
} | |||
int test_floatFloat2Int(unsigned uf) { | |||
float f = u2f(uf); | |||
int x = (int) f; | |||
return x; | |||
} | |||
// #include "floatPower2.c" |
@ -1,21 +0,0 @@ | |||
MIT License | |||
Copyright (c) 2023 AquaOH | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. |
@ -0,0 +1,971 @@ | |||
# All | |||
This is bomb 121. | |||
It belongs to 10225501432 (10225501432@stu.ecnu.edu.cn) | |||
# phase_1 | |||
## 查看phase_1汇编 | |||
~~~python | |||
=> 0x0000555555401204 <+0>: sub $0x8,%rsp | |||
0x0000555555401208 <+4>: lea 0x1801(%rip),%rsi # 0x555555402a10 | |||
0x000055555540120f <+11>: call 0x55555540172b <strings_not_equal> | |||
0x0000555555401214 <+16>: test %eax,%eax | |||
0x0000555555401216 <+18>: jne 0x55555540121d <phase_1+25> | |||
0x0000555555401218 <+20>: add $0x8,%rsp | |||
0x000055555540121c <+24>: ret | |||
0x000055555540121d <+25>: call 0x5555554019e6 <explode_bomb> | |||
0x0000555555401222 <+30>: jmp 0x555555401218 <phase_1+20> | |||
~~~ | |||
<+0>行进行了压栈操作 | |||
<+4>行将内存地址0x555555402a10赋予了寄存器%rsi | |||
<+11>行调用了函数<strings_not_equal>,从名称来看,是用于判断字符串是否相等,这串字符串就是密码 | |||
进入函数<strings_not_equal>查看,只看前几行 | |||
## 查看strings_not_equal汇编 | |||
~~~python | |||
=> 0x000055555540172b <+0>: push %r12 | |||
0x000055555540172d <+2>: push %rbp | |||
0x000055555540172e <+3>: push %rbx | |||
0x000055555540172f <+4>: mov %rdi,%rbx | |||
0x0000555555401732 <+7>: mov %rsi,%rbp | |||
0x0000555555401735 <+10>: call 0x55555540170e <string_length> | |||
~~~ | |||
发现调用了函数<string_length>,进入查看 | |||
## 查看string_length汇编 | |||
~~~python | |||
=> 0x000055555540170e <+0>: cmpb $0x0,(%rdi) | |||
0x0000555555401711 <+3>: je 0x555555401725 <string_length+23> | |||
0x0000555555401713 <+5>: mov %rdi,%rdx | |||
0x0000555555401716 <+8>: add $0x1,%rdx | |||
0x000055555540171a <+12>: mov %edx,%eax | |||
0x000055555540171c <+14>: sub %edi,%eax | |||
0x000055555540171e <+16>: cmpb $0x0,(%rdx) | |||
0x0000555555401721 <+19>: jne 0x555555401716 <string_length+8> | |||
0x0000555555401723 <+21>: repz ret | |||
0x0000555555401725 <+23>: mov $0x0,%eax | |||
0x000055555540172a <+28>: ret | |||
~~~ | |||
<0> <3>行用于判断是否是空地址,是的话返回0并退出 | |||
<5> 到 <19>行使用了循环的方式判断字符串有几位 | |||
首先将%rdi地址赋予%rdx,并且进入循环 | |||
随后进入循环,每次循环%rdx地址后后移一位 | |||
每完成一次循环,%eax就加一 | |||
跳出条件是 %rdx的地址指向的内存位置所储存的数据为0 | |||
故%rdi寄存器储存的是用户输入字符串的起始地址 | |||
## 继续查看strings_not_equal的后续部分 | |||
~~~python | |||
=> 0x000055555540172b <+0>: push %r12 | |||
0x000055555540172d <+2>: push %rbp | |||
0x000055555540172e <+3>: push %rbx | |||
0x000055555540172f <+4>: mov %rdi,%rbx | |||
0x0000555555401732 <+7>: mov %rsi,%rbp | |||
0x0000555555401735 <+10>: call 0x55555540170e <string_length> | |||
0x000055555540173a <+15>: mov %eax,%r12d | |||
0x000055555540173d <+18>: mov %rbp,%rdi | |||
0x0000555555401740 <+21>: call 0x55555540170e <string_length> | |||
0x0000555555401745 <+26>: mov $0x1,%edx | |||
0x000055555540174a <+31>: cmp %eax,%r12d | |||
0x000055555540174d <+34>: je 0x555555401756 <strings_not_equal+43> | |||
0x000055555540174f <+36>: mov %edx,%eax | |||
0x0000555555401751 <+38>: pop %rbx | |||
0x0000555555401752 <+39>: pop %rbp | |||
0x0000555555401753 <+40>: pop %r12 | |||
0x0000555555401755 <+42>: ret | |||
~~~ | |||
这一段实现了检查输入字符串和密码字符串的长度是否相等 | |||
可以锁定进入<strings_not_equal>函数前的%rsi寄存器就是密码部分 | |||
## 结论 | |||
使用命令'x/s 0x555555402a10'直接查看 | |||
发现密码为"I am not part of the problem. I am a Republican." | |||
# phase_2 | |||
## 查看phase_2汇编 | |||
首先查看部分汇编 | |||
~~~python | |||
=> 0x0000555555401224 <+0>: push %rbp | |||
0x0000555555401225 <+1>: push %rbx | |||
0x0000555555401226 <+2>: sub $0x28,%rsp | |||
0x000055555540122a <+6>: mov %fs:0x28,%rax | |||
0x0000555555401233 <+15>: mov %rax,0x18(%rsp) | |||
0x0000555555401238 <+20>: xor %eax,%eax | |||
0x000055555540123a <+22>: mov %rsp,%rsi | |||
0x000055555540123d <+25>: call 0x555555401a22 <read_six_numbers> | |||
~~~ | |||
可以发现其中途调用了函数<read_six_numbers>,其密码必然是六个数字 | |||
进入函数<read_six_numbers>查看 | |||
## 查看read_six_numbers汇编 | |||
~~~python | |||
=> 0x0000555555401a22 <+0>: sub $0x8,%rsp | |||
0x0000555555401a26 <+4>: mov %rsi,%rdx | |||
0x0000555555401a29 <+7>: lea 0x4(%rsi),%rcx | |||
0x0000555555401a2d <+11>: lea 0x14(%rsi),%rax | |||
0x0000555555401a31 <+15>: push %rax | |||
0x0000555555401a32 <+16>: lea 0x10(%rsi),%rax | |||
0x0000555555401a36 <+20>: push %rax | |||
0x0000555555401a37 <+21>: lea 0xc(%rsi),%r9 | |||
0x0000555555401a3b <+25>: lea 0x8(%rsi),%r8 | |||
0x0000555555401a3f <+29>: lea 0x12a3(%rip),%rsi # 0x555555402ce9 | |||
0x0000555555401a46 <+36>: mov $0x0,%eax | |||
0x0000555555401a4b <+41>: call 0x555555400ee0 <__isoc99_sscanf@plt> | |||
0x0000555555401a50 <+46>: add $0x10,%rsp | |||
0x0000555555401a54 <+50>: cmp $0x5,%eax | |||
0x0000555555401a57 <+53>: jle 0x555555401a5e <read_six_numbers+60> | |||
0x0000555555401a59 <+55>: add $0x8,%rsp | |||
0x0000555555401a5d <+59>: ret | |||
0x0000555555401a5e <+60>: call 0x5555554019e6 <explode_bomb> | |||
~~~ | |||
显然其判断起始地址储存在%rsi的数组中的数据 | |||
而由phase_2汇编可知,%rsi储存的地址由%rsp赋值而来 | |||
则%rsp含有用户输入数组的储存地址 | |||
## 继续查看phase_2汇编 | |||
### 进入循环 | |||
~~~python | |||
0x0000555555401242 <+30>: cmpl $0x0,(%rsp) | |||
0x0000555555401246 <+34>: jne 0x55555540124f <phase_2+43> | |||
0x0000555555401248 <+36>: cmpl $0x1,0x4(%rsp) | |||
0x000055555540124d <+41>: je 0x555555401254 <phase_2+48> | |||
0x000055555540124f <+43>: call 0x5555554019e6 <explode_bomb> | |||
~~~ | |||
判断数组第一位是否为0,不是则引爆炸弹 | |||
判断数组第二位是否为1,是则跳转至<48>行 | |||
~~~python | |||
0x0000555555401254 <+48>: mov %rsp,%rbx | |||
0x0000555555401257 <+51>: lea 0x10(%rbx),%rbp | |||
0x000055555540125b <+55>: jmp 0x555555401266 <phase_2+66> | |||
0x000055555540125d <+57>: add $0x4,%rbx | |||
0x0000555555401261 <+61>: cmp %rbp,%rbx | |||
0x0000555555401264 <+64>: je 0x555555401277 <phase_2+83> | |||
0x0000555555401266 <+66>: mov 0x4(%rbx),%eax | |||
0x0000555555401269 <+69>: add (%rbx),%eax | |||
0x000055555540126b <+71>: cmp %eax,0x8(%rbx) | |||
0x000055555540126e <+74>: je 0x55555540125d <phase_2+57> | |||
0x0000555555401270 <+76>: call 0x5555554019e6 <explode_bomb> | |||
0x0000555555401275 <+81>: jmp 0x55555540125d <phase_2+57> | |||
~~~ | |||
将%rsp的地址赋予%rbx,%rsp地址+16赋予%rbp | |||
随后跳转至<66>行 | |||
%eax储存%rbx+4地址所指向的值,并将其与%rbx所指向的值相加 | |||
随后%eax与%rbx+8地址所指向的值比较 | |||
相等则跳转至<57>行,%rbx的地址+4,如果%rsp+16与%rbx+4相等,则跳转至83行 | |||
从实际效果看,循环执行4次 | |||
每次将三元组,前两位相加与最后一位比较 | |||
相等则继续循环,不相等则爆炸 | |||
由上文可知,前两位分别为 0 1 | |||
~~~python | |||
第一次循环: 0 + 1 = 1 | |||
第二次循环: 1 + 1 = 2 | |||
第三次循环: 1 + 2 = 3 | |||
第四次循环: 2 + 3 = 5 | |||
~~~ | |||
故整个六位数字应该为 0 1 1 2 3 5 | |||
### 跳出循环&返回数值 | |||
~~~python | |||
0x0000555555401277 <+83>: mov 0x18(%rsp),%rax | |||
0x000055555540127c <+88>: xor %fs:0x28,%rax | |||
0x0000555555401285 <+97>: jne 0x55555540128e <phase_2+106> | |||
0x0000555555401287 <+99>: add $0x28,%rsp | |||
0x000055555540128b <+103>: pop %rbx | |||
0x000055555540128c <+104>: pop %rbp | |||
0x000055555540128d <+105>: ret | |||
0x000055555540128e <+106>: call 0x555555400e40 <__stack_chk_fail@plt> | |||
~~~ | |||
实际效果来看,跳转到<83>行后,循环跳出,函数收尾并正常返回 | |||
### 结论 | |||
故整个六位数字应该为 0 1 1 2 3 5 | |||
# phase_3 | |||
## 查看部分汇编 | |||
~~~python | |||
0x0000555555401293 <+0>: sub $0x18,%rsp | |||
0x0000555555401297 <+4>: mov %fs:0x28,%rax | |||
0x00005555554012a0 <+13>: mov %rax,0x8(%rsp) | |||
0x00005555554012a5 <+18>: xor %eax,%eax | |||
0x00005555554012a7 <+20>: lea 0x4(%rsp),%rcx | |||
0x00005555554012ac <+25>: mov %rsp,%rdx | |||
0x00005555554012af <+28>: lea 0x1a3f(%rip),%rsi # 0x555555402cf5 | |||
0x00005555554012b6 <+35>: call 0x555555400ee0 <__isoc99_sscanf@plt> | |||
0x00005555554012bb <+40>: cmp $0x1,%eax | |||
0x00005555554012be <+43>: jle 0x5555554012dd <phase_3+74> | |||
0x00005555554012c0 <+45>: cmpl $0x7,(%rsp) | |||
0x00005555554012c4 <+49>: ja 0x555555401363 <phase_3+208> | |||
~~~ | |||
可以看出phase_3调用了函数sscanf,其返回值为输入字符的个数 | |||
输入命令 `x/s 0x555555402cf5`显示"%d %d",可知sscanf输入两个字符,其返回值为2 | |||
<45><49>判断第一个数字是否小于0x7,故第一个数字应设置小于7 | |||
## 随意设置数字并带入 | |||
设置输入数据为5 32 | |||
随后断点设置在phase_3,使用nexti单步调试 | |||
查看汇编,并一路nexti | |||
~~~python | |||
0x00005555554012ca <+55>: mov (%rsp),%eax | |||
0x00005555554012cd <+58>: lea 0x17ac(%rip),%rdx # 0x555555402a80 | |||
0x00005555554012d4 <+65>: movslq (%rdx,%rax,4),%rax | |||
0x00005555554012d8 <+69>: add %rdx,%rax | |||
0x00005555554012db <+72>: jmp *%rax | |||
0x00005555554012dd <+74>: call 0x5555554019e6 <explode_bomb> | |||
~~~ | |||
发现执行完`jmp *%rax`后,跳转至`0x000055555540134e` | |||
## 继续查看汇编 | |||
继续nexti | |||
~~~python | |||
0x00005555554012e2 <+79>: jmp 0x5555554012c0 <phase_3+45> | |||
0x00005555554012e4 <+81>: mov $0x2ca,%eax | |||
0x00005555554012e9 <+86>: jmp 0x5555554012f0 <phase_3+93> | |||
0x00005555554012eb <+88>: mov $0x0,%eax | |||
0x00005555554012f0 <+93>: sub $0xf2,%eax | |||
0x00005555554012f5 <+98>: add $0x14a,%eax | |||
0x00005555554012fa <+103>: sub $0x162,%eax | |||
0x00005555554012ff <+108>: add $0x162,%eax | |||
0x0000555555401304 <+113>: sub $0x162,%eax | |||
0x0000555555401309 <+118>: add $0x162,%eax | |||
0x000055555540130e <+123>: sub $0x162,%eax | |||
0x0000555555401313 <+128>: cmpl $0x5,(%rsp) | |||
0x0000555555401317 <+132>: jg 0x55555540131f <phase_3+140> | |||
0x0000555555401319 <+134>: cmp %eax,0x4(%rsp) | |||
0x000055555540131d <+138>: je 0x555555401324 <phase_3+145> | |||
0x000055555540131f <+140>: call 0x5555554019e6 <explode_bomb> | |||
0x0000555555401324 <+145>: mov 0x8(%rsp),%rax | |||
0x0000555555401329 <+150>: xor %fs:0x28,%rax | |||
0x0000555555401332 <+159>: jne 0x55555540136f <phase_3+220> | |||
0x0000555555401334 <+161>: add $0x18,%rsp | |||
0x0000555555401338 <+165>: ret | |||
0x0000555555401339 <+166>: mov $0x0,%eax | |||
0x000055555540133e <+171>: jmp 0x5555554012f5 <phase_3+98> | |||
0x0000555555401340 <+173>: mov $0x0,%eax | |||
0x0000555555401345 <+178>: jmp 0x5555554012fa <phase_3+103> | |||
0x0000555555401347 <+180>: mov $0x0,%eax | |||
0x000055555540134c <+185>: jmp 0x5555554012ff <phase_3+108> | |||
0x000055555540134e <+187>: mov $0x0,%eax | |||
0x0000555555401353 <+192>: jmp 0x555555401304 <phase_3+113> | |||
0x0000555555401355 <+194>: mov $0x0,%eax | |||
0x000055555540135a <+199>: jmp 0x555555401309 <phase_3+118> | |||
0x000055555540135c <+201>: mov $0x0,%eax | |||
0x0000555555401361 <+206>: jmp 0x55555540130e <phase_3+123> | |||
0x0000555555401363 <+208>: call 0x5555554019e6 <explode_bomb> | |||
0x0000555555401368 <+213>: mov $0x0,%eax | |||
0x000055555540136d <+218>: jmp 0x555555401313 <phase_3+128> | |||
0x000055555540136f <+220>: call 0x555555400e40 <__stack_chk_fail@plt> | |||
~~~ | |||
发现其跳转至`0x0000555555401304` | |||
~~~python | |||
0x0000555555401313 <+128>: cmpl $0x5,(%rsp) | |||
0x0000555555401317 <+132>: jg 0x55555540131f <phase_3+140> | |||
~~~ | |||
判断第一个数字是否小于等于5,不是则引爆 | |||
~~~python | |||
0x0000555555401319 <+134>: cmp %eax,0x4(%rsp) | |||
0x000055555540131d <+138>: je 0x555555401324 <phase_3+145> | |||
0x000055555540131f <+140>: call 0x5555554019e6 <explode_bomb> | |||
~~~ | |||
判断第二个数字是否和eax寄存器中数据相等 | |||
此时查看寄存器eax中数值`p $eax`,返回数据为-354,故第二个数据为-354 | |||
## 结论 | |||
phase_3第一个数据要求小于等于5,第二个数据要求为-354 | |||
# phase_4 | |||
## 查看部分汇编代码 | |||
~~~python | |||
0x00005555554013ad <+0>: sub $0x18,%rsp | |||
0x00005555554013b1 <+4>: mov %fs:0x28,%rax | |||
0x00005555554013ba <+13>: mov %rax,0x8(%rsp) | |||
0x00005555554013bf <+18>: xor %eax,%eax | |||
0x00005555554013c1 <+20>: mov %rsp,%rcx | |||
0x00005555554013c4 <+23>: lea 0x4(%rsp),%rdx | |||
0x00005555554013c9 <+28>: lea 0x1925(%rip),%rsi # 0x555555402cf5 | |||
0x00005555554013d0 <+35>: call 0x555555400ee0 <__isoc99_sscanf@plt> | |||
0x00005555554013d5 <+40>: cmp $0x2,%eax | |||
0x00005555554013d8 <+43>: jne 0x5555554013e5 <phase_4+56> | |||
~~~ | |||
可以看到`0x00005555554013c9 <+28>: lea 0x1925(%rip),%rsi # 0x555555402cf5` | |||
输入`x/s 0x555555402cf5`,显示为`%d %d`,因此输入数据为两个整形 | |||
随意输入两个整数,例如`114 514` | |||
~~~python | |||
p *(int*)$rsp | |||
p *(int*)($rsp+4) | |||
$1 = 514 | |||
$2 = 114 | |||
~~~ | |||
所以rsp存储第二个数字的地址,rsp+4存储第一个数字的地址 | |||
## 继续查看部分汇编代码 | |||
~~~python | |||
0x00005555554013da <+45>: mov (%rsp),%eax | |||
0x00005555554013dd <+48>: sub $0x2,%eax | |||
0x00005555554013e0 <+51>: cmp $0x2,%eax | |||
0x00005555554013e3 <+54>: jbe 0x5555554013ea <phase_4+61> | |||
0x00005555554013e5 <+56>: call 0x5555554019e6 <explode_bomb> | |||
0x00005555554013ea <+61>: mov (%rsp),%esi | |||
0x00005555554013ed <+64>: mov $0x9,%edi | |||
0x00005555554013f2 <+69>: call 0x555555401374 <func4> | |||
0x00005555554013f7 <+74>: cmp %eax,0x4(%rsp) | |||
0x00005555554013fb <+78>: je 0x555555401402 <phase_4+85> | |||
0x00005555554013fd <+80>: call 0x5555554019e6 <explode_bomb> | |||
0x0000555555401402 <+85>: mov 0x8(%rsp),%rax | |||
0x0000555555401407 <+90>: xor %fs:0x28,%rax | |||
0x0000555555401410 <+99>: jne 0x555555401417 <phase_4+106> | |||
0x0000555555401412 <+101>: add $0x18,%rsp | |||
0x0000555555401416 <+105>: ret | |||
0x0000555555401417 <+106>: call 0x555555400e40 <__stack_chk_fail@plt> | |||
~~~ | |||
<+45> <+48> <+51> <+54> 要求输入的第二个数据小于等于4 | |||
不妨让输入数据变为`114 4`再次单步调试 | |||
一路nexti,直至,<+69>执行完毕 | |||
显然<+74> <+78>判断第一个数据是否和函数 func4 返回值是否相同 | |||
输入`p $eax`查看,显示为352 | |||
## 结论 | |||
因此,可行的一组数据为`352 4` | |||
# phase_5 | |||
## 查看部分汇编代码 | |||
~~~python | |||
0x000055555540141c <+0>: sub $0x18,%rsp | |||
0x0000555555401420 <+4>: mov %fs:0x28,%rax | |||
0x0000555555401429 <+13>: mov %rax,0x8(%rsp) | |||
0x000055555540142e <+18>: xor %eax,%eax | |||
0x0000555555401430 <+20>: lea 0x4(%rsp),%rcx | |||
0x0000555555401435 <+25>: mov %rsp,%rdx | |||
0x0000555555401438 <+28>: lea 0x18b6(%rip),%rsi # 0x555555402cf5 | |||
0x000055555540143f <+35>: call 0x555555400ee0 <__isoc99_sscanf@plt> | |||
0x0000555555401444 <+40>: cmp $0x1,%eax | |||
0x0000555555401447 <+43>: jle 0x5555554014a3 <phase_5+135> | |||
~~~ | |||
<+28>行可以看到`0x555555402cf5` | |||
输入`x/s 0x555555402cf5`显示`%d %d` | |||
故输入两个十进制整数 | |||
## 进入循环 | |||
### 循环起点 | |||
~~~python | |||
0x0000555555401449 <+45>: mov (%rsp),%eax | |||
0x000055555540144c <+48>: and $0xf,%eax | |||
0x000055555540144f <+51>: mov %eax,(%rsp) | |||
0x0000555555401452 <+54>: cmp $0xf,%eax | |||
0x0000555555401455 <+57>: je 0x555555401489 <phase_5+109> | |||
0x0000555555401457 <+59>: mov $0x0,%ecx | |||
0x000055555540145c <+64>: mov $0x0,%edx | |||
0x0000555555401461 <+69>: lea 0x1638(%rip),%rsi # 0x555555402aa0 <array.3415> | |||
~~~ | |||
发现有一个数组array.3415 | |||
查看后发现数组为{10, 2, 14, 7, 8, 12, 15, 11, 0, 4, 1, 13, 3, 9, 6, 5} | |||
### 进入循环 | |||
~~~python | |||
0x0000555555401468 <+76>: add $0x1,%edx | |||
0x000055555540146b <+79>: cltq | |||
0x000055555540146d <+81>: mov (%rsi,%rax,4),%eax | |||
0x0000555555401470 <+84>: add %eax,%ecx | |||
0x0000555555401472 <+86>: cmp $0xf,%eax | |||
0x0000555555401475 <+89>: jne 0x555555401468 <phase_5+76> | |||
0x0000555555401477 <+91>: movl $0xf,(%rsp) | |||
0x000055555540147e <+98>: cmp $0xf,%edx | |||
=> 0x0000555555401481 <+101>: jne 0x555555401489 <phase_5+109> | |||
0x0000555555401483 <+103>: cmp %ecx,0x4(%rsp) | |||
0x0000555555401487 <+107>: je 0x55555540148e <phase_5+114> | |||
~~~ | |||
当寄存器%edx与%eax同时为0xf时,循环跳出 | |||
因为%edx初始值为0,进入循环加1,则循环必定15次 | |||
故可逆推最开始进入循环时%eax的值 | |||
逆推得%eax为5 | |||
此时输入%ecx计算得结果为115 | |||
### 退出循环 | |||
正常检查栈金丝雀值 | |||
~~~python | |||
0x0000555555401489 <+109>: call 0x5555554019e6 <explode_bomb> | |||
0x000055555540148e <+114>: mov 0x8(%rsp),%rax | |||
0x0000555555401493 <+119>: xor %fs:0x28,%rax | |||
0x000055555540149c <+128>: jne 0x5555554014aa <phase_5+142> | |||
0x000055555540149e <+130>: add $0x18,%rsp | |||
0x00005555554014a2 <+134>: ret | |||
0x00005555554014a3 <+135>: call 0x5555554019e6 <explode_bomb> | |||
0x00005555554014a8 <+140>: jmp 0x555555401449 <phase_5+45> | |||
0x00005555554014aa <+142>: call 0x555555400e40 <__stack_chk_fail@plt> | |||
~~~ | |||
## 结论 | |||
密码为`5 115` | |||
# phase_6 | |||
## 读取数据 | |||
~~~python | |||
=> 0x00005555554014af <+0>: push %r14 | |||
0x00005555554014b1 <+2>: push %r13 | |||
0x00005555554014b3 <+4>: push %r12 | |||
0x00005555554014b5 <+6>: push %rbp | |||
0x00005555554014b6 <+7>: push %rbx | |||
0x00005555554014b7 <+8>: sub $0x60,%rsp | |||
0x00005555554014bb <+12>: mov %fs:0x28,%rax | |||
0x00005555554014c4 <+21>: mov %rax,0x58(%rsp) | |||
0x00005555554014c9 <+26>: xor %eax,%eax | |||
0x00005555554014cb <+28>: mov %rsp,%r13 | |||
0x00005555554014ce <+31>: mov %r13,%rsi | |||
0x00005555554014d1 <+34>: call 0x555555401a22 <read_six_numbers> | |||
0x00005555554014d6 <+39>: mov %r13,%r12 | |||
0x00005555554014d9 <+42>: mov $0x0,%r14d | |||
0x00005555554014df <+48>: jmp 0x555555401506 <phase_6+87> | |||
~~~ | |||
这段开辟了一个栈空间并且设置了一个金丝雀值 | |||
将%rsp赋予%r13,%rsi,%r12 | |||
将0x0赋予%r14d | |||
随后跳转到87行 | |||
## 第一个循环 | |||
~~~python | |||
0x00005555554014e1 <+50>: call 0x5555554019e6 <explode_bomb> | |||
0x00005555554014e6 <+55>: jmp 0x555555401515 <phase_6+102> | |||
0x00005555554014e8 <+57>: add $0x1,%ebx | |||
0x00005555554014eb <+60>: cmp $0x5,%ebx | |||
0x00005555554014ee <+63>: jg 0x555555401502 <phase_6+83> | |||
0x00005555554014f0 <+65>: movslq %ebx,%rax | |||
0x00005555554014f3 <+68>: mov (%rsp,%rax,4),%eax | |||
0x00005555554014f6 <+71>: cmp %eax,0x0(%rbp) | |||
0x00005555554014f9 <+74>: jne 0x5555554014e8 <phase_6+57> | |||
0x00005555554014fb <+76>: call 0x5555554019e6 <explode_bomb> | |||
0x0000555555401500 <+81>: jmp 0x5555554014e8 <phase_6+57> | |||
0x0000555555401502 <+83>: add $0x4,%r13 | |||
0x0000555555401506 <+87>: mov %r13,%rbp | |||
0x0000555555401509 <+90>: mov 0x0(%r13),%eax | |||
0x000055555540150d <+94>: sub $0x1,%eax | |||
0x0000555555401510 <+97>: cmp $0x5,%eax | |||
0x0000555555401513 <+100>: ja 0x5555554014e1 <phase_6+50> | |||
0x0000555555401515 <+102>: add $0x1,%r14d | |||
0x0000555555401519 <+106>: cmp $0x6,%r14d | |||
0x000055555540151d <+110>: je 0x555555401524 <phase_6+117> | |||
0x000055555540151f <+112>: mov %r14d,%ebx | |||
0x0000555555401522 <+115>: jmp 0x5555554014f0 <phase_6+65> | |||
~~~ | |||
分析可知,进入循环得初始处理是将%r13存储的%rsp地址赋给%rbp | |||
随后比较(%rsp)即第一个数字,减去1后与0x5比较,如果小于等于5才不会引爆 | |||
相当于每个输入的数据都必须小于等于6 | |||
并且由于是ja,故数字还必须-1后大于等于0,故数字大于等于1 | |||
%r14d初始值为0x0,每完成一次循环都+1,等于6的时候跳出,则循环执行6次,恰好遍历六个数字 | |||
%r14d未达到0x6时,会将当前值给%ebx,随后跳转到65行 | |||
执行等价于%eax等于第n个数字的操作 | |||
随后%eax与%rbp指向的数字比较,不相等则%ebx+1,继续判断%eax与%rbp指向的数字,直到%ebx大于5 | |||
随后%r13后移一位,继续进入最开始的循环 | |||
### 第一个循环结论 | |||
等效于数组各数字不能相等,并且都需要大于等于1,小于等于6 | |||
## 第二个与第三个循环 | |||
~~~python | |||
0x0000555555401524 <+117>: lea 0x18(%r12),%rcx | |||
0x0000555555401529 <+122>: mov $0x7,%edx | |||
0x000055555540152e <+127>: mov %edx,%eax | |||
0x0000555555401530 <+129>: sub (%r12),%eax | |||
0x0000555555401534 <+133>: mov %eax,(%r12) | |||
0x0000555555401538 <+137>: add $0x4,%r12 | |||
0x000055555540153c <+141>: cmp %r12,%rcx | |||
0x000055555540153f <+144>: jne 0x55555540152e <phase_6+127> | |||
0x0000555555401541 <+146>: mov $0x0,%esi | |||
0x0000555555401546 <+151>: jmp 0x555555401562 <phase_6+179> | |||
0x0000555555401548 <+153>: mov 0x8(%rdx),%rdx | |||
0x000055555540154c <+157>: add $0x1,%eax | |||
0x000055555540154f <+160>: cmp %ecx,%eax | |||
0x0000555555401551 <+162>: jne 0x555555401548 <phase_6+153> | |||
0x0000555555401553 <+164>: mov %rdx,0x20(%rsp,%rsi,8) | |||
0x0000555555401558 <+169>: add $0x1,%rsi | |||
0x000055555540155c <+173>: cmp $0x6,%rsi | |||
0x0000555555401560 <+177>: je 0x555555401578 <phase_6+201> | |||
0x0000555555401562 <+179>: mov (%rsp,%rsi,4),%ecx | |||
0x0000555555401565 <+182>: mov $0x1,%eax | |||
0x000055555540156a <+187>: lea 0x202cbf(%rip),%rdx # 0x555555604230 <node1> | |||
0x0000555555401571 <+194>: cmp $0x1,%ecx | |||
0x0000555555401574 <+197>: jg 0x555555401548 <phase_6+153> | |||
0x0000555555401576 <+199>: jmp 0x555555401553 <phase_6+164> | |||
~~~ | |||
跳出第一个循环后,进入第二个循环 | |||
%rcx被赋予等价为%rsp+0x18的地址,这个地址并不存放数组的数据 | |||
%edx被赋予0x7的值 | |||
令%eax = 7,随后7-(%rsp) | |||
%r12储存地址+4,等价%rsp+4 | |||
比较%r12与%rcx的地址,不一致则跳转到127行,127行继续进行上述操作 | |||
最终结果是%r12地址移动至%rsp+0x18,数组内每个数字都变为7-该数字,随后将%esi置0x0,跳转至179行 | |||
进入第三个循环 | |||
%ecx被赋予(%rsp)的值 | |||
%eax被置1 | |||
%rdx被赋予一个节点地址 | |||
并且判断%ecx的值是否大于1 | |||
如果是跳转到153行,(%rdx地址+0x8)赋给rdx,可以猜测,%rdx原先赋给的node1是个结构体,前八字节储存的是数据,后八字节储存的是地址 | |||
输入`x/128x 0x555555604230`即可查看 | |||
因此应该为一个链表,并且每次循环都向后移动一位 | |||
随后%eax+1,使得%ecx和%eax比较 | |||
循环,直至%ecx与%eax相等 | |||
如果不是大于1 | |||
跳转到164行 | |||
将节点地址赋给%rsp对应位置 | |||
最终效果为 | |||
数字为7-n对应第n个节点 | |||
节点地址在栈中存储顺序和数字输入顺序一致 | |||
完成后跳转到201行 | |||
## 第四个循环 | |||
~~~python | |||
0x0000555555401578 <+201>: mov 0x20(%rsp),%rbx | |||
0x000055555540157d <+206>: mov 0x28(%rsp),%rax | |||
0x0000555555401582 <+211>: mov %rax,0x8(%rbx) | |||
0x0000555555401586 <+215>: mov 0x30(%rsp),%rdx | |||
0x000055555540158b <+220>: mov %rdx,0x8(%rax) | |||
0x000055555540158f <+224>: mov 0x38(%rsp),%rax | |||
0x0000555555401594 <+229>: mov %rax,0x8(%rdx) | |||
0x0000555555401598 <+233>: mov 0x40(%rsp),%rdx | |||
0x000055555540159d <+238>: mov %rdx,0x8(%rax) | |||
0x00005555554015a1 <+242>: mov 0x48(%rsp),%rax | |||
0x00005555554015a6 <+247>: mov %rax,0x8(%rdx) | |||
0x00005555554015aa <+251>: movq $0x0,0x8(%rax) | |||
0x00005555554015b2 <+259>: mov $0x5,%ebp | |||
0x00005555554015b7 <+264>: jmp 0x5555554015c2 <phase_6+275> | |||
0x00005555554015b9 <+266>: mov 0x8(%rbx),%rbx | |||
0x00005555554015bd <+270>: sub $0x1,%ebp | |||
0x00005555554015c0 <+273>: je 0x5555554015d3 <phase_6+292> | |||
0x00005555554015c2 <+275>: mov 0x8(%rbx),%rax | |||
0x00005555554015c6 <+279>: mov (%rax),%eax | |||
0x00005555554015c8 <+281>: cmp %eax,(%rbx) | |||
0x00005555554015ca <+283>: jge 0x5555554015b9 <phase_6+266> | |||
0x00005555554015cc <+285>: call 0x5555554019e6 <explode_bomb> | |||
0x00005555554015d1 <+290>: jmp 0x5555554015b9 <phase_6+266> | |||
~~~ | |||
显然%rbx存储第一个数字对应的节点地址 | |||
%rax存储第二个数字对应的节点地址 | |||
0x8(%rbx)取出第一个数字对应节点储存的下一个节点的地址,这个地址被%rax中的地址取代 | |||
%rdx储存第三个数字对应的节点地址 | |||
0x8(%rax)取出第二个数字对应节点储存的下一个节点的地址,这个地址被%rdx中的地址取代 | |||
%rax被第四个数字对应的节点地址覆盖 | |||
0x8(%rdx)取出第三个数字对应节点储存的下一个节点的地址,这个地址被%rax中的地址取代,即第四个数字对应的节点地址 | |||
%rdx被第五个数字对应的节点地址覆盖 | |||
0x8(%rax)取出第四个数字对应下一个节点的地址,这个地址被%rdx中的地址取代,即第五个数字对应的节点地址 | |||
%rax被第六个数字对应节点的地址覆盖 | |||
0x8(%rdx)取出第五个数字对应下一个节点的地址,这个地址被%rdx中的地址取代,即第六个数字对应的节点地址 | |||
随后给%rax储存地址处赋予Null指针 | |||
综合结果即将原有链表顺序重新排列 | |||
效果为输入顺序为i,数值为k的一组数据 | |||
第7-k个节点被移动至第i位 | |||
然后置%ebp为5,跳转至275行 | |||
%rbx储存第一个节点的地址,赋予%rax第二个节点的地址 | |||
将第二个节点的数据赋给%eax | |||
比较第二个节点的数据b和第一个节点的数据a | |||
如果a>=b | |||
则继续循环,跳转到266行,%rbx移动至下一个节点,同时%ebp-1,并且如果%ebp = 1时,就跳出循环到292行 | |||
故进行了5次循环,每次效果都是判断相邻节点是否上一个的数据大于下一个 | |||
## 收尾 | |||
~~~python | |||
0x00005555554015d1 <+290>: jmp 0x5555554015b9 <phase_6+266> | |||
0x00005555554015d3 <+292>: mov 0x58(%rsp),%rax | |||
0x00005555554015d8 <+297>: xor %fs:0x28,%rax | |||
0x00005555554015e1 <+306>: jne 0x5555554015f0 <phase_6+321> | |||
0x00005555554015e3 <+308>: add $0x60,%rsp | |||
0x00005555554015e7 <+312>: pop %rbx | |||
0x00005555554015e8 <+313>: pop %rbp | |||
0x00005555554015e9 <+314>: pop %r12 | |||
0x00005555554015eb <+316>: pop %r13 | |||
0x00005555554015ed <+318>: pop %r14 | |||
0x00005555554015ef <+320>: ret | |||
0x00005555554015f0 <+321>: call 0x555555400e40 <__stack_chk_fail@plt> | |||
~~~ | |||
简单的探测金丝雀值变化并返回 | |||
## 安排密码 | |||
输入`x/128x 0x555555604230`查看各个节点 | |||
~~~python | |||
0x555555604230 <node1>: 0x3b 0x03 0x00 0x00 0x01 0x00 0x00 0x00 | |||
0x555555604238 <node1+8>: 0x40 0x42 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604240 <node2>: 0x1c 0x02 0x00 0x00 0x02 0x00 0x00 0x00 | |||
0x555555604248 <node2+8>: 0x50 0x42 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604250 <node3>: 0x0f 0x02 0x00 0x00 0x03 0x00 0x00 0x00 | |||
0x555555604258 <node3+8>: 0x60 0x42 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604260 <node4>: 0xea 0x00 0x00 0x00 0x04 0x00 0x00 0x00 | |||
0x555555604268 <node4+8>: 0x70 0x42 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604270 <node5>: 0x83 0x00 0x00 0x00 0x05 0x00 0x00 0x00 | |||
0x555555604278 <node5+8>: 0x10 0x41 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604280 <host_table>: 0x4f 0x2d 0x40 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604288 <host_table+8>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x555555604290 <host_table+16>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x555555604298 <host_table+24>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x5555556042a0 <host_table+32>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x5555556042a8 <host_table+40>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
~~~ | |||
node1的数据是0x0000033b | |||
node2的数据是0x0000021c | |||
node3的数据是0x0000020f | |||
node4的数据时0x000000ea | |||
node5的数据是0x00000083 | |||
注意到node6储存在node5的地址处,和这五个node不是连续储存 | |||
输入`x/64x 0x555555604110` | |||
node6的数据是0x000001e2 | |||
按照需要的,相邻数据成降序 | |||
则是 node1 node2 node3 node6 node4 node5 | |||
对应为 1 2 3 6 4 5 | |||
但是是用7-n得到的 | |||
所以实际顺序是 6 5 4 1 3 2 | |||
# secret_phase | |||
查询资料得其在phase_defused中被调用 | |||
## 查看phase_defused汇编 | |||
~~~python | |||
=> 0x0000555555401ba7 <+0>: sub $0x78,%rsp | |||
0x0000555555401bab <+4>: mov %fs:0x28,%rax | |||
0x0000555555401bb4 <+13>: mov %rax,0x68(%rsp) | |||
0x0000555555401bb9 <+18>: xor %eax,%eax | |||
0x0000555555401bbb <+20>: mov $0x1,%edi | |||
0x0000555555401bc0 <+25>: call 0x5555554018c2 <send_msg> | |||
0x0000555555401bc5 <+30>: cmpl $0x6,0x202ae0(%rip) # 0x5555556046ac <num_input_strings> | |||
0x0000555555401bcc <+37>: je 0x555555401be7 <phase_defused+64> | |||
0x0000555555401bce <+39>: mov 0x68(%rsp),%rax | |||
0x0000555555401bd3 <+44>: xor %fs:0x28,%rax | |||
0x0000555555401bdc <+53>: jne 0x555555401c66 <phase_defused+191> | |||
0x0000555555401be2 <+59>: add $0x78,%rsp | |||
0x0000555555401be6 <+63>: ret | |||
0x0000555555401be7 <+64>: lea 0xc(%rsp),%rcx | |||
0x0000555555401bec <+69>: lea 0x8(%rsp),%rdx | |||
0x0000555555401bf1 <+74>: lea 0x10(%rsp),%r8 | |||
0x0000555555401bf6 <+79>: lea 0x1142(%rip),%rsi # 0x555555402d3f | |||
0x0000555555401bfd <+86>: lea 0x202bac(%rip),%rdi # 0x5555556047b0 <input_strings+240> | |||
0x0000555555401c04 <+93>: mov $0x0,%eax | |||
0x0000555555401c09 <+98>: call 0x555555400ee0 <__isoc99_sscanf@plt> | |||
0x0000555555401c0e <+103>: cmp $0x3,%eax | |||
0x0000555555401c11 <+106>: je 0x555555401c2d <phase_defused+134> | |||
0x0000555555401c13 <+108>: lea 0xfe6(%rip),%rdi # 0x555555402c00 | |||
0x0000555555401c1a <+115>: call 0x555555400e20 <puts@plt> | |||
0x0000555555401c1f <+120>: lea 0x100a(%rip),%rdi # 0x555555402c30 | |||
0x0000555555401c26 <+127>: call 0x555555400e20 <puts@plt> | |||
0x0000555555401c2b <+132>: jmp 0x555555401bce <phase_defused+39> | |||
0x0000555555401c2d <+134>: lea 0x10(%rsp),%rdi | |||
0x0000555555401c32 <+139>: lea 0x110f(%rip),%rsi # 0x555555402d48 | |||
0x0000555555401c39 <+146>: call 0x55555540172b <strings_not_equal> | |||
0x0000555555401c3e <+151>: test %eax,%eax | |||
0x0000555555401c40 <+153>: jne 0x555555401c13 <phase_defused+108> | |||
0x0000555555401c42 <+155>: lea 0xf57(%rip),%rdi # 0x555555402ba0 | |||
0x0000555555401c49 <+162>: call 0x555555400e20 <puts@plt> | |||
0x0000555555401c4e <+167>: lea 0xf73(%rip),%rdi # 0x555555402bc8 | |||
0x0000555555401c55 <+174>: call 0x555555400e20 <puts@plt> | |||
0x0000555555401c5a <+179>: mov $0x0,%eax | |||
0x0000555555401c5f <+184>: call 0x555555401634 <secret_phase> | |||
0x0000555555401c64 <+189>: jmp 0x555555401c13 <phase_defused+108> | |||
0x0000555555401c66 <+191>: call 0x555555400e40 <__stack_chk_fail@plt> | |||
~~~ | |||
直奔`secret_phase`,向上寻找跳转,发现只有当`test %eax %eax`设置`ZF=1`时才能执行到`secret_phase` | |||
而%eax的值由`strings_not_equal`决定,从phase_1中可知,`strings_not_equal`使用的用来当“标准”的字符串储存在寄存器%rsi的地址指向的地方 | |||
而上一步%rsi储存地址为`0x555555402d48` | |||
输入`x/s 0x555555402d48`显示为`DrEvil` | |||
下一步是寻找触发条件 | |||
继续向上看,发现<+93> <+98> <+103> <+106>行出现 | |||
~~~python | |||
0x0000555555401c04 <+93>: mov $0x0,%eax | |||
0x0000555555401c09 <+98>: call 0x555555400ee0 <__isoc99_sscanf@plt> | |||
0x0000555555401c0e <+103>: cmp $0x3,%eax | |||
0x0000555555401c11 <+106>: je 0x555555401c2d <phase_defused+134> | |||
~~~ | |||
即判断sscanf函数接收的字符串是否为3个,是才能跳转到`strings_not_equal` | |||
继续向上看,能跳转到`__isoc99_sscanf@plt`部分,而非ret退出,需要一个jmp指令 | |||
~~~python | |||
0x0000555555401bc5 <+30>: cmpl $0x6,0x202ae0(%rip) # 0x5555556046ac <num_input_strings> | |||
~~~ | |||
这段意思为完成六个phase,先决条件 | |||
只能在`__isoc99_sscanf@plt`上面和`ret`下面寻找,即 | |||
~~~python | |||
0x0000555555401be7 <+64>: lea 0xc(%rsp),%rcx | |||
0x0000555555401bec <+69>: lea 0x8(%rsp),%rdx | |||
0x0000555555401bf1 <+74>: lea 0x10(%rsp),%r8 | |||
0x0000555555401bf6 <+79>: lea 0x1142(%rip),%rsi # 0x555555402d3f | |||
0x0000555555401bfd <+86>: lea 0x202bac(%rip),%rdi # 0x5555556047b0 <input_strings+240> | |||
~~~ | |||
输入`x/s 0x555555402d3f`得`"%d %d %s"`,结合之前拆弹经历,这是表示sscanf字符串的输入顺序 | |||
输入`x/s 0x5555556047b0`得`352 4` | |||
显然为phase_4触发 | |||
故在phase_4输入时改为`352 4 DrEvil` | |||
## 正式拆弹 | |||
### 查看secret_phase汇编 | |||
~~~python | |||
=> 0x0000555555401634 <+0>: push %rbx | |||
0x0000555555401635 <+1>: call 0x555555401a63 <read_line> | |||
0x000055555540163a <+6>: mov $0xa,%edx | |||
0x000055555540163f <+11>: mov $0x0,%esi | |||
0x0000555555401644 <+16>: mov %rax,%rdi | |||
0x0000555555401647 <+19>: call 0x555555400ec0 <strtol@plt> | |||
0x000055555540164c <+24>: mov %rax,%rbx | |||
0x000055555540164f <+27>: lea -0x1(%rax),%eax | |||
0x0000555555401652 <+30>: cmp $0x3e8,%eax | |||
0x0000555555401657 <+35>: ja 0x555555401684 <secret_phase+80> | |||
0x0000555555401659 <+37>: mov %ebx,%esi | |||
0x000055555540165b <+39>: lea 0x202aee(%rip),%rdi # 0x555555604150 <n1> | |||
0x0000555555401662 <+46>: call 0x5555554015f5 <fun7> | |||
0x0000555555401667 <+51>: cmp $0x2,%eax | |||
0x000055555540166a <+54>: je 0x555555401671 <secret_phase+61> | |||
0x000055555540166c <+56>: call 0x5555554019e6 <explode_bomb> | |||
0x0000555555401671 <+61>: lea 0x13d0(%rip),%rdi # 0x555555402a48 | |||
0x0000555555401678 <+68>: call 0x555555400e20 <puts@plt> | |||
0x000055555540167d <+73>: call 0x555555401ba7 <phase_defused> | |||
0x0000555555401682 <+78>: pop %rbx | |||
0x0000555555401683 <+79>: ret | |||
0x0000555555401684 <+80>: call 0x5555554019e6 <explode_bomb> | |||
0x0000555555401689 <+85>: jmp 0x555555401659 <secret_phase+37> | |||
~~~ | |||
strtol函数将字符串里的10进制整数提取出来 | |||
其返回值为对应的整数 | |||
将返回值减1后于0x3e8比较,如果小于等于就不引爆炸弹 | |||
随后令%esi也等于%ebx,即%rax最开始返回的整数 | |||
然后给%rdi赋给一个地址,从注释名称来看也为一个节点 | |||
随后进入`fun7`函数 | |||
查看`fun7`函数前先看看节点的结构 | |||
输入`x/256x 0x555555604150` | |||
~~~python | |||
0x555555604150 <n1>: 0x24 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x555555604158 <n1+8>: 0x70 0x41 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604160 <n1+16>: 0x90 0x41 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604168: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x555555604170 <n21>: 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x555555604178 <n21+8>: 0xf0 0x41 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604180 <n21+16>: 0xb0 0x41 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604188: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x555555604190 <n22>: 0x32 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x555555604198 <n22+8>: 0xd0 0x41 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x5555556041a0 <n22+16>: 0x10 0x42 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x5555556041a8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x5555556041b0 <n32>: 0x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x5555556041b8 <n32+8>: 0xb0 0x40 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x5555556041c0 <n32+16>: 0x70 0x40 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x5555556041c8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x5555556041d0 <n33>: 0x2d 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x5555556041d8 <n33+8>: 0x10 0x40 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x5555556041e0 <n33+16>: 0xd0 0x40 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x5555556041e8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x5555556041f0 <n31>: 0x06 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x5555556041f8 <n31+8>: 0x30 0x40 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604200 <n31+16>: 0x90 0x40 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604208: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x555555604210 <n34>: 0x6b 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
0x555555604218 <n34+8>: 0x50 0x40 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604220 <n34+16>: 0xf0 0x40 0x60 0x55 0x55 0x55 0x00 0x00 | |||
0x555555604228: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | |||
~~~ | |||
显然这是一棵二叉树 | |||
前8字节储存数据 | |||
中8字节储存左子节点地址 | |||
后8字节储存右子节点地址 | |||
则%rdi的地址实际上是二叉树的根的地址 | |||
## 查看fun7函数汇编 | |||
~~~python | |||
=> 0x00005555554015f5 <+0>: test %rdi,%rdi | |||
0x00005555554015f8 <+3>: je 0x55555540162e <fun7+57> | |||
0x00005555554015fa <+5>: sub $0x8,%rsp | |||
0x00005555554015fe <+9>: mov (%rdi),%edx | |||
0x0000555555401600 <+11>: cmp %esi,%edx | |||
0x0000555555401602 <+13>: jg 0x555555401612 <fun7+29> | |||
0x0000555555401604 <+15>: mov $0x0,%eax | |||
0x0000555555401609 <+20>: cmp %esi,%edx | |||
0x000055555540160b <+22>: jne 0x55555540161f <fun7+42> | |||
0x000055555540160d <+24>: add $0x8,%rsp | |||
0x0000555555401611 <+28>: ret | |||
0x0000555555401612 <+29>: mov 0x8(%rdi),%rdi | |||
0x0000555555401616 <+33>: call 0x5555554015f5 <fun7> | |||
0x000055555540161b <+38>: add %eax,%eax | |||
0x000055555540161d <+40>: jmp 0x55555540160d <fun7+24> | |||
0x000055555540161f <+42>: mov 0x10(%rdi),%rdi | |||
0x0000555555401623 <+46>: call 0x5555554015f5 <fun7> | |||
0x0000555555401628 <+51>: lea 0x1(%rax,%rax,1),%eax | |||
0x000055555540162c <+55>: jmp 0x55555540160d <fun7+24> | |||
0x000055555540162e <+57>: mov $0xffffffff,%eax | |||
0x0000555555401633 <+62>: ret | |||
~~~ | |||
<+0> <+3>行判断%rdi是否等于0,是的话就跳转至<+57>行,让%eax等于-1并返回,不是则继续 | |||
<+5> 行压栈 | |||
<+9> 行读取根节点数据,赋给%edx | |||
<+11> <+13>行比较%esi和%edx,%esi即提取的整数,如果%edx大于%esi,则跳转至<+29>行 | |||
看看<+29>行结果 | |||
<+29> 行即读取左子节点数据并赋给%rdi自身 | |||
<+33> 行调用自身`fun7`函数 | |||
<+38> 行如果`fun7`函数执行完毕,令%eax*2 | |||
随后jmp至<+24>行,弹出栈并返回%eax | |||
如果%edx小于等于%esi | |||
<+15> 行令%eax为0 | |||
<+20> 行比较%esi %edx是否相等,相等则让%rsp+8,弹出栈,并返回,不相等则跳转到<+42行> | |||
顺着分支 | |||
<+42> 行即读取右子节点数据并赋给%rdi自身 | |||
<+46> 行调用自身`fun7`函数 | |||
<+51> 行如果`fun7`函数执行完毕,令%eax = 2*%eax + 1 | |||
随后jmp至<+24>行,弹出栈并返回%eax | |||
整体运行效果是提取出的整数%esi | |||
大于该节点数据,则向左子节点找 | |||
等于就弹出 | |||
小于该节点数据,就向右子节点找 | |||
并且如果节点数据有0立刻返回-1 | |||
## 回看secret_phase汇编 | |||
~~~python | |||
0x0000555555401662 <+46>: call 0x5555554015f5 <fun7> | |||
0x0000555555401667 <+51>: cmp $0x2,%eax | |||
0x000055555540166a <+54>: je 0x555555401671 <secret_phase+61> | |||
0x000055555540166c <+56>: call 0x5555554019e6 <explode_bomb> | |||
0x0000555555401671 <+61>: lea 0x13d0(%rip),%rdi # 0x555555402a48 | |||
0x0000555555401678 <+68>: call 0x555555400e20 <puts@plt> | |||
0x000055555540167d <+73>: call 0x555555401ba7 <phase_defused> | |||
0x0000555555401682 <+78>: pop %rbx | |||
0x0000555555401683 <+79>: ret | |||
0x0000555555401684 <+80>: call 0x5555554019e6 <explode_bomb> | |||
0x0000555555401689 <+85>: jmp 0x555555401659 <secret_phase+37> | |||
~~~ | |||
如果`fun7`最终返回数据为2就收尾并解除炸弹 | |||
如果不是就引爆炸弹 | |||
## 逆推数据 | |||
### 什么样才能返回2 | |||
显然,改变%rax只有四种方式 | |||
1. 节点储存数据为0,返回-1 | |||
2. 节点右子节点等于该输入数据,返回2*%eax+1 | |||
3. 节点左子节点等于该输入数据,返回2*%eax | |||
4. 当节点数据小于等于输入数据时,令%eax = 0 | |||
故组合一下,有 | |||
1. 最后一次搜索: 当节点数据小于等于输入数据时,令%eax = 0,此时恰好相等,返回,完成一次搜索 | |||
2. 倒数第二次搜索: 最后一次搜索由右子节点搜索进入,返回的是%eax = 0,随后返回2*%eax+1 = 1 | |||
3. 倒数第三次搜索: 倒数第二次搜索由左子节点搜索进入,返回的是%eax = 1,随后返回2*%eax = 2 | |||
4. 进入左子节点搜索是因为父节点数据大于用户输入的数据 | |||
推测进入方式为 n1->n21->n32 | |||
n32数据为0x16,转换成10进制为22 | |||
可以推测用户输入字段包括22即可 | |||
### 总结 | |||
输入22+string即可,如22nn |
@ -1,35 +0,0 @@ | |||
### All | |||
A CSAPP LAB | |||
### 2023/10/7 Update: | |||
1. Create A Repo | |||
2. Finish bitXor | |||
3. Finish tmin | |||
### 2023/10/8 Update: | |||
1. Finish isTmax | |||
2. Finish allOddBits | |||
3. Finish negate | |||
4. Finish isAsciiDigit | |||
5. Finish isLessOrEqual | |||
6. Finish logicalNeg | |||
7. Update Makefile | |||
### 2023/10/9 Update: | |||
1. Finish howManyBits | |||
### 2023/10/9 Update: | |||
1. Finish floatScale2 | |||
2. Finish floatFloat2Int | |||
3. Congratulations!Finish datalab! |
@ -0,0 +1,115 @@ | |||
/*************************************************************************** | |||
* Dr. Evil's Insidious Bomb, Version 1.1 | |||
* Copyright 2011, Dr. Evil Incorporated. All rights reserved. | |||
* | |||
* LICENSE: | |||
* | |||
* Dr. Evil Incorporated (the PERPETRATOR) hereby grants you (the | |||
* VICTIM) explicit permission to use this bomb (the BOMB). This is a | |||
* time limited license, which expires on the death of the VICTIM. | |||
* The PERPETRATOR takes no responsibility for damage, frustration, | |||
* insanity, bug-eyes, carpal-tunnel syndrome, loss of sleep, or other | |||
* harm to the VICTIM. Unless the PERPETRATOR wants to take credit, | |||
* that is. The VICTIM may not distribute this bomb source code to | |||
* any enemies of the PERPETRATOR. No VICTIM may debug, | |||
* reverse-engineer, run "strings" on, decompile, decrypt, or use any | |||
* other technique to gain knowledge of and defuse the BOMB. BOMB | |||
* proof clothing may not be worn when handling this program. The | |||
* PERPETRATOR will not apologize for the PERPETRATOR's poor sense of | |||
* humor. This license is null and void where the BOMB is prohibited | |||
* by law. | |||
***************************************************************************/ | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include "support.h" | |||
#include "phases.h" | |||
/* | |||
* Note to self: Remember to erase this file so my victims will have no | |||
* idea what is going on, and so they will all blow up in a | |||
* spectaculary fiendish explosion. -- Dr. Evil | |||
*/ | |||
FILE *infile; | |||
int main(int argc, char *argv[]) | |||
{ | |||
char *input; | |||
/* Note to self: remember to port this bomb to Windows and put a | |||
* fantastic GUI on it. */ | |||
/* When run with no arguments, the bomb reads its input lines | |||
* from standard input. */ | |||
if (argc == 1) { | |||
infile = stdin; | |||
} | |||
/* When run with one argument <file>, the bomb reads from <file> | |||
* until EOF, and then switches to standard input. Thus, as you | |||
* defuse each phase, you can add its defusing string to <file> and | |||
* avoid having to retype it. */ | |||
else if (argc == 2) { | |||
if (!(infile = fopen(argv[1], "r"))) { | |||
printf("%s: Error: Couldn't open %s\n", argv[0], argv[1]); | |||
exit(8); | |||
} | |||
} | |||
/* You can't call the bomb with more than 1 command line argument. */ | |||
else { | |||
printf("Usage: %s [<input_file>]\n", argv[0]); | |||
exit(8); | |||
} | |||
/* Do all sorts of secret stuff that makes the bomb harder to defuse. */ | |||
initialize_bomb(); | |||
printf("Welcome to my fiendish little bomb. You have 6 phases with\n"); | |||
printf("which to blow yourself up. Have a nice day!\n"); | |||
/* Hmm... Six phases must be more secure than one phase! */ | |||
input = read_line(); /* Get input */ | |||
phase_1(input); /* Run the phase */ | |||
phase_defused(); /* Drat! They figured it out! | |||
* Let me know how they did it. */ | |||
printf("Phase 1 defused. How about the next one?\n"); | |||
/* The second phase is harder. No one will ever figure out | |||
* how to defuse this... */ | |||
input = read_line(); | |||
phase_2(input); | |||
phase_defused(); | |||
printf("That's number 2. Keep going!\n"); | |||
/* I guess this is too easy so far. Some more complex code will | |||
* confuse people. */ | |||
input = read_line(); | |||
phase_3(input); | |||
phase_defused(); | |||
printf("Halfway there!\n"); | |||
/* Oh yeah? Well, how good is your math? Try on this saucy problem! */ | |||
input = read_line(); | |||
phase_4(input); | |||
phase_defused(); | |||
printf("So you got that one. Try this one.\n"); | |||
/* Round and 'round in memory we go, where we stop, the bomb blows! */ | |||
input = read_line(); | |||
phase_5(input); | |||
phase_defused(); | |||
printf("Good work! On to the next...\n"); | |||
/* This phase will never be used, since no one will get past the | |||
* earlier ones. But just in case, make this one extra hard. */ | |||
input = read_line(); | |||
phase_6(input); | |||
phase_defused(); | |||
/* Wow, they got it! But isn't something... missing? Perhaps | |||
* something they overlooked? Mua ha ha ha ha! */ | |||
return 0; | |||
} |
@ -0,0 +1,7 @@ | |||
I am not part of the problem. I am a Republican. | |||
0 1 1 2 3 5 | |||
5 -354info | |||
352 4 DrEvil | |||
5 115 | |||
6 5 4 1 3 2 | |||
22nn |