Compare commits

...

2 Commits

Author SHA1 Message Date
tqcq
2ea67d6174 feat: add log 2024-10-21 01:52:08 +00:00
tqcq
c7227f88b6 feat: format code 2024-10-21 01:47:43 +00:00
7 changed files with 588 additions and 558 deletions

78
.clang-format Normal file
View File

@ -0,0 +1,78 @@
# Generated from CLion C/C++ Code Style settings
BinPackParameters: false
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignArrayOfStructures: Left
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments:
Enabled: true
AcrossEmptyLines: true
AcrossComments: false
AlignOperands: DontAlign
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: true
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: Yes
# 函数和返回类型分两行,方便阅读
AlwaysBreakAfterReturnType: TopLevelDefinitions
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: true
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
ConstructorInitializerAllOnOneLineOrOnePerLine: true
BreakInheritanceList: BeforeColon
ColumnLimit: 120
CompactNamespaces: false
ContinuationIndentWidth: 4
EmptyLineBeforeAccessModifier: LogicalBlock
SeparateDefinitionBlocks: Always
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: false
PointerAlignment: Right
ReflowComments: false
SortIncludes: CaseSensitive
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 0
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 4
UseTab: Never
PenaltyIndentedWhitespace: 1

View File

@ -4,7 +4,7 @@ LDIR=lib
BUILD=obj BUILD=obj
ODIR=src/.obj ODIR=src/.obj
CFLAGS=-I$(IDIR) #-ggdb CFLAGS=-I$(IDIR) -O3 #-ggdb
# CXX=g++ # CXX=g++
LDFLAGS= LDFLAGS=

View File

@ -1,14 +1,12 @@
#include "utils.h"
#include "unistd.h" #include "unistd.h"
#include "utils.h"
typedef struct { typedef struct {
char* v_addr; char *v_addr;
uint64_t p_addr; uint64_t p_addr;
} addr_tuple; } addr_tuple;
//---------------------------------------------------------- //----------------------------------------------------------
// Functions // Functions
void rev_mc(size_t sets_cnt, size_t threshold, size_t rounds, size_t m_size, char *o_file, uint64_t flags);
void rev_mc(size_t sets_cnt, size_t threshold, size_t rounds, size_t m_size, char* o_file, uint64_t flags);

View File

@ -3,93 +3,80 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#define BIT(x) (1ULL << (x))
#define BIT(x) (1ULL<<(x)) #define KB(x) ((x) << 10ULL)
#define KB(x) ((x)<<10ULL) #define MB(x) ((x) << 20ULL)
#define MB(x) ((x)<<20ULL) #define GB(x) ((x) << 30ULL)
#define GB(x) ((x)<<30ULL)
#define CL_SHIFT 6 #define CL_SHIFT 6
#define CL_SIZE 64 #define CL_SIZE 64
#define F_CLEAR 0L
#define F_VERBOSE BIT(0)
#define F_EXPORT BIT(1)
#define F_CLEAR 0L #define MEM_SHIFT (30L)
#define F_VERBOSE BIT(0) #define MEM_MASK 0b11111ULL << MEM_SHIFT
#define F_EXPORT BIT(1) #define F_ALLOC_HUGE BIT(MEM_SHIFT)
#define F_ALLOC_HUGE_1G F_ALLOC_HUGE | BIT(MEM_SHIFT + 1)
#define MEM_SHIFT (30L) #define F_ALLOC_HUGE_2M F_ALLOC_HUGE | BIT(MEM_SHIFT + 2)
#define MEM_MASK 0b11111ULL << MEM_SHIFT #define F_POPULATE BIT(MEM_SHIFT + 3)
#define F_ALLOC_HUGE BIT(MEM_SHIFT)
#define F_ALLOC_HUGE_1G F_ALLOC_HUGE | BIT(MEM_SHIFT+1)
#define F_ALLOC_HUGE_2M F_ALLOC_HUGE | BIT(MEM_SHIFT+2)
#define F_POPULATE BIT(MEM_SHIFT+3)
//---------------------------------------------------------- //----------------------------------------------------------
// Static functions // Static functions
static inline __attribute__((always_inline)) void clflush(volatile void *p) static inline __attribute__((always_inline)) void
clflush(volatile void *p)
{ {
asm volatile("clflush (%0)\n" asm volatile("clflush (%0)\n" ::"r"(p) : "memory");
:: "r" (p) : "memory");
} }
static inline __attribute__((always_inline)) void
static inline __attribute__((always_inline)) void mfence() mfence()
{ {
asm volatile ("mfence" : : : "memory"); asm volatile("mfence" : : : "memory");
} }
static inline __attribute__((always_inline)) void
static inline __attribute__((always_inline)) void lfence() lfence()
{ {
asm volatile ("lfence" : : : "memory"); asm volatile("lfence" : : : "memory");
} }
static inline __attribute__((always_inline)) uint64_t
static inline __attribute__((always_inline)) uint64_t rdtscp(void) rdtscp(void)
{ {
uint64_t lo, hi; uint64_t lo, hi;
asm volatile("rdtscp\n" asm volatile("rdtscp\n" : "=a"(lo), "=d"(hi)::"%rcx");
: "=a" (lo), "=d" (hi) return (hi << 32) | lo;
:: "%rcx");
return (hi << 32) | lo;
} }
static inline __attribute__((always_inline)) uint64_t
static inline __attribute__((always_inline)) uint64_t rdtsc(void) rdtsc(void)
{ {
uint64_t lo, hi; uint64_t lo, hi;
asm volatile("rdtsc\n" asm volatile("rdtsc\n" : "=a"(lo), "=d"(hi)::"%rcx");
: "=a" (lo), "=d" (hi) return (hi << 32) | lo;
:: "%rcx");
return (hi << 32) | lo;
} }
//---------------------------------------------------------- //----------------------------------------------------------
// Memory alloc // Memory alloc
typedef struct { typedef struct {
char* buffer; char *buffer;
uint64_t size; uint64_t size;
uint64_t flags; uint64_t flags;
} mem_buff_t; } mem_buff_t;
int alloc_buffer(mem_buff_t *mem);
int alloc_buffer(mem_buff_t* mem); int free_buffer(mem_buff_t *mem);
int free_buffer(mem_buff_t* mem);
//---------------------------------------------------------- //----------------------------------------------------------
// Helpers // Helpers
int gt(const void * a, const void * b); int gt(const void *a, const void *b);
double mean(uint64_t* vals, size_t size); double mean(uint64_t *vals, size_t size);
uint64_t median(uint64_t* vals, size_t size); uint64_t median(uint64_t *vals, size_t size);
char* bit_string(uint64_t val);
char *bit_string(uint64_t val);

