• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * copyright 2010-2012, the android open source project
3  *
4  * licensed under the apache license, version 2.0 (the "license");
5  * you may not use this file except in compliance with the license.
6  * you may obtain a copy of the license at
7  *
8  *     http://www.apache.org/licenses/license-2.0
9  *
10  * unless required by applicable law or agreed to in writing, software
11  * distributed under the license is distributed on an "as is" basis,
12  * without warranties or conditions of any kind, either express or implied.
13  * see the license for the specific language governing permissions and
14  * limitations under the license.
15  */
16 
17 #include "Script.h"
18 
19 #include "Config.h"
20 #include "bcinfo/BitcodeWrapper.h"
21 
22 #include "MCCacheReader.h"
23 #include "MCCacheWriter.h"
24 #include "CompilerOption.h"
25 
26 #include "DebugHelper.h"
27 #include "FileHandle.h"
28 #include "GDBJITRegistrar.h"
29 #include "ScriptCompiled.h"
30 #include "ScriptCached.h"
31 #include "Sha1Helper.h"
32 #include "SourceInfo.h"
33 
34 #include <errno.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <unistd.h>
38 
39 #include <new>
40 #include <string.h>
41 #include <cutils/properties.h>
42 
43 namespace {
44 
getBooleanProp(const char * str)45 bool getBooleanProp(const char *str) {
46   char buf[PROPERTY_VALUE_MAX];
47   property_get(str, buf, "0");
48   return strcmp(buf, "0") != 0;
49 }
50 
51 } // namespace anonymous
52 
53 namespace bcc {
54 
~Script()55 Script::~Script() {
56   switch (mStatus) {
57   case ScriptStatus::Compiled:
58     delete mCompiled;
59     break;
60 
61   case ScriptStatus::Cached:
62     delete mCached;
63     break;
64 
65   default:
66     break;
67   }
68 
69   for (size_t i = 0; i < 2; ++i) {
70     delete mSourceList[i];
71   }
72 }
73 
74 
addSourceBC(size_t idx,char const * resName,const char * bitcode,size_t bitcodeSize,unsigned long flags)75 int Script::addSourceBC(size_t idx,
76                         char const *resName,
77                         const char *bitcode,
78                         size_t bitcodeSize,
79                         unsigned long flags) {
80 
81   if (!resName) {
82     mErrorCode = BCC_INVALID_VALUE;
83     ALOGE("Invalid argument: resName = NULL\n");
84     return 1;
85   }
86 
87   if (mStatus != ScriptStatus::Unknown) {
88     mErrorCode = BCC_INVALID_OPERATION;
89     ALOGE("Bad operation: Adding source after bccPrepareExecutable\n");
90     return 1;
91   }
92 
93   if (!bitcode) {
94     mErrorCode = BCC_INVALID_VALUE;
95     ALOGE("Invalid argument: bitcode = NULL\n");
96     return 1;
97   }
98 
99   bcinfo::BitcodeWrapper wrapper(bitcode, bitcodeSize);
100 
101   mSourceList[idx] = SourceInfo::createFromBuffer(resName,
102                                                   bitcode, bitcodeSize,
103                                                   flags);
104 
105   if (!mSourceList[idx]) {
106     mErrorCode = BCC_OUT_OF_MEMORY;
107     ALOGE("Out of memory while adding source bitcode\n");
108     return 1;
109   }
110 
111   return 0;
112 }
113 
114 
addSourceModule(size_t idx,llvm::Module * module,unsigned long flags)115 int Script::addSourceModule(size_t idx,
116                             llvm::Module *module,
117                             unsigned long flags) {
118   if (mStatus != ScriptStatus::Unknown) {
119     mErrorCode = BCC_INVALID_OPERATION;
120     ALOGE("Bad operation: Adding source after bccPrepareExecutable\n");
121     return 1;
122   }
123 
124   if (!module) {
125     mErrorCode = BCC_INVALID_VALUE;
126     ALOGE("Invalid argument: module = NULL\n");
127     return 1;
128   }
129 
130   mSourceList[idx] = SourceInfo::createFromModule(module, flags);
131 
132   if (!mSourceList[idx]) {
133     mErrorCode = BCC_OUT_OF_MEMORY;
134     ALOGE("Out of memory when add source module\n");
135     return 1;
136   }
137 
138   return 0;
139 }
140 
141 
addSourceFile(size_t idx,char const * path,unsigned long flags)142 int Script::addSourceFile(size_t idx,
143                           char const *path,
144                           unsigned long flags) {
145   if (mStatus != ScriptStatus::Unknown) {
146     mErrorCode = BCC_INVALID_OPERATION;
147     ALOGE("Bad operation: Adding source after bccPrepareExecutable\n");
148     return 1;
149   }
150 
151   if (!path) {
152     mErrorCode = BCC_INVALID_VALUE;
153     ALOGE("Invalid argument: path = NULL\n");
154     return 1;
155   }
156 
157   struct stat sb;
158   if (stat(path, &sb) != 0) {
159     mErrorCode = BCC_INVALID_VALUE;
160     ALOGE("File not found: %s\n", path);
161     return 1;
162   }
163 
164   mSourceList[idx] = SourceInfo::createFromFile(path, flags);
165 
166   if (!mSourceList[idx]) {
167     mErrorCode = BCC_OUT_OF_MEMORY;
168     ALOGE("Out of memory while adding source file\n");
169     return 1;
170   }
171 
172   return 0;
173 }
174 
prepareRelocatable(char const * objPath,llvm::Reloc::Model RelocModel,unsigned long flags)175 int Script::prepareRelocatable(char const *objPath,
176                                llvm::Reloc::Model RelocModel,
177                                unsigned long flags) {
178   CompilerOption option;
179   option.RelocModelOpt = RelocModel;
180   option.LoadAfterCompile = false;
181 
182   int status = internalCompile(option);
183   if (status != 0) {
184     ALOGE("LLVM error message: %s\n", getCompilerErrorMessage());
185     return status;
186   }
187 
188   FileHandle objFile;
189   if (objFile.open(objPath, OpenMode::Write) < 0) {
190     ALOGE("Failed to open %s for write.\n", objPath);
191     return 1;
192   }
193 
194   if (static_cast<size_t>(objFile.write(getELF(),
195                                         getELFSize())) != getELFSize()) {
196       objFile.close();
197       ::unlink(objPath);
198       ALOGE("Unable to write ELF to file %s.\n", objPath);
199       return false;
200   }
201 
202   mObjectType = ScriptObject::Relocatable;
203 
204   return 0;
205 }
206 
207 
prepareSharedObject(char const * objPath,char const * dsoPath,unsigned long flags)208 int Script::prepareSharedObject(char const *objPath,
209                                 char const *dsoPath,
210                                 unsigned long flags) {
211   // TODO: Support cached shared object.
212   return 1;
213 }
214 
215 
prepareExecutable(char const * cacheDir,char const * cacheName,unsigned long flags)216 int Script::prepareExecutable(char const *cacheDir,
217                               char const *cacheName,
218                               unsigned long flags) {
219   if (mStatus != ScriptStatus::Unknown) {
220     mErrorCode = BCC_INVALID_OPERATION;
221     ALOGE("Invalid operation: %s\n", __func__);
222     return 1;
223   }
224 
225   int status = internalLoadCache(cacheDir, cacheName, /* checkOnly */ false);
226 
227   if (status != 0) {
228     CompilerOption option;
229     status = internalCompile(option);
230 
231     if (status != 0) {
232       ALOGE("LLVM error message: %s\n", getCompilerErrorMessage());
233       return status;
234     }
235 
236     status = writeCache();
237     if (status != 0) {
238       ALOGE("Failed to write the cache for %s\n", cacheName);
239       return status;
240     }
241   }
242 
243   // FIXME: Registration can be conditional on the presence of debug metadata
244   registerObjectWithGDB(getELF(), getELFSize()); // thread-safe registration
245 
246   mObjectType = ScriptObject::Executable;
247 
248   return status;
249 }
250 
internalLoadCache(char const * cacheDir,char const * cacheName,bool checkOnly)251 int Script::internalLoadCache(char const *cacheDir, char const *cacheName,
252                               bool checkOnly) {
253   if ((cacheDir == NULL) || (cacheName == NULL)) {
254     return 1;
255   }
256 
257   // Set cache file Name
258   mCacheName = cacheName;
259 
260   // Santize mCacheDir. Ensure that mCacheDir ends with '/'.
261   mCacheDir = cacheDir;
262   if (!mCacheDir.empty() && *mCacheDir.rbegin() != '/') {
263     mCacheDir.push_back('/');
264   }
265 
266   if (!isCacheable()) {
267     return 1;
268   }
269 
270   std::string objPath = getCachedObjectPath();
271   std::string infoPath = getCacheInfoPath();
272 
273   FileHandle objFile;
274   if (objFile.open(objPath.c_str(), OpenMode::Read) < 0) {
275     // Unable to open the executable file in read mode.
276     return 1;
277   }
278 
279   FileHandle infoFile;
280   if (infoFile.open(infoPath.c_str(), OpenMode::Read) < 0) {
281     // Unable to open the metadata information file in read mode.
282     return 1;
283   }
284 
285   MCCacheReader reader;
286 
287   // Register symbol lookup function
288   if (mpExtSymbolLookupFn) {
289     reader.registerSymbolCallback(mpExtSymbolLookupFn,
290                                       mpExtSymbolLookupFnContext);
291   }
292 
293   // Dependencies
294   reader.addDependency(BCC_FILE_RESOURCE, pathLibBCC_SHA1, sha1LibBCC_SHA1);
295   reader.addDependency(BCC_FILE_RESOURCE, pathLibRS, sha1LibRS);
296 
297   for (size_t i = 0; i < 2; ++i) {
298     if (mSourceList[i]) {
299       mSourceList[i]->introDependency(reader);
300     }
301   }
302 
303   if (checkOnly)
304     return !reader.checkCacheFile(&objFile, &infoFile, this);
305 
306   // Read cache file
307   ScriptCached *cached = reader.readCacheFile(&objFile, &infoFile, this);
308 
309   if (!cached) {
310     mIsContextSlotNotAvail = reader.isContextSlotNotAvail();
311     return 1;
312   }
313 
314   mCached = cached;
315   mStatus = ScriptStatus::Cached;
316 
317   // Dirty hack for libRS.
318   // TODO(all):  This dirty hack should be removed in the future.
319   if (!cached->isLibRSThreadable() && mpExtSymbolLookupFn) {
320     mpExtSymbolLookupFn(mpExtSymbolLookupFnContext, "__clearThreadable");
321   }
322 
323   return 0;
324 }
325 
internalCompile(const CompilerOption & option)326 int Script::internalCompile(const CompilerOption &option) {
327   // Create the ScriptCompiled object
328   mCompiled = new (std::nothrow) ScriptCompiled(this);
329 
330   if (!mCompiled) {
331     mErrorCode = BCC_OUT_OF_MEMORY;
332     ALOGE("Out of memory: %s %d\n", __FILE__, __LINE__);
333     return 1;
334   }
335 
336   mStatus = ScriptStatus::Compiled;
337 
338   // Register symbol lookup function
339   if (mpExtSymbolLookupFn) {
340     mCompiled->registerSymbolCallback(mpExtSymbolLookupFn,
341                                       mpExtSymbolLookupFnContext);
342   }
343 
344   if (!mSourceList[0]) {
345     ALOGE("Source bitcode is not set.\n");
346     return 1;
347   }
348 
349   // Parse Source bitcode file (if necessary)
350   if (mSourceList[0]->prepareModule() != 0) {
351     ALOGE("Unable to setup source module\n");
352     return 1;
353   }
354 
355   // Parse Library bitcode file (if necessary)
356   if (mSourceList[1]) {
357     if (mSourceList[1]->prepareModule(mSourceList[0]->getContext()) != 0) {
358       ALOGE("Unable to setup library module\n");
359       return 1;
360     }
361   }
362 
363   // Set the main source module
364   if (mCompiled->readModule(mSourceList[0]->getModule()) != 0) {
365     ALOGE("Unable to read source module\n");
366     return 1;
367   }
368 
369   // Link the source module with the library module
370   if (mSourceList[1]) {
371     if (mCompiled->linkModule(mSourceList[1]->getModule()) != 0) {
372       ALOGE("Unable to link library module\n");
373       return 1;
374     }
375   }
376 
377   // Compile and JIT the code
378   if (mCompiled->compile(option) != 0) {
379     ALOGE("Unable to compile.\n");
380     return 1;
381   }
382 
383   return 0;
384 }
385 
writeCache()386 int Script::writeCache() {
387   // Not compiled script or encountered error during the compilation.
388   if ((mStatus != ScriptStatus::Compiled) ||
389       (getCompilerErrorMessage() == NULL))
390     return 1;
391 
392   // Note: If we re-compile the script because the cached context slot not
393   // available, then we don't have to write the cache.
394 
395   // Note: If the address of the context is not in the context slot, then
396   // we don't have to cache it.
397 
398   if (isCacheable()) {
399 
400     std::string objPath = getCachedObjectPath();
401     std::string infoPath = getCacheInfoPath();
402 
403     // Remove the file if it already exists before writing the new file.
404     // The old file may still be mapped elsewhere in memory and we do not want
405     // to modify its contents.  (The same script may be running concurrently in
406     // the same process or a different process!)
407     ::unlink(objPath.c_str());
408     ::unlink(infoPath.c_str());
409 
410     FileHandle objFile;
411     FileHandle infoFile;
412 
413     if (objFile.open(objPath.c_str(), OpenMode::Write) >= 0 &&
414         infoFile.open(infoPath.c_str(), OpenMode::Write) >= 0) {
415 
416       MCCacheWriter writer;
417 
418 #ifdef TARGET_BUILD
419       // Dependencies
420       writer.addDependency(BCC_FILE_RESOURCE, pathLibBCC_SHA1, sha1LibBCC_SHA1);
421       writer.addDependency(BCC_FILE_RESOURCE, pathLibRS, sha1LibRS);
422 #endif
423 
424       for (size_t i = 0; i < 2; ++i) {
425         if (mSourceList[i]) {
426           mSourceList[i]->introDependency(writer);
427         }
428       }
429 
430       // libRS is threadable dirty hack
431       // TODO: This should be removed in the future
432       uint32_t libRS_threadable = 0;
433       if (mpExtSymbolLookupFn) {
434         libRS_threadable =
435           (uint32_t)mpExtSymbolLookupFn(mpExtSymbolLookupFnContext,
436                                         "__isThreadable");
437       }
438 
439       if (!writer.writeCacheFile(&objFile, &infoFile, this, libRS_threadable)) {
440         objFile.truncate();
441         objFile.close();
442 
443         if (unlink(objPath.c_str()) != 0) {
444           ALOGE("Unable to remove the invalid cache file: %s. (reason: %s)\n",
445                objPath.c_str(), strerror(errno));
446         }
447 
448         infoFile.truncate();
449         infoFile.close();
450 
451         if (unlink(infoPath.c_str()) != 0) {
452           ALOGE("Unable to remove the invalid cache file: %s. (reason: %s)\n",
453                infoPath.c_str(), strerror(errno));
454         }
455       }
456     }
457   }
458 
459   return 0;
460 }
461 
462 
getCompilerErrorMessage()463 char const *Script::getCompilerErrorMessage() {
464   if (mStatus != ScriptStatus::Compiled) {
465     mErrorCode = BCC_INVALID_OPERATION;
466     return NULL;
467   }
468 
469   return mCompiled->getCompilerErrorMessage();
470 }
471 
472 
lookup(const char * name)473 void *Script::lookup(const char *name) {
474   switch (mStatus) {
475     case ScriptStatus::Compiled: {
476       return mCompiled->lookup(name);
477     }
478 
479     case ScriptStatus::Cached: {
480       return mCached->lookup(name);
481     }
482 
483     default: {
484       mErrorCode = BCC_INVALID_OPERATION;
485       return NULL;
486     }
487   }
488 }
489 
490 
getExportVarCount() const491 size_t Script::getExportVarCount() const {
492   switch (mStatus) {
493     case ScriptStatus::Compiled: {
494       return mCompiled->getExportVarCount();
495     }
496 
497     case ScriptStatus::Cached: {
498       return mCached->getExportVarCount();
499     }
500 
501     default: {
502       return 0;
503     }
504   }
505 }
506 
507 
getExportFuncCount() const508 size_t Script::getExportFuncCount() const {
509   switch (mStatus) {
510     case ScriptStatus::Compiled: {
511       return mCompiled->getExportFuncCount();
512     }
513 
514     case ScriptStatus::Cached: {
515       return mCached->getExportFuncCount();
516     }
517 
518     default: {
519       return 0;
520     }
521   }
522 }
523 
524 
getExportForEachCount() const525 size_t Script::getExportForEachCount() const {
526   switch (mStatus) {
527     case ScriptStatus::Compiled: {
528       return mCompiled->getExportForEachCount();
529     }
530 
531     case ScriptStatus::Cached: {
532       return mCached->getExportForEachCount();
533     }
534 
535     default: {
536       return 0;
537     }
538   }
539 }
540 
541 
getPragmaCount() const542 size_t Script::getPragmaCount() const {
543   switch (mStatus) {
544     case ScriptStatus::Compiled: {
545       return mCompiled->getPragmaCount();
546     }
547 
548     case ScriptStatus::Cached: {
549       return mCached->getPragmaCount();
550     }
551 
552     default: {
553       return 0;
554     }
555   }
556 }
557 
558 
getFuncCount() const559 size_t Script::getFuncCount() const {
560   switch (mStatus) {
561     case ScriptStatus::Compiled: {
562       return mCompiled->getFuncCount();
563     }
564 
565     case ScriptStatus::Cached: {
566       return mCached->getFuncCount();
567     }
568 
569     default: {
570       return 0;
571     }
572   }
573 }
574 
575 
getObjectSlotCount() const576 size_t Script::getObjectSlotCount() const {
577   switch (mStatus) {
578     case ScriptStatus::Compiled: {
579       return mCompiled->getObjectSlotCount();
580     }
581 
582     case ScriptStatus::Cached: {
583       return mCached->getObjectSlotCount();
584     }
585 
586     default: {
587       return 0;
588     }
589   }
590 }
591 
592 
getExportVarList(size_t varListSize,void ** varList)593 void Script::getExportVarList(size_t varListSize, void **varList) {
594   switch (mStatus) {
595 #define DELEGATE(STATUS) \
596     case ScriptStatus::STATUS:                           \
597       m##STATUS->getExportVarList(varListSize, varList); \
598       break;
599 
600     DELEGATE(Cached);
601 
602     DELEGATE(Compiled);
603 #undef DELEGATE
604 
605     default: {
606       mErrorCode = BCC_INVALID_OPERATION;
607     }
608   }
609 }
610 
getExportVarNameList(std::vector<std::string> & varList)611 void Script::getExportVarNameList(std::vector<std::string> &varList) {
612   switch (mStatus) {
613     case ScriptStatus::Compiled: {
614       return mCompiled->getExportVarNameList(varList);
615     }
616 
617     default: {
618       mErrorCode = BCC_INVALID_OPERATION;
619     }
620   }
621 }
622 
623 
getExportFuncList(size_t funcListSize,void ** funcList)624 void Script::getExportFuncList(size_t funcListSize, void **funcList) {
625   switch (mStatus) {
626 #define DELEGATE(STATUS) \
627     case ScriptStatus::STATUS:                              \
628       m##STATUS->getExportFuncList(funcListSize, funcList); \
629       break;
630 
631     DELEGATE(Cached);
632 
633     DELEGATE(Compiled);
634 #undef DELEGATE
635 
636     default: {
637       mErrorCode = BCC_INVALID_OPERATION;
638     }
639   }
640 }
641 
getExportFuncNameList(std::vector<std::string> & funcList)642 void Script::getExportFuncNameList(std::vector<std::string> &funcList) {
643   switch (mStatus) {
644     case ScriptStatus::Compiled: {
645       return mCompiled->getExportFuncNameList(funcList);
646     }
647 
648     default: {
649       mErrorCode = BCC_INVALID_OPERATION;
650     }
651   }
652 }
653 
getExportForEachList(size_t funcListSize,void ** funcList)654 void Script::getExportForEachList(size_t funcListSize, void **funcList) {
655   switch (mStatus) {
656 #define DELEGATE(STATUS) \
657     case ScriptStatus::STATUS:                                 \
658       m##STATUS->getExportForEachList(funcListSize, funcList); \
659       break;
660 
661     DELEGATE(Cached);
662 
663     DELEGATE(Compiled);
664 #undef DELEGATE
665 
666     default: {
667       mErrorCode = BCC_INVALID_OPERATION;
668     }
669   }
670 }
671 
getExportForEachNameList(std::vector<std::string> & forEachList)672 void Script::getExportForEachNameList(std::vector<std::string> &forEachList) {
673   switch (mStatus) {
674     case ScriptStatus::Compiled: {
675       return mCompiled->getExportForEachNameList(forEachList);
676     }
677 
678     default: {
679       mErrorCode = BCC_INVALID_OPERATION;
680     }
681   }
682 }
683 
getPragmaList(size_t pragmaListSize,char const ** keyList,char const ** valueList)684 void Script::getPragmaList(size_t pragmaListSize,
685                            char const **keyList,
686                            char const **valueList) {
687   switch (mStatus) {
688 #define DELEGATE(STATUS) \
689     case ScriptStatus::STATUS:                                      \
690       m##STATUS->getPragmaList(pragmaListSize, keyList, valueList); \
691       break;
692 
693     DELEGATE(Cached);
694 
695     DELEGATE(Compiled);
696 #undef DELEGATE
697 
698     default: {
699       mErrorCode = BCC_INVALID_OPERATION;
700     }
701   }
702 }
703 
704 
getFuncInfoList(size_t funcInfoListSize,FuncInfo * funcInfoList)705 void Script::getFuncInfoList(size_t funcInfoListSize,
706                              FuncInfo *funcInfoList) {
707   switch (mStatus) {
708 #define DELEGATE(STATUS) \
709     case ScriptStatus::STATUS:                                    \
710       m##STATUS->getFuncInfoList(funcInfoListSize, funcInfoList); \
711       break;
712 
713     DELEGATE(Cached);
714 
715     DELEGATE(Compiled);
716 #undef DELEGATE
717 
718     default: {
719       mErrorCode = BCC_INVALID_OPERATION;
720     }
721   }
722 }
723 
724 
getObjectSlotList(size_t objectSlotListSize,uint32_t * objectSlotList)725 void Script::getObjectSlotList(size_t objectSlotListSize,
726                                uint32_t *objectSlotList) {
727   switch (mStatus) {
728 #define DELEGATE(STATUS)     \
729     case ScriptStatus::STATUS:                                          \
730       m##STATUS->getObjectSlotList(objectSlotListSize, objectSlotList); \
731       break;
732 
733     DELEGATE(Cached);
734 
735     DELEGATE(Compiled);
736 #undef DELEGATE
737 
738     default: {
739       mErrorCode = BCC_INVALID_OPERATION;
740     }
741   }
742 }
743 
744 
registerSymbolCallback(BCCSymbolLookupFn pFn,void * pContext)745 int Script::registerSymbolCallback(BCCSymbolLookupFn pFn, void *pContext) {
746   mpExtSymbolLookupFn = pFn;
747   mpExtSymbolLookupFnContext = pContext;
748 
749   if (mStatus != ScriptStatus::Unknown) {
750     mErrorCode = BCC_INVALID_OPERATION;
751     ALOGE("Invalid operation: %s\n", __func__);
752     return 1;
753   }
754   return 0;
755 }
756 
isCacheable() const757 bool Script::isCacheable() const {
758   if (getBooleanProp("debug.bcc.nocache")) {
759     // Android system environment property: Disables the cache mechanism by
760     // setting "debug.bcc.nocache".  So we will not load the cache file any
761     // way.
762     return false;
763   }
764 
765   if (mCacheDir.empty() || mCacheName.empty()) {
766     // The application developer has not specified the cachePath, so
767     // we don't know where to open the cache file.
768     return false;
769   }
770 
771   return true;
772 }
773 
getELFSize() const774 size_t Script::getELFSize() const {
775   switch (mStatus) {
776     case ScriptStatus::Compiled: {
777       return mCompiled->getELFSize();
778     }
779 
780     case ScriptStatus::Cached: {
781       return mCached->getELFSize();
782     }
783 
784     default: {
785       return 0;
786     }
787   }
788 }
789 
getELF() const790 const char *Script::getELF() const {
791   switch (mStatus) {
792     case ScriptStatus::Compiled: {
793       return mCompiled->getELF();
794     }
795 
796     case ScriptStatus::Cached: {
797       return mCached->getELF();
798     }
799 
800     default: {
801       return NULL;
802     }
803   }
804 }
805 
806 } // namespace bcc
807