View File

@ -1,134 +1,119 @@
#include "stdio.h" #include "stdio.h"
// #include <x86intrin.h> /* for rdtsc, rdtscp, clflush */ // #include <x86intrin.h> /* for rdtsc, rdtscp, clflush */
#include <stdint.h>
#include <assert.h> #include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sched.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdbool.h>
#include <getopt.h> #include <getopt.h>
#include <sched.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "utils.h"
#include "rev-mc.h" #include "rev-mc.h"
#include "utils.h"
#define SETS_std (2 * 16)// 1rk-1ch
#define SETS_std (2*16) // 1rk-1ch #define ROUNDS_std 1000
#define ROUNDS_std 1000 #define THRESHOLD_std 340
#define THRESHOLD_std 340 #define MEM_SIZE_std GB(5L)
#define MEM_SIZE_std GB(5L) #define O_FILE_std "access.csv"
#define O_FILE_std "access.csv"
#define FIELDS "base,probe,rounds,time" #define FIELDS "base,probe,rounds,time"
//----------------------------------------------- //-----------------------------------------------
// GLOBALS // GLOBALS
//----------------------------------------------- //-----------------------------------------------
void print_usage() { void
fprintf(stderr, "[ LOG ] - Usage ./test [-h] [-s sets] [-r rounds] [-t threshold] [-o o_file] [-v] [--mem mem_size]\n"); print_usage()
{
fprintf(stderr,
"[ LOG ] - Usage ./test [-h] [-s sets] [-r rounds] [-t threshold] [-o o_file] [-v] [--mem mem_size]\n");
fprintf(stderr, " -h = this help message\n"); fprintf(stderr, " -h = this help message\n");
fprintf(stderr, " -s sets = number of expected sets (default: %d)\n", SETS_std); fprintf(stderr, " -s sets = number of expected sets (default: %d)\n", SETS_std);
fprintf(stderr, " -r rounds = number of rounds per tuple (default: %d)\n", ROUNDS_std); fprintf(stderr, " -r rounds = number of rounds per tuple (default: %d)\n",
fprintf(stderr, " -t threshold = time threshold for conflicts (default: %d)\n", THRESHOLD_std); ROUNDS_std);
fprintf(stderr, " -o o_file = output file for mem profiling (default: %s)\n", O_FILE_std); fprintf(stderr, " -t threshold = time threshold for conflicts (default: %d)\n",
fprintf(stderr, " --mem mem_size = allocation size (default: %ld)\n", (uint64_t) MEM_SIZE_std); THRESHOLD_std);
fprintf(stderr, " -o o_file = output file for mem profiling (default: %s)\n",
O_FILE_std);
fprintf(stderr, " --mem mem_size = allocation size (default: %ld)\n",
(uint64_t) MEM_SIZE_std);
fprintf(stderr, " -v = verbose\n\n"); fprintf(stderr, " -v = verbose\n\n");
} }
//----------------------------------------------- //-----------------------------------------------
int main(int argc, char** argv) { int
main(int argc, char **argv)
{
uint64_t flags = 0ULL; uint64_t flags = 0ULL;
size_t sets_cnt = SETS_std; size_t sets_cnt = SETS_std;
size_t rounds = ROUNDS_std; size_t rounds = ROUNDS_std;
size_t m_size = MEM_SIZE_std; size_t m_size = MEM_SIZE_std;
size_t threshold = THRESHOLD_std; size_t threshold = THRESHOLD_std;
char* o_file = (char*) O_FILE_std; char *o_file = (char *) O_FILE_std;
flags |= F_POPULATE; flags |= F_POPULATE;
if(geteuid() != 0) { if (geteuid() != 0) {
fprintf(stderr, "[ERROR] - You need to run as root to access pagemap!\n"); fprintf(stderr, "[ERROR] - You need to run as root to access pagemap!\n");
exit(1); exit(1);
} }
while (1) { while (1) {
int this_option_optind = optind ? optind : 1; int this_option_optind = optind ? optind : 1;
int option_index = 0; int option_index = 0;
static struct option long_options[] = static struct option long_options[] = {
{ /* These options set a flag. */
/* These options set a flag. */ {"mem", required_argument, 0, 0},
{"mem", required_argument, 0, 0}, {0, 0, 0, 0}
{0, 0, 0, 0} };
}; int arg = getopt_long(argc, argv, "o:s:r:t:hv", long_options, &option_index);
int arg = getopt_long (argc, argv, "o:s:r:t:hv",
long_options, &option_index);
if (arg == -1) if (arg == -1) break;
break;
switch(arg) { switch (arg) {
case 0:
switch (option_index) {
case 0: case 0:
switch (option_index){ m_size = atoi(optarg);// TODO proper parsing of this
case 0:
m_size = atoi(optarg); // TODO proper parsing of this
break;
default:
break;
}
break; break;
case 'o':
o_file = (char*) malloc(sizeof(char)*strlen(optarg));
strncpy(o_file, optarg, strlen(optarg));
flags |= F_EXPORT;
break;
case 's':
sets_cnt = atoi(optarg);
break;
case 'r':
rounds = atoi(optarg);
break;
case 't':
threshold = atoi(optarg);
break;
case 'v':
flags |= F_VERBOSE;
break;
case 'h':
default: default:
print_usage(); break;
return 0; }
break;
case 'o':
o_file = (char *) malloc(sizeof(char) * strlen(optarg));
strncpy(o_file, optarg, strlen(optarg));
flags |= F_EXPORT;
break;
case 's':
sets_cnt = atoi(optarg);
break;
case 'r':
rounds = atoi(optarg);
break;
case 't':
threshold = atoi(optarg);
break;
case 'v':
flags |= F_VERBOSE;
break;
case 'h':
default:
print_usage();
return 0;
} }
} }
rev_mc(sets_cnt, threshold, rounds, m_size, o_file, flags); rev_mc(sets_cnt, threshold, rounds, m_size, o_file, flags);
return 0; return 0;
} }
/*I'm refactoring the set struct to add also timing in there. */ /*I'm refactoring the set struct to add also timing in there. */

View File

@ -19,24 +19,21 @@
#define BOOL_XOR(a, b) ((a) != (b)) #define BOOL_XOR(a, b) ((a) != (b))
#define O_HEADER "base,probe,time\n" #define O_HEADER "base,probe,time\n"
#define ALIGN_TO(X, Y) \ #define ALIGN_TO(X, Y) ((X) & (~((1LL << (Y)) - 1LL)))// Mask out the lower Y bits
((X) & (~((1LL << (Y)) - 1LL))) // Mask out the lower Y bits #define LS_BITMASK(X) ((1LL << (X)) - 1LL) // Mask only the lower X bits
#define LS_BITMASK(X) ((1LL << (X)) - 1LL) // Mask only the lower X bits
#define SET_SIZE 40 // elements per set #define SET_SIZE 40// elements per set
#define VALID_THRESH 0.75f #define VALID_THRESH 0.75f
#define SET_THRESH 0.95f #define SET_THRESH 0.95f
#define BITSET_SIZE 256 // bitset used to exploit bitwise operations #define BITSET_SIZE 256// bitset used to exploit bitwise operations
#define ROW_SET_CNT 5 #define ROW_SET_CNT 5
// from // from
// https://stackoverflow.com/questions/1644868/define-macro-for-debug-printing-in-c // https://stackoverflow.com/questions/1644868/define-macro-for-debug-printing-in-c
#define verbose_printerr(fmt, ...) \ #define verbose_printerr(fmt, ...) \
do { \ do { \
if (flags & F_VERBOSE) { \ if (flags & F_VERBOSE) { fprintf(stderr, fmt, ##__VA_ARGS__); } \
fprintf(stderr, fmt, ##__VA_ARGS__); \ } while (0)
} \
} while (0)
typedef std::vector<addr_tuple> set_t; typedef std::vector<addr_tuple> set_t;
@ -52,200 +49,198 @@ void verify_sets(std::vector<set_t> &sets, uint64_t threshold, size_t rounds);
#pragma push_options #pragma push_options
#pragma optimize("O0") #pragma optimize("O0")
static std::vector<uint64_t> time_vals; static std::vector<uint64_t> time_vals;
uint64_t time_tuple(volatile char *a1, volatile char *a2, size_t rounds) {
// uint64_t* time_vals = (uint64_t*) calloc(rounds, sizeof(uint64_t)); uint64_t
if (rounds > time_vals.size()) time_tuple(volatile char *a1, volatile char *a2, size_t rounds)
time_vals.resize(rounds); {
uint64_t t0; // uint64_t* time_vals = (uint64_t*) calloc(rounds, sizeof(uint64_t));
sched_yield(); if (rounds > time_vals.size()) time_vals.resize(rounds);
for (size_t i = 0; i < rounds; i++) {
mfence();
t0 = rdtscp();
*a1;
*a2;
time_vals[i] = rdtscp() - t0;
lfence();
clflush(a1);
clflush(a2);
}
uint64_t mdn = median(time_vals.data(), rounds); uint64_t t0;
// free(time_vals); sched_yield();
return mdn; for (size_t i = 0; i < rounds; i++) {
mfence();
t0 = rdtscp();
*a1;
*a2;
time_vals[i] = rdtscp() - t0;
lfence();
clflush(a1);
clflush(a2);
}
uint64_t mdn = median(time_vals.data(), rounds);
// free(time_vals);
return mdn;
} }
#pragma pop_options #pragma pop_options
//---------------------------------------------------------- //----------------------------------------------------------
char *get_rnd_addr(char *base, size_t m_size, size_t align) { char *
return (char *)ALIGN_TO((uint64_t)base, (uint64_t)align) + get_rnd_addr(char *base, size_t m_size, size_t align)
ALIGN_TO(rand() % m_size, (uint64_t)align); {
return (char *) ALIGN_TO((uint64_t) base, (uint64_t) align) + ALIGN_TO(rand() % m_size, (uint64_t) align);
} }
//---------------------------------------------------------- //----------------------------------------------------------
uint64_t get_pfn(uint64_t entry) { return ((entry) & 0x3fffffffffffff); } uint64_t
get_pfn(uint64_t entry)
//---------------------------------------------------------- {
uint64_t get_phys_addr(uint64_t v_addr) { return ((entry) & 0x3fffffffffffff);
uint64_t entry;
uint64_t offset = (v_addr / 4096) * sizeof(entry);
uint64_t pfn;
int fd = open("/proc/self/pagemap", O_RDONLY);
assert(fd >= 0);
int bytes_read = pread(fd, &entry, sizeof(entry), offset);
close(fd);
assert(bytes_read == 8);
assert(entry & (1ULL << 63));
pfn = get_pfn(entry);
assert(pfn != 0 && "pfn is zero !!!!!");
return (pfn * 4096) | (v_addr & 4095);
} }
//---------------------------------------------------------- //----------------------------------------------------------
addr_tuple gen_addr_tuple(char *v_addr) { uint64_t
return (addr_tuple){v_addr, get_phys_addr((uint64_t)v_addr)}; get_phys_addr(uint64_t v_addr)
{
uint64_t entry;
uint64_t offset = (v_addr / 4096) * sizeof(entry);
uint64_t pfn;
int fd = open("/proc/self/pagemap", O_RDONLY);
assert(fd >= 0);
int bytes_read = pread(fd, &entry, sizeof(entry), offset);
close(fd);
assert(bytes_read == 8);
assert(entry & (1ULL << 63));
pfn = get_pfn(entry);
assert(pfn != 0 && "pfn is zero !!!!!");
return (pfn * 4096) | (v_addr & 4095);
}
//----------------------------------------------------------
addr_tuple
gen_addr_tuple(char *v_addr)
{
return (addr_tuple){v_addr, get_phys_addr((uint64_t) v_addr)};
} }
//---------------------------------------------------------- //----------------------------------------------------------
// https://www.cs.umd.edu/~gasarch/TOPICS/factoring/fastgauss.pdf // https://www.cs.umd.edu/~gasarch/TOPICS/factoring/fastgauss.pdf
// gaussian elimination in GF2 // gaussian elimination in GF2
std::vector<uint64_t> reduce_masks(std::vector<uint64_t> masks) { std::vector<uint64_t>
reduce_masks(std::vector<uint64_t> masks)
{
size_t height, width, height_t, width_t; size_t height, width, height_t, width_t;
height = masks.size(); height = masks.size();
width = 0; width = 0;
for (auto m : masks) { for (auto m : masks) {
uint64_t max_one = 64 - __builtin_clzl(m); uint64_t max_one = 64 - __builtin_clzl(m);
width = (max_one > width) ? max_one : width; width = (max_one > width) ? max_one : width;
}
height_t = width;
width_t = height;
std::vector<std::vector<bool>> mtx(height, std::vector<bool>(width));
std::vector<std::vector<bool>> mtx_t(height_t, std::vector<bool>(width_t));
std::vector<uint64_t> filtered_masks;
for (size_t i = 0; i < height; i++) {
for (size_t j = 0; j < width; j++) {
mtx[i][width - j - 1] = (masks[i] & (1ULL << (j)));
} }
}
for (size_t i = 0; i < height; i++) { height_t = width;
for (size_t j = 0; j < width; j++) { width_t = height;
mtx_t[j][i] = mtx[i][j];
std::vector<std::vector<bool>> mtx(height, std::vector<bool>(width));
std::vector<std::vector<bool>> mtx_t(height_t, std::vector<bool>(width_t));
std::vector<uint64_t> filtered_masks;
for (size_t i = 0; i < height; i++) {
for (size_t j = 0; j < width; j++) { mtx[i][width - j - 1] = (masks[i] & (1ULL << (j))); }
} }
}
int64_t pvt_col = 0; for (size_t i = 0; i < height; i++) {
for (size_t j = 0; j < width; j++) { mtx_t[j][i] = mtx[i][j]; }
}
while (pvt_col < width_t) { int64_t pvt_col = 0;
for (uint64_t row = 0; row < height_t; row++) {
if (mtx_t[row][pvt_col]) {
filtered_masks.push_back(masks[pvt_col]);
for (size_t c = 0; c < width_t; c++) {
if (c == pvt_col)
continue;
if (!(mtx_t[row][c]))
continue;
// column sum while (pvt_col < width_t) {
for (size_t r = 0; r < height_t; r++) { for (uint64_t row = 0; row < height_t; row++) {
mtx_t[r][c] = BOOL_XOR(mtx_t[r][c], mtx_t[r][pvt_col]); if (mtx_t[row][pvt_col]) {
} filtered_masks.push_back(masks[pvt_col]);
for (size_t c = 0; c < width_t; c++) {
if (c == pvt_col) continue;
if (!(mtx_t[row][c])) continue;
// column sum
for (size_t r = 0; r < height_t; r++) { mtx_t[r][c] = BOOL_XOR(mtx_t[r][c], mtx_t[r][pvt_col]); }
}
break;
}
} }
break; pvt_col++;
}
} }
pvt_col++;
}
return filtered_masks; return filtered_masks;
} }
//---------------------------------------------------------- //----------------------------------------------------------
// from https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation // from https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
uint64_t next_bit_permutation(uint64_t v) { uint64_t
uint64_t t = v | (v - 1); next_bit_permutation(uint64_t v)
return (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctzl(v) + 1)); {
uint64_t t = v | (v - 1);
return (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctzl(v) + 1));
} }
//---------------------------------------------------------- //----------------------------------------------------------
std::vector<uint64_t> find_functions(std::vector<set_t> sets, std::vector<uint64_t>
size_t max_fn_bits, size_t msb, find_functions(std::vector<set_t> sets, size_t max_fn_bits, size_t msb, uint64_t flags)
uint64_t flags) { {
std::vector<uint64_t> masks; std::vector<uint64_t> masks;
verbose_printerr("~~~~~~~~~~ Candidate functions ~~~~~~~~~~\n"); verbose_printerr("~~~~~~~~~~ Candidate functions ~~~~~~~~~~\n");
for (size_t bits = 1L; bits <= max_fn_bits; bits++) { for (size_t bits = 1L; bits <= max_fn_bits; bits++) {
uint64_t fn_mask = uint64_t fn_mask = ((1L << (bits)) - 1);// avoid the first 6 bits since they are the cacheline bits
((1L << (bits)) - uint64_t last_mask = (fn_mask << (msb - bits));
1); // avoid the first 6 bits since they are the cacheline bits fn_mask <<= CL_SHIFT;
uint64_t last_mask = (fn_mask << (msb - bits)); verbose_printerr("[ LOG ] - #Bits: %ld \n", bits);
fn_mask <<= CL_SHIFT; while (fn_mask != last_mask) {
verbose_printerr("[ LOG ] - #Bits: %ld \n", bits); if (fn_mask & LS_BITMASK(6)) {
while (fn_mask != last_mask) { fn_mask = next_bit_permutation(fn_mask);
if (fn_mask & LS_BITMASK(6)) { continue;
fn_mask = next_bit_permutation(fn_mask); }
continue; for (size_t idx = 0; idx < sets.size(); idx++) {
} set_t curr_set = sets[idx];
for (size_t idx = 0; idx < sets.size(); idx++) { size_t inner_cnt = 0;
set_t curr_set = sets[idx]; for (size_t i = 1; i < curr_set.size(); i++) {
size_t inner_cnt = 0; uint64_t res_base = __builtin_parityl(curr_set[0].p_addr & fn_mask);
for (size_t i = 1; i < curr_set.size(); i++) { uint64_t res_probe = __builtin_parityl(curr_set[i].p_addr & fn_mask);
uint64_t res_base = __builtin_parityl(curr_set[0].p_addr & fn_mask); if (res_base != res_probe) { goto next_mask; }
uint64_t res_probe = __builtin_parityl(curr_set[i].p_addr & fn_mask); }
if (res_base != res_probe) { }
goto next_mask; verbose_printerr("\t Candidate: 0x%0lx \t\t bits: %s\n", fn_mask, bit_string(fn_mask));
} masks.push_back(fn_mask);
next_mask:
fn_mask = next_bit_permutation(fn_mask);
} }
}
verbose_printerr("\t Candidate: 0x%0lx \t\t bits: %s\n", fn_mask,
bit_string(fn_mask));
masks.push_back(fn_mask);
next_mask:
fn_mask = next_bit_permutation(fn_mask);
} }
} verbose_printerr("~~~~~~~~~~ Found Functions ~~~~~~~~~~\n");
verbose_printerr("~~~~~~~~~~ Found Functions ~~~~~~~~~~\n"); masks = reduce_masks(masks);
masks = reduce_masks(masks); if (flags & F_VERBOSE) {
if (flags & F_VERBOSE) { for (auto m : masks) { fprintf(stderr, "\t Valid Function: 0x%0lx \t\t bits: %s\n", m, bit_string(m)); }
for (auto m : masks) {
fprintf(stderr, "\t Valid Function: 0x%0lx \t\t bits: %s\n", m,
bit_string(m));
} }
} for (auto m : masks) { fprintf(stdout, "0x%lx\n", m); }
for (auto m : masks) { return masks;
fprintf(stdout, "0x%lx\n", m);
}
return masks;
} }
std::vector<int> find_set_bits(uint64_t val) { std::vector<int>
std::vector<int> set_bits; find_set_bits(uint64_t val)
for (int i = 0; i < 64; i++) { {
if (!(val & (1ULL << i))) std::vector<int> set_bits;
continue; for (int i = 0; i < 64; i++) {
if (!(val & (1ULL << i))) continue;
set_bits.push_back(i); set_bits.push_back(i);
} }
return set_bits; return set_bits;
} }
//---------------------------------------------------------- //----------------------------------------------------------
std::vector<uint8_t> get_dram_fn(uint64_t addr, std::vector<uint8_t>
std::vector<uint64_t> fn_masks) { get_dram_fn(uint64_t addr, std::vector<uint64_t> fn_masks)
std::vector<uint8_t> addr_dram; {
for (auto fn : fn_masks) { std::vector<uint8_t> addr_dram;
addr_dram.push_back(__builtin_parityl(addr & fn)); for (auto fn : fn_masks) { addr_dram.push_back(__builtin_parityl(addr & fn)); }
} return addr_dram;
return addr_dram;
} }
//---------------------------------------------------------- //----------------------------------------------------------
@ -254,168 +249,157 @@ It currently finds some of the interesting bits for the row addressing.
@TODO still need to figure out which bits are used for the row addressing and @TODO still need to figure out which bits are used for the row addressing and
which are from the bank selection. This is currently done manually which are from the bank selection. This is currently done manually
*/ */
uint64_t find_row_mask(std::vector<set_t> &sets, std::vector<uint64_t> fn_masks, uint64_t
mem_buff_t mem, uint64_t threshold, uint64_t flags) { find_row_mask(std::vector<set_t> &sets,
std::vector<uint64_t> fn_masks,
mem_buff_t mem,
uint64_t threshold,
uint64_t flags)
{
addr_tuple base_addr = gen_addr_tuple(get_rnd_addr(mem.buffer, mem.size, 0)); addr_tuple base_addr = gen_addr_tuple(get_rnd_addr(mem.buffer, mem.size, 0));
std::vector<set_t> same_row_sets; std::vector<set_t> same_row_sets;
verbose_printerr("~~~~~~~~~~ Looking for row bits ~~~~~~~~~~\n"); verbose_printerr("~~~~~~~~~~ Looking for row bits ~~~~~~~~~~\n");
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
verbose_printerr("[LOG] - Set #%d\n", i); verbose_printerr("[LOG] - Set #%d\n", i);
addr_tuple base_addr = sets[i][0]; addr_tuple base_addr = sets[i][0];
std::vector<uint8_t> base_dram = std::vector<uint8_t> base_dram = get_dram_fn((uint64_t) base_addr.p_addr, fn_masks);
get_dram_fn((uint64_t)base_addr.p_addr, fn_masks); same_row_sets.push_back({base_addr});
same_row_sets.push_back({base_addr}); uint64_t cnt = 0;
uint64_t cnt = 0; while (cnt < ROW_SET_CNT) {
while (cnt < ROW_SET_CNT) {
addr_tuple tmp = gen_addr_tuple(get_rnd_addr(mem.buffer, mem.size, 0)); addr_tuple tmp = gen_addr_tuple(get_rnd_addr(mem.buffer, mem.size, 0));
if (get_dram_fn((uint64_t)tmp.p_addr, fn_masks) != base_dram) if (get_dram_fn((uint64_t) tmp.p_addr, fn_masks) != base_dram) continue;
continue;
uint64_t time = time_tuple((volatile char *)base_addr.v_addr, uint64_t time = time_tuple((volatile char *) base_addr.v_addr, (volatile char *) tmp.v_addr, 1000);
(volatile char *)tmp.v_addr, 1000);
if (time > threshold) if (time > threshold) continue;
continue;
verbose_printerr("[LOG] - %lx - %lx\t Time: %ld <== GOTCHA\n", verbose_printerr("[LOG] - %lx - %lx\t Time: %ld <== GOTCHA\n", base_addr.p_addr, tmp.p_addr, time);
base_addr.p_addr, tmp.p_addr, time);
same_row_sets[i].push_back(tmp); same_row_sets[i].push_back(tmp);
cnt++; cnt++;
}
}
uint64_t row_mask = LS_BITMASK(16); // use 16 bits for the row
uint64_t last_mask = (row_mask << (40 - 16));
row_mask <<=
CL_SHIFT; // skip the lowest 6 bits since they're used for CL addressing
while (row_mask < last_mask) {
if (row_mask & LS_BITMASK(CL_SHIFT)) {
row_mask = next_bit_permutation(row_mask);
continue;
}
for (auto addr_pool : same_row_sets) {
addr_tuple base_addr = addr_pool[0];
for (int i = 1; i < addr_pool.size(); i++) {
addr_tuple tmp = addr_pool[i];
if ((tmp.p_addr & row_mask) != (base_addr.p_addr & row_mask)) {
goto next_mask;
} }
}
} }
break; uint64_t row_mask = LS_BITMASK(16);// use 16 bits for the row
uint64_t last_mask = (row_mask << (40 - 16));
row_mask <<= CL_SHIFT;// skip the lowest 6 bits since they're used for CL addressing
next_mask: while (row_mask < last_mask) {
row_mask = next_bit_permutation(row_mask); if (row_mask & LS_BITMASK(CL_SHIFT)) {
} row_mask = next_bit_permutation(row_mask);
continue;
}
// super hackish way to recover the real row mask for (auto addr_pool : same_row_sets) {
for (auto m : fn_masks) { addr_tuple base_addr = addr_pool[0];
uint64_t lsb = (1 << (__builtin_ctzl(m) + 1)); for (int i = 1; i < addr_pool.size(); i++) {
if (lsb & row_mask) { addr_tuple tmp = addr_pool[i];
row_mask ^= (1 << __builtin_ctzl(m)); if ((tmp.p_addr & row_mask) != (base_addr.p_addr & row_mask)) { goto next_mask; }
}
}
break;
next_mask:
row_mask = next_bit_permutation(row_mask);
} }
}
verbose_printerr("[LOG] - Row mask: 0x%0lx \t\t bits: %s\n", row_mask, // super hackish way to recover the real row mask
bit_string(row_mask)); for (auto m : fn_masks) {
printf("0x%lx\n", row_mask); uint64_t lsb = (1 << (__builtin_ctzl(m) + 1));
return row_mask; if (lsb & row_mask) { row_mask ^= (1 << __builtin_ctzl(m)); }
}
verbose_printerr("[LOG] - Row mask: 0x%0lx \t\t bits: %s\n", row_mask, bit_string(row_mask));
printf("0x%lx\n", row_mask);
return row_mask;
} }
//---------------------------------------------------------- //----------------------------------------------------------
void rev_mc(size_t sets_cnt, size_t threshold, size_t rounds, size_t m_size, void
char *o_file, uint64_t flags) { rev_mc(size_t sets_cnt, size_t threshold, size_t rounds, size_t m_size, char *o_file, uint64_t flags)
{
time_t t; time_t t;
int o_fd = 0; int o_fd = 0;
int huge_fd = 0; int huge_fd = 0;
std::vector<set_t> sets; std::vector<set_t> sets;
// std::vector<char*> used_addr; // std::vector<char*> used_addr;
std::set<char *> used_addr; std::set<char *> used_addr;
std::vector<uint64_t> fn_masks; std::vector<uint64_t> fn_masks;
srand((unsigned)time(&t)); srand((unsigned) time(&t));
if (flags & F_EXPORT) { if (flags & F_EXPORT) {
if (o_file == NULL) { if (o_file == NULL) {
fprintf(stderr, "[ERROR] - Missing export file name\n"); fprintf(stderr, "[ERROR] - Missing export file name\n");
exit(1); exit(1);
}
if ((o_fd = open(o_file, O_CREAT | O_RDWR, 0644)) == -1) {
perror("[ERROR] - Unable to create export file");
exit(1);
}
dprintf(o_fd, O_HEADER);
} }
if ((o_fd = open(o_file, O_CREAT | O_RDWR, 0644)) == -1) {
perror("[ERROR] - Unable to create export file"); mem_buff_t mem = {
exit(1); .buffer = NULL,
.size = m_size,
.flags = flags,
};
alloc_buffer(&mem);
while (!found_enough(sets, sets_cnt, SET_SIZE)) {
char *rnd_addr = get_rnd_addr(mem.buffer, mem.size, CL_SHIFT);
if (is_in(rnd_addr, used_addr)) continue;
// used_addr.push_back(rnd_addr);
used_addr.insert(rnd_addr);
addr_tuple tp = gen_addr_tuple(rnd_addr);
bool found_set = false;
for (size_t idx = 0; idx < sets.size(); idx++) {
uint64_t time = 0;
addr_tuple tmp = sets[idx][0];
time = time_tuple((volatile char *) tmp.v_addr, (volatile char *) tp.v_addr, rounds);
if (flags & F_EXPORT) { dprintf(o_fd, "%lx,%lx,%ld\n", (uint64_t) tp.v_addr, (uint64_t) tmp.v_addr, time); }
if (time > threshold) {
verbose_printerr("[LOG] - [%ld] Set: %03ld -\t %lx - %lx\t Time: %ld\n", used_addr.size(), idx,
tp.p_addr, tmp.p_addr, time);
sets[idx].push_back(tp);
found_set = true;
break;
}
}
if (!found_set) {
sets.push_back({tp});
verbose_printerr("[LOG] - Set: %03ld -\t %p "
" <== NEW!!\n",
sets.size(), tp.v_addr);
}
} }
dprintf(o_fd, O_HEADER);
}
mem_buff_t mem = { filter_sets(sets, SET_SIZE);
.buffer = NULL,
.size = m_size,
.flags = flags,
};
alloc_buffer(&mem);
while (!found_enough(sets, sets_cnt, SET_SIZE)) {
char *rnd_addr = get_rnd_addr(mem.buffer, mem.size, CL_SHIFT);
if (is_in(rnd_addr, used_addr))
continue;
// used_addr.push_back(rnd_addr);
used_addr.insert(rnd_addr);
addr_tuple tp = gen_addr_tuple(rnd_addr);
bool found_set = false;
for (size_t idx = 0; idx < sets.size(); idx++) {
uint64_t time = 0;
addr_tuple tmp = sets[idx][0];
time = time_tuple((volatile char *)tmp.v_addr, (volatile char *)tp.v_addr,
rounds);
if (flags & F_EXPORT) {
dprintf(o_fd, "%lx,%lx,%ld\n", (uint64_t)tp.v_addr,
(uint64_t)tmp.v_addr, time);
}
if (time > threshold) {
verbose_printerr("[LOG] - [%ld] Set: %03ld -\t %lx - %lx\t Time: %ld\n",
used_addr.size(), idx, tp.p_addr, tmp.p_addr, time);
sets[idx].push_back(tp);
found_set = true;
break;
}
}
if (!found_set) {
sets.push_back({tp});
verbose_printerr("[LOG] - Set: %03ld -\t %p "
" <== NEW!!\n",
sets.size(), tp.v_addr);
}
}
filter_sets(sets, SET_SIZE);
#ifdef DEBUG_SETS #ifdef DEBUG_SETS
fprintf(stderr, fprintf(stderr, "[ LOG ] - Cleansing sets. This may take a while... stay put\n");
"[ LOG ] - Cleansing sets. This may take a while... stay put\n"); verify_sets(sets, threshold, rounds);
verify_sets(sets, threshold, rounds); fprintf(stderr, "[ LOG ] - Done\n");
fprintf(stderr, "[ LOG ] - Done\n");
#endif #endif
if (flags & F_VERBOSE) { if (flags & F_VERBOSE) { print_sets(sets); }
print_sets(sets);
}
fn_masks = find_functions(sets, 6, 30, flags); fprintf(stderr, "Start Find Function\n");
uint64_t row_mask = find_row_mask(sets, fn_masks, mem, threshold, flags); fn_masks = find_functions(sets, 6, 30, flags);
fprintf(stderr, "Start Find RowMask\n");
uint64_t row_mask = find_row_mask(sets, fn_masks, mem, threshold, flags);
free_buffer(&mem); free_buffer(&mem);
} }
// Fin. // Fin.
@ -423,87 +407,88 @@ void rev_mc(size_t sets_cnt, size_t threshold, size_t rounds, size_t m_size,
//---------------------------------------------------------- //----------------------------------------------------------
// Helpers // Helpers
bool is_in(char *val, std::set<char *> arr) { bool
return arr.find(val) != arr.end(); is_in(char *val, std::set<char *> arr)
{
return arr.find(val) != arr.end();
} }
bool is_in(char *val, std::vector<char *> arr) { bool
for (auto v : arr) { is_in(char *val, std::vector<char *> arr)
if (val == v) { {
return true; for (auto v : arr) {
if (val == v) { return true; }
} }
} return false;
return false;
} }
//---------------------------------------------------------- //----------------------------------------------------------
bool found_enough(std::vector<set_t> sets, uint64_t set_cnt, size_t set_size) { bool
found_enough(std::vector<set_t> sets, uint64_t set_cnt, size_t set_size)
{
size_t found_sets = 0; size_t found_sets = 0;
for (int i = 0; i < sets.size(); i++) { for (int i = 0; i < sets.size(); i++) {
set_t curr_set = sets[i]; set_t curr_set = sets[i];
if (curr_set.size() > set_size) { if (curr_set.size() > set_size) { found_sets += 1; }
found_sets += 1;
} }
}
if (found_sets > set_cnt) { if (found_sets > set_cnt) {
fprintf( fprintf(stderr, "[ERROR] - Found too many sets. Is %ld the correct number of sets?\n", set_cnt);
stderr, exit(1);
"[ERROR] - Found too many sets. Is %ld the correct number of sets?\n", }
set_cnt);
exit(1);
}
return (found_sets >= (set_cnt * SET_THRESH)) ? true : false; return (found_sets >= (set_cnt * SET_THRESH)) ? true : false;
} }
void filter_sets(std::vector<set_t> &sets, size_t set_size) { void
filter_sets(std::vector<set_t> &sets, size_t set_size)
{
for (auto s = sets.begin(); s < sets.end(); s++) { for (auto s = sets.begin(); s < sets.end();) {
if (s->size() < set_size) { if (s->size() < set_size) {
sets.erase(s); s = sets.erase(s);
s -= 1; // s -= 1;
} else {
++s;
}
} }
}
} }
void print_sets(std::vector<set_t> sets) { void
print_sets(std::vector<set_t> sets)
{
for (int idx = 0; idx < sets.size(); idx++) { for (int idx = 0; idx < sets.size(); idx++) {
fprintf(stderr, "[LOG] - Set: %d\tSize: %ld\n", idx, sets[idx].size()); fprintf(stderr, "[LOG] - Set: %d\tSize: %ld\n", idx, sets[idx].size());
for (auto tmp : sets[idx]) { for (auto tmp : sets[idx]) { fprintf(stderr, "\tv_addr:%p - p_addr:%p\n", tmp.v_addr, (void *) tmp.p_addr); }
fprintf(stderr, "\tv_addr:%p - p_addr:%p\n", tmp.v_addr,
(void *)tmp.p_addr);
} }
}
} }
#ifdef DEBUG_SETS #ifdef DEBUG_SETS
void verify_sets(std::vector<set_t> &sets, uint64_t threshold, size_t rounds) { void
verify_sets(std::vector<set_t> &sets, uint64_t threshold, size_t rounds)
{
for (auto s : sets) { for (auto s : sets) {
// test every address against all the addresses in the set // test every address against all the addresses in the set
for (auto tp_base = s.begin(); tp_base < s.end(); tp_base++) { for (auto tp_base = s.begin(); tp_base < s.end(); tp_base++) {
uint64_t conflicts = 0; uint64_t conflicts = 0;
for (auto tp_probe = s.begin(); tp_probe < s.end(); tp_probe++) { for (auto tp_probe = s.begin(); tp_probe < s.end(); tp_probe++) {
if (tp_base == tp_probe) if (tp_base == tp_probe) continue;
continue;
uint64_t time = time_tuple((volatile char *)tp_base->v_addr, uint64_t time =
(volatile char *)tp_probe->v_addr, rounds); time_tuple((volatile char *) tp_base->v_addr, (volatile char *) tp_probe->v_addr, rounds);
if (time > threshold) { if (time > threshold) { conflicts += 1; }
conflicts += 1; }
if (!(conflicts > VALID_THRESH * s.size())) {
fprintf(stderr, "[ LOG ] - Removing: %p\n", tp_base->v_addr);
s.erase(tp_base--);// reset the iterator
}
} }
}
if (!(conflicts > VALID_THRESH * s.size())) {
fprintf(stderr, "[ LOG ] - Removing: %p\n", tp_base->v_addr);
s.erase(tp_base--); // reset the iterator
}
} }
}
} }
#endif #endif

View File

@ -1,87 +1,84 @@
#include <sys/types.h> #include <assert.h>
#include <sys/stat.h> #include <errno.h>
#include <sys/mman.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <string.h> #include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "utils.h" #include "utils.h"
//----------------------------------------------- //-----------------------------------------------
// Memory alloc // Memory alloc
int alloc_buffer(mem_buff_t* mem) { int
if (mem->buffer != NULL) { alloc_buffer(mem_buff_t *mem)
fprintf(stderr, "[ERROR] - Memory already allocated\n"); {
} if (mem->buffer != NULL) { fprintf(stderr, "[ERROR] - Memory already allocated\n"); }
uint64_t alloc_flags = MAP_PRIVATE | MAP_POPULATE | MAP_ANONYMOUS; uint64_t alloc_flags = MAP_PRIVATE | MAP_POPULATE | MAP_ANONYMOUS;
mem->buffer = (char*) mmap(NULL, mem->size, PROT_READ | PROT_WRITE, alloc_flags, -1, 0); mem->buffer = (char *) mmap(NULL, mem->size, PROT_READ | PROT_WRITE, alloc_flags, -1, 0);
if (mem->buffer == MAP_FAILED) { if (mem->buffer == MAP_FAILED) {
perror("[ERROR] - mmap() failed"); perror("[ERROR] - mmap() failed");
exit(1); exit(1);
} }
if (mem->flags & F_VERBOSE) {
fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
fprintf(stderr, "[ MEM ] - Buffer: %p\n", mem->buffer);
fprintf(stderr, "[ MEM ] - Size: %ld\n", mem->size);
fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
}
return 0;
if (mem->flags & F_VERBOSE) {
fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
fprintf(stderr, "[ MEM ] - Buffer: %p\n", mem->buffer);
fprintf(stderr, "[ MEM ] - Size: %ld\n", mem->size);
fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
}
return 0;
} }
int
free_buffer(mem_buff_t *mem)
int free_buffer(mem_buff_t* mem) { {
return munmap(mem->buffer, mem->size); return munmap(mem->buffer, mem->size);
} }
//----------------------------------------------- //-----------------------------------------------
// Helpers // Helpers
double mean(uint64_t* vals, size_t size) { double
uint64_t avg = 0; mean(uint64_t *vals, size_t size)
for (size_t i = 0; i < size; i++) { {
avg += vals[i]; uint64_t avg = 0;
} for (size_t i = 0; i < size; i++) { avg += vals[i]; }
return ((double)avg) / size; return ((double) avg) / size;
} }
int gt(const void * a, const void * b) { int
return ( *(int*)a - *(int*)b ); gt(const void *a, const void *b)
{
return (*(int *) a - *(int *) b);
} }
uint64_t median(uint64_t* vals, size_t size) { uint64_t
qsort(vals, size, sizeof(uint64_t), gt); median(uint64_t *vals, size_t size)
return ((size%2)==0) ? vals[size/2] : (vals[(size_t)size/2]+vals[((size_t)size/2+1)])/2; {
qsort(vals, size, sizeof(uint64_t), gt);
return ((size % 2) == 0) ? vals[size / 2] : (vals[(size_t) size / 2] + vals[((size_t) size / 2 + 1)]) / 2;
} }
char *
bit_string(uint64_t val)
{
static char bit_str[256];
char itoa_str[8];
strcpy(bit_str, "");
for (int shift = 0; shift < 64; shift++) {
if ((val >> shift) & 1) {
if (strcmp(bit_str, "") != 0) { strcat(bit_str, "+ "); }
sprintf(itoa_str, "%d ", shift);
strcat(bit_str, itoa_str);
}
}
char* bit_string(uint64_t val) { return bit_str;
static char bit_str[256];
char itoa_str[8];
strcpy(bit_str, "");
for (int shift = 0; shift < 64; shift++) {
if ((val >> shift) & 1) {
if (strcmp(bit_str, "") != 0) {
strcat(bit_str, "+ ");
}
sprintf(itoa_str, "%d ", shift);
strcat(bit_str, itoa_str);
}
}
return bit_str;
} }